@@ -18,20 +18,25 @@ import (
1818 "k8s.io/klog/v2"
1919 "sigs.k8s.io/yaml"
2020
21+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2223 "k8s.io/apimachinery/pkg/runtime/schema"
24+ "k8s.io/apimachinery/pkg/util/sets"
2325 "k8s.io/cli-runtime/pkg/genericiooptions"
2426 "k8s.io/client-go/rest"
2527 kcmdutil "k8s.io/kubectl/pkg/cmd/util"
2628 "k8s.io/kubectl/pkg/util/templates"
2729
30+ configv1 "github.com/openshift/api/config/v1"
2831 imagev1 "github.com/openshift/api/image/v1"
32+ configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
2933 "github.com/openshift/library-go/pkg/image/dockerv1client"
3034 "github.com/openshift/library-go/pkg/manifest"
3135 "github.com/openshift/oc/pkg/cli/image/extract"
3236 "github.com/openshift/oc/pkg/cli/image/imagesource"
3337 imagemanifest "github.com/openshift/oc/pkg/cli/image/manifest"
3438 "github.com/openshift/oc/pkg/cli/image/workqueue"
39+ "github.com/openshift/oc/pkg/version"
3540 "github.com/pkg/errors"
3641)
3742
@@ -349,20 +354,26 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
349354 }
350355 }
351356
352- tarEntryCallbacks := []extract. TarEntryFunc {}
357+ var manifestsCallbacks []func ( string , []manifest. Manifest , io. Reader , []configv1. ClusterVersionCapability ) ( bool , error )
353358
354- var manifestErrs []error
359+ var needEnabledCapabilities bool
360+ inclusionConfig := manifestInclusionConfiguration {}
355361 if o .ExtractManifests {
356362 expectedProviderSpecKind := credRequestCloudProviderSpecKindMapping [o .Cloud ]
357-
358- include := func (m * manifest.Manifest ) error { return nil } // default to including everything
359363 if o .Included {
360364 context := "connected cluster"
361- inclusionConfig := manifestInclusionConfiguration {}
365+ clientVersion , reportedVersion , err := version .ExtractVersion ()
366+ if err != nil {
367+ return err
368+ }
369+ if reportedVersion == "" {
370+ reportedVersion = clientVersion .String ()
371+ }
362372 if o .InstallConfig == "" {
363- inclusionConfig , err = findClusterIncludeConfig (ctx , o .RESTConfig )
373+ needEnabledCapabilities = true
374+ inclusionConfig , err = findClusterIncludeConfig (ctx , o .RESTConfig , reportedVersion )
364375 } else {
365- inclusionConfig , err = findClusterIncludeConfigFromInstallConfig (ctx , o .InstallConfig )
376+ inclusionConfig , err = findClusterIncludeConfigFromInstallConfig (ctx , o .InstallConfig , reportedVersion )
366377 context = o .InstallConfig
367378 }
368379 if err != nil {
@@ -377,11 +388,10 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
377388 return fmt .Errorf ("unrecognized platform for CredentialsRequests: %q" , * inclusionConfig .Platform )
378389 }
379390 }
380- include = newIncluder (inclusionConfig )
381391 }
382392
383- tarEntryCallbacks = append (tarEntryCallbacks , func (hdr * tar. Header , _ extract. LayerInfo , r io.Reader ) (bool , error ) {
384- if hdr . Name == "image-references" && ! o .CredentialsRequests {
393+ manifestsCallbacks = append (manifestsCallbacks , func (name string , ms []manifest. Manifest , r io.Reader , enabled []configv1. ClusterVersionCapability ) (cont bool , err error ) {
394+ if name == "image-references" && ! o .CredentialsRequests {
385395 buf := & bytes.Buffer {}
386396 if _ , err := io .Copy (buf , r ); err != nil {
387397 return false , fmt .Errorf ("unable to load image-references from release payload: %w" , err )
@@ -399,7 +409,7 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
399409
400410 out := o .Out
401411 if o .Directory != "" {
402- out , err = os .Create (filepath .Join (o .Directory , hdr . Name ))
412+ out , err = os .Create (filepath .Join (o .Directory , name ))
403413 if err != nil {
404414 return false , err
405415 }
@@ -409,10 +419,10 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
409419 return true , err
410420 }
411421 return true , nil
412- } else if hdr . Name == "release-metadata" && ! o .CredentialsRequests {
422+ } else if name == "release-metadata" && ! o .CredentialsRequests {
413423 out := o .Out
414424 if o .Directory != "" {
415- out , err = os .Create (filepath .Join (o .Directory , hdr . Name ))
425+ out , err = os .Create (filepath .Join (o .Directory , name ))
416426 if err != nil {
417427 return false , err
418428 }
@@ -424,22 +434,18 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
424434 return true , nil
425435 }
426436
427- if ext := path .Ext (hdr .Name ); len (ext ) == 0 || ! (ext == ".yaml" || ext == ".yml" || ext == ".json" ) {
428- return true , nil
429- }
430- klog .V (4 ).Infof ("Found manifest %s" , hdr .Name )
431- ms , err := manifest .ParseManifests (r )
432- if err != nil {
433- manifestErrs = append (manifestErrs , errors .Wrapf (err , "error parsing %s" , hdr .Name ))
434- return true , nil
435- }
436-
437437 for i := len (ms ) - 1 ; i >= 0 ; i -- {
438438 if o .Included && o .CredentialsRequests && ms [i ].GVK == credentialsRequestGVK && len (ms [i ].Obj .GetAnnotations ()) == 0 {
439439 klog .V (4 ).Infof ("Including %s for manual CredentialsRequests, despite lack of annotations" , ms [i ].String ())
440- } else if err := include (& ms [i ]); err != nil {
441- klog .V (4 ).Infof ("Excluding %s: %s" , ms [i ].String (), err )
442- ms = append (ms [:i ], ms [i + 1 :]... )
440+ } else if o .Included {
441+ clusterVersionCapabilitiesStatus := & configv1.ClusterVersionCapabilitiesStatus {
442+ KnownCapabilities : sets .New [configv1.ClusterVersionCapability ](append (inclusionConfig .Capabilities .KnownCapabilities , configv1 .KnownClusterVersionCapabilities ... )... ).UnsortedList (),
443+ EnabledCapabilities : sets .New [configv1.ClusterVersionCapability ](append (inclusionConfig .Capabilities .EnabledCapabilities , enabled ... )... ).UnsortedList (),
444+ }
445+ if err := ms [i ].Include (inclusionConfig .ExcludeIdentifier , inclusionConfig .RequiredFeatureSet , inclusionConfig .Profile , clusterVersionCapabilitiesStatus , inclusionConfig .Overrides ); err != nil {
446+ klog .V (4 ).Infof ("Excluding %s: %s" , ms [i ].String (), err )
447+ ms = append (ms [:i ], ms [i + 1 :]... )
448+ }
443449 }
444450 }
445451
@@ -470,20 +476,20 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
470476
471477 out := o .Out
472478 if o .Directory != "" {
473- out , err = os .Create (filepath .Join (o .Directory , hdr . Name ))
479+ out , err = os .Create (filepath .Join (o .Directory , name ))
474480 if err != nil {
475- return false , errors .Wrapf (err , "error creating manifest in %s" , hdr . Name )
481+ return false , errors .Wrapf (err , "error creating manifest in %s" , name )
476482 }
477483 }
478484 if out != nil {
479485 for _ , m := range manifestsToWrite {
480486 yamlBytes , err := yaml .JSONToYAML (m .Raw )
481487 if err != nil {
482- return false , errors .Wrapf (err , "error serializing manifest in %s" , hdr . Name )
488+ return false , errors .Wrapf (err , "error serializing manifest in %s" , name )
483489 }
484490 fmt .Fprintf (out , "---\n " )
485491 if _ , err := out .Write (yamlBytes ); err != nil {
486- return false , errors .Wrapf (err , "error writing manifest in %s" , hdr . Name )
492+ return false , errors .Wrapf (err , "error writing manifest in %s" , name )
487493 }
488494 }
489495 }
@@ -493,39 +499,72 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
493499
494500 fileFound := false
495501 if o .File != "" {
496- tarEntryCallbacks = append (tarEntryCallbacks , func (hdr * tar. Header , _ extract. LayerInfo , r io.Reader ) (bool , error ) {
497- if hdr . Name != o .File {
502+ manifestsCallbacks = append (manifestsCallbacks , func (name string , _ []manifest. Manifest , r io.Reader , _ []configv1. ClusterVersionCapability ) (bool , error ) {
503+ if name != o .File {
498504 return true , nil
499505 }
500506 fileFound = true
501507 _ , err := io .Copy (o .Out , r )
502508 return false , err
503509 })
504510 }
505-
506- if len (tarEntryCallbacks ) > 0 {
507- tarEntryCallbacksDone := make ([]bool , len (tarEntryCallbacks ))
508- opts .TarEntryCallback = func (hdr * tar.Header , layer extract.LayerInfo , r io.Reader ) (bool , error ) {
509- for i , callback := range tarEntryCallbacks {
510- if tarEntryCallbacksDone [i ] {
511+ manifestsCallback := func (name string , ms []manifest.Manifest , r io.Reader , enabled []configv1.ClusterVersionCapability ) (bool , error ) {
512+ if len (manifestsCallbacks ) > 0 {
513+ callbacksDone := make ([]bool , len (manifestsCallbacks ))
514+ for i , callback := range manifestsCallbacks {
515+ if callbacksDone [i ] {
511516 continue
512517 }
513- if cont , err := callback (hdr , layer , r ); err != nil {
518+ if cont , err := callback (name , ms , r , enabled ); err != nil {
514519 return cont , err
515520 } else if ! cont {
516- tarEntryCallbacksDone [i ] = true
521+ callbacksDone [i ] = true
517522 }
518523 }
519524
520- for _ , done := range tarEntryCallbacksDone {
525+ for _ , done := range callbacksDone {
521526 if ! done {
522527 return true , nil // still some callbacks that want to keep working
523528 }
524529 }
525530
526531 return false , nil
527532 }
533+ return true , nil
534+ }
535+
536+ var currentPayloadManifests []manifest.Manifest
537+ if needEnabledCapabilities {
538+ optsToGetCurrentPayloadManifests , err := getOptsToGetCurrentPayloadManifests (ctx , opts , o .RESTConfig , inclusionConfig )
539+ if err != nil {
540+ return fmt .Errorf ("error getting opts to get current payload manifests: %w" , err )
541+ }
542+ optsToGetCurrentPayloadManifests .TarEntryCallback = func (h * tar.Header , _ extract.LayerInfo , r io.Reader ) (cont bool , err error ) {
543+ if ext := path .Ext (h .Name ); len (ext ) == 0 || ! (ext == ".yaml" || ext == ".yml" || ext == ".json" ) {
544+ return true , nil
545+ }
546+ klog .V (4 ).Infof ("Found manifest %s in the current release payload" , h .Name )
547+ ms , err := manifest .ParseManifests (r )
548+ if err != nil {
549+ return false , err
550+ }
551+ for i := len (ms ) - 1 ; i >= 0 ; i -- {
552+ if err := ms [i ].Include (inclusionConfig .ExcludeIdentifier , inclusionConfig .RequiredFeatureSet , inclusionConfig .Profile , inclusionConfig .Capabilities , inclusionConfig .Overrides ); err != nil {
553+ klog .V (4 ).Infof ("Excluding %s in the current release payload: %s" , ms [i ].String (), err )
554+ ms = append (ms [:i ], ms [i + 1 :]... )
555+ }
556+ }
557+ currentPayloadManifests = append (currentPayloadManifests , ms ... )
558+ return true , nil
559+ }
560+ if err := optsToGetCurrentPayloadManifests .Run (); err != nil {
561+ return fmt .Errorf ("error getting current payload manifests: %w" , err )
562+ }
563+
528564 }
565+ manifestReceiver := NewManifestReceiver (manifestsCallback , needEnabledCapabilities , sets .New [string ]("image-references" , "release-metadata" ), currentPayloadManifests , inclusionConfig )
566+ opts .TarEntryCallback = manifestReceiver .TarEntryCallback
567+ opts .TarEntryCallbackDoneCallback = manifestReceiver .TarEntryCallbackDoneCallback
529568
530569 if err := opts .Run (); err != nil {
531570 return err
@@ -553,14 +592,48 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
553592
554593 // Only output manifest errors if manifests were being extracted.
555594 // Do not return an error so current operation, e.g. mirroring, continues.
556- if o .ExtractManifests && len (manifestErrs ) > 0 {
557- fmt .Fprintf (o .ErrOut , "Errors: %s\n " , errorList (manifestErrs ))
595+ if o .ExtractManifests && len (manifestReceiver . ManifestErrs ) > 0 {
596+ fmt .Fprintf (o .ErrOut , "Errors: %s\n " , errorList (manifestReceiver . ManifestErrs ))
558597 }
559598
560599 return nil
561600
562601}
563602
603+ func getOptsToGetCurrentPayloadManifests (ctx context.Context , source * extract.ExtractOptions , config * rest.Config , inclusionConfiguration manifestInclusionConfiguration ) (* extract.ExtractOptions , error ) {
604+ client , err := configv1client .NewForConfig (config )
605+ if err != nil {
606+ return nil , err
607+ }
608+
609+ clusterVersion , err := client .ClusterVersions ().Get (ctx , "version" , metav1.GetOptions {})
610+ if err != nil {
611+ return nil , err
612+ }
613+
614+ src := clusterVersion .Status .Desired .Image
615+ ref , err := imagesource .ParseReference (src )
616+ if err != nil {
617+ return nil , err
618+ }
619+ klog .V (4 ).Infof ("The outgoing release payload from %s is running on the cluster: %s" , src , config .Host )
620+ opts := extract .NewExtractOptions (genericiooptions.IOStreams {Out : source .Out , ErrOut : source .ErrOut })
621+ opts .ParallelOptions = source .ParallelOptions
622+ opts .SecurityOptions = source .SecurityOptions
623+ opts .FilterOptions = source .FilterOptions
624+ opts .FileDir = source .FileDir
625+ opts .OnlyFiles = true
626+ opts .ICSPFile = source .ICSPFile
627+ opts .IDMSFile = source .IDMSFile
628+ opts .Mappings = []extract.Mapping {
629+ {
630+ ImageRef : ref ,
631+ From : "release-manifests/" ,
632+ },
633+ }
634+ return opts , nil
635+ }
636+
564637func (o * ExtractOptions ) extractGit (dir string ) error {
565638 switch o .Output {
566639 case "commit" , "" :
0 commit comments