mirror of
https://github.com/ansible/awx.git
synced 2026-04-06 02:29:21 -02:30
Compare commits
34 Commits
21.8.0
...
test_cyber
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c437a37be7 | ||
|
|
b59cee97d8 | ||
|
|
9ca554ce75 | ||
|
|
81e20c727d | ||
|
|
8e5af2b5f2 | ||
|
|
918db89dc8 | ||
|
|
6e25a552d3 | ||
|
|
83c48bb5fa | ||
|
|
1c65339a24 | ||
|
|
75e6366c5e | ||
|
|
af6fec5592 | ||
|
|
893dba7076 | ||
|
|
b28cc34ff3 | ||
|
|
776d39f057 | ||
|
|
61b242d194 | ||
|
|
22b81f5dd3 | ||
|
|
99e1920d42 | ||
|
|
2218fd5c25 | ||
|
|
bd7635e74e | ||
|
|
0faa999ceb | ||
|
|
577f102e53 | ||
|
|
6538d34b48 | ||
|
|
f3482f4038 | ||
|
|
878035c13b | ||
|
|
2cc971a43f | ||
|
|
9d77c54612 | ||
|
|
546fabbb97 | ||
|
|
ef651a3a21 | ||
|
|
68862d5085 | ||
|
|
66c7d5e9be | ||
|
|
487efb77ce | ||
|
|
4a7335676d | ||
|
|
aae57378f0 | ||
|
|
8e83f9b134 |
24
.github/workflows/feature_branch_deletion.yml
vendored
Normal file
24
.github/workflows/feature_branch_deletion.yml
vendored
Normal 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"
|
||||||
|
|
||||||
|
|
||||||
1
.github/workflows/upload_schema.yml
vendored
1
.github/workflows/upload_schema.yml
vendored
@@ -5,6 +5,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- devel
|
- devel
|
||||||
- release_**
|
- release_**
|
||||||
|
- feature_**
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -2221,6 +2221,15 @@ class InventorySourceUpdateSerializer(InventorySourceSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
fields = ('can_update',)
|
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):
|
class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSerializer):
|
||||||
|
|
||||||
@@ -4272,17 +4281,10 @@ class JobLaunchSerializer(BaseSerializer):
|
|||||||
# Basic validation - cannot run a playbook without a playbook
|
# Basic validation - cannot run a playbook without a playbook
|
||||||
if not template.project:
|
if not template.project:
|
||||||
errors['project'] = _("A project is required to run a job.")
|
errors['project'] = _("A project is required to run a job.")
|
||||||
elif template.project.status in ('error', 'failed'):
|
else:
|
||||||
errors['playbook'] = _("Missing a revision to run due to failed project update.")
|
failure_reason = template.project.get_reason_if_failed()
|
||||||
|
if failure_reason:
|
||||||
latest_update = template.project.project_updates.last()
|
errors['playbook'] = failure_reason
|
||||||
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.")
|
|
||||||
|
|
||||||
# cannot run a playbook without an inventory
|
# cannot run a playbook without an inventory
|
||||||
if template.inventory and template.inventory.pending_deletion is True:
|
if template.inventory and template.inventory.pending_deletion is True:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Launch a Job Template:
|
Launch a Job Template:
|
||||||
|
{% ifmeth GET %}
|
||||||
Make a GET request to this resource to determine if the job_template can be
|
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.
|
launched and whether any passwords are required to launch the job_template.
|
||||||
The response will include the following fields:
|
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
|
* `inventory_needed_to_start`: Flag indicating the presence of an inventory
|
||||||
associated with the job template. If not then one should be supplied when
|
associated with the job template. If not then one should be supplied when
|
||||||
launching the job (boolean, read-only)
|
launching the job (boolean, read-only)
|
||||||
|
{% endifmeth %}
|
||||||
Make a POST request to this resource to launch the job_template. If any
|
{% 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
|
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
|
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
|
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
|
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
|
inventory are not allowed to be used by the user, then a 403 status code will
|
||||||
be returned.
|
be returned.
|
||||||
|
{% endifmeth %}
|
||||||
@@ -2221,6 +2221,8 @@ class InventorySourceUpdateView(RetrieveAPIView):
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
serializer = self.get_serializer(instance=obj, data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
if obj.can_update:
|
if obj.can_update:
|
||||||
update = obj.update()
|
update = obj.update()
|
||||||
if not update:
|
if not update:
|
||||||
|
|||||||
@@ -9,10 +9,16 @@ aim_inputs = {
|
|||||||
'fields': [
|
'fields': [
|
||||||
{
|
{
|
||||||
'id': 'url',
|
'id': 'url',
|
||||||
'label': _('CyberArk AIM URL'),
|
'label': _('CyberArk CCP URL'),
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'format': 'url',
|
'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',
|
'id': 'app_id',
|
||||||
'label': _('Application ID'),
|
'label': _('Application ID'),
|
||||||
@@ -64,10 +70,13 @@ def aim_backend(**kwargs):
|
|||||||
client_cert = kwargs.get('client_cert', None)
|
client_cert = kwargs.get('client_cert', None)
|
||||||
client_key = kwargs.get('client_key', None)
|
client_key = kwargs.get('client_key', None)
|
||||||
verify = kwargs['verify']
|
verify = kwargs['verify']
|
||||||
|
webservice_id = kwargs['webservice_id']
|
||||||
app_id = kwargs['app_id']
|
app_id = kwargs['app_id']
|
||||||
object_query = kwargs['object_query']
|
object_query = kwargs['object_query']
|
||||||
object_query_format = kwargs['object_query_format']
|
object_query_format = kwargs['object_query_format']
|
||||||
reason = kwargs.get('reason', None)
|
reason = kwargs.get('reason', None)
|
||||||
|
if webservice_id == '':
|
||||||
|
webservice_id = 'AIMWebService'
|
||||||
|
|
||||||
query_params = {
|
query_params = {
|
||||||
'AppId': app_id,
|
'AppId': app_id,
|
||||||
@@ -78,7 +87,7 @@ def aim_backend(**kwargs):
|
|||||||
query_params['reason'] = reason
|
query_params['reason'] = reason
|
||||||
|
|
||||||
request_qs = '?' + urlencode(query_params, quote_via=quote)
|
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:
|
with CertFiles(client_cert, client_key) as cert:
|
||||||
res = requests.get(
|
res = requests.get(
|
||||||
@@ -92,4 +101,4 @@ def aim_backend(**kwargs):
|
|||||||
return res.json()['Content']
|
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)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from .plugin import CredentialPlugin, CertFiles, raise_for_status
|
from .plugin import CredentialPlugin, CertFiles, raise_for_status
|
||||||
|
|
||||||
import base64
|
|
||||||
from urllib.parse import urljoin, quote
|
from urllib.parse import urljoin, quote
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
@@ -61,7 +60,7 @@ def conjur_backend(**kwargs):
|
|||||||
cacert = kwargs.get('cacert', None)
|
cacert = kwargs.get('cacert', None)
|
||||||
|
|
||||||
auth_kwargs = {
|
auth_kwargs = {
|
||||||
'headers': {'Content-Type': 'text/plain'},
|
'headers': {'Content-Type': 'text/plain', 'Accept-Encoding': 'base64'},
|
||||||
'data': api_key,
|
'data': api_key,
|
||||||
'allow_redirects': False,
|
'allow_redirects': False,
|
||||||
}
|
}
|
||||||
@@ -69,9 +68,9 @@ def conjur_backend(**kwargs):
|
|||||||
with CertFiles(cacert) as cert:
|
with CertFiles(cacert) as cert:
|
||||||
# https://www.conjur.org/api.html#authentication-authenticate-post
|
# https://www.conjur.org/api.html#authentication-authenticate-post
|
||||||
auth_kwargs['verify'] = cert
|
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)
|
raise_for_status(resp)
|
||||||
token = base64.b64encode(resp.content).decode('utf-8')
|
token = resp.content.decode('utf-8')
|
||||||
|
|
||||||
lookup_kwargs = {
|
lookup_kwargs = {
|
||||||
'headers': {'Authorization': 'Token token="{}"'.format(token)},
|
'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
|
# 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:
|
if version:
|
||||||
path = '?'.join([path, version])
|
ver = "version={}".format(version)
|
||||||
|
path = '?'.join([path, ver])
|
||||||
|
|
||||||
with CertFiles(cacert) as cert:
|
with CertFiles(cacert) as cert:
|
||||||
lookup_kwargs['verify'] = cert
|
lookup_kwargs['verify'] = cert
|
||||||
@@ -90,4 +90,4 @@ def conjur_backend(**kwargs):
|
|||||||
return resp.text
|
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)
|
||||||
|
|||||||
@@ -471,6 +471,29 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn
|
|||||||
def get_absolute_url(self, request=None):
|
def get_absolute_url(self, request=None):
|
||||||
return reverse('api:project_detail', kwargs={'pk': self.pk}, request=request)
|
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
|
RelatedJobsMixin
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
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.notifications.base import AWXBaseEmailBackend
|
||||||
from awx.main.utils import get_awx_http_client_headers
|
from awx.main.utils import get_awx_http_client_headers
|
||||||
from awx.main.notifications.custom_notification_base import CustomNotificationBase
|
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):
|
class WebhookBackend(AWXBaseEmailBackend, CustomNotificationBase):
|
||||||
|
|
||||||
|
MAX_RETRIES = 5
|
||||||
|
|
||||||
init_parameters = {
|
init_parameters = {
|
||||||
"url": {"label": "Target URL", "type": "string"},
|
"url": {"label": "Target URL", "type": "string"},
|
||||||
"http_method": {"label": "HTTP Method", "type": "string", "default": "POST"},
|
"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']:
|
if self.http_method.lower() not in ['put', 'post']:
|
||||||
raise ValueError("HTTP method must be either 'POST' or 'PUT'.")
|
raise ValueError("HTTP method must be either 'POST' or 'PUT'.")
|
||||||
chosen_method = getattr(requests, self.http_method.lower(), None)
|
chosen_method = getattr(requests, self.http_method.lower(), None)
|
||||||
|
|
||||||
for m in messages:
|
for m in messages:
|
||||||
|
|
||||||
auth = None
|
auth = None
|
||||||
if self.username or self.password:
|
if self.username or self.password:
|
||||||
auth = (self.username, self.password)
|
auth = (self.username, self.password)
|
||||||
r = chosen_method(
|
|
||||||
"{}".format(m.recipients()[0]),
|
# 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
|
||||||
auth=auth,
|
# sometimes an empty dict is intentional and we added this conditional to enforce that
|
||||||
data=json.dumps(m.body, ensure_ascii=False).encode('utf-8'),
|
if not m.body:
|
||||||
headers=dict(list(get_awx_http_client_headers().items()) + list((self.headers or {}).items())),
|
m.body = {}
|
||||||
verify=(not self.disable_ssl_verification),
|
|
||||||
)
|
url = str(m.recipients()[0])
|
||||||
if r.status_code >= 400:
|
data = json.dumps(m.body, ensure_ascii=False).encode('utf-8')
|
||||||
logger.error(smart_str(_("Error sending notification webhook: {}").format(r.status_code)))
|
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:
|
if not self.fail_silently:
|
||||||
raise Exception(smart_str(_("Error sending notification webhook: {}").format(r.status_code)))
|
raise Exception(err)
|
||||||
sent_messages += 1
|
|
||||||
|
# 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
|
return sent_messages
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
from django.db.models.signals import pre_save, post_save, pre_delete, m2m_changed
|
from django.db.models.signals import pre_save, post_save, pre_delete, m2m_changed
|
||||||
|
|
||||||
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
|
|
||||||
class ActivityStreamRegistrar(object):
|
class ActivityStreamRegistrar(object):
|
||||||
def __init__(self):
|
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")
|
pre_delete.connect(activity_stream_delete, sender=model, dispatch_uid=str(self.__class__) + str(model) + "_delete")
|
||||||
|
|
||||||
for m2mfield in model._meta.many_to_many:
|
for m2mfield in model._meta.many_to_many:
|
||||||
|
if isinstance(m2mfield, TaggableManager):
|
||||||
|
continue # Special case for taggit app
|
||||||
try:
|
try:
|
||||||
m2m_attr = getattr(model, m2mfield.name)
|
m2m_attr = getattr(model, m2mfield.name)
|
||||||
m2m_changed.connect(
|
m2m_changed.connect(
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import json
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
from collections import deque
|
from collections import deque
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
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)
|
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
|
# We opened a connection just for that save, close it here now
|
||||||
connections.close_all()
|
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':
|
elif status_data['status'] == 'error':
|
||||||
result_traceback = status_data.get('result_traceback', None)
|
result_traceback = status_data.get('result_traceback', None)
|
||||||
if result_traceback:
|
if result_traceback:
|
||||||
|
|||||||
@@ -767,6 +767,10 @@ class SourceControlMixin(BaseTask):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
original_branch = None
|
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)
|
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 project.scm_type == 'git' and (scm_branch and scm_branch != project.scm_branch):
|
||||||
if os.path.exists(project_path):
|
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.')
|
error = _('Job could not start because no Execution Environment could be found.')
|
||||||
self.update_model(job.pk, status='error', job_explanation=error)
|
self.update_model(job.pk, status='error', job_explanation=error)
|
||||||
raise RuntimeError(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':
|
if job.inventory.kind == 'smart':
|
||||||
# cache smart inventory memberships so that the host_filter query is not
|
# cache smart inventory memberships so that the host_filter query is not
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ def test_encrypted_subfields(get, post, user, organization):
|
|||||||
url = reverse('api:notification_template_detail', kwargs={'pk': response.data['id']})
|
url = reverse('api:notification_template_detail', kwargs={'pk': response.data['id']})
|
||||||
response = get(url, u)
|
response = get(url, u)
|
||||||
assert response.data['notification_configuration']['account_token'] == "$encrypted$"
|
assert response.data['notification_configuration']['account_token'] == "$encrypted$"
|
||||||
|
|
||||||
with mock.patch.object(notification_template_actual.notification_class, "send_messages", assert_send):
|
with mock.patch.object(notification_template_actual.notification_class, "send_messages", assert_send):
|
||||||
notification_template_actual.send("Test", {'body': "Test"})
|
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
|
fake_send.side_effect = _send_side_effect
|
||||||
template.send('subject', 'message')
|
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
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ def test_send_messages_as_POST():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
requests_mock.post.assert_called_once_with(
|
requests_mock.post.assert_called_once_with(
|
||||||
'http://example.com',
|
url='http://example.com',
|
||||||
auth=None,
|
auth=None,
|
||||||
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
|
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)'},
|
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
|
||||||
verify=True,
|
verify=True,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
@@ -57,11 +58,12 @@ def test_send_messages_as_PUT():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
requests_mock.put.assert_called_once_with(
|
requests_mock.put.assert_called_once_with(
|
||||||
'http://example.com',
|
url='http://example.com',
|
||||||
auth=None,
|
auth=None,
|
||||||
data=json.dumps({'text': 'test body 2'}, ensure_ascii=False).encode('utf-8'),
|
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)'},
|
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
|
||||||
verify=True,
|
verify=True,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
@@ -87,11 +89,12 @@ def test_send_messages_with_username():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
requests_mock.post.assert_called_once_with(
|
requests_mock.post.assert_called_once_with(
|
||||||
'http://example.com',
|
url='http://example.com',
|
||||||
auth=('userstring', None),
|
auth=('userstring', None),
|
||||||
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
|
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)'},
|
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
|
||||||
verify=True,
|
verify=True,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
@@ -117,11 +120,12 @@ def test_send_messages_with_password():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
requests_mock.post.assert_called_once_with(
|
requests_mock.post.assert_called_once_with(
|
||||||
'http://example.com',
|
url='http://example.com',
|
||||||
auth=(None, 'passwordstring'),
|
auth=(None, 'passwordstring'),
|
||||||
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
|
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)'},
|
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
|
||||||
verify=True,
|
verify=True,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
@@ -147,11 +151,12 @@ def test_send_messages_with_username_and_password():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
requests_mock.post.assert_called_once_with(
|
requests_mock.post.assert_called_once_with(
|
||||||
'http://example.com',
|
url='http://example.com',
|
||||||
auth=('userstring', 'passwordstring'),
|
auth=('userstring', 'passwordstring'),
|
||||||
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
|
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)'},
|
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
|
||||||
verify=True,
|
verify=True,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
@@ -177,11 +182,12 @@ def test_send_messages_with_no_verify_ssl():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
requests_mock.post.assert_called_once_with(
|
requests_mock.post.assert_called_once_with(
|
||||||
'http://example.com',
|
url='http://example.com',
|
||||||
auth=None,
|
auth=None,
|
||||||
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
|
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)'},
|
headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'},
|
||||||
verify=False,
|
verify=False,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
@@ -207,7 +213,7 @@ def test_send_messages_with_additional_headers():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
requests_mock.post.assert_called_once_with(
|
requests_mock.post.assert_called_once_with(
|
||||||
'http://example.com',
|
url='http://example.com',
|
||||||
auth=None,
|
auth=None,
|
||||||
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
|
data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'),
|
||||||
headers={
|
headers={
|
||||||
@@ -217,5 +223,6 @@ def test_send_messages_with_additional_headers():
|
|||||||
'X-Test-Header2': 'test-content-2',
|
'X-Test-Header2': 'test-content-2',
|
||||||
},
|
},
|
||||||
verify=True,
|
verify=True,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|||||||
@@ -254,6 +254,14 @@ START_TASK_LIMIT = 100
|
|||||||
TASK_MANAGER_TIMEOUT = 300
|
TASK_MANAGER_TIMEOUT = 300
|
||||||
TASK_MANAGER_TIMEOUT_GRACE_PERIOD = 60
|
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
|
# Disallow sending session cookies over insecure connections
|
||||||
SESSION_COOKIE_SECURE = True
|
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
|
# Mount exposed paths as hostPath resource in k8s/ocp
|
||||||
AWX_MOUNT_ISOLATED_PATHS_ON_K8S = False
|
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
|
# This is overridden downstream via /etc/tower/conf.d/cluster_host_id.py
|
||||||
CLUSTER_HOST_ID = socket.gethostname()
|
CLUSTER_HOST_ID = socket.gethostname()
|
||||||
|
|||||||
86
awx/ui/package-lock.json
generated
86
awx/ui/package-lock.json
generated
@@ -7,9 +7,9 @@
|
|||||||
"name": "ui",
|
"name": "ui",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lingui/react": "3.14.0",
|
"@lingui/react": "3.14.0",
|
||||||
"@patternfly/patternfly": "4.210.2",
|
"@patternfly/patternfly": "4.217.1",
|
||||||
"@patternfly/react-core": "^4.239.0",
|
"@patternfly/react-core": "^4.250.1",
|
||||||
"@patternfly/react-icons": "4.90.0",
|
"@patternfly/react-icons": "4.92.10",
|
||||||
"@patternfly/react-table": "4.108.0",
|
"@patternfly/react-table": "4.108.0",
|
||||||
"ace-builds": "^1.10.1",
|
"ace-builds": "^1.10.1",
|
||||||
"ansi-to-html": "0.7.2",
|
"ansi-to-html": "0.7.2",
|
||||||
@@ -3747,26 +3747,26 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/patternfly": {
|
"node_modules/@patternfly/patternfly": {
|
||||||
"version": "4.210.2",
|
"version": "4.217.1",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.210.2.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.217.1.tgz",
|
||||||
"integrity": "sha512-aZiW24Bxi6uVmk5RyNTp+6q6ThtlJZotNRJfWVeGuwu1UlbBuV4DFa1bpjA6jfTZpfEpX2YL5+R+4ZVSCFAVdw=="
|
"integrity": "sha512-uN7JgfQsyR16YHkuGRCTIcBcnyKIqKjGkB2SGk9x1XXH3yYGenL83kpAavX9Xtozqp17KppOlybJuzcKvZMrgw=="
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/react-core": {
|
"node_modules/@patternfly/react-core": {
|
||||||
"version": "4.239.0",
|
"version": "4.250.1",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.239.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.250.1.tgz",
|
||||||
"integrity": "sha512-6CmYABCJLUXTlzCk6C3WouMNZpS0BCT+aHU8CvYpFQ/NrpYp3MJaDsYbqgCRWV42rmIO5iXun/4WhXBJzJEoQg==",
|
"integrity": "sha512-vAOZPQdZzYXl/vkHnHMIt1eC3nrPDdsuuErPatkNPwmSvilXuXmWP5wxoJ36FbSNRRURkprFwx52zMmWS3iHJA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@patternfly/react-icons": "^4.90.0",
|
"@patternfly/react-icons": "^4.92.6",
|
||||||
"@patternfly/react-styles": "^4.89.0",
|
"@patternfly/react-styles": "^4.91.6",
|
||||||
"@patternfly/react-tokens": "^4.91.0",
|
"@patternfly/react-tokens": "^4.93.6",
|
||||||
"focus-trap": "6.9.2",
|
"focus-trap": "6.9.2",
|
||||||
"react-dropzone": "9.0.0",
|
"react-dropzone": "9.0.0",
|
||||||
"tippy.js": "5.1.2",
|
"tippy.js": "5.1.2",
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17.0.0",
|
"react": "^16.8 || ^17 || ^18",
|
||||||
"react-dom": "^16.8.0 || ^17.0.0"
|
"react-dom": "^16.8 || ^17 || ^18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/react-core/node_modules/tslib": {
|
"node_modules/@patternfly/react-core/node_modules/tslib": {
|
||||||
@@ -3775,18 +3775,18 @@
|
|||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/react-icons": {
|
"node_modules/@patternfly/react-icons": {
|
||||||
"version": "4.90.0",
|
"version": "4.92.10",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.90.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.92.10.tgz",
|
||||||
"integrity": "sha512-qEnQKbxbUgyiosiKSkeKEBwmhgJwWEqniIAFyoxj+kpzAdeu7ueWe5iBbqo06mvDOedecFiM5mIE1N0MXwk8Yw==",
|
"integrity": "sha512-vwCy7b+OyyuvLDSLqLUG2DkJZgMDogjld8tJTdAaG8HiEhC1sJPZac+5wD7AuS3ym/sQolS4vYtNiVDnMEORxA==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17.0.0",
|
"react": "^16.8 || ^17 || ^18",
|
||||||
"react-dom": "^16.8.0 || ^17.0.0"
|
"react-dom": "^16.8 || ^17 || ^18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/react-styles": {
|
"node_modules/@patternfly/react-styles": {
|
||||||
"version": "4.89.0",
|
"version": "4.91.10",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.89.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.91.10.tgz",
|
||||||
"integrity": "sha512-SkT+qx3Xqu70T5s+i/AUT2hI2sKAPDX4ffeiJIUDu/oyWiFdk+/9DEivnLSyJMruroXXN33zKibvzb5rH7DKTQ=="
|
"integrity": "sha512-fAG4Vjp63ohiR92F4e/Gkw5q1DSSckHKqdnEF75KUpSSBORzYP0EKMpupSd6ItpQFJw3iWs3MJi3/KIAAfU1Jw=="
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/react-table": {
|
"node_modules/@patternfly/react-table": {
|
||||||
"version": "4.108.0",
|
"version": "4.108.0",
|
||||||
@@ -3811,9 +3811,9 @@
|
|||||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/react-tokens": {
|
"node_modules/@patternfly/react-tokens": {
|
||||||
"version": "4.91.0",
|
"version": "4.93.10",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.91.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.93.10.tgz",
|
||||||
"integrity": "sha512-QeQCy8o8E/16fAr8mxqXIYRmpTsjCHJXi5p5jmgEDFmYMesN6Pqfv6N5D0FHb+CIaNOZWRps7GkWvlIMIE81sw=="
|
"integrity": "sha512-F+j1irDc9M6zvY6qNtDryhbpnHz3R8ymHRdGelNHQzPTIK88YSWEnT1c9iUI+uM/iuZol7sJmO5STtg2aPIDRQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@pmmmwh/react-refresh-webpack-plugin": {
|
"node_modules/@pmmmwh/react-refresh-webpack-plugin": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
@@ -25089,18 +25089,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@patternfly/patternfly": {
|
"@patternfly/patternfly": {
|
||||||
"version": "4.210.2",
|
"version": "4.217.1",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.210.2.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.217.1.tgz",
|
||||||
"integrity": "sha512-aZiW24Bxi6uVmk5RyNTp+6q6ThtlJZotNRJfWVeGuwu1UlbBuV4DFa1bpjA6jfTZpfEpX2YL5+R+4ZVSCFAVdw=="
|
"integrity": "sha512-uN7JgfQsyR16YHkuGRCTIcBcnyKIqKjGkB2SGk9x1XXH3yYGenL83kpAavX9Xtozqp17KppOlybJuzcKvZMrgw=="
|
||||||
},
|
},
|
||||||
"@patternfly/react-core": {
|
"@patternfly/react-core": {
|
||||||
"version": "4.239.0",
|
"version": "4.250.1",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.239.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.250.1.tgz",
|
||||||
"integrity": "sha512-6CmYABCJLUXTlzCk6C3WouMNZpS0BCT+aHU8CvYpFQ/NrpYp3MJaDsYbqgCRWV42rmIO5iXun/4WhXBJzJEoQg==",
|
"integrity": "sha512-vAOZPQdZzYXl/vkHnHMIt1eC3nrPDdsuuErPatkNPwmSvilXuXmWP5wxoJ36FbSNRRURkprFwx52zMmWS3iHJA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@patternfly/react-icons": "^4.90.0",
|
"@patternfly/react-icons": "^4.92.6",
|
||||||
"@patternfly/react-styles": "^4.89.0",
|
"@patternfly/react-styles": "^4.91.6",
|
||||||
"@patternfly/react-tokens": "^4.91.0",
|
"@patternfly/react-tokens": "^4.93.6",
|
||||||
"focus-trap": "6.9.2",
|
"focus-trap": "6.9.2",
|
||||||
"react-dropzone": "9.0.0",
|
"react-dropzone": "9.0.0",
|
||||||
"tippy.js": "5.1.2",
|
"tippy.js": "5.1.2",
|
||||||
@@ -25115,15 +25115,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@patternfly/react-icons": {
|
"@patternfly/react-icons": {
|
||||||
"version": "4.90.0",
|
"version": "4.92.10",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.90.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.92.10.tgz",
|
||||||
"integrity": "sha512-qEnQKbxbUgyiosiKSkeKEBwmhgJwWEqniIAFyoxj+kpzAdeu7ueWe5iBbqo06mvDOedecFiM5mIE1N0MXwk8Yw==",
|
"integrity": "sha512-vwCy7b+OyyuvLDSLqLUG2DkJZgMDogjld8tJTdAaG8HiEhC1sJPZac+5wD7AuS3ym/sQolS4vYtNiVDnMEORxA==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@patternfly/react-styles": {
|
"@patternfly/react-styles": {
|
||||||
"version": "4.89.0",
|
"version": "4.91.10",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.89.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.91.10.tgz",
|
||||||
"integrity": "sha512-SkT+qx3Xqu70T5s+i/AUT2hI2sKAPDX4ffeiJIUDu/oyWiFdk+/9DEivnLSyJMruroXXN33zKibvzb5rH7DKTQ=="
|
"integrity": "sha512-fAG4Vjp63ohiR92F4e/Gkw5q1DSSckHKqdnEF75KUpSSBORzYP0EKMpupSd6ItpQFJw3iWs3MJi3/KIAAfU1Jw=="
|
||||||
},
|
},
|
||||||
"@patternfly/react-table": {
|
"@patternfly/react-table": {
|
||||||
"version": "4.108.0",
|
"version": "4.108.0",
|
||||||
@@ -25146,9 +25146,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@patternfly/react-tokens": {
|
"@patternfly/react-tokens": {
|
||||||
"version": "4.91.0",
|
"version": "4.93.10",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.91.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.93.10.tgz",
|
||||||
"integrity": "sha512-QeQCy8o8E/16fAr8mxqXIYRmpTsjCHJXi5p5jmgEDFmYMesN6Pqfv6N5D0FHb+CIaNOZWRps7GkWvlIMIE81sw=="
|
"integrity": "sha512-F+j1irDc9M6zvY6qNtDryhbpnHz3R8ymHRdGelNHQzPTIK88YSWEnT1c9iUI+uM/iuZol7sJmO5STtg2aPIDRQ=="
|
||||||
},
|
},
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": {
|
"@pmmmwh/react-refresh-webpack-plugin": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lingui/react": "3.14.0",
|
"@lingui/react": "3.14.0",
|
||||||
"@patternfly/patternfly": "4.210.2",
|
"@patternfly/patternfly": "4.217.1",
|
||||||
"@patternfly/react-core": "^4.239.0",
|
"@patternfly/react-core": "^4.250.1",
|
||||||
"@patternfly/react-icons": "4.90.0",
|
"@patternfly/react-icons": "4.92.10",
|
||||||
"@patternfly/react-table": "4.108.0",
|
"@patternfly/react-table": "4.108.0",
|
||||||
"ace-builds": "^1.10.1",
|
"ace-builds": "^1.10.1",
|
||||||
"ansi-to-html": "0.7.2",
|
"ansi-to-html": "0.7.2",
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ const mockInputSources = {
|
|||||||
summary_fields: {
|
summary_fields: {
|
||||||
source_credential: {
|
source_credential: {
|
||||||
id: 20,
|
id: 20,
|
||||||
name: 'CyberArk Conjur Secret Lookup',
|
name: 'CyberArk Conjur Secrets Manager Lookup',
|
||||||
description: '',
|
description: '',
|
||||||
kind: 'conjur',
|
kind: 'conjur',
|
||||||
cloud: false,
|
cloud: false,
|
||||||
@@ -301,7 +301,7 @@ const mockInputSources = {
|
|||||||
summary_fields: {
|
summary_fields: {
|
||||||
source_credential: {
|
source_credential: {
|
||||||
id: 20,
|
id: 20,
|
||||||
name: 'CyberArk Conjur Secret Lookup',
|
name: 'CyberArk Conjur Secrets Manager Lookup',
|
||||||
description: '',
|
description: '',
|
||||||
kind: 'conjur',
|
kind: 'conjur',
|
||||||
cloud: false,
|
cloud: false,
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ const mockCredentialTypeDetail = {
|
|||||||
url: '/api/v2/credential_types/20/',
|
url: '/api/v2/credential_types/20/',
|
||||||
related: {
|
related: {
|
||||||
named_url:
|
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/',
|
credentials: '/api/v2/credential_types/20/credentials/',
|
||||||
activity_stream: '/api/v2/credential_types/20/activity_stream/',
|
activity_stream: '/api/v2/credential_types/20/activity_stream/',
|
||||||
},
|
},
|
||||||
summary_fields: { user_capabilities: { edit: false, delete: false } },
|
summary_fields: { user_capabilities: { edit: false, delete: false } },
|
||||||
created: '2020-05-18T21:53:35.398260Z',
|
created: '2020-05-18T21:53:35.398260Z',
|
||||||
modified: '2020-05-18T21:54:05.451444Z',
|
modified: '2020-05-18T21:54:05.451444Z',
|
||||||
name: 'CyberArk Conjur Secret Lookup',
|
name: 'CyberArk Conjur Secrets Manager Lookup',
|
||||||
description: '',
|
description: '',
|
||||||
kind: 'external',
|
kind: 'external',
|
||||||
namespace: 'conjur',
|
namespace: 'conjur',
|
||||||
|
|||||||
@@ -465,7 +465,7 @@
|
|||||||
},
|
},
|
||||||
"created": "2020-05-18T21:53:35.370730Z",
|
"created": "2020-05-18T21:53:35.370730Z",
|
||||||
"modified": "2020-05-18T21:54:05.436400Z",
|
"modified": "2020-05-18T21:54:05.436400Z",
|
||||||
"name": "CyberArk AIM Central Credential Provider Lookup",
|
"name": "CyberArk Central Credential Provider Lookup",
|
||||||
"description": "",
|
"description": "",
|
||||||
"kind": "external",
|
"kind": "external",
|
||||||
"namespace": "aim",
|
"namespace": "aim",
|
||||||
@@ -546,7 +546,7 @@
|
|||||||
},
|
},
|
||||||
"created": "2020-05-18T21:53:35.398260Z",
|
"created": "2020-05-18T21:53:35.398260Z",
|
||||||
"modified": "2020-05-18T21:54:05.451444Z",
|
"modified": "2020-05-18T21:54:05.451444Z",
|
||||||
"name": "CyberArk Conjur Secret Lookup",
|
"name": "CyberArk Conjur Secrets Manager Lookup",
|
||||||
"description": "",
|
"description": "",
|
||||||
"kind": "external",
|
"kind": "external",
|
||||||
"namespace": "conjur",
|
"namespace": "conjur",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"type": "credential",
|
"type": "credential",
|
||||||
"url": "/api/v2/credentials/1/",
|
"url": "/api/v2/credentials/1/",
|
||||||
"related": {
|
"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/",
|
"created_by": "/api/v2/users/1/",
|
||||||
"modified_by": "/api/v2/users/1/",
|
"modified_by": "/api/v2/users/1/",
|
||||||
"activity_stream": "/api/v2/credentials/1/activity_stream/",
|
"activity_stream": "/api/v2/credentials/1/activity_stream/",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"summary_fields": {
|
"summary_fields": {
|
||||||
"credential_type": {
|
"credential_type": {
|
||||||
"id": 20,
|
"id": 20,
|
||||||
"name": "CyberArk Conjur Secret Lookup",
|
"name": "CyberArk Conjur Secrets Manager Lookup",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"created_by": {
|
"created_by": {
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
},
|
},
|
||||||
"created": "2020-05-19T12:51:36.956029Z",
|
"created": "2020-05-19T12:51:36.956029Z",
|
||||||
"modified": "2020-05-19T12:51:36.956086Z",
|
"modified": "2020-05-19T12:51:36.956086Z",
|
||||||
"name": "CyberArk Conjur Secret Lookup",
|
"name": "CyberArk Conjur Secrets Manager Lookup",
|
||||||
"description": "",
|
"description": "",
|
||||||
"organization": null,
|
"organization": null,
|
||||||
"credential_type": 20,
|
"credential_type": 20,
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ options:
|
|||||||
- The credential type being created.
|
- 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"
|
- 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,
|
- 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,
|
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
|
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,
|
Bearer Token, OpenStack, Red Hat Ansible Automation Platform, Red Hat Satellite 6, Red Hat Virtualization, Source Control,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
credential:
|
credential:
|
||||||
description: Credential for Testing Source
|
description: Credential for Testing Source
|
||||||
name: "{{ src_cred_name }}"
|
name: "{{ src_cred_name }}"
|
||||||
credential_type: CyberArk AIM Central Credential Provider Lookup
|
credential_type: CyberArk Central Credential Provider Lookup
|
||||||
inputs:
|
inputs:
|
||||||
url: "https://cyberark.example.com"
|
url: "https://cyberark.example.com"
|
||||||
app_id: "My-App-ID"
|
app_id: "My-App-ID"
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
credential:
|
credential:
|
||||||
description: Credential for Testing Source Change
|
description: Credential for Testing Source Change
|
||||||
name: "{{ src_cred_name }}-2"
|
name: "{{ src_cred_name }}-2"
|
||||||
credential_type: CyberArk AIM Central Credential Provider Lookup
|
credential_type: CyberArk Central Credential Provider Lookup
|
||||||
inputs:
|
inputs:
|
||||||
url: "https://cyberark-prod.example.com"
|
url: "https://cyberark-prod.example.com"
|
||||||
app_id: "My-App-ID"
|
app_id: "My-App-ID"
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
credential:
|
credential:
|
||||||
name: "{{ src_cred_name }}"
|
name: "{{ src_cred_name }}"
|
||||||
organization: Default
|
organization: Default
|
||||||
credential_type: CyberArk AIM Central Credential Provider Lookup
|
credential_type: CyberArk Central Credential Provider Lookup
|
||||||
state: absent
|
state: absent
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
credential:
|
credential:
|
||||||
name: "{{ src_cred_name }}-2"
|
name: "{{ src_cred_name }}-2"
|
||||||
organization: Default
|
organization: Default
|
||||||
credential_type: CyberArk AIM Central Credential Provider Lookup
|
credential_type: CyberArk Central Credential Provider Lookup
|
||||||
state: absent
|
state: absent
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Here are the main `make` targets:
|
|||||||
Notable files:
|
Notable files:
|
||||||
|
|
||||||
- `tools/docker-compose/inventory` file - used to configure the AWX development environment.
|
- `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
|
### Prerequisites
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user