Skip to content

Commit def90a6

Browse files
committed
OTA-1010: extract included manifests with net-new capabilities
1 parent e3b2070 commit def90a6

File tree

3 files changed

+295
-68
lines changed

3 files changed

+295
-68
lines changed

pkg/cli/admin/release/extract.go

Lines changed: 116 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
564637
func (o *ExtractOptions) extractGit(dir string) error {
565638
switch o.Output {
566639
case "commit", "":

0 commit comments

Comments
 (0)