diff --git a/pkg/cli/admin/release/extract.go b/pkg/cli/admin/release/extract.go index a3c0a471cc..c117282df8 100644 --- a/pkg/cli/admin/release/extract.go +++ b/pkg/cli/admin/release/extract.go @@ -349,6 +349,28 @@ func (o *ExtractOptions) Run(ctx context.Context) error { } } + // versionInImageConfig stores the version from the label of the image + // At the moment it is used later only for the logging purpose and thus is not blocking if failures occur upon getting its value + var versionInImageConfig string + opts.ImageConfigCallback = func(imageConfig *dockerv1client.DockerImageConfig) { + if imageConfig == nil { + // This should never happen + klog.Error("Cannot retrieve the version because no image configuration is provided in the image to extract") + return + } + if imageConfig.Config == nil { + klog.Error("Cannot retrieve the version from image configuration in the image to extract because it has no configuration") + return + } + v, ok := imageConfig.Config.Labels["io.openshift.release"] + if !ok { + klog.Error("Cannot retrieve the version from image configuration in the image to extract because it does not have the required label 'io.openshift.release'") + return + } + klog.V(2).Infof("Retrieved the version from image configuration in the image to extract: %s", v) + versionInImageConfig = v + } + var manifestErrs []error // o.ExtractManifests implies o.File == "" if o.ExtractManifests { @@ -359,9 +381,9 @@ func (o *ExtractOptions) Run(ctx context.Context) error { context := "connected cluster" inclusionConfig := manifestInclusionConfiguration{} if o.InstallConfig == "" { - inclusionConfig, err = findClusterIncludeConfig(ctx, o.RESTConfig) + inclusionConfig, err = findClusterIncludeConfig(ctx, o.RESTConfig, versionInImageConfig) } else { - inclusionConfig, err = findClusterIncludeConfigFromInstallConfig(ctx, o.InstallConfig) + inclusionConfig, err = findClusterIncludeConfigFromInstallConfig(ctx, o.InstallConfig, versionInImageConfig) context = o.InstallConfig } if err != nil { diff --git a/pkg/cli/admin/release/extract_tools.go b/pkg/cli/admin/release/extract_tools.go index a1d12aa966..4451fd6994 100644 --- a/pkg/cli/admin/release/extract_tools.go +++ b/pkg/cli/admin/release/extract_tools.go @@ -1167,16 +1167,29 @@ func copyAndReplace(errorOutput io.Writer, w io.Writer, r io.Reader, bufferSize } -func findClusterIncludeConfigFromInstallConfig(ctx context.Context, installConfigPath string) (manifestInclusionConfiguration, error) { - config := manifestInclusionConfiguration{} - +// logCapabilitySetMayDiffer logs the messages if the oc-cli version is not the same as the target version +// * the enabled capability set may differ if the baseline is vCurrent, and +// * the known capability set may differ +// It raises an error if it fails to determine the oc-cli version. +func logCapabilitySetMayDiffer(capabilitiesSpec *configv1.ClusterVersionCapabilitiesSpec, targetVersion string) error { clientVersion, reportedVersion, err := version.ExtractVersion() if err != nil { - return config, err + return fmt.Errorf("failed to determine the version of 'oc': %w", err) } if reportedVersion == "" { reportedVersion = clientVersion.String() } + if reportedVersion != targetVersion { + if capabilitiesSpec != nil && capabilitiesSpec.BaselineCapabilitySet == configv1.ClusterVersionCapabilitySetCurrent { + klog.Infof("The eventual cluster %s will not be the same minor version as this %s 'oc', the actual %s capability set may differ.", targetVersion, reportedVersion, capabilitiesSpec.BaselineCapabilitySet) + } + klog.Infof("The eventual cluster %s will not be the same minor version as this %s 'oc', the known capability set may differ.", targetVersion, reportedVersion) + } + return nil +} + +func findClusterIncludeConfigFromInstallConfig(ctx context.Context, installConfigPath string, versionInImageConfig string) (manifestInclusionConfiguration, error) { + config := manifestInclusionConfiguration{} installConfigBytes, err := os.ReadFile(installConfigPath) if err != nil { @@ -1204,21 +1217,19 @@ func findClusterIncludeConfigFromInstallConfig(ctx context.Context, installConfi if enabled, ok := configv1.ClusterVersionCapabilitySets[data.Capabilities.BaselineCapabilitySet]; !ok { return config, fmt.Errorf("unrecognized baselineCapabilitySet %q", data.Capabilities.BaselineCapabilitySet) } else { - if data.Capabilities.BaselineCapabilitySet == configv1.ClusterVersionCapabilitySetCurrent { - klog.Infof("If the eventual cluster will not be the same minor version as this %s 'oc', the actual %s capability set may differ.", reportedVersion, data.Capabilities.BaselineCapabilitySet) + if err := logCapabilitySetMayDiffer(data.Capabilities, versionInImageConfig); err != nil { + return config, err } config.Capabilities.EnabledCapabilities = append(config.Capabilities.EnabledCapabilities, enabled...) } config.Capabilities.EnabledCapabilities = append(config.Capabilities.EnabledCapabilities, data.Capabilities.AdditionalEnabledCapabilities...) - - klog.Infof("If the eventual cluster will not be the same minor version as this %s 'oc', the known capability sets may differ.", reportedVersion) config.Capabilities.KnownCapabilities = configv1.KnownClusterVersionCapabilities } return config, nil } -func findClusterIncludeConfig(ctx context.Context, restConfig *rest.Config) (manifestInclusionConfiguration, error) { +func findClusterIncludeConfig(ctx context.Context, restConfig *rest.Config, versionInImageConfig string) (manifestInclusionConfiguration, error) { config := manifestInclusionConfiguration{} client, err := configv1client.NewForConfig(restConfig) @@ -1250,6 +1261,10 @@ func findClusterIncludeConfig(ctx context.Context, restConfig *rest.Config) (man config.Capabilities.EnabledCapabilities = append(config.Capabilities.EnabledCapabilities, configv1.ClusterVersionCapabilityMachineAPI, build, deploymentConfig, imageRegistry) config.Capabilities.KnownCapabilities = append(config.Capabilities.KnownCapabilities, configv1.ClusterVersionCapabilityMachineAPI, build, deploymentConfig, imageRegistry) } + + if err := logCapabilitySetMayDiffer(clusterVersion.Spec.Capabilities, versionInImageConfig); err != nil { + return config, err + } } if infrastructure, err := client.Infrastructures().Get(ctx, "cluster", metav1.GetOptions{}); err != nil { diff --git a/pkg/cli/image/extract/extract.go b/pkg/cli/image/extract/extract.go index ae4ac5042d..b1e9d3c745 100644 --- a/pkg/cli/image/extract/extract.go +++ b/pkg/cli/image/extract/extract.go @@ -141,6 +141,8 @@ type ExtractOptions struct { genericiooptions.IOStreams + // ImageConfigCallback is invoked once image config retrieved + ImageConfigCallback func(imageConfig *dockerv1client.DockerImageConfig) // ImageMetadataCallback is invoked once per image retrieved, and may be called in parallel if // MaxPerRegistry is set higher than 1. ImageMetadataCallback ImageMetadataFunc @@ -421,6 +423,9 @@ func (o *ExtractOptions) Run() error { if err != nil { return fmt.Errorf("unable to parse image %s: %v", from, err) } + if o.ImageConfigCallback != nil { + o.ImageConfigCallback(imageConfig) + } if mapping.ConditionFn != nil { ok, err := mapping.ConditionFn(&mapping, location.Manifest, imageConfig)