Skip to content

Commit c1909d5

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

File tree

3 files changed

+272
-58
lines changed

3 files changed

+272
-58
lines changed

pkg/cli/admin/release/extract.go

Lines changed: 106 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@ 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"
@@ -349,17 +353,16 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
349353
}
350354
}
351355

352-
tarEntryCallbacks := []extract.TarEntryFunc{}
356+
var manifestsCallbacks []func(string, []manifest.Manifest, io.Reader, []configv1.ClusterVersionCapability) (bool, error)
353357

354-
var manifestErrs []error
358+
var needEnabledCapabilities bool
359+
inclusionConfig := manifestInclusionConfiguration{}
355360
if o.ExtractManifests {
356361
expectedProviderSpecKind := credRequestCloudProviderSpecKindMapping[o.Cloud]
357-
358-
include := func(m *manifest.Manifest) error { return nil } // default to including everything
359362
if o.Included {
360363
context := "connected cluster"
361-
inclusionConfig := manifestInclusionConfiguration{}
362364
if o.InstallConfig == "" {
365+
needEnabledCapabilities = true
363366
inclusionConfig, err = findClusterIncludeConfig(ctx, o.RESTConfig)
364367
} else {
365368
inclusionConfig, err = findClusterIncludeConfigFromInstallConfig(ctx, o.InstallConfig)
@@ -377,11 +380,10 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
377380
return fmt.Errorf("unrecognized platform for CredentialsRequests: %q", *inclusionConfig.Platform)
378381
}
379382
}
380-
include = newIncluder(inclusionConfig)
381383
}
382384

383-
tarEntryCallbacks = append(tarEntryCallbacks, func(hdr *tar.Header, _ extract.LayerInfo, r io.Reader) (bool, error) {
384-
if hdr.Name == "image-references" && !o.CredentialsRequests {
385+
manifestsCallbacks = append(manifestsCallbacks, func(name string, ms []manifest.Manifest, r io.Reader, enabled []configv1.ClusterVersionCapability) (cont bool, err error) {
386+
if name == "image-references" && !o.CredentialsRequests {
385387
buf := &bytes.Buffer{}
386388
if _, err := io.Copy(buf, r); err != nil {
387389
return false, fmt.Errorf("unable to load image-references from release payload: %w", err)
@@ -399,7 +401,7 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
399401

400402
out := o.Out
401403
if o.Directory != "" {
402-
out, err = os.Create(filepath.Join(o.Directory, hdr.Name))
404+
out, err = os.Create(filepath.Join(o.Directory, name))
403405
if err != nil {
404406
return false, err
405407
}
@@ -409,10 +411,10 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
409411
return true, err
410412
}
411413
return true, nil
412-
} else if hdr.Name == "release-metadata" && !o.CredentialsRequests {
414+
} else if name == "release-metadata" && !o.CredentialsRequests {
413415
out := o.Out
414416
if o.Directory != "" {
415-
out, err = os.Create(filepath.Join(o.Directory, hdr.Name))
417+
out, err = os.Create(filepath.Join(o.Directory, name))
416418
if err != nil {
417419
return false, err
418420
}
@@ -424,22 +426,18 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
424426
return true, nil
425427
}
426428

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-
437429
for i := len(ms) - 1; i >= 0; i-- {
438430
if o.Included && o.CredentialsRequests && ms[i].GVK == credentialsRequestGVK && len(ms[i].Obj.GetAnnotations()) == 0 {
439431
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:]...)
432+
} else if o.Included {
433+
clusterVersionCapabilitiesStatus := &configv1.ClusterVersionCapabilitiesStatus{
434+
KnownCapabilities: sets.New[configv1.ClusterVersionCapability](append(inclusionConfig.Capabilities.KnownCapabilities, configv1.KnownClusterVersionCapabilities...)...).UnsortedList(),
435+
EnabledCapabilities: sets.New[configv1.ClusterVersionCapability](append(inclusionConfig.Capabilities.EnabledCapabilities, enabled...)...).UnsortedList(),
436+
}
437+
if err := ms[i].Include(inclusionConfig.ExcludeIdentifier, inclusionConfig.RequiredFeatureSet, inclusionConfig.Profile, clusterVersionCapabilitiesStatus, inclusionConfig.Overrides); err != nil {
438+
klog.V(4).Infof("Excluding %s: %s", ms[i].String(), err)
439+
ms = append(ms[:i], ms[i+1:]...)
440+
}
443441
}
444442
}
445443

@@ -470,20 +468,20 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
470468

471469
out := o.Out
472470
if o.Directory != "" {
473-
out, err = os.Create(filepath.Join(o.Directory, hdr.Name))
471+
out, err = os.Create(filepath.Join(o.Directory, name))
474472
if err != nil {
475-
return false, errors.Wrapf(err, "error creating manifest in %s", hdr.Name)
473+
return false, errors.Wrapf(err, "error creating manifest in %s", name)
476474
}
477475
}
478476
if out != nil {
479477
for _, m := range manifestsToWrite {
480478
yamlBytes, err := yaml.JSONToYAML(m.Raw)
481479
if err != nil {
482-
return false, errors.Wrapf(err, "error serializing manifest in %s", hdr.Name)
480+
return false, errors.Wrapf(err, "error serializing manifest in %s", name)
483481
}
484482
fmt.Fprintf(out, "---\n")
485483
if _, err := out.Write(yamlBytes); err != nil {
486-
return false, errors.Wrapf(err, "error writing manifest in %s", hdr.Name)
484+
return false, errors.Wrapf(err, "error writing manifest in %s", name)
487485
}
488486
}
489487
}
@@ -493,39 +491,72 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
493491

494492
fileFound := false
495493
if o.File != "" {
496-
tarEntryCallbacks = append(tarEntryCallbacks, func(hdr *tar.Header, _ extract.LayerInfo, r io.Reader) (bool, error) {
497-
if hdr.Name != o.File {
494+
manifestsCallbacks = append(manifestsCallbacks, func(name string, _ []manifest.Manifest, r io.Reader, _ []configv1.ClusterVersionCapability) (bool, error) {
495+
if name != o.File {
498496
return true, nil
499497
}
500498
fileFound = true
501499
_, err := io.Copy(o.Out, r)
502500
return false, err
503501
})
504502
}
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] {
503+
manifestsCallback := func(name string, ms []manifest.Manifest, r io.Reader, enabled []configv1.ClusterVersionCapability) (bool, error) {
504+
if len(manifestsCallbacks) > 0 {
505+
callbacksDone := make([]bool, len(manifestsCallbacks))
506+
for i, callback := range manifestsCallbacks {
507+
if callbacksDone[i] {
511508
continue
512509
}
513-
if cont, err := callback(hdr, layer, r); err != nil {
510+
if cont, err := callback(name, ms, r, enabled); err != nil {
514511
return cont, err
515512
} else if !cont {
516-
tarEntryCallbacksDone[i] = true
513+
callbacksDone[i] = true
517514
}
518515
}
519516

520-
for _, done := range tarEntryCallbacksDone {
517+
for _, done := range callbacksDone {
521518
if !done {
522519
return true, nil // still some callbacks that want to keep working
523520
}
524521
}
525522

526523
return false, nil
527524
}
525+
return true, nil
526+
}
527+
528+
var currentPayloadManifests []manifest.Manifest
529+
if needEnabledCapabilities {
530+
optsToGetCurrentPayloadManifests, err := getOptsToGetCurrentPayloadManifests(ctx, opts, o.RESTConfig, inclusionConfig)
531+
if err != nil {
532+
return fmt.Errorf("error getting opts to get current payload manifests: %w", err)
533+
}
534+
optsToGetCurrentPayloadManifests.TarEntryCallback = func(h *tar.Header, _ extract.LayerInfo, r io.Reader) (cont bool, err error) {
535+
if ext := path.Ext(h.Name); len(ext) == 0 || !(ext == ".yaml" || ext == ".yml" || ext == ".json") {
536+
return true, nil
537+
}
538+
klog.V(4).Infof("Found manifest %s in the current release payload", h.Name)
539+
ms, err := manifest.ParseManifests(r)
540+
if err != nil {
541+
return false, err
542+
}
543+
for i := len(ms) - 1; i >= 0; i-- {
544+
if err := ms[i].Include(inclusionConfig.ExcludeIdentifier, inclusionConfig.RequiredFeatureSet, inclusionConfig.Profile, inclusionConfig.Capabilities, inclusionConfig.Overrides); err != nil {
545+
klog.V(4).Infof("Excluding %s in the current release payload: %s", ms[i].String(), err)
546+
ms = append(ms[:i], ms[i+1:]...)
547+
}
548+
}
549+
currentPayloadManifests = append(currentPayloadManifests, ms...)
550+
return true, nil
551+
}
552+
if err := optsToGetCurrentPayloadManifests.Run(); err != nil {
553+
return fmt.Errorf("error getting current payload manifests: %w", err)
554+
}
555+
528556
}
557+
manifestReceiver := NewManifestReceiver(manifestsCallback, needEnabledCapabilities, sets.New[string]("image-references", "release-metadata"), currentPayloadManifests, inclusionConfig)
558+
opts.TarEntryCallback = manifestReceiver.TarEntryCallback
559+
opts.TarEntryCallbackDoneCallback = manifestReceiver.TarEntryCallbackDoneCallback
529560

530561
if err := opts.Run(); err != nil {
531562
return err
@@ -553,14 +584,48 @@ func (o *ExtractOptions) Run(ctx context.Context) error {
553584

554585
// Only output manifest errors if manifests were being extracted.
555586
// 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))
587+
if o.ExtractManifests && len(manifestReceiver.ManifestErrs) > 0 {
588+
fmt.Fprintf(o.ErrOut, "Errors: %s\n", errorList(manifestReceiver.ManifestErrs))
558589
}
559590

560591
return nil
561592

562593
}
563594

595+
func getOptsToGetCurrentPayloadManifests(ctx context.Context, source *extract.ExtractOptions, config *rest.Config, inclusionConfiguration manifestInclusionConfiguration) (*extract.ExtractOptions, error) {
596+
client, err := configv1client.NewForConfig(config)
597+
if err != nil {
598+
return nil, err
599+
}
600+
601+
clusterVersion, err := client.ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
602+
if err != nil {
603+
return nil, err
604+
}
605+
606+
src := clusterVersion.Status.Desired.Image
607+
ref, err := imagesource.ParseReference(src)
608+
if err != nil {
609+
return nil, err
610+
}
611+
klog.V(4).Infof("The outgoing release payload from %s is running on the cluster: %s", src, config.Host)
612+
opts := extract.NewExtractOptions(genericiooptions.IOStreams{Out: source.Out, ErrOut: source.ErrOut})
613+
opts.ParallelOptions = source.ParallelOptions
614+
opts.SecurityOptions = source.SecurityOptions
615+
opts.FilterOptions = source.FilterOptions
616+
opts.FileDir = source.FileDir
617+
opts.OnlyFiles = true
618+
opts.ICSPFile = source.ICSPFile
619+
opts.IDMSFile = source.IDMSFile
620+
opts.Mappings = []extract.Mapping{
621+
{
622+
ImageRef: ref,
623+
From: "release-manifests/",
624+
},
625+
}
626+
return opts, nil
627+
}
628+
564629
func (o *ExtractOptions) extractGit(dir string) error {
565630
switch o.Output {
566631
case "commit", "":

0 commit comments

Comments
 (0)