From ed541e23274a084d90f299dbd22e4fb0a404c63a Mon Sep 17 00:00:00 2001 From: Hans Krijger Date: Tue, 31 Jul 2018 11:18:53 -0700 Subject: [PATCH 1/7] initial commit --- README.md | 13 +++ azurelinuxagent/common/conf.py | 117 ++++++++++++++-------- azurelinuxagent/ga/exthandlers.py | 4 + azurelinuxagent/ga/update.py | 7 +- azurelinuxagent/pa/provision/cloudinit.py | 5 +- azurelinuxagent/pa/provision/default.py | 21 +++- config/alpine/waagent.conf | 3 + config/bigip/waagent.conf | 3 + config/coreos/waagent.conf | 3 + config/debian/waagent.conf | 3 + config/freebsd/waagent.conf | 3 + config/gaia/waagent.conf | 3 + config/suse/waagent.conf | 3 + config/ubuntu/waagent.conf | 3 + config/waagent.conf | 3 + tests/common/test_conf.py | 79 +++++++-------- tests/data/test_waagent.conf | 3 + tests/test_agent.py | 1 + 18 files changed, 185 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 66672ab27a..625fb199fc 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,7 @@ A configuration file (/etc/waagent.conf) controls the actions of waagent. Blank A sample configuration file is shown below: ```yml +Extensions.Enabled=y Provisioning.Enabled=y Provisioning.UseCloudInit=n Provisioning.DeleteRootPassword=n @@ -213,6 +214,18 @@ may be used for some string type configuration entries as detailed below. ### Configuration File Options +#### __Extensions.Enabled__ + +_Type: Boolean_ +_Default: y_ + +This allows the user to enable or disable the extension handling functionality in the +agent. Valid values are "y" or "n". If extension handling is disabled, the goal state +will still be processed and VM status is still reported, but only every 5 minutes. +Extension config within the goal state will be ignored. Note that functionality such +as password reset, ssh key updates and backups depend on extensions. Only disable this +if you do not need extensions at all. + #### __Provisioning.Enabled__ _Type: Boolean_ diff --git a/azurelinuxagent/common/conf.py b/azurelinuxagent/common/conf.py index a0f7c89dfa..3b482294b7 100644 --- a/azurelinuxagent/common/conf.py +++ b/azurelinuxagent/common/conf.py @@ -85,57 +85,59 @@ def load_conf_from_file(conf_file_path, conf=__conf__): raise AgentConfigError(("Failed to load conf file:{0}, {1}" "").format(conf_file_path, err)) + __SWITCH_OPTIONS__ = { - "OS.AllowHTTP" : False, - "OS.EnableFirewall" : False, - "OS.EnableFIPS" : False, - "OS.EnableRDMA" : False, - "OS.UpdateRdmaDriver" : False, - "OS.CheckRdmaDriver" : False, - "Logs.Verbose" : False, - "Provisioning.Enabled" : True, - "Provisioning.UseCloudInit" : False, - "Provisioning.AllowResetSysUser" : False, - "Provisioning.RegenerateSshHostKeyPair" : False, - "Provisioning.DeleteRootPassword" : False, - "Provisioning.DecodeCustomData" : False, - "Provisioning.ExecuteCustomData" : False, - "Provisioning.MonitorHostName" : False, - "DetectScvmmEnv" : False, - "ResourceDisk.Format" : False, - "DetectScvmmEnv" : False, - "ResourceDisk.Format" : False, - "ResourceDisk.EnableSwap" : False, - "AutoUpdate.Enabled" : True, - "EnableOverProvisioning" : True + "OS.AllowHTTP": False, + "OS.EnableFirewall": False, + "OS.EnableFIPS": False, + "OS.EnableRDMA": False, + "OS.UpdateRdmaDriver": False, + "OS.CheckRdmaDriver": False, + "Logs.Verbose": False, + "Extensions.Enabled": True, + "Provisioning.Enabled": True, + "Provisioning.UseCloudInit": False, + "Provisioning.AllowResetSysUser": False, + "Provisioning.RegenerateSshHostKeyPair": False, + "Provisioning.DeleteRootPassword": False, + "Provisioning.DecodeCustomData": False, + "Provisioning.ExecuteCustomData": False, + "Provisioning.MonitorHostName": False, + "DetectScvmmEnv": False, + "ResourceDisk.Format": False, + "ResourceDisk.EnableSwap": False, + "AutoUpdate.Enabled": True, + "EnableOverProvisioning": True } + __STRING_OPTIONS__ = { - "Lib.Dir" : "/var/lib/waagent", - "DVD.MountPoint" : "/mnt/cdrom/secure", - "Pid.File" : "/var/run/waagent.pid", - "Extension.LogDir" : "/var/log/azure", - "OS.OpensslPath" : "/usr/bin/openssl", - "OS.SshDir" : "/etc/ssh", - "OS.HomeDir" : "/home", - "OS.PasswordPath" : "/etc/shadow", - "OS.SudoersDir" : "/etc/sudoers.d", - "OS.RootDeviceScsiTimeout" : None, - "Provisioning.SshHostKeyPairType" : "rsa", - "Provisioning.PasswordCryptId" : "6", - "HttpProxy.Host" : None, - "ResourceDisk.MountPoint" : "/mnt/resource", - "ResourceDisk.MountOptions" : None, - "ResourceDisk.Filesystem" : "ext3", - "AutoUpdate.GAFamily" : "Prod" + "Lib.Dir": "/var/lib/waagent", + "DVD.MountPoint": "/mnt/cdrom/secure", + "Pid.File": "/var/run/waagent.pid", + "Extension.LogDir": "/var/log/azure", + "OS.OpensslPath": "/usr/bin/openssl", + "OS.SshDir": "/etc/ssh", + "OS.HomeDir": "/home", + "OS.PasswordPath": "/etc/shadow", + "OS.SudoersDir": "/etc/sudoers.d", + "OS.RootDeviceScsiTimeout": None, + "Provisioning.SshHostKeyPairType": "rsa", + "Provisioning.PasswordCryptId": "6", + "HttpProxy.Host": None, + "ResourceDisk.MountPoint": "/mnt/resource", + "ResourceDisk.MountOptions": None, + "ResourceDisk.Filesystem": "ext3", + "AutoUpdate.GAFamily": "Prod" } + __INTEGER_OPTIONS__ = { - "OS.SshClientAliveInterval" : 180, - "Provisioning.PasswordCryptSaltLength" : 10, - "HttpProxy.Port" : None, - "ResourceDisk.SwapSizeMB" : 0, - "Autoupdate.Frequency" : 3600 + "OS.SshClientAliveInterval": 180, + "Provisioning.PasswordCryptSaltLength": 10, + "HttpProxy.Port": None, + "ResourceDisk.SwapSizeMB": 0, + "Autoupdate.Frequency": 3600 } @@ -152,17 +154,21 @@ def get_configuration(conf=__conf__): return options + def enable_firewall(conf=__conf__): return conf.get_switch("OS.EnableFirewall", False) + def enable_rdma(conf=__conf__): return conf.get_switch("OS.EnableRDMA", False) or \ conf.get_switch("OS.UpdateRdmaDriver", False) or \ conf.get_switch("OS.CheckRdmaDriver", False) + def enable_rdma_update(conf=__conf__): return conf.get_switch("OS.UpdateRdmaDriver", False) + def get_logs_verbose(conf=__conf__): return conf.get_switch("Logs.Verbose", False) @@ -186,44 +192,57 @@ def get_agent_pid_file_path(conf=__conf__): def get_ext_log_dir(conf=__conf__): return conf.get("Extension.LogDir", "/var/log/azure") + def get_fips_enabled(conf=__conf__): return conf.get_switch("OS.EnableFIPS", False) + def get_openssl_cmd(conf=__conf__): return conf.get("OS.OpensslPath", "/usr/bin/openssl") + def get_ssh_client_alive_interval(conf=__conf__): return conf.get("OS.SshClientAliveInterval", 180) + def get_ssh_dir(conf=__conf__): return conf.get("OS.SshDir", "/etc/ssh") + def get_home_dir(conf=__conf__): return conf.get("OS.HomeDir", "/home") + def get_passwd_file_path(conf=__conf__): return conf.get("OS.PasswordPath", "/etc/shadow") + def get_sudoers_dir(conf=__conf__): return conf.get("OS.SudoersDir", "/etc/sudoers.d") + def get_sshd_conf_file_path(conf=__conf__): return os.path.join(get_ssh_dir(conf), "sshd_config") + def get_ssh_key_glob(conf=__conf__): return os.path.join(get_ssh_dir(conf), 'ssh_host_*key*') + def get_ssh_key_private_path(conf=__conf__): return os.path.join(get_ssh_dir(conf), 'ssh_host_{0}_key'.format(get_ssh_host_keypair_type(conf))) + def get_ssh_key_public_path(conf=__conf__): return os.path.join(get_ssh_dir(conf), 'ssh_host_{0}_key.pub'.format(get_ssh_host_keypair_type(conf))) + def get_root_device_scsi_timeout(conf=__conf__): return conf.get("OS.RootDeviceScsiTimeout", None) + def get_ssh_host_keypair_type(conf=__conf__): keypair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa") if keypair_type == "auto": @@ -234,15 +253,23 @@ def get_ssh_host_keypair_type(conf=__conf__): return "rsa" return keypair_type + def get_ssh_host_keypair_mode(conf=__conf__): return conf.get("Provisioning.SshHostKeyPairType", "rsa") + def get_provision_enabled(conf=__conf__): return conf.get_switch("Provisioning.Enabled", True) + +def get_extensions_enabled(conf=__conf__): + return conf.get_switch("Extensions.Enabled", True) + + def get_provision_cloudinit(conf=__conf__): return conf.get_switch("Provisioning.UseCloudInit", False) + def get_allow_reset_sys_user(conf=__conf__): return conf.get_switch("Provisioning.AllowResetSysUser", False) @@ -322,8 +349,10 @@ def get_autoupdate_enabled(conf=__conf__): def get_autoupdate_frequency(conf=__conf__): return conf.get_int("Autoupdate.Frequency", 3600) + def get_enable_overprovisioning(conf=__conf__): return conf.get_switch("EnableOverProvisioning", True) + def get_allow_http(conf=__conf__): return conf.get_switch("OS.AllowHTTP", False) diff --git a/azurelinuxagent/ga/exthandlers.py b/azurelinuxagent/ga/exthandlers.py index a66aacbd1b..cdfa825a7c 100644 --- a/azurelinuxagent/ga/exthandlers.py +++ b/azurelinuxagent/ga/exthandlers.py @@ -294,6 +294,10 @@ def cleanup_outdated_handlers(self): logger.warn("Failed to remove extension package {0}: {1}".format(pkg, e.strerror)) def handle_ext_handlers(self, etag=None): + if not conf.get_extensions_enabled(): + logger.verbose("Extension handling is disabled") + return + if self.ext_handlers.extHandlers is None or \ len(self.ext_handlers.extHandlers) == 0: logger.verbose("No extension handler config found") diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py index 4292fc26c7..d3c39c1771 100644 --- a/azurelinuxagent/ga/update.py +++ b/azurelinuxagent/ga/update.py @@ -72,6 +72,7 @@ MAX_FAILURE = 3 # Max failure allowed for agent before blacklisted GOAL_STATE_INTERVAL = 3 +GOAL_STATE_INTERVAL_DISABLED = 5 * 60 ORPHAN_WAIT_INTERVAL = 15 * 60 @@ -274,6 +275,10 @@ def run(self): self._ensure_partition_assigned() self._ensure_readonly_files() + goal_state_interval = GOAL_STATE_INTERVAL \ + if conf.get_extensions_enabled() \ + else GOAL_STATE_INTERVAL_DISABLED + while self.running: if self._is_orphaned: logger.info("Agent {0} is an orphan -- exiting", @@ -320,7 +325,7 @@ def run(self): duration=duration, message="Incarnation {0}".format(exthandlers_handler.last_etag)) - time.sleep(GOAL_STATE_INTERVAL) + time.sleep(goal_state_interval) except Exception as e: msg = u"Agent {0} failed with exception: {1}".format(CURRENT_AGENT, ustr(e)) diff --git a/azurelinuxagent/pa/provision/cloudinit.py b/azurelinuxagent/pa/provision/cloudinit.py index fb36699951..9609d7da4f 100644 --- a/azurelinuxagent/pa/provision/cloudinit.py +++ b/azurelinuxagent/pa/provision/cloudinit.py @@ -83,10 +83,7 @@ def wait_for_ovfenv(self, max_retry=1800, sleep_time=1): if os.path.isfile(ovf_file_path): try: ovf_env = OvfEnv(fileutil.read_file(ovf_file_path)) - self.report_event(message=ovf_env.provision_guest_agent, - is_success=True, - duration=0, - operation=WALAEventOperation.ProvisionGuestAgent) + self.handle_provision_guest_agent(ovf_env.provision_guest_agent) return except ProtocolError as pe: raise ProvisionError("OVF xml could not be parsed " diff --git a/azurelinuxagent/pa/provision/default.py b/azurelinuxagent/pa/provision/default.py index ab9bb13bf8..722246142a 100644 --- a/azurelinuxagent/pa/provision/default.py +++ b/azurelinuxagent/pa/provision/default.py @@ -46,6 +46,7 @@ CLOUD_INIT_REGEX = re.compile(CLOUD_INIT_PATTERN) PROVISIONED_FILE = 'provisioned' +DISABLE_AGENT_FILE = 'disable_agent' class ProvisionHandler(object): @@ -92,10 +93,7 @@ def run(self): is_success=True, duration=elapsed_milliseconds(utc_start)) - self.report_event(message=ovf_env.provision_guest_agent, - is_success=True, - duration=0, - operation=WALAEventOperation.ProvisionGuestAgent) + self.handle_provision_guest_agent(ovf_env.provision_guest_agent) self.report_ready(thumbprint) logger.info("Provisioning complete") @@ -168,6 +166,9 @@ def get_ssh_host_key_thumbprint(self, chk_err=True): def provisioned_file_path(self): return os.path.join(conf.get_lib_dir(), PROVISIONED_FILE) + def disable_agent_file_path(self): + return os.path.join(conf.get_lib_dir(), DISABLE_AGENT_FILE) + def is_provisioned(self): ''' A VM is considered provisionend *anytime* the provisioning @@ -205,6 +206,18 @@ def write_provisioned(self): self.provisioned_file_path(), get_osutil().get_instance_id()) + def write_agent_disabled(self): + logger.warn("Disabling guest agent") + fileutil.write_file(self.disable_agent_file_path(), '') + + def handle_provision_guest_agent(self, provision_guest_agent): + self.report_event(message=provision_guest_agent, + is_success=True, + duration=0, + operation=WALAEventOperation.ProvisionGuestAgent) + if not provision_guest_agent: + self.write_agent_disabled() + def provision(self, ovfenv): logger.info("Handle ovf-env.xml.") try: diff --git a/config/alpine/waagent.conf b/config/alpine/waagent.conf index 5c620e7049..64207e050c 100644 --- a/config/alpine/waagent.conf +++ b/config/alpine/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/config/bigip/waagent.conf b/config/bigip/waagent.conf index 501292e55c..9d8b2023d5 100644 --- a/config/bigip/waagent.conf +++ b/config/bigip/waagent.conf @@ -16,6 +16,9 @@ Role.TopologyConsumer=None # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/config/coreos/waagent.conf b/config/coreos/waagent.conf index a58c998591..a17be97552 100644 --- a/config/coreos/waagent.conf +++ b/config/coreos/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/config/debian/waagent.conf b/config/debian/waagent.conf index 0c49d87b84..8cf900c46a 100644 --- a/config/debian/waagent.conf +++ b/config/debian/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/config/freebsd/waagent.conf b/config/freebsd/waagent.conf index 6e611d1071..0f542b1c69 100644 --- a/config/freebsd/waagent.conf +++ b/config/freebsd/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/config/gaia/waagent.conf b/config/gaia/waagent.conf index 168beda055..9c2d68254a 100644 --- a/config/gaia/waagent.conf +++ b/config/gaia/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/config/suse/waagent.conf b/config/suse/waagent.conf index 2a514584cd..edc5d8218b 100644 --- a/config/suse/waagent.conf +++ b/config/suse/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/config/ubuntu/waagent.conf b/config/ubuntu/waagent.conf index e67cdfe7f5..58997c9b5c 100644 --- a/config/ubuntu/waagent.conf +++ b/config/ubuntu/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=n +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=y diff --git a/config/waagent.conf b/config/waagent.conf index 90efa37527..367e3e8639 100644 --- a/config/waagent.conf +++ b/config/waagent.conf @@ -5,6 +5,9 @@ # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=n diff --git a/tests/common/test_conf.py b/tests/common/test_conf.py index c38a8afdbf..002ea2a33d 100644 --- a/tests/common/test_conf.py +++ b/tests/common/test_conf.py @@ -27,45 +27,46 @@ class TestConf(AgentTestCase): # Note: # -- These values *MUST* match those from data/test_waagent.conf EXPECTED_CONFIGURATION = { - "Provisioning.Enabled" : True, - "Provisioning.UseCloudInit" : True, - "Provisioning.DeleteRootPassword" : True, - "Provisioning.RegenerateSshHostKeyPair" : True, - "Provisioning.SshHostKeyPairType" : "rsa", - "Provisioning.MonitorHostName" : True, - "Provisioning.DecodeCustomData" : False, - "Provisioning.ExecuteCustomData" : False, - "Provisioning.PasswordCryptId" : '6', - "Provisioning.PasswordCryptSaltLength" : 10, - "Provisioning.AllowResetSysUser" : False, - "ResourceDisk.Format" : True, - "ResourceDisk.Filesystem" : "ext4", - "ResourceDisk.MountPoint" : "/mnt/resource", - "ResourceDisk.EnableSwap" : False, - "ResourceDisk.SwapSizeMB" : 0, - "ResourceDisk.MountOptions" : None, - "Logs.Verbose" : False, - "OS.EnableFIPS" : True, - "OS.RootDeviceScsiTimeout" : '300', - "OS.OpensslPath" : '/usr/bin/openssl', - "OS.SshClientAliveInterval" : 42, - "OS.SshDir" : "/notareal/path", - "HttpProxy.Host" : None, - "HttpProxy.Port" : None, - "DetectScvmmEnv" : False, - "Lib.Dir" : "/var/lib/waagent", - "DVD.MountPoint" : "/mnt/cdrom/secure", - "Pid.File" : "/var/run/waagent.pid", - "Extension.LogDir" : "/var/log/azure", - "OS.HomeDir" : "/home", - "OS.EnableRDMA" : False, - "OS.UpdateRdmaDriver" : False, - "OS.CheckRdmaDriver" : False, - "AutoUpdate.Enabled" : True, - "AutoUpdate.GAFamily" : "Prod", - "EnableOverProvisioning" : True, - "OS.AllowHTTP" : False, - "OS.EnableFirewall" : False + "Extensions.Enabled": True, + "Provisioning.Enabled": True, + "Provisioning.UseCloudInit": True, + "Provisioning.DeleteRootPassword": True, + "Provisioning.RegenerateSshHostKeyPair": True, + "Provisioning.SshHostKeyPairType": "rsa", + "Provisioning.MonitorHostName": True, + "Provisioning.DecodeCustomData": False, + "Provisioning.ExecuteCustomData": False, + "Provisioning.PasswordCryptId": '6', + "Provisioning.PasswordCryptSaltLength": 10, + "Provisioning.AllowResetSysUser": False, + "ResourceDisk.Format": True, + "ResourceDisk.Filesystem": "ext4", + "ResourceDisk.MountPoint": "/mnt/resource", + "ResourceDisk.EnableSwap": False, + "ResourceDisk.SwapSizeMB": 0, + "ResourceDisk.MountOptions": None, + "Logs.Verbose": False, + "OS.EnableFIPS": True, + "OS.RootDeviceScsiTimeout": '300', + "OS.OpensslPath": '/usr/bin/openssl', + "OS.SshClientAliveInterval": 42, + "OS.SshDir": "/notareal/path", + "HttpProxy.Host": None, + "HttpProxy.Port": None, + "DetectScvmmEnv": False, + "Lib.Dir": "/var/lib/waagent", + "DVD.MountPoint": "/mnt/cdrom/secure", + "Pid.File": "/var/run/waagent.pid", + "Extension.LogDir": "/var/log/azure", + "OS.HomeDir": "/home", + "OS.EnableRDMA": False, + "OS.UpdateRdmaDriver": False, + "OS.CheckRdmaDriver": False, + "AutoUpdate.Enabled": True, + "AutoUpdate.GAFamily": "Prod", + "EnableOverProvisioning": True, + "OS.AllowHTTP": False, + "OS.EnableFirewall": False } def setUp(self): diff --git a/tests/data/test_waagent.conf b/tests/data/test_waagent.conf index b779efc6ae..101cd320b7 100644 --- a/tests/data/test_waagent.conf +++ b/tests/data/test_waagent.conf @@ -11,6 +11,9 @@ FauxKey3=delalloc,rw,noatime,nobarrier,users,mode=777 # Enable instance creation Provisioning.Enabled=y +# Enable extension handling +Extensions.Enabled=y + # Rely on cloud-init to provision Provisioning.UseCloudInit=y diff --git a/tests/test_agent.py b/tests/test_agent.py index 0f71259a96..81e64bf01b 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -30,6 +30,7 @@ DetectScvmmEnv = False EnableOverProvisioning = True Extension.LogDir = /var/log/azure +Extensions.Enabled = True HttpProxy.Host = None HttpProxy.Port = None Lib.Dir = /var/lib/waagent From aabdd081cbffb0073f83eeaaf172cb972e4333d5 Mon Sep 17 00:00:00 2001 From: Hans Krijger Date: Thu, 2 Aug 2018 10:52:32 -0700 Subject: [PATCH 2/7] sleep forever when disabled --- azurelinuxagent/common/conf.py | 7 +++++++ azurelinuxagent/daemon/main.py | 13 +++++++++++++ azurelinuxagent/pa/provision/default.py | 11 ++++------- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/azurelinuxagent/common/conf.py b/azurelinuxagent/common/conf.py index 3b482294b7..fd9369fe1d 100644 --- a/azurelinuxagent/common/conf.py +++ b/azurelinuxagent/common/conf.py @@ -26,6 +26,8 @@ import azurelinuxagent.common.utils.fileutil as fileutil from azurelinuxagent.common.exception import AgentConfigError +DISABLE_AGENT_FILE = 'disable_agent' + class ConfigurationProvider(object): """ @@ -356,3 +358,8 @@ def get_enable_overprovisioning(conf=__conf__): def get_allow_http(conf=__conf__): return conf.get_switch("OS.AllowHTTP", False) + + +@property +def get_disable_agent_file_path(conf=__conf__): + return os.path.join(get_lib_dir(conf), DISABLE_AGENT_FILE) diff --git a/azurelinuxagent/daemon/main.py b/azurelinuxagent/daemon/main.py index d44af469e6..12531761fa 100644 --- a/azurelinuxagent/daemon/main.py +++ b/azurelinuxagent/daemon/main.py @@ -99,6 +99,17 @@ def check_pid(self): fileutil.write_file(pid_file, ustr(os.getpid())) + def sleep_if_disabled(self): + agent_disabled_file_path = conf.get_disable_agent_file_path() + if os.path.exists(agent_disabled_file_path): + import threading + logger.warn("Disabling the guest agent by sleeping forever; " + "to re-enable, remove {0} and restart" + .format(agent_disabled_file_path)) + self.running = False + disable_event = threading.Event() + disable_event.wait() + def initialize_environment(self): # Create lib dir if not os.path.isdir(conf.get_lib_dir()): @@ -149,5 +160,7 @@ def daemon(self, child_args=None): else: logger.info("RDMA capabilities are not enabled, skipping") + self.sleep_if_disabled() + while self.running: self.update_handler.run_latest(child_args=child_args) diff --git a/azurelinuxagent/pa/provision/default.py b/azurelinuxagent/pa/provision/default.py index 722246142a..6f44b44e08 100644 --- a/azurelinuxagent/pa/provision/default.py +++ b/azurelinuxagent/pa/provision/default.py @@ -46,7 +46,6 @@ CLOUD_INIT_REGEX = re.compile(CLOUD_INIT_PATTERN) PROVISIONED_FILE = 'provisioned' -DISABLE_AGENT_FILE = 'disable_agent' class ProvisionHandler(object): @@ -166,9 +165,6 @@ def get_ssh_host_key_thumbprint(self, chk_err=True): def provisioned_file_path(self): return os.path.join(conf.get_lib_dir(), PROVISIONED_FILE) - def disable_agent_file_path(self): - return os.path.join(conf.get_lib_dir(), DISABLE_AGENT_FILE) - def is_provisioned(self): ''' A VM is considered provisionend *anytime* the provisioning @@ -206,9 +202,10 @@ def write_provisioned(self): self.provisioned_file_path(), get_osutil().get_instance_id()) - def write_agent_disabled(self): - logger.warn("Disabling guest agent") - fileutil.write_file(self.disable_agent_file_path(), '') + @staticmethod + def write_agent_disabled(): + logger.warn("Disabling guest agent in accordance with ovf-env.xml") + fileutil.write_file(conf.get_disable_agent_file_path(), '') def handle_provision_guest_agent(self, provision_guest_agent): self.report_event(message=provision_guest_agent, From 896e911934790327bb3419cc94a5a5eefca8b211 Mon Sep 17 00:00:00 2001 From: Hans Krijger Date: Fri, 3 Aug 2018 00:04:47 -0700 Subject: [PATCH 3/7] add unit tests for exensions.enabled --- azurelinuxagent/common/conf.py | 1 - tests/common/test_conf.py | 8 ++++++++ tests/ga/test_extension.py | 14 ++++++++++++++ tests/ga/test_update.py | 22 +++++++++++++++++++++- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/azurelinuxagent/common/conf.py b/azurelinuxagent/common/conf.py index fd9369fe1d..4b7972bbda 100644 --- a/azurelinuxagent/common/conf.py +++ b/azurelinuxagent/common/conf.py @@ -360,6 +360,5 @@ def get_allow_http(conf=__conf__): return conf.get_switch("OS.AllowHTTP", False) -@property def get_disable_agent_file_path(conf=__conf__): return os.path.join(get_lib_dir(conf), DISABLE_AGENT_FILE) diff --git a/tests/common/test_conf.py b/tests/common/test_conf.py index 002ea2a33d..053414536b 100644 --- a/tests/common/test_conf.py +++ b/tests/common/test_conf.py @@ -114,3 +114,11 @@ def test_get_configuration(self): TestConf.EXPECTED_CONFIGURATION[k], configuration[k], k) + + def test_get_agent_disabled_file_path(self): + self.assertEqual(get_disable_agent_file_path(self.conf), + os.path.join(self.tmp_dir, DISABLE_AGENT_FILE)) + + def test_get_extensions_enabled(self): + self.assertTrue(get_extensions_enabled(self.conf)) + diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py index 9e6f3a4490..6fc6b8c15f 100644 --- a/tests/ga/test_extension.py +++ b/tests/ga/test_extension.py @@ -713,6 +713,20 @@ def test_ext_handler_version_decide_between_minor_versions(self, *args): ext_handler_instance.decide_version() self.assertEqual(expected_version, ext_handler.properties.version) + @patch('azurelinuxagent.common.conf.get_extensions_enabled', return_value=False) + def test_extensions_disabled(self, _, *args): + # test status is reported for no extensions + test_data = WireProtocolData(DATA_FILE_NO_EXT) + exthandlers_handler, protocol = self._create_mock(test_data, *args) + exthandlers_handler.run() + self._assert_no_handler_status(protocol.report_vm_status) + + # test status is reported, but extensions are not processed + test_data = WireProtocolData(DATA_FILE) + exthandlers_handler, protocol = self._create_mock(test_data, *args) + exthandlers_handler.run() + self._assert_no_handler_status(protocol.report_vm_status) + if __name__ == '__main__': unittest.main() diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py index a3aa7ae95c..ac5039f60d 100644 --- a/tests/ga/test_update.py +++ b/tests/ga/test_update.py @@ -1194,7 +1194,7 @@ def test_run_latest_creates_only_one_signal_handler(self, mock_signal): self._test_run_latest() self.assertEqual(0, mock_signal.call_count) - def _test_run(self, invocations=1, calls=[call.run()], enable_updates=False): + def _test_run(self, invocations=1, calls=[call.run()], enable_updates=False, sleep_interval=(3,)): conf.get_autoupdate_enabled = Mock(return_value=enable_updates) # Note: @@ -1229,6 +1229,8 @@ def iterator(*args, **kwargs): self.assertEqual(1, mock_ra_handler.call_count) self.assertEqual(mock_ra_handler.return_value.method_calls, calls) self.assertEqual(invocations, mock_sleep.call_count) + if invocations > 0: + self.assertEqual(sleep_interval, mock_sleep.call_args[0]) self.assertEqual(1, mock_monitor.call_count) self.assertEqual(1, mock_env.call_count) self.assertEqual(1, mock_exit.call_count) @@ -1480,6 +1482,24 @@ def test_package_filter_for_agent_manifest(self, _): self.assertTrue(ga_manifest_2.allowed_versions[0] == '2.2.13') self.assertTrue(ga_manifest_2.allowed_versions[1] == '2.2.14') + @patch('azurelinuxagent.common.conf.get_extensions_enabled', return_value=False) + def test_update_happens_when_extensions_disabled(self, _): + """ + Although the extension enabled config will not get checked + before an update is found, this test attempts to ensure that + behavior never changes. + """ + self.update_handler._upgrade_available = Mock(return_value=True) + self._test_run(invocations=0, calls=[], enable_updates=True, sleep_interval=(300,)) + + @patch('azurelinuxagent.common.conf.get_extensions_enabled', return_value=False) + def test_interval_changes_when_extensions_disabled(self, _): + """ + When extension processing is disabled, the goal state interval should be larger. + """ + self.update_handler._upgrade_available = Mock(return_value=False) + self._test_run(invocations=15, calls=[call.run()] * 15, sleep_interval=(300,)) + class MonitorThreadTest(AgentTestCase): def setUp(self): From 4bcba0d1ef2e2ae51629f76d21657e9495b345be Mon Sep 17 00:00:00 2001 From: Hans Krijger Date: Fri, 3 Aug 2018 16:23:06 -0700 Subject: [PATCH 4/7] unit tests for provision guest agent --- azurelinuxagent/pa/provision/default.py | 4 +- tests/common/test_conf.py | 12 ++ tests/daemon/test_daemon.py | 14 +- tests/data/ovf-env-2.xml | 40 ++++++ tests/data/ovf-env-3.xml | 40 ++++++ tests/data/ovf-env-4.xml | 40 ++++++ tests/pa/test_provision.py | 162 +++++++++++++++++++++--- 7 files changed, 284 insertions(+), 28 deletions(-) create mode 100644 tests/data/ovf-env-2.xml create mode 100644 tests/data/ovf-env-3.xml create mode 100644 tests/data/ovf-env-4.xml diff --git a/azurelinuxagent/pa/provision/default.py b/azurelinuxagent/pa/provision/default.py index 6f44b44e08..a6e5082467 100644 --- a/azurelinuxagent/pa/provision/default.py +++ b/azurelinuxagent/pa/provision/default.py @@ -99,7 +99,7 @@ def run(self): except (ProtocolError, ProvisionError) as e: self.report_not_ready("ProvisioningFailed", ustr(e)) - self.report_event(ustr(e)) + self.report_event(ustr(e), is_success=False) logger.error("Provisioning failed: {0}", ustr(e)) return @@ -212,7 +212,7 @@ def handle_provision_guest_agent(self, provision_guest_agent): is_success=True, duration=0, operation=WALAEventOperation.ProvisionGuestAgent) - if not provision_guest_agent: + if provision_guest_agent and provision_guest_agent.lower() == 'false': self.write_agent_disabled() def provision(self, ovfenv): diff --git a/tests/common/test_conf.py b/tests/common/test_conf.py index 053414536b..be935db3c0 100644 --- a/tests/common/test_conf.py +++ b/tests/common/test_conf.py @@ -119,6 +119,18 @@ def test_get_agent_disabled_file_path(self): self.assertEqual(get_disable_agent_file_path(self.conf), os.path.join(self.tmp_dir, DISABLE_AGENT_FILE)) + def test_write_agent_disabled(self): + """ + Test writing disable_agent is empty + """ + from azurelinuxagent.pa.provision.default import ProvisionHandler + + disable_file_path = get_disable_agent_file_path(self.conf) + self.assertFalse(os.path.exists(disable_file_path)) + ProvisionHandler.write_agent_disabled() + self.assertTrue(os.path.exists(disable_file_path)) + self.assertEqual('', fileutil.read_file(disable_file_path)) + def test_get_extensions_enabled(self): self.assertTrue(get_extensions_enabled(self.conf)) diff --git a/tests/daemon/test_daemon.py b/tests/daemon/test_daemon.py index 6f9f8b1a81..5b9ea75778 100644 --- a/tests/daemon/test_daemon.py +++ b/tests/daemon/test_daemon.py @@ -27,16 +27,17 @@ def __init__(self, daemon_handler, count): def __call__(self, *args, **kw): self.count = self.count - 1 - #Stop daemon after restarting for n times + # Stop daemon after restarting for n times if self.count <= 0: self.daemon_handler.running = False raise Exception("Mock unhandled exception") + class TestDaemon(AgentTestCase): @patch("time.sleep") def test_daemon_restart(self, mock_sleep): - #Mock daemon function + # Mock daemon function daemon_handler = get_daemon_handler() mock_daemon = Mock(side_effect=MockDaemonCall(daemon_handler, 2)) daemon_handler.daemon = mock_daemon @@ -51,7 +52,7 @@ def test_daemon_restart(self, mock_sleep): @patch("time.sleep") @patch("azurelinuxagent.daemon.main.conf") @patch("azurelinuxagent.daemon.main.sys.exit") - def test_check_pid(self, mock_exit, mock_conf, mock_sleep): + def test_check_pid(self, mock_exit, mock_conf, _): daemon_handler = get_daemon_handler() mock_pid_file = os.path.join(self.tmp_dir, "pid") @@ -65,7 +66,7 @@ def test_check_pid(self, mock_exit, mock_conf, mock_sleep): @patch("azurelinuxagent.daemon.main.DaemonHandler.check_pid") @patch("azurelinuxagent.common.conf.get_fips_enabled", return_value=True) - def test_set_openssl_fips(self, mock_conf, mock_daemon): + def test_set_openssl_fips(self, _, __): daemon_handler = get_daemon_handler() daemon_handler.running = False with patch.dict("os.environ"): @@ -75,13 +76,14 @@ def test_set_openssl_fips(self, mock_conf, mock_daemon): @patch("azurelinuxagent.daemon.main.DaemonHandler.check_pid") @patch("azurelinuxagent.common.conf.get_fips_enabled", return_value=False) - def test_does_not_set_openssl_fips(self, mock_conf, mock_daemon): + def test_does_not_set_openssl_fips(self, _, __): daemon_handler = get_daemon_handler() daemon_handler.running = False with patch.dict("os.environ"): daemon_handler.run() self.assertFalse(OPENSSL_FIPS_ENVIRONMENT in os.environ) - + + if __name__ == '__main__': unittest.main() diff --git a/tests/data/ovf-env-2.xml b/tests/data/ovf-env-2.xml new file mode 100644 index 0000000000..5281d84bb2 --- /dev/null +++ b/tests/data/ovf-env-2.xml @@ -0,0 +1,40 @@ + + + + 1.0 + + LinuxProvisioningConfiguration + HostName + UserName + UserPassword + false + + + + EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62 + $HOME/UserName/.ssh/authorized_keys + ssh-rsa AAAANOTAREALKEY== foo@bar.local + + + + + EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62 + $HOME/UserName/.ssh/id_rsa + + + + CustomData + + + + 1.0 + + kms.core.windows.net + true + + true + true + false + + + diff --git a/tests/data/ovf-env-3.xml b/tests/data/ovf-env-3.xml new file mode 100644 index 0000000000..78129a0c74 --- /dev/null +++ b/tests/data/ovf-env-3.xml @@ -0,0 +1,40 @@ + + + + 1.0 + + LinuxProvisioningConfiguration + HostName + UserName + UserPassword + false + + + + EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62 + $HOME/UserName/.ssh/authorized_keys + ssh-rsa AAAANOTAREALKEY== foo@bar.local + + + + + EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62 + $HOME/UserName/.ssh/id_rsa + + + + CustomData + + + + 1.0 + + kms.core.windows.net + + + true + true + false + + + diff --git a/tests/data/ovf-env-4.xml b/tests/data/ovf-env-4.xml new file mode 100644 index 0000000000..4a22f5dea2 --- /dev/null +++ b/tests/data/ovf-env-4.xml @@ -0,0 +1,40 @@ + + + + 1.0 + + LinuxProvisioningConfiguration + HostName + UserName + UserPassword + false + + + + EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62 + $HOME/UserName/.ssh/authorized_keys + ssh-rsa AAAANOTAREALKEY== foo@bar.local + + + + + EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62 + $HOME/UserName/.ssh/id_rsa + + + + CustomData + + + + 1.0 + + kms.core.windows.net + bad data + + true + true + false + + + diff --git a/tests/pa/test_provision.py b/tests/pa/test_provision.py index f62e9d42e0..0335bc9c48 100644 --- a/tests/pa/test_provision.py +++ b/tests/pa/test_provision.py @@ -113,10 +113,77 @@ def test_is_provisioned_not_deprovisioned(self, self.assertEqual(1, deprovision_handler.run_changed_unique_id.call_count) @distros() + def test_provision_telemetry_pga_false(self, + distro_name, + distro_version, + distro_full_name): + """ + ProvisionGuestAgent flag is 'false' + """ + self._provision_test(distro_name, + distro_version, + distro_full_name, + OVF_FILE_NAME, + 'false', + True) + + @distros() + def test_provision_telemetry_pga_true(self, + distro_name, + distro_version, + distro_full_name): + """ + ProvisionGuestAgent flag is 'true' + """ + self._provision_test(distro_name, + distro_version, + distro_full_name, + 'ovf-env-2.xml', + 'true', + True) + + @distros() + def test_provision_telemetry_pga_empty(self, + distro_name, + distro_version, + distro_full_name): + """ + ProvisionGuestAgent flag is '' + """ + self._provision_test(distro_name, + distro_version, + distro_full_name, + 'ovf-env-3.xml', + 'true', + False) + + @distros() + def test_provision_telemetry_pga_bad(self, + distro_name, + distro_version, + distro_full_name): + """ + ProvisionGuestAgent flag is 'bad data' + """ + self._provision_test(distro_name, + distro_version, + distro_full_name, + 'ovf-env-4.xml', + 'bad data', + True) + @patch('azurelinuxagent.common.osutil.default.DefaultOSUtil.get_instance_id', - return_value='B9F3C233-9913-9F42-8EB3-BA656DF32502') - def test_provision_telemetry_success(self, mock_util, distro_name, distro_version, - distro_full_name): + return_value='B9F3C233-9913-9F42-8EB3-BA656DF32502') + @patch('azurelinuxagent.pa.provision.default.ProvisionHandler.write_agent_disabled') + def _provision_test(self, + distro_name, + distro_version, + distro_full_name, + ovf_file, + provisionMessage, + expect_success, + patch_write_agent_disabled, + patch_get_instance_id): """ Assert that the agent issues two telemetry messages as part of a successful provisioning. @@ -124,7 +191,8 @@ def test_provision_telemetry_success(self, mock_util, distro_name, distro_versio 1. Provision 2. GuestState """ - ph = get_provision_handler(distro_name, distro_version, + ph = get_provision_handler(distro_name, + distro_version, distro_full_name) ph.report_event = MagicMock() ph.reg_ssh_host_key = MagicMock(return_value='--thumprint--') @@ -138,30 +206,43 @@ def test_provision_telemetry_success(self, mock_util, distro_name, distro_versio conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir) ovfenv_file = os.path.join(self.tmp_dir, OVF_FILE_NAME) - ovfenv_data = load_data("ovf-env.xml") + ovfenv_data = load_data(ovf_file) fileutil.write_file(ovfenv_file, ovfenv_data) ph.run() - self.assertEqual(2, ph.report_event.call_count) - positional_args, kw_args = ph.report_event.call_args_list[0] - # [call('Provisioning succeeded (146473.68s)', duration=65, is_success=True)] - self.assertTrue(re.match(r'Provisioning succeeded \(\d+\.\d+s\)', positional_args[0]) is not None) - self.assertTrue(isinstance(kw_args['duration'], int)) - self.assertTrue(kw_args['is_success']) - - positional_args, kw_args = ph.report_event.call_args_list[1] - self.assertTrue(kw_args['operation'] == 'ProvisionGuestAgent') - self.assertTrue(kw_args['message'] == 'false') - self.assertTrue(kw_args['is_success']) + if expect_success: + self.assertEqual(2, ph.report_event.call_count) + positional_args, kw_args = ph.report_event.call_args_list[0] + # [call('Provisioning succeeded (146473.68s)', duration=65, is_success=True)] + self.assertTrue(re.match(r'Provisioning succeeded \(\d+\.\d+s\)', positional_args[0]) is not None) + self.assertTrue(isinstance(kw_args['duration'], int)) + self.assertTrue(kw_args['is_success']) + + positional_args, kw_args = ph.report_event.call_args_list[1] + self.assertTrue(kw_args['operation'] == 'ProvisionGuestAgent') + self.assertTrue(kw_args['message'] == provisionMessage) + self.assertTrue(kw_args['is_success']) + + expected_disabled = True if provisionMessage == 'false' else False + self.assertTrue(patch_write_agent_disabled.call_count == expected_disabled) + + else: + self.assertEqual(1, ph.report_event.call_count) + positional_args, kw_args = ph.report_event.call_args_list[0] + # [call(u'[ProtocolError] Failed to validate OVF: ProvisionGuestAgent not found')] + self.assertTrue('Failed to validate OVF: ProvisionGuestAgent not found' in positional_args[0]) + self.assertFalse(kw_args['is_success']) @distros() @patch( 'azurelinuxagent.common.osutil.default.DefaultOSUtil.get_instance_id', return_value='B9F3C233-9913-9F42-8EB3-BA656DF32502') - def test_provision_telemetry_fail(self, mock_util, distro_name, - distro_version, - distro_full_name): + def test_provision_telemetry_fail(self, + mock_util, + distro_name, + distro_version, + distro_full_name): """ Assert that the agent issues one telemetry message as part of a failed provisioning. @@ -188,7 +269,48 @@ def test_provision_telemetry_fail(self, mock_util, distro_name, ph.run() ph.report_event.assert_called_once_with( - "[ProvisionError] --unit-test--") + '[ProvisionError] --unit-test--', is_success=False) + + @patch('azurelinuxagent.pa.provision.default.ProvisionHandler.write_agent_disabled') + @distros() + def test_handle_provision_guest_agent(self, + patch_write_agent_disabled, + distro_name, + distro_version, + distro_full_name): + ph = get_provision_handler(distro_name, + distro_version, + distro_full_name) + + patch_write_agent_disabled.call_count = 0 + + ph.handle_provision_guest_agent(provision_guest_agent='false') + self.assertEqual(1, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent='False') + self.assertEqual(2, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent='FALSE') + self.assertEqual(3, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent='') + self.assertEqual(3, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent=' ') + self.assertEqual(3, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent=None) + self.assertEqual(3, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent='true') + self.assertEqual(3, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent='True') + self.assertEqual(3, patch_write_agent_disabled.call_count) + + ph.handle_provision_guest_agent(provision_guest_agent='TRUE') + self.assertEqual(3, patch_write_agent_disabled.call_count) + if __name__ == '__main__': unittest.main() From 9a644feed41ef40112b3515bf17c2f40176ed65a Mon Sep 17 00:00:00 2001 From: Hans Krijger Date: Mon, 6 Aug 2018 14:09:33 -0700 Subject: [PATCH 5/7] add daemon unit tests --- tests/daemon/test_daemon.py | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/daemon/test_daemon.py b/tests/daemon/test_daemon.py index 5b9ea75778..eb51d0e372 100644 --- a/tests/daemon/test_daemon.py +++ b/tests/daemon/test_daemon.py @@ -14,9 +14,11 @@ # # Requires Python 2.6+ and Openssl 1.0+ # +from multiprocessing import Process from azurelinuxagent.daemon import * from azurelinuxagent.daemon.main import OPENSSL_FIPS_ENVIRONMENT +from azurelinuxagent.pa.provision.default import ProvisionHandler from tests.tools import * @@ -83,6 +85,50 @@ def test_does_not_set_openssl_fips(self, _, __): daemon_handler.run() self.assertFalse(OPENSSL_FIPS_ENVIRONMENT in os.environ) + @patch('azurelinuxagent.ga.update.UpdateHandler.run_latest') + @patch('azurelinuxagent.pa.provision.default.ProvisionHandler.run') + @patch('azurelinuxagent.pa.provision.get_provision_handler', return_value=ProvisionHandler()) + def test_daemon_agent_enabled(self, _, patch_run_provision, patch_run_latest): + """ + Agent should run normally when no disable_agent is found + """ + + self.assertFalse(os.path.exists(conf.get_disable_agent_file_path())) + daemon_handler = get_daemon_handler() + + def stop_daemon(child_args): + daemon_handler.running = False + + patch_run_latest.side_effect = stop_daemon + daemon_handler.run() + + self.assertEqual(1, patch_run_provision.call_count) + self.assertEqual(1, patch_run_latest.call_count) + + @patch('azurelinuxagent.ga.update.UpdateHandler.run_latest', side_effect=AgentTestCase.fail) + @patch('azurelinuxagent.pa.provision.default.ProvisionHandler.run', side_effect=ProvisionHandler.write_agent_disabled) + @patch('azurelinuxagent.pa.provision.get_provision_handler', return_value=ProvisionHandler()) + def test_daemon_agent_disabled(self, _, __, patch_run_latest): + """ + Agent should provision, then sleep forever when disable_agent is found + """ + + # file is created by provisioning handler + self.assertFalse(os.path.exists(conf.get_disable_agent_file_path())) + daemon_handler = get_daemon_handler() + + # we need to assert this thread will sleep forever, so fork it + daemon = Process(target=daemon_handler.run) + daemon.start() + daemon.join(timeout=5) + + self.assertTrue(daemon.is_alive()) + daemon.terminate() + + # disable_agent was written, run_latest was not called + self.assertTrue(os.path.exists(conf.get_disable_agent_file_path())) + self.assertEqual(0, patch_run_latest.call_count) + if __name__ == '__main__': unittest.main() From 588edd3da65812bcc841249d473db2bcef8d6986 Mon Sep 17 00:00:00 2001 From: Hans Krijger Date: Wed, 8 Aug 2018 13:16:03 -0700 Subject: [PATCH 6/7] updated config with warning --- config/alpine/waagent.conf | 3 ++- config/bigip/waagent.conf | 3 ++- config/coreos/waagent.conf | 3 ++- config/debian/waagent.conf | 3 ++- config/freebsd/waagent.conf | 3 ++- config/gaia/waagent.conf | 3 ++- config/suse/waagent.conf | 3 ++- config/ubuntu/waagent.conf | 3 ++- config/waagent.conf | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/config/alpine/waagent.conf b/config/alpine/waagent.conf index 64207e050c..f45ac1fcd1 100644 --- a/config/alpine/waagent.conf +++ b/config/alpine/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/bigip/waagent.conf b/config/bigip/waagent.conf index 9d8b2023d5..bdf7a8159a 100644 --- a/config/bigip/waagent.conf +++ b/config/bigip/waagent.conf @@ -16,7 +16,8 @@ Role.TopologyConsumer=None # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/coreos/waagent.conf b/config/coreos/waagent.conf index a17be97552..942d922b6c 100644 --- a/config/coreos/waagent.conf +++ b/config/coreos/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/debian/waagent.conf b/config/debian/waagent.conf index 8cf900c46a..60c443756a 100644 --- a/config/debian/waagent.conf +++ b/config/debian/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/freebsd/waagent.conf b/config/freebsd/waagent.conf index 0f542b1c69..62c66a6cce 100644 --- a/config/freebsd/waagent.conf +++ b/config/freebsd/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/gaia/waagent.conf b/config/gaia/waagent.conf index 9c2d68254a..83d0162801 100644 --- a/config/gaia/waagent.conf +++ b/config/gaia/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/suse/waagent.conf b/config/suse/waagent.conf index edc5d8218b..242aa61429 100644 --- a/config/suse/waagent.conf +++ b/config/suse/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/ubuntu/waagent.conf b/config/ubuntu/waagent.conf index 58997c9b5c..ff4ce8d043 100644 --- a/config/ubuntu/waagent.conf +++ b/config/ubuntu/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=n -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision diff --git a/config/waagent.conf b/config/waagent.conf index 367e3e8639..448c26c22b 100644 --- a/config/waagent.conf +++ b/config/waagent.conf @@ -5,7 +5,8 @@ # Enable instance creation Provisioning.Enabled=y -# Enable extension handling +# Enable extension handling. Do not disable this unless you do not need password reset, +# backup, monitoring, or any extension handling whatsoever. Extensions.Enabled=y # Rely on cloud-init to provision From f194c7d34bc89558652bde76398bca247fe55a40 Mon Sep 17 00:00:00 2001 From: Hans Krijger Date: Wed, 8 Aug 2018 13:54:00 -0700 Subject: [PATCH 7/7] added more info the to README regardling extensions enabled --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 625fb199fc..f0467d9db3 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,12 @@ agent. Valid values are "y" or "n". If extension handling is disabled, the goal will still be processed and VM status is still reported, but only every 5 minutes. Extension config within the goal state will be ignored. Note that functionality such as password reset, ssh key updates and backups depend on extensions. Only disable this -if you do not need extensions at all. +if you do not need extensions at all. + +_Note_: disabling extensions in this manner is not the same as running completely +without the agent. In order to do that, the `provisionVMAgent` flag must be set at +provisioning time, via whichever API is being used. We will provide more details on +this on our wiki when it is generally available. #### __Provisioning.Enabled__