From eb5a4dda1fc221bf29c45eef47f0bfadec250943 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 6 Apr 2016 13:27:23 -0400 Subject: rh_subscription: only check subscription if configured The rh_subscription config module would attempt to connect to the RHN servers even when no config is provided. Now, instead check to make sure that valid config is provided first. That consists of username and password or a activation key. LP: #1536706 --- cloudinit/config/cc_rh_subscription.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_rh_subscription.py b/cloudinit/config/cc_rh_subscription.py index 6087c45c..2871984a 100644 --- a/cloudinit/config/cc_rh_subscription.py +++ b/cloudinit/config/cc_rh_subscription.py @@ -19,9 +19,14 @@ from cloudinit import util -def handle(_name, cfg, _cloud, log, _args): +def handle(name, cfg, _cloud, log, _args): sm = SubscriptionManager(cfg) sm.log = log + if not sm.is_configured(): + log.debug("Activation key not provided, config module %s disabled.", + _name) + return None + if not sm.is_registered: try: verify, verify_msg = sm._verify_keys() @@ -95,7 +100,6 @@ class SubscriptionManager(object): self.disable_repo = self.rhel_cfg.get('disable-repo') self.servicelevel = self.rhel_cfg.get('service-level') self.subman = ['subscription-manager'] - self.is_registered = self._is_registered() def log_success(self, msg): '''Simple wrapper for logging info messages. Useful for unittests''' @@ -134,7 +138,7 @@ class SubscriptionManager(object): return False, no_auto return True, None - def _is_registered(self): + def is_registered(self): ''' Checks if the system is already registered and returns True if so, else False @@ -400,3 +404,6 @@ class SubscriptionManager(object): self.log.debug("Disabled the following repos: %s" % (", ".join(disable_list)).replace('--disable=', '')) return True + + def is_configured(self): + return (self.userid and self.password) or self.activation_key -- cgit v1.2.3 From 578fed15061293ce421eec1c9c1e2e056631a734 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 6 Apr 2016 13:57:00 -0400 Subject: fix tests and hopefully actually work --- cloudinit/config/cc_rh_subscription.py | 7 +++---- tests/unittests/test_rh_subscription.py | 9 +++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_rh_subscription.py b/cloudinit/config/cc_rh_subscription.py index 2871984a..3a113aea 100644 --- a/cloudinit/config/cc_rh_subscription.py +++ b/cloudinit/config/cc_rh_subscription.py @@ -23,11 +23,10 @@ def handle(name, cfg, _cloud, log, _args): sm = SubscriptionManager(cfg) sm.log = log if not sm.is_configured(): - log.debug("Activation key not provided, config module %s disabled.", - _name) + log.debug("%s: module not configured.", name) return None - if not sm.is_registered: + if not sm.is_registered(): try: verify, verify_msg = sm._verify_keys() if verify is not True: @@ -406,4 +405,4 @@ class SubscriptionManager(object): return True def is_configured(self): - return (self.userid and self.password) or self.activation_key + return bool((self.userid and self.password) or self.activation_key) diff --git a/tests/unittests/test_rh_subscription.py b/tests/unittests/test_rh_subscription.py index 38d5763a..8c586ad7 100644 --- a/tests/unittests/test_rh_subscription.py +++ b/tests/unittests/test_rh_subscription.py @@ -126,7 +126,8 @@ class TestBadInput(unittest.TestCase): 'enable-repo': 'not_a_list' }} config_badkey = {'rh_subscription': - {'activation_key': 'abcdef1234', + {'activation-key': 'abcdef1234', + 'fookey': 'bar', 'org': '123', }} @@ -138,7 +139,11 @@ class TestBadInput(unittest.TestCase): ''' Attempt to register without the password key/value ''' - self.input_is_missing_data(self.config_no_password) + self.SM._sub_man_cli = mock.MagicMock( + side_effect=[util.ProcessExecutionError, (self.reg, 'bar')]) + self.handle(self.name, self.config_no_password, self.cloud_init, + self.log, self.args) + self.assertEqual(self.SM._sub_man_cli.call_count, 0) def test_no_org(self): ''' -- cgit v1.2.3 From a7de90b2ebdd097bb98fef72f970354973982e81 Mon Sep 17 00:00:00 2001 From: Stéphane Graber Date: Mon, 11 Apr 2016 00:09:44 -0400 Subject: Add support for lxd-bridge configuration --- cloudinit/config/cc_lxd.py | 111 ++++++++++++++++++++++++++++++++------ doc/examples/cloud-config-lxd.txt | 27 ++++++++++ 2 files changed, 123 insertions(+), 15 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_lxd.py b/cloudinit/config/cc_lxd.py index 63b8fb63..7d90b606 100644 --- a/cloudinit/config/cc_lxd.py +++ b/cloudinit/config/cc_lxd.py @@ -30,9 +30,23 @@ Example config: storage_create_loop: storage_pool: trust_password: + bridge: + mode: + name: + ipv4_address: + ipv4_netmask: + ipv4_dhcp_first: + ipv4_dhcp_last: + ipv4_dhcp_leases: + ipv4_nat: + ipv6_address: + ipv6_netmask: + ipv6_nat: + domain: """ from cloudinit import util +import os def handle(name, cfg, cloud, log, args): @@ -46,22 +60,24 @@ def handle(name, cfg, cloud, log, args): type(lxd_cfg)) return + # Grab the configuration init_cfg = lxd_cfg.get('init') if not isinstance(init_cfg, dict): log.warn("lxd/init config must be a dictionary. found a '%s'", type(init_cfg)) init_cfg = {} - if not init_cfg: - log.debug("no lxd/init config. disabled.") - return + bridge_cfg = lxd_cfg.get('bridge') + if not isinstance(bridge_cfg, dict): + log.warn("lxd/bridge config must be a dictionary. found a '%s'", + type(bridge_cfg)) + bridge_cfg = {} + # Install the needed packages packages = [] - # Ensure lxd is installed if not util.which("lxd"): packages.append('lxd') - # if using zfs, get the utils if init_cfg.get("storage_backend") == "zfs" and not util.which('zfs'): packages.append('zfs') @@ -73,13 +89,78 @@ def handle(name, cfg, cloud, log, args): return # Set up lxd if init config is given - init_keys = ( - 'network_address', 'network_port', 'storage_backend', - 'storage_create_device', 'storage_create_loop', - 'storage_pool', 'trust_password') - cmd = ['lxd', 'init', '--auto'] - for k in init_keys: - if init_cfg.get(k): - cmd.extend(["--%s=%s" % - (k.replace('_', '-'), str(init_cfg[k]))]) - util.subp(cmd) + if init_cfg: + init_keys = ( + 'network_address', 'network_port', 'storage_backend', + 'storage_create_device', 'storage_create_loop', + 'storage_pool', 'trust_password') + cmd = ['lxd', 'init', '--auto'] + for k in init_keys: + if init_cfg.get(k): + cmd.extend(["--%s=%s" % + (k.replace('_', '-'), str(init_cfg[k]))]) + util.subp(cmd) + + # Set up lxd-bridge if bridge config is given + if bridge_cfg: + debconf = {} + + if bridge_cfg.get("mode") == "none": + debconf["lxd/setup-bridge"] = "false" + debconf["lxd/bridge-name"] = "" + + elif bridge_cfg.get("mode") == "existing": + debconf["lxd/setup-bridge"] = "false" + debconf["lxd/use-existing-bridge"] = "true" + debconf["lxd/bridge-name"] = bridge_cfg.get("name") + + elif bridge_cfg.get("mode") == "new": + debconf["lxd/setup-bridge"] = "true" + debconf["lxd/bridge-name"] = bridge_cfg.get("name", "lxdbr0") + if bridge_cfg.get("ipv4_address"): + debconf["lxd/bridge-ipv4"] = "true" + debconf["lxd/bridge-ipv4-address"] = \ + bridge_cfg.get("ipv4_address") + debconf["lxd/bridge-ipv4-netmask"] = \ + bridge_cfg.get("ipv4_netmask") + debconf["lxd/bridge-ipv4-dhcp-first"] = \ + bridge_cfg.get("ipv4_dhcp_first") + debconf["lxd/bridge-ipv4-dhcp-last"] = \ + bridge_cfg.get("ipv4_dhcp_last") + debconf["lxd/bridge-ipv4-dhcp-leases"] = \ + bridge_cfg.get("ipv4_dhcp_leases") + debconf["lxd/bridge-ipv4-nat"] = \ + bridge_cfg.get("ipv4_nat", "true") + + if bridge_cfg.get("ipv6_address"): + debconf["lxd/bridge-ipv6"] = "true" + debconf["lxd/bridge-ipv6-address"] = \ + bridge_cfg.get("ipv6_address") + debconf["lxd/bridge-ipv6-netmask"] = \ + bridge_cfg.get("ipv6_netmask") + debconf["lxd/bridge-ipv6-nat"] = \ + bridge_cfg.get("ipv6_nat", "false") + + else: + log.warn("invalid bridge mode \"%s\"" % bridge_cfg.get("mode")) + return + + # Update debconf database + try: + log.debug("Setting lxd debconf-set-selections") + for k, v in debconf.items(): + util.subp(['debconf-communicate'], "set %s %s\n" % (k, v)) + except: + util.logexc(log, "Failed to run debconf-communicate for lxd") + + # Remove the existing configuration file (forces re-generation) + if os.path.exists("/etc/default/lxd-bridge"): + os.remove("/etc/default/lxd-bridge") + + # Run reconfigure + try: + log.debug("Running dpkg-reconfigure for lxd") + util.subp(['dpkg-reconfigure', 'lxd', + '--frontend=noninteractive']) + except: + util.logexc(log, "Failed to run dpkg-reconfigure for lxd") diff --git a/doc/examples/cloud-config-lxd.txt b/doc/examples/cloud-config-lxd.txt index b9bb4aa5..e96f314b 100644 --- a/doc/examples/cloud-config-lxd.txt +++ b/doc/examples/cloud-config-lxd.txt @@ -12,6 +12,20 @@ # storage_create_loop: set up loop based storage with size in GB # storage_pool: name of storage pool to use or create # trust_password: password required to add new clients +# bridge: dict of options for the lxd bridge +# mode: one of "new", "existing" or "none". Defaults to "new" +# name: the name of the bridge. Defaults to "lxdbr0" +# ipv4_address: an IPv4 address (e.g. 10.0.8.1) +# ipv4_netmask: a CIDR mask value (e.g. 24) +# ipv4_dhcp_first: the first IP of the DHCP range (e.g. 10.0.8.2) +# ipv4_dhcp_last: the last IP of the DHCP range (e.g. 10.0.8.254) +# ipv4_dhcp_leases: the size of the DHCP pool (e.g. 250) +# ipv4_nat: either "true" or "false" +# ipv6_address: an IPv6 address (e.g. fd98:9e0:3744::1) +# ipv6_netmask: a CIDR mask value (e.g. 64) +# ipv6_nat: either "true" or "false" +# domain: domain name to use for the bridge + lxd: init: @@ -20,6 +34,19 @@ lxd: storage_backend: zfs storage_pool: datapool storage_create_loop: 10 + bridge: + mode: new + name: lxdbr0 + ipv4_address: 10.0.8.1 + ipv4_netmask: 24 + ipv4_dhcp_first: 10.0.8.2 + ipv4_dhcp_last: 10.0.8.3 + ipv4_dhcp_leases: 250 + ipv4_nat: true + ipv6_address: fd98:9e0:3744::1 + ipv6_netmask: 64 + ipv6_nat: true + domain: lxd # The simplist working configuration is -- cgit v1.2.3 From 072510d7251653701c23ded9be38832044fb42b8 Mon Sep 17 00:00:00 2001 From: Stéphane Graber Date: Mon, 11 Apr 2016 11:58:28 -0400 Subject: Update lxd-bridge code to do a single debconf-communicate run and split debconf logic to a function --- cloudinit/config/cc_lxd.py | 101 +++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 49 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_lxd.py b/cloudinit/config/cc_lxd.py index 7d90b606..b467df20 100644 --- a/cloudinit/config/cc_lxd.py +++ b/cloudinit/config/cc_lxd.py @@ -103,53 +103,14 @@ def handle(name, cfg, cloud, log, args): # Set up lxd-bridge if bridge config is given if bridge_cfg: - debconf = {} - - if bridge_cfg.get("mode") == "none": - debconf["lxd/setup-bridge"] = "false" - debconf["lxd/bridge-name"] = "" - - elif bridge_cfg.get("mode") == "existing": - debconf["lxd/setup-bridge"] = "false" - debconf["lxd/use-existing-bridge"] = "true" - debconf["lxd/bridge-name"] = bridge_cfg.get("name") - - elif bridge_cfg.get("mode") == "new": - debconf["lxd/setup-bridge"] = "true" - debconf["lxd/bridge-name"] = bridge_cfg.get("name", "lxdbr0") - if bridge_cfg.get("ipv4_address"): - debconf["lxd/bridge-ipv4"] = "true" - debconf["lxd/bridge-ipv4-address"] = \ - bridge_cfg.get("ipv4_address") - debconf["lxd/bridge-ipv4-netmask"] = \ - bridge_cfg.get("ipv4_netmask") - debconf["lxd/bridge-ipv4-dhcp-first"] = \ - bridge_cfg.get("ipv4_dhcp_first") - debconf["lxd/bridge-ipv4-dhcp-last"] = \ - bridge_cfg.get("ipv4_dhcp_last") - debconf["lxd/bridge-ipv4-dhcp-leases"] = \ - bridge_cfg.get("ipv4_dhcp_leases") - debconf["lxd/bridge-ipv4-nat"] = \ - bridge_cfg.get("ipv4_nat", "true") - - if bridge_cfg.get("ipv6_address"): - debconf["lxd/bridge-ipv6"] = "true" - debconf["lxd/bridge-ipv6-address"] = \ - bridge_cfg.get("ipv6_address") - debconf["lxd/bridge-ipv6-netmask"] = \ - bridge_cfg.get("ipv6_netmask") - debconf["lxd/bridge-ipv6-nat"] = \ - bridge_cfg.get("ipv6_nat", "false") - - else: - log.warn("invalid bridge mode \"%s\"" % bridge_cfg.get("mode")) - return + debconf = bridge_to_debconf(bridge_cfg) # Update debconf database try: log.debug("Setting lxd debconf-set-selections") - for k, v in debconf.items(): - util.subp(['debconf-communicate'], "set %s %s\n" % (k, v)) + data = "\n".join(["set %s %s" % (k, v) + for k, v in debconf.items()]) + util.subp(['debconf-communicate'], data) except: util.logexc(log, "Failed to run debconf-communicate for lxd") @@ -158,9 +119,51 @@ def handle(name, cfg, cloud, log, args): os.remove("/etc/default/lxd-bridge") # Run reconfigure - try: - log.debug("Running dpkg-reconfigure for lxd") - util.subp(['dpkg-reconfigure', 'lxd', - '--frontend=noninteractive']) - except: - util.logexc(log, "Failed to run dpkg-reconfigure for lxd") + log.debug("Running dpkg-reconfigure for lxd") + util.subp(['dpkg-reconfigure', 'lxd', + '--frontend=noninteractive']) + + +def bridge_to_debconf(bridge_cfg): + debconf = {} + + if bridge_cfg.get("mode") == "none": + debconf["lxd/setup-bridge"] = "false" + debconf["lxd/bridge-name"] = "" + + elif bridge_cfg.get("mode") == "existing": + debconf["lxd/setup-bridge"] = "false" + debconf["lxd/use-existing-bridge"] = "true" + debconf["lxd/bridge-name"] = bridge_cfg.get("name") + + elif bridge_cfg.get("mode") == "new": + debconf["lxd/setup-bridge"] = "true" + debconf["lxd/bridge-name"] = bridge_cfg.get("name", "lxdbr0") + if bridge_cfg.get("ipv4_address"): + debconf["lxd/bridge-ipv4"] = "true" + debconf["lxd/bridge-ipv4-address"] = \ + bridge_cfg.get("ipv4_address") + debconf["lxd/bridge-ipv4-netmask"] = \ + bridge_cfg.get("ipv4_netmask") + debconf["lxd/bridge-ipv4-dhcp-first"] = \ + bridge_cfg.get("ipv4_dhcp_first") + debconf["lxd/bridge-ipv4-dhcp-last"] = \ + bridge_cfg.get("ipv4_dhcp_last") + debconf["lxd/bridge-ipv4-dhcp-leases"] = \ + bridge_cfg.get("ipv4_dhcp_leases") + debconf["lxd/bridge-ipv4-nat"] = \ + bridge_cfg.get("ipv4_nat", "true") + + if bridge_cfg.get("ipv6_address"): + debconf["lxd/bridge-ipv6"] = "true" + debconf["lxd/bridge-ipv6-address"] = \ + bridge_cfg.get("ipv6_address") + debconf["lxd/bridge-ipv6-netmask"] = \ + bridge_cfg.get("ipv6_netmask") + debconf["lxd/bridge-ipv6-nat"] = \ + bridge_cfg.get("ipv6_nat", "false") + + else: + raise Exception("invalid bridge mode \"%s\"" % bridge_cfg.get("mode")) + + return debconf -- cgit v1.2.3 From dcc2c894b9f12527ce0254fd7a0306e0d41af5bf Mon Sep 17 00:00:00 2001 From: Stéphane Graber Date: Mon, 11 Apr 2016 12:57:26 -0400 Subject: lxd-bridge: Don't require a bridge name and implement support for domain name --- cloudinit/config/cc_lxd.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_lxd.py b/cloudinit/config/cc_lxd.py index b467df20..0b10077a 100644 --- a/cloudinit/config/cc_lxd.py +++ b/cloudinit/config/cc_lxd.py @@ -138,7 +138,9 @@ def bridge_to_debconf(bridge_cfg): elif bridge_cfg.get("mode") == "new": debconf["lxd/setup-bridge"] = "true" - debconf["lxd/bridge-name"] = bridge_cfg.get("name", "lxdbr0") + if bridge_cfg.get("name"): + debconf["lxd/bridge-name"] = bridge_cfg.get("name") + if bridge_cfg.get("ipv4_address"): debconf["lxd/bridge-ipv4"] = "true" debconf["lxd/bridge-ipv4-address"] = \ @@ -163,6 +165,9 @@ def bridge_to_debconf(bridge_cfg): debconf["lxd/bridge-ipv6-nat"] = \ bridge_cfg.get("ipv6_nat", "false") + if bridge_cfg.get("domain"): + debconf["lxd/bridge-domain"] = bridge_cfg.get("domain") + else: raise Exception("invalid bridge mode \"%s\"" % bridge_cfg.get("mode")) -- cgit v1.2.3 From 860d98bce4b607a513dc94d96335e8f105a36294 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 11 Apr 2016 15:08:08 -0400 Subject: minor cleanups - use util.del_file rather than os.remove - raise exception if debconf-communicate is not present - add a trailing newline into debconf-communicate input --- cloudinit/config/cc_lxd.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_lxd.py b/cloudinit/config/cc_lxd.py index 0b10077a..bf735648 100644 --- a/cloudinit/config/cc_lxd.py +++ b/cloudinit/config/cc_lxd.py @@ -46,7 +46,6 @@ Example config: """ from cloudinit import util -import os def handle(name, cfg, cloud, log, args): @@ -102,26 +101,29 @@ def handle(name, cfg, cloud, log, args): util.subp(cmd) # Set up lxd-bridge if bridge config is given - if bridge_cfg: + dconf_comm = "debconf-communicate" + if bridge_cfg and util.which(dconf_comm): debconf = bridge_to_debconf(bridge_cfg) # Update debconf database try: - log.debug("Setting lxd debconf-set-selections") + log.debug("Setting lxd debconf via " + dconf_comm) data = "\n".join(["set %s %s" % (k, v) - for k, v in debconf.items()]) + for k, v in debconf.items()]) + "\n" util.subp(['debconf-communicate'], data) except: - util.logexc(log, "Failed to run debconf-communicate for lxd") + util.logexc(log, "Failed to run '%s' for lxd with" % dconf_comm) # Remove the existing configuration file (forces re-generation) - if os.path.exists("/etc/default/lxd-bridge"): - os.remove("/etc/default/lxd-bridge") + util.del_file("/etc/default/lxd-bridge") # Run reconfigure log.debug("Running dpkg-reconfigure for lxd") util.subp(['dpkg-reconfigure', 'lxd', '--frontend=noninteractive']) + elif bridge_cfg: + raise RuntimeError( + "Unable to configure lxd bridge without %s." + dconf_comm) def bridge_to_debconf(bridge_cfg): -- cgit v1.2.3 From 7122f7d6fac6eb78922a474facfd9d439d1bf5b6 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 12 Apr 2016 10:38:09 -0400 Subject: chef: straighten out validation_cert and validation_key Now, validation_key is always a path to a file, as it is in chef's client.rb syntax. validation_cert is always the *content* of that file that should be written. However, if validation_cert is the string "system", then we do not write that value, but rather assume the file exists. LP: #1568940 --- cloudinit/config/cc_chef.py | 21 ++++---- doc/examples/cloud-config-chef.txt | 4 +- templates/chef_client.rb.tmpl | 2 +- tests/unittests/test_handler/test_handler_chef.py | 65 ++++++++++++++++++++++- 4 files changed, 79 insertions(+), 13 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_chef.py b/cloudinit/config/cc_chef.py index 28711a59..07dacb0c 100644 --- a/cloudinit/config/cc_chef.py +++ b/cloudinit/config/cc_chef.py @@ -38,8 +38,10 @@ It can be configured with the following option structure:: chef: directories: (defaulting to /etc/chef, /var/log/chef, /var/lib/chef, /var/cache/chef, /var/backups/chef, /var/run/chef) - validation_key or validation_cert: (optional string to be written to - /etc/chef/validation.pem) + validation_cert: (optional string to be written to file validation_key) + special value 'system' means set use existing file + validation_key: (optional the path for validation_cert. default + /etc/chef/validation.pem) firstboot_path: (path to write run_list and initial_attributes keys that should also be present in this configuration, defaults to /etc/chef/firstboot.json) @@ -64,6 +66,7 @@ It can be configured with the following option structure:: server_url: show_time: ssl_verify_mode: + validation_cert: validation_key: validation_name: """ @@ -105,6 +108,7 @@ CHEF_RB_TPL_DEFAULTS = { # These are not symbols... 'log_location': '/var/log/chef/client.log', 'validation_key': CHEF_VALIDATION_PEM_PATH, + 'validation_cert': None, 'client_key': "/etc/chef/client.pem", 'json_attribs': CHEF_FB_PATH, 'file_cache_path': "/var/cache/chef", @@ -201,13 +205,12 @@ def handle(name, cfg, cloud, log, _args): for d in itertools.chain(chef_dirs, REQUIRED_CHEF_DIRS): util.ensure_dir(d) - # Set the validation key based on the presence of either 'validation_key' - # or 'validation_cert'. In the case where both exist, 'validation_key' - # takes precedence - for key in ('validation_key', 'validation_cert'): - if key in chef_cfg and chef_cfg[key]: - util.write_file(CHEF_VALIDATION_PEM_PATH, chef_cfg[key]) - break + vkey_path = chef_cfg.get('validation_key', CHEF_VALIDATION_PEM_PATH) + vcert = chef_cfg.get('validation_cert') + # special value 'system' means do not overwrite the file + # but still render the template to contain 'validation_key' + if vcert and vcert != "system": + util.write_file(vkey_path, vcert) # Create the chef config from template template_fn = cloud.get_template_filename('chef_client.rb') diff --git a/doc/examples/cloud-config-chef.txt b/doc/examples/cloud-config-chef.txt index 4edad653..b886cba2 100644 --- a/doc/examples/cloud-config-chef.txt +++ b/doc/examples/cloud-config-chef.txt @@ -67,7 +67,9 @@ chef: # Default validation name is chef-validator validation_name: "yourorg-validator" - validation_key: | + # if validation_cert's value is "system" then it is expected + # that the file already exists on the system. + validation_cert: | -----BEGIN RSA PRIVATE KEY----- YOUR-ORGS-VALIDATION-KEY-HERE -----END RSA PRIVATE KEY----- diff --git a/templates/chef_client.rb.tmpl b/templates/chef_client.rb.tmpl index c4069d22..cbb6b15f 100644 --- a/templates/chef_client.rb.tmpl +++ b/templates/chef_client.rb.tmpl @@ -26,7 +26,7 @@ log_location "{{log_location}}" {% if validation_name %} validation_client_name "{{validation_name}}" {% endif %} -{% if validation_key %} +{% if validation_cert %} validation_key "{{validation_key}}" {% endif %} {% if client_key %} diff --git a/tests/unittests/test_handler/test_handler_chef.py b/tests/unittests/test_handler/test_handler_chef.py index edad88cb..7763f23b 100644 --- a/tests/unittests/test_handler/test_handler_chef.py +++ b/tests/unittests/test_handler/test_handler_chef.py @@ -75,17 +75,28 @@ class TestChef(t_help.FilesystemMockingTestCase): 'chef': { 'server_url': 'localhost', 'validation_name': 'bob', + 'validation_key': "/etc/chef/vkey.pem", + 'validation_cert': "this is my cert", }, } cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) for d in cc_chef.CHEF_DIRS: self.assertTrue(os.path.isdir(d)) c = util.load_file(cc_chef.CHEF_RB_PATH) + + # the content of these keys is not expected to be rendered to tmpl + unrendered_keys = ('validation_cert',) for k, v in cfg['chef'].items(): + if k in unrendered_keys: + continue self.assertIn(v, c) for k, v in cc_chef.CHEF_RB_TPL_DEFAULTS.items(): - if isinstance(v, six.string_types): - self.assertIn(v, c) + if k in unrendered_keys: + continue + # the value from the cfg overrides that in the default + val = cfg['chef'].get(k, v) + if isinstance(val, six.string_types): + self.assertIn(val, c) c = util.load_file(cc_chef.CHEF_FB_PATH) self.assertEqual({}, json.loads(c)) @@ -131,3 +142,53 @@ class TestChef(t_help.FilesystemMockingTestCase): c = util.load_file(cc_chef.CHEF_RB_PATH) self.assertNotIn('json_attribs', c) self.assertNotIn('Formatter.show_time', c) + + @t_help.skipIf(not os.path.isfile(CLIENT_TEMPL), + CLIENT_TEMPL + " is not available") + def test_validation_cert_and_validation_key(self): + # test validation_cert content is written to validation_key path + tpl_file = util.load_file('templates/chef_client.rb.tmpl') + self.patchUtils(self.tmp) + self.patchOS(self.tmp) + + util.write_file('/etc/cloud/templates/chef_client.rb.tmpl', tpl_file) + v_path = '/etc/chef/vkey.pem' + v_cert = 'this is my cert' + cfg = { + 'chef': { + 'server_url': 'localhost', + 'validation_name': 'bob', + 'validation_key': v_path, + 'validation_cert': v_cert + }, + } + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) + content = util.load_file(cc_chef.CHEF_RB_PATH) + self.assertIn(v_path, content) + util.load_file(v_path) + self.assertEqual(v_cert, util.load_file(v_path)) + + def test_validation_cert_with_system(self): + # test validation_cert content is not written over system file + tpl_file = util.load_file('templates/chef_client.rb.tmpl') + self.patchUtils(self.tmp) + self.patchOS(self.tmp) + + v_path = '/etc/chef/vkey.pem' + v_cert = "system" + expected_cert = "this is the system file certificate" + cfg = { + 'chef': { + 'server_url': 'localhost', + 'validation_name': 'bob', + 'validation_key': v_path, + 'validation_cert': v_cert + }, + } + util.write_file('/etc/cloud/templates/chef_client.rb.tmpl', tpl_file) + util.write_file(v_path, expected_cert) + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) + content = util.load_file(cc_chef.CHEF_RB_PATH) + self.assertIn(v_path, content) + util.load_file(v_path) + self.assertEqual(expected_cert, util.load_file(v_path)) -- cgit v1.2.3 From 2b3f56294576998246e13f9b07074bad7b4bf212 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 12 Apr 2016 10:46:29 -0400 Subject: provide a warning if 'system' but file does not exist --- cloudinit/config/cc_chef.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_chef.py b/cloudinit/config/cc_chef.py index 07dacb0c..6ecaf8db 100644 --- a/cloudinit/config/cc_chef.py +++ b/cloudinit/config/cc_chef.py @@ -209,8 +209,13 @@ def handle(name, cfg, cloud, log, _args): vcert = chef_cfg.get('validation_cert') # special value 'system' means do not overwrite the file # but still render the template to contain 'validation_key' - if vcert and vcert != "system": - util.write_file(vkey_path, vcert) + if vcert: + if vcert != "system": + util.write_file(vkey_path, vcert) + elif not os.path.isfile(vkey_path): + log.warn("chef validation_cert provided as 'system', but " + "validation_key path '%s' does not exist.", + vkey_path) # Create the chef config from template template_fn = cloud.get_template_filename('chef_client.rb') -- cgit v1.2.3 From a551cb080388c2016bcf23981f99a4a6aa0fe198 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 13 Apr 2016 12:35:50 -0400 Subject: phone_home: allow usage of fqdn This simply allows the phone_home template to pass the systems fully qualified domain name. LP: #1566824 --- ChangeLog | 1 + cloudinit/config/cc_phone_home.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'cloudinit/config') diff --git a/ChangeLog b/ChangeLog index d7c2fc36..7bfd0c0a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -104,6 +104,7 @@ - systemd: do not specify After of obsolete syslog.target (LP: #1536964) - centos: Ensure that resolve conf object is written as a str (LP: #1479988) - chef: straighten out validation_cert and validation_key (LP: #1568940) + - phone_home: allow usage of fqdn (LP: #1566824) [Ollie Armstrong] 0.7.6: - open 0.7.6 diff --git a/cloudinit/config/cc_phone_home.py b/cloudinit/config/cc_phone_home.py index 18a7ddad..3dcc9459 100644 --- a/cloudinit/config/cc_phone_home.py +++ b/cloudinit/config/cc_phone_home.py @@ -30,7 +30,8 @@ POST_LIST_ALL = [ 'pub_key_rsa', 'pub_key_ecdsa', 'instance_id', - 'hostname' + 'hostname', + 'fdqn' ] @@ -41,7 +42,8 @@ POST_LIST_ALL = [ # # phone_home: # url: http://my.foo.bar/$INSTANCE_ID/ -# post: [ pub_key_dsa, pub_key_rsa, pub_key_ecdsa, instance_id +# post: [ pub_key_dsa, pub_key_rsa, pub_key_ecdsa, instance_id, hostname, +# fqdn ] # def handle(name, cfg, cloud, log, args): if len(args) != 0: @@ -74,6 +76,7 @@ def handle(name, cfg, cloud, log, args): all_keys = {} all_keys['instance_id'] = cloud.get_instance_id() all_keys['hostname'] = cloud.get_hostname() + all_keys['fqdn'] = cloud.get_hostname(fqdn=True) pubkeys = { 'pub_key_dsa': '/etc/ssh/ssh_host_dsa_key.pub', -- cgit v1.2.3