Skip to content

chore: Use oc-mirror v2 as v1 is deprecated in OCP 4.18#1646

Merged
openshift-merge-bot[bot] merged 24 commits into
redhat-developer:mainfrom
Fortune-Ndlovu:oc-mirror-v2-migration-from-oc-mirror-v1-deprecation
Oct 13, 2025
Merged

chore: Use oc-mirror v2 as v1 is deprecated in OCP 4.18#1646
openshift-merge-bot[bot] merged 24 commits into
redhat-developer:mainfrom
Fortune-Ndlovu:oc-mirror-v2-migration-from-oc-mirror-v1-deprecation

Conversation

@Fortune-Ndlovu

@Fortune-Ndlovu Fortune-Ndlovu commented Sep 16, 2025

Copy link
Copy Markdown
Member

Description

This PR updates the airgap script to use oc-mirror v2 as v1 has been deprecated in OCP 4.18. Changes essentially include the consistent use of the oc-mirror --v2 OpenShift CLI plugin and only applying relevant feature updates throughout the workflow. This migration ensures compatibility with OCP 4.18+.

Which issue(s) does this PR fix or relate to

PR acceptance criteria

  • Tests
  • Documentation

How to test changes / Special notes to the reviewer

# All these workflows now work perfectly with oc-mirror v2:

# 1. Export for airgapped environments
bash .rhdh/scripts/prepare-restricted-environment.sh \
  --use-oc-mirror true \
  --to-dir /tmp/rhdh-export \
  --filter-versions "1.7"

# 2. Import in airgapped environments  
bash .rhdh/scripts/prepare-restricted-environment.sh \
  --use-oc-mirror true \
  --from-dir /tmp/rhdh-export \
  --to-registry quay.io/user/rhdh/

# 3. Direct mirroring (connected)
bash .rhdh/scripts/prepare-restricted-environment.sh \
  --use-oc-mirror true \
  --to-registry quay.io/user/rhdh/ \
  --filter-versions "1.7"

# 4. OCP internal registry
bash .rhdh/scripts/prepare-restricted-environment.sh \
  --use-oc-mirror true \
  --to-registry OCP_INTERNAL

Summary by Sourcery

Switch to oc-mirror v2 for image mirroring by updating script flags, config API version, and resource handling while removing deprecated v1 workarounds

Enhancements:

  • Require and use oc-mirror v2 by updating flags (-c, --v2) and ImageSetConfiguration apiVersion to mirror.openshift.io/v2alpha1
  • Remove v1-specific workarounds for XDG_RUNTIME_DIR override and docker config backups
  • Rely on REGISTRY_AUTH_FILE for registry authentication and adjust its default path
  • Process cluster resources from working-dir/cluster-resources and apply ImageDigestMirrorSet/ImageTagMirrorSet instead of ICSP, and update CatalogSource manifests

Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

RHDHBUGS-342 - Partially compliant

Compliant requirements:

  • Migrate airgap install script to oc-mirror v2 (use --v2 flag consistently).
  • Ensure ImageSetConfiguration manifest uses apiVersion v2alpha1.
  • Stop relying on v1-specific workarounds (docker config backup, XDG_RUNTIME_DIR override).
  • Align generated resources to v2 outputs (ImageDigestMirrorSet/ImageTagMirrorSet instead of ICSP).

Non-compliant requirements:

  • None observed in the provided diff for the operator script scope.

Requires further human verification:

  • Validate end-to-end mirroring on real clusters (ROSA, hosted control planes, OCP 4.18+) with oc-mirror v2.
  • Confirm Helm chart procedures are validated and documentation follow-up is tracked if required.
  • Verify that oc-mirror v2 output directory structure assumptions (working-dir/cluster-resources) match current oc-mirror release used in CI/CD.
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

The help text now states oc-mirror v2 is recommended, but the default for --use-oc-mirror remains false. This may cause users to run the non-oc-mirror path by default, diverging from the v2 migration intent. Validate whether the default should be true or docs updated accordingly.

--install-operator <true|false>        : Install the RHDH operator right after creating the CatalogSource (default: true)
--extra-images <list>                  : Comma-separated list of extra images to mirror
--use-oc-mirror <true|false>           : Whether to use the 'oc-mirror' tool v2 (default: false).
                                          This is the recommended way for mirroring on regular OpenShift clusters.
                                          oc-mirror v2 generates ImageDigestMirrorSet and ImageTagMirrorSet resources
                                          instead of ImageContentSourcePolicy, providing better compatibility with
                                          ROSA clusters and clusters with hosted control planes.
--oc-mirror-path <path>                : Path to the oc-mirror binary (default: 'oc-mirror').
--oc-mirror-flags <string>             : Additional flags to pass to all oc-mirror commands.
--install-yq                           : Install yq $YQ_VERSION from https://github.com/mikefarah/yq (not the jq python wrapper)
Robustness

The script assumes oc-mirror v2 outputs under working-dir/cluster-resources. Paths and file globs (imageDigestMirrorSet*.yaml, imageTagMirrorSet*.yaml, catalogSource*.yaml) may vary between oc-mirror versions. Add existence checks and clearer errors when expected manifests are missing.

# oc-mirror v2 generates manifests in working-dir/cluster-resources/
clusterResourcesDir="${TMPDIR}/working-dir/cluster-resources"
if [[ -n "${TO_DIR}" ]]; then
  clusterResourcesDir="${TO_DIR}/working-dir/cluster-resources"
fi

if [ -d "${clusterResourcesDir}" ]; then
  debugf "Processing cluster resources from: ${clusterResourcesDir}"

  # Apply ImageDigestMirrorSet and ImageTagMirrorSet resources
  if [[ -n "${TO_REGISTRY}" ]]; then
    for manifest in "${clusterResourcesDir}"/imageDigestMirrorSet*.yaml; do
      if [[ -f "${manifest}" ]]; then
        debugf "Applying ImageDigestMirrorSet: ${manifest}"
        invoke_cluster_cli apply -f "${manifest}"
      fi
    done

    for manifest in "${clusterResourcesDir}"/imageTagMirrorSet*.yaml; do
      if [[ -f "${manifest}" ]]; then
        debugf "Applying ImageTagMirrorSet: ${manifest}"
        invoke_cluster_cli apply -f "${manifest}"
      fi
    done
  fi

  # Process CatalogSource resources
  for manifest in "${clusterResourcesDir}"/catalogSource*.yaml; do
    if [[ -f "${manifest}" ]]; then
      debugf "Processing CatalogSource: ${manifest}"
      # Replace some metadata and add the default list of secrets
      "$YQ" -i '.metadata.name = "rhdh-catalog"' -i "${manifest}"
      "$YQ" -i '.spec.displayName = "Red Hat Developer Hub Catalog (Airgapped)"' -i "${manifest}"
      "$YQ" -i '.spec.secrets = (.spec.secrets // []) + ["internal-reg-auth-for-rhdh", "internal-reg-ext-auth-for-rhdh", "reg-pull-secret"]' -i "${manifest}"
      if [[ -n "${TO_REGISTRY}" ]]; then
        invoke_cluster_cli apply -f "${manifest}"
      fi
    fi
  done
fi
CLI Flags

The script mixes passing "$OC_MIRROR_FLAGS" and explicit flags; ensure ordering does not conflict. Also verify -c is supported in all used oc-mirror v2 subcommands and that --v2 is consistently required and recognized across invocations.

if [[ -n "${TO_DIR}" ]]; then
  "${OC_MIRROR_PATH}" \
    -c "${TMPDIR}/imageset-config.yaml" \
    file://"${TO_DIR}" \
    --skip-missing \
    --dest-skip-tls \
    --continue-on-error \
    --max-nested-paths=1 \
    "$OC_MIRROR_FLAGS" \
    --v2 |
    tee "${ocMirrorLogFile}"
  if [[ "${TO_DIR}" != "${TMPDIR}" ]]; then
    cp -f "${TMPDIR}/imageset-config.yaml" "${TO_DIR}/imageset-config.yaml"
  fi
  # targetCatalog needs to exist in the target registry. Copying a fake image..
  mirror_image_to_archive "registry.redhat.io/ubi9/ubi:latest" "${TO_DIR}/rhdh-catalog"
fi
if [[ -n "$TO_REGISTRY" ]]; then
  registryUrl=$(buildRegistryUrl)
  if [[ "${TO_REGISTRY}" == "OCP_INTERNAL" ]]; then
    registryUrl+="/oc-mirror"
  fi
  # targetCatalog needs to exist in the target registry. Copying a fake image..
  catalog_reg_path="rhdh-catalog"
  if [[ "${TO_REGISTRY}" == "OCP_INTERNAL" ]]; then
    catalog_reg_path="oc-mirror/rhdh-catalog"
  fi
  my_operator_index="$(buildCatalogImageUrl external "${catalog_reg_path}")"
  mirror_image_to_registry "registry.redhat.io/ubi9/ubi:latest" "${my_operator_index}-tmp"

  "${OC_MIRROR_PATH}" \
    -c "${TMPDIR}/imageset-config.yaml" \
    "docker://${registryUrl}" \
    --skip-missing \
    --dest-skip-tls \
    --continue-on-error \
    --max-nested-paths=2 \
    "$OC_MIRROR_FLAGS" \
    --v2 |
    tee "${ocMirrorLogFile}"
📄 References
  1. No matching references available

@qodo-code-review qodo-code-review Bot added the enhancement New feature or request label Sep 16, 2025
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Type

Enhancement


Description

  • Migrate from deprecated oc-mirror v1 to v2 API

  • Update ImageSetConfiguration from v1alpha2 to v2alpha1

  • Replace ICSP with ImageDigestMirrorSet/ImageTagMirrorSet

  • Remove v1 workarounds and authentication hacks


File Walkthrough

Relevant files
Enhancement
prepare-restricted-environment.sh
Migrate oc-mirror from v1 to v2 API                                           

.rhdh/scripts/prepare-restricted-environment.sh

  • Updated ImageSetConfiguration API version from v1alpha2 to v2alpha1
  • Added --v2 flag to all oc-mirror commands and updated syntax (-c
    instead of --config)
  • Removed v1 workarounds including docker config backup and
    XDG_RUNTIME_DIR override
  • Replaced ICSP processing with ImageDigestMirrorSet/ImageTagMirrorSet
    handling
  • Updated cluster resources directory path to
    working-dir/cluster-resources/
  • Enhanced documentation to reflect v2 compatibility improvements
+56/-52 

@Fortune-Ndlovu Fortune-Ndlovu self-assigned this Sep 16, 2025

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `.rhdh/scripts/prepare-restricted-environment.sh:1052-1059` </location>
<code_context>
+      if [[ -f "${manifest}" ]]; then
+        debugf "Processing CatalogSource: ${manifest}"
+        # Replace some metadata and add the default list of secrets
+        "$YQ" -i '.metadata.name = "rhdh-catalog"' -i "${manifest}"
+        "$YQ" -i '.spec.displayName = "Red Hat Developer Hub Catalog (Airgapped)"' -i "${manifest}"
+        "$YQ" -i '.spec.secrets = (.spec.secrets // []) + ["internal-reg-auth-for-rhdh", "internal-reg-ext-auth-for-rhdh", "reg-pull-secret"]' -i "${manifest}"
+        if [[ -n "${TO_REGISTRY}" ]]; then
+          invoke_cluster_cli apply -f "${manifest}"
</code_context>

<issue_to_address>
**suggestion (performance):** Multiple in-place yq edits could be consolidated for efficiency.

Combine all yq edits into a single command per manifest to minimize process overhead and avoid race conditions.

```suggestion
      if [[ -f "${manifest}" ]]; then
        debugf "Processing CatalogSource: ${manifest}"
        # Replace some metadata and add the default list of secrets in a single yq command
        "$YQ" -i '
          .metadata.name = "rhdh-catalog" |
          .spec.displayName = "Red Hat Developer Hub Catalog (Airgapped)" |
          .spec.secrets = (.spec.secrets // []) + ["internal-reg-auth-for-rhdh", "internal-reg-ext-auth-for-rhdh", "reg-pull-secret"]
        ' "${manifest}"
        if [[ -n "${TO_REGISTRY}" ]]; then
          invoke_cluster_cli apply -f "${manifest}"
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .rhdh/scripts/prepare-restricted-environment.sh Outdated
@qodo-code-review

qodo-code-review Bot commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Use correct path for generated manifests
Suggestion Impact:The commit changed how cluster resource manifests are located and processed, avoiding the previously hardcoded path. It now conditions processing on TO_REGISTRY, adjusts the base directory depending on FROM_DIR, and updates file patterns (idms/itms/cs) and handling. While not exactly using find over oc-mirror-workspace as suggested, it addressed the core issue of incorrect hardcoded paths by correctly selecting the working-dir based on the operation phase.

code diff:

@@ -1022,44 +999,64 @@
   fi
 
   # oc-mirror v2 generates manifests in working-dir/cluster-resources/
-  clusterResourcesDir="${TMPDIR}/working-dir/cluster-resources"
-  if [[ -n "${TO_DIR}" ]]; then
-    clusterResourcesDir="${TO_DIR}/working-dir/cluster-resources"
-  fi
-  
-  if [ -d "${clusterResourcesDir}" ]; then
-    debugf "Processing cluster resources from: ${clusterResourcesDir}"
+  # These are only generated during diskToMirror operations (when TO_REGISTRY is set)
+  if [[ -n "${TO_REGISTRY}" ]]; then
+    clusterResourcesDir="${TMPDIR}/working-dir/cluster-resources"
+    if [[ -n "${FROM_DIR}" ]]; then
+      clusterResourcesDir="${FROM_DIR}/working-dir/cluster-resources"
+    fi
     
-    # Apply ImageDigestMirrorSet and ImageTagMirrorSet resources
-    if [[ -n "${TO_REGISTRY}" ]]; then
-      for manifest in "${clusterResourcesDir}"/imageDigestMirrorSet*.yaml; do
+    if [ -d "${clusterResourcesDir}" ]; then
+      debugf "Processing cluster resources from: ${clusterResourcesDir}"
+      
+      # Apply ImageDigestMirrorSet and ImageTagMirrorSet resources
+      foundResources=false
+      # oc-mirror v2 generates files with patterns: idms-*.yaml, itms-*.yaml
+      
+      # Skip IDMS/ITMS on Hosted Control Plane clusters (ROSA HCP, HyperShift, etc.)
+      # as they don't support direct IDMS/ITMS creation
+      if [[ "${IS_HOSTED_CONTROL_PLANE}" = "true" ]]; then
+        warnf "Hosted Control Plane cluster detected. Skipping ImageDigestMirrorSet/ImageTagMirrorSet application."
+        warnf "On HCP clusters, image mirrors must be configured in the HostedCluster resource."
+        warnf "Since images are already in the internal registry, the CatalogSource will reference them directly."
+      else
+        for manifest in "${clusterResourcesDir}"/idms-*.yaml "${clusterResourcesDir}"/imageDigestMirrorSet*.yaml; do
+          if [[ -f "${manifest}" ]]; then
+            debugf "Applying ImageDigestMirrorSet: ${manifest}"
+            invoke_cluster_cli apply -f "${manifest}"
+            foundResources=true
+          fi
+        done
+        
+        for manifest in "${clusterResourcesDir}"/itms-*.yaml "${clusterResourcesDir}"/imageTagMirrorSet*.yaml; do
+          if [[ -f "${manifest}" ]]; then
+            debugf "Applying ImageTagMirrorSet: ${manifest}"
+            invoke_cluster_cli apply -f "${manifest}"
+            foundResources=true
+          fi
+        done
+      fi
+      
+      # Process CatalogSource resources
+      # oc-mirror v2 generates files with pattern: cs-*.yaml
+      for manifest in "${clusterResourcesDir}"/cs-*.yaml "${clusterResourcesDir}"/catalogSource*.yaml; do
         if [[ -f "${manifest}" ]]; then
-          debugf "Applying ImageDigestMirrorSet: ${manifest}"
+          debugf "Processing CatalogSource: ${manifest}"
+          # Replace some metadata and add the default list of secrets
+          "$YQ" -i '.metadata.name = "rhdh-catalog"' "${manifest}"
+          "$YQ" -i '.spec.displayName = "Red Hat Developer Hub Catalog (Airgapped)"' "${manifest}"
+          "$YQ" -i '.spec.secrets = (.spec.secrets // []) + ["internal-reg-auth-for-rhdh", "internal-reg-ext-auth-for-rhdh", "reg-pull-secret"]' "${manifest}"
           invoke_cluster_cli apply -f "${manifest}"
+          foundResources=true
         fi
       done
       
-      for manifest in "${clusterResourcesDir}"/imageTagMirrorSet*.yaml; do
-        if [[ -f "${manifest}" ]]; then
-          debugf "Applying ImageTagMirrorSet: ${manifest}"
-          invoke_cluster_cli apply -f "${manifest}"
-        fi
-      done
-    fi
-    
-    # Process CatalogSource resources
-    for manifest in "${clusterResourcesDir}"/catalogSource*.yaml; do
-      if [[ -f "${manifest}" ]]; then
-        debugf "Processing CatalogSource: ${manifest}"
-        # Replace some metadata and add the default list of secrets
-        "$YQ" -i '.metadata.name = "rhdh-catalog"' -i "${manifest}"
-        "$YQ" -i '.spec.displayName = "Red Hat Developer Hub Catalog (Airgapped)"' -i "${manifest}"
-        "$YQ" -i '.spec.secrets = (.spec.secrets // []) + ["internal-reg-auth-for-rhdh", "internal-reg-ext-auth-for-rhdh", "reg-pull-secret"]' -i "${manifest}"
-        if [[ -n "${TO_REGISTRY}" ]]; then
-          invoke_cluster_cli apply -f "${manifest}"
-        fi
-      fi
-    done
+      if [[ "${foundResources}" != "true" ]]; then
+        warnf "No cluster resources found in ${clusterResourcesDir}. This may indicate that oc-mirror v2 did not generate them."
+      fi
+    else
+      warnf "Cluster resources directory not found: ${clusterResourcesDir}. This may indicate that oc-mirror v2 did not generate cluster resources."
+    fi
   fi

The hardcoded path for oc-mirror v2 manifests is incorrect. Update the script to
dynamically find the correct path within the oc-mirror-workspace/results-*/
directory structure.

.rhdh/scripts/prepare-restricted-environment.sh [1024-1028]

-# oc-mirror v2 generates manifests in working-dir/cluster-resources/
-clusterResourcesDir="${TMPDIR}/working-dir/cluster-resources"
-if [[ -n "${TO_DIR}" ]]; then
-  clusterResourcesDir="${TO_DIR}/working-dir/cluster-resources"
+# oc-mirror v2 generates manifests in a results directory
+clusterResourcesDir=""
+if [[ -n "${FROM_DIR}" ]]; then
+  # When publishing, the resources are in the source directory
+  if [[ -d "${FROM_DIR}/oc-mirror-workspace" ]]; then
+    clusterResourcesDir=$(find "${FROM_DIR}/oc-mirror-workspace" -maxdepth 2 -type d -name "cluster-resources" | head -n 1)
+  fi
+elif [[ -n "${TO_DIR}" ]]; then
+  # When mirroring to a directory, the resources are in the destination directory
+  if [[ -d "${TO_DIR}/oc-mirror-workspace" ]]; then
+    clusterResourcesDir=$(find "${TO_DIR}/oc-mirror-workspace" -maxdepth 2 -type d -name "cluster-resources" | head -n 1)
+  fi
+else
+  # When mirroring directly to a registry, oc-mirror creates a 'working-dir' in the current directory.
+  # The script should change to TMPDIR to ensure 'working-dir' is created there.
+  if [[ -d "${TMPDIR}/working-dir" ]]; then
+    clusterResourcesDir="${TMPDIR}/working-dir/cluster-resources"
+  fi
 fi

[Suggestion processed]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical bug where the script uses a hardcoded, incorrect path for oc-mirror v2 manifests, which would cause the script to fail.

High
  • Update

… equivalent in v2 (images must exist or command fails)' AND '--dest-skip-tls: Replaced with --dest-tls-verify=false' AND '--continue-on-error: No equivalent in v2 (command stops on first error)'

Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
Replace complex yq filter generation that was returning null values
with direct use of existing auth file containing valid credentials.
Prevents authentication failures in oc-mirror v2 workflow.

Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
Comment thread .rhdh/scripts/prepare-restricted-environment.sh Fixed
@Fortune-Ndlovu Fortune-Ndlovu changed the title chore: Use oc-mirror v2 as v1 is deprecated in OCP 4.18 WIP chore: Use oc-mirror v2 as v1 is deprecated in OCP 4.18 Sep 17, 2025
…ster resource generation AND Add --cache-dir option for persistent caching to improve performance and enable incremental mirroring AND Fix ImageSetConfiguration channel structure to ensure proper YAML formatting

AND Add cache directory support with default location at /home/fndlovu/.oc-mirror-cache

Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
@github-actions

github-actions Bot commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

@Fortune-Ndlovu Fortune-Ndlovu requested a review from rm3l October 7, 2025 14:16
Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
@github-actions

github-actions Bot commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Comment thread .rhdh/scripts/prepare-restricted-environment.sh Outdated
Comment thread .rhdh/scripts/prepare-restricted-environment.sh Outdated
…ipping

Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
@github-actions

github-actions Bot commented Oct 8, 2025

Copy link
Copy Markdown
Contributor

Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
@github-actions

github-actions Bot commented Oct 8, 2025

Copy link
Copy Markdown
Contributor

…rning => Continue anyway

Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>
@github-actions

github-actions Bot commented Oct 9, 2025

Copy link
Copy Markdown
Contributor

@Fortune-Ndlovu

Copy link
Copy Markdown
Member Author

cc/ @rm3l / @gazarenkov please review

Comment thread .rhdh/scripts/prepare-restricted-environment.sh Outdated
Signed-off-by: Fortune Ndlovu <fndlovu@redhat.com>

@rm3l rm3l left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@openshift-ci

openshift-ci Bot commented Oct 13, 2025

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rm3l

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants