github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/catalog/manifests.go (about)

     1  package catalog
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  
     7  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
     8  	"github.com/operator-framework/operator-registry/pkg/configmap"
     9  	errorwrap "github.com/pkg/errors"
    10  	"github.com/sirupsen/logrus"
    11  	v1 "k8s.io/client-go/listers/core/v1"
    12  
    13  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
    14  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/projection"
    15  )
    16  
    17  // ManifestResolver can dereference a manifest for a step. Steps may embed manifests directly or reference content
    18  // in configmaps
    19  type ManifestResolver interface {
    20  	ManifestForStep(step *v1alpha1.Step) (string, error)
    21  }
    22  
    23  // manifestResolver caches manifest from unpacked bundles (via configmaps)
    24  type manifestResolver struct {
    25  	configMapLister v1.ConfigMapLister
    26  	unpackedSteps   map[string][]v1alpha1.StepResource
    27  	namespace       string
    28  	logger          logrus.FieldLogger
    29  }
    30  
    31  func newManifestResolver(namespace string, configMapLister v1.ConfigMapLister, logger logrus.FieldLogger) *manifestResolver {
    32  	return &manifestResolver{
    33  		namespace:       namespace,
    34  		configMapLister: configMapLister,
    35  		unpackedSteps:   map[string][]v1alpha1.StepResource{},
    36  		logger:          logger,
    37  	}
    38  }
    39  
    40  // ManifestForStep always returns the manifest that should be applied to the cluster for a given step
    41  // the manifest field in the installplan status can contain a reference to a configmap instead
    42  func (r *manifestResolver) ManifestForStep(step *v1alpha1.Step) (string, error) {
    43  	manifest := step.Resource.Manifest
    44  	ref := refForStep(step, r.logger)
    45  	if ref == nil {
    46  		return manifest, nil
    47  	}
    48  
    49  	log := r.logger.WithFields(logrus.Fields{"resolving": step.Resolving, "step": step.Resource.Name})
    50  	log.WithField("ref", ref).Debug("step is a reference to configmap")
    51  
    52  	usteps, err := r.unpackedStepsForBundle(step.Resolving, ref)
    53  	if err != nil {
    54  		return "", err
    55  	}
    56  
    57  	log.Debugf("checking cache for unpacked step")
    58  	// need to find the real manifest from the unpacked steps
    59  	for _, u := range usteps {
    60  		if u.Name == step.Resource.Name &&
    61  			u.Kind == step.Resource.Kind &&
    62  			u.Version == step.Resource.Version &&
    63  			u.Group == step.Resource.Group {
    64  			manifest = u.Manifest
    65  			log.WithField("manifest", manifest).Debug("step replaced with unpacked value")
    66  			break
    67  		}
    68  	}
    69  	if manifest == step.Resource.Manifest {
    70  		return "", fmt.Errorf("couldn't find unpacked step for %v", step)
    71  	}
    72  	return manifest, nil
    73  }
    74  
    75  func (r *manifestResolver) unpackedStepsForBundle(bundleName string, ref *UnpackedBundleReference) ([]v1alpha1.StepResource, error) {
    76  	usteps, ok := r.unpackedSteps[bundleName]
    77  	if ok {
    78  		return usteps, nil
    79  	}
    80  	cm, err := r.configMapLister.ConfigMaps(ref.Namespace).Get(ref.Name)
    81  	if err != nil {
    82  		return nil, errorwrap.Wrapf(err, "error finding unpacked bundle configmap for ref %v", *ref)
    83  	}
    84  	loader := configmap.NewBundleLoader()
    85  	bundle, err := loader.Load(cm)
    86  	if err != nil {
    87  		return nil, errorwrap.Wrapf(err, "error loading unpacked bundle configmap for ref %v", *ref)
    88  	}
    89  
    90  	if ref.Properties != "" {
    91  		props, err := projection.PropertyListFromPropertiesAnnotation(ref.Properties)
    92  		if err != nil {
    93  			return nil, fmt.Errorf("failed to load bundle properties for %q: %w", bundle.CsvName, err)
    94  		}
    95  		bundle.Properties = props
    96  	}
    97  
    98  	steps, err := resolver.NewStepResourceFromBundle(bundle, r.namespace, ref.Replaces, ref.CatalogSourceName, ref.CatalogSourceNamespace)
    99  	if err != nil {
   100  		return nil, errorwrap.Wrapf(err, "error calculating steps for ref %v", *ref)
   101  	}
   102  	r.unpackedSteps[bundleName] = steps
   103  	return steps, nil
   104  }
   105  
   106  func refForStep(step *v1alpha1.Step, log logrus.FieldLogger) *UnpackedBundleReference {
   107  	log = log.WithFields(logrus.Fields{"resolving": step.Resolving, "step": step.Resource.Name})
   108  	var ref UnpackedBundleReference
   109  	if err := json.Unmarshal([]byte(step.Resource.Manifest), &ref); err != nil {
   110  		log.Debug("step is not a reference to an unpacked bundle (this is not an error if the step is a manifest)")
   111  		return nil
   112  	}
   113  	log = log.WithField("ref", ref)
   114  	if ref.Kind != "ConfigMap" || ref.Name == "" || ref.Namespace == "" || ref.CatalogSourceName == "" || ref.CatalogSourceNamespace == "" {
   115  		log.Debug("step is not a reference to an unpacked bundle (this is not an error if the step is a manifest)")
   116  		return nil
   117  	}
   118  	return &ref
   119  }