...

Source file src/github.com/redhat-developer/odo/pkg/service/service_binding_builder.go

Documentation: github.com/redhat-developer/odo/pkg/service

     1  package service
     2  
     3  // This file is a fork of github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/collect/impl.go
     4  // This file was created to deal with the permission issue that comes up when user does not access to clusterwide resources,
     5  // and they want to use odo link without the Service Binding Operator.
     6  // The only difference between the original and forked implementation is that we check if the user is forbidden from accessing
     7  // CRD, and if they are, then we simply ignore checking CRD while linking.
     8  // For more information, see issue: https://github.com/redhat-developer/odo/issues/4965
     9  // In case there is a need to revert the changes, or we figure out an alternate way of allowing forbidden users to link without SBO,
    10  // we can go back to using the builder.DefaultBuilder instead of the OdoDefaultBuilder in getPipeline.
    11  
    12  import (
    13  	"fmt"
    14  
    15  	"github.com/redhat-developer/odo/pkg/log"
    16  
    17  	"github.com/redhat-developer/service-binding-operator/apis"
    18  	"github.com/redhat-developer/service-binding-operator/pkg/binding"
    19  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  
    21  	"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline"
    22  	"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/builder"
    23  	"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/collect"
    24  	"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/mapping"
    25  	"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/naming"
    26  	"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/project"
    27  	"github.com/redhat-developer/service-binding-operator/pkg/util"
    28  	kerrors "k8s.io/apimachinery/pkg/api/errors"
    29  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    30  )
    31  
    32  var defaultFlow = []pipeline.Handler{
    33  	pipeline.HandlerFunc(project.Unbind),
    34  	pipeline.HandlerFunc(collect.PreFlight),
    35  	pipeline.HandlerFunc(ProvisionedService),
    36  	pipeline.HandlerFunc(collect.DirectSecretReference),
    37  	pipeline.HandlerFunc(BindingDefinitions),
    38  	pipeline.HandlerFunc(collect.BindingItems),
    39  	pipeline.HandlerFunc(collect.OwnedResources),
    40  	pipeline.HandlerFunc(mapping.Handle),
    41  	pipeline.HandlerFunc(naming.Handle),
    42  	pipeline.HandlerFunc(project.PreFlightCheck()),
    43  	pipeline.HandlerFunc(project.InjectSecretRef),
    44  	pipeline.HandlerFunc(project.BindingsAsEnv),
    45  	pipeline.HandlerFunc(project.BindingsAsFiles),
    46  	pipeline.HandlerFunc(project.PostFlightCheck),
    47  }
    48  
    49  var OdoDefaultBuilder = builder.Builder().WithHandlers(defaultFlow...)
    50  
    51  func ProvisionedService(ctx pipeline.Context) {
    52  	services, _ := ctx.Services()
    53  
    54  	for _, service := range services {
    55  		res := service.Resource()
    56  		secretName, found, err := unstructured.NestedString(res.Object, "status", "binding", "name")
    57  		if err != nil {
    58  			requestRetry(ctx, collect.ErrorReadingBindingReason, err)
    59  			return
    60  		}
    61  		if found {
    62  			if secretName != "" {
    63  				secret, err := ctx.ReadSecret(res.GetNamespace(), secretName)
    64  				if err != nil {
    65  					requestRetry(ctx, collect.ErrorReadingSecret, err)
    66  					return
    67  				}
    68  				ctx.AddBindings(&pipeline.SecretBackedBindings{Service: service, Secret: secret})
    69  			}
    70  		} else {
    71  			crd, err := service.CustomResourceDefinition()
    72  			if err != nil {
    73  				// If the user does not have permission to access CRD, ignore
    74  				if !kerrors.IsForbidden(err) {
    75  					requestRetry(ctx, collect.ErrorReadingCRD, err)
    76  					return
    77  				} else {
    78  					log.Warning("Skipping the check for CRD, user does not have access")
    79  					continue
    80  				}
    81  			}
    82  			if crd == nil {
    83  				continue
    84  			}
    85  			v, ok := crd.Resource().GetAnnotations()[binding.ProvisionedServiceAnnotationKey]
    86  			if ok && v == "true" {
    87  				requestRetry(ctx, collect.ErrorReadingBindingReason, fmt.Errorf("CRD of service %v/%v indicates provisioned service, but no secret name provided under .status.binding.name", res.GetNamespace(), res.GetName()))
    88  				return
    89  			}
    90  		}
    91  	}
    92  }
    93  
    94  func BindingDefinitions(ctx pipeline.Context) {
    95  	services, _ := ctx.Services()
    96  
    97  	for _, service := range services {
    98  		anns := make(map[string]string)
    99  		crd, err := service.CustomResourceDefinition()
   100  		if err != nil {
   101  			// If the user does not have permission to access CRD, ignore
   102  			if !kerrors.IsForbidden(err) {
   103  				requestRetry(ctx, collect.ErrorReadingCRD, err)
   104  				return
   105  			} else {
   106  				log.Warning("Skipping the check for CRD, user does not have access")
   107  				//continue
   108  			}
   109  		}
   110  		if crd != nil {
   111  			descr, err := crd.Descriptor()
   112  			if err != nil {
   113  				requestRetry(ctx, collect.ErrorReadingDescriptorReason, err)
   114  				return
   115  			}
   116  			if descr != nil {
   117  				util.MergeMaps(anns, descr.BindingAnnotations())
   118  			}
   119  			util.MergeMaps(anns, crd.Resource().GetAnnotations())
   120  		}
   121  
   122  		util.MergeMaps(anns, service.Resource().GetAnnotations())
   123  
   124  		for k, v := range anns {
   125  			definition, err := makeBindingDefinition(k, v, ctx)
   126  			if err != nil {
   127  				condition := notCollectionReadyCond(collect.InvalidAnnotation, fmt.Errorf("failed to create binding definition from \"%v: %v\": %v", k, v, err))
   128  				ctx.SetCondition(condition)
   129  				ctx.Error(err)
   130  				ctx.StopProcessing()
   131  			}
   132  			if definition != nil {
   133  				service.AddBindingDef(definition)
   134  			}
   135  		}
   136  	}
   137  }
   138  
   139  func requestRetry(ctx pipeline.Context, reason string, err error) {
   140  	ctx.RetryProcessing(err)
   141  	ctx.SetCondition(notCollectionReadyCond(reason, err))
   142  }
   143  
   144  func notCollectionReadyCond(reason string, err error) *metav1.Condition {
   145  	return apis.Conditions().NotCollectionReady().Reason(reason).Msg(err.Error()).Build()
   146  }
   147  
   148  func makeBindingDefinition(key string, value string, ctx pipeline.Context) (binding.Definition, error) {
   149  	return binding.NewDefinitionBuilder(key,
   150  		value,
   151  		func(namespace string, name string) (*unstructured.Unstructured, error) {
   152  			return ctx.ReadConfigMap(namespace, name)
   153  		},
   154  		func(namespace string, name string) (*unstructured.Unstructured, error) {
   155  			return ctx.ReadSecret(namespace, name)
   156  		}).Build()
   157  }
   158  

View as plain text