Skip to content

Commit

Permalink
Merge pull request ansible#9683 from rebeccahhh/devel
Browse files Browse the repository at this point in the history
Pull with credentials from protected registries

SUMMARY

relates to ansible#7066
if a credential is associated with an EE this will create a JSON authfile that is then passed with the pull request to the host of the registry

ISSUE TYPE


Feature Pull Request

COMPONENT NAME


API

AWX VERSION

awx: 18.0.0


TODOs

 Remove separate token field from the registry credential.
 Rename the existing password field to say "password/token"
 Ensure only registry credentials can be associated with an EE ansible#9628
 Write out the auth.json file to the pdd_wrapper_ directory. ansible#9683 (comment)
 Use secure permissions for auth.json ansible#9683 (comment)

Reviewed-by: Ryan Petrello <None>
Reviewed-by: Shane McDonald <[email protected]>
Reviewed-by: Rebeccah Hunter <[email protected]>
Reviewed-by: Elijah DeLee <[email protected]>
Reviewed-by: Alan Rominger <[email protected]>
Reviewed-by: Bianca Henderson <[email protected]>
Reviewed-by: Nana  <[email protected]>
  • Loading branch information
softwarefactory-project-zuul[bot] authored Apr 13, 2021
2 parents b823181 + 0d2ab5f commit 6dd5fc9
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 15 deletions.
5 changes: 5 additions & 0 deletions awx/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,11 @@ def get_related(self, obj):
res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential.pk})
return res

def validate_credential(self, value):
if value and value.kind != 'registry':
raise serializers.ValidationError(_('Only Container Registry credentials can be associated with an Execution Environment'))
return value

def validate(self, attrs):
# prevent changing organization of ee. Unsetting (change to null) is allowed
if self.instance:
Expand Down
19 changes: 11 additions & 8 deletions awx/main/models/credential/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,15 @@ def has_input(self, field_name):
return True
return field_name in self.inputs and self.inputs[field_name] not in ('', None)

def has_inputs(self, field_names=()):
for name in field_names:
if name in self.inputs:
if self.inputs[name] in ('', None):
return False
else:
raise ValueError('{} is not an input field'.format(name))
return True

def _get_dynamic_input(self, field_name):
for input_source in self.input_sources.all():
if input_source.input_field_name == field_name:
Expand Down Expand Up @@ -1097,16 +1106,10 @@ def create(self):
},
{
'id': 'password',
'label': ugettext_noop('Password'),
'type': 'string',
'secret': True,
},
{
'id': 'token',
'label': ugettext_noop('Access Token'),
'label': ugettext_noop('Password or Token'),
'type': 'string',
'secret': True,
'help_text': ugettext_noop('A token to use to authenticate with. ' 'This should not be set if username/password are being used.'),
'help_text': ugettext_noop('A password or token used to authenticate with'),
},
],
'required': ['host'],
Expand Down
31 changes: 24 additions & 7 deletions awx/main/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ def get_path_to(self, *args):
"""
return os.path.abspath(os.path.join(os.path.dirname(__file__), *args))

def build_execution_environment_params(self, instance):
def build_execution_environment_params(self, instance, private_data_dir):
if settings.IS_K8S:
return {}

Expand All @@ -851,6 +851,23 @@ def build_execution_environment_params(self, instance):
"container_options": ['--user=root'],
}

if instance.execution_environment.credential:
cred = instance.execution_environment.credential
if cred.has_inputs(field_names=('host', 'username', 'password')):
path = os.path.split(private_data_dir)[0]
with open(path + '/auth.json', 'w') as authfile:
host = cred.get_input('host')
username = cred.get_input('username')
password = cred.get_input('password')
token = "{}:{}".format(username, password)
auth_data = {'auths': {host: {'auth': b64encode(token.encode('ascii')).decode()}}}
authfile.write(json.dumps(auth_data, indent=4))
authfile.close()
os.chmod(authfile.name, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
params["container_options"].append(f'--authfile={authfile.name}')
else:
raise RuntimeError('Please recheck that your host, username, and password fields are all filled.')

pull = instance.execution_environment.pull
if pull:
params['container_options'].append(f'--pull={pull}')
Expand Down Expand Up @@ -1709,11 +1726,11 @@ def should_use_resource_profiling(self, job):
"""
return settings.AWX_RESOURCE_PROFILING_ENABLED

def build_execution_environment_params(self, instance):
def build_execution_environment_params(self, instance, private_data_dir):
if settings.IS_K8S:
return {}

params = super(RunJob, self).build_execution_environment_params(instance)
params = super(RunJob, self).build_execution_environment_params(instance, private_data_dir)
# If this has an insights agent and it is not already mounted then show it
insights_dir = os.path.dirname(settings.INSIGHTS_SYSTEM_ID_FILE)
if instance.use_fact_cache and os.path.exists(insights_dir):
Expand Down Expand Up @@ -2324,11 +2341,11 @@ def post_run_hook(self, instance, status):
if status == 'successful' and instance.launch_type != 'sync':
self._update_dependent_inventories(instance, dependent_inventory_sources)

def build_execution_environment_params(self, instance):
def build_execution_environment_params(self, instance, private_data_dir):
if settings.IS_K8S:
return {}

params = super(RunProjectUpdate, self).build_execution_environment_params(instance)
params = super(RunProjectUpdate, self).build_execution_environment_params(instance, private_data_dir)
project_path = instance.get_project_path(check_if_exists=False)
cache_path = instance.get_cache_path()
params.setdefault('container_volume_mounts', [])
Expand Down Expand Up @@ -2831,7 +2848,7 @@ class RunSystemJob(BaseTask):
event_model = SystemJobEvent
event_data_key = 'system_job_id'

def build_execution_environment_params(self, system_job):
def build_execution_environment_params(self, system_job, private_data_dir):
return {}

def build_args(self, system_job, private_data_dir, passwords):
Expand Down Expand Up @@ -2947,7 +2964,7 @@ def __init__(self, task=None, runner_params=None):
self.unit_id = None

if self.task and not self.task.instance.is_container_group_task:
execution_environment_params = self.task.build_execution_environment_params(self.task.instance)
execution_environment_params = self.task.build_execution_environment_params(self.task.instance, runner_params['private_data_dir'])
self.runner_params['settings'].update(execution_environment_params)

def run(self):
Expand Down

0 comments on commit 6dd5fc9

Please sign in to comment.