diff --git a/CODEOWNERS b/CODEOWNERS index aebbe4c94..35db8d37b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,6 +18,13 @@ /azurelinuxagent/common/rdma.py @longlimsft /azurelinuxagent/pa/rdma/ @longlimsft +# +# Azure Container Linux (ACL) +# +/azurelinuxagent/common/osutil/acl.py @mayankfz +/config/acl/ @mayankfz +/init/acl/ @mayankfz + # # Linux Agent team # diff --git a/azurelinuxagent/common/osutil/acl.py b/azurelinuxagent/common/osutil/acl.py new file mode 100644 index 000000000..9da215b06 --- /dev/null +++ b/azurelinuxagent/common/osutil/acl.py @@ -0,0 +1,79 @@ +# +# Copyright 2018 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.6+ and Openssl 1.0+ +# +# Azure Container Linux (ACL) is an immutable, sysext-based distro derived +# from Flatcar. This osutil is a standalone copy of MarinerOSUtil with +# ACL-specific overrides so that future Azure-Linux changes do not +# inadvertently affect the immutable ACL image. +# + +from azurelinuxagent.common.osutil.default import DefaultOSUtil + + +class AclOSUtil(DefaultOSUtil): + def __init__(self): + super(AclOSUtil, self).__init__() + self.jit_enabled = True + + @staticmethod + def get_systemd_unit_file_install_path(): + # ACL delivers waagent as a sysext; /usr is read-only. + # Writable systemd units must go to /etc/systemd/system. + return "/etc/systemd/system" + + @staticmethod + def get_agent_bin_path(): + return "/usr/bin" + + def is_dhcp_enabled(self): + return True + + def start_network(self): + self._run_command_without_raising(["systemctl", "start", "systemd-networkd"], log_error=False) + + def restart_if(self, ifname=None, retries=None, wait=None): + self._run_command_without_raising(["systemctl", "restart", "systemd-networkd"]) + + def restart_ssh_service(self): + # ACL uses sshd.socket for socket-activated SSH (similar to + # Flatcar/CoreOS). Restarting sshd.service would conflict with + # the active sshd.socket. + pass + + def stop_dhcp_service(self): + self._run_command_without_raising(["systemctl", "stop", "systemd-networkd"], log_error=False) + + def start_dhcp_service(self): + self._run_command_without_raising(["systemctl", "start", "systemd-networkd"], log_error=False) + + def start_agent_service(self): + self._run_command_without_raising(["systemctl", "start", "{0}".format(self.service_name)], log_error=False) + + def stop_agent_service(self): + self._run_command_without_raising(["systemctl", "stop", "{0}".format(self.service_name)], log_error=False) + + def register_agent_service(self): + self._run_command_without_raising(["systemctl", "enable", "{0}".format(self.service_name)], log_error=False) + + def unregister_agent_service(self): + self._run_command_without_raising(["systemctl", "disable", "{0}".format(self.service_name)], log_error=False) + + def get_dhcp_pid(self): + return self._get_dhcp_pid(["pidof", "systemd-networkd"]) + + def conf_sshd(self, disable_password): + pass diff --git a/azurelinuxagent/common/osutil/factory.py b/azurelinuxagent/common/osutil/factory.py index 60975b942..af4768069 100644 --- a/azurelinuxagent/common/osutil/factory.py +++ b/azurelinuxagent/common/osutil/factory.py @@ -19,6 +19,7 @@ import azurelinuxagent.common.logger as logger from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_CODE_NAME, DISTRO_VERSION, DISTRO_FULL_NAME from azurelinuxagent.common.utils.distro_version import DistroVersion +from .acl import AclOSUtil from .alpine import AlpineOSUtil from .arch import ArchUtil from .bigip import BigIpOSUtil @@ -146,6 +147,12 @@ def _get_osutil(distro_name, distro_code_name, distro_version, distro_full_name) if distro_name == "iosxe": return IosxeOSUtil() + # distro_name is same for azurelinux/mariner and Azure Container Linux (ACL). + # ACL is a flavor of Azure Linux, differentiation is based on distro_full_name. + # For Azure Container Linux, distro_name will be made "azurecontainerlinux". + if distro_name == "azurecontainerlinux": + return AclOSUtil() + if distro_name in ["mariner", "azurelinux"]: return MarinerOSUtil() diff --git a/azurelinuxagent/common/version.py b/azurelinuxagent/common/version.py index 16e2c970c..433a402ef 100644 --- a/azurelinuxagent/common/version.py +++ b/azurelinuxagent/common/version.py @@ -137,6 +137,14 @@ def get_distro(): if os.path.exists("/etc/mariner-release"): osinfo[0] = "mariner" + if os.path.exists("/etc/os-release"): + try: + with open("/etc/os-release", "r") as f: + if re.search(r'^VARIANT_ID=azurecontainerlinux$', f.read(), re.MULTILINE): + osinfo[0] = "azurecontainerlinux" + except Exception: + pass + # The platform.py lib has issue with detecting BIG-IP linux distribution. # Merge the following patch provided by F5. if os.path.exists("/shared/vadc"): diff --git a/config/acl/waagent.conf b/config/acl/waagent.conf new file mode 100644 index 000000000..497bd432a --- /dev/null +++ b/config/acl/waagent.conf @@ -0,0 +1,37 @@ +# +# Microsoft Azure Linux Agent Configuration (ACL) +# + +Extensions.Enabled=y + +# Auto-detect the provisioning agent (coreos-cloudinit or waagent). +Provisioning.Agent=auto +Provisioning.DeleteRootPassword=n +Provisioning.RegenerateSshHostKeyPair=n +Provisioning.SshHostKeyPairType=auto +Provisioning.MonitorHostName=y +Provisioning.DecodeCustomData=y +Provisioning.ExecuteCustomData=n +Provisioning.AllowResetSysUser=n + +ResourceDisk.Format=y +ResourceDisk.Filesystem=ext4 +ResourceDisk.MountPoint=/mnt/resource +ResourceDisk.EnableSwap=n +ResourceDisk.SwapSizeMB=0 +ResourceDisk.MountOptions=None + +LBProbeResponder=y + +Logs.Verbose=n +# Logs.Console=y + +OS.EnableFIPS=n +OS.SshDir=/etc/ssh +OS.RootDeviceScsiTimeout=300 +OS.OpensslPath=None + +#AutoUpdate.Enabled=n + +# Add firewall rules to protect access to Azure host node services +OS.EnableFirewall=y diff --git a/init/acl/10-waagent-sysext.conf b/init/acl/10-waagent-sysext.conf new file mode 100644 index 000000000..8a5c80c4c --- /dev/null +++ b/init/acl/10-waagent-sysext.conf @@ -0,0 +1,13 @@ +# Drop-in for multi-user.target on Azure Container Linux (ACL). +# +# WALinuxAgent is delivered as a systemd-sysext image. Sysext units are +# only visible after systemd-sysext.service merges the overlay, which +# happens *after* the default target is already queued. Without an +# explicit "Upholds=", systemd has no dependency edge to pull +# waagent.service into the boot transaction. +# +# "Upholds=" (systemd 249+) is a soft requirement: it starts the unit +# whenever multi-user.target is active but does not fail the target if +# waagent.service cannot start. +[Unit] +Upholds=waagent.service \ No newline at end of file diff --git a/init/acl/waagent.service b/init/acl/waagent.service new file mode 100644 index 000000000..570c1dc6d --- /dev/null +++ b/init/acl/waagent.service @@ -0,0 +1,18 @@ +[Unit] +Description=Microsoft Azure Linux Agent (ACL) +Wants=network-online.target sshd.service sshd-keygen.service +After=network-online.target sshd-keygen.service systemd-sysext.service + +ConditionFileIsExecutable=/usr/bin/waagent +ConditionPathExists=/etc/waagent.conf + +[Service] +Type=simple + +ExecStart=/usr/bin/python -u /usr/bin/waagent -daemon + +Restart=always +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/setup.py b/setup.py index a3b7b71f9..946d18a50 100755 --- a/setup.py +++ b/setup.py @@ -164,6 +164,16 @@ def get_data_files(name, version, fullname): # pylint: disable=R0912 src=["config/clearlinux/waagent.conf"]) set_systemd_files(data_files, dest=systemd_dir_path, src=["init/clearlinux/waagent.service"]) + elif name == 'azurecontainerlinux': + set_bin_files(data_files, dest=agent_bin_path) + set_logrotate_files(data_files) + set_conf_files(data_files, dest="/etc", + src=["config/acl/waagent.conf"]) + set_systemd_files(data_files, dest=systemd_dir_path, + src=["init/acl/waagent.service"]) + multi_user_target_drop_in_dir = os.path.join(systemd_dir_path, "multi-user.target.d") + set_systemd_files(data_files, dest=multi_user_target_drop_in_dir, + src=["init/acl/10-waagent-sysext.conf"]) elif name in ["mariner", "azurelinux"]: set_bin_files(data_files, dest=agent_bin_path) set_conf_files(data_files, dest="/etc", diff --git a/tests/common/osutil/test_factory.py b/tests/common/osutil/test_factory.py index 5bfb867d4..542634244 100644 --- a/tests/common/osutil/test_factory.py +++ b/tests/common/osutil/test_factory.py @@ -34,6 +34,7 @@ from azurelinuxagent.common.osutil.suse import SUSEOSUtil, SUSE11OSUtil from azurelinuxagent.common.osutil.ubuntu import UbuntuOSUtil, Ubuntu12OSUtil, Ubuntu14OSUtil, \ UbuntuSnappyOSUtil, Ubuntu16OSUtil, Ubuntu18OSUtil +from azurelinuxagent.common.osutil.acl import AclOSUtil from tests.lib.tools import AgentTestCase, patch @@ -158,6 +159,14 @@ def test_get_osutil_it_should_return_coreos(self): self.assertTrue(isinstance(ret, CoreOSUtil)) self.assertEqual(ret.get_service_name(), "waagent") + def test_get_osutil_it_should_return_acl(self): + ret = _get_osutil(distro_name="azurecontainerlinux", + distro_code_name="", + distro_version="3.0", + distro_full_name="Microsoft Azure Container Linux") + self.assertTrue(isinstance(ret, AclOSUtil)) + self.assertEqual(ret.get_service_name(), "waagent") + def test_get_osutil_it_should_return_suse(self): ret = _get_osutil(distro_name="suse", distro_code_name="",