From 2861eaadccf570307daeb50ed8f2aebaf6754e84 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Mon, 24 Oct 2022 10:33:11 +0800 Subject: [PATCH 01/22] Add parser container_inspect Signed-off-by: jiazhang --- insights/specs/__init__.py | 2 +- .../specs/datasources/container_inspect.py | 107 +++ insights/specs/default.py | 3 +- .../datasources/test_container_inspect.py | 757 ++++++++++++++++++ 4 files changed, 867 insertions(+), 2 deletions(-) create mode 100644 insights/specs/datasources/container_inspect.py create mode 100644 insights/tests/datasources/test_container_inspect.py diff --git a/insights/specs/__init__.py b/insights/specs/__init__.py index 00ae212506..ed82f3c127 100644 --- a/insights/specs/__init__.py +++ b/insights/specs/__init__.py @@ -132,7 +132,7 @@ class Specs(SpecSet): dnf_module_list = RegistryPoint() dnf_modules = RegistryPoint() dnsmasq_config = RegistryPoint(multi_output=True) - docker_container_inspect = RegistryPoint(multi_output=True) + docker_container_inspect = RegistryPoint(multi_output=True, filterable=True) docker_host_machine_id = RegistryPoint() docker_image_inspect = RegistryPoint(multi_output=True) docker_info = RegistryPoint() diff --git a/insights/specs/datasources/container_inspect.py b/insights/specs/datasources/container_inspect.py new file mode 100644 index 0000000000..c49725435d --- /dev/null +++ b/insights/specs/datasources/container_inspect.py @@ -0,0 +1,107 @@ +""" +Custom datasources for awx_manage information +""" +from insights.core.context import HostContext +from insights.core.dr import SkipComponent +from insights.core.plugins import datasource +from insights.core.spec_factory import DatasourceProvider, foreach_execute +from insights.core.filters import get_filters +from insights.specs import Specs +import json +from insights.specs.datasources import DEFAULT_SHELL_TIMEOUT +# from insights.parsers.podman_list import PodmanListContainers +from insights.parsers.docker_list import DockerListContainers + + +@datasource(DockerListContainers, HostContext) +def running_docker_rhel_containers(broker): + """ + Returns a list of container_id of the running containers. + """ + def _is_rhel_image(ctx, c_info): + """Only collect the containers based from RHEL images""" + try: + ret = ctx.shell_out("/usr/bin/docker exec %s cat /etc/redhat-release" % c_info, timeout=DEFAULT_SHELL_TIMEOUT) + if ret and "red hat enterprise linux" in ret[0].lower(): + return True + except Exception: + # return False when there is no such file "/etc/redhat-releas" + pass + return False + + cs = [] + if (DockerListContainers in broker): + docker_c = broker[DockerListContainers] + for name in docker_c.running_containers: + c_info = docker_c.containers[name]['CONTAINER ID'][:12] + cs.append(c_info) if _is_rhel_image(broker[HostContext], c_info) else None + if cs: + return cs + raise SkipComponent + + +class LocalSpecs(Specs): + """ Local specs used only by docker_inspect datasources """ + + docker_container_inspect_data_raw = foreach_execute(running_docker_rhel_containers, "/usr/bin/docker inspect %s") + """ Returns the output of command ``/usr/bin/docker inspect `` """ + + +@datasource(LocalSpecs.docker_container_inspect_data_raw, HostContext) +def docker_container_inspect_data_datasource(broker): + """ + This datasource provides the filtered information collected + from ``/usr/bin/docker inspect ``. + + Typical content of ``/usr/bin/docker inspect `` file is:: + + [ + { + "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", + "Created": "2022-10-21T23:47:24.506159696-04:00", + "Path": "sleep" + } + ... + ] + Returns: + list: item is JSON string containing filtered information. + + Raises: + SkipComponent: When the filter/path does not exist or any exception occurs. + """ + def _find(d, tag, path): + if tag in d: + yield d[tag] + for k, v in d.items(): + if isinstance(v, dict): + for i in _find(v, tag, path): + path.append(k) + yield i + + try: + filters = get_filters(Specs.docker_container_inspect) + contents = [] + for item in broker[LocalSpecs.docker_container_inspect_data_raw]: + contents.append(item.content) + if contents and filters: + total_results = [] + for content in contents: + content = "".join(content) + raw_data = json.loads(content)[0] + filter_result = {} + filter_result['Id'] = raw_data['Id'] + filter_result['Image'] = raw_data['Image'] + filter_result['ImageName'] = raw_data['ImageName'] + for item in filters: + path = [] + for val in _find(raw_data, item, path): + mid_data = {item: val} + for path_item in path: + mid_data = {path_item: mid_data} + filter_result.update(mid_data) + total_results.append(filter_result) + if total_results: + return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_commands/docker_inspect') + except Exception as e: + raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) + raise SkipComponent diff --git a/insights/specs/default.py b/insights/specs/default.py index daa40a7e21..adef6ec16f 100644 --- a/insights/specs/default.py +++ b/insights/specs/default.py @@ -24,7 +24,7 @@ from insights.components.satellite import IsCapsule, IsSatellite611, IsSatellite from insights.specs import Specs from insights.specs.datasources import ( - aws, awx_manage, cloud_init, candlepin_broker, corosync as corosync_ds, + aws, awx_manage, container_inspect, cloud_init, candlepin_broker, corosync as corosync_ds, dir_list, ethernet, httpd, ipcs, kernel_module_list, lpstat, md5chk, package_provides, ps as ps_datasource, sap, satellite_missed_queues, semanage, ssl_certificate, sys_fs_cgroup_memory_tasks_number, system_user_dirs, user_group, yum_updates, luks_devices) @@ -167,6 +167,7 @@ class DefaultSpecs(Specs): dnf_conf = simple_file("/etc/dnf/dnf.conf") dnf_modules = glob_file("/etc/dnf/modules.d/*.module") docker_info = simple_command("/usr/bin/docker info") + docker_container_inspect = container_inspect.docker_container_inspect_data_datasource docker_list_containers = simple_command("/usr/bin/docker ps --all --no-trunc") docker_list_images = simple_command("/usr/bin/docker images --all --no-trunc --digests") docker_storage_setup = simple_file("/etc/sysconfig/docker-storage-setup") diff --git a/insights/tests/datasources/test_container_inspect.py b/insights/tests/datasources/test_container_inspect.py new file mode 100644 index 0000000000..987386bc89 --- /dev/null +++ b/insights/tests/datasources/test_container_inspect.py @@ -0,0 +1,757 @@ +import pytest +from mock.mock import Mock +from insights.core.dr import SkipComponent +from insights.core.spec_factory import DatasourceProvider +from insights.specs.datasources.container_inspect import docker_container_inspect_data_datasource, LocalSpecs +from insights.specs import Specs +from insights.core import filters + +DOCKER_INSPECT_1 = """ +[ + { + "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", + "Created": "2022-10-21T23:47:24.506159696-04:00", + "Path": "sleep", + "Args": [ + "1000000" + ], + "State": { + "OciVersion": "1.0.2-dev", + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 27625, + "ConmonPid": 27612, + "ExitCode": 0, + "Error": "", + "StartedAt": "2022-10-21T23:47:24.95366414-04:00", + "FinishedAt": "0001-01-01T00:00:00Z", + "Healthcheck": { + "Status": "", + "FailingStreak": 0, + "Log": null + } + }, + "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", + "ImageName": "registry.access.redhat.com/rhel:latest", + "Rootfs": "", + "Pod": "", + "ResolvConfPath": "/var/run/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata/resolv.conf", + "HostnamePath": "/var/run/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata/hostname", + "HostsPath": "/var/run/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata/hosts", + "StaticDir": "/var/lib/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata", + "OCIConfigPath": "/var/lib/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata/config.json", + "OCIRuntime": "runc", + "ConmonPidFile": "/var/run/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata/conmon.pid", + "PidFile": "/var/run/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata/pidfile", + "Name": "eager_bell", + "RestartCount": 0, + "Driver": "overlay", + "MountLabel": "system_u:object_r:container_file_t:s0:c163,c861", + "ProcessLabel": "system_u:system_r:container_t:s0:c163,c861", + "AppArmorProfile": "", + "EffectiveCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + "CAP_NET_RAW", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYS_CHROOT" + ], + "BoundingCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + "CAP_NET_RAW", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYS_CHROOT" + ], + "ExecIDs": [], + "GraphDriver": { + "Name": "overlay", + "Data": { + "LowerDir": "/var/lib/containers/storage/overlay/37a6f5f155300a48480d92a4ecf01c8694e39c3f8a52f77a39f154e5e0a3598f/diff:/var/lib/containers/storage/overlay/a27a6e18a918e68cdc3db82956cd2c0bba42d34c1513f1a0ab841903762d72a2/diff", + "MergedDir": "/var/lib/containers/storage/overlay/de84239ee747de645c453b3b81e10849ea3e957e4316bc84ed3d0c32d7de88ee/merged", + "UpperDir": "/var/lib/containers/storage/overlay/de84239ee747de645c453b3b81e10849ea3e957e4316bc84ed3d0c32d7de88ee/diff", + "WorkDir": "/var/lib/containers/storage/overlay/de84239ee747de645c453b3b81e10849ea3e957e4316bc84ed3d0c32d7de88ee/work" + } + }, + "Mounts": [], + "Dependencies": [], + "NetworkSettings": { + "EndpointID": "", + "Gateway": "10.88.0.1", + "IPAddress": "10.88.0.9", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "d6:1b:73:04:81:03", + "Bridge": "", + "SandboxID": "", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": {}, + "SandboxKey": "/run/netns/cni-f925b703-b150-a170-20e9-b52030a89784", + "Networks": { + "podman": { + "EndpointID": "", + "Gateway": "10.88.0.1", + "IPAddress": "10.88.0.9", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "d6:1b:73:04:81:03", + "NetworkID": "podman", + "DriverOpts": null, + "IPAMConfig": null, + "Links": null + } + } + }, + "ExitCommand": [ + "/usr/bin/podman", + "--root", + "/var/lib/containers/storage", + "--runroot", + "/var/run/containers/storage", + "--log-level", + "warning", + "--cgroup-manager", + "systemd", + "--tmpdir", + "/var/run/libpod", + "--runtime", + "runc", + "--storage-driver", + "overlay", + "--storage-opt", + "overlay.mountopt=nodev,metacopy=on", + "--events-backend", + "file", + "container", + "cleanup", + "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8" + ], + "Namespace": "", + "IsInfra": false, + "Config": { + "Hostname": "aeaea3ead527", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "container=oci", + "HOME=/root", + "HOSTNAME=aeaea3ead527" + ], + "Cmd": [ + "sleep", + "1000000" + ], + "Image": "registry.access.redhat.com/rhel:latest", + "Volumes": null, + "WorkingDir": "/", + "Entrypoint": "", + "OnBuild": null, + "Labels": { + "architecture": "x86_64", + "build-date": "2021-07-13T15:12:27.073772", + "com.redhat.build-host": "cpt-1006.osbs.prod.upshift.rdu2.redhat.com", + "com.redhat.component": "rhel-server-container", + "com.redhat.license_terms": "https://www.redhat.com/agreements", + "description": "The Red Hat Enterprise Linux Base image is designed to be a fully supported foundation for your containerized applications. This base image provides your operations and application teams with the packages, language runtimes and tools necessary to run, maintain, and troubleshoot all of your applications. This image is maintained by Red Hat and updated regularly. It is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. When used as the source for all of your containers, only one copy will ever be downloaded and cached in your production environment. Use this image just like you would a regular Red Hat Enterprise Linux distribution. Tools like yum, gzip, and bash are provided by default. For further information on how this image was built look at the /root/anacanda-ks.cfg file.", + "distribution-scope": "public", + "io.k8s.description": "The Red Hat Enterprise Linux Base image is designed to be a fully supported foundation for your containerized applications. This base image provides your operations and application teams with the packages, language runtimes and tools necessary to run, maintain, and troubleshoot all of your applications. This image is maintained by Red Hat and updated regularly. It is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. When used as the source for all of your containers, only one copy will ever be downloaded and cached in your production environment. Use this image just like you would a regular Red Hat Enterprise Linux distribution. Tools like yum, gzip, and bash are provided by default. For further information on how this image was built look at the /root/anacanda-ks.cfg file.", + "io.k8s.display-name": "Red Hat Enterprise Linux 7", + "io.openshift.tags": "base rhel7", + "name": "rhel7", + "release": "437", + "summary": "Provides the latest release of Red Hat Enterprise Linux 7 in a fully featured and supported base image.", + "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/rhel7/images/7.9-437", + "vcs-ref": "a4d1f0b8a9b923ca309da182943d17fe639d8c95", + "vcs-type": "git", + "vendor": "Red Hat, Inc.", + "version": "7.9" + }, + "Annotations": { + "io.container.manager": "libpod", + "io.kubernetes.cri-o.Created": "2022-10-21T23:47:24.506159696-04:00", + "io.kubernetes.cri-o.TTY": "false", + "io.podman.annotations.autoremove": "FALSE", + "io.podman.annotations.init": "FALSE", + "io.podman.annotations.privileged": "FALSE", + "io.podman.annotations.publish-all": "FALSE", + "org.opencontainers.image.stopSignal": "15" + }, + "StopSignal": 15, + "CreateCommand": [ + "podman", + "run", + "538460c14d75", + "sleep", + "1000000" + ], + "Umask": "0022", + "Timeout": 0, + "StopTimeout": 10 + }, + "HostConfig": { + "Binds": [], + "CgroupManager": "systemd", + "CgroupMode": "host", + "ContainerIDFile": "", + "LogConfig": { + "Type": "k8s-file", + "Config": null, + "Path": "/var/lib/containers/storage/overlay-containers/aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8/userdata/ctr.log", + "Tag": "", + "Size": "0B" + }, + "NetworkMode": "bridge", + "PortBindings": {}, + "RestartPolicy": { + "Name": "", + "MaximumRetryCount": 0 + }, + "AutoRemove": false, + "VolumeDriver": "", + "VolumesFrom": null, + "CapAdd": [], + "CapDrop": [ + "CAP_AUDIT_WRITE", + "CAP_MKNOD" + ], + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": [], + "GroupAdd": [], + "IpcMode": "private", + "Cgroup": "", + "Cgroups": "default", + "Links": null, + "OomScoreAdj": 0, + "PidMode": "private", + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "SecurityOpt": [], + "Tmpfs": {}, + "UTSMode": "private", + "UsernsMode": "", + "ShmSize": 65536000, + "Runtime": "oci", + "ConsoleSize": [ + 0, + 0 + ], + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteIOps": null, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DiskQuota": 0, + "KernelMemory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": 0, + "OomKillDisable": false, + "PidsLimit": 2048, + "Ulimits": [ + { + "Name": "RLIMIT_NOFILE", + "Soft": 1048576, + "Hard": 1048576 + }, + { + "Name": "RLIMIT_NPROC", + "Soft": 32768, + "Hard": 32768 + } + ], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "CgroupConf": null + } + } +] +""".strip() + +DOCKER_INSPECT_2 = """ +[ + { + "Id": "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e", + "Created": "2022-10-21T23:49:20.02296977-04:00", + "Path": "sleep", + "Args": [ + "1000000" + ], + "State": { + "OciVersion": "1.0.2-dev", + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 27942, + "ConmonPid": 27921, + "ExitCode": 0, + "Error": "", + "StartedAt": "2022-10-21T23:49:20.455664509-04:00", + "FinishedAt": "0001-01-01T00:00:00Z", + "Healthcheck": { + "Status": "", + "FailingStreak": 0, + "Log": null + } + }, + "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", + "ImageName": "registry.access.redhat.com/rhel:latest", + "Rootfs": "", + "Pod": "", + "ResolvConfPath": "/var/run/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata/resolv.conf", + "HostnamePath": "/var/run/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata/hostname", + "HostsPath": "/var/run/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata/hosts", + "StaticDir": "/var/lib/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata", + "OCIConfigPath": "/var/lib/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata/config.json", + "OCIRuntime": "runc", + "ConmonPidFile": "/var/run/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata/conmon.pid", + "PidFile": "/var/run/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata/pidfile", + "Name": "youthful_goodall", + "RestartCount": 0, + "Driver": "overlay", + "MountLabel": "system_u:object_r:container_file_t:s0:c1022,c1023", + "ProcessLabel": "", + "AppArmorProfile": "", + "EffectiveCaps": [ + "CAP_AUDIT_CONTROL", + "CAP_AUDIT_READ", + "CAP_AUDIT_WRITE", + "CAP_BLOCK_SUSPEND", + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_IPC_LOCK", + "CAP_IPC_OWNER", + "CAP_KILL", + "CAP_LEASE", + "CAP_LINUX_IMMUTABLE", + "CAP_MAC_ADMIN", + "CAP_MAC_OVERRIDE", + "CAP_MKNOD", + "CAP_NET_ADMIN", + "CAP_NET_BIND_SERVICE", + "CAP_NET_BROADCAST", + "CAP_NET_RAW", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYSLOG", + "CAP_SYS_ADMIN", + "CAP_SYS_BOOT", + "CAP_SYS_CHROOT", + "CAP_SYS_MODULE", + "CAP_SYS_NICE", + "CAP_SYS_PACCT", + "CAP_SYS_PTRACE", + "CAP_SYS_RAWIO", + "CAP_SYS_RESOURCE", + "CAP_SYS_TIME", + "CAP_SYS_TTY_CONFIG", + "CAP_WAKE_ALARM" + ], + "BoundingCaps": [ + "CAP_AUDIT_CONTROL", + "CAP_AUDIT_READ", + "CAP_AUDIT_WRITE", + "CAP_BLOCK_SUSPEND", + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_IPC_LOCK", + "CAP_IPC_OWNER", + "CAP_KILL", + "CAP_LEASE", + "CAP_LINUX_IMMUTABLE", + "CAP_MAC_ADMIN", + "CAP_MAC_OVERRIDE", + "CAP_MKNOD", + "CAP_NET_ADMIN", + "CAP_NET_BIND_SERVICE", + "CAP_NET_BROADCAST", + "CAP_NET_RAW", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYSLOG", + "CAP_SYS_ADMIN", + "CAP_SYS_BOOT", + "CAP_SYS_CHROOT", + "CAP_SYS_MODULE", + "CAP_SYS_NICE", + "CAP_SYS_PACCT", + "CAP_SYS_PTRACE", + "CAP_SYS_RAWIO", + "CAP_SYS_RESOURCE", + "CAP_SYS_TIME", + "CAP_SYS_TTY_CONFIG", + "CAP_WAKE_ALARM" + ], + "ExecIDs": [], + "GraphDriver": { + "Name": "overlay", + "Data": { + "LowerDir": "/var/lib/containers/storage/overlay/37a6f5f155300a48480d92a4ecf01c8694e39c3f8a52f77a39f154e5e0a3598f/diff:/var/lib/containers/storage/overlay/a27a6e18a918e68cdc3db82956cd2c0bba42d34c1513f1a0ab841903762d72a2/diff", + "MergedDir": "/var/lib/containers/storage/overlay/4be93ab17659c0d187211cd4ab4a49463b3d74f77aebc9e0c958479f9a0a3304/merged", + "UpperDir": "/var/lib/containers/storage/overlay/4be93ab17659c0d187211cd4ab4a49463b3d74f77aebc9e0c958479f9a0a3304/diff", + "WorkDir": "/var/lib/containers/storage/overlay/4be93ab17659c0d187211cd4ab4a49463b3d74f77aebc9e0c958479f9a0a3304/work" + } + }, + "Mounts": [], + "Dependencies": [], + "NetworkSettings": { + "EndpointID": "", + "Gateway": "10.88.0.1", + "IPAddress": "10.88.0.10", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "5a:a0:3f:b3:1a:98", + "Bridge": "", + "SandboxID": "", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": {}, + "SandboxKey": "/run/netns/cni-d3dae08c-c0be-7a7d-c53c-c76f720c964c", + "Networks": { + "podman": { + "EndpointID": "", + "Gateway": "10.88.0.1", + "IPAddress": "10.88.0.10", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "5a:a0:3f:b3:1a:98", + "NetworkID": "podman", + "DriverOpts": null, + "IPAMConfig": null, + "Links": null + } + } + }, + "ExitCommand": [ + "/usr/bin/podman", + "--root", + "/var/lib/containers/storage", + "--runroot", + "/var/run/containers/storage", + "--log-level", + "warning", + "--cgroup-manager", + "systemd", + "--tmpdir", + "/var/run/libpod", + "--runtime", + "runc", + "--storage-driver", + "overlay", + "--storage-opt", + "overlay.mountopt=nodev,metacopy=on", + "--events-backend", + "file", + "container", + "cleanup", + "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e" + ], + "Namespace": "", + "IsInfra": false, + "Config": { + "Hostname": "28fb57be8bb2", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "container=oci", + "HOME=/root", + "HOSTNAME=28fb57be8bb2" + ], + "Cmd": [ + "sleep", + "1000000" + ], + "Image": "registry.access.redhat.com/rhel:latest", + "Volumes": null, + "WorkingDir": "/", + "Entrypoint": "", + "OnBuild": null, + "Labels": { + "architecture": "x86_64", + "build-date": "2021-07-13T15:12:27.073772", + "com.redhat.build-host": "cpt-1006.osbs.prod.upshift.rdu2.redhat.com", + "com.redhat.component": "rhel-server-container", + "com.redhat.license_terms": "https://www.redhat.com/agreements", + "description": "The Red Hat Enterprise Linux Base image is designed to be a fully supported foundation for your containerized applications. This base image provides your operations and application teams with the packages, language runtimes and tools necessary to run, maintain, and troubleshoot all of your applications. This image is maintained by Red Hat and updated regularly. It is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. When used as the source for all of your containers, only one copy will ever be downloaded and cached in your production environment. Use this image just like you would a regular Red Hat Enterprise Linux distribution. Tools like yum, gzip, and bash are provided by default. For further information on how this image was built look at the /root/anacanda-ks.cfg file.", + "distribution-scope": "public", + "io.k8s.description": "The Red Hat Enterprise Linux Base image is designed to be a fully supported foundation for your containerized applications. This base image provides your operations and application teams with the packages, language runtimes and tools necessary to run, maintain, and troubleshoot all of your applications. This image is maintained by Red Hat and updated regularly. It is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. When used as the source for all of your containers, only one copy will ever be downloaded and cached in your production environment. Use this image just like you would a regular Red Hat Enterprise Linux distribution. Tools like yum, gzip, and bash are provided by default. For further information on how this image was built look at the /root/anacanda-ks.cfg file.", + "io.k8s.display-name": "Red Hat Enterprise Linux 7", + "io.openshift.tags": "base rhel7", + "name": "rhel7", + "release": "437", + "summary": "Provides the latest release of Red Hat Enterprise Linux 7 in a fully featured and supported base image.", + "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/rhel7/images/7.9-437", + "vcs-ref": "a4d1f0b8a9b923ca309da182943d17fe639d8c95", + "vcs-type": "git", + "vendor": "Red Hat, Inc.", + "version": "7.9" + }, + "Annotations": { + "io.container.manager": "libpod", + "io.kubernetes.cri-o.Created": "2022-10-21T23:49:20.02296977-04:00", + "io.kubernetes.cri-o.TTY": "false", + "io.podman.annotations.autoremove": "FALSE", + "io.podman.annotations.init": "FALSE", + "io.podman.annotations.privileged": "TRUE", + "io.podman.annotations.publish-all": "FALSE", + "org.opencontainers.image.stopSignal": "15" + }, + "StopSignal": 15, + "CreateCommand": [ + "podman", + "run", + "--privileged", + "538460c14d75", + "sleep", + "1000000" + ], + "Umask": "0022", + "Timeout": 0, + "StopTimeout": 10 + }, + "HostConfig": { + "Binds": [], + "CgroupManager": "systemd", + "CgroupMode": "host", + "ContainerIDFile": "", + "LogConfig": { + "Type": "k8s-file", + "Config": null, + "Path": "/var/lib/containers/storage/overlay-containers/28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e/userdata/ctr.log", + "Tag": "", + "Size": "0B" + }, + "NetworkMode": "bridge", + "PortBindings": {}, + "RestartPolicy": { + "Name": "", + "MaximumRetryCount": 0 + }, + "AutoRemove": false, + "VolumeDriver": "", + "VolumesFrom": null, + "CapAdd": [], + "CapDrop": [ + "CAP_BPF", + "CAP_CHECKPOINT_RESTORE", + "CAP_PERFMON" + ], + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": [], + "GroupAdd": [], + "IpcMode": "private", + "Cgroup": "", + "Cgroups": "default", + "Links": null, + "OomScoreAdj": 0, + "PidMode": "private", + "Privileged": true, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "SecurityOpt": [], + "Tmpfs": {}, + "UTSMode": "private", + "UsernsMode": "", + "ShmSize": 65536000, + "Runtime": "oci", + "ConsoleSize": [ + 0, + 0 + ], + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteIOps": null, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DiskQuota": 0, + "KernelMemory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": 0, + "OomKillDisable": false, + "PidsLimit": 2048, + "Ulimits": [ + { + "Name": "RLIMIT_NOFILE", + "Soft": 1048576, + "Hard": 1048576 + }, + { + "Name": "RLIMIT_NPROC", + "Soft": 32768, + "Hard": 32768 + } + ], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "CgroupConf": null + } + } +] +""".strip() + +DOCKER_INSPECT_3 = """ +[ + { + "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8" + } +] +""".strip() + +DOCKER_INSPECT_4 = """ +Error: error inspecting object: no such object: "testnoid" +""".strip() + + +EXPECTED_RESULT = """ +[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}}}, {"Id": "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "Config": {"Annotations": {"io.podman.annotations.privileged": "TRUE"}}}] +""".strip() + + +RELATIVE_PATH = 'insights_commands/docker_inspect' + + +def setup_function(func): + if Specs.docker_container_inspect in filters._CACHE: + del filters._CACHE[Specs.docker_container_inspect] + if Specs.docker_container_inspect in filters.FILTERS: + del filters.FILTERS[Specs.docker_container_inspect] + + if func is test_docker_inspect_datasource or func is test_docker_inspect_datasource_NG_output_1 or func is test_docker_inspect_datasource_NG_output_2: + filters.add_filter(Specs.docker_container_inspect, ["io.podman.annotations.privileged"]) + if func is test_docker_inspect_datasource_no_filter: + filters.add_filter(Specs.docker_container_inspect, []) + + +def test_docker_inspect_datasource(): + docker_inspect_data_1 = Mock() + docker_inspect_data_2 = Mock() + docker_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() + docker_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() + broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_1, docker_inspect_data_2]} + result = docker_container_inspect_data_datasource(broker) + assert result is not None + assert isinstance(result, DatasourceProvider) + assert result.content[0] == EXPECTED_RESULT + assert result.relative_path == RELATIVE_PATH + + +def test_docker_inspect_datasource_no_filter(): + docker_inspect_data_1 = Mock() + docker_inspect_data_2 = Mock() + docker_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() + docker_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() + broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_1, docker_inspect_data_2]} + with pytest.raises(SkipComponent) as e: + docker_container_inspect_data_datasource(broker) + assert 'SkipComponent' in str(e) + + +def test_docker_inspect_datasource_NG_output_1(): + docker_inspect_data_3 = Mock() + docker_inspect_data_3.content = DOCKER_INSPECT_3.splitlines() + broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_3]} + with pytest.raises(SkipComponent) as e: + docker_container_inspect_data_datasource(broker) + assert 'SkipComponent' in str(e) + + +def test_docker_inspect_datasource_NG_output_2(): + docker_inspect_data_4 = Mock() + docker_inspect_data_4.content = DOCKER_INSPECT_4.splitlines() + broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_4]} + with pytest.raises(SkipComponent) as e: + docker_container_inspect_data_datasource(broker) + assert 'Unexpected exception' in str(e) From f77830efb400ec87dd30447d87233448ad73c5d3 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 25 Oct 2022 16:10:26 +0800 Subject: [PATCH 02/22] Update containers_inspect Signed-off-by: jiazhang --- insights/specs/__init__.py | 3 +- ...ainer_inspect.py => containers_inspect.py} | 70 ++++++------ insights/specs/default.py | 4 +- ..._inspect.py => test_containers_inspect.py} | 102 ++++++++++++------ 4 files changed, 105 insertions(+), 74 deletions(-) rename insights/specs/datasources/{container_inspect.py => containers_inspect.py} (53%) rename insights/tests/datasources/{test_container_inspect.py => test_containers_inspect.py} (86%) diff --git a/insights/specs/__init__.py b/insights/specs/__init__.py index ed82f3c127..7626700f21 100644 --- a/insights/specs/__init__.py +++ b/insights/specs/__init__.py @@ -78,6 +78,8 @@ class Specs(SpecSet): cobbler_modules_conf = RegistryPoint() cobbler_settings = RegistryPoint() containers_policy = RegistryPoint() + containers_inspect_vars = RegistryPoint(filterable=True) + containers_inspect = RegistryPoint() corosync = RegistryPoint() corosync_cmapctl = RegistryPoint(multi_output=True) corosync_conf = RegistryPoint() @@ -132,7 +134,6 @@ class Specs(SpecSet): dnf_module_list = RegistryPoint() dnf_modules = RegistryPoint() dnsmasq_config = RegistryPoint(multi_output=True) - docker_container_inspect = RegistryPoint(multi_output=True, filterable=True) docker_host_machine_id = RegistryPoint() docker_image_inspect = RegistryPoint(multi_output=True) docker_info = RegistryPoint() diff --git a/insights/specs/datasources/container_inspect.py b/insights/specs/datasources/containers_inspect.py similarity index 53% rename from insights/specs/datasources/container_inspect.py rename to insights/specs/datasources/containers_inspect.py index c49725435d..261043d4aa 100644 --- a/insights/specs/datasources/container_inspect.py +++ b/insights/specs/datasources/containers_inspect.py @@ -8,52 +8,48 @@ from insights.core.filters import get_filters from insights.specs import Specs import json -from insights.specs.datasources import DEFAULT_SHELL_TIMEOUT -# from insights.parsers.podman_list import PodmanListContainers -from insights.parsers.docker_list import DockerListContainers +from insights.specs.datasources.container import running_rhel_containers -@datasource(DockerListContainers, HostContext) -def running_docker_rhel_containers(broker): +@datasource(running_rhel_containers, HostContext) +def running_rhel_containers_id(broker): """ Returns a list of container_id of the running containers. """ - def _is_rhel_image(ctx, c_info): - """Only collect the containers based from RHEL images""" - try: - ret = ctx.shell_out("/usr/bin/docker exec %s cat /etc/redhat-release" % c_info, timeout=DEFAULT_SHELL_TIMEOUT) - if ret and "red hat enterprise linux" in ret[0].lower(): - return True - except Exception: - # return False when there is no such file "/etc/redhat-releas" - pass - return False + containers_info = [] + docker_containers = [] + podman_containers = [] + for container in broker[running_rhel_containers]: + if container[1] == "podman": + podman_containers.append(container[2]) + else: + docker_containers.append(container[2]) - cs = [] - if (DockerListContainers in broker): - docker_c = broker[DockerListContainers] - for name in docker_c.running_containers: - c_info = docker_c.containers[name]['CONTAINER ID'][:12] - cs.append(c_info) if _is_rhel_image(broker[HostContext], c_info) else None - if cs: - return cs + common_ids = list(set(podman_containers).intersection(docker_containers)) + new_dcoker_containers = [item for item in docker_containers if item not in common_ids] + for item in podman_containers: + containers_info.append(("podman", item)) + for item in new_dcoker_containers: + containers_info.append(("docker", item)) + if containers_info: + return containers_info raise SkipComponent class LocalSpecs(Specs): - """ Local specs used only by docker_inspect datasources """ + """ Local specs used only by docker|podman_inspect datasources """ - docker_container_inspect_data_raw = foreach_execute(running_docker_rhel_containers, "/usr/bin/docker inspect %s") - """ Returns the output of command ``/usr/bin/docker inspect `` """ + containers_inspect_data_raw = foreach_execute(running_rhel_containers_id, "/usr/bin/%s inspect %s") + """ Returns the output of command ``/usr/bin/docker|podman inspect `` """ -@datasource(LocalSpecs.docker_container_inspect_data_raw, HostContext) -def docker_container_inspect_data_datasource(broker): +@datasource(LocalSpecs.containers_inspect_data_raw, HostContext) +def containers_inspect_data_datasource(broker): """ This datasource provides the filtered information collected - from ``/usr/bin/docker inspect ``. + from ``/usr/bin/docker|podman inspect ``. - Typical content of ``/usr/bin/docker inspect `` file is:: + Typical content of ``/usr/bin/docker|podman inspect `` file is:: [ { @@ -79,19 +75,21 @@ def _find(d, tag, path): yield i try: - filters = get_filters(Specs.docker_container_inspect) + filters = get_filters(Specs.containers_inspect_vars) contents = [] - for item in broker[LocalSpecs.docker_container_inspect_data_raw]: - contents.append(item.content) + for item in broker[LocalSpecs.containers_inspect_data_raw]: + engine = item.file_name.split()[0].split("bin/")[-1] + contents.append((item.content, engine)) if contents and filters: total_results = [] for content in contents: - content = "".join(content) - raw_data = json.loads(content)[0] + content_raw = "".join(content[0]) + raw_data = json.loads(content_raw)[0] filter_result = {} filter_result['Id'] = raw_data['Id'] filter_result['Image'] = raw_data['Image'] filter_result['ImageName'] = raw_data['ImageName'] + filter_result['engine'] = content[1] for item in filters: path = [] for val in _find(raw_data, item, path): @@ -101,7 +99,7 @@ def _find(d, tag, path): filter_result.update(mid_data) total_results.append(filter_result) if total_results: - return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_commands/docker_inspect') + return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_commands/containers_inspect') except Exception as e: raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) raise SkipComponent diff --git a/insights/specs/default.py b/insights/specs/default.py index adef6ec16f..5dba77e2eb 100644 --- a/insights/specs/default.py +++ b/insights/specs/default.py @@ -24,7 +24,7 @@ from insights.components.satellite import IsCapsule, IsSatellite611, IsSatellite from insights.specs import Specs from insights.specs.datasources import ( - aws, awx_manage, container_inspect, cloud_init, candlepin_broker, corosync as corosync_ds, + aws, awx_manage, containers_inspect, cloud_init, candlepin_broker, corosync as corosync_ds, dir_list, ethernet, httpd, ipcs, kernel_module_list, lpstat, md5chk, package_provides, ps as ps_datasource, sap, satellite_missed_queues, semanage, ssl_certificate, sys_fs_cgroup_memory_tasks_number, system_user_dirs, user_group, yum_updates, luks_devices) @@ -129,6 +129,7 @@ class DefaultSpecs(Specs): corosync = simple_file("/etc/sysconfig/corosync") corosync_cmapctl = foreach_execute(corosync_ds.corosync_cmapctl_cmds, "%s") corosync_conf = simple_file("/etc/corosync/corosync.conf") + containers_inspect = containers_inspect.containers_inspect_data_datasource cpu_cores = glob_file("sys/devices/system/cpu/cpu[0-9]*/online") cpu_siblings = glob_file("sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list") cpu_smt_active = simple_file("sys/devices/system/cpu/smt/active") @@ -167,7 +168,6 @@ class DefaultSpecs(Specs): dnf_conf = simple_file("/etc/dnf/dnf.conf") dnf_modules = glob_file("/etc/dnf/modules.d/*.module") docker_info = simple_command("/usr/bin/docker info") - docker_container_inspect = container_inspect.docker_container_inspect_data_datasource docker_list_containers = simple_command("/usr/bin/docker ps --all --no-trunc") docker_list_images = simple_command("/usr/bin/docker images --all --no-trunc --digests") docker_storage_setup = simple_file("/etc/sysconfig/docker-storage-setup") diff --git a/insights/tests/datasources/test_container_inspect.py b/insights/tests/datasources/test_containers_inspect.py similarity index 86% rename from insights/tests/datasources/test_container_inspect.py rename to insights/tests/datasources/test_containers_inspect.py index 987386bc89..b4bd176450 100644 --- a/insights/tests/datasources/test_container_inspect.py +++ b/insights/tests/datasources/test_containers_inspect.py @@ -2,9 +2,12 @@ from mock.mock import Mock from insights.core.dr import SkipComponent from insights.core.spec_factory import DatasourceProvider -from insights.specs.datasources.container_inspect import docker_container_inspect_data_datasource, LocalSpecs +from insights.specs.datasources.containers_inspect import containers_inspect_data_datasource, LocalSpecs, running_rhel_containers_id +from insights.specs.datasources.container import running_rhel_containers from insights.specs import Specs from insights.core import filters +from insights import dr + DOCKER_INSPECT_1 = """ [ @@ -696,62 +699,91 @@ EXPECTED_RESULT = """ -[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}}}, {"Id": "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "Config": {"Annotations": {"io.podman.annotations.privileged": "TRUE"}}}] +[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "engine": "docker", "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}}}, {"Id": "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "engine": "podman", "Config": {"Annotations": {"io.podman.annotations.privileged": "TRUE"}}}] """.strip() -RELATIVE_PATH = 'insights_commands/docker_inspect' +RELATIVE_PATH = 'insights_commands/containers_inspect' + +CONTAINERS_ID_EXPECTED_RESULT = [ + ('podman', 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8'), + ('podman', '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e'), + ('podman', '528890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54'), + ('docker', '38fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e'), + ('docker', '538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54') +] def setup_function(func): - if Specs.docker_container_inspect in filters._CACHE: - del filters._CACHE[Specs.docker_container_inspect] - if Specs.docker_container_inspect in filters.FILTERS: - del filters.FILTERS[Specs.docker_container_inspect] + if Specs.containers_inspect_vars in filters._CACHE: + del filters._CACHE[Specs.containers_inspect_vars] + if Specs.containers_inspect_vars in filters.FILTERS: + del filters.FILTERS[Specs.containers_inspect_vars] + + if func is test_containers_inspect_datasource or func is test_containers_inspect_datasource_NG_output_1 or func is test_containers_inspect_datasource_NG_output_2: + filters.add_filter(Specs.containers_inspect_vars, ["io.podman.annotations.privileged"]) + if func is test_containers_inspect_datasource_no_filter: + filters.add_filter(Specs.containers_inspect_vars, []) + - if func is test_docker_inspect_datasource or func is test_docker_inspect_datasource_NG_output_1 or func is test_docker_inspect_datasource_NG_output_2: - filters.add_filter(Specs.docker_container_inspect, ["io.podman.annotations.privileged"]) - if func is test_docker_inspect_datasource_no_filter: - filters.add_filter(Specs.docker_container_inspect, []) +def test_running_rhel_containers_id(): + broker = dr.Broker() + containers_info = [ + ("registry.access.redhat.com/rhel", "podman", "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8"), + ("registry.access.redhat.com/rhel", "podman", "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e"), + ("registry.access.redhat.com/rhel", "podman", "528890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54"), + ("registry.access.redhat.com/rhel", "docker", "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8"), + ("registry.access.redhat.com/rhel", "docker", "38fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e"), + ("registry.access.redhat.com/rhel", "docker", "538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54") + ] + broker[running_rhel_containers] = containers_info + result = running_rhel_containers_id(broker) + assert result == CONTAINERS_ID_EXPECTED_RESULT -def test_docker_inspect_datasource(): - docker_inspect_data_1 = Mock() - docker_inspect_data_2 = Mock() - docker_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() - docker_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() - broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_1, docker_inspect_data_2]} - result = docker_container_inspect_data_datasource(broker) +def test_containers_inspect_datasource(): + containers_inspect_data_1 = Mock() + containers_inspect_data_2 = Mock() + containers_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() + containers_inspect_data_1.file_name = "/usr/bin/docker inspect aeaea3ead527" + containers_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() + containers_inspect_data_2.file_name = "/usr/bin/podman inspect 28fb57be8bb2" + broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_1, containers_inspect_data_2]} + result = containers_inspect_data_datasource(broker) assert result is not None assert isinstance(result, DatasourceProvider) assert result.content[0] == EXPECTED_RESULT assert result.relative_path == RELATIVE_PATH -def test_docker_inspect_datasource_no_filter(): - docker_inspect_data_1 = Mock() - docker_inspect_data_2 = Mock() - docker_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() - docker_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() - broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_1, docker_inspect_data_2]} +def test_containers_inspect_datasource_no_filter(): + containers_inspect_data_1 = Mock() + containers_inspect_data_2 = Mock() + containers_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() + containers_inspect_data_1.file_name = "/usr/bin/docker inspect aeaea3ead527" + containers_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() + containers_inspect_data_2.file_name = "/usr/bin/podman inspect 28fb57be8bb2" + broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_1, containers_inspect_data_2]} with pytest.raises(SkipComponent) as e: - docker_container_inspect_data_datasource(broker) + containers_inspect_data_datasource(broker) assert 'SkipComponent' in str(e) -def test_docker_inspect_datasource_NG_output_1(): - docker_inspect_data_3 = Mock() - docker_inspect_data_3.content = DOCKER_INSPECT_3.splitlines() - broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_3]} +def test_containers_inspect_datasource_NG_output_1(): + containers_inspect_data_3 = Mock() + containers_inspect_data_3.content = DOCKER_INSPECT_3.splitlines() + containers_inspect_data_3.file_name = "/usr/bin/podman inspect 28fb57be8bb2" + broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_3]} with pytest.raises(SkipComponent) as e: - docker_container_inspect_data_datasource(broker) + containers_inspect_data_datasource(broker) assert 'SkipComponent' in str(e) -def test_docker_inspect_datasource_NG_output_2(): - docker_inspect_data_4 = Mock() - docker_inspect_data_4.content = DOCKER_INSPECT_4.splitlines() - broker = {LocalSpecs.docker_container_inspect_data_raw: [docker_inspect_data_4]} +def test_containers_inspect_datasource_NG_output_2(): + containers_inspect_data_4 = Mock() + containers_inspect_data_4.content = DOCKER_INSPECT_4.splitlines() + containers_inspect_data_4.file_name = "/usr/bin/docker inspect aeaea3ead527" + broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_4]} with pytest.raises(SkipComponent) as e: - docker_container_inspect_data_datasource(broker) + containers_inspect_data_datasource(broker) assert 'Unexpected exception' in str(e) From 22a687496a62eaa02f4ed1d098e283d980420e3c Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 25 Oct 2022 18:12:43 +0800 Subject: [PATCH 03/22] Add parser Signed-off-by: jiazhang --- insights/parsers/containers_inspect.py | 46 +++++++++++++++++++ .../specs/datasources/containers_inspect.py | 2 +- .../datasources/test_containers_inspect.py | 12 ++--- .../tests/parsers/test_containers_inspect.py | 24 ++++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 insights/parsers/containers_inspect.py create mode 100644 insights/tests/parsers/test_containers_inspect.py diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py new file mode 100644 index 0000000000..f5f2b61ba2 --- /dev/null +++ b/insights/parsers/containers_inspect.py @@ -0,0 +1,46 @@ +""" +Containers inspect +================== + +This parser reads the output of commands: "/usr/bin/docker|podman inspect " +which are used to show metadata information of containers. +""" +import json + +from insights import parser, CommandParser +from insights.specs import Specs + + +@parser(Specs.containers_inspect) +class ContainersInspect(CommandParser): + """ + Class for parsing the output of the containers inspect commands + ``/usr/bin/docker|podman inspect `` + + + Typical Output of this command after datasource containers_inspect is:: + + [ + { + "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", + "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", + "ImageName": "registry.access.redhat.com/rhel:latest", + "engine": "podman", + "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}} + } + ] + + Attributes: + data (list): A list containing the parsed information + + Examples: + >>> inspect_containers.data[0]["Id"] + 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8' + >>> inspect_containers.data[0]["engine"] + 'podman' + >>> inspect_containers.data[0]["Config"]["Annotations"]["io.podman.annotations.privileged"] + 'FALSE' + """ + + def parse_content(self, content): + self.data = json.loads(content[0]) diff --git a/insights/specs/datasources/containers_inspect.py b/insights/specs/datasources/containers_inspect.py index 261043d4aa..a1db6f99b2 100644 --- a/insights/specs/datasources/containers_inspect.py +++ b/insights/specs/datasources/containers_inspect.py @@ -78,7 +78,7 @@ def _find(d, tag, path): filters = get_filters(Specs.containers_inspect_vars) contents = [] for item in broker[LocalSpecs.containers_inspect_data_raw]: - engine = item.file_name.split()[0].split("bin/")[-1] + engine = item.cmd.split()[0].split("bin/")[-1] contents.append((item.content, engine)) if contents and filters: total_results = [] diff --git a/insights/tests/datasources/test_containers_inspect.py b/insights/tests/datasources/test_containers_inspect.py index b4bd176450..f29fb31795 100644 --- a/insights/tests/datasources/test_containers_inspect.py +++ b/insights/tests/datasources/test_containers_inspect.py @@ -745,9 +745,9 @@ def test_containers_inspect_datasource(): containers_inspect_data_1 = Mock() containers_inspect_data_2 = Mock() containers_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() - containers_inspect_data_1.file_name = "/usr/bin/docker inspect aeaea3ead527" + containers_inspect_data_1.cmd = "/usr/bin/docker inspect aeaea3ead527" containers_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() - containers_inspect_data_2.file_name = "/usr/bin/podman inspect 28fb57be8bb2" + containers_inspect_data_2.cmd = "/usr/bin/podman inspect 28fb57be8bb2" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_1, containers_inspect_data_2]} result = containers_inspect_data_datasource(broker) assert result is not None @@ -760,9 +760,9 @@ def test_containers_inspect_datasource_no_filter(): containers_inspect_data_1 = Mock() containers_inspect_data_2 = Mock() containers_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() - containers_inspect_data_1.file_name = "/usr/bin/docker inspect aeaea3ead527" + containers_inspect_data_1.cmd = "/usr/bin/docker inspect aeaea3ead527" containers_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() - containers_inspect_data_2.file_name = "/usr/bin/podman inspect 28fb57be8bb2" + containers_inspect_data_2.cmd = "/usr/bin/podman inspect 28fb57be8bb2" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_1, containers_inspect_data_2]} with pytest.raises(SkipComponent) as e: containers_inspect_data_datasource(broker) @@ -772,7 +772,7 @@ def test_containers_inspect_datasource_no_filter(): def test_containers_inspect_datasource_NG_output_1(): containers_inspect_data_3 = Mock() containers_inspect_data_3.content = DOCKER_INSPECT_3.splitlines() - containers_inspect_data_3.file_name = "/usr/bin/podman inspect 28fb57be8bb2" + containers_inspect_data_3.cmd = "/usr/bin/podman inspect 28fb57be8bb2" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_3]} with pytest.raises(SkipComponent) as e: containers_inspect_data_datasource(broker) @@ -782,7 +782,7 @@ def test_containers_inspect_datasource_NG_output_1(): def test_containers_inspect_datasource_NG_output_2(): containers_inspect_data_4 = Mock() containers_inspect_data_4.content = DOCKER_INSPECT_4.splitlines() - containers_inspect_data_4.file_name = "/usr/bin/docker inspect aeaea3ead527" + containers_inspect_data_4.cmd = "/usr/bin/docker inspect aeaea3ead527" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_4]} with pytest.raises(SkipComponent) as e: containers_inspect_data_datasource(broker) diff --git a/insights/tests/parsers/test_containers_inspect.py b/insights/tests/parsers/test_containers_inspect.py new file mode 100644 index 0000000000..0c5169d44d --- /dev/null +++ b/insights/tests/parsers/test_containers_inspect.py @@ -0,0 +1,24 @@ +import doctest + +from insights.parsers import containers_inspect +from insights.parsers.containers_inspect import ContainersInspect +from insights.tests import context_wrap + + +CONTAINERS_INSPECT = """ +[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "engine": "podman", "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}}}] +""".strip() + + +def test_containers_inspect(): + containers_inspect_result = ContainersInspect(context_wrap(CONTAINERS_INSPECT)) + assert containers_inspect_result.data[0]["Id"] == "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8" + assert containers_inspect_result.data[0]["Config"]["Annotations"]["io.podman.annotations.privileged"] == "FALSE" + + +def test_doc_examples(): + env = { + 'inspect_containers': ContainersInspect(context_wrap(CONTAINERS_INSPECT)) + } + failed, total = doctest.testmod(containers_inspect, globs=env) + assert failed == 0 From 9cbe90e8a664f42da6cec4b5999305467f168c40 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 25 Oct 2022 18:21:22 +0800 Subject: [PATCH 04/22] Update docs Signed-off-by: jiazhang --- docs/custom_datasources_index.rst | 8 + .../containers_inspect.rst | 3 + insights/tests/parsers/test_docker_inspect.py | 314 ------------------ 3 files changed, 11 insertions(+), 314 deletions(-) create mode 100644 docs/shared_parsers_catalog/containers_inspect.rst delete mode 100644 insights/tests/parsers/test_docker_inspect.py diff --git a/docs/custom_datasources_index.rst b/docs/custom_datasources_index.rst index 726ae7340e..e28ac32439 100644 --- a/docs/custom_datasources_index.rst +++ b/docs/custom_datasources_index.rst @@ -43,6 +43,14 @@ insights.specs.datasources.cloud_init :show-inheritance: :undoc-members: +insights.specs.datasources.containers_inspect +------------------------------------ + +.. automodule:: insights.specs.datasources.containers_inspect + :members: running_rhel_containers_id, containers_inspect_data_datasource + :show-inheritance: + :undoc-members: + insights.specs.datasources.container ------------------------------------ diff --git a/docs/shared_parsers_catalog/containers_inspect.rst b/docs/shared_parsers_catalog/containers_inspect.rst new file mode 100644 index 0000000000..9657252744 --- /dev/null +++ b/docs/shared_parsers_catalog/containers_inspect.rst @@ -0,0 +1,3 @@ +.. automodule:: insights.parsers.containers_inspect + :members: + :show-inheritance: diff --git a/insights/tests/parsers/test_docker_inspect.py b/insights/tests/parsers/test_docker_inspect.py deleted file mode 100644 index 98c2c9ec36..0000000000 --- a/insights/tests/parsers/test_docker_inspect.py +++ /dev/null @@ -1,314 +0,0 @@ -import pytest -import doctest -from insights.parsers import docker_inspect, SkipException -from insights.tests import context_wrap - -DOCKER_CONTAINER_INSPECT = """ -[ -{ - "Id": "97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07", - "Created": "2016-06-23T05:12:25.433469799Z", - "Path": "/bin/bash", - "Args": [], - "State": { - "Status": "running", - "Running": true, - "Paused": false, - "Restarting": false, - "OOMKilled": false, - "Dead": false, - "Pid": 15096, - "ExitCode": 0, - "Error": "", - "StartedAt": "2016-06-23T05:37:56.925378831Z", - "FinishedAt": "2016-06-23T05:33:02.012653984Z" - }, - "Image": "882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2", - "ResolvConfPath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/resolv.conf", - "HostnamePath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/hostname", - "HostsPath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/hosts", - "LogPath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07-json.log", - "Name": "/hehe2", - "RestartCount": 0, - "Driver": "devicemapper", - "ExecDriver": "native-0.2", - "MountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c429,c690", - "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c429,c690", - "AppArmorProfile": "", - "ExecIDs": null, - "HostConfig": { - "Binds": null, - "ContainerIDFile": "", - "LxcConf": [], - "Memory": 0, - "MemoryReservation": 0, - "MemorySwap": 0, - "KernelMemory": 0, - "CpuShares": 0, - "CpuPeriod": 0, - "CpusetCpus": "", - "CpusetMems": "", - "CpuQuota": 0, - "BlkioWeight": 0, - "OomKillDisable": false, - "MemorySwappiness": -1, - "Privileged": false, - "PortBindings": {}, - "Links": null, - "PublishAllPorts": false, - "Dns": [], - "DnsOptions": [], - "DnsSearch": [], - "ExtraHosts": null, - "VolumesFrom": null, - "Devices": [], - "NetworkMode": "default", - "IpcMode": "", - "PidMode": "", - "UTSMode": "", - "CapAdd": null, - "CapDrop": null, - "GroupAdd": null, - "RestartPolicy": { - "Name": "no", - "MaximumRetryCount": 0 - }, - "SecurityOpt": null, - "ReadonlyRootfs": false, - "Ulimits": null, - "Sysctls": {}, - "LogConfig": { - "Type": "json-file", - "Config": { - "max-file": "7", - "max-size": "10m" - } - }, - "CgroupParent": "", - "ConsoleSize": [ - 0, - 0 - ], - "VolumeDriver": "", - "ShmSize": 67108864 - }, - "GraphDriver": { - "Name": "devicemapper", - "Data": { - "DeviceId": "433", - "DeviceName": "docker-253:0-71431059-97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07", - "DeviceSize": "107374182400" - } - }, - "Mounts": [], - "Config": { - "Hostname": "97d7cd1a5d8f", - "Domainname": "", - "User": "root", - "AttachStdin": true, - "AttachStdout": true, - "AttachStderr": true, - "Tty": true, - "OpenStdin": true, - "StdinOnce": true, - "Env": [ - "container=docker", - "PKGM=yum", - "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin" - ], - "Cmd": [ - "/bin/bash" - ], - "Image": "rhel7_imagemagick", - "Volumes": null, - "WorkingDir": "", - "Entrypoint": null, - "OnBuild": null, - "Labels": { - "Architecture": "x86_64", - "Authoritative_Registry": "registry.access.redhat.com", - "BZComponent": "rhel-server-docker", - "Build_Host": "rcm-img03.build.eng.bos.redhat.com", - "Name": "rhel7/rhel", - "Release": "61", - "Vendor": "Red Hat, Inc.", - "Version": "7.2", - "io.projectatomic.Temporary": "true" - }, - "StopSignal": "SIGTERM" - }, - "NetworkSettings": { - "Bridge": "", - "SandboxID": "f1cce5397340364aff043879ff5bd7e2ce2fcc5b81cfb7fe1833ce7b57eb6cf8", - "HairpinMode": false, - "LinkLocalIPv6Address": "", - "LinkLocalIPv6PrefixLen": 0, - "Ports": {}, - "SandboxKey": "/var/run/docker/netns/f1cce5397340", - "SecondaryIPAddresses": null, - "SecondaryIPv6Addresses": null, - "EndpointID": "59be4c94b2a1346eb0ec16472bc132e071d18733fd956c34b3b1defff9bba389", - "Gateway": "172.17.0.1", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "MacAddress": "02:42:ac:11:00:02", - "Networks": { - "bridge": { - "EndpointID": "59be4c94b2a1346eb0ec16472bc132e071d18733fd956c34b3b1defff9bba389", - "Gateway": "172.17.0.1", - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "MacAddress": "02:42:ac:11:00:02" - } - } - } -} -] -""".splitlines() - -DOCKER_IMAGE_INSPECT = """ -[ -{ - "Id": "882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2", - "RepoTags": [ - "rhel7_imagemagick:latest" - ], - "RepoDigests": [], - "Parent": "34c167d900afb820ecab622a214ce3207af80ec755c0dcb6165b425087ddbc3a", - "Comment": "", - "Created": "2016-06-23T03:39:15.068803433Z", - "Container": "65410bf8809af52d2074c882917ea0651b119a91f460c1037bc99d4d5976532a", - "ContainerConfig": { - "Hostname": "cf3092658f01", - "Domainname": "", - "User": "root", - "AttachStdin": false, - "AttachStdout": false, - "AttachStderr": false, - "Tty": false, - "OpenStdin": false, - "StdinOnce": false, - "Env": [ - "container=docker", - "PKGM=yum", - "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin" - ], - "Cmd": [ - "/bin/sh", - "-c", - "yum install -y ImageMagick-6.7.8.9-10.el7" - ], - "Image": "34c167d900afb820ecab622a214ce3207af80ec755c0dcb6165b425087ddbc3a", - "Volumes": null, - "WorkingDir": "", - "Entrypoint": null, - "OnBuild": [], - "Labels": { - "Architecture": "x86_64", - "Authoritative_Registry": "registry.access.redhat.com", - "BZComponent": "rhel-server-docker", - "Build_Host": "rcm-img03.build.eng.bos.redhat.com", - "Name": "rhel7/rhel", - "Release": "61", - "Vendor": "Red Hat, Inc.", - "Version": "7.2" - } - }, - "DockerVersion": "1.9.1", - "Author": "", - "Config": { - "Hostname": "cf3092658f01", - "Domainname": "", - "User": "root", - "AttachStdin": false, - "AttachStdout": false, - "AttachStderr": false, - "Tty": false, - "OpenStdin": false, - "StdinOnce": false, - "Env": [ - "container=docker", - "PKGM=yum", - "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin" - ], - "Cmd": [ - "/usr/bin/bash" - ], - "Image": "34c167d900afb820ecab622a214ce3207af80ec755c0dcb6165b425087ddbc3a", - "Volumes": null, - "WorkingDir": "", - "Entrypoint": null, - "OnBuild": [], - "Labels": { - "Architecture": "x86_64", - "Authoritative_Registry": "registry.access.redhat.com", - "BZComponent": "rhel-server-docker", - "Build_Host": "rcm-img03.build.eng.bos.redhat.com", - "Name": "rhel7/rhel", - "Release": "61", - "Vendor": "Red Hat, Inc.", - "Version": "7.2" - } - }, - "Architecture": "amd64", - "Os": "linux", - "Size": 580094174, - "VirtualSize": 785437820, - "GraphDriver": { - "Name": "devicemapper", - "Data": { - "DeviceId": "431", - "DeviceName": "docker-253:0-71431059-882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2", - "DeviceSize": "107374182400" - } - } -} -] -""".splitlines() - - -DOCKER_CONTAINER_INSPECT_TRUNCATED = """ -[ -{ - "Id": "97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07", - "Created": "2016-06-23T05:12:25.433469799Z", - "Path": "/bin/bash", -""" - - -def test_docker_object_container_inspect(): - result = docker_inspect.DockerInspect(context_wrap(DOCKER_CONTAINER_INSPECT)) - assert result.get('Id') == "97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07" - assert result.get('NetworkSettings').get('HairpinMode') is False - assert result.get('Config').get('Env') == ['container=docker', 'PKGM=yum', 'PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin'] - assert result.get('GraphDriver').get('Data').get('DeviceSize') == '107374182400' - - -def test_docker_object_image_inspect(): - result = docker_inspect.DockerInspect(context_wrap(DOCKER_IMAGE_INSPECT)) - assert result.get('Id') == "882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2" - assert result.get('Size') == 580094174 - assert result.get('Config').get('AttachStdin') is False - assert result.get('RepoDigests') == [] - - -def test_docker_container_inspect_truncated_input(): - with pytest.raises(SkipException): - docker_inspect.DockerInspectContainer(context_wrap(DOCKER_CONTAINER_INSPECT_TRUNCATED)) - - -def test_doc_test(): - dic = docker_inspect.DockerInspectContainer(context_wrap(DOCKER_CONTAINER_INSPECT)) - dii = docker_inspect.DockerInspectImage(context_wrap(DOCKER_IMAGE_INSPECT)) - env = { - 'container': dic, - 'image': dii, - } - failed, total = doctest.testmod(docker_inspect, globs=env) - assert failed == 0 From d1a30a20e3bc2b87b323a130ef4ddff93864e751 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Wed, 26 Oct 2022 09:05:26 +0800 Subject: [PATCH 05/22] Update format and description Signed-off-by: jiazhang --- docs/custom_datasources_index.rst | 2 +- insights/parsers/containers_inspect.py | 2 +- insights/specs/datasources/containers_inspect.py | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/custom_datasources_index.rst b/docs/custom_datasources_index.rst index e28ac32439..86fcac0ec7 100644 --- a/docs/custom_datasources_index.rst +++ b/docs/custom_datasources_index.rst @@ -44,7 +44,7 @@ insights.specs.datasources.cloud_init :undoc-members: insights.specs.datasources.containers_inspect ------------------------------------- +--------------------------------------------- .. automodule:: insights.specs.datasources.containers_inspect :members: running_rhel_containers_id, containers_inspect_data_datasource diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py index f5f2b61ba2..eea95b30e2 100644 --- a/insights/parsers/containers_inspect.py +++ b/insights/parsers/containers_inspect.py @@ -3,7 +3,7 @@ ================== This parser reads the output of commands: "/usr/bin/docker|podman inspect " -which are used to show metadata information of containers. +which are used to show the metadata information of containers. """ import json diff --git a/insights/specs/datasources/containers_inspect.py b/insights/specs/datasources/containers_inspect.py index a1db6f99b2..f464fea7cb 100644 --- a/insights/specs/datasources/containers_inspect.py +++ b/insights/specs/datasources/containers_inspect.py @@ -1,5 +1,5 @@ """ -Custom datasources for awx_manage information +Custom datasources for containers inspect information """ from insights.core.context import HostContext from insights.core.dr import SkipComponent @@ -24,7 +24,7 @@ def running_rhel_containers_id(broker): podman_containers.append(container[2]) else: docker_containers.append(container[2]) - + # If there are same items from "podman ps" and "docker ps", add the items into "podman" list, and ignore "docker". common_ids = list(set(podman_containers).intersection(docker_containers)) new_dcoker_containers = [item for item in docker_containers if item not in common_ids] for item in podman_containers: @@ -56,15 +56,18 @@ def containers_inspect_data_datasource(broker): "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Created": "2022-10-21T23:47:24.506159696-04:00", "Path": "sleep" + ... } ... ] + Returns: list: item is JSON string containing filtered information. Raises: SkipComponent: When the filter/path does not exist or any exception occurs. """ + def _find(d, tag, path): if tag in d: yield d[tag] @@ -73,7 +76,6 @@ def _find(d, tag, path): for i in _find(v, tag, path): path.append(k) yield i - try: filters = get_filters(Specs.containers_inspect_vars) contents = [] From a191425a5fda298a88a23689667896fb07b0800e Mon Sep 17 00:00:00 2001 From: jiazhang Date: Wed, 26 Oct 2022 10:01:55 +0800 Subject: [PATCH 06/22] Add back docker_container_inspect Signed-off-by: jiazhang --- insights/specs/__init__.py | 1 + insights/tests/parsers/test_docker_inspect.py | 314 ++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 insights/tests/parsers/test_docker_inspect.py diff --git a/insights/specs/__init__.py b/insights/specs/__init__.py index 7626700f21..aaadb0a3ef 100644 --- a/insights/specs/__init__.py +++ b/insights/specs/__init__.py @@ -134,6 +134,7 @@ class Specs(SpecSet): dnf_module_list = RegistryPoint() dnf_modules = RegistryPoint() dnsmasq_config = RegistryPoint(multi_output=True) + docker_container_inspect = RegistryPoint(multi_output=True) docker_host_machine_id = RegistryPoint() docker_image_inspect = RegistryPoint(multi_output=True) docker_info = RegistryPoint() diff --git a/insights/tests/parsers/test_docker_inspect.py b/insights/tests/parsers/test_docker_inspect.py new file mode 100644 index 0000000000..98c2c9ec36 --- /dev/null +++ b/insights/tests/parsers/test_docker_inspect.py @@ -0,0 +1,314 @@ +import pytest +import doctest +from insights.parsers import docker_inspect, SkipException +from insights.tests import context_wrap + +DOCKER_CONTAINER_INSPECT = """ +[ +{ + "Id": "97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07", + "Created": "2016-06-23T05:12:25.433469799Z", + "Path": "/bin/bash", + "Args": [], + "State": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 15096, + "ExitCode": 0, + "Error": "", + "StartedAt": "2016-06-23T05:37:56.925378831Z", + "FinishedAt": "2016-06-23T05:33:02.012653984Z" + }, + "Image": "882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2", + "ResolvConfPath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/resolv.conf", + "HostnamePath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/hostname", + "HostsPath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/hosts", + "LogPath": "/var/lib/docker/containers/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07/97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07-json.log", + "Name": "/hehe2", + "RestartCount": 0, + "Driver": "devicemapper", + "ExecDriver": "native-0.2", + "MountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c429,c690", + "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c429,c690", + "AppArmorProfile": "", + "ExecIDs": null, + "HostConfig": { + "Binds": null, + "ContainerIDFile": "", + "LxcConf": [], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "KernelMemory": 0, + "CpuShares": 0, + "CpuPeriod": 0, + "CpusetCpus": "", + "CpusetMems": "", + "CpuQuota": 0, + "BlkioWeight": 0, + "OomKillDisable": false, + "MemorySwappiness": -1, + "Privileged": false, + "PortBindings": {}, + "Links": null, + "PublishAllPorts": false, + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": null, + "VolumesFrom": null, + "Devices": [], + "NetworkMode": "default", + "IpcMode": "", + "PidMode": "", + "UTSMode": "", + "CapAdd": null, + "CapDrop": null, + "GroupAdd": null, + "RestartPolicy": { + "Name": "no", + "MaximumRetryCount": 0 + }, + "SecurityOpt": null, + "ReadonlyRootfs": false, + "Ulimits": null, + "Sysctls": {}, + "LogConfig": { + "Type": "json-file", + "Config": { + "max-file": "7", + "max-size": "10m" + } + }, + "CgroupParent": "", + "ConsoleSize": [ + 0, + 0 + ], + "VolumeDriver": "", + "ShmSize": 67108864 + }, + "GraphDriver": { + "Name": "devicemapper", + "Data": { + "DeviceId": "433", + "DeviceName": "docker-253:0-71431059-97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07", + "DeviceSize": "107374182400" + } + }, + "Mounts": [], + "Config": { + "Hostname": "97d7cd1a5d8f", + "Domainname": "", + "User": "root", + "AttachStdin": true, + "AttachStdout": true, + "AttachStderr": true, + "Tty": true, + "OpenStdin": true, + "StdinOnce": true, + "Env": [ + "container=docker", + "PKGM=yum", + "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin" + ], + "Cmd": [ + "/bin/bash" + ], + "Image": "rhel7_imagemagick", + "Volumes": null, + "WorkingDir": "", + "Entrypoint": null, + "OnBuild": null, + "Labels": { + "Architecture": "x86_64", + "Authoritative_Registry": "registry.access.redhat.com", + "BZComponent": "rhel-server-docker", + "Build_Host": "rcm-img03.build.eng.bos.redhat.com", + "Name": "rhel7/rhel", + "Release": "61", + "Vendor": "Red Hat, Inc.", + "Version": "7.2", + "io.projectatomic.Temporary": "true" + }, + "StopSignal": "SIGTERM" + }, + "NetworkSettings": { + "Bridge": "", + "SandboxID": "f1cce5397340364aff043879ff5bd7e2ce2fcc5b81cfb7fe1833ce7b57eb6cf8", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": {}, + "SandboxKey": "/var/run/docker/netns/f1cce5397340", + "SecondaryIPAddresses": null, + "SecondaryIPv6Addresses": null, + "EndpointID": "59be4c94b2a1346eb0ec16472bc132e071d18733fd956c34b3b1defff9bba389", + "Gateway": "172.17.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "MacAddress": "02:42:ac:11:00:02", + "Networks": { + "bridge": { + "EndpointID": "59be4c94b2a1346eb0ec16472bc132e071d18733fd956c34b3b1defff9bba389", + "Gateway": "172.17.0.1", + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "02:42:ac:11:00:02" + } + } + } +} +] +""".splitlines() + +DOCKER_IMAGE_INSPECT = """ +[ +{ + "Id": "882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2", + "RepoTags": [ + "rhel7_imagemagick:latest" + ], + "RepoDigests": [], + "Parent": "34c167d900afb820ecab622a214ce3207af80ec755c0dcb6165b425087ddbc3a", + "Comment": "", + "Created": "2016-06-23T03:39:15.068803433Z", + "Container": "65410bf8809af52d2074c882917ea0651b119a91f460c1037bc99d4d5976532a", + "ContainerConfig": { + "Hostname": "cf3092658f01", + "Domainname": "", + "User": "root", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "container=docker", + "PKGM=yum", + "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin" + ], + "Cmd": [ + "/bin/sh", + "-c", + "yum install -y ImageMagick-6.7.8.9-10.el7" + ], + "Image": "34c167d900afb820ecab622a214ce3207af80ec755c0dcb6165b425087ddbc3a", + "Volumes": null, + "WorkingDir": "", + "Entrypoint": null, + "OnBuild": [], + "Labels": { + "Architecture": "x86_64", + "Authoritative_Registry": "registry.access.redhat.com", + "BZComponent": "rhel-server-docker", + "Build_Host": "rcm-img03.build.eng.bos.redhat.com", + "Name": "rhel7/rhel", + "Release": "61", + "Vendor": "Red Hat, Inc.", + "Version": "7.2" + } + }, + "DockerVersion": "1.9.1", + "Author": "", + "Config": { + "Hostname": "cf3092658f01", + "Domainname": "", + "User": "root", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "container=docker", + "PKGM=yum", + "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin" + ], + "Cmd": [ + "/usr/bin/bash" + ], + "Image": "34c167d900afb820ecab622a214ce3207af80ec755c0dcb6165b425087ddbc3a", + "Volumes": null, + "WorkingDir": "", + "Entrypoint": null, + "OnBuild": [], + "Labels": { + "Architecture": "x86_64", + "Authoritative_Registry": "registry.access.redhat.com", + "BZComponent": "rhel-server-docker", + "Build_Host": "rcm-img03.build.eng.bos.redhat.com", + "Name": "rhel7/rhel", + "Release": "61", + "Vendor": "Red Hat, Inc.", + "Version": "7.2" + } + }, + "Architecture": "amd64", + "Os": "linux", + "Size": 580094174, + "VirtualSize": 785437820, + "GraphDriver": { + "Name": "devicemapper", + "Data": { + "DeviceId": "431", + "DeviceName": "docker-253:0-71431059-882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2", + "DeviceSize": "107374182400" + } + } +} +] +""".splitlines() + + +DOCKER_CONTAINER_INSPECT_TRUNCATED = """ +[ +{ + "Id": "97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07", + "Created": "2016-06-23T05:12:25.433469799Z", + "Path": "/bin/bash", +""" + + +def test_docker_object_container_inspect(): + result = docker_inspect.DockerInspect(context_wrap(DOCKER_CONTAINER_INSPECT)) + assert result.get('Id') == "97d7cd1a5d8fd7730e83bb61ecbc993742438e966ac5c11910776b5d53f4ae07" + assert result.get('NetworkSettings').get('HairpinMode') is False + assert result.get('Config').get('Env') == ['container=docker', 'PKGM=yum', 'PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin'] + assert result.get('GraphDriver').get('Data').get('DeviceSize') == '107374182400' + + +def test_docker_object_image_inspect(): + result = docker_inspect.DockerInspect(context_wrap(DOCKER_IMAGE_INSPECT)) + assert result.get('Id') == "882ab98aae5394aebe91fe6d8a4297fa0387c3cfd421b2d892bddf218ac373b2" + assert result.get('Size') == 580094174 + assert result.get('Config').get('AttachStdin') is False + assert result.get('RepoDigests') == [] + + +def test_docker_container_inspect_truncated_input(): + with pytest.raises(SkipException): + docker_inspect.DockerInspectContainer(context_wrap(DOCKER_CONTAINER_INSPECT_TRUNCATED)) + + +def test_doc_test(): + dic = docker_inspect.DockerInspectContainer(context_wrap(DOCKER_CONTAINER_INSPECT)) + dii = docker_inspect.DockerInspectImage(context_wrap(DOCKER_IMAGE_INSPECT)) + env = { + 'container': dic, + 'image': dii, + } + failed, total = doctest.testmod(docker_inspect, globs=env) + assert failed == 0 From eddbaf9194fe6eb09b6c15f2e1deb09326b5a169 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Wed, 26 Oct 2022 14:37:53 +0800 Subject: [PATCH 07/22] Add deprecated warning Signed-off-by: jiazhang --- insights/parsers/docker_inspect.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/insights/parsers/docker_inspect.py b/insights/parsers/docker_inspect.py index f5bcc1effc..bb77e78317 100644 --- a/insights/parsers/docker_inspect.py +++ b/insights/parsers/docker_inspect.py @@ -12,6 +12,7 @@ from insights.core.marshalling import unmarshal from insights.parsers import SkipException from insights.specs import Specs +from insights.util import deprecated class DockerInspect(CommandParser, dict): @@ -70,6 +71,10 @@ class DockerInspectImage(DockerInspect): @parser(Specs.docker_container_inspect) class DockerInspectContainer(DockerInspect): """ + .. warning:: + This parser is deprecated, please use + :py:class:`insights.parsers.containers_inspect.ContainersInspect` instead. + Parse docker container inspect output using the DockerInspect parser class. Sample input:: @@ -98,4 +103,10 @@ class DockerInspectContainer(DockerInspect): False """ - pass + def __init__(self, *args, **kwargs): + deprecated( + DockerInspectContainer, + "Please use the :class:`insights.parsers.containers_inspect.ContainersInspect` instead.", + "3.0.300" + ) + super(DockerInspectContainer, self).__init__(*args, **kwargs) From dfd57cbc1f6d387ad7290f97764fa665cdd6d5a4 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Thu, 27 Oct 2022 09:39:25 +0800 Subject: [PATCH 08/22] Fix string format Signed-off-by: jiazhang --- insights/parsers/containers_inspect.py | 6 +++--- insights/tests/datasources/test_containers_inspect.py | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py index eea95b30e2..b0275ebef5 100644 --- a/insights/parsers/containers_inspect.py +++ b/insights/parsers/containers_inspect.py @@ -35,11 +35,11 @@ class ContainersInspect(CommandParser): Examples: >>> inspect_containers.data[0]["Id"] - 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8' + u'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8' >>> inspect_containers.data[0]["engine"] - 'podman' + u'podman' >>> inspect_containers.data[0]["Config"]["Annotations"]["io.podman.annotations.privileged"] - 'FALSE' + u'FALSE' """ def parse_content(self, content): diff --git a/insights/tests/datasources/test_containers_inspect.py b/insights/tests/datasources/test_containers_inspect.py index f29fb31795..633f1d5485 100644 --- a/insights/tests/datasources/test_containers_inspect.py +++ b/insights/tests/datasources/test_containers_inspect.py @@ -752,6 +752,9 @@ def test_containers_inspect_datasource(): result = containers_inspect_data_datasource(broker) assert result is not None assert isinstance(result, DatasourceProvider) + print("505050505") + print(result.content[0]) + print(EXPECTED_RESULT) assert result.content[0] == EXPECTED_RESULT assert result.relative_path == RELATIVE_PATH From e2ba9acce2a96a7933c36ff18ad4fa0f8231cbe9 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Thu, 27 Oct 2022 10:32:04 +0800 Subject: [PATCH 09/22] Update test data format Signed-off-by: jiazhang --- insights/parsers/containers_inspect.py | 12 ++++++------ .../specs/datasources/containers_inspect.py | 14 +------------- .../datasources/test_containers_inspect.py | 18 ++++++------------ 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py index b0275ebef5..6240df135b 100644 --- a/insights/parsers/containers_inspect.py +++ b/insights/parsers/containers_inspect.py @@ -34,12 +34,12 @@ class ContainersInspect(CommandParser): data (list): A list containing the parsed information Examples: - >>> inspect_containers.data[0]["Id"] - u'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8' - >>> inspect_containers.data[0]["engine"] - u'podman' - >>> inspect_containers.data[0]["Config"]["Annotations"]["io.podman.annotations.privileged"] - u'FALSE' + >>> str(inspect_containers.data[0]["Id"]) + 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8' + >>> str(inspect_containers.data[0]["engine"]) + 'podman' + >>> str(inspect_containers.data[0]["Config"]["Annotations"]["io.podman.annotations.privileged"]) + 'FALSE' """ def parse_content(self, content): diff --git a/insights/specs/datasources/containers_inspect.py b/insights/specs/datasources/containers_inspect.py index f464fea7cb..df6489dd67 100644 --- a/insights/specs/datasources/containers_inspect.py +++ b/insights/specs/datasources/containers_inspect.py @@ -17,20 +17,8 @@ def running_rhel_containers_id(broker): Returns a list of container_id of the running containers. """ containers_info = [] - docker_containers = [] - podman_containers = [] for container in broker[running_rhel_containers]: - if container[1] == "podman": - podman_containers.append(container[2]) - else: - docker_containers.append(container[2]) - # If there are same items from "podman ps" and "docker ps", add the items into "podman" list, and ignore "docker". - common_ids = list(set(podman_containers).intersection(docker_containers)) - new_dcoker_containers = [item for item in docker_containers if item not in common_ids] - for item in podman_containers: - containers_info.append(("podman", item)) - for item in new_dcoker_containers: - containers_info.append(("docker", item)) + containers_info.append((container[1], container[2])) if containers_info: return containers_info raise SkipComponent diff --git a/insights/tests/datasources/test_containers_inspect.py b/insights/tests/datasources/test_containers_inspect.py index 633f1d5485..17e771b6d5 100644 --- a/insights/tests/datasources/test_containers_inspect.py +++ b/insights/tests/datasources/test_containers_inspect.py @@ -7,6 +7,7 @@ from insights.specs import Specs from insights.core import filters from insights import dr +import json DOCKER_INSPECT_1 = """ @@ -697,12 +698,6 @@ Error: error inspecting object: no such object: "testnoid" """.strip() - -EXPECTED_RESULT = """ -[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "engine": "docker", "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}}}, {"Id": "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "engine": "podman", "Config": {"Annotations": {"io.podman.annotations.privileged": "TRUE"}}}] -""".strip() - - RELATIVE_PATH = 'insights_commands/containers_inspect' CONTAINERS_ID_EXPECTED_RESULT = [ @@ -713,6 +708,8 @@ ('docker', '538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54') ] +EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'ImageName': 'registry.access.redhat.com/rhel:latest', 'engine': 'docker', 'Config': {'Annotations': {'io.podman.annotations.privileged': 'FALSE'}}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'ImageName': 'registry.access.redhat.com/rhel:latest', 'engine': 'podman', 'Config': {'Annotations': {'io.podman.annotations.privileged': 'TRUE'}}}] + def setup_function(func): if Specs.containers_inspect_vars in filters._CACHE: @@ -732,7 +729,6 @@ def test_running_rhel_containers_id(): ("registry.access.redhat.com/rhel", "podman", "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8"), ("registry.access.redhat.com/rhel", "podman", "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e"), ("registry.access.redhat.com/rhel", "podman", "528890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54"), - ("registry.access.redhat.com/rhel", "docker", "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8"), ("registry.access.redhat.com/rhel", "docker", "38fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e"), ("registry.access.redhat.com/rhel", "docker", "538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54") ] @@ -752,11 +748,9 @@ def test_containers_inspect_datasource(): result = containers_inspect_data_datasource(broker) assert result is not None assert isinstance(result, DatasourceProvider) - print("505050505") - print(result.content[0]) - print(EXPECTED_RESULT) - assert result.content[0] == EXPECTED_RESULT - assert result.relative_path == RELATIVE_PATH + expected = DatasourceProvider(content=json.dumps(EXPECTED_RESULT), relative_path=RELATIVE_PATH) + assert result.content[0] == expected.content[0] + assert result.relative_path == expected.relative_path def test_containers_inspect_datasource_no_filter(): From 321e60c292b098b2396d9d1e910c891eaea824a4 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Thu, 27 Oct 2022 14:41:33 +0800 Subject: [PATCH 10/22] Update test data Signed-off-by: jiazhang --- .../specs/datasources/containers_inspect.py | 1 - .../datasources/test_containers_inspect.py | 231 ++++++++++++++++-- 2 files changed, 217 insertions(+), 15 deletions(-) diff --git a/insights/specs/datasources/containers_inspect.py b/insights/specs/datasources/containers_inspect.py index df6489dd67..455dffcd7c 100644 --- a/insights/specs/datasources/containers_inspect.py +++ b/insights/specs/datasources/containers_inspect.py @@ -78,7 +78,6 @@ def _find(d, tag, path): filter_result = {} filter_result['Id'] = raw_data['Id'] filter_result['Image'] = raw_data['Image'] - filter_result['ImageName'] = raw_data['ImageName'] filter_result['engine'] = content[1] for item in filters: path = [] diff --git a/insights/tests/datasources/test_containers_inspect.py b/insights/tests/datasources/test_containers_inspect.py index 17e771b6d5..604f5ae3ae 100644 --- a/insights/tests/datasources/test_containers_inspect.py +++ b/insights/tests/datasources/test_containers_inspect.py @@ -10,7 +10,7 @@ import json -DOCKER_INSPECT_1 = """ +INSPECT_1 = """ [ { "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", @@ -321,7 +321,7 @@ ] """.strip() -DOCKER_INSPECT_2 = """ +INSPECT_2 = """ [ { "Id": "28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e", @@ -686,7 +686,207 @@ ] """.strip() -DOCKER_INSPECT_3 = """ +INSPECT_3 = """ +[ + { + "Id": "c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921", + "Created": "2022-10-27T02:53:02.989997608Z", + "Path": "sleep", + "Args": [ + "1000000" + ], + "State": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 32688, + "ExitCode": 0, + "Error": "", + "StartedAt": "2022-10-27T02:53:03.380640461Z", + "FinishedAt": "0001-01-01T00:00:00Z" + }, + "Image": "sha256:acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b", + "ResolvConfPath": "/var/lib/docker/containers/c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921/resolv.conf", + "HostnamePath": "/var/lib/docker/containers/c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921/hostname", + "HostsPath": "/var/lib/docker/containers/c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921/hosts", + "LogPath": "", + "Name": "/quizzical_yalow", + "RestartCount": 0, + "Driver": "overlay2", + "MountLabel": "", + "ProcessLabel": "", + "AppArmorProfile": "", + "ExecIDs": null, + "HostConfig": { + "Binds": null, + "ContainerIDFile": "", + "LogConfig": { + "Type": "journald", + "Config": {} + }, + "NetworkMode": "default", + "PortBindings": {}, + "RestartPolicy": { + "Name": "no", + "MaximumRetryCount": 0 + }, + "AutoRemove": false, + "VolumeDriver": "", + "VolumesFrom": null, + "CapAdd": null, + "CapDrop": null, + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": null, + "GroupAdd": null, + "IpcMode": "", + "Cgroup": "", + "Links": null, + "OomScoreAdj": 0, + "PidMode": "", + "Privileged": true, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "SecurityOpt": [ + "label=disable" + ], + "UTSMode": "", + "UsernsMode": "", + "ShmSize": 67108864, + "Runtime": "docker-runc", + "ConsoleSize": [ + 0, + 0 + ], + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteIOps": null, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DiskQuota": 0, + "KernelMemory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": -1, + "OomKillDisable": false, + "PidsLimit": 0, + "Ulimits": null, + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0 + }, + "GraphDriver": { + "Name": "overlay2", + "Data": { + "LowerDir": "/var/lib/docker/overlay2/07b7d0c623c8ffe8951295328e3532f20f08e00922b77c7532c8e7afe0f05dc9-init/diff:/var/lib/docker/overlay2/64c2a13b1b7f27b198c9bb72cd61d766803d2d110646b536068e0021927f0682/diff:/var/lib/docker/overlay2/86b61c65ca8a7f18a48f15ee80e9d032adaee38835a0b0aceca8d02d50d78dd6/diff", + "MergedDir": "/var/lib/docker/overlay2/07b7d0c623c8ffe8951295328e3532f20f08e00922b77c7532c8e7afe0f05dc9/merged", + "UpperDir": "/var/lib/docker/overlay2/07b7d0c623c8ffe8951295328e3532f20f08e00922b77c7532c8e7afe0f05dc9/diff", + "WorkDir": "/var/lib/docker/overlay2/07b7d0c623c8ffe8951295328e3532f20f08e00922b77c7532c8e7afe0f05dc9/work" + } + }, + "Mounts": [], + "Config": { + "Hostname": "c7efee959ea8", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": true, + "AttachStderr": true, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "container=oci" + ], + "Cmd": [ + "sleep", + "1000000" + ], + "Image": "acf3e09a39c9", + "Volumes": null, + "WorkingDir": "", + "Entrypoint": null, + "OnBuild": null, + "Labels": { + "architecture": "x86_64", + "build-date": "2022-05-12T23:59:24.895710", + "com.redhat.build-host": "cpt-1003.osbs.prod.upshift.rdu2.redhat.com", + "com.redhat.component": "rhel-server-container", + "com.redhat.license_terms": "https://www.redhat.com/agreements", + "description": "The Red Hat Enterprise Linux Base image is designed to be a fully supported foundation for your containerized applications. This base image provides your operations and application teams with the packages, language runtimes and tools necessary to run, maintain, and troubleshoot all of your applications. This image is maintained by Red Hat and updated regularly. It is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. When used as the source for all of your containers, only one copy will ever be downloaded and cached in your production environment. Use this image just like you would a regular Red Hat Enterprise Linux distribution. Tools like yum, gzip, and bash are provided by default. For further information on how this image was built look at the /root/anacanda-ks.cfg file.", + "distribution-scope": "public", + "io.k8s.description": "The Red Hat Enterprise Linux Base image is designed to be a fully supported foundation for your containerized applications. This base image provides your operations and application teams with the packages, language runtimes and tools necessary to run, maintain, and troubleshoot all of your applications. This image is maintained by Red Hat and updated regularly. It is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. When used as the source for all of your containers, only one copy will ever be downloaded and cached in your production environment. Use this image just like you would a regular Red Hat Enterprise Linux distribution. Tools like yum, gzip, and bash are provided by default. For further information on how this image was built look at the /root/anacanda-ks.cfg file.", + "io.k8s.display-name": "Red Hat Enterprise Linux 7", + "io.openshift.tags": "base rhel7", + "name": "rhel7", + "release": "702", + "summary": "Provides the latest release of Red Hat Enterprise Linux 7 in a fully featured and supported base image.", + "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/rhel7/images/7.9-702", + "vcs-ref": "a4d1f0b8a9b923ca309da182943d17fe639d8c95", + "vcs-type": "git", + "vendor": "Red Hat, Inc.", + "version": "7.9" + } + }, + "NetworkSettings": { + "Bridge": "", + "SandboxID": "0a9908c68f05eac23b6ed79dce9843b6dda2469e1021671d58d882938bffc7b6", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": {}, + "SandboxKey": "/var/run/docker/netns/0a9908c68f05", + "SecondaryIPAddresses": null, + "SecondaryIPv6Addresses": null, + "EndpointID": "49f752615a815c97996215e72378d7e049036eef44149e96ae26e9049641d4c1", + "Gateway": "172.17.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.3", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "MacAddress": "02:42:ac:11:00:03", + "Networks": { + "bridge": { + "IPAMConfig": null, + "Links": null, + "Aliases": null, + "NetworkID": "3c2f5c9a9a9c21994ddaee6252842b8a9fb45c24d6dabf00ee36ffb12b70f0b7", + "EndpointID": "49f752615a815c97996215e72378d7e049036eef44149e96ae26e9049641d4c1", + "Gateway": "172.17.0.1", + "IPAddress": "172.17.0.3", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "02:42:ac:11:00:03" + } + } + } + } +] +""".strip() + +INSPECT_4 = """ [ { "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8" @@ -694,7 +894,7 @@ ] """.strip() -DOCKER_INSPECT_4 = """ +INSPECT_5 = """ Error: error inspecting object: no such object: "testnoid" """.strip() @@ -708,7 +908,7 @@ ('docker', '538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54') ] -EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'ImageName': 'registry.access.redhat.com/rhel:latest', 'engine': 'docker', 'Config': {'Annotations': {'io.podman.annotations.privileged': 'FALSE'}}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'ImageName': 'registry.access.redhat.com/rhel:latest', 'engine': 'podman', 'Config': {'Annotations': {'io.podman.annotations.privileged': 'TRUE'}}}] +EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': False}, 'Config': {'Annotations': {'io.podman.annotations.privileged': 'FALSE'}}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': True}, 'Config': {'Annotations': {'io.podman.annotations.privileged': 'TRUE'}}}, {'Id': 'c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921', 'Image': 'sha256:acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', 'engine': 'docker', 'HostConfig': {'Privileged': True}}] def setup_function(func): @@ -718,7 +918,7 @@ def setup_function(func): del filters.FILTERS[Specs.containers_inspect_vars] if func is test_containers_inspect_datasource or func is test_containers_inspect_datasource_NG_output_1 or func is test_containers_inspect_datasource_NG_output_2: - filters.add_filter(Specs.containers_inspect_vars, ["io.podman.annotations.privileged"]) + filters.add_filter(Specs.containers_inspect_vars, ["io.podman.annotations.privileged", "Privileged"]) if func is test_containers_inspect_datasource_no_filter: filters.add_filter(Specs.containers_inspect_vars, []) @@ -740,11 +940,14 @@ def test_running_rhel_containers_id(): def test_containers_inspect_datasource(): containers_inspect_data_1 = Mock() containers_inspect_data_2 = Mock() - containers_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() - containers_inspect_data_1.cmd = "/usr/bin/docker inspect aeaea3ead527" - containers_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() + containers_inspect_data_3 = Mock() + containers_inspect_data_1.content = INSPECT_1.splitlines() + containers_inspect_data_1.cmd = "/usr/bin/podman inspect aeaea3ead527" + containers_inspect_data_2.content = INSPECT_2.splitlines() containers_inspect_data_2.cmd = "/usr/bin/podman inspect 28fb57be8bb2" - broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_1, containers_inspect_data_2]} + containers_inspect_data_3.content = INSPECT_3.splitlines() + containers_inspect_data_3.cmd = "/usr/bin/docker inspect c7efee959ea8" + broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_1, containers_inspect_data_2, containers_inspect_data_3]} result = containers_inspect_data_datasource(broker) assert result is not None assert isinstance(result, DatasourceProvider) @@ -756,9 +959,9 @@ def test_containers_inspect_datasource(): def test_containers_inspect_datasource_no_filter(): containers_inspect_data_1 = Mock() containers_inspect_data_2 = Mock() - containers_inspect_data_1.content = DOCKER_INSPECT_1.splitlines() + containers_inspect_data_1.content = INSPECT_1.splitlines() containers_inspect_data_1.cmd = "/usr/bin/docker inspect aeaea3ead527" - containers_inspect_data_2.content = DOCKER_INSPECT_2.splitlines() + containers_inspect_data_2.content = INSPECT_2.splitlines() containers_inspect_data_2.cmd = "/usr/bin/podman inspect 28fb57be8bb2" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_1, containers_inspect_data_2]} with pytest.raises(SkipComponent) as e: @@ -768,7 +971,7 @@ def test_containers_inspect_datasource_no_filter(): def test_containers_inspect_datasource_NG_output_1(): containers_inspect_data_3 = Mock() - containers_inspect_data_3.content = DOCKER_INSPECT_3.splitlines() + containers_inspect_data_3.content = INSPECT_4.splitlines() containers_inspect_data_3.cmd = "/usr/bin/podman inspect 28fb57be8bb2" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_3]} with pytest.raises(SkipComponent) as e: @@ -778,7 +981,7 @@ def test_containers_inspect_datasource_NG_output_1(): def test_containers_inspect_datasource_NG_output_2(): containers_inspect_data_4 = Mock() - containers_inspect_data_4.content = DOCKER_INSPECT_4.splitlines() + containers_inspect_data_4.content = INSPECT_5.splitlines() containers_inspect_data_4.cmd = "/usr/bin/docker inspect aeaea3ead527" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_4]} with pytest.raises(SkipComponent) as e: From 7302090de1454e7c2d95e63d017a4e42d2068fb5 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Thu, 27 Oct 2022 16:11:16 +0800 Subject: [PATCH 11/22] Update test data Signed-off-by: jiazhang --- insights/tests/datasources/test_containers_inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insights/tests/datasources/test_containers_inspect.py b/insights/tests/datasources/test_containers_inspect.py index 604f5ae3ae..30a5e2864d 100644 --- a/insights/tests/datasources/test_containers_inspect.py +++ b/insights/tests/datasources/test_containers_inspect.py @@ -908,7 +908,7 @@ ('docker', '538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54') ] -EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': False}, 'Config': {'Annotations': {'io.podman.annotations.privileged': 'FALSE'}}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': True}, 'Config': {'Annotations': {'io.podman.annotations.privileged': 'TRUE'}}}, {'Id': 'c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921', 'Image': 'sha256:acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', 'engine': 'docker', 'HostConfig': {'Privileged': True}}] +EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': False}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': True}}, {'Id': 'c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921', 'Image': 'sha256:acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', 'engine': 'docker', 'HostConfig': {'Privileged': True}}] def setup_function(func): @@ -918,7 +918,7 @@ def setup_function(func): del filters.FILTERS[Specs.containers_inspect_vars] if func is test_containers_inspect_datasource or func is test_containers_inspect_datasource_NG_output_1 or func is test_containers_inspect_datasource_NG_output_2: - filters.add_filter(Specs.containers_inspect_vars, ["io.podman.annotations.privileged", "Privileged"]) + filters.add_filter(Specs.containers_inspect_vars, ["Privileged"]) if func is test_containers_inspect_datasource_no_filter: filters.add_filter(Specs.containers_inspect_vars, []) From 7756aac709c1af8a7c424261049a29634e10d179 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Thu, 27 Oct 2022 16:29:40 +0800 Subject: [PATCH 12/22] Update return value format Signed-off-by: jiazhang --- insights/specs/datasources/containers_inspect.py | 2 +- insights/tests/datasources/test_containers_inspect.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/insights/specs/datasources/containers_inspect.py b/insights/specs/datasources/containers_inspect.py index 455dffcd7c..4f81fa5570 100644 --- a/insights/specs/datasources/containers_inspect.py +++ b/insights/specs/datasources/containers_inspect.py @@ -77,7 +77,7 @@ def _find(d, tag, path): raw_data = json.loads(content_raw)[0] filter_result = {} filter_result['Id'] = raw_data['Id'] - filter_result['Image'] = raw_data['Image'] + filter_result['Image'] = raw_data['Image'].split("sha256:")[-1] filter_result['engine'] = content[1] for item in filters: path = [] diff --git a/insights/tests/datasources/test_containers_inspect.py b/insights/tests/datasources/test_containers_inspect.py index 30a5e2864d..84d69283a9 100644 --- a/insights/tests/datasources/test_containers_inspect.py +++ b/insights/tests/datasources/test_containers_inspect.py @@ -908,7 +908,7 @@ ('docker', '538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54') ] -EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': False}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': True}}, {'Id': 'c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921', 'Image': 'sha256:acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', 'engine': 'docker', 'HostConfig': {'Privileged': True}}] +EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': False}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': True}}, {'Id': 'c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921', 'Image': 'acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', 'engine': 'docker', 'HostConfig': {'Privileged': True}}] def setup_function(func): From fbc6b7cad21a3377f61fa5126bd76ab1d237947a Mon Sep 17 00:00:00 2001 From: jiazhang Date: Thu, 27 Oct 2022 16:48:54 +0800 Subject: [PATCH 13/22] Update parser test data Signed-off-by: jiazhang --- insights/parsers/containers_inspect.py | 7 +++---- insights/tests/parsers/test_containers_inspect.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py index 6240df135b..898cdbab87 100644 --- a/insights/parsers/containers_inspect.py +++ b/insights/parsers/containers_inspect.py @@ -24,9 +24,8 @@ class ContainersInspect(CommandParser): { "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", - "ImageName": "registry.access.redhat.com/rhel:latest", "engine": "podman", - "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}} + "HostConfig": {"Privileged": false}} } ] @@ -38,8 +37,8 @@ class ContainersInspect(CommandParser): 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8' >>> str(inspect_containers.data[0]["engine"]) 'podman' - >>> str(inspect_containers.data[0]["Config"]["Annotations"]["io.podman.annotations.privileged"]) - 'FALSE' + >>> inspect_containers.data[0]["HostConfig"]["Privileged"] + False """ def parse_content(self, content): diff --git a/insights/tests/parsers/test_containers_inspect.py b/insights/tests/parsers/test_containers_inspect.py index 0c5169d44d..ed3a3e376f 100644 --- a/insights/tests/parsers/test_containers_inspect.py +++ b/insights/tests/parsers/test_containers_inspect.py @@ -6,14 +6,14 @@ CONTAINERS_INSPECT = """ -[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "ImageName": "registry.access.redhat.com/rhel:latest", "engine": "podman", "Config": {"Annotations": {"io.podman.annotations.privileged": "FALSE"}}}] +[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "engine": "podman", "HostConfig": {"Privileged": false}}] """.strip() def test_containers_inspect(): containers_inspect_result = ContainersInspect(context_wrap(CONTAINERS_INSPECT)) assert containers_inspect_result.data[0]["Id"] == "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8" - assert containers_inspect_result.data[0]["Config"]["Annotations"]["io.podman.annotations.privileged"] == "FALSE" + assert containers_inspect_result.data[0]["HostConfig"]["Privileged"] is False def test_doc_examples(): From b5efc399cc6763984a72634820c1b57fcac86a12 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Mon, 31 Oct 2022 18:03:10 +0800 Subject: [PATCH 14/22] Update datasource details Signed-off-by: jiazhang --- insights/parsers/containers_inspect.py | 15 ++-- insights/parsers/docker_inspect.py | 2 +- insights/parsers/podman_inspect.py | 13 +++- insights/specs/__init__.py | 4 +- .../{ => container}/containers_inspect.py | 68 +++++++++---------- insights/specs/default.py | 7 +- .../test_containers_inspect.py | 39 ++++++----- .../tests/parsers/test_containers_inspect.py | 7 +- 8 files changed, 84 insertions(+), 71 deletions(-) rename insights/specs/datasources/{ => container}/containers_inspect.py (56%) rename insights/tests/datasources/{ => container}/test_containers_inspect.py (96%) diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py index 898cdbab87..ff94220bf6 100644 --- a/insights/parsers/containers_inspect.py +++ b/insights/parsers/containers_inspect.py @@ -5,14 +5,12 @@ This parser reads the output of commands: "/usr/bin/docker|podman inspect " which are used to show the metadata information of containers. """ -import json - -from insights import parser, CommandParser +from insights import parser, JSONParser from insights.specs import Specs @parser(Specs.containers_inspect) -class ContainersInspect(CommandParser): +class ContainersInspect(JSONParser): """ Class for parsing the output of the containers inspect commands ``/usr/bin/docker|podman inspect `` @@ -25,7 +23,8 @@ class ContainersInspect(CommandParser): "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "engine": "podman", - "HostConfig": {"Privileged": false}} + "Privileged": false, + "Cmd": ["sleep", "1000000"] } ] @@ -34,12 +33,12 @@ class ContainersInspect(CommandParser): Examples: >>> str(inspect_containers.data[0]["Id"]) - 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8' + 'aeaea3ead527' >>> str(inspect_containers.data[0]["engine"]) 'podman' - >>> inspect_containers.data[0]["HostConfig"]["Privileged"] + >>> inspect_containers.data[0]["Privileged"] False """ def parse_content(self, content): - self.data = json.loads(content[0]) + super(ContainersInspect, self).parse_content(content[0]) diff --git a/insights/parsers/docker_inspect.py b/insights/parsers/docker_inspect.py index bb77e78317..366ac4dc8c 100644 --- a/insights/parsers/docker_inspect.py +++ b/insights/parsers/docker_inspect.py @@ -107,6 +107,6 @@ def __init__(self, *args, **kwargs): deprecated( DockerInspectContainer, "Please use the :class:`insights.parsers.containers_inspect.ContainersInspect` instead.", - "3.0.300" + "3.2.25" ) super(DockerInspectContainer, self).__init__(*args, **kwargs) diff --git a/insights/parsers/podman_inspect.py b/insights/parsers/podman_inspect.py index d3a9e03c9d..7a6049e7f4 100644 --- a/insights/parsers/podman_inspect.py +++ b/insights/parsers/podman_inspect.py @@ -12,6 +12,7 @@ from insights.core.marshalling import unmarshal from insights.parsers import SkipException from insights.specs import Specs +from insights.util import deprecated class PodmanInspect(CommandParser, dict): @@ -66,6 +67,10 @@ class PodmanInspectImage(PodmanInspect): @parser(Specs.podman_container_inspect) class PodmanInspectContainer(PodmanInspect): """ + .. warning:: + This parser is deprecated, please use + :py:class:`insights.parsers.containers_inspect.ContainersInspect` instead. + Parse podman container inspect output using the PodmanInspect parser class. Sample input:: @@ -91,4 +96,10 @@ class PodmanInspectContainer(PodmanInspect): >>> container.get('State').get('Paused') is False True """ - pass + def __init__(self, *args, **kwargs): + deprecated( + PodmanInspectContainer, + "Please use the :class:`insights.parsers.containers_inspect.ContainersInspect` instead.", + "3.2.25" + ) + super(PodmanInspectContainer, self).__init__(*args, **kwargs) diff --git a/insights/specs/__init__.py b/insights/specs/__init__.py index aaadb0a3ef..225a8c0364 100644 --- a/insights/specs/__init__.py +++ b/insights/specs/__init__.py @@ -78,8 +78,6 @@ class Specs(SpecSet): cobbler_modules_conf = RegistryPoint() cobbler_settings = RegistryPoint() containers_policy = RegistryPoint() - containers_inspect_vars = RegistryPoint(filterable=True) - containers_inspect = RegistryPoint() corosync = RegistryPoint() corosync_cmapctl = RegistryPoint(multi_output=True) corosync_conf = RegistryPoint() @@ -794,3 +792,5 @@ class Specs(SpecSet): container_redhat_release = RegistryPoint(multi_output=True) container_nginx_conf = RegistryPoint(multi_output=True) container_installed_rpms = RegistryPoint(multi_output=True) + container_inspect_keys = RegistryPoint(filterable=True) + containers_inspect = RegistryPoint() diff --git a/insights/specs/datasources/containers_inspect.py b/insights/specs/datasources/container/containers_inspect.py similarity index 56% rename from insights/specs/datasources/containers_inspect.py rename to insights/specs/datasources/container/containers_inspect.py index 4f81fa5570..bc5a906ca1 100644 --- a/insights/specs/datasources/containers_inspect.py +++ b/insights/specs/datasources/container/containers_inspect.py @@ -1,13 +1,14 @@ """ Custom datasources for containers inspect information """ +import json +import os from insights.core.context import HostContext from insights.core.dr import SkipComponent -from insights.core.plugins import datasource -from insights.core.spec_factory import DatasourceProvider, foreach_execute from insights.core.filters import get_filters +from insights.core.plugins import datasource, ContentException +from insights.core.spec_factory import DatasourceProvider, foreach_execute from insights.specs import Specs -import json from insights.specs.datasources.container import running_rhel_containers @@ -19,9 +20,7 @@ def running_rhel_containers_id(broker): containers_info = [] for container in broker[running_rhel_containers]: containers_info.append((container[1], container[2])) - if containers_info: - return containers_info - raise SkipComponent + return containers_info class LocalSpecs(Specs): @@ -37,6 +36,12 @@ def containers_inspect_data_datasource(broker): This datasource provides the filtered information collected from ``/usr/bin/docker|podman inspect ``. + .. note:: + The path of the target key is like raw_data[key1][key2][target_key], + then the filter pattern is like "key1|key2|target_key". + If the value type of raw_data[key1][key2] is list, although the target_key is in the list, + the filter pattern must be "key1|key2", this datasource returns the whole value of the list. + Typical content of ``/usr/bin/docker|podman inspect `` file is:: [ @@ -55,40 +60,31 @@ def containers_inspect_data_datasource(broker): Raises: SkipComponent: When the filter/path does not exist or any exception occurs. """ - - def _find(d, tag, path): - if tag in d: - yield d[tag] - for k, v in d.items(): - if isinstance(v, dict): - for i in _find(v, tag, path): - path.append(k) - yield i try: - filters = get_filters(Specs.containers_inspect_vars) - contents = [] - for item in broker[LocalSpecs.containers_inspect_data_raw]: - engine = item.cmd.split()[0].split("bin/")[-1] - contents.append((item.content, engine)) - if contents and filters: + filters = list(get_filters(Specs.container_inspect_keys)) + if filters: + filters.sort() total_results = [] - for content in contents: - content_raw = "".join(content[0]) - raw_data = json.loads(content_raw)[0] - filter_result = {} - filter_result['Id'] = raw_data['Id'] - filter_result['Image'] = raw_data['Image'].split("sha256:")[-1] - filter_result['engine'] = content[1] + for item in broker[LocalSpecs.containers_inspect_data_raw]: + raw_data = json.loads(''.join(item.content))[0] + engine, _, container_id = item.cmd.split(None) + filter_result = dict(Id=container_id, engine=os.path.basename(engine)) + if 'Image' in raw_data: + filter_result['Image'] = raw_data['Image'].split("sha256:")[-1] for item in filters: - path = [] - for val in _find(raw_data, item, path): - mid_data = {item: val} - for path_item in path: - mid_data = {path_item: mid_data} - filter_result.update(mid_data) + val = raw_data + for key in item.split("|"): + if key in val: + val = val[key] + else: + break + # If the filtered key does not exist, skip it + if val == raw_data: + continue + filter_result[item.split("|")[-1]] = val total_results.append(filter_result) if total_results: - return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_commands/containers_inspect') + return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_commands/insights_containers/containers_inspect') except Exception as e: - raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) + raise ContentException("Unexpected content exception:{e}".format(e=str(e))) raise SkipComponent diff --git a/insights/specs/default.py b/insights/specs/default.py index 5dba77e2eb..1b4c3409ed 100644 --- a/insights/specs/default.py +++ b/insights/specs/default.py @@ -24,13 +24,13 @@ from insights.components.satellite import IsCapsule, IsSatellite611, IsSatellite from insights.specs import Specs from insights.specs.datasources import ( - aws, awx_manage, containers_inspect, cloud_init, candlepin_broker, corosync as corosync_ds, + aws, awx_manage, cloud_init, candlepin_broker, corosync as corosync_ds, dir_list, ethernet, httpd, ipcs, kernel_module_list, lpstat, md5chk, package_provides, ps as ps_datasource, sap, satellite_missed_queues, semanage, ssl_certificate, sys_fs_cgroup_memory_tasks_number, system_user_dirs, user_group, yum_updates, luks_devices) from insights.specs.datasources.sap import sap_hana_sid, sap_hana_sid_SID_nr from insights.specs.datasources.pcp import pcp_enabled, pmlog_summary_args -from insights.specs.datasources.container import running_rhel_containers +from insights.specs.datasources.container import running_rhel_containers, containers_inspect from insights.specs.datasources.container.nginx_conf import nginx_conf as container_nginx_conf_ds @@ -129,7 +129,6 @@ class DefaultSpecs(Specs): corosync = simple_file("/etc/sysconfig/corosync") corosync_cmapctl = foreach_execute(corosync_ds.corosync_cmapctl_cmds, "%s") corosync_conf = simple_file("/etc/corosync/corosync.conf") - containers_inspect = containers_inspect.containers_inspect_data_datasource cpu_cores = glob_file("sys/devices/system/cpu/cpu[0-9]*/online") cpu_siblings = glob_file("sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list") cpu_smt_active = simple_file("sys/devices/system/cpu/smt/active") @@ -680,3 +679,5 @@ class DefaultSpecs(Specs): container_installed_rpms = container_execute(running_rhel_containers, "rpm -qa --qf '%s'" % format_rpm(), context=HostContext, signum=signal.SIGTERM) container_nginx_conf = container_collect(container_nginx_conf_ds) container_redhat_release = container_collect(running_rhel_containers, "/etc/redhat-release") + containers_inspect = containers_inspect.containers_inspect_data_datasource + diff --git a/insights/tests/datasources/test_containers_inspect.py b/insights/tests/datasources/container/test_containers_inspect.py similarity index 96% rename from insights/tests/datasources/test_containers_inspect.py rename to insights/tests/datasources/container/test_containers_inspect.py index 84d69283a9..3bb42b1a24 100644 --- a/insights/tests/datasources/test_containers_inspect.py +++ b/insights/tests/datasources/container/test_containers_inspect.py @@ -1,13 +1,13 @@ import pytest +import json from mock.mock import Mock +from insights import dr +from insights.core import filters from insights.core.dr import SkipComponent from insights.core.spec_factory import DatasourceProvider -from insights.specs.datasources.containers_inspect import containers_inspect_data_datasource, LocalSpecs, running_rhel_containers_id -from insights.specs.datasources.container import running_rhel_containers from insights.specs import Specs -from insights.core import filters -from insights import dr -import json +from insights.specs.datasources.container.containers_inspect import containers_inspect_data_datasource, LocalSpecs, running_rhel_containers_id +from insights.specs.datasources.container import running_rhel_containers INSPECT_1 = """ @@ -898,7 +898,7 @@ Error: error inspecting object: no such object: "testnoid" """.strip() -RELATIVE_PATH = 'insights_commands/containers_inspect' +RELATIVE_PATH = 'insights_commands/insights_containers/containers_inspect' CONTAINERS_ID_EXPECTED_RESULT = [ ('podman', 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8'), @@ -908,19 +908,21 @@ ('docker', '538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54') ] -EXPECTED_RESULT = [{'Id': 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': False}}, {'Id': '28fb57be8bb204e652c472a406e0d99956c8d35d6e88abfc13253d101a00911e', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'engine': 'podman', 'HostConfig': {'Privileged': True}}, {'Id': 'c7efee959ea8910d68eaa5038d3ebf62ae593bfe96757b456c06f16281394921', 'Image': 'acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', 'engine': 'docker', 'HostConfig': {'Privileged': True}}] +EXPECTED_RESULT = [{'Id': 'aeaea3ead527', 'engine': 'podman', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', "Cmd": ["sleep", "1000000"], 'Privileged': False}, {'Id': '28fb57be8bb2', 'engine': 'podman', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', "Cmd": ["sleep", "1000000"], 'Privileged': True}, {'Id': 'c7efee959ea8', 'engine': 'docker', 'Image': 'acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', "Cmd": ["sleep", "1000000"], 'Privileged': True}] + +EXPECTED_RESULT_NG = [{'Id': '28fb57be8bb2', 'engine': 'podman'}] def setup_function(func): - if Specs.containers_inspect_vars in filters._CACHE: - del filters._CACHE[Specs.containers_inspect_vars] - if Specs.containers_inspect_vars in filters.FILTERS: - del filters.FILTERS[Specs.containers_inspect_vars] + if Specs.container_inspect_keys in filters._CACHE: + del filters._CACHE[Specs.container_inspect_keys] + if Specs.container_inspect_keys in filters.FILTERS: + del filters.FILTERS[Specs.container_inspect_keys] if func is test_containers_inspect_datasource or func is test_containers_inspect_datasource_NG_output_1 or func is test_containers_inspect_datasource_NG_output_2: - filters.add_filter(Specs.containers_inspect_vars, ["Privileged"]) + filters.add_filter(Specs.container_inspect_keys, ["HostConfig|Privileged", "NoSuchKey|Privileged", "Config|Cmd"]) if func is test_containers_inspect_datasource_no_filter: - filters.add_filter(Specs.containers_inspect_vars, []) + filters.add_filter(Specs.container_inspect_keys, []) def test_running_rhel_containers_id(): @@ -974,9 +976,12 @@ def test_containers_inspect_datasource_NG_output_1(): containers_inspect_data_3.content = INSPECT_4.splitlines() containers_inspect_data_3.cmd = "/usr/bin/podman inspect 28fb57be8bb2" broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_3]} - with pytest.raises(SkipComponent) as e: - containers_inspect_data_datasource(broker) - assert 'SkipComponent' in str(e) + result = containers_inspect_data_datasource(broker) + assert result is not None + assert isinstance(result, DatasourceProvider) + expected = DatasourceProvider(content=json.dumps(EXPECTED_RESULT_NG), relative_path=RELATIVE_PATH) + assert result.content[0] == expected.content[0] + assert result.relative_path == expected.relative_path def test_containers_inspect_datasource_NG_output_2(): @@ -986,4 +991,4 @@ def test_containers_inspect_datasource_NG_output_2(): broker = {LocalSpecs.containers_inspect_data_raw: [containers_inspect_data_4]} with pytest.raises(SkipComponent) as e: containers_inspect_data_datasource(broker) - assert 'Unexpected exception' in str(e) + assert 'Unexpected content exception' in str(e) diff --git a/insights/tests/parsers/test_containers_inspect.py b/insights/tests/parsers/test_containers_inspect.py index ed3a3e376f..46d5951b3c 100644 --- a/insights/tests/parsers/test_containers_inspect.py +++ b/insights/tests/parsers/test_containers_inspect.py @@ -6,14 +6,15 @@ CONTAINERS_INSPECT = """ -[{"Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "engine": "podman", "HostConfig": {"Privileged": false}}] +[{"Id": "aeaea3ead527", "engine": "podman", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "Cmd": ["sleep", "1000000"], "Privileged": false}, {"Id": "28fb57be8bb2", "engine": "podman", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "Cmd": ["sleep", "1000000"], "Privileged": true}, {"Id": "c7efee959ea8", "engine": "docker", "Image": "acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b", "Cmd": ["sleep", "1000000"], "Privileged": true}] """.strip() def test_containers_inspect(): containers_inspect_result = ContainersInspect(context_wrap(CONTAINERS_INSPECT)) - assert containers_inspect_result.data[0]["Id"] == "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8" - assert containers_inspect_result.data[0]["HostConfig"]["Privileged"] is False + assert containers_inspect_result.data[0]["Id"] == "aeaea3ead527" + assert containers_inspect_result.data[0]["Privileged"] is False + assert containers_inspect_result.data[0]["Cmd"] == ["sleep", "1000000"] def test_doc_examples(): From db1ebc2ac6f0245a0baec3143dd804ffef56e82d Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 1 Nov 2022 10:46:46 +0800 Subject: [PATCH 15/22] Update filter Signed-off-by: jiazhang --- insights/parsers/containers_inspect.py | 11 +++++++---- .../specs/datasources/container/containers_inspect.py | 4 ++-- .../datasources/container/test_containers_inspect.py | 4 ++-- insights/tests/parsers/test_containers_inspect.py | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py index ff94220bf6..da984089d6 100644 --- a/insights/parsers/containers_inspect.py +++ b/insights/parsers/containers_inspect.py @@ -20,11 +20,11 @@ class ContainersInspect(JSONParser): [ { - "Id": "aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8", + "Id": "aeaea3ead527", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "engine": "podman", - "Privileged": false, - "Cmd": ["sleep", "1000000"] + "HostConfig|Privileged": false, + "Config|Cmd": ["sleep", "1000000"] } ] @@ -32,11 +32,14 @@ class ContainersInspect(JSONParser): data (list): A list containing the parsed information Examples: + >>> from insights.core.filters import add_filter + >>> from insights.specs import Specs + >>> add_filter(Specs.container_inspect_keys, ['HostConfig|Privileged']) >>> str(inspect_containers.data[0]["Id"]) 'aeaea3ead527' >>> str(inspect_containers.data[0]["engine"]) 'podman' - >>> inspect_containers.data[0]["Privileged"] + >>> inspect_containers.data[0]["HostConfig|Privileged"] False """ diff --git a/insights/specs/datasources/container/containers_inspect.py b/insights/specs/datasources/container/containers_inspect.py index bc5a906ca1..804acb8678 100644 --- a/insights/specs/datasources/container/containers_inspect.py +++ b/insights/specs/datasources/container/containers_inspect.py @@ -81,10 +81,10 @@ def containers_inspect_data_datasource(broker): # If the filtered key does not exist, skip it if val == raw_data: continue - filter_result[item.split("|")[-1]] = val + filter_result[item] = val total_results.append(filter_result) if total_results: - return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_commands/insights_containers/containers_inspect') + return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_containers/containers_inspect') except Exception as e: raise ContentException("Unexpected content exception:{e}".format(e=str(e))) raise SkipComponent diff --git a/insights/tests/datasources/container/test_containers_inspect.py b/insights/tests/datasources/container/test_containers_inspect.py index 3bb42b1a24..4eced74643 100644 --- a/insights/tests/datasources/container/test_containers_inspect.py +++ b/insights/tests/datasources/container/test_containers_inspect.py @@ -898,7 +898,7 @@ Error: error inspecting object: no such object: "testnoid" """.strip() -RELATIVE_PATH = 'insights_commands/insights_containers/containers_inspect' +RELATIVE_PATH = 'insights_containers/containers_inspect' CONTAINERS_ID_EXPECTED_RESULT = [ ('podman', 'aeaea3ead52724bb525bb2b5c619d67836250756920f0cb9884431ba53b476d8'), @@ -908,7 +908,7 @@ ('docker', '538890e93bf71736e00a87c7a1fa33e5bb03a9a196e5b10faaa9e545e749aa54') ] -EXPECTED_RESULT = [{'Id': 'aeaea3ead527', 'engine': 'podman', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', "Cmd": ["sleep", "1000000"], 'Privileged': False}, {'Id': '28fb57be8bb2', 'engine': 'podman', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', "Cmd": ["sleep", "1000000"], 'Privileged': True}, {'Id': 'c7efee959ea8', 'engine': 'docker', 'Image': 'acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', "Cmd": ["sleep", "1000000"], 'Privileged': True}] +EXPECTED_RESULT = [{'Id': 'aeaea3ead527', 'engine': 'podman', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'Config|Cmd': ["sleep", "1000000"], 'HostConfig|Privileged': False}, {'Id': '28fb57be8bb2', 'engine': 'podman', 'Image': '538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b', 'Config|Cmd': ["sleep", "1000000"], 'HostConfig|Privileged': True}, {'Id': 'c7efee959ea8', 'engine': 'docker', 'Image': 'acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b', 'Config|Cmd': ["sleep", "1000000"], 'HostConfig|Privileged': True}] EXPECTED_RESULT_NG = [{'Id': '28fb57be8bb2', 'engine': 'podman'}] diff --git a/insights/tests/parsers/test_containers_inspect.py b/insights/tests/parsers/test_containers_inspect.py index 46d5951b3c..83ba3e1277 100644 --- a/insights/tests/parsers/test_containers_inspect.py +++ b/insights/tests/parsers/test_containers_inspect.py @@ -6,15 +6,15 @@ CONTAINERS_INSPECT = """ -[{"Id": "aeaea3ead527", "engine": "podman", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "Cmd": ["sleep", "1000000"], "Privileged": false}, {"Id": "28fb57be8bb2", "engine": "podman", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "Cmd": ["sleep", "1000000"], "Privileged": true}, {"Id": "c7efee959ea8", "engine": "docker", "Image": "acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b", "Cmd": ["sleep", "1000000"], "Privileged": true}] +[{"Id": "aeaea3ead527", "engine": "podman", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "Config|Cmd": ["sleep", "1000000"], "HostConfig|Privileged": false}, {"Id": "28fb57be8bb2", "engine": "podman", "Image": "538460c14d75dee1504e56ad8ddb7fe039093b1530ef8f90442a454b9aa3dc8b", "Config|Cmd": ["sleep", "1000000"], "HostConfig|Privileged": true}, {"Id": "c7efee959ea8", "engine": "docker", "Image": "acf3e09a39c95d354539b6591298be0b0814f5d74e95e722863241192b9a079b", "Config|Cmd": ["sleep", "1000000"], "HostConfig|Privileged": true}] """.strip() def test_containers_inspect(): containers_inspect_result = ContainersInspect(context_wrap(CONTAINERS_INSPECT)) assert containers_inspect_result.data[0]["Id"] == "aeaea3ead527" - assert containers_inspect_result.data[0]["Privileged"] is False - assert containers_inspect_result.data[0]["Cmd"] == ["sleep", "1000000"] + assert containers_inspect_result.data[0]["HostConfig|Privileged"] is False + assert containers_inspect_result.data[0]["Config|Cmd"] == ["sleep", "1000000"] def test_doc_examples(): From 80169290917d88e023ebfca4bded28fa65aaec41 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 1 Nov 2022 10:50:58 +0800 Subject: [PATCH 16/22] Update docstring Signed-off-by: jiazhang --- insights/parsers/containers_inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insights/parsers/containers_inspect.py b/insights/parsers/containers_inspect.py index da984089d6..715152d386 100644 --- a/insights/parsers/containers_inspect.py +++ b/insights/parsers/containers_inspect.py @@ -1,6 +1,6 @@ """ -Containers inspect -================== +ContainersInspect - commands ``docker|podman inspect`` +====================================================== This parser reads the output of commands: "/usr/bin/docker|podman inspect " which are used to show the metadata information of containers. From 0ba5466849c2c8a18bf5af303d818d2a6a11972d Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 1 Nov 2022 11:07:13 +0800 Subject: [PATCH 17/22] Update doc Signed-off-by: jiazhang --- docs/custom_datasources_index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/custom_datasources_index.rst b/docs/custom_datasources_index.rst index 86fcac0ec7..2dd11b1e36 100644 --- a/docs/custom_datasources_index.rst +++ b/docs/custom_datasources_index.rst @@ -43,10 +43,10 @@ insights.specs.datasources.cloud_init :show-inheritance: :undoc-members: -insights.specs.datasources.containers_inspect ---------------------------------------------- +insights.specs.datasources.container.containers_inspect +------------------------------------------------------- -.. automodule:: insights.specs.datasources.containers_inspect +.. automodule:: insights.specs.datasources.container.containers_inspect :members: running_rhel_containers_id, containers_inspect_data_datasource :show-inheritance: :undoc-members: From e82454a8b05dab95ea8493709884c880959c4d11 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 1 Nov 2022 15:18:37 +0800 Subject: [PATCH 18/22] Update no_filters Signed-off-by: jiazhang --- insights/specs/datasources/container/containers_inspect.py | 5 +++++ .../tests/datasources/container/test_containers_inspect.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/insights/specs/datasources/container/containers_inspect.py b/insights/specs/datasources/container/containers_inspect.py index 804acb8678..9cacd61518 100644 --- a/insights/specs/datasources/container/containers_inspect.py +++ b/insights/specs/datasources/container/containers_inspect.py @@ -41,6 +41,7 @@ def containers_inspect_data_datasource(broker): then the filter pattern is like "key1|key2|target_key". If the value type of raw_data[key1][key2] is list, although the target_key is in the list, the filter pattern must be "key1|key2", this datasource returns the whole value of the list. + The value of "Id" and "Image" are checked from raw data directly, no need filter these items. Typical content of ``/usr/bin/docker|podman inspect `` file is:: @@ -60,9 +61,13 @@ def containers_inspect_data_datasource(broker): Raises: SkipComponent: When the filter/path does not exist or any exception occurs. """ + NO_FILTERS = ["Id", "Image"] try: filters = list(get_filters(Specs.container_inspect_keys)) if filters: + for item in NO_FILTERS: + if item in filters: + filters.remove(item) filters.sort() total_results = [] for item in broker[LocalSpecs.containers_inspect_data_raw]: diff --git a/insights/tests/datasources/container/test_containers_inspect.py b/insights/tests/datasources/container/test_containers_inspect.py index 4eced74643..13077ebd21 100644 --- a/insights/tests/datasources/container/test_containers_inspect.py +++ b/insights/tests/datasources/container/test_containers_inspect.py @@ -920,7 +920,7 @@ def setup_function(func): del filters.FILTERS[Specs.container_inspect_keys] if func is test_containers_inspect_datasource or func is test_containers_inspect_datasource_NG_output_1 or func is test_containers_inspect_datasource_NG_output_2: - filters.add_filter(Specs.container_inspect_keys, ["HostConfig|Privileged", "NoSuchKey|Privileged", "Config|Cmd"]) + filters.add_filter(Specs.container_inspect_keys, ["HostConfig|Privileged", "NoSuchKey|Privileged", "Config|Cmd", "Id", "Image"]) if func is test_containers_inspect_datasource_no_filter: filters.add_filter(Specs.container_inspect_keys, []) From 6b45a714322c6c293370823110691035933b0b84 Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 1 Nov 2022 15:39:27 +0800 Subject: [PATCH 19/22] Update default format Signed-off-by: jiazhang --- insights/specs/default.py | 1 - 1 file changed, 1 deletion(-) diff --git a/insights/specs/default.py b/insights/specs/default.py index 1b4c3409ed..b01e2ff958 100644 --- a/insights/specs/default.py +++ b/insights/specs/default.py @@ -680,4 +680,3 @@ class DefaultSpecs(Specs): container_nginx_conf = container_collect(container_nginx_conf_ds) container_redhat_release = container_collect(running_rhel_containers, "/etc/redhat-release") containers_inspect = containers_inspect.containers_inspect_data_datasource - From 1c84f7d61b73106630ced2760eecdacf17decbca Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 1 Nov 2022 16:16:40 +0800 Subject: [PATCH 20/22] Update deprecated info Signed-off-by: jiazhang --- insights/parsers/docker_inspect.py | 11 +++++++++++ insights/parsers/podman_inspect.py | 11 +++++++++++ .../specs/datasources/container/containers_inspect.py | 8 ++------ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/insights/parsers/docker_inspect.py b/insights/parsers/docker_inspect.py index 366ac4dc8c..fbdcd0be68 100644 --- a/insights/parsers/docker_inspect.py +++ b/insights/parsers/docker_inspect.py @@ -17,6 +17,10 @@ class DockerInspect(CommandParser, dict): """ + .. warning:: + This class is deprecated, please use + :py:class:`insights.parsers.containers_inspect.ContainersInspect` instead. + Parse the output of command "docker inspect --type=image" and "docker inspect --type=container". The output of these two commands is formatted as JSON, so "json.loads" is an option to parse the output in the future. @@ -24,6 +28,13 @@ class DockerInspect(CommandParser, dict): Raises: SkipException: If content is not provided """ + def __init__(self, *args, **kwargs): + deprecated( + DockerInspect, + "Please use the :class:`insights.parsers.containers_inspect.ContainersInspect` instead.", + "3.2.25" + ) + super(DockerInspect, self).__init__(*args, **kwargs) def parse_content(self, content): if not content: diff --git a/insights/parsers/podman_inspect.py b/insights/parsers/podman_inspect.py index 7a6049e7f4..6c279dd824 100644 --- a/insights/parsers/podman_inspect.py +++ b/insights/parsers/podman_inspect.py @@ -17,6 +17,10 @@ class PodmanInspect(CommandParser, dict): """ + .. warning:: + This class is deprecated, please use + :py:class:`insights.parsers.containers_inspect.ContainersInspect` instead. + Parse the output of command "podman inspect --type=image" and "podman inspect --type=container". The output of these two commands is formatted as JSON, so "json.loads" is an option to parse the output in the future. @@ -24,6 +28,13 @@ class PodmanInspect(CommandParser, dict): Raises: SkipException: If content is not provided """ + def __init__(self, *args, **kwargs): + deprecated( + PodmanInspect, + "Please use the :class:`insights.parsers.containers_inspect.ContainersInspect` instead.", + "3.2.25" + ) + super(PodmanInspect, self).__init__(*args, **kwargs) def parse_content(self, content): if not content: diff --git a/insights/specs/datasources/container/containers_inspect.py b/insights/specs/datasources/container/containers_inspect.py index 9cacd61518..d893d42092 100644 --- a/insights/specs/datasources/container/containers_inspect.py +++ b/insights/specs/datasources/container/containers_inspect.py @@ -61,14 +61,10 @@ def containers_inspect_data_datasource(broker): Raises: SkipComponent: When the filter/path does not exist or any exception occurs. """ - NO_FILTERS = ["Id", "Image"] try: - filters = list(get_filters(Specs.container_inspect_keys)) + NO_FILTERS = {'Id', 'Image'} + filters = sorted(set(get_filters(Specs.container_inspect_keys)) - NO_FILTERS) if filters: - for item in NO_FILTERS: - if item in filters: - filters.remove(item) - filters.sort() total_results = [] for item in broker[LocalSpecs.containers_inspect_data_raw]: raw_data = json.loads(''.join(item.content))[0] From 5a54a16aac30076aadc84a5555fa2a7eaec9fc7d Mon Sep 17 00:00:00 2001 From: jiazhang Date: Tue, 1 Nov 2022 16:26:36 +0800 Subject: [PATCH 21/22] Update set format Signed-off-by: jiazhang --- insights/specs/datasources/container/containers_inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insights/specs/datasources/container/containers_inspect.py b/insights/specs/datasources/container/containers_inspect.py index d893d42092..1b2aa31e86 100644 --- a/insights/specs/datasources/container/containers_inspect.py +++ b/insights/specs/datasources/container/containers_inspect.py @@ -62,8 +62,8 @@ def containers_inspect_data_datasource(broker): SkipComponent: When the filter/path does not exist or any exception occurs. """ try: - NO_FILTERS = {'Id', 'Image'} - filters = sorted(set(get_filters(Specs.container_inspect_keys)) - NO_FILTERS) + no_filters = ['Id', 'Image'] + filters = sorted(set(get_filters(Specs.container_inspect_keys)) - set(no_filters)) if filters: total_results = [] for item in broker[LocalSpecs.containers_inspect_data_raw]: From 85a72f06ab9abbdfeb9aa0874d88f9c516fc5cba Mon Sep 17 00:00:00 2001 From: jiazhang Date: Thu, 3 Nov 2022 15:29:40 +0800 Subject: [PATCH 22/22] Add a comment for condition checking if total_results is not null Signed-off-by: jiazhang --- insights/specs/datasources/container/containers_inspect.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/insights/specs/datasources/container/containers_inspect.py b/insights/specs/datasources/container/containers_inspect.py index 1b2aa31e86..662656f11c 100644 --- a/insights/specs/datasources/container/containers_inspect.py +++ b/insights/specs/datasources/container/containers_inspect.py @@ -84,6 +84,9 @@ def containers_inspect_data_datasource(broker): continue filter_result[item] = val total_results.append(filter_result) + # Generally the False branch of this condition will never reach, since the required component + # LocalSpecs.containers_inspect_data_raw can guarantee total_results is not null. However, it's worth + # leaving this condition as an explicit assertion to avoid unexpected situation. if total_results: return DatasourceProvider(content=json.dumps(total_results), relative_path='insights_containers/containers_inspect') except Exception as e: