Compare commits

..

34 Commits

Author SHA1 Message Date
Cesar Francisco San Nicolas Martinez
c437a37be7 Merge pull request #13171 from infamousjoeg/cyberark-ccp-branding-webserviceid
Cyberark ccp branding webserviceid
2022-11-08 18:42:27 +01:00
Cesar Francisco San Nicolas Martinez
b59cee97d8 Merge branch 'test_cyberark' into cyberark-ccp-branding-webserviceid 2022-11-08 18:42:10 +01:00
Cesar Francisco San Nicolas Martinez
9ca554ce75 Merge pull request #13172 from infamousjoeg/fix-12846-conjur-versioning
Add proper declaration of secret version if present
2022-11-08 18:41:20 +01:00
Cesar Francisco San Nicolas Martinez
81e20c727d Merge pull request #13170 from infamousjoeg/conjur-cloud-and-branding
Conjur cloud and branding
2022-11-08 18:40:46 +01:00
Alan Rominger
8e5af2b5f2 Merge pull request #13092 from AlanCoding/tags_no_record
Fix error when applying taggit app tags to objects
2022-11-07 23:07:59 -05:00
Sarabraj Singh
918db89dc8 fixed GET and POST descriptors in swagger.json - /api/v2/job_templates/{id}/launch/ endpoint 2022-11-07 16:23:11 -05:00
Hao Liu
6e25a552d3 enable feature branch api-schema test (#12550)
- enable schema upload to s3 bucket for feature branch
- add workflow to delete schema from s3 bucket when feature branch is deleted

Signed-off-by: Hao Liu <haoli@redhat.com>
2022-11-04 16:14:55 +00:00
Michael Abashian
83c48bb5fa Revert "Merge pull request #12993 from AlexSCorey/fixWorkflow"
This reverts commit 75e6366c5e, reversing
changes made to af6fec5592.
2022-11-03 14:39:31 -04:00
Rick Elrod
1c65339a24 No InventoryUpdates when source Project is failed (#13063)
Previously, in some cases, an InventoryUpdate sourced by an SCM project
would still run and be successful even after the project it is sourced
from failed to update. This would happen because the InventoryUpdate
would revert the project back to its last working revision. This
behavior is confusing and inconsistent with how we handle jobs (which
just refuse to launch when the project is failed).

This change pulls out the logic that the job launch serializer and
RunJob#pre_run_hook had implemented (independently) to check if the
project is in a failed state, and puts it into a method on the Project
model. This is then checked in the project launch serializer as well as
the inventory update serializer, along with
SourceControlMixin#sync_and_copy as a fallback for things that don't run
the serializer validation (such as scheduled jobs and WFJT jobs).

Signed-off-by: Rick Elrod <rick@elrod.me>
2022-11-03 13:23:34 -05:00
Alex Corey
75e6366c5e Merge pull request #12993 from AlexSCorey/fixWorkflow
Gives enhanced permissions so that it can update dependabot pr bodies
2022-11-03 12:02:35 -04:00
John Westcott IV
af6fec5592 Merge pull request #12971 from mayaCostantini/precise-migrate-playbook-path
Precise migrate.yml path in development docs
2022-11-03 10:06:18 -04:00
Alan Rominger
893dba7076 Merge pull request #13114 from AlanCoding/use_seth
Remove ssh_key_data fix, handled in runner now
2022-11-02 15:41:00 -04:00
Alex Corey
b28cc34ff3 Merge pull request #13141 from ansible/dependabot/npm_and_yarn/awx/ui/devel/patternfly/react-core-4.250.1
Bump @patternfly/react-core from 4.239.0 to 4.250.1 in /awx/ui
2022-11-02 10:49:48 -04:00
dependabot[bot]
776d39f057 Bump @patternfly/react-core from 4.239.0 to 4.250.1 in /awx/ui
Bumps [@patternfly/react-core](https://github.com/patternfly/patternfly-react) from 4.239.0 to 4.250.1.
- [Release notes](https://github.com/patternfly/patternfly-react/releases)
- [Commits](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.239.0...@patternfly/react-core@4.250.1)

---
updated-dependencies:
- dependency-name: "@patternfly/react-core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-02 14:29:33 +00:00
Sarabraj Singh
61b242d194 initial commit of new machinery to handle redirects for webhook notifications (#13083) 2022-11-02 11:28:18 -03:00
Alex Corey
22b81f5dd3 Merge pull request #13150 from ansible/dependabot/npm_and_yarn/awx/ui/devel/patternfly/react-icons-4.92.10
Bump @patternfly/react-icons from 4.90.0 to 4.92.10 in /awx/ui
2022-11-02 10:27:25 -04:00
dependabot[bot]
99e1920d42 Bump @patternfly/react-icons from 4.90.0 to 4.92.10 in /awx/ui
Bumps [@patternfly/react-icons](https://github.com/patternfly/patternfly-react) from 4.90.0 to 4.92.10.
- [Release notes](https://github.com/patternfly/patternfly-react/releases)
- [Commits](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-icons@4.90.0...@patternfly/react-icons@4.92.10)

---
updated-dependencies:
- dependency-name: "@patternfly/react-icons"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-02 14:01:59 +00:00
Alex Corey
2218fd5c25 Merge pull request #13144 from ansible/dependabot/npm_and_yarn/awx/ui/devel/patternfly/patternfly-4.217.1
Bump @patternfly/patternfly from 4.210.2 to 4.217.1 in /awx/ui
2022-11-02 10:01:43 -04:00
Rebeccah Hunter
bd7635e74e Merge pull request #13146 from ansible/dedup-timeout
de-duplicate default settings
2022-11-01 16:47:00 -04:00
Elijah DeLee
0faa999ceb de-duplicate default settings 2022-11-01 14:50:32 -04:00
dependabot[bot]
577f102e53 Bump @patternfly/patternfly from 4.210.2 to 4.217.1 in /awx/ui
Bumps [@patternfly/patternfly](https://github.com/patternfly/patternfly) from 4.210.2 to 4.217.1.
- [Release notes](https://github.com/patternfly/patternfly/releases)
- [Changelog](https://github.com/patternfly/patternfly/blob/main/RELEASE-NOTES.md)
- [Commits](https://github.com/patternfly/patternfly/compare/prerelease-v4.210.2...prerelease-v4.217.1)

---
updated-dependencies:
- dependency-name: "@patternfly/patternfly"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 08:18:34 +00:00
Alan Rominger
6538d34b48 Remove ssh_key_data fix, handled in runner now 2022-10-31 11:01:28 -04:00
Joe Garcia
f3482f4038 Removed final reference to CyberArk Conjur Secret Lookup 2022-10-28 12:42:02 -04:00
Joe Garcia
878035c13b Fixed webservice_id check to string 2022-10-26 12:45:59 -04:00
Joe Garcia
2cc971a43f default to AIMWebService if no val provided 2022-10-26 12:41:15 -04:00
Joe Garcia
9d77c54612 Remove references to AIM everywhere 2022-10-26 12:32:12 -04:00
Joe Garcia
546fabbb97 Update references across the board 2022-10-26 12:28:50 -04:00
Joe Garcia
ef651a3a21 Add Web Service ID & update branding 2022-10-26 11:54:09 -04:00
Joe Garcia
68862d5085 rm base64 import to pass lint 2022-10-26 11:14:14 -04:00
Joe Garcia
66c7d5e9be Fixes #13119 #13120 Cloud support & update brand 2022-10-26 10:25:19 -04:00
Alan Rominger
487efb77ce Fix error when apply taggit app tags to objects 2022-10-21 14:53:31 -04:00
Joe Garcia
4a7335676d Add proper declaration of secret version if present 2022-10-17 14:43:16 -05:00
Alex Corey
aae57378f0 Gives enhanced permissions so that it can update dependabot pr bodies 2022-10-03 16:28:04 -04:00
Maya Costantini
8e83f9b134 Precise migrate.yml path in development docs 2022-09-28 10:57:40 +02:00
24 changed files with 281 additions and 138 deletions

View File

@@ -0,0 +1,24 @@
---
name: Feature branch deletion cleanup
on:
delete:
branches:
- feature_**
jobs:
push:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Delete API Schema
env:
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
AWS_REGION: 'us-east-1'
run: |
ansible localhost -c local, -m command -a "{{ ansible_python_interpreter + ' -m pip install boto3'}}"
ansible localhost -c local -m aws_s3 \
-a "bucket=awx-public-ci-files object=${GITHUB_REF##*/}/schema.json mode=delete permission=public-read"

View File

@@ -5,6 +5,7 @@ on:
branches:
- devel
- release_**
- feature_**
jobs:
push:
runs-on: ubuntu-latest

View File

@@ -2221,6 +2221,15 @@ class InventorySourceUpdateSerializer(InventorySourceSerializer):
class Meta:
fields = ('can_update',)
def validate(self, attrs):
project = self.instance.source_project
if project:
failed_reason = project.get_reason_if_failed()
if failed_reason:
raise serializers.ValidationError(failed_reason)
return super(InventorySourceUpdateSerializer, self).validate(attrs)
class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSerializer):
@@ -4272,17 +4281,10 @@ class JobLaunchSerializer(BaseSerializer):
# Basic validation - cannot run a playbook without a playbook
if not template.project:
errors['project'] = _("A project is required to run a job.")
elif template.project.status in ('error', 'failed'):
errors['playbook'] = _("Missing a revision to run due to failed project update.")
latest_update = template.project.project_updates.last()
if latest_update is not None and latest_update.failed:
failed_validation_tasks = latest_update.project_update_events.filter(
event='runner_on_failed',
play="Perform project signature/checksum verification",
)
if failed_validation_tasks:
errors['playbook'] = _("Last project update failed due to signature validation failure.")
else:
failure_reason = template.project.get_reason_if_failed()
if failure_reason:
errors['playbook'] = failure_reason
# cannot run a playbook without an inventory
if template.inventory and template.inventory.pending_deletion is True:

View File

@@ -1,5 +1,5 @@
Launch a Job Template:
{% ifmeth GET %}
Make a GET request to this resource to determine if the job_template can be
launched and whether any passwords are required to launch the job_template.
The response will include the following fields:
@@ -29,8 +29,8 @@ The response will include the following fields:
* `inventory_needed_to_start`: Flag indicating the presence of an inventory
associated with the job template. If not then one should be supplied when
launching the job (boolean, read-only)
Make a POST request to this resource to launch the job_template. If any
{% endifmeth %}
{% ifmeth POST %}Make a POST request to this resource to launch the job_template. If any
passwords, inventory, or extra variables (extra_vars) are required, they must
be passed via POST data, with extra_vars given as a YAML or JSON string and
escaped parentheses. If the `inventory_needed_to_start` is `True` then the
@@ -41,3 +41,4 @@ are not provided, a 400 status code will be returned. If the job cannot be
launched, a 405 status code will be returned. If the provided credential or
inventory are not allowed to be used by the user, then a 403 status code will
be returned.
{% endifmeth %}

View File

@@ -2221,6 +2221,8 @@ class InventorySourceUpdateView(RetrieveAPIView):
def post(self, request, *args, **kwargs):
obj = self.get_object()
serializer = self.get_serializer(instance=obj, data=request.data)
serializer.is_valid(raise_exception=True)
if obj.can_update:
update = obj.update()
if not update:

View File

@@ -9,10 +9,16 @@ aim_inputs = {
'fields': [
{
'id': 'url',
'label': _('CyberArk AIM URL'),
'label': _('CyberArk CCP URL'),
'type': 'string',
'format': 'url',
},
{
'id': 'webservice_id',
'label': _('Web Service ID'),
'type': 'string',
'help_text': _('The CCP Web Service ID. Leave blank to default to AIMWebService.'),
},
{
'id': 'app_id',
'label': _('Application ID'),
@@ -64,10 +70,13 @@ def aim_backend(**kwargs):
client_cert = kwargs.get('client_cert', None)
client_key = kwargs.get('client_key', None)
verify = kwargs['verify']
webservice_id = kwargs['webservice_id']
app_id = kwargs['app_id']
object_query = kwargs['object_query']
object_query_format = kwargs['object_query_format']
reason = kwargs.get('reason', None)
if webservice_id == '':
webservice_id = 'AIMWebService'
query_params = {
'AppId': app_id,
@@ -78,7 +87,7 @@ def aim_backend(**kwargs):
query_params['reason'] = reason
request_qs = '?' + urlencode(query_params, quote_via=quote)
request_url = urljoin(url, '/'.join(['AIMWebService', 'api', 'Accounts']))
request_url = urljoin(url, '/'.join([webservice_id, 'api', 'Accounts']))
with CertFiles(client_cert, client_key) as cert:
res = requests.get(
@@ -92,4 +101,4 @@ def aim_backend(**kwargs):
return res.json()['Content']
aim_plugin = CredentialPlugin('CyberArk AIM Central Credential Provider Lookup', inputs=aim_inputs, backend=aim_backend)
aim_plugin = CredentialPlugin('CyberArk Central Credential Provider Lookup', inputs=aim_inputs, backend=aim_backend)

View File

@@ -1,6 +1,5 @@
from .plugin import CredentialPlugin, CertFiles, raise_for_status
import base64
from urllib.parse import urljoin, quote
from django.utils.translation import gettext_lazy as _
@@ -61,7 +60,7 @@ def conjur_backend(**kwargs):
cacert = kwargs.get('cacert', None)
auth_kwargs = {
'headers': {'Content-Type': 'text/plain'},
'headers': {'Content-Type': 'text/plain', 'Accept-Encoding': 'base64'},
'data': api_key,
'allow_redirects': False,
}
@@ -69,9 +68,9 @@ def conjur_backend(**kwargs):
with CertFiles(cacert) as cert:
# https://www.conjur.org/api.html#authentication-authenticate-post
auth_kwargs['verify'] = cert
resp = requests.post(urljoin(url, '/'.join(['authn', account, username, 'authenticate'])), **auth_kwargs)
resp = requests.post(urljoin(url, '/'.join(['api', 'authn', account, username, 'authenticate'])), **auth_kwargs)
raise_for_status(resp)
token = base64.b64encode(resp.content).decode('utf-8')
token = resp.content.decode('utf-8')
lookup_kwargs = {
'headers': {'Authorization': 'Token token="{}"'.format(token)},
@@ -79,9 +78,10 @@ def conjur_backend(**kwargs):
}
# https://www.conjur.org/api.html#secrets-retrieve-a-secret-get
path = urljoin(url, '/'.join(['secrets', account, 'variable', secret_path]))
path = urljoin(url, '/'.join(['api', 'secrets', account, 'variable', secret_path]))
if version:
path = '?'.join([path, version])
ver = "version={}".format(version)
path = '?'.join([path, ver])
with CertFiles(cacert) as cert:
lookup_kwargs['verify'] = cert
@@ -90,4 +90,4 @@ def conjur_backend(**kwargs):
return resp.text
conjur_plugin = CredentialPlugin('CyberArk Conjur Secret Lookup', inputs=conjur_inputs, backend=conjur_backend)
conjur_plugin = CredentialPlugin('CyberArk Conjur Secrets Manager Lookup', inputs=conjur_inputs, backend=conjur_backend)

View File

@@ -471,6 +471,29 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn
def get_absolute_url(self, request=None):
return reverse('api:project_detail', kwargs={'pk': self.pk}, request=request)
def get_reason_if_failed(self):
"""
If the project is in a failed or errored state, return a human-readable
error message explaining why. Otherwise return None.
This is used during validation in the serializer and also by
RunProjectUpdate/RunInventoryUpdate.
"""
if self.status not in ('error', 'failed'):
return None
latest_update = self.project_updates.last()
if latest_update is not None and latest_update.failed:
failed_validation_tasks = latest_update.project_update_events.filter(
event='runner_on_failed',
play="Perform project signature/checksum verification",
)
if failed_validation_tasks:
return _("Last project update failed due to signature validation failure.")
return _("Missing a revision to run due to failed project update.")
'''
RelatedJobsMixin
'''

View File

@@ -5,9 +5,6 @@ import json
import logging
import requests
from django.utils.encoding import smart_str
from django.utils.translation import gettext_lazy as _
from awx.main.notifications.base import AWXBaseEmailBackend
from awx.main.utils import get_awx_http_client_headers
from awx.main.notifications.custom_notification_base import CustomNotificationBase
@@ -17,6 +14,8 @@ logger = logging.getLogger('awx.main.notifications.webhook_backend')
class WebhookBackend(AWXBaseEmailBackend, CustomNotificationBase):
MAX_RETRIES = 5
init_parameters = {
"url": {"label": "Target URL", "type": "string"},
"http_method": {"label": "HTTP Method", "type": "string", "default": "POST"},
@@ -64,20 +63,67 @@ class WebhookBackend(AWXBaseEmailBackend, CustomNotificationBase):
if self.http_method.lower() not in ['put', 'post']:
raise ValueError("HTTP method must be either 'POST' or 'PUT'.")
chosen_method = getattr(requests, self.http_method.lower(), None)
for m in messages:
auth = None
if self.username or self.password:
auth = (self.username, self.password)
r = chosen_method(
"{}".format(m.recipients()[0]),
auth=auth,
data=json.dumps(m.body, ensure_ascii=False).encode('utf-8'),
headers=dict(list(get_awx_http_client_headers().items()) + list((self.headers or {}).items())),
verify=(not self.disable_ssl_verification),
)
if r.status_code >= 400:
logger.error(smart_str(_("Error sending notification webhook: {}").format(r.status_code)))
# the constructor for EmailMessage - https://docs.djangoproject.com/en/4.1/_modules/django/core/mail/message will turn an empty dictionary to an empty string
# sometimes an empty dict is intentional and we added this conditional to enforce that
if not m.body:
m.body = {}
url = str(m.recipients()[0])
data = json.dumps(m.body, ensure_ascii=False).encode('utf-8')
headers = {**(get_awx_http_client_headers()), **(self.headers or {})}
err = None
for retries in range(self.MAX_RETRIES):
# Sometimes we hit redirect URLs. We must account for this. We still extract the redirect URL from the response headers and try again. Max retires == 5
resp = chosen_method(
url=url,
auth=auth,
data=data,
headers=headers,
verify=(not self.disable_ssl_verification),
allow_redirects=False, # override default behaviour for redirects
)
# either success or error reached if this conditional fires
if resp.status_code not in [301, 307]:
break
# we've hit a redirect. extract the redirect URL out of the first response header and try again
logger.warning(
f"Received a {resp.status_code} from {url}, trying to reach redirect url {resp.headers.get('Location', None)}; attempt #{retries+1}"
)
# take the first redirect URL in the response header and try that
url = resp.headers.get("Location", None)
if url is None:
err = f"Webhook notification received redirect to a blank URL from {url}. Response headers={resp.headers}"
break
else:
# no break condition in the loop encountered; therefore we have hit the maximum number of retries
err = f"Webhook notification max number of retries [{self.MAX_RETRIES}] exceeded. Failed to send webhook notification to {url}"
if resp.status_code >= 400:
err = f"Error sending webhook notification: {resp.status_code}"
# log error message
if err:
logger.error(err)
if not self.fail_silently:
raise Exception(smart_str(_("Error sending notification webhook: {}").format(r.status_code)))
sent_messages += 1
raise Exception(err)
# no errors were encountered therefore we successfully sent off the notification webhook
if resp.status_code in range(200, 299):
logger.debug(f"Notification webhook successfully sent to {url}. Received {resp.status_code}")
sent_messages += 1
return sent_messages

View File

@@ -3,6 +3,8 @@
from django.db.models.signals import pre_save, post_save, pre_delete, m2m_changed
from taggit.managers import TaggableManager
class ActivityStreamRegistrar(object):
def __init__(self):
@@ -19,6 +21,8 @@ class ActivityStreamRegistrar(object):
pre_delete.connect(activity_stream_delete, sender=model, dispatch_uid=str(self.__class__) + str(model) + "_delete")
for m2mfield in model._meta.many_to_many:
if isinstance(m2mfield, TaggableManager):
continue # Special case for taggit app
try:
m2m_attr = getattr(model, m2mfield.name)
m2m_changed.connect(

View File

@@ -2,8 +2,6 @@ import json
import time
import logging
from collections import deque
import os
import stat
# Django
from django.conf import settings
@@ -206,21 +204,6 @@ class RunnerCallback:
self.instance = self.update_model(self.instance.pk, job_args=json.dumps(runner_config.command), job_cwd=runner_config.cwd, job_env=job_env)
# We opened a connection just for that save, close it here now
connections.close_all()
elif status_data['status'] == 'failed':
# For encrypted ssh_key_data, ansible-runner worker will open and write the
# ssh_key_data to a named pipe. Then, once the podman container starts, ssh-agent will
# read from this named pipe so that the key can be used in ansible-playbook.
# Once the podman container exits, the named pipe is deleted.
# However, if the podman container fails to start in the first place, e.g. the image
# name is incorrect, then this pipe is not cleaned up. Eventually ansible-runner
# processor will attempt to write artifacts to the private data dir via unstream_dir, requiring
# that it open this named pipe. This leads to a hang. Thus, before any artifacts
# are written by the processor, it's important to remove this ssh_key_data pipe.
private_data_dir = self.instance.job_env.get('AWX_PRIVATE_DATA_DIR', None)
if private_data_dir:
key_data_file = os.path.join(private_data_dir, 'artifacts', str(self.instance.id), 'ssh_key_data')
if os.path.exists(key_data_file) and stat.S_ISFIFO(os.stat(key_data_file).st_mode):
os.remove(key_data_file)
elif status_data['status'] == 'error':
result_traceback = status_data.get('result_traceback', None)
if result_traceback:

View File

@@ -767,6 +767,10 @@ class SourceControlMixin(BaseTask):
try:
original_branch = None
failed_reason = project.get_reason_if_failed()
if failed_reason:
self.update_model(self.instance.pk, status='failed', job_explanation=failed_reason)
raise RuntimeError(failed_reason)
project_path = project.get_project_path(check_if_exists=False)
if project.scm_type == 'git' and (scm_branch and scm_branch != project.scm_branch):
if os.path.exists(project_path):
@@ -1056,10 +1060,6 @@ class RunJob(SourceControlMixin, BaseTask):
error = _('Job could not start because no Execution Environment could be found.')
self.update_model(job.pk, status='error', job_explanation=error)
raise RuntimeError(error)
elif job.project.status in ('error', 'failed'):
msg = _('The project revision for this job template is unknown due to a failed update.')
job = self.update_model(job.pk, status='failed', job_explanation=msg)
raise RuntimeError(msg)
if job.inventory.kind == 'smart':
# cache smart inventory memberships so that the host_filter query is not

View File

@@ -75,6 +75,7 @@ def test_encrypted_subfields(get, post, user, organization):
url = reverse('api:notification_template_detail', kwargs={'pk': response.data['id']})
response = get(url, u)
assert response.data['notification_configuration']['account_token'] == "$encrypted$"
with mock.patch.object(notification_template_actual.notification_class, "send_messages", assert_send):
notification_template_actual.send("Test", {'body': "Test"})
@@ -175,3 +176,46 @@ def test_custom_environment_injection(post, user, organization):
fake_send.side_effect = _send_side_effect
template.send('subject', 'message')
def mock_post(*args, **kwargs):
class MockGoodResponse:
def __init__(self):
self.status_code = 200
class MockRedirectResponse:
def __init__(self):
self.status_code = 301
self.headers = {"Location": "http://goodendpoint"}
if kwargs['url'] == "http://goodendpoint":
return MockGoodResponse()
else:
return MockRedirectResponse()
@pytest.mark.django_db
@mock.patch('requests.post', side_effect=mock_post)
def test_webhook_notification_pointed_to_a_redirect_launch_endpoint(post, admin, organization):
n1 = NotificationTemplate.objects.create(
name="test-webhook",
description="test webhook",
organization=organization,
notification_type="webhook",
notification_configuration=dict(
url="http://some.fake.url",
disable_ssl_verification=True,
http_method="POST",
headers={
"Content-Type": "application/json",
},
username=admin.username,
password=admin.password,
),
messages={
"success": {"message": "", "body": "{}"},
},
)
assert n1.send("", n1.messages.get("success").get("body")) == 1

View File

@@ -27,11 +27,12 @@ def test_send_messages_as_POST():
]
)
requests_mock.post.assert_called_once_with(
'http://example.com',
url='http://example.com',
auth=None,
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
verify=True,
allow_redirects=False,
)
assert sent_messages == 1
@@ -57,11 +58,12 @@ def test_send_messages_as_PUT():
]
)
requests_mock.put.assert_called_once_with(
'http://example.com',
url='http://example.com',
auth=None,
data=json.dumps({'text': 'test body 2'}, ensure_ascii=False).encode('utf-8'),
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
verify=True,
allow_redirects=False,
)
assert sent_messages == 1
@@ -87,11 +89,12 @@ def test_send_messages_with_username():
]
)
requests_mock.post.assert_called_once_with(
'http://example.com',
url='http://example.com',
auth=('userstring', None),
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
verify=True,
allow_redirects=False,
)
assert sent_messages == 1
@@ -117,11 +120,12 @@ def test_send_messages_with_password():
]
)
requests_mock.post.assert_called_once_with(
'http://example.com',
url='http://example.com',
auth=(None, 'passwordstring'),
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
verify=True,
allow_redirects=False,
)
assert sent_messages == 1
@@ -147,11 +151,12 @@ def test_send_messages_with_username_and_password():
]
)
requests_mock.post.assert_called_once_with(
'http://example.com',
url='http://example.com',
auth=('userstring', 'passwordstring'),
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
verify=True,
allow_redirects=False,
)
assert sent_messages == 1
@@ -177,11 +182,12 @@ def test_send_messages_with_no_verify_ssl():
]
)
requests_mock.post.assert_called_once_with(
'http://example.com',
url='http://example.com',
auth=None,
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
verify=False,
allow_redirects=False,
)
assert sent_messages == 1
@@ -207,7 +213,7 @@ def test_send_messages_with_additional_headers():
]
)
requests_mock.post.assert_called_once_with(
'http://example.com',
url='http://example.com',
auth=None,
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
headers={
@@ -217,5 +223,6 @@ def test_send_messages_with_additional_headers():
'X-Test-Header2': 'test-content-2',
},
verify=True,
allow_redirects=False,
)
assert sent_messages == 1

View File

@@ -254,6 +254,14 @@ START_TASK_LIMIT = 100
TASK_MANAGER_TIMEOUT = 300
TASK_MANAGER_TIMEOUT_GRACE_PERIOD = 60
# Number of seconds _in addition to_ the task manager timeout a job can stay
# in waiting without being reaped
JOB_WAITING_GRACE_PERIOD = 60
# Number of seconds after a container group job finished time to wait
# before the awx_k8s_reaper task will tear down the pods
K8S_POD_REAPER_GRACE_PERIOD = 60
# Disallow sending session cookies over insecure connections
SESSION_COOKIE_SECURE = True
@@ -1004,16 +1012,5 @@ DEFAULT_CONTAINER_RUN_OPTIONS = ['--network', 'slirp4netns:enable_ipv6=true']
# Mount exposed paths as hostPath resource in k8s/ocp
AWX_MOUNT_ISOLATED_PATHS_ON_K8S = False
# Time out task managers if they take longer than this many seconds
TASK_MANAGER_TIMEOUT = 300
# Number of seconds _in addition to_ the task manager timeout a job can stay
# in waiting without being reaped
JOB_WAITING_GRACE_PERIOD = 60
# Number of seconds after a container group job finished time to wait
# before the awx_k8s_reaper task will tear down the pods
K8S_POD_REAPER_GRACE_PERIOD = 60
# This is overridden downstream via /etc/tower/conf.d/cluster_host_id.py
CLUSTER_HOST_ID = socket.gethostname()

View File

@@ -7,9 +7,9 @@
"name": "ui",
"dependencies": {
"@lingui/react": "3.14.0",
"@patternfly/patternfly": "4.210.2",
"@patternfly/react-core": "^4.239.0",
"@patternfly/react-icons": "4.90.0",
"@patternfly/patternfly": "4.217.1",
"@patternfly/react-core": "^4.250.1",
"@patternfly/react-icons": "4.92.10",
"@patternfly/react-table": "4.108.0",
"ace-builds": "^1.10.1",
"ansi-to-html": "0.7.2",
@@ -3747,26 +3747,26 @@
"dev": true
},
"node_modules/@patternfly/patternfly": {
"version": "4.210.2",
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.210.2.tgz",
"integrity": "sha512-aZiW24Bxi6uVmk5RyNTp+6q6ThtlJZotNRJfWVeGuwu1UlbBuV4DFa1bpjA6jfTZpfEpX2YL5+R+4ZVSCFAVdw=="
"version": "4.217.1",
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.217.1.tgz",
"integrity": "sha512-uN7JgfQsyR16YHkuGRCTIcBcnyKIqKjGkB2SGk9x1XXH3yYGenL83kpAavX9Xtozqp17KppOlybJuzcKvZMrgw=="
},
"node_modules/@patternfly/react-core": {
"version": "4.239.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.239.0.tgz",
"integrity": "sha512-6CmYABCJLUXTlzCk6C3WouMNZpS0BCT+aHU8CvYpFQ/NrpYp3MJaDsYbqgCRWV42rmIO5iXun/4WhXBJzJEoQg==",
"version": "4.250.1",
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.250.1.tgz",
"integrity": "sha512-vAOZPQdZzYXl/vkHnHMIt1eC3nrPDdsuuErPatkNPwmSvilXuXmWP5wxoJ36FbSNRRURkprFwx52zMmWS3iHJA==",
"dependencies": {
"@patternfly/react-icons": "^4.90.0",
"@patternfly/react-styles": "^4.89.0",
"@patternfly/react-tokens": "^4.91.0",
"@patternfly/react-icons": "^4.92.6",
"@patternfly/react-styles": "^4.91.6",
"@patternfly/react-tokens": "^4.93.6",
"focus-trap": "6.9.2",
"react-dropzone": "9.0.0",
"tippy.js": "5.1.2",
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
"react": "^16.8 || ^17 || ^18",
"react-dom": "^16.8 || ^17 || ^18"
}
},
"node_modules/@patternfly/react-core/node_modules/tslib": {
@@ -3775,18 +3775,18 @@
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@patternfly/react-icons": {
"version": "4.90.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.90.0.tgz",
"integrity": "sha512-qEnQKbxbUgyiosiKSkeKEBwmhgJwWEqniIAFyoxj+kpzAdeu7ueWe5iBbqo06mvDOedecFiM5mIE1N0MXwk8Yw==",
"version": "4.92.10",
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.92.10.tgz",
"integrity": "sha512-vwCy7b+OyyuvLDSLqLUG2DkJZgMDogjld8tJTdAaG8HiEhC1sJPZac+5wD7AuS3ym/sQolS4vYtNiVDnMEORxA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
"react": "^16.8 || ^17 || ^18",
"react-dom": "^16.8 || ^17 || ^18"
}
},
"node_modules/@patternfly/react-styles": {
"version": "4.89.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.89.0.tgz",
"integrity": "sha512-SkT+qx3Xqu70T5s+i/AUT2hI2sKAPDX4ffeiJIUDu/oyWiFdk+/9DEivnLSyJMruroXXN33zKibvzb5rH7DKTQ=="
"version": "4.91.10",
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.91.10.tgz",
"integrity": "sha512-fAG4Vjp63ohiR92F4e/Gkw5q1DSSckHKqdnEF75KUpSSBORzYP0EKMpupSd6ItpQFJw3iWs3MJi3/KIAAfU1Jw=="
},
"node_modules/@patternfly/react-table": {
"version": "4.108.0",
@@ -3811,9 +3811,9 @@
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/@patternfly/react-tokens": {
"version": "4.91.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.91.0.tgz",
"integrity": "sha512-QeQCy8o8E/16fAr8mxqXIYRmpTsjCHJXi5p5jmgEDFmYMesN6Pqfv6N5D0FHb+CIaNOZWRps7GkWvlIMIE81sw=="
"version": "4.93.10",
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.93.10.tgz",
"integrity": "sha512-F+j1irDc9M6zvY6qNtDryhbpnHz3R8ymHRdGelNHQzPTIK88YSWEnT1c9iUI+uM/iuZol7sJmO5STtg2aPIDRQ=="
},
"node_modules/@pmmmwh/react-refresh-webpack-plugin": {
"version": "0.5.4",
@@ -25089,18 +25089,18 @@
"dev": true
},
"@patternfly/patternfly": {
"version": "4.210.2",
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.210.2.tgz",
"integrity": "sha512-aZiW24Bxi6uVmk5RyNTp+6q6ThtlJZotNRJfWVeGuwu1UlbBuV4DFa1bpjA6jfTZpfEpX2YL5+R+4ZVSCFAVdw=="
"version": "4.217.1",
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.217.1.tgz",
"integrity": "sha512-uN7JgfQsyR16YHkuGRCTIcBcnyKIqKjGkB2SGk9x1XXH3yYGenL83kpAavX9Xtozqp17KppOlybJuzcKvZMrgw=="
},
"@patternfly/react-core": {
"version": "4.239.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.239.0.tgz",
"integrity": "sha512-6CmYABCJLUXTlzCk6C3WouMNZpS0BCT+aHU8CvYpFQ/NrpYp3MJaDsYbqgCRWV42rmIO5iXun/4WhXBJzJEoQg==",
"version": "4.250.1",
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.250.1.tgz",
"integrity": "sha512-vAOZPQdZzYXl/vkHnHMIt1eC3nrPDdsuuErPatkNPwmSvilXuXmWP5wxoJ36FbSNRRURkprFwx52zMmWS3iHJA==",
"requires": {
"@patternfly/react-icons": "^4.90.0",
"@patternfly/react-styles": "^4.89.0",
"@patternfly/react-tokens": "^4.91.0",
"@patternfly/react-icons": "^4.92.6",
"@patternfly/react-styles": "^4.91.6",
"@patternfly/react-tokens": "^4.93.6",
"focus-trap": "6.9.2",
"react-dropzone": "9.0.0",
"tippy.js": "5.1.2",
@@ -25115,15 +25115,15 @@
}
},
"@patternfly/react-icons": {
"version": "4.90.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.90.0.tgz",
"integrity": "sha512-qEnQKbxbUgyiosiKSkeKEBwmhgJwWEqniIAFyoxj+kpzAdeu7ueWe5iBbqo06mvDOedecFiM5mIE1N0MXwk8Yw==",
"version": "4.92.10",
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.92.10.tgz",
"integrity": "sha512-vwCy7b+OyyuvLDSLqLUG2DkJZgMDogjld8tJTdAaG8HiEhC1sJPZac+5wD7AuS3ym/sQolS4vYtNiVDnMEORxA==",
"requires": {}
},
"@patternfly/react-styles": {
"version": "4.89.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.89.0.tgz",
"integrity": "sha512-SkT+qx3Xqu70T5s+i/AUT2hI2sKAPDX4ffeiJIUDu/oyWiFdk+/9DEivnLSyJMruroXXN33zKibvzb5rH7DKTQ=="
"version": "4.91.10",
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.91.10.tgz",
"integrity": "sha512-fAG4Vjp63ohiR92F4e/Gkw5q1DSSckHKqdnEF75KUpSSBORzYP0EKMpupSd6ItpQFJw3iWs3MJi3/KIAAfU1Jw=="
},
"@patternfly/react-table": {
"version": "4.108.0",
@@ -25146,9 +25146,9 @@
}
},
"@patternfly/react-tokens": {
"version": "4.91.0",
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.91.0.tgz",
"integrity": "sha512-QeQCy8o8E/16fAr8mxqXIYRmpTsjCHJXi5p5jmgEDFmYMesN6Pqfv6N5D0FHb+CIaNOZWRps7GkWvlIMIE81sw=="
"version": "4.93.10",
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.93.10.tgz",
"integrity": "sha512-F+j1irDc9M6zvY6qNtDryhbpnHz3R8ymHRdGelNHQzPTIK88YSWEnT1c9iUI+uM/iuZol7sJmO5STtg2aPIDRQ=="
},
"@pmmmwh/react-refresh-webpack-plugin": {
"version": "0.5.4",

View File

@@ -7,9 +7,9 @@
},
"dependencies": {
"@lingui/react": "3.14.0",
"@patternfly/patternfly": "4.210.2",
"@patternfly/react-core": "^4.239.0",
"@patternfly/react-icons": "4.90.0",
"@patternfly/patternfly": "4.217.1",
"@patternfly/react-core": "^4.250.1",
"@patternfly/react-icons": "4.92.10",
"@patternfly/react-table": "4.108.0",
"ace-builds": "^1.10.1",
"ansi-to-html": "0.7.2",

View File

@@ -282,7 +282,7 @@ const mockInputSources = {
summary_fields: {
source_credential: {
id: 20,
name: 'CyberArk Conjur Secret Lookup',
name: 'CyberArk Conjur Secrets Manager Lookup',
description: '',
kind: 'conjur',
cloud: false,
@@ -301,7 +301,7 @@ const mockInputSources = {
summary_fields: {
source_credential: {
id: 20,
name: 'CyberArk Conjur Secret Lookup',
name: 'CyberArk Conjur Secrets Manager Lookup',
description: '',
kind: 'conjur',
cloud: false,

View File

@@ -36,14 +36,14 @@ const mockCredentialTypeDetail = {
url: '/api/v2/credential_types/20/',
related: {
named_url:
'/api/v2/credential_types/CyberArk Conjur Secret Lookup+external/',
'/api/v2/credential_types/CyberArk Conjur Secrets Manager Lookup+external/',
credentials: '/api/v2/credential_types/20/credentials/',
activity_stream: '/api/v2/credential_types/20/activity_stream/',
},
summary_fields: { user_capabilities: { edit: false, delete: false } },
created: '2020-05-18T21:53:35.398260Z',
modified: '2020-05-18T21:54:05.451444Z',
name: 'CyberArk Conjur Secret Lookup',
name: 'CyberArk Conjur Secrets Manager Lookup',
description: '',
kind: 'external',
namespace: 'conjur',

View File

@@ -465,7 +465,7 @@
},
"created": "2020-05-18T21:53:35.370730Z",
"modified": "2020-05-18T21:54:05.436400Z",
"name": "CyberArk AIM Central Credential Provider Lookup",
"name": "CyberArk Central Credential Provider Lookup",
"description": "",
"kind": "external",
"namespace": "aim",
@@ -546,7 +546,7 @@
},
"created": "2020-05-18T21:53:35.398260Z",
"modified": "2020-05-18T21:54:05.451444Z",
"name": "CyberArk Conjur Secret Lookup",
"name": "CyberArk Conjur Secrets Manager Lookup",
"description": "",
"kind": "external",
"namespace": "conjur",

View File

@@ -3,7 +3,7 @@
"type": "credential",
"url": "/api/v2/credentials/1/",
"related": {
"named_url": "/api/v2/credentials/CyberArk Conjur Secret Lookup++CyberArk Conjur Secret Lookup+external++/",
"named_url": "/api/v2/credentials/CyberArk Conjur Secrets Manager Lookup+external++/",
"created_by": "/api/v2/users/1/",
"modified_by": "/api/v2/users/1/",
"activity_stream": "/api/v2/credentials/1/activity_stream/",
@@ -19,7 +19,7 @@
"summary_fields": {
"credential_type": {
"id": 20,
"name": "CyberArk Conjur Secret Lookup",
"name": "CyberArk Conjur Secrets Manager Lookup",
"description": ""
},
"created_by": {
@@ -69,7 +69,7 @@
},
"created": "2020-05-19T12:51:36.956029Z",
"modified": "2020-05-19T12:51:36.956086Z",
"name": "CyberArk Conjur Secret Lookup",
"name": "CyberArk Conjur Secrets Manager Lookup",
"description": "",
"organization": null,
"credential_type": 20,

View File

@@ -52,7 +52,7 @@ options:
- The credential type being created.
- Can be a built-in credential type such as "Machine", or a custom credential type such as "My Credential Type"
- Choices include Amazon Web Services, Ansible Galaxy/Automation Hub API Token, Centrify Vault Credential Provider Lookup,
Container Registry, CyberArk AIM Central Credential Provider Lookup, CyberArk Conjur Secret Lookup, Google Compute Engine,
Container Registry, CyberArk Central Credential Provider Lookup, CyberArk Conjur Secret Lookup, Google Compute Engine,
GitHub Personal Access Token, GitLab Personal Access Token, GPG Public Key, HashiCorp Vault Secret Lookup, HashiCorp Vault Signed SSH,
Insights, Machine, Microsoft Azure Key Vault, Microsoft Azure Resource Manager, Network, OpenShift or Kubernetes API
Bearer Token, OpenStack, Red Hat Ansible Automation Platform, Red Hat Satellite 6, Red Hat Virtualization, Source Control,

View File

@@ -14,7 +14,7 @@
credential:
description: Credential for Testing Source
name: "{{ src_cred_name }}"
credential_type: CyberArk AIM Central Credential Provider Lookup
credential_type: CyberArk Central Credential Provider Lookup
inputs:
url: "https://cyberark.example.com"
app_id: "My-App-ID"
@@ -58,7 +58,7 @@
credential:
description: Credential for Testing Source Change
name: "{{ src_cred_name }}-2"
credential_type: CyberArk AIM Central Credential Provider Lookup
credential_type: CyberArk Central Credential Provider Lookup
inputs:
url: "https://cyberark-prod.example.com"
app_id: "My-App-ID"
@@ -92,7 +92,7 @@
credential:
name: "{{ src_cred_name }}"
organization: Default
credential_type: CyberArk AIM Central Credential Provider Lookup
credential_type: CyberArk Central Credential Provider Lookup
state: absent
register: result
@@ -100,7 +100,7 @@
credential:
name: "{{ src_cred_name }}-2"
organization: Default
credential_type: CyberArk AIM Central Credential Provider Lookup
credential_type: CyberArk Central Credential Provider Lookup
state: absent
register: result

View File

@@ -28,7 +28,7 @@ Here are the main `make` targets:
Notable files:
- `tools/docker-compose/inventory` file - used to configure the AWX development environment.
- `migrate.yml` - playbook for migrating data from Local Docker to the Development Environment
- `tools/docker-compose/ansible/migrate.yml` - playbook for migrating data from Local Docker to the Development Environment
### Prerequisites