Blame SOURCES/sos-bz2058279-ocp-backports.patch

46fb0a
From d0f9d507b0ec63c9e8f3e5d7b6507d9d0f97c038 Mon Sep 17 00:00:00 2001
46fb0a
From: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
Date: Tue, 15 Feb 2022 16:24:47 -0500
46fb0a
Subject: [PATCH] [runtimes] Allow container IDs to be used with
46fb0a
 `container_exists()`
46fb0a
46fb0a
As container runtimes can interchange container names and container IDs,
46fb0a
sos should also allow the use of container IDs when checking for the
46fb0a
presence of a given container.
46fb0a
46fb0a
In particular, this change unblocks the use of `Plugin.exec_cmd()` when
46fb0a
used in conjunction with `Plugin.get_container_by_name()` to pick a
46fb0a
container based on a provided regex that the container name may match.
46fb0a
46fb0a
Related: #2856
46fb0a
46fb0a
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
---
46fb0a
 sos/policies/runtimes/__init__.py | 17 +++++++++++++++++
46fb0a
 sos/report/plugins/__init__.py    |  6 +++---
46fb0a
 2 files changed, 20 insertions(+), 3 deletions(-)
46fb0a
46fb0a
diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py
46fb0a
index 5ac67354..d2837349 100644
46fb0a
--- a/sos/policies/runtimes/__init__.py
46fb0a
+++ b/sos/policies/runtimes/__init__.py
46fb0a
@@ -147,6 +147,23 @@ class ContainerRuntime():
46fb0a
                     vols.append(ent[-1])
46fb0a
         return vols
46fb0a
 
46fb0a
+    def container_exists(self, container):
46fb0a
+        """Check if a given container ID or name exists on the system from the
46fb0a
+        perspective of the container runtime.
46fb0a
+
46fb0a
+        Note that this will only check _running_ containers
46fb0a
+
46fb0a
+        :param container:       The name or ID of the container
46fb0a
+        :type container:        ``str``
46fb0a
+
46fb0a
+        :returns:               True if the container exists, else False
46fb0a
+        :rtype:                 ``bool``
46fb0a
+        """
46fb0a
+        for _contup in self.containers:
46fb0a
+            if container in _contup:
46fb0a
+                return True
46fb0a
+        return False
46fb0a
+
46fb0a
     def fmt_container_cmd(self, container, cmd, quotecmd):
46fb0a
         """Format a command to run inside a container using the runtime
46fb0a
 
46fb0a
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
46fb0a
index 2988be08..cc5cb65b 100644
46fb0a
--- a/sos/report/plugins/__init__.py
46fb0a
+++ b/sos/report/plugins/__init__.py
46fb0a
@@ -2593,7 +2593,7 @@ class Plugin():
46fb0a
         """If a container runtime is present, check to see if a container with
46fb0a
         a given name is currently running
46fb0a
 
46fb0a
-        :param name:    The name of the container to check presence of
46fb0a
+        :param name:    The name or ID of the container to check presence of
46fb0a
         :type name: ``str``
46fb0a
 
46fb0a
         :returns: ``True`` if `name` exists, else ``False``
46fb0a
@@ -2601,8 +2601,8 @@ class Plugin():
46fb0a
         """
46fb0a
         _runtime = self._get_container_runtime()
46fb0a
         if _runtime is not None:
46fb0a
-            con = _runtime.get_container_by_name(name)
46fb0a
-            return con is not None
46fb0a
+            return (_runtime.container_exists(name) or
46fb0a
+                    _runtime.get_container_by_name(name) is not None)
46fb0a
         return False
46fb0a
 
46fb0a
     def get_all_containers_by_regex(self, regex, get_all=False):
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From 2ae16e0245e1b01b8547e507abb69c11871a8467 Mon Sep 17 00:00:00 2001
46fb0a
From: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
Date: Mon, 21 Feb 2022 14:37:09 -0500
46fb0a
Subject: [PATCH] [sosnode] Handle downstream versioning for runtime option
46fb0a
 check
46fb0a
46fb0a
First, adds parsing and formatting for an sos installation's release
46fb0a
version according to the loaded package manager for that node.
46fb0a
46fb0a
Adds a fallback version check for 4.2-13 for RHEL downstreams that
46fb0a
backport the `container-runtime` option into sos-4.2.
46fb0a
46fb0a
Carry this in upstream to account for use cases where a workstation used
46fb0a
to run `collect` from may be from a different stream than those used by
46fb0a
cluster nodes.
46fb0a
46fb0a
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
---
46fb0a
 sos/collector/sosnode.py | 60 ++++++++++++++++++++++++++++++++++------
46fb0a
 1 file changed, 51 insertions(+), 9 deletions(-)
46fb0a
46fb0a
diff --git a/sos/collector/sosnode.py b/sos/collector/sosnode.py
46fb0a
index 7bbe0cd1..d9b998b0 100644
46fb0a
--- a/sos/collector/sosnode.py
46fb0a
+++ b/sos/collector/sosnode.py
46fb0a
@@ -275,21 +275,34 @@ class SosNode():
46fb0a
     def _load_sos_info(self):
46fb0a
         """Queries the node for information about the installed version of sos
46fb0a
         """
46fb0a
+        ver = None
46fb0a
+        rel = None
46fb0a
         if self.host.container_version_command is None:
46fb0a
             pkg = self.host.package_manager.pkg_version(self.host.sos_pkg_name)
46fb0a
             if pkg is not None:
46fb0a
                 ver = '.'.join(pkg['version'])
46fb0a
-                self.sos_info['version'] = ver
46fb0a
+                if pkg['release']:
46fb0a
+                    rel = pkg['release']
46fb0a
+
46fb0a
         else:
46fb0a
             # use the containerized policy's command
46fb0a
             pkgs = self.run_command(self.host.container_version_command,
46fb0a
                                     use_container=True, need_root=True)
46fb0a
             if pkgs['status'] == 0:
46fb0a
-                ver = pkgs['output'].strip().split('-')[1]
46fb0a
-                if ver:
46fb0a
-                    self.sos_info['version'] = ver
46fb0a
-            else:
46fb0a
-                self.sos_info['version'] = None
46fb0a
+                _, ver, rel = pkgs['output'].strip().split('-')
46fb0a
+
46fb0a
+        if ver:
46fb0a
+            if len(ver.split('.')) == 2:
46fb0a
+                # safeguard against maintenance releases throwing off the
46fb0a
+                # comparison by LooseVersion
46fb0a
+                ver += '.0'
46fb0a
+            try:
46fb0a
+                ver += '-%s' % rel.split('.')[0]
46fb0a
+            except Exception as err:
46fb0a
+                self.log_debug("Unable to fully parse sos release: %s" % err)
46fb0a
+
46fb0a
+        self.sos_info['version'] = ver
46fb0a
+
46fb0a
         if self.sos_info['version']:
46fb0a
             self.log_info('sos version is %s' % self.sos_info['version'])
46fb0a
         else:
46fb0a
@@ -381,9 +394,37 @@ class SosNode():
46fb0a
         """Checks to see if the sos installation on the node is AT LEAST the
46fb0a
         given ver. This means that if the installed version is greater than
46fb0a
         ver, this will still return True
46fb0a
+
46fb0a
+        :param ver: Version number we are trying to verify is installed
46fb0a
+        :type ver:  ``str``
46fb0a
+
46fb0a
+        :returns:   True if installed version is at least ``ver``, else False
46fb0a
+        :rtype:     ``bool``
46fb0a
         """
46fb0a
-        return self.sos_info['version'] is not None and \
46fb0a
-            LooseVersion(self.sos_info['version']) >= ver
46fb0a
+        def _format_version(ver):
46fb0a
+            # format the version we're checking to a standard form of X.Y.Z-R
46fb0a
+            try:
46fb0a
+                _fver = ver.split('-')[0]
46fb0a
+                _rel = ''
46fb0a
+                if '-' in ver:
46fb0a
+                    _rel = '-' + ver.split('-')[-1].split('.')[0]
46fb0a
+                if len(_fver.split('.')) == 2:
46fb0a
+                    _fver += '.0'
46fb0a
+
46fb0a
+                return _fver + _rel
46fb0a
+            except Exception as err:
46fb0a
+                self.log_debug("Unable to format '%s': %s" % (ver, err))
46fb0a
+                return ver
46fb0a
+
46fb0a
+        _ver = _format_version(ver)
46fb0a
+
46fb0a
+        try:
46fb0a
+            _node_ver = LooseVersion(self.sos_info['version'])
46fb0a
+            _test_ver = LooseVersion(_ver)
46fb0a
+            return _node_ver >= _test_ver
46fb0a
+        except Exception as err:
46fb0a
+            self.log_error("Error checking sos version: %s" % err)
46fb0a
+            return False
46fb0a
 
46fb0a
     def is_installed(self, pkg):
46fb0a
         """Checks if a given package is installed on the node"""
46fb0a
@@ -587,7 +628,8 @@ class SosNode():
46fb0a
                 sos_opts.append('--cmd-timeout=%s'
46fb0a
                                 % quote(str(self.opts.cmd_timeout)))
46fb0a
 
46fb0a
-        if self.check_sos_version('4.3'):
46fb0a
+        # handle downstream versions that backported this option
46fb0a
+        if self.check_sos_version('4.3') or self.check_sos_version('4.2-13'):
46fb0a
             if self.opts.container_runtime != 'auto':
46fb0a
                 sos_opts.append(
46fb0a
                     "--container-runtime=%s" % self.opts.container_runtime
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From cc60fa5ee25bffed9203a4f786256185b7fe0115 Mon Sep 17 00:00:00 2001
46fb0a
From: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
Date: Tue, 15 Mar 2022 11:49:57 +0100
46fb0a
Subject: [PATCH] Add ovs datapath and groups collection commands Add
46fb0a
 ct-zone-list command for openshift-ovn
46fb0a
46fb0a
Signed-off-by: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
---
46fb0a
 sos/report/plugins/openshift_ovn.py | 4 ++++
46fb0a
 sos/report/plugins/openvswitch.py   | 3 +++
46fb0a
 2 files changed, 7 insertions(+)
46fb0a
46fb0a
diff --git a/sos/report/plugins/openshift_ovn.py b/sos/report/plugins/openshift_ovn.py
46fb0a
index 168f1dd3..b4787b8e 100644
46fb0a
--- a/sos/report/plugins/openshift_ovn.py
46fb0a
+++ b/sos/report/plugins/openshift_ovn.py
46fb0a
@@ -34,6 +34,10 @@ class OpenshiftOVN(Plugin, RedHatPlugin):
46fb0a
             'ovn-appctl -t /var/run/ovn/ovnsb_db.ctl ' +
46fb0a
             'cluster/status OVN_Southbound'],
46fb0a
             container='ovnkube-master')
46fb0a
+        self.add_cmd_output([
46fb0a
+            'ovs-appctl -t /var/run/ovn/ovn-controller.*.ctl ' +
46fb0a
+            'ct-zone-list'],
46fb0a
+            container='ovnkube-node')
46fb0a
         self.add_cmd_output([
46fb0a
             'ovs-appctl -t ovs-monitor-ipsec tunnels/show',
46fb0a
             'ipsec status',
46fb0a
diff --git a/sos/report/plugins/openvswitch.py b/sos/report/plugins/openvswitch.py
46fb0a
index 179d1532..159b0bd2 100644
46fb0a
--- a/sos/report/plugins/openvswitch.py
46fb0a
+++ b/sos/report/plugins/openvswitch.py
46fb0a
@@ -124,6 +124,8 @@ class OpenVSwitch(Plugin):
46fb0a
             "ovs-vsctl -t 5 list interface",
46fb0a
             # Capture OVS detailed information from all the bridges
46fb0a
             "ovs-vsctl -t 5 list bridge",
46fb0a
+            # Capture OVS datapath list
46fb0a
+            "ovs-vsctl -t 5 list datapath",
46fb0a
             # Capture DPDK queue to pmd mapping
46fb0a
             "ovs-appctl dpif-netdev/pmd-rxq-show",
46fb0a
             # Capture DPDK pmd stats
46fb0a
@@ -229,6 +231,7 @@ class OpenVSwitch(Plugin):
46fb0a
                     "ovs-ofctl queue-get-config %s" % br,
46fb0a
                     "ovs-ofctl queue-stats %s" % br,
46fb0a
                     "ovs-ofctl show %s" % br,
46fb0a
+                    "ovs-ofctl dump-groups %s" % br,
46fb0a
                 ])
46fb0a
 
46fb0a
                 # Flow protocols currently supported
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From af40be92f502b35fa9d39ce4d4fea7d80c367830 Mon Sep 17 00:00:00 2001
46fb0a
From: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
Date: Tue, 15 Mar 2022 13:09:55 +0100
46fb0a
Subject: [PATCH] Improve sos collect for OCP: 1. wait for sos tmp project to
46fb0a
 be deleted (just calling delete changes project state to Terminating, and
46fb0a
 running a new sos collect is not possible before this project is fully
46fb0a
 deleted) 2. use --retries flag to copy sos reports from the nodes more
46fb0a
 reliably. The flag has been recently added to kubectl, and the most reliable
46fb0a
 way to check if it's available or not is to check command error output for
46fb0a
 "unknown flag" substring
46fb0a
46fb0a
Signed-off-by: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
---
46fb0a
 sos/collector/clusters/ocp.py  | 5 +++++
46fb0a
 sos/collector/transports/oc.py | 6 +++++-
46fb0a
 2 files changed, 10 insertions(+), 1 deletion(-)
46fb0a
46fb0a
diff --git a/sos/collector/clusters/ocp.py b/sos/collector/clusters/ocp.py
46fb0a
index f1714239..9beb2f9b 100644
46fb0a
--- a/sos/collector/clusters/ocp.py
46fb0a
+++ b/sos/collector/clusters/ocp.py
46fb0a
@@ -123,6 +123,11 @@ class ocp(Cluster):
46fb0a
             if not ret['status'] == 0:
46fb0a
                 self.log_error("Error deleting temporary project: %s"
46fb0a
                                % ret['output'])
46fb0a
+            ret = self.exec_primary_cmd("oc wait namespace/%s --for=delete "
46fb0a
+                                        "--timeout=30s" % self.project)
46fb0a
+            if not ret['status'] == 0:
46fb0a
+                self.log_error("Error waiting for temporary project to be "
46fb0a
+                               "deleted: %s" % ret['output'])
46fb0a
             # don't leave the config on a non-existing project
46fb0a
             self.exec_primary_cmd("oc project default")
46fb0a
             self.project = None
46fb0a
diff --git a/sos/collector/transports/oc.py b/sos/collector/transports/oc.py
46fb0a
index 0fc9eee8..90a802b2 100644
46fb0a
--- a/sos/collector/transports/oc.py
46fb0a
+++ b/sos/collector/transports/oc.py
46fb0a
@@ -231,5 +231,9 @@ class OCTransport(RemoteTransport):
46fb0a
                 % (self.project, self.pod_name))
46fb0a
 
46fb0a
     def _retrieve_file(self, fname, dest):
46fb0a
-        cmd = self.run_oc("cp %s:%s %s" % (self.pod_name, fname, dest))
46fb0a
+        # check if --retries flag is available for given version of oc
46fb0a
+        result = self.run_oc("cp --retries", stderr=True)
46fb0a
+        flags = '' if "unknown flag" in result["output"] else '--retries=5'
46fb0a
+        cmd = self.run_oc("cp %s %s:%s %s"
46fb0a
+                          % (flags, self.pod_name, fname, dest))
46fb0a
         return cmd['status'] == 0
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From 3b0676b90ff65f20eaba3062775ff72b89386ffc Mon Sep 17 00:00:00 2001
46fb0a
From: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
Date: Tue, 22 Mar 2022 14:25:24 -0400
46fb0a
Subject: [PATCH] [Plugin] Allow plugins to define default command environment
46fb0a
 vars
46fb0a
46fb0a
Adds the ability for plugins to define a default set of environment vars
46fb0a
to pass to all commands executed by the plugin. This may be done either
46fb0a
via the new `set_default_cmd_environment()` or
46fb0a
`add_default_cmd_environment()` methods. The former will override any
46fb0a
previously set values, whereas the latter will add/update/modify any
46fb0a
existing values.
46fb0a
46fb0a
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
---
46fb0a
 sos/report/plugins/__init__.py                | 55 ++++++++++++++++++-
46fb0a
 .../plugin_tests/plugin_environment.py        | 44 +++++++++++++++
46fb0a
 .../fake_plugins/default_env_test.py          | 28 ++++++++++
46fb0a
 tests/unittests/plugin_tests.py               | 15 +++++
46fb0a
 4 files changed, 140 insertions(+), 2 deletions(-)
46fb0a
 create mode 100644 tests/report_tests/plugin_tests/plugin_environment.py
46fb0a
 create mode 100644 tests/test_data/fake_plugins/default_env_test.py
46fb0a
46fb0a
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
46fb0a
index 336b4d22..74b4f4be 100644
46fb0a
--- a/sos/report/plugins/__init__.py
46fb0a
+++ b/sos/report/plugins/__init__.py
46fb0a
@@ -571,6 +571,7 @@ class Plugin():
46fb0a
         self.manifest = None
46fb0a
         self.skip_files = commons['cmdlineopts'].skip_files
46fb0a
         self.skip_commands = commons['cmdlineopts'].skip_commands
46fb0a
+        self.default_environment = {}
46fb0a
 
46fb0a
         self.soslog = self.commons['soslog'] if 'soslog' in self.commons \
46fb0a
             else logging.getLogger('sos')
46fb0a
@@ -624,6 +625,52 @@ class Plugin():
46fb0a
         self.manifest.add_field('strings', {})
46fb0a
         self.manifest.add_field('containers', {})
46fb0a
 
46fb0a
+    def set_default_cmd_environment(self, env_vars):
46fb0a
+        """
46fb0a
+        Specify a collection of environment variables that should always be
46fb0a
+        passed to commands being executed by this plugin.
46fb0a
+
46fb0a
+        :param env_vars:    The environment variables and their values to set
46fb0a
+        :type env_vars:     ``dict{ENV_VAR_NAME: ENV_VAR_VALUE}``
46fb0a
+        """
46fb0a
+        if not isinstance(env_vars, dict):
46fb0a
+            raise TypeError(
46fb0a
+                "Environment variables for Plugin must be specified by dict"
46fb0a
+            )
46fb0a
+        self.default_environment = env_vars
46fb0a
+        self._log_debug("Default environment for all commands now set to %s"
46fb0a
+                        % self.default_environment)
46fb0a
+
46fb0a
+    def add_default_cmd_environment(self, env_vars):
46fb0a
+        """
46fb0a
+        Add or modify a specific environment variable in the set of default
46fb0a
+        environment variables used by this Plugin.
46fb0a
+
46fb0a
+        :param env_vars:    The environment variables to add to the current
46fb0a
+                            set of env vars in use
46fb0a
+        :type env_vars:     ``dict``
46fb0a
+        """
46fb0a
+        if not isinstance(env_vars, dict):
46fb0a
+            raise TypeError("Environment variables must be added via dict")
46fb0a
+        self._log_debug("Adding %s to default environment" % env_vars)
46fb0a
+        self.default_environment.update(env_vars)
46fb0a
+
46fb0a
+    def _get_cmd_environment(self, env=None):
46fb0a
+        """
46fb0a
+        Get the merged set of environment variables for a command about to be
46fb0a
+        executed by this plugin.
46fb0a
+
46fb0a
+        :returns: The set of env vars to use for a command
46fb0a
+        :rtype: ``dict``
46fb0a
+        """
46fb0a
+        if env is None:
46fb0a
+            return self.default_environment
46fb0a
+        if not isinstance(env, dict):
46fb0a
+            raise TypeError("Command env vars must be passed as dict")
46fb0a
+        _env = self.default_environment.copy()
46fb0a
+        _env.update(env)
46fb0a
+        return _env
46fb0a
+
46fb0a
     def timeout_from_options(self, optname, plugoptname, default_timeout):
46fb0a
         """Returns either the default [plugin|cmd] timeout value, the value as
46fb0a
         provided on the commandline via -k plugin.[|cmd-]timeout=value, or the
46fb0a
@@ -2258,6 +2305,8 @@ class Plugin():
46fb0a
 
46fb0a
         _tags = list(set(_tags))
46fb0a
 
46fb0a
+        _env = self._get_cmd_environment(env)
46fb0a
+
46fb0a
         if chroot or self.commons['cmdlineopts'].chroot == 'always':
46fb0a
             root = self.sysroot
46fb0a
         else:
46fb0a
@@ -2282,7 +2331,7 @@ class Plugin():
46fb0a
 
46fb0a
         result = sos_get_command_output(
46fb0a
             cmd, timeout=timeout, stderr=stderr, chroot=root,
46fb0a
-            chdir=runat, env=env, binary=binary, sizelimit=sizelimit,
46fb0a
+            chdir=runat, env=_env, binary=binary, sizelimit=sizelimit,
46fb0a
             poller=self.check_timeout, foreground=foreground,
46fb0a
             to_file=out_file
46fb0a
         )
46fb0a
@@ -2510,6 +2559,8 @@ class Plugin():
46fb0a
         else:
46fb0a
             root = None
46fb0a
 
46fb0a
+        _env = self._get_cmd_environment(env)
46fb0a
+
46fb0a
         if container:
46fb0a
             if self._get_container_runtime() is None:
46fb0a
                 self._log_info("Cannot run cmd '%s' in container %s: no "
46fb0a
@@ -2522,7 +2573,7 @@ class Plugin():
46fb0a
                                "container is running." % (cmd, container))
46fb0a
 
46fb0a
         return sos_get_command_output(cmd, timeout=timeout, chroot=root,
46fb0a
-                                      chdir=runat, binary=binary, env=env,
46fb0a
+                                      chdir=runat, binary=binary, env=_env,
46fb0a
                                       foreground=foreground, stderr=stderr)
46fb0a
 
46fb0a
     def _add_container_file_to_manifest(self, container, path, arcpath, tags):
46fb0a
diff --git a/tests/report_tests/plugin_tests/plugin_environment.py b/tests/report_tests/plugin_tests/plugin_environment.py
46fb0a
new file mode 100644
46fb0a
index 00000000..3158437a
46fb0a
--- /dev/null
46fb0a
+++ b/tests/report_tests/plugin_tests/plugin_environment.py
46fb0a
@@ -0,0 +1,44 @@
46fb0a
+# This file is part of the sos project: https://github.com/sosreport/sos
46fb0a
+#
46fb0a
+# This copyrighted material is made available to anyone wishing to use,
46fb0a
+# modify, copy, or redistribute it subject to the terms and conditions of
46fb0a
+# version 2 of the GNU General Public License.
46fb0a
+#
46fb0a
+# See the LICENSE file in the source distribution for further information.
46fb0a
+
46fb0a
+import os
46fb0a
+
46fb0a
+from sos_tests import StageTwoReportTest
46fb0a
+
46fb0a
+
46fb0a
+class PluginDefaultEnvironmentTest(StageTwoReportTest):
46fb0a
+    """
46fb0a
+    Ensure that being able to set a default set of environment variables is
46fb0a
+    working correctly and does not leave a lingering env var on the system
46fb0a
+
46fb0a
+    :avocado: tags=stageone
46fb0a
+    """
46fb0a
+
46fb0a
+    install_plugins = ['default_env_test']
46fb0a
+    sos_cmd = '-o default_env_test'
46fb0a
+
46fb0a
+    def test_environment_used_in_cmd(self):
46fb0a
+        self.assertFileHasContent(
46fb0a
+            'sos_commands/default_env_test/env_var_test',
46fb0a
+            'Does Linus play hockey?'
46fb0a
+        )
46fb0a
+
46fb0a
+    def test_environment_setting_logged(self):
46fb0a
+        self.assertSosLogContains(
46fb0a
+            'Default environment for all commands now set to'
46fb0a
+        )
46fb0a
+
46fb0a
+    def test_environment_not_set_on_host(self):
46fb0a
+        self.assertTrue('TORVALDS' not in os.environ)
46fb0a
+        self.assertTrue('GREATESTSPORT' not in os.environ)
46fb0a
+
46fb0a
+    def test_environment_not_captured(self):
46fb0a
+        # we should still have an empty environment file
46fb0a
+        self.assertFileCollected('environment')
46fb0a
+        self.assertFileNotHasContent('environment', 'TORVALDS')
46fb0a
+        self.assertFileNotHasContent('environment', 'GREATESTSPORT')
46fb0a
diff --git a/tests/test_data/fake_plugins/default_env_test.py b/tests/test_data/fake_plugins/default_env_test.py
46fb0a
new file mode 100644
46fb0a
index 00000000..d1d1fb78
46fb0a
--- /dev/null
46fb0a
+++ b/tests/test_data/fake_plugins/default_env_test.py
46fb0a
@@ -0,0 +1,28 @@
46fb0a
+# This file is part of the sos project: https://github.com/sosreport/sos
46fb0a
+#
46fb0a
+# This copyrighted material is made available to anyone wishing to use,
46fb0a
+# modify, copy, or redistribute it subject to the terms and conditions of
46fb0a
+# version 2 of the GNU General Public License.
46fb0a
+#
46fb0a
+# See the LICENSE file in the source distribution for further information.
46fb0a
+
46fb0a
+from sos.report.plugins import Plugin, IndependentPlugin
46fb0a
+
46fb0a
+
46fb0a
+class DefaultEnv(Plugin, IndependentPlugin):
46fb0a
+
46fb0a
+    plugin_name = 'default_env_test'
46fb0a
+    short_desc = 'Fake plugin to test default env var handling'
46fb0a
+
46fb0a
+    def setup(self):
46fb0a
+        self.set_default_cmd_environment({
46fb0a
+            'TORVALDS': 'Linus',
46fb0a
+            'GREATESTSPORT': 'hockey'
46fb0a
+        })
46fb0a
+
46fb0a
+        self.add_cmd_output(
46fb0a
+            "sh -c 'echo Does '$TORVALDS' play '$GREATESTSPORT'?'",
46fb0a
+            suggest_filename='env_var_test'
46fb0a
+        )
46fb0a
+
46fb0a
+        self.add_env_var(['TORVALDS', 'GREATESTSPORT'])
46fb0a
diff --git a/tests/unittests/plugin_tests.py b/tests/unittests/plugin_tests.py
46fb0a
index 0dfa243d..e469b78e 100644
46fb0a
--- a/tests/unittests/plugin_tests.py
46fb0a
+++ b/tests/unittests/plugin_tests.py
46fb0a
@@ -305,6 +305,21 @@ class PluginTests(unittest.TestCase):
46fb0a
         p.postproc()
46fb0a
         self.assertTrue(p.did_postproc)
46fb0a
 
46fb0a
+    def test_set_default_cmd_env(self):
46fb0a
+        p = MockPlugin({
46fb0a
+            'sysroot': self.sysroot,
46fb0a
+            'policy': LinuxPolicy(init=InitSystem(), probe_runtime=False),
46fb0a
+            'cmdlineopts': MockOptions(),
46fb0a
+            'devices': {}
46fb0a
+        })
46fb0a
+        e = {'TORVALDS': 'Linus'}
46fb0a
+        p.set_default_cmd_environment(e)
46fb0a
+        self.assertEquals(p.default_environment, e)
46fb0a
+        add_e = {'GREATESTSPORT': 'hockey'}
46fb0a
+        p.add_default_cmd_environment(add_e)
46fb0a
+        self.assertEquals(p.default_environment['GREATESTSPORT'], 'hockey')
46fb0a
+        self.assertEquals(p.default_environment['TORVALDS'], 'Linus')
46fb0a
+
46fb0a
 
46fb0a
 class AddCopySpecTests(unittest.TestCase):
46fb0a
 
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From 1e12325efaa500d304dcbfbeeb50e72ed0f938f5 Mon Sep 17 00:00:00 2001
46fb0a
From: Vladislav Walek <22072258+vwalek@users.noreply.github.com>
46fb0a
Date: Thu, 17 Mar 2022 14:10:26 -0700
46fb0a
Subject: [PATCH] [openshift] Adding ability to use the localhost.kubeconfig
46fb0a
 and KUBECONFIG env to use system:admin
46fb0a
46fb0a
Signed-off-by: Vladislav Walek <22072258+vwalek@users.noreply.github.com>
46fb0a
---
46fb0a
 sos/report/plugins/openshift.py | 45 +++++++++++++++++++++++++++++++--
46fb0a
 1 file changed, 43 insertions(+), 2 deletions(-)
46fb0a
46fb0a
diff --git a/sos/report/plugins/openshift.py b/sos/report/plugins/openshift.py
46fb0a
index 5ae38178..d643f04c 100644
46fb0a
--- a/sos/report/plugins/openshift.py
46fb0a
+++ b/sos/report/plugins/openshift.py
46fb0a
@@ -53,12 +53,19 @@ class Openshift(Plugin, RedHatPlugin):
46fb0a
     profiles = ('openshift',)
46fb0a
     packages = ('openshift-hyperkube',)
46fb0a
 
46fb0a
+    master_localhost_kubeconfig = (
46fb0a
+        '/etc/kubernetes/static-pod-resources/'
46fb0a
+        'kube-apiserver-certs/secrets/node-kubeconfigs/localhost.kubeconfig'
46fb0a
+        )
46fb0a
+
46fb0a
     option_list = [
46fb0a
         PluginOpt('token', default=None, val_type=str,
46fb0a
                   desc='admin token to allow API queries'),
46fb0a
+        PluginOpt('kubeconfig', default=None, val_type=str,
46fb0a
+                  desc='Path to a locally available kubeconfig file'),
46fb0a
         PluginOpt('host', default='https://localhost:6443',
46fb0a
                   desc='host address to use for oc login, including port'),
46fb0a
-        PluginOpt('no-oc', default=False, desc='do not collect `oc` output'),
46fb0a
+        PluginOpt('no-oc', default=True, desc='do not collect `oc` output'),
46fb0a
         PluginOpt('podlogs', default=True, desc='collect logs from each pod'),
46fb0a
         PluginOpt('podlogs-filter', default='', val_type=str,
46fb0a
                   desc='only collect logs from pods matching this pattern'),
46fb0a
@@ -73,6 +80,10 @@ class Openshift(Plugin, RedHatPlugin):
46fb0a
         """Check to see if we can run `oc` commands"""
46fb0a
         return self.exec_cmd('oc whoami')['status'] == 0
46fb0a
 
46fb0a
+    def _check_localhost_kubeconfig(self):
46fb0a
+        """Check if the localhost.kubeconfig exists with system:admin user"""
46fb0a
+        return self.path_exists(self.get_option('kubeconfig'))
46fb0a
+
46fb0a
     def _check_oc_logged_in(self):
46fb0a
         """See if we're logged in to the API service, and if not attempt to do
46fb0a
         so using provided plugin options
46fb0a
@@ -80,8 +91,38 @@ class Openshift(Plugin, RedHatPlugin):
46fb0a
         if self._check_oc_function():
46fb0a
             return True
46fb0a
 
46fb0a
-        # Not logged in currently, attempt to do so
46fb0a
+        if self.get_option('kubeconfig') is None:
46fb0a
+            # If admin doesn't add the kubeconfig
46fb0a
+            # use default localhost.kubeconfig
46fb0a
+            self.set_option(
46fb0a
+                'kubeconfig',
46fb0a
+                self.master_localhost_kubeconfig
46fb0a
+            )
46fb0a
+
46fb0a
+        # Check first if we can use the localhost.kubeconfig before
46fb0a
+        # using token. We don't want to use 'host' option due we use
46fb0a
+        # cluster url from kubeconfig. Default is localhost.
46fb0a
+        if self._check_localhost_kubeconfig():
46fb0a
+            self.set_default_cmd_environment({
46fb0a
+                'KUBECONFIG': self.get_option('kubeconfig')
46fb0a
+            })
46fb0a
+
46fb0a
+            oc_res = self.exec_cmd(
46fb0a
+                "oc login -u system:admin "
46fb0a
+                "--insecure-skip-tls-verify=True"
46fb0a
+            )
46fb0a
+            if oc_res['status'] == 0 and self._check_oc_function():
46fb0a
+                return True
46fb0a
+
46fb0a
+            self._log_warn(
46fb0a
+                "The login command failed with status: %s and error: %s"
46fb0a
+                % (oc_res['status'], oc_res['output'])
46fb0a
+            )
46fb0a
+            return False
46fb0a
+
46fb0a
+        # If kubeconfig is not defined, check if token is provided.
46fb0a
         token = self.get_option('token') or os.getenv('SOSOCPTOKEN', None)
46fb0a
+
46fb0a
         if token:
46fb0a
             oc_res = self.exec_cmd("oc login %s --token=%s "
46fb0a
                                    "--insecure-skip-tls-verify=True"
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From 61765992812afb785e9552e01e3b5579118a6963 Mon Sep 17 00:00:00 2001
46fb0a
From: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
Date: Fri, 1 Apr 2022 12:05:36 +0200
46fb0a
Subject: [PATCH] Add one more container for plugin enablement
46fb0a
46fb0a
Signed-off-by: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
---
46fb0a
 sos/report/plugins/openshift_ovn.py | 2 +-
46fb0a
 1 file changed, 1 insertion(+), 1 deletion(-)
46fb0a
46fb0a
diff --git a/sos/report/plugins/openshift_ovn.py b/sos/report/plugins/openshift_ovn.py
46fb0a
index b4787b8e..98522b1e 100644
46fb0a
--- a/sos/report/plugins/openshift_ovn.py
46fb0a
+++ b/sos/report/plugins/openshift_ovn.py
46fb0a
@@ -16,7 +16,7 @@ class OpenshiftOVN(Plugin, RedHatPlugin):
46fb0a
     """
46fb0a
     short_desc = 'Openshift OVN'
46fb0a
     plugin_name = "openshift_ovn"
46fb0a
-    containers = ('ovnkube-master', 'ovn-ipsec')
46fb0a
+    containers = ('ovnkube-master', 'ovnkube-node', 'ovn-ipsec')
46fb0a
     profiles = ('openshift',)
46fb0a
 
46fb0a
     def setup(self):
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From d3aa071efc85507341cf65dd61414a734654f50a Mon Sep 17 00:00:00 2001
46fb0a
From: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
Date: Mon, 28 Mar 2022 14:47:09 -0400
46fb0a
Subject: [PATCH] [presets] Adjust OCP preset options
46fb0a
46fb0a
Adjust the options used by the 'ocp' preset to better reflect the
46fb0a
current collection needs and approach.
46fb0a
46fb0a
This includes disabling the `cgroups` plugin due to the large amount of
46fb0a
mostly irrelevant data captured due to the high number of containers
46fb0a
present on OCP nodes, ensuring the `--container-runtime` option is set
46fb0a
to `crio` to align container-based collections, disabling HTML report
46fb0a
generation and increasing the base log size rather than blindly enabling
46fb0a
all-logs.
46fb0a
46fb0a
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
---
46fb0a
 sos/presets/redhat/__init__.py | 13 +++++++++----
46fb0a
 1 file changed, 9 insertions(+), 4 deletions(-)
46fb0a
46fb0a
diff --git a/sos/presets/redhat/__init__.py b/sos/presets/redhat/__init__.py
46fb0a
index 865c9b6b..0b9f6f11 100644
46fb0a
--- a/sos/presets/redhat/__init__.py
46fb0a
+++ b/sos/presets/redhat/__init__.py
46fb0a
@@ -36,10 +36,15 @@ RHOSP_OPTS = SoSOptions(plugopts=[
46fb0a
 
46fb0a
 RHOCP = "ocp"
46fb0a
 RHOCP_DESC = "OpenShift Container Platform by Red Hat"
46fb0a
-RHOCP_OPTS = SoSOptions(all_logs=True, verify=True, plugopts=[
46fb0a
-                             'networking.timeout=600',
46fb0a
-                             'networking.ethtool_namespaces=False',
46fb0a
-                             'networking.namespaces=200'])
46fb0a
+RHOCP_OPTS = SoSOptions(
46fb0a
+    verify=True, skip_plugins=['cgroups'], container_runtime='crio',
46fb0a
+    no_report=True, log_size=100,
46fb0a
+    plugopts=[
46fb0a
+        'crio.timeout=600',
46fb0a
+        'networking.timeout=600',
46fb0a
+        'networking.ethtool_namespaces=False',
46fb0a
+        'networking.namespaces=200'
46fb0a
+    ])
46fb0a
 
46fb0a
 RH_CFME = "cfme"
46fb0a
 RH_CFME_DESC = "Red Hat CloudForms"
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From f2b67ab820070063995689fed03492cdaa012d01 Mon Sep 17 00:00:00 2001
46fb0a
From: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
Date: Fri, 1 Apr 2022 17:01:35 +0200
46fb0a
Subject: [PATCH] Use /etc/os-release instead of /etc/redhat-release as the
46fb0a
 most compatible way to find host release
46fb0a
46fb0a
Signed-off-by: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
---
46fb0a
 sos/policies/distros/redhat.py | 5 ++---
46fb0a
 1 file changed, 2 insertions(+), 3 deletions(-)
46fb0a
46fb0a
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
46fb0a
index 0c72a5e4..2e117f37 100644
46fb0a
--- a/sos/policies/distros/redhat.py
46fb0a
+++ b/sos/policies/distros/redhat.py
46fb0a
@@ -40,7 +40,6 @@ class RedHatPolicy(LinuxPolicy):
46fb0a
         ('Distribution Website', 'https://www.redhat.com/'),
46fb0a
         ('Commercial Support', 'https://www.access.redhat.com/')
46fb0a
     ]
46fb0a
-    _redhat_release = '/etc/redhat-release'
46fb0a
     _tmp_dir = "/var/tmp"
46fb0a
     _in_container = False
46fb0a
     default_scl_prefix = '/opt/rh'
46fb0a
@@ -471,7 +470,7 @@ support representative.
46fb0a
         atomic = False
46fb0a
         if ENV_HOST_SYSROOT not in os.environ:
46fb0a
             return atomic
46fb0a
-        host_release = os.environ[ENV_HOST_SYSROOT] + cls._redhat_release
46fb0a
+        host_release = os.environ[ENV_HOST_SYSROOT] + OS_RELEASE
46fb0a
         if not os.path.exists(host_release):
46fb0a
             return False
46fb0a
         try:
46fb0a
@@ -558,7 +557,7 @@ support representative.
46fb0a
         coreos = False
46fb0a
         if ENV_HOST_SYSROOT not in os.environ:
46fb0a
             return coreos
46fb0a
-        host_release = os.environ[ENV_HOST_SYSROOT] + cls._redhat_release
46fb0a
+        host_release = os.environ[ENV_HOST_SYSROOT] + OS_RELEASE
46fb0a
         try:
46fb0a
             for line in open(host_release, 'r').read().splitlines():
46fb0a
                 coreos |= 'Red Hat Enterprise Linux CoreOS' in line
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From ee0dd68199a2c9296eafe64ead5b2263c8270e4a Mon Sep 17 00:00:00 2001
46fb0a
From: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
Date: Wed, 6 Apr 2022 11:56:41 +0200
46fb0a
Subject: [PATCH] Use --force-pull-image option for pods created with oc. Set
46fb0a
 --force-pull-image=True by default, can be turned off with
46fb0a
 --force-pull-image=False
46fb0a
46fb0a
Signed-off-by: Nadia Pinaeva <npinaeva@redhat.com>
46fb0a
---
46fb0a
 man/en/sos-collect.1           | 16 +++++++++++-----
46fb0a
 sos/collector/__init__.py      |  9 +++++----
46fb0a
 sos/collector/transports/oc.py |  2 ++
46fb0a
 sos/options.py                 | 20 ++++++++++++++------
46fb0a
 4 files changed, 32 insertions(+), 15 deletions(-)
46fb0a
46fb0a
diff --git a/man/en/sos-collect.1 b/man/en/sos-collect.1
46fb0a
index 9b0a5d7b..2f60332b 100644
46fb0a
--- a/man/en/sos-collect.1
46fb0a
+++ b/man/en/sos-collect.1
46fb0a
@@ -28,7 +28,7 @@ sos collect \- Collect sosreports from multiple (cluster) nodes
46fb0a
     [\-\-no\-local]
46fb0a
     [\-\-primary PRIMARY]
46fb0a
     [\-\-image IMAGE]
46fb0a
-    [\-\-force-pull-image]
46fb0a
+    [\-\-force-pull-image TOGGLE, --pull TOGGLE]
46fb0a
     [\-\-registry-user USER]
46fb0a
     [\-\-registry-password PASSWORD]
46fb0a
     [\-\-registry-authfile FILE]
46fb0a
@@ -262,10 +262,16 @@ Specify an image to use for the temporary container created for collections on
46fb0a
 containerized host, if you do not want to use the default image specifed by the
46fb0a
 host's policy. Note that this should include the registry.
46fb0a
 .TP
46fb0a
-\fB\-\-force-pull-image\fR
46fb0a
-Use this option to force the container runtime to pull the specified image (even
46fb0a
-if it is the policy default image) even if the image already exists on the host.
46fb0a
-This may be useful to update an older container image on containerized hosts.
46fb0a
+\fB\-\-force-pull-image TOGGLE, \-\-pull TOGGLE\fR
46fb0a
+When collecting an sos report from a containerized host, force the host to always
46fb0a
+pull the specified image, even if that image already exists on the host.
46fb0a
+This is useful to ensure that the latest version of that image is always in use.
46fb0a
+Disabling this option will use whatever version of the image is present on the node,
46fb0a
+and only attempt a pull if there is no copy of the image present at all.
46fb0a
+
46fb0a
+Enable with true/on/yes or disable with false/off/no
46fb0a
+
46fb0a
+Default: true
46fb0a
 .TP
46fb0a
 \fB\-\-registry-user USER\fR
46fb0a
 Specify the username to authenticate to the registry with in order to pull the container
46fb0a
diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py
46fb0a
index d898ca34..66c3d932 100644
46fb0a
--- a/sos/collector/__init__.py
46fb0a
+++ b/sos/collector/__init__.py
46fb0a
@@ -27,7 +27,7 @@ from pipes import quote
46fb0a
 from textwrap import fill
46fb0a
 from sos.cleaner import SoSCleaner
46fb0a
 from sos.collector.sosnode import SosNode
46fb0a
-from sos.options import ClusterOption
46fb0a
+from sos.options import ClusterOption, str_to_bool
46fb0a
 from sos.component import SoSComponent
46fb0a
 from sos.utilities import bold
46fb0a
 from sos import __version__
46fb0a
@@ -85,7 +85,7 @@ class SoSCollector(SoSComponent):
46fb0a
         'encrypt_pass': '',
46fb0a
         'group': None,
46fb0a
         'image': '',
46fb0a
-        'force_pull_image': False,
46fb0a
+        'force_pull_image': True,
46fb0a
         'jobs': 4,
46fb0a
         'keywords': [],
46fb0a
         'keyword_file': None,
46fb0a
@@ -357,8 +357,9 @@ class SoSCollector(SoSComponent):
46fb0a
         collect_grp.add_argument('--image',
46fb0a
                                  help=('Specify the container image to use for'
46fb0a
                                        ' containerized hosts.'))
46fb0a
-        collect_grp.add_argument('--force-pull-image', '--pull', default=False,
46fb0a
-                                 action='store_true',
46fb0a
+        collect_grp.add_argument('--force-pull-image', '--pull',
46fb0a
+                                 default=True, choices=(True, False),
46fb0a
+                                 type=str_to_bool,
46fb0a
                                  help='Force pull the container image even if '
46fb0a
                                       'it already exists on the host')
46fb0a
         collect_grp.add_argument('--registry-user', default=None,
46fb0a
diff --git a/sos/collector/transports/oc.py b/sos/collector/transports/oc.py
46fb0a
index 90a802b2..8f6aa9b4 100644
46fb0a
--- a/sos/collector/transports/oc.py
46fb0a
+++ b/sos/collector/transports/oc.py
46fb0a
@@ -147,6 +147,8 @@ class OCTransport(RemoteTransport):
46fb0a
                         "tty": True
46fb0a
                     }
46fb0a
                 ],
46fb0a
+                "imagePullPolicy":
46fb0a
+                    "Always" if self.opts.force_pull_image else "IfNotPresent",
46fb0a
                 "restartPolicy": "Never",
46fb0a
                 "nodeName": self.address,
46fb0a
                 "hostNetwork": True,
46fb0a
diff --git a/sos/options.py b/sos/options.py
46fb0a
index 4846a509..2d5a5135 100644
46fb0a
--- a/sos/options.py
46fb0a
+++ b/sos/options.py
46fb0a
@@ -18,6 +18,16 @@ def _is_seq(val):
46fb0a
     return val_type is list or val_type is tuple
46fb0a
 
46fb0a
 
46fb0a
+def str_to_bool(val):
46fb0a
+    _val = val.lower()
46fb0a
+    if _val in ['true', 'on', 'yes']:
46fb0a
+        return True
46fb0a
+    elif _val in ['false', 'off', 'no']:
46fb0a
+        return False
46fb0a
+    else:
46fb0a
+        return None
46fb0a
+
46fb0a
+
46fb0a
 class SoSOptions():
46fb0a
 
46fb0a
     def _merge_opt(self, opt, src, is_default):
46fb0a
@@ -153,15 +163,13 @@ class SoSOptions():
46fb0a
         if isinstance(self.arg_defaults[key], list):
46fb0a
             return [v for v in val.split(',')]
46fb0a
         if isinstance(self.arg_defaults[key], bool):
46fb0a
-            _val = val.lower()
46fb0a
-            if _val in ['true', 'on', 'yes']:
46fb0a
-                return True
46fb0a
-            elif _val in ['false', 'off', 'no']:
46fb0a
-                return False
46fb0a
-            else:
46fb0a
+            val = str_to_bool(val)
46fb0a
+            if val is None:
46fb0a
                 raise Exception(
46fb0a
                     "Value of '%s' in %s must be True or False or analagous"
46fb0a
                     % (key, conf))
46fb0a
+            else:
46fb0a
+                return val
46fb0a
         if isinstance(self.arg_defaults[key], int):
46fb0a
             try:
46fb0a
                 return int(val)
46fb0a
-- 
46fb0a
2.34.3
46fb0a
46fb0a
From ce289a3ae7101a898efdb84ddfd575576ba5819b Mon Sep 17 00:00:00 2001
46fb0a
From: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
Date: Tue, 5 Apr 2022 11:32:11 -0400
46fb0a
Subject: [PATCH] [ocp, openshift] Re-align API collection options and rename
46fb0a
 option
46fb0a
46fb0a
Previously, in #2888, the `openshift` plugin was extended to allow API
46fb0a
collections by using a default-available kubeconfig file rather than
46fb0a
relying on user-provided tokens. This also included flipping the default
46fb0a
value of the `no-oc` plugin option to `True` (meaning do not collect API
46fb0a
output by default).
46fb0a
46fb0a
This worked for the plugin, but it introduced a gap in `sos collect`
46fb0a
whereby the cluster profile could no longer reliably enable API
46fb0a
collections when trying to leverage the new functionality of not
46fb0a
requiring a user token.
46fb0a
46fb0a
Fix this by updating the cluster profile to align with the new
46fb0a
default-off approach of API collections.
46fb0a
46fb0a
Along with this, add a toggle to the cluster profile directly to allow
46fb0a
users to toggle API collections on or off (default off) directly. This
46fb0a
is done via a new `with-api` cluster option (e.g. `-c ocp.with-api`).
46fb0a
Further, rename the `openshift` plugin option from `no-oc` to
46fb0a
`with-api`. This change not only makes the option use case far more
46fb0a
obvious, it will also align the use of the option to both `collect` and
46fb0a
`report` so that users need only be aware of a single option for either
46fb0a
method.
46fb0a
46fb0a
The cluster profile also has logic to detect which plugin option,
46fb0a
`no-oc` or `with-api` to use based on the (RHEL) sos version installed
46fb0a
on the nodes being inspected by the `ocp` cluster profile.
46fb0a
46fb0a
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
46fb0a
---
46fb0a
 sos/collector/clusters/ocp.py   | 72 +++++++++++++++++++++++++++------
46fb0a
 sos/report/plugins/openshift.py | 26 +++++++-----
46fb0a
 2 files changed, 77 insertions(+), 21 deletions(-)
46fb0a
46fb0a
diff --git a/sos/collector/clusters/ocp.py b/sos/collector/clusters/ocp.py
46fb0a
index 9beb2f9b..e31d1903 100644
46fb0a
--- a/sos/collector/clusters/ocp.py
46fb0a
+++ b/sos/collector/clusters/ocp.py
46fb0a
@@ -30,7 +30,11 @@ class ocp(Cluster):
46fb0a
     clusterAdmin privileges.
46fb0a
 
46fb0a
     If this requires the use of a secondary configuration file, specify that
46fb0a
-    path with the 'kubeconfig' cluster option.
46fb0a
+    path with the 'kubeconfig' cluster option. This config file will also be
46fb0a
+    used on a single master node to perform API collections if the `with-api`
46fb0a
+    option is enabled (default disabled). If no `kubeconfig` option is given,
46fb0a
+    but `with-api` is enabled, the cluster profile will attempt to use a
46fb0a
+    well-known default kubeconfig file if it is available on the host.
46fb0a
 
46fb0a
     Alternatively, provide a clusterAdmin access token either via the 'token'
46fb0a
     cluster option or, preferably, the SOSOCPTOKEN environment variable.
46fb0a
@@ -45,7 +49,7 @@ class ocp(Cluster):
46fb0a
     option mentioned above.
46fb0a
 
46fb0a
     To avoid redundant collections of OCP API information (e.g. 'oc get'
46fb0a
-    commands), this profile will attempt to enable the openshift plugin on only
46fb0a
+    commands), this profile will attempt to enable the API collections on only
46fb0a
     a single master node. If the none of the master nodes have a functional
46fb0a
     'oc' binary available, *and* the --no-local option is used, that means that
46fb0a
     no API data will be collected.
46fb0a
@@ -63,7 +67,8 @@ class ocp(Cluster):
46fb0a
         ('label', '', 'Colon delimited list of labels to select nodes with'),
46fb0a
         ('role', 'master', 'Colon delimited list of roles to filter on'),
46fb0a
         ('kubeconfig', '', 'Path to the kubeconfig file'),
46fb0a
-        ('token', '', 'Service account token to use for oc authorization')
46fb0a
+        ('token', '', 'Service account token to use for oc authorization'),
46fb0a
+        ('with-api', False, 'Collect OCP API data from a master node')
46fb0a
     ]
46fb0a
 
46fb0a
     def fmt_oc_cmd(self, cmd):
46fb0a
@@ -219,13 +224,52 @@ class ocp(Cluster):
46fb0a
             return False
46fb0a
         return 'master' in self.node_dict[sosnode.address]['roles']
46fb0a
 
46fb0a
+    def _toggle_api_opt(self, node, use_api):
46fb0a
+        """In earlier versions of sos, the openshift plugin option that is
46fb0a
+        used to toggle the API collections was called `no-oc` rather than
46fb0a
+        `with-api`. This older plugin option had the inverse logic of the
46fb0a
+        current `with-api` option.
46fb0a
+
46fb0a
+        Use this to toggle the correct plugin option given the node's sos
46fb0a
+        version. Note that the use of version 4.2 here is tied to the RHEL
46fb0a
+        release (the only usecase for this cluster profile) rather than
46fb0a
+        the upstream version given the backports for that downstream.
46fb0a
+
46fb0a
+        :param node:    The node being inspected for API collections
46fb0a
+        :type node:     ``SoSNode``
46fb0a
+
46fb0a
+        :param use_api: Should this node enable API collections?
46fb0a
+        :type use_api:  ``bool``
46fb0a
+        """
46fb0a
+        if node.check_sos_version('4.2-16'):
46fb0a
+            _opt = 'with-api'
46fb0a
+            _val = 'on' if use_api else 'off'
46fb0a
+        else:
46fb0a
+            _opt = 'no-oc'
46fb0a
+            _val = 'off' if use_api else 'on'
46fb0a
+        node.plugopts.append("openshift.%s=%s" % (_opt, _val))
46fb0a
+
46fb0a
     def set_primary_options(self, node):
46fb0a
+
46fb0a
         node.enable_plugins.append('openshift')
46fb0a
+        if not self.get_option('with-api'):
46fb0a
+            self._toggle_api_opt(node, False)
46fb0a
+            return
46fb0a
         if self.api_collect_enabled:
46fb0a
             # a primary has already been enabled for API collection, disable
46fb0a
             # it among others
46fb0a
-            node.plugopts.append('openshift.no-oc=on')
46fb0a
+            self._toggle_api_opt(node, False)
46fb0a
         else:
46fb0a
+            # running in a container, so reference the /host mount point
46fb0a
+            master_kube = (
46fb0a
+                '/host/etc/kubernetes/static-pod-resources/'
46fb0a
+                'kube-apiserver-certs/secrets/node-kubeconfigs/'
46fb0a
+                'localhost.kubeconfig'
46fb0a
+            )
46fb0a
+            _optconfig = self.get_option('kubeconfig')
46fb0a
+            if _optconfig and not _optconfig.startswith('/host'):
46fb0a
+                _optconfig = '/host/' + _optconfig
46fb0a
+            _kubeconfig = _optconfig or master_kube
46fb0a
             _oc_cmd = 'oc'
46fb0a
             if node.host.containerized:
46fb0a
                 _oc_cmd = '/host/bin/oc'
46fb0a
@@ -244,17 +288,21 @@ class ocp(Cluster):
46fb0a
                                       need_root=True)
46fb0a
             if can_oc['status'] == 0:
46fb0a
                 # the primary node can already access the API
46fb0a
+                self._toggle_api_opt(node, True)
46fb0a
                 self.api_collect_enabled = True
46fb0a
             elif self.token:
46fb0a
                 node.sos_env_vars['SOSOCPTOKEN'] = self.token
46fb0a
+                self._toggle_api_opt(node, True)
46fb0a
+                self.api_collect_enabled = True
46fb0a
+            elif node.file_exists(_kubeconfig):
46fb0a
+                # if the file exists, then the openshift sos plugin will use it
46fb0a
+                # if the with-api option is turned on
46fb0a
+                if not _kubeconfig == master_kube:
46fb0a
+                    node.plugopts.append(
46fb0a
+                        "openshift.kubeconfig=%s" % _kubeconfig
46fb0a
+                    )
46fb0a
+                self._toggle_api_opt(node, True)
46fb0a
                 self.api_collect_enabled = True
46fb0a
-            elif self.get_option('kubeconfig'):
46fb0a
-                kc = self.get_option('kubeconfig')
46fb0a
-                if node.file_exists(kc):
46fb0a
-                    if node.host.containerized:
46fb0a
-                        kc = "/host/%s" % kc
46fb0a
-                    node.sos_env_vars['KUBECONFIG'] = kc
46fb0a
-                    self.api_collect_enabled = True
46fb0a
             if self.api_collect_enabled:
46fb0a
                 msg = ("API collections will be performed on %s\nNote: API "
46fb0a
                        "collections may extend runtime by 10s of minutes\n"
46fb0a
@@ -264,6 +312,6 @@ class ocp(Cluster):
46fb0a
 
46fb0a
     def set_node_options(self, node):
46fb0a
         # don't attempt OC API collections on non-primary nodes
46fb0a
-        node.plugopts.append('openshift.no-oc=on')
46fb0a
+        self._toggle_api_opt(node, False)
46fb0a
 
46fb0a
 # vim: set et ts=4 sw=4 :
46fb0a
diff --git a/sos/report/plugins/openshift.py b/sos/report/plugins/openshift.py
46fb0a
index d643f04c..a41ab62b 100644
46fb0a
--- a/sos/report/plugins/openshift.py
46fb0a
+++ b/sos/report/plugins/openshift.py
46fb0a
@@ -19,7 +19,10 @@ class Openshift(Plugin, RedHatPlugin):
46fb0a
     further extending the kubernetes plugin (or the OCP 3.x extensions included
46fb0a
     in the Red Hat version of the kube plugin).
46fb0a
 
46fb0a
-    By default, this plugin will collect cluster information and inspect the
46fb0a
+    This plugin may collect OCP API information when the `with-api` option is
46fb0a
+    enabled. This option is disabled by default.
46fb0a
+
46fb0a
+    When enabled, this plugin will collect cluster information and inspect the
46fb0a
     default namespaces/projects that are created during deployment - i.e. the
46fb0a
     namespaces of the cluster projects matching openshift.* and kube.*. At the
46fb0a
     time of this plugin's creation that number of default projects is already
46fb0a
@@ -34,16 +37,20 @@ class Openshift(Plugin, RedHatPlugin):
46fb0a
 
46fb0a
     Users will need to either:
46fb0a
 
46fb0a
-        1) Provide the bearer token via the `-k openshift.token` option
46fb0a
-        2) Provide the bearer token via the `SOSOCPTOKEN` environment variable
46fb0a
-        3) Otherwise ensure that the root user can successfully run `oc` and
46fb0a
+        1) Accept the use of a well-known stock kubeconfig file provided via a
46fb0a
+           static pod resource for the kube-apiserver
46fb0a
+        2) Provide the bearer token via the `-k openshift.token` option
46fb0a
+        3) Provide the bearer token via the `SOSOCPTOKEN` environment variable
46fb0a
+        4) Otherwise ensure that the root user can successfully run `oc` and
46fb0a
            get proper output prior to running this plugin
46fb0a
 
46fb0a
 
46fb0a
-    It is highly suggested that option #2 be used first, as this will prevent
46fb0a
-    the token from being recorded in output saved to the archive. Option #1 may
46fb0a
+    It is highly suggested that option #1 be used first, as this uses well
46fb0a
+    known configurations and requires the least information from the user. If
46fb0a
+    using a token, it is recommended to use option #3 as this will prevent
46fb0a
+    the token from being recorded in output saved to the archive. Option #2 may
46fb0a
     be used if this is considered an acceptable risk. It is not recommended to
46fb0a
-    rely on option #3, though it will provide the functionality needed.
46fb0a
+    rely on option #4, though it will provide the functionality needed.
46fb0a
     """
46fb0a
 
46fb0a
     short_desc = 'Openshift Container Platform 4.x'
46fb0a
@@ -65,7 +72,8 @@ class Openshift(Plugin, RedHatPlugin):
46fb0a
                   desc='Path to a locally available kubeconfig file'),
46fb0a
         PluginOpt('host', default='https://localhost:6443',
46fb0a
                   desc='host address to use for oc login, including port'),
46fb0a
-        PluginOpt('no-oc', default=True, desc='do not collect `oc` output'),
46fb0a
+        PluginOpt('with-api', default=False,
46fb0a
+                  desc='collect output from the OCP API'),
46fb0a
         PluginOpt('podlogs', default=True, desc='collect logs from each pod'),
46fb0a
         PluginOpt('podlogs-filter', default='', val_type=str,
46fb0a
                   desc='only collect logs from pods matching this pattern'),
46fb0a
@@ -212,7 +220,7 @@ class Openshift(Plugin, RedHatPlugin):
46fb0a
         self.add_copy_spec('/etc/kubernetes/*')
46fb0a
 
46fb0a
         # see if we run `oc` commands
46fb0a
-        if not self.get_option('no-oc'):
46fb0a
+        if self.get_option('with-api'):
46fb0a
             can_run_oc = self._check_oc_logged_in()
46fb0a
         else:
46fb0a
             can_run_oc = False
46fb0a
-- 
46fb0a
2.34.3
46fb0a