Skip to content
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

RFC: allow for environment driven env settings #541

Open
wants to merge 1 commit into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 71 additions & 9 deletions ansible_runner/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,24 +162,86 @@ def load_file(self, path, objtype=None, encoding='utf-8'):

try:
debug('cache miss, attempting to load file from disk: %s' % path)
contents = parsed_data = self.get_contents(path)
if encoding:
parsed_data = contents.encode(encoding)
contents = self.get_contents(path)
except ConfigurationError as exc:
debug(exc)
raise
except UnicodeEncodeError:
raise ConfigurationError('unable to encode file contents')

return self.deserialize(contents, objtype, encoding, key=path)


def load_env(self, key, objtype=None, encoding='utf-8'):
'''
Load the object specified by env key

This method will try to load object contents from environment

Args:
key (string): the key that contains the serialized data

encoding (string): The file contents text encoding

objtype (object): The object type of the file contents. This
is used to type check the deserialized content against the
contents loaded from disk.
Ignore serializing if objtype is string_types

Returns:
object: The deserialized file contents which could be either a
string object or a dict object

Raises:
ConfigurationError:
'''
try:
debug('cache miss, attempting to key from env %s' % key)
contents = os.environ[key]
except KeyError as exc:
debug(exc)
raise ConfigurationError('key %s is not in environment' % key)

return self.deserialize(contents, objtype, encoding)


def deserialize(self, data, objtype=None, encoding='utf-8', key=None):
'''
deserialize and type check data

Args:
data (string): The possibly encoded data to be deserialized

key (string): if specified, a key to use to memoize/cache deserialized contents

encoding (string): The data text encoding

objtype (object): The object type of the deserialized data. This
is used to type check the deserialized content is as expected.
Ignore serializing if objtype is string_types

Returns:
object: The deserialized data which could be either a
string object or a dict object

Raises:
ConfigurationError:
'''
parsed_data = data
if encoding:
try:
parsed_data = data.encode(encoding)
except UnicodeEncodeError:
raise ConfigurationError('unable to encode file contents')

if objtype is not string_types:
for deserializer in (self._load_json, self._load_yaml):
parsed_data = deserializer(contents)
parsed_data = deserializer(data)
if parsed_data:
break

if objtype and not isinstance(parsed_data, objtype):
debug('specified file %s is not of type %s' % (path, objtype))
raise ConfigurationError('invalid file serialization type for contents')
debug('specified data %s is not of type %s' % (data, objtype))
raise ConfigurationError('invalid file serialization type for data')

self._cache[path] = parsed_data
if key:
self._cache[key] = parsed_data
return parsed_data
20 changes: 16 additions & 4 deletions ansible_runner/runner_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,10 @@ def prepare_env(self):
with existing values so the :py:class:`ansible_runner.runner.Runner` object can read and use them easily
"""
try:
passwords = self.loader.load_file('env/passwords', Mapping)
if 'ANSIBLE_RUNNER_PASSWORDS' in os.environ:
passwords = self.loader.load_env('ANSIBLE_RUNNER_PASSWORDS', Mapping)
else:
passwords = self.loader.load_file('env/passwords', Mapping)
self.expect_passwords = {
re.compile(pattern, re.M): password
for pattern, password in iteritems(passwords)
Expand All @@ -318,7 +321,10 @@ def prepare_env(self):
self.expect_passwords[pexpect.EOF] = None

try:
self.settings = self.loader.load_file('env/settings', Mapping)
if 'ANSIBLE_RUNNER_SETTINGS' in os.environ:
self.settings = self.loader.load_env('ANSIBLE_RUNNER_SETTINGS', Mapping)
else:
self.settings = self.loader.load_file('env/settings', Mapping)
except ConfigurationError:
output.debug("Not loading settings")
self.settings = dict()
Expand All @@ -343,7 +349,10 @@ def prepare_env(self):
self.env.update(self.envvars)

try:
envvars = self.loader.load_file('env/envvars', Mapping)
if 'ANSIBLE_RUNNER_ENVVARS' in os.environ:
envvars = self.loader.load_env('ANSIBLE_RUNNER_ENVVARS', Mapping)
else:
envvars = self.loader.load_file('env/envvars', Mapping)
if envvars:
self.env.update({str(k):str(v) for k, v in envvars.items()})
except ConfigurationError:
Expand All @@ -352,7 +361,10 @@ def prepare_env(self):

try:
if self.ssh_key_data is None:
self.ssh_key_data = self.loader.load_file('env/ssh_key', string_types)
if 'ANSIBLE_RUNNER_SSH_KEY' in os.environ:
self.ssh_key_data = self.loader.load_env('ANSIBLE_RUNNER_SSH_KEY', Mapping)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be preferable to cut right to the Ansible settings?

https://github.com/ansible/ansible/blob/devel/lib/ansible/config/base.yml

You could pass ANSIBLE_PRIVATE_KEY_FILE, and ansible-runner should pass that to the subprocess, and then Ansible should respect it for the same function as this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I didn't know that existed actually, yes for ssh key it would indeed make sense. we could either duplicate it for parity with other ANSIBLE_RUNNER_* or just remove it in lieu of using ANSIBLE_PRIVATE_KEY_FILE. my main motivation is really avoiding the passwords file actually :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually looking a bit closer, ANSIBLE_PRIVATE_KEY_FILE is a path to a key, not the privkey contents itself, so a bit different.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlanCoding any further thoughts on this functionality?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @mattp-. Alan is out for a couple more weeks after his wife gave birth. Hang tight and he'll get to this eventually.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah had no idea. congratulations :) and no rush, thanks.

else:
self.ssh_key_data = self.loader.load_file('env/ssh_key', string_types)
except ConfigurationError:
output.debug("Not loading ssh key")
self.ssh_key_data = None
Expand Down