github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/resolver/steps.go (about)

     1  package resolver
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  
     9  	olmerrors "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/errors"
    10  	"github.com/operator-framework/operator-registry/pkg/api"
    11  	extScheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    14  	"k8s.io/apimachinery/pkg/runtime"
    15  	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
    16  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    17  	"k8s.io/apimachinery/pkg/util/yaml"
    18  	k8sscheme "k8s.io/client-go/kubernetes/scheme"
    19  
    20  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    21  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache"
    22  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/projection"
    23  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
    24  )
    25  
    26  const (
    27  	secretKind       = "Secret"
    28  	BundleSecretKind = "BundleSecret"
    29  )
    30  
    31  var (
    32  	scheme = runtime.NewScheme()
    33  )
    34  
    35  func init() {
    36  	utilruntime.Must(k8sscheme.AddToScheme(scheme))
    37  	utilruntime.Must(extScheme.AddToScheme(scheme))
    38  	utilruntime.Must(v1alpha1.AddToScheme(scheme))
    39  }
    40  
    41  // NewStepResourceForObject returns a new StepResource for the provided object
    42  func NewStepResourceFromObject(obj runtime.Object, catalogSourceName, catalogSourceNamespace string) (v1alpha1.StepResource, error) {
    43  	var resource v1alpha1.StepResource
    44  
    45  	// set up object serializer
    46  	serializer := k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false)
    47  
    48  	// create an object manifest
    49  	var manifest bytes.Buffer
    50  	err := serializer.Encode(obj, &manifest)
    51  	if err != nil {
    52  		return resource, err
    53  	}
    54  
    55  	if err := ownerutil.InferGroupVersionKind(obj); err != nil {
    56  		return resource, err
    57  	}
    58  
    59  	gvk := obj.GetObjectKind().GroupVersionKind()
    60  
    61  	metaObj, ok := obj.(metav1.Object)
    62  	if !ok {
    63  		return resource, fmt.Errorf("couldn't get object metadata")
    64  	}
    65  
    66  	name := metaObj.GetName()
    67  	if name == "" {
    68  		name = metaObj.GetGenerateName()
    69  	}
    70  
    71  	// create the resource
    72  	resource = v1alpha1.StepResource{
    73  		Name:                   name,
    74  		Kind:                   gvk.Kind,
    75  		Group:                  gvk.Group,
    76  		Version:                gvk.Version,
    77  		Manifest:               manifest.String(),
    78  		CatalogSource:          catalogSourceName,
    79  		CatalogSourceNamespace: catalogSourceNamespace,
    80  	}
    81  
    82  	// BundleSecret is a synthetic kind that OLM uses to distinguish between secrets included in the bundle and
    83  	// pull secrets included in the installplan
    84  	if obj.GetObjectKind().GroupVersionKind().Kind == secretKind {
    85  		resource.Kind = BundleSecretKind
    86  	}
    87  
    88  	return resource, nil
    89  }
    90  
    91  func NewSubscriptionStepResource(namespace string, info cache.OperatorSourceInfo) (v1alpha1.StepResource, error) {
    92  	return NewStepResourceFromObject(&v1alpha1.Subscription{
    93  		ObjectMeta: metav1.ObjectMeta{
    94  			Namespace: namespace,
    95  			Name:      strings.Join([]string{info.Package, info.Channel, info.Catalog.Name, info.Catalog.Namespace}, "-"),
    96  		},
    97  		Spec: &v1alpha1.SubscriptionSpec{
    98  			CatalogSource:          info.Catalog.Name,
    99  			CatalogSourceNamespace: info.Catalog.Namespace,
   100  			Package:                info.Package,
   101  			Channel:                info.Channel,
   102  			StartingCSV:            info.StartingCSV,
   103  			InstallPlanApproval:    v1alpha1.ApprovalAutomatic,
   104  		},
   105  	}, info.Catalog.Name, info.Catalog.Namespace)
   106  }
   107  
   108  func V1alpha1CSVFromBundle(bundle *api.Bundle) (*v1alpha1.ClusterServiceVersion, error) {
   109  	csv := &v1alpha1.ClusterServiceVersion{}
   110  	if err := json.Unmarshal([]byte(bundle.CsvJson), csv); err != nil {
   111  		return nil, err
   112  	}
   113  	return csv, nil
   114  }
   115  
   116  func NewStepResourceFromBundle(bundle *api.Bundle, namespace, replaces, catalogSourceName, catalogSourceNamespace string) ([]v1alpha1.StepResource, error) {
   117  	csv, err := V1alpha1CSVFromBundle(bundle)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	// Check unpacked bundled for for missing APIVersion or Kind
   123  	if csv.APIVersion == "" {
   124  		return nil, olmerrors.NewFatalError(fmt.Errorf("bundle CSV %s missing APIVersion", csv.Name))
   125  	}
   126  
   127  	if csv.Kind == "" {
   128  		return nil, olmerrors.NewFatalError(fmt.Errorf("bundle CSV %s missing Kind", csv.Name))
   129  	}
   130  
   131  	csv.SetNamespace(namespace)
   132  	csv.Spec.Replaces = replaces
   133  	anno, err := projection.PropertiesAnnotationFromPropertyList(bundle.Properties)
   134  	if err != nil {
   135  		return nil, fmt.Errorf("failed to construct properties annotation for %q: %w", csv.GetName(), err)
   136  	}
   137  
   138  	annos := csv.GetAnnotations()
   139  	if annos == nil {
   140  		annos = make(map[string]string)
   141  	}
   142  	annos[projection.PropertiesAnnotationKey] = anno
   143  	csv.SetAnnotations(annos)
   144  
   145  	step, err := NewStepResourceFromObject(csv, catalogSourceName, catalogSourceNamespace)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	steps := []v1alpha1.StepResource{step}
   150  
   151  	for _, object := range bundle.Object {
   152  		dec := yaml.NewYAMLOrJSONDecoder(strings.NewReader(object), 10)
   153  		unst := &unstructured.Unstructured{}
   154  		if err := dec.Decode(unst); err != nil {
   155  			return nil, err
   156  		}
   157  
   158  		if unst.GetObjectKind().GroupVersionKind().Kind == v1alpha1.ClusterServiceVersionKind {
   159  			continue
   160  		}
   161  
   162  		step, err := NewStepResourceFromObject(unst, catalogSourceName, catalogSourceNamespace)
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  		steps = append(steps, step)
   167  	}
   168  
   169  	operatorServiceAccountSteps, err := NewServiceAccountStepResources(csv, catalogSourceName, catalogSourceNamespace)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	steps = append(steps, operatorServiceAccountSteps...)
   174  	return steps, nil
   175  }
   176  
   177  func NewStepsFromBundle(bundle *api.Bundle, namespace, replaces, catalogSourceName, catalogSourceNamespace string) ([]*v1alpha1.Step, error) {
   178  	bundleSteps, err := NewStepResourceFromBundle(bundle, namespace, replaces, catalogSourceName, catalogSourceNamespace)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	var steps []*v1alpha1.Step
   184  	for _, s := range bundleSteps {
   185  		steps = append(steps, &v1alpha1.Step{
   186  			Resolving: bundle.CsvName,
   187  			Resource:  s,
   188  			Status:    v1alpha1.StepStatusUnknown,
   189  		})
   190  	}
   191  
   192  	return steps, nil
   193  }
   194  
   195  // NewServiceAccountStepResources returns a list of step resources required to satisfy the RBAC requirements of the given CSV's InstallStrategy
   196  func NewServiceAccountStepResources(csv *v1alpha1.ClusterServiceVersion, catalogSourceName, catalogSourceNamespace string) ([]v1alpha1.StepResource, error) {
   197  	var rbacSteps []v1alpha1.StepResource
   198  
   199  	operatorPermissions, err := RBACForClusterServiceVersion(csv)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	for _, perms := range operatorPermissions {
   205  		if perms.ServiceAccount.Name != "default" {
   206  			step, err := NewStepResourceFromObject(perms.ServiceAccount, catalogSourceName, catalogSourceNamespace)
   207  			if err != nil {
   208  				return nil, err
   209  			}
   210  			rbacSteps = append(rbacSteps, step)
   211  		}
   212  		for _, role := range perms.Roles {
   213  			step, err := NewStepResourceFromObject(role, catalogSourceName, catalogSourceNamespace)
   214  			if err != nil {
   215  				return nil, err
   216  			}
   217  			rbacSteps = append(rbacSteps, step)
   218  		}
   219  		for _, roleBinding := range perms.RoleBindings {
   220  			step, err := NewStepResourceFromObject(roleBinding, catalogSourceName, catalogSourceNamespace)
   221  			if err != nil {
   222  				return nil, err
   223  			}
   224  			rbacSteps = append(rbacSteps, step)
   225  		}
   226  		for _, clusterRole := range perms.ClusterRoles {
   227  			step, err := NewStepResourceFromObject(clusterRole, catalogSourceName, catalogSourceNamespace)
   228  			if err != nil {
   229  				return nil, err
   230  			}
   231  			rbacSteps = append(rbacSteps, step)
   232  		}
   233  		for _, clusterRoleBinding := range perms.ClusterRoleBindings {
   234  			step, err := NewStepResourceFromObject(clusterRoleBinding, catalogSourceName, catalogSourceNamespace)
   235  			if err != nil {
   236  				return nil, err
   237  			}
   238  			rbacSteps = append(rbacSteps, step)
   239  		}
   240  	}
   241  	return rbacSteps, nil
   242  }