Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

WIP Initial support for parametrization of descriptor files #116

Open
wants to merge 3 commits into
base: master
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
14 changes: 14 additions & 0 deletions dogen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def run(self):
parser.add_argument('--skip-ssl-verification', action='store_true', help='Should we skip SSL verification when retrieving data?')
parser.add_argument('--scripts-path', help='Location of the scripts directory containing script packages.')
parser.add_argument('--additional-script', action='append', help='Location of additional script (can be url). Can be specified multiple times.')
parser.add_argument('--param', action='append', dest='params', help='List of key and value pairs (format: KEY=value) used for substitution in the image descriptor. Can be specified multiple times.')
parser.add_argument('--template', help='Path to custom template (can be url)')

parser.add_argument('path', help="Path to yaml descriptor to process")
Expand All @@ -69,6 +70,19 @@ def run(self):
parser.epilog = epilog
args = parser.parse_args()

params = {}

if args.params:
for param in args.params:
if '=' not in param:
self.log.error("The --param argument with value '%s' could not be parsed. Please make sure you use the 'KEY=value' format" % param)
sys.exit(1)

k, v = param.split("=", 1)
params[k] = v

args.params = params
Copy link
Contributor

Choose a reason for hiding this comment

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

this is very hacky - you should not mess with args object and store your values here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right. I guess the problem is that argparse doesn't support a dict directly. I could leave validation and just pass what I've got via CLI, but I guess I wanted to have the validation as close as possible to where argparse does it. I'm open for suggestions.


if args.verbose:
self.log.setLevel(logging.DEBUG)
else:
Expand Down
14 changes: 13 additions & 1 deletion dogen/generator.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-

import hashlib
import re
import os
import shutil
import six
import requests
import yaml
import tempfile
Expand All @@ -29,6 +31,7 @@ def __init__(self, log, args, plugins=[]):
self.template = args.template
self.scripts_path = args.scripts_path
self.additional_scripts = args.additional_script
self.params = args.params

ssl_verify = None
if args.skip_ssl_verification:
Expand Down Expand Up @@ -203,7 +206,16 @@ def _validate_cfg(self):
plugin.extend_schema(schema)

with open(self.descriptor, 'r') as stream:
self.cfg = yaml.safe_load(stream)
content = stream.read()

for k, v in six.iteritems(self.params):
Copy link
Contributor

Choose a reason for hiding this comment

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

in python 2.7 we don need six for this. do we support python 2.6 and so?

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't support 2.6.

self.log.debug("Substituting '%s' with '%s' value..." % (k, v))
content = re.sub(r"{{%s.*}}" % k, v, content)

# See if there any params without substitutions. If yes, use specified default values.
content = re.sub(r"{{\w+:(.*)}}", '\g<1>', content)

self.cfg = yaml.safe_load(content)

c = Core(source_data=self.cfg, schema_data=schema)
try:
Expand Down
55 changes: 51 additions & 4 deletions tests/test_dockerfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def setUp(self):
os.mkdir(self.target)
self.args = argparse.Namespace(path=self.yaml, output=self.target, without_sources=None,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None)
skip_ssl_verification=None, params={})
with open(self.yaml, 'wb') as f:
f.write(self.basic_config.encode())

Expand Down Expand Up @@ -112,9 +112,6 @@ def test_set_entrypoint(self):
self.assertRegexpMatches(dockerfile, regex)

def test_volumes(self):
"""
Test that cmd: is mapped into a CMD instruction
"""
with open(self.yaml, 'ab') as f:
f.write("volumes:\n - '/var/lib'\n - '/usr/lib'".encode())

Expand All @@ -128,3 +125,53 @@ def test_volumes(self):
dockerfile = f.read()
regex = re.compile(r'.*VOLUME \["/var/lib"\]\nVOLUME \["/usr/lib"\]', re.MULTILINE)
self.assertRegexpMatches(dockerfile, regex)

def test_default_substitution(self):
with open(self.yaml, 'ab') as f:
f.write("from: {{FROM:rhel:7}}\nversion: 1.0".encode())

generator = Generator(self.log, self.args)
generator.configure()
generator.render_from_template()
Copy link
Contributor

Choose a reason for hiding this comment

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

this code is copy pasted multiple times maybe it should be extracted to common setUp method

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably a good idea, yes.


self.assertEqual(generator.cfg['from'], 'rhel:7')

with open(os.path.join(self.target, "Dockerfile"), "r") as f:
dockerfile = f.read()
regex = re.compile(r'.*FROM rhel:7', re.MULTILINE)
self.assertRegexpMatches(dockerfile, regex)

def test_substitution_with_parameters(self):
with open(self.yaml, 'ab') as f:
f.write("from: {{FROM:rhel:7}}\nversion: 1.0".encode())

self.args.params = {'FROM': 'centos:7'}

generator = Generator(self.log, self.args)
generator.configure()
generator.render_from_template()

self.assertEqual(generator.cfg['from'], 'centos:7')

with open(os.path.join(self.target, "Dockerfile"), "r") as f:
dockerfile = f.read()
regex = re.compile(r'.*FROM centos:7', re.MULTILINE)
self.assertRegexpMatches(dockerfile, regex)

def test_substitution_of_multiple_parameters(self):
with open(self.yaml, 'ab') as f:
f.write("from: rhel:7\ndescription: 'Image {{VERSION}}'\nenvs:\n information:\n - name: VERSION\n value: {{VERSION}}\n".encode())

self.args.params = {'VERSION': '1.0'}

generator = Generator(self.log, self.args)
generator.configure()
generator.render_from_template()

self.assertEqual(generator.cfg['description'], 'Image 1.0')
self.assertEqual(generator.cfg['envs']['information'], [{'name': 'VERSION', 'value': 1.0}])

with open(os.path.join(self.target, "Dockerfile"), "r") as f:
dockerfile = f.read()
regex = re.compile(r'.*VERSION="1.0".*', re.MULTILINE)
self.assertRegexpMatches(dockerfile, regex)
2 changes: 1 addition & 1 deletion tests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def write_config(self, config):
def prepare_dogen(self, repo_files_dir=None):
args = argparse.Namespace(path=self.descriptor.name, output=self.target_dir, without_sources=None,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None, repo_files_dir=repo_files_dir)
skip_ssl_verification=None, repo_files_dir=repo_files_dir, params={})
self.dogen = Generator(self.log, args, [Repo])

def test_custom_repo_files_should_add_two(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_plugin_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def write_config(self, config):
def prepare_dogen(self, repo_files_dir=None):
args = argparse.Namespace(path=self.descriptor.name, output=self.target_dir, without_sources=None,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None, repo_files_dir=repo_files_dir)
skip_ssl_verification=None, repo_files_dir=repo_files_dir, params={})
self.dogen = Generator(self.log, args, [Repo])

def test_should_skip_plugin_if_no_path_to_repo_is_provided(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def gen_test(path, good):
def test(self):
args = argparse.Namespace(path=path, output="target", without_sources=None,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None)
skip_ssl_verification=None, params={})
generator = Generator(self.log, args)
if good:
generator.configure()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_unit_generate_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def setUp(self):
self.descriptor.write(self.basic_config.encode())
self.args = argparse.Namespace(path=self.descriptor.name, output="target", without_sources=False,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None)
skip_ssl_verification=None, params={})
def tearDown(self):
os.remove(self.descriptor.name)

Expand Down
8 changes: 4 additions & 4 deletions tests/test_unit_generate_handle_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def setUp(self):
self.log = mock.Mock()
args = argparse.Namespace(path="image.yaml", output="target", without_sources=None,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None)
skip_ssl_verification=None, params={})
self.generator = Generator(self.log, args)

def test_local_file(self):
Expand All @@ -29,7 +29,7 @@ def setUp(self):
self.log = mock.Mock()
args = argparse.Namespace(path="image.yaml", output="target", without_sources=None,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None)
skip_ssl_verification=None, params={})
self.generator = Generator(self.log, args)

@mock.patch('dogen.generator.requests.get')
Expand Down Expand Up @@ -72,13 +72,13 @@ def setUp(self):
self.log = mock.Mock()
args = argparse.Namespace(path="image.yaml", output="target", without_sources=None,
template="http://host/custom-template", scripts_path=None,
additional_script=None, skip_ssl_verification=None)
additional_script=None, skip_ssl_verification=None, params={})
self.generator = Generator(self.log, args)

def test_do_not_fail_if_no_template_is_provided(self):
args = argparse.Namespace(path="image.yaml", output="target", without_sources=None,
template=None, scripts_path=None, additional_script=None,
skip_ssl_verification=None)
skip_ssl_verification=None, params={})
self.generator = Generator(self.log, args)

fetch_file_mock = mock.Mock()
Expand Down