diff --git a/.ci-operator.yaml b/.ci-operator.yaml index 9a3f7b8fb..844f0d26a 100644 --- a/.ci-operator.yaml +++ b/.ci-operator.yaml @@ -1,4 +1,4 @@ build_root_image: name: release namespace: openshift - tag: rhel-8-release-golang-1.19-openshift-4.14 + tag: rhel-8-release-golang-1.20-openshift-4.14 diff --git a/images/cli/Dockerfile.ci b/images/cli/Dockerfile.ci index 63aa1f84b..cbdd142de 100644 --- a/images/cli/Dockerfile.ci +++ b/images/cli/Dockerfile.ci @@ -1,5 +1,5 @@ # This Dockerfile is used by CI to publish the oc-mirror image. -FROM registry.ci.openshift.org/ocp/builder:rhel-8-golang-1.19-openshift-4.14 AS builder +FROM registry.ci.openshift.org/ocp/builder:rhel-8-golang-1.20-openshift-4.14 AS builder WORKDIR /go/src/github.com/openshift/oc-mirror COPY . . RUN make build diff --git a/pkg/cli/mirror/catalog_images.go b/pkg/cli/mirror/catalog_images.go index 2847afc5b..dd4e342a4 100644 --- a/pkg/cli/mirror/catalog_images.go +++ b/pkg/cli/mirror/catalog_images.go @@ -176,7 +176,7 @@ func (o *MirrorOptions) processCatalogRefs(ctx context.Context, catalogsByImage containertools.ConfigsLocationLabel: "/configs", } cfg.Config.Labels = labels - cfg.Config.Cmd = []string{"serve", "/configs", "--cache-dir", "/tmp/cache"} + cfg.Config.Cmd = []string{"serve", "/configs"} cfg.Config.Entrypoint = []string{"/bin/opm"} } if err := imgBuilder.Run(ctx, refExact, layoutPath, update, layers...); err != nil { diff --git a/pkg/cli/mirror/mirror.go b/pkg/cli/mirror/mirror.go index 889a0b04c..3b804deae 100644 --- a/pkg/cli/mirror/mirror.go +++ b/pkg/cli/mirror/mirror.go @@ -255,11 +255,11 @@ func (o *MirrorOptions) Validate() error { mirrorToMirror := len(o.ToMirror) > 0 && len(o.ConfigPath) > 0 // mirrorToDisk workflow is not supported with the oci feature - if o.UseOCIFeature && mirrorToDisk { + if o.IncludeLocalOCICatalogs && mirrorToDisk { return fmt.Errorf("oci feature cannot be used when mirroring to local archive") } // diskToMirror workflow is not supported with the oci feature - if o.UseOCIFeature && diskToMirror { + if o.IncludeLocalOCICatalogs && diskToMirror { return fmt.Errorf("oci feature cannot be used when publishing from a local archive to a registry") } // mirrorToMirror workflow using the oci feature must have at least on operator set with oci:// prefix @@ -274,15 +274,15 @@ func (o *MirrorOptions) Validate() error { bIsFBOCI = true } } - if o.UseOCIFeature && !bIsFBOCI { - return fmt.Errorf("no operator found with OCI FBC catalog prefix (oci://) in configuration file, please execute without the --use-oci-feature flag") + if o.IncludeLocalOCICatalogs && !bIsFBOCI { + return fmt.Errorf("no operator found with OCI FBC catalog prefix (oci://) in configuration file, please execute without the --include-local-oci-catalogs flag") } - if !o.UseOCIFeature && bIsFBOCI { - return fmt.Errorf("use of OCI FBC catalogs (prefix oci://) in configuration file is authorized only with flag --use-oci-feature") + if !o.IncludeLocalOCICatalogs && bIsFBOCI { + return fmt.Errorf("use of OCI FBC catalogs (prefix oci://) in configuration file is authorized only with flag --include-local-oci-catalogs") } } - if !o.UseOCIFeature && len(o.OCIRegistriesConfig) > 0 { - return fmt.Errorf("oci-registries-config flag can only be used with the --use-oci-feature flag") + if !o.IncludeLocalOCICatalogs && len(o.OCIRegistriesConfig) > 0 { + return fmt.Errorf("oci-registries-config flag can only be used with the --include-local-oci-catalog flag") } if o.SkipPruning { @@ -632,7 +632,7 @@ func (o *MirrorOptions) mirrorToMirrorWrapper(ctx context.Context, cfg v1alpha2. if err != nil { return err } - if !o.UseOCIFeature { + if !o.IncludeLocalOCICatalogs { var curr v1alpha2.Metadata berr := targetBackend.ReadMetadata(ctx, &curr, config.MetadataBasePath) if err := o.checkSequence(meta, curr, berr); err != nil { @@ -858,8 +858,16 @@ func (o *MirrorOptions) diskToMirrorWrapper(ctx context.Context, cleanup cleanup // Publish from disk to registry // this takes care of syncing the metadata to the // registry backends. + mapping, err := o.Publish(ctx) if err != nil { + // OCPBUGS-4959 for automation processes to end gracefully + // when we have the same sequence - i.e nothing to do + msqErr := &ErrMirrorSequence{} + if errors.As(err, &msqErr) { + klog.Info("No diff from previous mirror (sequence is the same), nothing to do") + return cleanup() + } serr := &ErrInvalidSequence{} if errors.As(err, &serr) { return fmt.Errorf("error during publishing, expecting imageset with prefix mirror_seq%d: %v", serr.wantSeq, err) diff --git a/pkg/cli/mirror/mirror_test.go b/pkg/cli/mirror/mirror_test.go index c0924a0a8..a8d10b4db 100644 --- a/pkg/cli/mirror/mirror_test.go +++ b/pkg/cli/mirror/mirror_test.go @@ -291,38 +291,38 @@ func TestMirrorValidate(t *testing.T) { { name: "Invalid/MirrortoDisk/OCIFlag", opts: &MirrorOptions{ - OutputDir: t.TempDir(), - ConfigPath: "foo", - UseOCIFeature: true, + OutputDir: t.TempDir(), + ConfigPath: "foo", + IncludeLocalOCICatalogs: true, }, expError: "oci feature cannot be used when mirroring to local archive", }, { name: "Invalid/DisktoMirror/OCIFlag", opts: &MirrorOptions{ - From: t.TempDir(), - ToMirror: u.Host, - UseOCIFeature: true, + From: t.TempDir(), + ToMirror: u.Host, + IncludeLocalOCICatalogs: true, }, expError: "oci feature cannot be used when publishing from a local archive to a registry", }, { name: "Invalid/MirrorToMirror/ImageSetConfigWithOCI", opts: &MirrorOptions{ - ConfigPath: "testdata/configs/iscfg_oci_ops.yaml", - ToMirror: u.Host, - UseOCIFeature: false, + ConfigPath: "testdata/configs/iscfg_oci_ops.yaml", + ToMirror: u.Host, + IncludeLocalOCICatalogs: false, }, - expError: "use of OCI FBC catalogs (prefix oci://) in configuration file is authorized only with flag --use-oci-feature", + expError: "use of OCI FBC catalogs (prefix oci://) in configuration file is authorized only with flag --include-local-oci-catalogs", }, { name: "Invalid/MirrorToMirror/ImageSetConfigWithoutOCI", opts: &MirrorOptions{ - ConfigPath: "testdata/configs/iscfg.yaml", - ToMirror: u.Host, - UseOCIFeature: true, + ConfigPath: "testdata/configs/iscfg.yaml", + ToMirror: u.Host, + IncludeLocalOCICatalogs: true, }, - expError: "no operator found with OCI FBC catalog prefix (oci://) in configuration file, please execute without the --use-oci-feature flag", + expError: "no operator found with OCI FBC catalog prefix (oci://) in configuration file, please execute without the --include-local-oci-catalogs flag", }, { name: "Valid/ManifestOnlyWithFakeMirror", diff --git a/pkg/cli/mirror/options.go b/pkg/cli/mirror/options.go index 57751a51a..16653757d 100644 --- a/pkg/cli/mirror/options.go +++ b/pkg/cli/mirror/options.go @@ -34,7 +34,7 @@ type MirrorOptions struct { ContinueOnError bool IgnoreHistory bool MaxPerRegistry int - UseOCIFeature bool + IncludeLocalOCICatalogs bool OCIRegistriesConfig string OCIInsecureSignaturePolicy bool MaxNestedPaths int @@ -68,12 +68,13 @@ func (o *MirrorOptions) BindFlags(fs *pflag.FlagSet) { "404/NotFound errors encountered while pulling images explicitly specified in the config "+ "will not be skipped") fs.IntVar(&o.MaxPerRegistry, "max-per-registry", 6, "Number of concurrent requests allowed per registry") - fs.BoolVar(&o.UseOCIFeature, "use-oci-feature", o.UseOCIFeature, "Use the new oci feature for oc mirror (oci formatted copy") + fs.BoolVar(&o.IncludeLocalOCICatalogs, "use-oci-feature", o.IncludeLocalOCICatalogs, "Deprecated, use --include-local-oci-catalog instead") + fs.BoolVar(&o.IncludeLocalOCICatalogs, "include-local-oci-catalogs", o.IncludeLocalOCICatalogs, "If set, enables including local OCI-formatted catalogs (prefix oci://) in the list of operator catalogs defined in ImageSetConfig, so that these local catalogs are mirrored from the disk directly") fs.StringVar(&o.OCIRegistriesConfig, "oci-registries-config", o.OCIRegistriesConfig, "Registries config file location (used only with --use-oci-feature flag)") fs.BoolVar(&o.OCIInsecureSignaturePolicy, "oci-insecure-signature-policy", o.OCIInsecureSignaturePolicy, "If set, OCI catalog push will not try to push signatures") fs.BoolVar(&o.SkipPruning, "skip-pruning", o.SkipPruning, "If set, will disable pruning globally") fs.IntVar(&o.MaxNestedPaths, "max-nested-paths", 2, "Number of nested paths, for destination registries that limit nested paths") - + fs.MarkDeprecated("use-oci-feature", "Use --include-local-oci-catalogs instead") } func (o *MirrorOptions) init() { diff --git a/pkg/cli/mirror/sequence.go b/pkg/cli/mirror/sequence.go index 084fd219e..80949ebf6 100644 --- a/pkg/cli/mirror/sequence.go +++ b/pkg/cli/mirror/sequence.go @@ -17,10 +17,18 @@ type ErrInvalidSequence struct { gotSeq int } +type ErrMirrorSequence struct { + msg string +} + func (s *ErrInvalidSequence) Error() string { return fmt.Sprintf("invalid mirror sequence order, want %v, got %v", s.wantSeq, s.gotSeq) } +func (s *ErrMirrorSequence) Error() string { + return fmt.Sprintf(s.msg) +} + func (o *MirrorOptions) checkSequence(incoming, current v1alpha2.Metadata, backendErr error) error { switch { case backendErr != nil && !errors.Is(backendErr, storage.ErrMetadataNotExist): @@ -40,6 +48,10 @@ func (o *MirrorOptions) checkSequence(incoming, current v1alpha2.Metadata, backe klog.V(3).Info("Checking metadata sequence number") currRun := current.PastMirror incomingRun := incoming.PastMirror + // OCPBUGS-4959 + if incomingRun.Sequence == currRun.Sequence { + return &ErrMirrorSequence{msg: "mirror sequence is the same"} + } if incomingRun.Sequence != (currRun.Sequence + 1) { return &ErrInvalidSequence{currRun.Sequence + 1, incomingRun.Sequence} } diff --git a/pkg/operator/diff/internal/diff.go b/pkg/operator/diff/internal/diff.go index e631e2bb5..a23659a18 100644 --- a/pkg/operator/diff/internal/diff.go +++ b/pkg/operator/diff/internal/diff.go @@ -405,6 +405,12 @@ func addAllDependencies(newModel, oldModel, outputModel model.Model) error { head := heads[newCh.Name] graph := makeUpgradeGraph(newCh) + + //OCPBUGS-11371 - bundles in skips field should not be considered since they are not part of upgradeGraph + if isToSkip(*b, *head) { + continue + } + intersectingBundles, intersectionFound := findIntersectingBundles(newCh, b, head, graph) if !intersectionFound { // This should never happen, since b and head are from the same model. @@ -494,6 +500,7 @@ func getBundlesThatProvide(pkg *model.Package, reqGVKs map[property.GVK]struct{} if isPkgRequired { bundlesByRange = make([][]*model.Bundle, len(ranges)) } + // Collect package bundles that provide a GVK or are in a range. bundlesProvidingGVK := make(map[property.GVK][]*model.Bundle) for _, ch := range pkg.Channels { @@ -563,6 +570,17 @@ func getBundlesThatProvide(pkg *model.Package, reqGVKs map[property.GVK]struct{} return providingBundles } +// isToSkip detects if the bundle is skipped by the head of the channel +func isToSkip(b model.Bundle, head model.Bundle) bool { + for _, versionToSkip := range head.Skips { + if versionToSkip == b.Name { + return true + } + } + + return false +} + func convertFromModelBundle(b *model.Bundle) declcfg.Bundle { return declcfg.Bundle{ Schema: declcfg.SchemaBundle, diff --git a/pkg/operator/diff/internal/diff_test.go b/pkg/operator/diff/internal/diff_test.go index 7389d2f27..599e98fa3 100644 --- a/pkg/operator/diff/internal/diff_test.go +++ b/pkg/operator/diff/internal/diff_test.go @@ -1890,7 +1890,7 @@ func TestDiffHeadsOnly(t *testing.T) { {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, + {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3"}, }}, {Schema: declcfg.SchemaChannel, Name: "stable", Package: "foo", Entries: []declcfg.ChannelEntry{ {Name: "foo.v0.1.0"}, @@ -1991,7 +1991,7 @@ func TestDiffHeadsOnly(t *testing.T) { {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, + {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3"}, }}, {Schema: declcfg.SchemaChannel, Name: "stable", Package: "foo", Entries: []declcfg.ChannelEntry{ {Name: "foo.v0.1.0"}, @@ -2063,6 +2063,159 @@ func TestDiffHeadsOnly(t *testing.T) { }, }, }, + { + name: "HasDiff/SelectDependencies/SkipHeadSkipsList", + newCfg: declcfg.DeclarativeConfig{ + Packages: []declcfg.Package{ + {Schema: declcfg.SchemaPackage, Name: "etcd", DefaultChannel: "stable"}, + {Schema: declcfg.SchemaPackage, Name: "foo", DefaultChannel: "stable"}, + {Schema: declcfg.SchemaPackage, Name: "bar", DefaultChannel: "stable"}, + }, + Channels: []declcfg.Channel{ + {Schema: declcfg.SchemaChannel, Name: "stable", Package: "etcd", Entries: []declcfg.ChannelEntry{ + {Name: "etcd.v0.9.0"}, + {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, + {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, + {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, + {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, + }}, + {Schema: declcfg.SchemaChannel, Name: "stable", Package: "foo", Entries: []declcfg.ChannelEntry{ + {Name: "foo.v0.1.0"}, + }}, + {Schema: declcfg.SchemaChannel, Name: "stable", Package: "bar", Entries: []declcfg.ChannelEntry{ + {Name: "bar.v0.1.0"}, + }}, + }, + Bundles: []declcfg.Bundle{ + { + Schema: declcfg.SchemaBundle, + Name: "foo.v0.1.0", + Package: "foo", + Image: "reg/foo:latest", + Properties: []property.Property{ + property.MustBuildPackageRequired("etcd", "<0.9.2"), + property.MustBuildPackage("foo", "0.1.0"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "bar.v0.1.0", + Package: "bar", + Image: "reg/bar:latest", + Properties: []property.Property{ + property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), + property.MustBuildPackage("bar", "0.1.0"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "etcd.v0.9.0", + Package: "etcd", + Image: "reg/etcd:latest", + Properties: []property.Property{ + property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), + property.MustBuildPackage("etcd", "0.9.0"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "etcd.v0.9.1", + Package: "etcd", + Image: "reg/etcd:latest", + Properties: []property.Property{ + property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), + property.MustBuildPackage("etcd", "0.9.1"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "etcd.v0.9.2", + Package: "etcd", + Image: "reg/etcd:latest", + Properties: []property.Property{ + property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), + property.MustBuildPackage("etcd", "0.9.2"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "etcd.v0.9.3", + Package: "etcd", + Image: "reg/etcd:latest", + Properties: []property.Property{ + property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), + property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), + property.MustBuildPackage("etcd", "0.9.3"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "etcd.v1.0.0", + Package: "etcd", + Image: "reg/etcd:latest", + Properties: []property.Property{ + property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), + property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), + property.MustBuildPackage("etcd", "1.0.0"), + }, + }, + }, + }, + g: &DiffGenerator{ + HeadsOnly: true, + }, + expCfg: declcfg.DeclarativeConfig{ + Packages: []declcfg.Package{ + {Schema: declcfg.SchemaPackage, Name: "bar", DefaultChannel: "stable"}, + {Schema: declcfg.SchemaPackage, Name: "etcd", DefaultChannel: "stable"}, + {Schema: declcfg.SchemaPackage, Name: "foo", DefaultChannel: "stable"}, + }, + Channels: []declcfg.Channel{ + {Schema: declcfg.SchemaChannel, Name: "stable", Package: "bar", Entries: []declcfg.ChannelEntry{ + {Name: "bar.v0.1.0"}, + }}, + {Schema: declcfg.SchemaChannel, Name: "stable", Package: "etcd", Entries: []declcfg.ChannelEntry{ + {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, + }}, + {Schema: declcfg.SchemaChannel, Name: "stable", Package: "foo", Entries: []declcfg.ChannelEntry{ + {Name: "foo.v0.1.0"}, + }}, + }, + Bundles: []declcfg.Bundle{ + { + Schema: declcfg.SchemaBundle, + Name: "bar.v0.1.0", + Package: "bar", + Image: "reg/bar:latest", + Properties: []property.Property{ + property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), + property.MustBuildPackage("bar", "0.1.0"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "etcd.v1.0.0", + Package: "etcd", + Image: "reg/etcd:latest", + Properties: []property.Property{ + property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), + property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), + property.MustBuildPackage("etcd", "1.0.0"), + }, + }, + { + Schema: declcfg.SchemaBundle, + Name: "foo.v0.1.0", + Package: "foo", + Image: "reg/foo:latest", + Properties: []property.Property{ + property.MustBuildPackage("foo", "0.1.0"), + property.MustBuildPackageRequired("etcd", "<0.9.2"), + }, + }, + }, + }, + }, { name: "HasDiff/SelectDependenciesInclude", newCfg: declcfg.DeclarativeConfig{ @@ -2230,7 +2383,7 @@ func TestDiffHeadsOnly(t *testing.T) { {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, + {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.2", "etcd.v0.9.3"}}, }}, {Schema: declcfg.SchemaChannel, Name: "stable", Package: "foo", Entries: []declcfg.ChannelEntry{ {Name: "foo.v0.1.0"}, @@ -2346,7 +2499,7 @@ func TestDiffHeadsOnly(t *testing.T) { {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, + {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.2", "etcd.v0.9.3"}}, }}, {Schema: declcfg.SchemaChannel, Name: "stable", Package: "foo", Entries: []declcfg.ChannelEntry{ {Name: "foo.v0.1.0"}, diff --git a/test/e2e/testcases.sh b/test/e2e/testcases.sh index a550aa678..a76642d24 100644 --- a/test/e2e/testcases.sh +++ b/test/e2e/testcases.sh @@ -244,7 +244,7 @@ function no_updates_exist { # Test OCI local catalog function oci_catalog { - workflow_oci_mirror imageset-config-oci-mirror.yaml "docker://localhost.localdomain:${REGISTRY_DISCONN_PORT}/test" -c="--use-oci-feature --dest-skip-tls --oci-insecure-signature-policy" + workflow_oci_mirror imageset-config-oci-mirror.yaml "docker://localhost.localdomain:${REGISTRY_DISCONN_PORT}/test" -c="--include-local-oci-catalogs --dest-skip-tls --oci-insecure-signature-policy" # podman pull docker://localhost.localdomain:5001/test/redhatgov/oc-mirror-dev:test-catalog-latest --tls-verify=false # baz.v1.0.0 crane digest --insecure localhost.localdomain:${REGISTRY_DISCONN_PORT}/test/${CATALOGNAMESPACE}@sha256:f5bf1128937e7486764341e7bfdce15150f70d0e48c57de1386602c7b25ad7b4 @@ -268,7 +268,7 @@ function oci_local_all { test/e2e/graph/main & PID_GO=$! echo -e "go cincinnatti web service PID: ${PID_GO}" # copy relevant files and start the mirror process - workflow_oci_mirror_all imageset-config-oci-mirror-all.yaml "docker://localhost.localdomain:${REGISTRY_DISCONN_PORT}/test-catalog-latest" -c="--dest-skip-tls --oci-insecure-signature-policy --use-oci-feature" + workflow_oci_mirror_all imageset-config-oci-mirror-all.yaml "docker://localhost.localdomain:${REGISTRY_DISCONN_PORT}/test-catalog-latest" -c="--dest-skip-tls --oci-insecure-signature-policy --include-local-oci-catalogs" # use crane digest to verify crane digest --insecure localhost.localdomain:${REGISTRY_DISCONN_PORT}/test-catalog-latest/redhatgov/oc-mirror-dev:bar-v0.1.0