Skip to content
This repository has been archived by the owner on Oct 13, 2023. It is now read-only.

Commit

Permalink
Covscan: Fix the command and add RHEL 9 support
Browse files Browse the repository at this point in the history
- Fixes `images:covscan` command for 4.13. SSL CA certificates are
  currently added too late so we see certificate errors with yum
installs (`rhsm-pulp.corp.redhat.com` is now on HTTPS).
- Fixes missing `cppcheck` package error by adding
  `rhel-8-codeready-builder-rpms` repo.
- Adds support for scanning RHEL 9 based images.
- Adds `--podman-sudo` flag. Unless specified, podman will be run in
  rootless mode so that we will not see files owned by root on Jenkins.
- Adds `--podman-tmpdir` option to customize the directory for image
  downloads. Currently, this directory is a subdirectory of `--result-archive`,
  which might be on an NFS share that doesn't support SELinux labeling
  and cause SELinux issues.
  • Loading branch information
vfreex committed Apr 6, 2023
1 parent effdcd6 commit aad9795
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 34 deletions.
30 changes: 21 additions & 9 deletions doozerlib/cli/__main__.py
Expand Up @@ -7,7 +7,7 @@
import sys
import subprocess
import urllib.request, urllib.parse, urllib.error
from typing import Dict
from typing import Dict, cast
import tempfile
import traceback
import koji
Expand All @@ -18,6 +18,7 @@

from future import standard_library
from doozerlib import Runtime, state, cli as cli_package
from doozerlib.distgit import ImageDistGitRepo
from doozerlib.pushd import Dir
from doozerlib.model import Missing
from doozerlib.brew import get_watch_task_info_copy
Expand Down Expand Up @@ -239,6 +240,8 @@ def db_select(runtime, operation, attribute, match, like, where, sort_by, limit,
help="Absolute path of yum repo to make available for rhel-7 scanner images")
@click.option("--local-repo-rhel-8", metavar="REPO_DIR", default=[], multiple=True,
help="Absolute path of yum repo to make available for rhel-8 scanner images")
@click.option("--local-repo-rhel-9", metavar="REPO_DIR", default=[], multiple=True,
help="Absolute path of yum repo to make available for rhel-9 scanner images")
@click.option("--repo-type", metavar="REPO_TYPE", envvar="OIT_IMAGES_REPO_TYPE",
default="unsigned",
help="Repo group type to use for version autodetection scan (e.g. signed, unsigned).")
Expand All @@ -249,9 +252,12 @@ def db_select(runtime, operation, attribute, match, like, where, sort_by, limit,
@click.option('--ignore-waived', default=False, is_flag=True,
help='Ignore any previously detected waived results (all=diff)')
@click.option('--https-proxy', default='', help='HTTPS proxy to be used during image builds')
@click.option('--podman-sudo', is_flag=True, help="Run podman with sudo")
@click.option("--podman-tmpdir", help='Set the temporary storage location of downloaded container images for podman')
@pass_runtime
def images_covscan(runtime, result_archive, local_repo_rhel_7, local_repo_rhel_8, repo_type,
preserve_builder_images, force_analysis, ignore_waived, https_proxy):
def images_covscan(runtime: Runtime, result_archive, local_repo_rhel_7, local_repo_rhel_8, local_repo_rhel_9, repo_type,
preserve_builder_images, force_analysis, ignore_waived, https_proxy,
podman_sudo: bool, podman_tmpdir: Optional[str]):
"""
Runs a coverity scan against the specified images.
Expand Down Expand Up @@ -345,19 +351,21 @@ def absolutize(path):
result_archive = absolutize(result_archive)
local_repo_rhel_7 = [absolutize(repo) for repo in local_repo_rhel_7]
local_repo_rhel_8 = [absolutize(repo) for repo in local_repo_rhel_8]
local_repo_rhel_9 = [absolutize(repo) for repo in local_repo_rhel_9]

runtime.initialize()

def delete_images(key, value):
"""
Deletes images with a particular label value
"""
rc, image_list, stderr = exectools.cmd_gather(f'sudo podman images -q --filter label={key}={value}', strip=True)
podman_cmd = "sudo podman" if podman_sudo else "podman"
rc, image_list, stderr = exectools.cmd_gather(f'{podman_cmd} images -q --filter label={key}={value}', strip=True)
if not image_list:
runtime.logger.info(f'No images found with {key}={value} label exist: {stderr}')
return
targets = " ".join(image_list.split())
rc, _, _ = exectools.cmd_gather(f'sudo podman rmi {targets}')
rc, _, _ = exectools.cmd_gather(f'{podman_cmd} rmi {targets}')
if rc != 0:
runtime.logger.warning(f'Unable to remove all images with {key}={value}')

Expand All @@ -370,13 +378,17 @@ def delete_images(key, value):
successes = []
failures = []
for image in runtime.image_metas():
with Dir(image.distgit_repo().distgit_dir):
image.distgit_repo().pull_sources()
dg_commit_hash, _ = exectools.cmd_assert('git rev-parse HEAD', strip=True)
dg = cast(ImageDistGitRepo, image.distgit_repo())
with Dir(dg.distgit_dir):
dg.pull_sources()
dg_commit_hash = dg.sha
if not dg_commit_hash:
raise ValueError(f"Distgit commit hash for {dg.name} is unknown.")
cc = coverity.CoverityContext(image, dg_commit_hash, result_archive, repo_type=repo_type,
local_repo_rhel_7=local_repo_rhel_7, local_repo_rhel_8=local_repo_rhel_8,
local_repo_rhel_9=local_repo_rhel_9,
force_analysis=force_analysis, ignore_waived=ignore_waived,
https_proxy=https_proxy)
https_proxy=https_proxy, podman_sudo=podman_sudo, podman_tmpdir=podman_tmpdir)

if image.covscan(cc):
successes.append(image.distgit_key)
Expand Down
102 changes: 79 additions & 23 deletions doozerlib/coverity.py
Expand Up @@ -20,34 +20,38 @@
class CoverityContext(object):

def __init__(self, image, dg_commit_hash: str, result_archive: str, repo_type: str = 'unsigned',
local_repo_rhel_7: List[str] = [], local_repo_rhel_8: List[str] = [], force_analysis: bool = False,
ignore_waived: bool = False, https_proxy: str = ''):
local_repo_rhel_7: List[str] = [], local_repo_rhel_8: List[str] = [],
local_repo_rhel_9: List[str] = [], force_analysis: bool = False,
ignore_waived: bool = False, https_proxy: str = '', podman_sudo: bool = False,
podman_tmpdir: Optional[str] = None):
self.image = image # ImageMetadata
self.dg_commit_hash = dg_commit_hash
self.result_archive_path = pathlib.Path(result_archive)
self.tmp_path = self.result_archive_path.joinpath('tmp')
self.tmp_path.mkdir(exist_ok=True, parents=True)
self.dg_archive_path = self.result_archive_path.joinpath(image.distgit_key)
self.dg_archive_path.mkdir(parents=True, exist_ok=True) # /<archive-dir>/<dg-key>
self.archive_commit_results_path = self.dg_archive_path.joinpath(dg_commit_hash) # /<archive-dir>/<dg-key>/<hash>
self.repo_type = repo_type
self.local_repo_rhel_7 = local_repo_rhel_7
self.local_repo_rhel_8 = local_repo_rhel_8
self.local_repo_rhel_9 = local_repo_rhel_9
self.logger = image.logger
self.runtime = image.runtime
self.force_analysis = force_analysis
self.ignore_waived = ignore_waived
self.https_proxy = https_proxy
self.podman_sudo = podman_sudo
self.podman_cmd = 'sudo podman' if self.podman_sudo else 'podman'

# Podman is going to create a significant amount of container image data
# Make sure there is plenty of space. Override TMPDIR, because podman
# uses this environment variable.
self.podman_tmpdir = self.tmp_path.joinpath('podman')
self.podman_tmpdir.mkdir(exist_ok=True)
self.podman_cmd = 'sudo podman '
self.podman_env = {
'TMPDIR': str(self.podman_tmpdir)
}
self.podman_env = {}
if podman_tmpdir:
podman_tmpdir_path = pathlib.Path(podman_tmpdir)
podman_tmpdir_path.mkdir(exist_ok=True)
self.podman_env = {
'TMPDIR': str(podman_tmpdir_path.absolute())
}

# Runtime coverity scanning output directory; each stage will have an entry beneath this.
self.cov_root_path: pathlib.Path = image.distgit_repo().dg_path.joinpath('cov')
Expand Down Expand Up @@ -180,7 +184,24 @@ def parent_repo_injection_info(self) -> (str, str):
vol_mount_arg += f' -v {lr}:/covscan_local_{idx}_rhel_8:z'
else:
make_image_repo_files += 'RUN if cat /etc/redhat-release | grep "release 8"; then curl -k https://copr.devel.redhat.com/coprs/kdudka/covscan/repo/epel-8/kdudka-covscan-epel-8.repo --output /etc/yum.repos.d/covscan.repo; fi\n'
make_image_repo_files += 'RUN if cat /etc/redhat-release | grep "release 8"; then curl -k https://copr.devel.redhat.com/coprs/kdudka/covscan-testing/repo/epel-8/kdudka-covscan-testing-epel-8.repo --output /etc/yum.repos.d/covscan-testing.repo; fi\n'

if self.local_repo_rhel_9:
for idx, lr in enumerate(self.local_repo_rhel_9):
make_image_repo_files += f"""
# Create a repo able to pull from the local filesystem and prioritize it for speed.
RUN if cat /etc/redhat-release | grep "release 9"; then echo '[covscan_local_{idx}]' > /etc/yum.repos.d/covscan_local_{idx}.repo && \
echo 'baseurl=file:///covscan_local_{idx}_rhel_9' >> /etc/yum.repos.d/covscan_local_{idx}.repo && \
echo skip_if_unavailable=True >> /etc/yum.repos.d/covscan_local_{idx}.repo && \
echo gpgcheck=0 >> /etc/yum.repos.d/covscan_local_{idx}.repo && \
echo enabled=1 >> /etc/yum.repos.d/covscan_local_{idx}.repo && \
echo enabled_metadata=1 >> /etc/yum.repos.d/covscan_local_{idx}.repo && \
echo priority=1 >> /etc/yum.repos.d/covscan_local_{idx}.repo; fi
"""
vol_mount_arg += f' -v {lr}:/covscan_local_{idx}_rhel_9:z'
else:
make_image_repo_files += 'RUN if cat /etc/redhat-release | grep "release 9"; then curl -k https://copr.devel.redhat.com/coprs/kdudka/covscan/repo/epel-9/kdudka-covscan-epel-9.repo --output /etc/yum.repos.d/covscan.repo; fi\n'
make_image_repo_files += 'RUN if cat /etc/redhat-release | grep "release 9"; then curl -k https://copr.devel.redhat.com/coprs/kdudka/covscan-testing/repo/epel-9/kdudka-covscan-testing-epel-9.repo --output /etc/yum.repos.d/covscan-testing.repo; fi\n'
return make_image_repo_files, vol_mount_arg

def build_args(self) -> str:
Expand Down Expand Up @@ -213,31 +234,65 @@ def _covscan_prepare_parent(cc: CoverityContext, parent_image_name, parent_tag)
f.write('''
#!/bin/sh
set -o xtrace
if cat /etc/redhat-release | grep "release 9"; then
# For an el9 layer, make sure baseos & appstream are
# available for tools like python to install.
cat <<EOF > /etc/yum.repos.d/el9.repo
[rhel-9-appstream-rpms-x86_64]
baseurl = https://rhsm-pulp.corp.redhat.com/content/dist/rhel9/9/x86_64/appstream/os/
enabled = 1
name = rhel-9-appstream-rpms-x86_64
gpgcheck = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
[rhel-9-baseos-rpms-x86_64]
baseurl = https://rhsm-pulp.corp.redhat.com/content/dist/rhel9/9/x86_64/baseos/os/
enabled = 1
name = rhel-9-baseos-rpms-x86_64
gpgcheck = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
EOF
# Enable epel for csmock
yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
# Install Python 3.9
yum -y install python3
if cat /etc/redhat-release | grep "release 8"; then
elif cat /etc/redhat-release | grep "release 8"; then
cp /tmp/oit.repo /etc/yum.repos.d/oit.repo
# For an el8 layer, make sure baseos & appstream are
# available for tools like python to install.
cat <<EOF > /etc/yum.repos.d/el8.repo
[rhel-8-appstream-rpms-x86_64]
baseurl = http://rhsm-pulp.corp.redhat.com/content/dist/rhel8/8/x86_64/appstream/os/
baseurl = https://rhsm-pulp.corp.redhat.com/content/dist/rhel8/8/x86_64/appstream/os/
enabled = 1
name = rhel-8-appstream-rpms-x86_64
gpgcheck = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
[rhel-8-baseos-rpms-x86_64]
baseurl = http://rhsm-pulp.corp.redhat.com/content/dist/rhel8/8/x86_64/baseos/os/
baseurl = https://rhsm-pulp.corp.redhat.com/content/dist/rhel8/8/x86_64/baseos/os/
enabled = 1
name = rhel-8-baseos-rpms-x86_64
gpgcheck = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
[rhel-8-codeready-builder-rpms-x86_64]
baseurl = https://rhsm-pulp.corp.redhat.com/content/dist/rhel8/8/x86_64/codeready-builder/os/
enabled = 1
name = rhel-8-codeready-builder-rpms-x86_64
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
EOF
# Enable epel for csmock
curl https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm --output epel8.rpm
yum -y install epel8.rpm
# Install Python 3.6
yum -y install python36
else
# For rhel-7, just enable the basic rhel repos so that we
# can install python and other dependencies.
Expand Down Expand Up @@ -267,6 +322,9 @@ def _covscan_prepare_parent(cc: CoverityContext, parent_image_name, parent_tag)
# Enable epel for csmock
curl https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm --output epel7.rpm
yum -y install epel7.rpm
# Install Python 3.6
yum -y install rh-python36
fi
''')

Expand All @@ -277,14 +335,19 @@ def _covscan_prepare_parent(cc: CoverityContext, parent_image_name, parent_tag)

df_parent_out.write(f'''
FROM {parent_image_url}
LABEL DOOZER_COVSCAN_GROUP_PARENT={cc.runtime.group_config.name}
LABEL DOOZER_COVSCAN_PARENT={cc.runtime.group_config.name}
USER 0
# Set https proxy
ARG HTTPS_PROXY
RUN if [[ ! -z ${{HTTPS_PROXY}} ]]; then echo "Using proxy: $HTTPS_PROXY"; fi
ENV https_proxy ${{HTTPS_PROXY}}
# Certs necessary to install from internal repos
RUN curl -k https://certs.corp.redhat.com/certs/2022-IT-Root-CA.pem --output /etc/pki/ca-trust/source/anchors/2022-IT-Root-CA.pem
RUN curl -k https://certs.corp.redhat.com/certs/2015-IT-Root-CA.pem --output /etc/pki/ca-trust/source/anchors/2015-IT-Root-CA.pem
RUN update-ca-trust && update-ca-trust enable
# Add typical build repos to the image, but don't add to /etc/yum.repos.d
# until we know whether we are on el7 or el8. As of 4.8, repos are only
# appropriate for el8, so this repo file should only be installed in el8.
Expand All @@ -297,14 +360,6 @@ def _covscan_prepare_parent(cc: CoverityContext, parent_image_name, parent_tag)
ADD {rhel_repo_gen_sh} .
RUN chmod +x {rhel_repo_gen_sh} && ./{rhel_repo_gen_sh}
RUN yum install -y python36
# Certs necessary to install from covscan repos
RUN curl -k https://password.corp.redhat.com/RH-IT-Root-CA.crt --output /etc/pki/ca-trust/source/anchors/RH-IT-Root-CA.crt
RUN curl -k https://engineering.redhat.com/Eng-CA.crt --output /etc/pki/ca-trust/source/anchors/Eng-CA.crt
RUN update-ca-trust
RUN update-ca-trust enable
RUN yum install -y cov-sa csmock csmock-plugin-coverity csdiff
''')
df_parent_out.write('ENV PATH=/opt/coverity/bin:${PATH}\n') # Ensure coverity is in the path
Expand Down Expand Up @@ -425,7 +480,8 @@ def append_analysis(stage_number):

# Before running cov-analyze, make sure that all_js doesn't exist (i.e. we haven't already run it
# in this workspace AND summary.txt exist (i.e. at least one item in this stage emitted results).
df_out.write(f'''
if cc.podman_sudo: # no need to chown if running as a rootless container
df_out.write(f'''
# Dockerfile steps run as root; chang permissions back to doozer user before leaving stage
RUN chown -R {os.getuid()}:{os.getgid()} {container_stage_cov_dir}
''')
Expand Down
4 changes: 2 additions & 2 deletions doozerlib/metadata.py
Expand Up @@ -21,7 +21,7 @@
from doozerlib import exectools, logutil
from doozerlib.assembly import assembly_basis_event, assembly_metadata_config
from doozerlib.brew import BuildStates
from doozerlib.distgit import ImageDistGitRepo, RPMDistGitRepo
from doozerlib.distgit import DistGitRepo, ImageDistGitRepo, RPMDistGitRepo
from doozerlib.model import Missing, Model
from doozerlib.pushd import Dir
from doozerlib.util import (isolate_el_version_in_brew_tag,
Expand Down Expand Up @@ -224,7 +224,7 @@ def distgit_remote_url(self):
return f'ssh://{self.runtime.user}@{pkgs_host}/{self.qualified_name}'
return f'ssh://{pkgs_host}/{self.qualified_name}'

def distgit_repo(self, autoclone=True) -> RPMDistGitRepo:
def distgit_repo(self, autoclone=True) -> DistGitRepo:
if self._distgit_repo is None:
self._distgit_repo = DISTGIT_TYPES[self.meta_type](self, autoclone=autoclone)
return self._distgit_repo
Expand Down

0 comments on commit aad9795

Please sign in to comment.