diff options
-rw-r--r-- | man/en/sos-collect.1 | 30 | ||||
-rw-r--r-- | sos/collector/__init__.py | 17 | ||||
-rw-r--r-- | sos/collector/sosnode.py | 40 | ||||
-rw-r--r-- | sos/policies/distros/__init__.py | 16 | ||||
-rw-r--r-- | sos/policies/distros/redhat.py | 25 | ||||
-rw-r--r-- | sos/policies/runtimes/__init__.py | 25 |
6 files changed, 140 insertions, 13 deletions
diff --git a/man/en/sos-collect.1 b/man/en/sos-collect.1 index 286bfe71..cdbc3257 100644 --- a/man/en/sos-collect.1 +++ b/man/en/sos-collect.1 @@ -26,6 +26,11 @@ sos collect \- Collect sosreports from multiple (cluster) nodes [\-\-no\-pkg\-check] [\-\-no\-local] [\-\-master MASTER] + [\-\-image IMAGE] + [\-\-force-pull-image] + [\-\-registry-user USER] + [\-\-registry-password PASSWORD] + [\-\-registry-authfile FILE] [\-o ONLY_PLUGINS] [\-p SSH_PORT] [\-\-password] @@ -245,6 +250,31 @@ Specify a master node for the cluster. If provided, then sos collect will check the master node, not localhost, for determining the type of cluster in use. .TP +\fB\-\-image IMAGE\fR +Specify an image to use for the temporary container created for collections on +containerized host, if you do not want to use the default image specifed by the +host's policy. Note that this should include the registry. +.TP +\fB\-\-force-pull-image\fR +Use this option to force the container runtime to pull the specified image (even +if it is the policy default image) even if the image already exists on the host. +This may be useful to update an older container image on containerized hosts. +.TP +\fB\-\-registry-user USER\fR +Specify the username to authenticate to the registry with in order to pull the container +image +.TP +\fB\-\-registry-password PASSWORD\fR +Specify the password to authenticate to the registry with in order to pull the container +image. If no password is required, leave this blank. +.TP +\fB\-\-registry-authfile FILE\fR +Specify the filename to use for providing authentication credentials to the registry +to pull the container image. + +Note that this file must exist on the node(s) performing the pull operations, not the +node from which \fBsos collect\fR was run. +.TP \fB\-o\fR ONLY_PLUGINS, \fB\-\-only\-plugins\fR ONLY_PLUGINS Sosreport option. Run ONLY the plugins listed. diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py index 1c742cf5..0624caad 100644 --- a/sos/collector/__init__.py +++ b/sos/collector/__init__.py @@ -63,6 +63,7 @@ class SoSCollector(SoSComponent): 'encrypt_pass': '', 'group': None, 'image': '', + 'force_pull_image': False, 'jobs': 4, 'keywords': [], 'keyword_file': None, @@ -84,6 +85,9 @@ class SoSCollector(SoSComponent): 'plugin_timeout': None, 'cmd_timeout': None, 'preset': '', + 'registry_user': None, + 'registry_password': None, + 'registry_authfile': None, 'save_group': '', 'since': '', 'skip_commands': [], @@ -319,6 +323,19 @@ class SoSCollector(SoSComponent): collect_grp.add_argument('--image', help=('Specify the container image to use for' ' containerized hosts.')) + collect_grp.add_argument('--force-pull-image', '--pull', default=False, + action='store_true', + help='Force pull the container image even if ' + 'it already exists on the host') + collect_grp.add_argument('--registry-user', default=None, + help='Username to authenticate to the ' + 'registry with for pulling an image') + collect_grp.add_argument('--registry-password', default=None, + help='Password to authenticate to the ' + 'registry with for pulling an image') + collect_grp.add_argument('--registry-authfile', default=None, + help='Use this authfile to provide registry ' + 'authentication when pulling an image') collect_grp.add_argument('-i', '--ssh-key', help='Specify an ssh key') collect_grp.add_argument('-j', '--jobs', default=4, type=int, help='Number of concurrent nodes to collect') diff --git a/sos/collector/sosnode.py b/sos/collector/sosnode.py index 48693342..d1c11824 100644 --- a/sos/collector/sosnode.py +++ b/sos/collector/sosnode.py @@ -134,9 +134,27 @@ class SosNode(): """If the host is containerized, create the container we'll be using """ if self.host.containerized: - res = self.run_command(self.host.create_sos_container(), - need_root=True) - if res['status'] in [0, 125]: # 125 means container exists + cmd = self.host.create_sos_container( + image=self.opts.image, + auth=self.get_container_auth(), + force_pull=self.opts.force_pull_image + ) + res = self.run_command(cmd, need_root=True) + if res['status'] in [0, 125]: + if res['status'] == 125: + if 'unable to retrieve auth token' in res['stdout']: + self.log_error( + "Could not pull image. Provide either a username " + "and password or authfile" + ) + raise Exception + elif 'unknown: Not found' in res['stdout']: + self.log_error('Specified image not found on registry') + raise Exception + # 'name exists' with code 125 means the container was + # created successfully, so ignore it. + # initial creations leads to an exited container, restarting it + # here will keep it alive for us to exec through ret = self.run_command(self.host.restart_sos_container(), need_root=True) if ret['status'] == 0: @@ -152,6 +170,20 @@ class SosNode(): % res['stdout']) raise Exception + def get_container_auth(self): + """Determine what the auth string should be to pull the image used to + deploy our temporary container + """ + if self.opts.registry_user: + return self.host.runtimes['default'].fmt_registry_credentials( + self.opts.registry_user, + self.opts.registry_password + ) + else: + return self.host.runtimes['default'].fmt_registry_authfile( + self.opts.registry_authfile or self.host.container_authfile + ) + def file_exists(self, fname): """Checks for the presence of fname on the remote node""" if not self.local: @@ -343,7 +375,7 @@ class SosNode(): % self.commons['policy'].distro) return self.commons['policy'] host = load(cache={}, sysroot=self.opts.sysroot, init=InitSystem(), - probe_runtime=False, remote_exec=self.ssh_cmd, + probe_runtime=True, remote_exec=self.ssh_cmd, remote_check=self.read_file('/etc/os-release')) if host: self.log_info("loaded policy %s for host" % host.distro) diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py index 9fe31513..f5b9fd5b 100644 --- a/sos/policies/distros/__init__.py +++ b/sos/policies/distros/__init__.py @@ -62,6 +62,7 @@ class LinuxPolicy(Policy): sos_bin_path = '/usr/bin' sos_container_name = 'sos-collector-tmp' container_version_command = None + container_authfile = None def __init__(self, sysroot=None, init=None, probe_runtime=True): super(LinuxPolicy, self).__init__(sysroot=sysroot, @@ -626,13 +627,26 @@ class LinuxPolicy(Policy): """ return '' - def create_sos_container(self): + def create_sos_container(self, image=None, auth=None, force_pull=False): """Returns the command that will create the container that will be used for running commands inside a container on hosts that require it. This will use the container runtime defined for the host type to launch a container. From there, we use the defined runtime to exec into the container's namespace. + + :param image: The name of the image if not using the policy default + :type image: ``str`` or ``None`` + + :param auth: The auth string required by the runtime to pull an + image from the registry + :type auth: ``str`` or ``None`` + + :param force_pull: Should the runtime forcibly pull the image + :type force_pull: ``bool`` + + :returns: The command to execute to launch the temp container + :rtype: ``str`` """ return '' diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py index 241d3f13..20afbcc4 100644 --- a/sos/policies/distros/redhat.py +++ b/sos/policies/distros/redhat.py @@ -452,15 +452,19 @@ support representative. return self.find_preset(ATOMIC) - def create_sos_container(self): + def create_sos_container(self, image=None, auth=None, force_pull=False): _cmd = ("{runtime} run -di --name {name} --privileged --ipc=host" " --net=host --pid=host -e HOST=/host -e NAME={name} -e " - "IMAGE={image} -v /run:/run -v /var/log:/var/log -v " + "IMAGE={image} {pull} -v /run:/run -v /var/log:/var/log -v " "/etc/machine-id:/etc/machine-id -v " - "/etc/localtime:/etc/localtime -v /:/host {image}") + "/etc/localtime:/etc/localtime -v /:/host {auth} {image}") + _image = image or self.container_image + _pull = '--pull=always' if force_pull else '' return _cmd.format(runtime=self.container_runtime, name=self.sos_container_name, - image=self.container_image) + image=_image, + pull=_pull, + auth=auth or '') def set_cleanup_cmd(self): return 'docker rm --force sos-collector-tmp' @@ -482,6 +486,7 @@ support representative. container_image = 'registry.redhat.io/rhel8/support-tools' sos_path_strip = '/host' container_version_command = 'rpm -q sos' + container_authfile = '/var/lib/kubelet/config.json' def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): @@ -511,15 +516,19 @@ support representative. # RH OCP environments. return self.find_preset(RHOCP) - def create_sos_container(self): + def create_sos_container(self, image=None, auth=None, force_pull=False): _cmd = ("{runtime} run -di --name {name} --privileged --ipc=host" " --net=host --pid=host -e HOST=/host -e NAME={name} -e " - "IMAGE={image} -v /run:/run -v /var/log:/var/log -v " + "IMAGE={image} {pull} -v /run:/run -v /var/log:/var/log -v " "/etc/machine-id:/etc/machine-id -v " - "/etc/localtime:/etc/localtime -v /:/host {image}") + "/etc/localtime:/etc/localtime -v /:/host {auth} {image}") + _image = image or self.container_image + _pull = '--pull=always' if force_pull else '' return _cmd.format(runtime=self.container_runtime, name=self.sos_container_name, - image=self.container_image) + image=_image, + pull=_pull, + auth=auth or '') def set_cleanup_cmd(self): return 'podman rm --force %s' % self.sos_container_name diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py index 1a61b644..f28d6a1d 100644 --- a/sos/policies/runtimes/__init__.py +++ b/sos/policies/runtimes/__init__.py @@ -157,6 +157,31 @@ class ContainerRuntime(): quoted_cmd = cmd return "%s %s %s" % (self.run_cmd, container, quoted_cmd) + def fmt_registry_credentials(self, username, password): + """Format a string to pass to the 'run' command of the runtime to + enable authorization for pulling the image during `sos collect`, if + needed using username and optional password creds + + :param username: The name of the registry user + :type username: ``str`` + + :param password: The password of the registry user + :type password: ``str`` or ``None`` + + :returns: The string to use to enable a run command to pull the image + :rtype: ``str`` + """ + return "--creds=%s%s" % (username, ':' + password if password else '') + + def fmt_registry_authfile(self, authfile): + """Format a string to pass to the 'run' command of the runtime to + enable authorization for pulling the image during `sos collect`, if + needed using an authfile. + """ + if authfile: + return "--authfile %s" % authfile + return '' + def get_logs_command(self, container): """Get the command string used to dump container logs from the runtime |