-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable support for injecting complex extra vars #13267
Enable support for injecting complex extra vars #13267
Conversation
I'm opening this PR to generate some discussion -- I'll update with tests and documentation if this passes muster. |
Let's get your example documented in |
I'll document the major part of this PR, but not the ability to template the key (yet) as that needs some discussion. |
2f7a3d0
to
9629119
Compare
This is awesome 😄 Before we merge this we'll need to get some tests added. Check out |
9629119
to
c00c6dc
Compare
Added two tests for nested and templated extra vars keys, but still TBD: Updating the jsonschema for the |
@philipsd6 if you are all set with your changes can you convert this from a draft PR to an opened PR? |
I don't think it's ready yet -- I'm struggling with the jsonschema that will allow recursively nested injector vars. Currently in my test env, I'm still getting this error when trying to create a nested extra_vars injector:
|
d243279
to
34ea161
Compare
I updated
|
My bad, the jsonschema update worked fine, it was the validator that was failing on the nested part. Fixed with latest commit. |
b80a1b5
to
26f55f3
Compare
26f55f3
to
628ab81
Compare
awx/main/fields.py
Outdated
@@ -790,13 +790,11 @@ def schema(self, model_instance): | |||
'extra_vars': { | |||
'type': 'object', | |||
'patternProperties': { | |||
# http://docs.ansible.com/ansible/playbooks_variables.html#what-makes-a-valid-variable-name | |||
'^[a-zA-Z_]+[a-zA-Z0-9_]*$': {'type': 'string'}, | |||
r'^(?:(?:{(?:{|%)[^{}]*?(?:%|})})|(?:[a-zA-Z_]+[a-zA-Z0-9_]*)+)+$': {"anyOf": [{'type': 'string'}, {'$ref': '#/properties/extra_vars'}]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This regex is ridiculous, but the only alternative I can see is to just make it r'^.+$'
raise django_exceptions.ValidationError(_('Encountered unsafe code execution: {}').format(e)) | ||
except TemplateSyntaxError as e: | ||
raise django_exceptions.ValidationError( | ||
_('Syntax error rendering template for {sub_key} inside of {type} ({error_msg})').format(sub_key=key, type=type_, error_msg=e), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting a server error due to the fact that key
is undefined here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, valid -- and not only that, so is type_
. I'll have to revise a little deeper after closer inspection. Thanks for the review.
awx/main/fields.py
Outdated
|
||
def validate_extra_vars(key, node): | ||
if isinstance(node, dict): | ||
return {validate_extra_vars(key, k): validate_extra_vars(k, v) for k, v in node.items()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We really don't want to lose context in error messages. The key
passed in should always be a string right? So what if you combined k
and key
for the value here, so instead of validate_extra_vars(k, v)
do validate_extra_vars(f'{key}:{k}', v)
, or whatever you'd prefer for delimiter. That would keep stacking layers of context on a rolling basis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used a '.' (dot) as the dict key delimiter, seemed appropriate for this.
awx/main/fields.py
Outdated
if isinstance(node, dict): | ||
return {validate_extra_vars(key, k): validate_extra_vars(k, v) for k, v in node.items()} | ||
elif isinstance(node, list): | ||
return [validate_extra_vars(key, x) for x in node] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to change for x in node
to for i, x in enumerate(node)
and then stack onto key
, like f'{key}:{i}
. It doesn't distinguish between dicts and lists... but it shouldn't matter because the user know what injectors they submitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the context is fully maintained now in the validation error messages. I used key[i][i]
style for the validation error message, as between the dict dot style and this, it should be pseudo jq/jmespath style.
@AlanCoding: running podman exec -it awx-tools_awx_1 make test_unit
works passes the new unit tests, But I'm not sure how to actually test those validation error paths.
454ed23
to
35433de
Compare
891fe8d
to
3c51d01
Compare
params={'value': value}, | ||
) | ||
if type_ == 'extra_vars': | ||
validate_extra_vars(None, injector) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This None
is coming through in the error message with a grammar that's not really desirable. I've actually been using the docs as an error case, because the existing example is badly formatted. That does need to be changed... but the value is:
"injectors": {
"env": {
"THIRD_PARTY_CLOUD_API_TOKEN": "{{api_token}}"
},
"extra_vars": {
"some_extra_var": "{{username}}:{{password}",
"auth": {
"username": "{{username}}",
"password": "{{password}}"
}
}
}
A bit here is missing a closing }
. The currently produced error message is:
{'injectors': ["Syntax error rendering template for None.auth.username inside of extra_vars (unexpected '}')"]}
You know, in this line, injector
must be a dictionary. This is fixable, but I think it will take an extra line or two of string formatting inside of validate_extra_vars
.
I am going to write down a few validation test cases and save them for later.
3c51d01
to
4073d0e
Compare
4073d0e
to
7f6f57b
Compare
I poked at this some more and put up philipsd6#571, which is mainly concerned with error messages. |
SUMMARY
Until now, it's only been possible to inject flat strings with Jinja templating into extra vars from Custom Credential Types. However, it's quite desirable to be able to pass a data structure containing authentication information for certain collections/modules. For example, the Infoblox collection accepts authentication under the
nios_provider
key.This PR enables that data structure to be created by an injector config.
related #1965 #13070
ISSUE TYPE
COMPONENT NAME
AWX VERSION
ADDITIONAL INFORMATION
This PR also makes it possible to template the keys of an injector configuration, as requested in #13070.
For example, given an input configuration providing values for
environment
,host
,apikey
,username
, it's possible to use an extra_vars injector configuration of:resulting in the following extra var available to the playbooks:
This would lay the groundwork for allowing multiple credentials of the same credential type, as another credential might supply values for a different environment.