Files
odo/pkg/service/service_binding_builder.go
Armel Soro 06550eb233 Add additional checks to golangci-lint (#5567) (#5687)
* Document golangci-lint configuration options

* Do not limit the number of issues reported per linter

* Add check preventing the use of the deprecated 'github.com/pkg/errors' package

* Upgrade golangci-lint to 1.37.0, so we can use 'revive' Linter

- revive is a drop-in replacement for the deprecated golint Linter
- revive will allow to check for error strings

Note: We are purposely not using the latest golangci-lint (1.45.0)
but the minimum version from which revive is available.
This is because the latest 1.45.0 reports additional linting issues
(from govet and staticcheck for example). And fixing those side errors
is outside of the scope of this issue.
Also some of those issues are already fixed in #5497 (update to Go 1.17).

* Configure revive to check for error strings

More rules can be added later on if needed

* Fix issues reported by revive's error-strings rule

Some rules are purposely ignored when the
error messages represent top-level errors that are
displayed to be displayed as is to end users

* Fix more error-strings issues

For some reason, those were not reported by revive's error-strings rule,
but only by GoLand inspection tool.

* Fix missing `revive:disable:error-strings` comment directive

Also replace "fmt.Errorf" by "errors.New" when the error message is static
2022-04-25 07:32:20 -04:00

158 lines
5.8 KiB
Go

package service
// This file is a fork of github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/collect/impl.go
// This file was created to deal with the permission issue that comes up when user does not access to clusterwide resources,
// and they want to use odo link without the Service Binding Operator.
// The only difference between the original and forked implementation is that we check if the user is forbidden from accessing
// CRD, and if they are, then we simply ignore checking CRD while linking.
// For more information, see issue: https://github.com/redhat-developer/odo/issues/4965
// 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,
// we can go back to using the builder.DefaultBuilder instead of the OdoDefaultBuilder in getPipeline.
import (
"fmt"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/service-binding-operator/apis"
"github.com/redhat-developer/service-binding-operator/pkg/binding"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/builder"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/collect"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/mapping"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/naming"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/project"
"github.com/redhat-developer/service-binding-operator/pkg/util"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
var defaultFlow = []pipeline.Handler{
pipeline.HandlerFunc(project.Unbind),
pipeline.HandlerFunc(collect.PreFlight),
pipeline.HandlerFunc(ProvisionedService),
pipeline.HandlerFunc(collect.DirectSecretReference),
pipeline.HandlerFunc(BindingDefinitions),
pipeline.HandlerFunc(collect.BindingItems),
pipeline.HandlerFunc(collect.OwnedResources),
pipeline.HandlerFunc(mapping.Handle),
pipeline.HandlerFunc(naming.Handle),
pipeline.HandlerFunc(project.PreFlightCheck()),
pipeline.HandlerFunc(project.InjectSecretRef),
pipeline.HandlerFunc(project.BindingsAsEnv),
pipeline.HandlerFunc(project.BindingsAsFiles),
pipeline.HandlerFunc(project.PostFlightCheck),
}
var OdoDefaultBuilder = builder.Builder().WithHandlers(defaultFlow...)
func ProvisionedService(ctx pipeline.Context) {
services, _ := ctx.Services()
for _, service := range services {
res := service.Resource()
secretName, found, err := unstructured.NestedString(res.Object, "status", "binding", "name")
if err != nil {
requestRetry(ctx, collect.ErrorReadingBindingReason, err)
return
}
if found {
if secretName != "" {
secret, err := ctx.ReadSecret(res.GetNamespace(), secretName)
if err != nil {
requestRetry(ctx, collect.ErrorReadingSecret, err)
return
}
ctx.AddBindings(&pipeline.SecretBackedBindings{Service: service, Secret: secret})
}
} else {
crd, err := service.CustomResourceDefinition()
if err != nil {
// If the user does not have permission to access CRD, ignore
if !kerrors.IsForbidden(err) {
requestRetry(ctx, collect.ErrorReadingCRD, err)
return
} else {
log.Warning("Skipping the check for CRD, user does not have access")
continue
}
}
if crd == nil {
continue
}
v, ok := crd.Resource().GetAnnotations()[binding.ProvisionedServiceAnnotationKey]
if ok && v == "true" {
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()))
return
}
}
}
}
func BindingDefinitions(ctx pipeline.Context) {
services, _ := ctx.Services()
for _, service := range services {
anns := make(map[string]string)
crd, err := service.CustomResourceDefinition()
if err != nil {
// If the user does not have permission to access CRD, ignore
if !kerrors.IsForbidden(err) {
requestRetry(ctx, collect.ErrorReadingCRD, err)
return
} else {
log.Warning("Skipping the check for CRD, user does not have access")
//continue
}
}
if crd != nil {
descr, err := crd.Descriptor()
if err != nil {
requestRetry(ctx, collect.ErrorReadingDescriptorReason, err)
return
}
if descr != nil {
util.MergeMaps(anns, descr.BindingAnnotations())
}
util.MergeMaps(anns, crd.Resource().GetAnnotations())
}
util.MergeMaps(anns, service.Resource().GetAnnotations())
for k, v := range anns {
definition, err := makeBindingDefinition(k, v, ctx)
if err != nil {
condition := notCollectionReadyCond(collect.InvalidAnnotation, fmt.Errorf("failed to create binding definition from \"%v: %v\": %v", k, v, err))
ctx.SetCondition(condition)
ctx.Error(err)
ctx.StopProcessing()
}
if definition != nil {
service.AddBindingDef(definition)
}
}
}
}
func requestRetry(ctx pipeline.Context, reason string, err error) {
ctx.RetryProcessing(err)
ctx.SetCondition(notCollectionReadyCond(reason, err))
}
func notCollectionReadyCond(reason string, err error) *metav1.Condition {
return apis.Conditions().NotCollectionReady().Reason(reason).Msg(err.Error()).Build()
}
func makeBindingDefinition(key string, value string, ctx pipeline.Context) (binding.Definition, error) {
return binding.NewDefinitionBuilder(key,
value,
func(namespace string, name string) (*unstructured.Unstructured, error) {
return ctx.ReadConfigMap(namespace, name)
},
func(namespace string, name string) (*unstructured.Unstructured, error) {
return ctx.ReadSecret(namespace, name)
}).Build()
}