diff --git a/go.mod b/go.mod index e6ce900aa6..281469c1e4 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/openshift/api v0.0.0-20200331152225-585af27e34fd github.com/operator-framework/api v0.10.1 github.com/operator-framework/operator-lifecycle-manager v0.0.0-00010101000000-000000000000 - github.com/operator-framework/operator-registry v1.13.6 + github.com/operator-framework/operator-registry v1.17.5 github.com/spf13/cobra v1.1.3 github.com/stretchr/testify v1.7.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 diff --git a/manifests/0000_50_olm_02-services.yaml b/manifests/0000_50_olm_02-services.yaml index d82d2b8a87..a57315d5bd 100644 --- a/manifests/0000_50_olm_02-services.yaml +++ b/manifests/0000_50_olm_02-services.yaml @@ -14,9 +14,9 @@ spec: type: ClusterIP ports: - name: https-metrics - port: 8081 + port: 8443 protocol: TCP - targetPort: metrics + targetPort: 8443 selector: app: olm-operator --- @@ -36,8 +36,8 @@ spec: type: ClusterIP ports: - name: https-metrics - port: 8081 + port: 8443 protocol: TCP - targetPort: metrics + targetPort: 8443 selector: app: catalog-operator diff --git a/manifests/0000_50_olm_07-olm-operator.deployment.ibm-cloud-managed.yaml b/manifests/0000_50_olm_07-olm-operator.deployment.ibm-cloud-managed.yaml index 57f5b9d2bf..1a62fcd2d5 100644 --- a/manifests/0000_50_olm_07-olm-operator.deployment.ibm-cloud-managed.yaml +++ b/manifests/0000_50_olm_07-olm-operator.deployment.ibm-cloud-managed.yaml @@ -22,8 +22,27 @@ spec: target.workload.openshift.io/management: '{"effect": "PreferredDuringScheduling"}' spec: serviceAccountName: olm-operator-serviceaccount + volumes: + - name: srv-cert + secret: + secretName: olm-operator-serving-cert + - name: profile-collector-cert + secret: + secretName: olm-operator-serving-cert + - name: serving-cert + secret: + secretName: olm-operator-serving-cert containers: - name: olm-operator + volumeMounts: + - name: srv-cert + mountPath: "/srv-cert" + readOnly: true + - name: profile-collector-cert + mountPath: "/profile-collector-cert" + readOnly: true + - mountPath: /var/run/secrets/serving-cert + name: serving-cert command: - /bin/olm args: @@ -34,24 +53,26 @@ spec: - --writePackageServerStatusName - operator-lifecycle-manager-packageserver - --tls-cert - - /var/run/secrets/serving-cert/tls.crt + - /srv-cert/tls.crt - --tls-key - - /var/run/secrets/serving-cert/tls.key + - /srv-cert/tls.key + - --client-ca + - /profile-collector-cert/tls.crt image: quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 imagePullPolicy: IfNotPresent ports: - - containerPort: 8080 - - containerPort: 8081 + - containerPort: 8443 name: metrics - protocol: TCP livenessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS readinessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS terminationMessagePolicy: FallbackToLogsOnError env: - name: OPERATOR_NAMESPACE @@ -66,9 +87,6 @@ spec: requests: cpu: 10m memory: 160Mi - volumeMounts: - - mountPath: /var/run/secrets/serving-cert - name: serving-cert nodeSelector: kubernetes.io/os: linux tolerations: @@ -84,7 +102,3 @@ spec: operator: Exists tolerationSeconds: 120 priorityClassName: system-cluster-critical - volumes: - - name: serving-cert - secret: - secretName: olm-operator-serving-cert diff --git a/manifests/0000_50_olm_07-olm-operator.deployment.yaml b/manifests/0000_50_olm_07-olm-operator.deployment.yaml index e2f013a6c3..1a23661fae 100644 --- a/manifests/0000_50_olm_07-olm-operator.deployment.yaml +++ b/manifests/0000_50_olm_07-olm-operator.deployment.yaml @@ -23,8 +23,27 @@ spec: target.workload.openshift.io/management: '{"effect": "PreferredDuringScheduling"}' spec: serviceAccountName: olm-operator-serviceaccount + volumes: + - name: srv-cert + secret: + secretName: olm-operator-serving-cert + - name: profile-collector-cert + secret: + secretName: olm-operator-serving-cert + - name: serving-cert + secret: + secretName: olm-operator-serving-cert containers: - name: olm-operator + volumeMounts: + - name: srv-cert + mountPath: "/srv-cert" + readOnly: true + - name: profile-collector-cert + mountPath: "/profile-collector-cert" + readOnly: true + - mountPath: /var/run/secrets/serving-cert + name: serving-cert command: - /bin/olm args: @@ -35,24 +54,26 @@ spec: - --writePackageServerStatusName - operator-lifecycle-manager-packageserver - --tls-cert - - /var/run/secrets/serving-cert/tls.crt + - /srv-cert/tls.crt - --tls-key - - /var/run/secrets/serving-cert/tls.key + - /srv-cert/tls.key + - --client-ca + - /profile-collector-cert/tls.crt image: quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 imagePullPolicy: IfNotPresent ports: - - containerPort: 8080 - - containerPort: 8081 + - containerPort: 8443 name: metrics - protocol: TCP livenessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS readinessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS terminationMessagePolicy: FallbackToLogsOnError env: - name: OPERATOR_NAMESPACE @@ -67,9 +88,6 @@ spec: requests: cpu: 10m memory: 160Mi - volumeMounts: - - mountPath: /var/run/secrets/serving-cert - name: serving-cert nodeSelector: kubernetes.io/os: linux node-role.kubernetes.io/master: "" @@ -86,7 +104,3 @@ spec: operator: Exists tolerationSeconds: 120 priorityClassName: system-cluster-critical - volumes: - - name: serving-cert - secret: - secretName: olm-operator-serving-cert diff --git a/manifests/0000_50_olm_08-catalog-operator.deployment.ibm-cloud-managed.yaml b/manifests/0000_50_olm_08-catalog-operator.deployment.ibm-cloud-managed.yaml index eb80db2297..f5da0de00e 100644 --- a/manifests/0000_50_olm_08-catalog-operator.deployment.ibm-cloud-managed.yaml +++ b/manifests/0000_50_olm_08-catalog-operator.deployment.ibm-cloud-managed.yaml @@ -22,37 +22,59 @@ spec: target.workload.openshift.io/management: '{"effect": "PreferredDuringScheduling"}' spec: serviceAccountName: olm-operator-serviceaccount + volumes: + - name: srv-cert + secret: + secretName: catalog-operator-serving-cert + - name: profile-collector-cert + secret: + secretName: catalog-operator-serving-cert + - name: serving-cert + secret: + secretName: catalog-operator-serving-cert containers: - name: catalog-operator + volumeMounts: + - name: srv-cert + mountPath: "/srv-cert" + readOnly: true + - name: profile-collector-cert + mountPath: "/profile-collector-cert" + readOnly: true + - mountPath: /var/run/secrets/serving-cert + name: serving-cert command: - /bin/catalog args: - '-namespace' - openshift-marketplace - -configmapServerImage=quay.io/operator-framework/configmap-operator-registry:latest + - -opmImage=quay.io/operator-framework/configmap-operator-registry:latest - -util-image - quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 - -writeStatusName - operator-lifecycle-manager-catalog - - -tls-cert - - /var/run/secrets/serving-cert/tls.crt - - -tls-key - - /var/run/secrets/serving-cert/tls.key + - --tls-cert + - /srv-cert/tls.crt + - --tls-key + - /srv-cert/tls.key + - --client-ca + - /profile-collector-cert/tls.crt image: quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 imagePullPolicy: IfNotPresent ports: - - containerPort: 8080 - - containerPort: 8081 + - containerPort: 8443 name: metrics - protocol: TCP livenessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS readinessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS terminationMessagePolicy: FallbackToLogsOnError resources: requests: @@ -61,9 +83,6 @@ spec: env: - name: RELEASE_VERSION value: "0.0.1-snapshot" - volumeMounts: - - mountPath: /var/run/secrets/serving-cert - name: serving-cert nodeSelector: kubernetes.io/os: linux tolerations: @@ -79,7 +98,3 @@ spec: operator: Exists tolerationSeconds: 120 priorityClassName: system-cluster-critical - volumes: - - name: serving-cert - secret: - secretName: catalog-operator-serving-cert diff --git a/manifests/0000_50_olm_08-catalog-operator.deployment.yaml b/manifests/0000_50_olm_08-catalog-operator.deployment.yaml index 901bf0df8f..fde9d56591 100644 --- a/manifests/0000_50_olm_08-catalog-operator.deployment.yaml +++ b/manifests/0000_50_olm_08-catalog-operator.deployment.yaml @@ -23,37 +23,59 @@ spec: target.workload.openshift.io/management: '{"effect": "PreferredDuringScheduling"}' spec: serviceAccountName: olm-operator-serviceaccount + volumes: + - name: srv-cert + secret: + secretName: catalog-operator-serving-cert + - name: profile-collector-cert + secret: + secretName: catalog-operator-serving-cert + - name: serving-cert + secret: + secretName: catalog-operator-serving-cert containers: - name: catalog-operator + volumeMounts: + - name: srv-cert + mountPath: "/srv-cert" + readOnly: true + - name: profile-collector-cert + mountPath: "/profile-collector-cert" + readOnly: true + - mountPath: /var/run/secrets/serving-cert + name: serving-cert command: - /bin/catalog args: - '-namespace' - openshift-marketplace - -configmapServerImage=quay.io/operator-framework/configmap-operator-registry:latest + - -opmImage=quay.io/operator-framework/configmap-operator-registry:latest - -util-image - quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 - -writeStatusName - operator-lifecycle-manager-catalog - - -tls-cert - - /var/run/secrets/serving-cert/tls.crt - - -tls-key - - /var/run/secrets/serving-cert/tls.key + - --tls-cert + - /srv-cert/tls.crt + - --tls-key + - /srv-cert/tls.key + - --client-ca + - /profile-collector-cert/tls.crt image: quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 imagePullPolicy: IfNotPresent ports: - - containerPort: 8080 - - containerPort: 8081 + - containerPort: 8443 name: metrics - protocol: TCP livenessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS readinessProbe: httpGet: path: /healthz - port: 8080 + port: 8443 + scheme: HTTPS terminationMessagePolicy: FallbackToLogsOnError resources: requests: @@ -62,9 +84,6 @@ spec: env: - name: RELEASE_VERSION value: "0.0.1-snapshot" - volumeMounts: - - mountPath: /var/run/secrets/serving-cert - name: serving-cert nodeSelector: kubernetes.io/os: linux node-role.kubernetes.io/master: "" @@ -81,7 +100,3 @@ spec: operator: Exists tolerationSeconds: 120 priorityClassName: system-cluster-critical - volumes: - - name: serving-cert - secret: - secretName: catalog-operator-serving-cert diff --git a/staging/api/pkg/operators/v1alpha1/subscription_types.go b/staging/api/pkg/operators/v1alpha1/subscription_types.go index 7eaae1e68c..e048d4988c 100644 --- a/staging/api/pkg/operators/v1alpha1/subscription_types.go +++ b/staging/api/pkg/operators/v1alpha1/subscription_types.go @@ -102,6 +102,9 @@ const ( // SubscriptionInstallPlanFailed indicates that the installation of a Subscription's InstallPlan has failed. SubscriptionInstallPlanFailed SubscriptionConditionType = "InstallPlanFailed" + + // SubscriptionResolutionFailed indicates that the dependency resolution in the namespace in which the subscription is created has failed + SubscriptionResolutionFailed SubscriptionConditionType = "ResolutionFailed" ) const ( diff --git a/staging/operator-lifecycle-manager/.github/workflows/test-scripts.yml b/staging/operator-lifecycle-manager/.github/workflows/test-scripts.yml index 5ec1f7a31e..1c18d7a9c9 100644 --- a/staging/operator-lifecycle-manager/.github/workflows/test-scripts.yml +++ b/staging/operator-lifecycle-manager/.github/workflows/test-scripts.yml @@ -23,6 +23,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: '~1.16' - run: | sudo apt-get install conntrack curl -sLo minikube "$(curl -sL https://api.github.com/repos/kubernetes/minikube/releases/latest | jq -r '[.assets[] | select(.name == "minikube-linux-amd64")] | first | .browser_download_url')" @@ -34,6 +37,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: '~1.16' - run: | curl -sLo kind "$(curl -sL https://api.github.com/repos/kubernetes-sigs/kind/releases/latest | jq -r '[.assets[] | select(.name == "kind-linux-amd64")] | first | .browser_download_url')" chmod +x kind diff --git a/staging/operator-lifecycle-manager/cmd/catalog/main.go b/staging/operator-lifecycle-manager/cmd/catalog/main.go index 4f945eb008..f8128a4c2c 100644 --- a/staging/operator-lifecycle-manager/cmd/catalog/main.go +++ b/staging/operator-lifecycle-manager/cmd/catalog/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "crypto/tls" "flag" "fmt" "net/http" @@ -10,7 +9,6 @@ import ( "time" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" - "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" utilclock "k8s.io/apimachinery/pkg/util/clock" k8sscheme "k8s.io/client-go/kubernetes/scheme" @@ -18,10 +16,9 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals" "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version" @@ -31,7 +28,8 @@ const ( catalogNamespaceEnvVarName = "GLOBAL_CATALOG_NAMESPACE" defaultWakeupInterval = 15 * time.Minute defaultCatalogNamespace = "openshift-operator-lifecycle-manager" - defaultConfigMapServerImage = "quay.io/operatorframework/configmap-operator-registry:latest" + defaultConfigMapServerImage = "quay.io/operator-framework/configmap-operator-registry:latest" + defaultOPMImage = "quay.io/operator-framework/upstream-opm-builder:latest" defaultUtilImage = "quay.io/operator-framework/olm:latest" defaultOperatorName = "" ) @@ -50,6 +48,9 @@ var ( configmapServerImage = flag.String( "configmapServerImage", defaultConfigMapServerImage, "the image to use for serving the operator registry api for a configmap") + opmImage = flag.String( + "opmImage", defaultOPMImage, "the image to use for unpacking bundle content with opm") + utilImage = flag.String( "util-image", defaultUtilImage, "an image containing custom olm utilities") @@ -67,8 +68,9 @@ var ( tlsCertPath = flag.String( "tls-cert", "", "Path to use for certificate key (requires tls-key)") - profiling = flag.Bool( - "profiling", false, "serve profiling data (on port 8080)") + profiling = flag.Bool("profiling", false, "deprecated") + + clientCAPath = flag.String("client-ca", "", "path to watch for client ca bundle") installPlanTimeout = flag.Duration("install-plan-retry-timeout", 1*time.Minute, "time since first attempt at which plan execution errors are considered fatal") bundleUnpackTimeout = flag.Duration("bundle-unpack-timeout", 10*time.Minute, "The time limit for bundle unpacking, after which InstallPlan execution is considered to have failed. 0 is considered as having no timeout.") @@ -106,59 +108,16 @@ func main() { *catalogNamespace = catalogNamespaceEnvVarValue } - var useTLS bool - if *tlsCertPath != "" && *tlsKeyPath == "" || *tlsCertPath == "" && *tlsKeyPath != "" { - logger.Warn("both --tls-key and --tls-crt must be provided for TLS to be enabled, falling back to non-https") - } else if *tlsCertPath == "" && *tlsKeyPath == "" { - logger.Info("TLS keys not set, using non-https for metrics") - } else { - logger.Info("TLS keys set, using https for metrics") - useTLS = true - } - - // Serve a health check. - healthMux := http.NewServeMux() - healthMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - // Serve profiling if enabled - if *profiling { - logger.Infof("profiling enabled") - profile.RegisterHandlers(healthMux) + listenAndServe, err := server.GetListenAndServeFunc(logger, tlsCertPath, tlsKeyPath, clientCAPath) + if err != nil { + logger.Fatal("Error setting up health/metric/pprof service: %v", err) } - go http.ListenAndServe(":8080", healthMux) - - metricsMux := http.NewServeMux() - metricsMux.Handle("/metrics", promhttp.Handler()) - if useTLS { - tlsGetCertFn, err := filemonitor.OLMGetCertRotationFn(logger, *tlsCertPath, *tlsKeyPath) - if err != nil { - logger.Errorf("Certificate monitoring for metrics (https) failed: %v", err) + go func() { + if err := listenAndServe(); err != nil && err != http.ErrServerClosed { + logger.Error(err) } - - go func() { - httpsServer := &http.Server{ - Addr: ":8081", - Handler: metricsMux, - TLSConfig: &tls.Config{ - GetCertificate: tlsGetCertFn, - }, - } - err := httpsServer.ListenAndServeTLS("", "") - if err != nil { - logger.Errorf("Metrics (https) serving failed: %v", err) - } - }() - } else { - go func() { - err := http.ListenAndServe(":8081", metricsMux) - if err != nil { - logger.Errorf("Metrics (http) serving failed: %v", err) - } - }() - } + }() // create a config client for operator status config, err := clientcmd.BuildConfigFromFlags("", *kubeConfigPath) @@ -176,7 +135,7 @@ func main() { } // Create a new instance of the operator. - op, err := catalog.NewOperator(ctx, *kubeConfigPath, utilclock.RealClock{}, logger, *wakeupInterval, *configmapServerImage, *utilImage, *catalogNamespace, k8sscheme.Scheme, *installPlanTimeout, *bundleUnpackTimeout) + op, err := catalog.NewOperator(ctx, *kubeConfigPath, utilclock.RealClock{}, logger, *wakeupInterval, *configmapServerImage, *opmImage, *utilImage, *catalogNamespace, k8sscheme.Scheme, *installPlanTimeout, *bundleUnpackTimeout) if err != nil { log.Panicf("error configuring operator: %s", err.Error()) } diff --git a/staging/operator-lifecycle-manager/cmd/olm/main.go b/staging/operator-lifecycle-manager/cmd/olm/main.go index 087ebb74a8..f9bec9f689 100644 --- a/staging/operator-lifecycle-manager/cmd/olm/main.go +++ b/staging/operator-lifecycle-manager/cmd/olm/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "crypto/tls" "flag" "fmt" "net/http" @@ -12,7 +11,6 @@ import ( configclientset "github.com/openshift/client-go/config/clientset/versioned" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" - "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" "github.com/spf13/pflag" v1 "k8s.io/api/core/v1" @@ -23,11 +21,10 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/openshift" "github.com/operator-framework/operator-lifecycle-manager/pkg/feature" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals" "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version" @@ -66,8 +63,9 @@ var ( tlsCertPath = pflag.String( "tls-cert", "", "Path to use for certificate key (requires tls-key)") - profiling = pflag.Bool( - "profiling", false, "serve profiling data (on port 8080)") + profiling = pflag.Bool("profiling", false, "deprecated") + + clientCAPath = pflag.String("client-ca", "", "path to watch for client ca bundle") namespace = pflag.String( "namespace", "", "namespace where cleanup runs") @@ -120,65 +118,17 @@ func main() { } logger.Infof("log level %s", logger.Level) - var useTLS bool - if *tlsCertPath != "" && *tlsKeyPath == "" || *tlsCertPath == "" && *tlsKeyPath != "" { - logger.Warn("both --tls-key and --tls-crt must be provided for TLS to be enabled, falling back to non-https") - } else if *tlsCertPath == "" && *tlsKeyPath == "" { - logger.Info("TLS keys not set, using non-https for metrics") - } else { - logger.Info("TLS keys set, using https for metrics") - useTLS = true - } - - // Serve a health check. - healthMux := http.NewServeMux() - healthMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - // Serve profiling if enabled - if *profiling { - logger.Infof("profiling enabled") - profile.RegisterHandlers(healthMux) + listenAndServe, err := server.GetListenAndServeFunc(logger, tlsCertPath, tlsKeyPath, clientCAPath) + if err != nil { + logger.Fatal("Error setting up health/metric/pprof service: %v", err) } go func() { - err := http.ListenAndServe(":8080", healthMux) - if err != nil { - logger.Errorf("Health serving failed: %v", err) + if err := listenAndServe(); err != nil && err != http.ErrServerClosed { + logger.Error(err) } }() - metricsMux := http.NewServeMux() - metricsMux.Handle("/metrics", promhttp.Handler()) - if useTLS { - tlsGetCertFn, err := filemonitor.OLMGetCertRotationFn(logger, *tlsCertPath, *tlsKeyPath) - if err != nil { - logger.Errorf("Certificate monitoring for metrics (https) failed: %v", err) - } - - go func() { - httpsServer := &http.Server{ - Addr: ":8081", - Handler: metricsMux, - TLSConfig: &tls.Config{ - GetCertificate: tlsGetCertFn, - }, - } - err := httpsServer.ListenAndServeTLS("", "") - if err != nil { - logger.Errorf("Metrics (https) serving failed: %v", err) - } - }() - } else { - go func() { - err := http.ListenAndServe(":8081", metricsMux) - if err != nil { - logger.Errorf("Metrics (http) serving failed: %v", err) - } - }() - } - mgr, err := Manager(ctx, *debug) if err != nil { logger.WithError(err).Fatalf("error configuring controller manager") diff --git a/staging/operator-lifecycle-manager/cmd/olm/manager.go b/staging/operator-lifecycle-manager/cmd/olm/manager.go index f01b6c77c6..5e8e33ab73 100644 --- a/staging/operator-lifecycle-manager/cmd/olm/manager.go +++ b/staging/operator-lifecycle-manager/cmd/olm/manager.go @@ -2,31 +2,62 @@ package main import ( "context" - "k8s.io/apimachinery/pkg/labels" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/selection" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/log/zap" + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators" "github.com/operator-framework/operator-lifecycle-manager/pkg/feature" ) +var ( + copiedLabelDoesNotExist labels.Selector +) + +func init() { + requirement, err := labels.NewRequirement(operatorsv1alpha1.CopiedLabelKey, selection.DoesNotExist, nil) + if err != nil { + panic(err) + } + copiedLabelDoesNotExist = labels.NewSelector().Add(*requirement) +} + func Manager(ctx context.Context, debug bool) (ctrl.Manager, error) { ctrl.SetLogger(zap.New(zap.UseDevMode(debug))) setupLog := ctrl.Log.WithName("setup").V(1) - // Setup a Manager + scheme := runtime.NewScheme() + if err := metav1.AddMetaToScheme(scheme); err != nil { + return nil, err + } + if err := operators.AddToScheme(scheme); err != nil { + // ctrl.NewManager needs the Scheme to be populated + // up-front so that the NewCache implementation we + // provide can configure custom cache behavior on + // non-core types. + return nil, err + } + setupLog.Info("configuring manager") mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, MetricsBindAddress: "0", // TODO(njhale): Enable metrics on non-conflicting port (not 8080) NewCache: cache.BuilderWithOptions(cache.Options{ SelectorsByObject: cache.SelectorsByObject{ &corev1.Secret{}: { Label: labels.SelectorFromValidatedSet(map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}), }, + &operatorsv1alpha1.ClusterServiceVersion{}: { + Label: copiedLabelDoesNotExist, + }, }, }), }) diff --git a/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_02-services.yaml b/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_02-services.yaml index 923cdaccec..51fb8df0e3 100644 --- a/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_02-services.yaml +++ b/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_02-services.yaml @@ -12,9 +12,9 @@ spec: type: ClusterIP ports: - name: https-metrics - port: 8081 + port: {{ .Values.olm.service.externalPort }} protocol: TCP - targetPort: metrics + targetPort: {{ .Values.olm.service.internalPort }} selector: app: olm-operator --- @@ -31,9 +31,9 @@ spec: type: ClusterIP ports: - name: https-metrics - port: 8081 + port: {{ .Values.catalog.service.externalPort }} protocol: TCP - targetPort: metrics + targetPort: {{ .Values.catalog.service.internalPort }} selector: app: catalog-operator {{ end }} diff --git a/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml b/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml index 7225c10ae8..b71fbebd0d 100644 --- a/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml +++ b/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml @@ -18,8 +18,34 @@ spec: app: olm-operator spec: serviceAccountName: olm-operator-serviceaccount + {{- if or .Values.olm.tlsSecret .Values.olm.clientCASecret }} + volumes: + {{- end }} + {{- if .Values.olm.tlsSecret }} + - name: srv-cert + secret: + secretName: {{ .Values.olm.tlsSecret }} + {{- end }} + {{- if .Values.olm.clientCASecret }} + - name: profile-collector-cert + secret: + secretName: {{ .Values.olm.clientCASecret }} + {{- end }} containers: - name: olm-operator + {{- if or .Values.olm.tlsSecret .Values.olm.clientCASecret }} + volumeMounts: + {{- end }} + {{- if .Values.olm.tlsSecret }} + - name: srv-cert + mountPath: "/srv-cert" + readOnly: true + {{- end }} + {{- if .Values.olm.clientCASecret }} + - name: profile-collector-cert + mountPath: "/profile-collector-cert" + readOnly: true + {{- end }} command: - /bin/olm args: @@ -43,29 +69,31 @@ spec: - --writePackageServerStatusName - {{ .Values.writePackageServerStatusName }} {{- end }} - {{- if .Values.olm.tlsCertPath }} + {{- if .Values.olm.tlsSecret }} - --tls-cert - - {{ .Values.olm.tlsCertPath }} - {{- end }} - {{- if .Values.olm.tlsKeyPath }} + - /srv-cert/tls.crt - --tls-key - - {{ .Values.olm.tlsKeyPath }} + - /srv-cert/tls.key + {{- end }} + {{- if .Values.olm.clientCASecret }} + - --client-ca + - /profile-collector-cert/tls.crt {{- end }} image: {{ .Values.olm.image.ref }} imagePullPolicy: {{ .Values.olm.image.pullPolicy }} ports: - containerPort: {{ .Values.olm.service.internalPort }} - - containerPort: 8081 name: metrics - protocol: TCP livenessProbe: httpGet: path: /healthz port: {{ .Values.olm.service.internalPort }} + scheme: {{ if .Values.olm.tlsSecret }}HTTPS{{ else }}HTTP{{end}} readinessProbe: httpGet: path: /healthz port: {{ .Values.olm.service.internalPort }} + scheme: {{ if .Values.olm.tlsSecret }}HTTPS{{ else }}HTTP{{end}} terminationMessagePolicy: FallbackToLogsOnError env: - name: OPERATOR_NAMESPACE diff --git a/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_08-catalog-operator.deployment.yaml b/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_08-catalog-operator.deployment.yaml index ab1a36166b..e9210d69ba 100644 --- a/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_08-catalog-operator.deployment.yaml +++ b/staging/operator-lifecycle-manager/deploy/chart/templates/0000_50_olm_08-catalog-operator.deployment.yaml @@ -18,8 +18,34 @@ spec: app: catalog-operator spec: serviceAccountName: olm-operator-serviceaccount + {{- if or .Values.catalog.tlsSecret .Values.catalog.clientCASecret }} + volumes: + {{- end }} + {{- if .Values.catalog.tlsSecret }} + - name: srv-cert + secret: + secretName: {{ .Values.catalog.tlsSecret }} + {{- end }} + {{- if .Values.catalog.clientCASecret }} + - name: profile-collector-cert + secret: + secretName: {{ .Values.catalog.clientCASecret }} + {{- end }} containers: - name: catalog-operator + {{- if or .Values.catalog.tlsSecret .Values.catalog.clientCASecret }} + volumeMounts: + {{- end }} + {{- if .Values.catalog.tlsSecret }} + - name: srv-cert + mountPath: "/srv-cert" + readOnly: true + {{- end }} + {{- if .Values.catalog.clientCASecret }} + - name: profile-collector-cert + mountPath: "/profile-collector-cert" + readOnly: true + {{- end }} command: - /bin/catalog args: @@ -31,35 +57,40 @@ spec: {{- if .Values.catalog.commandArgs }} - {{ .Values.catalog.commandArgs }} {{- end }} + {{- if .Values.catalog.opmImageArgs }} + - {{ .Values.catalog.opmImageArgs }} + {{- end }} - -util-image - {{ .Values.catalog.image.ref }} {{- if .Values.writeStatusNameCatalog }} - -writeStatusName - {{ .Values.writeStatusNameCatalog }} {{- end }} - {{- if .Values.olm.tlsCertPath }} - - -tls-cert - - {{ .Values.olm.tlsCertPath }} + {{- if .Values.catalog.tlsSecret }} + - --tls-cert + - /srv-cert/tls.crt + - --tls-key + - /srv-cert/tls.key {{- end }} - {{- if .Values.olm.tlsKeyPath }} - - -tls-key - - {{ .Values.olm.tlsKeyPath }} + {{- if .Values.catalog.clientCASecret }} + - --client-ca + - /profile-collector-cert/tls.crt {{- end }} image: {{ .Values.catalog.image.ref }} imagePullPolicy: {{ .Values.catalog.image.pullPolicy }} ports: - - containerPort: {{ .Values.catalog.service.internalPort }} - - containerPort: 8081 + - containerPort: {{ .Values.olm.service.internalPort }} name: metrics - protocol: TCP livenessProbe: httpGet: path: /healthz port: {{ .Values.catalog.service.internalPort }} + scheme: {{ if .Values.catalog.tlsSecret }}HTTPS{{ else }}HTTP{{end}} readinessProbe: httpGet: path: /healthz port: {{ .Values.catalog.service.internalPort }} + scheme: {{ if .Values.catalog.tlsSecret }}HTTPS{{ else }}HTTP{{end}} terminationMessagePolicy: FallbackToLogsOnError {{- if .Values.catalog.resources }} resources: diff --git a/staging/operator-lifecycle-manager/deploy/chart/values.yaml b/staging/operator-lifecycle-manager/deploy/chart/values.yaml index f0b4b2d75d..4f7f684b9a 100644 --- a/staging/operator-lifecycle-manager/deploy/chart/values.yaml +++ b/staging/operator-lifecycle-manager/deploy/chart/values.yaml @@ -14,6 +14,9 @@ olm: pullPolicy: Always service: internalPort: 8080 + externalPort: metrics + # tlsSecret: olm-operator-serving-cert + # clientCASecret: pprof-serving-cert nodeSelector: kubernetes.io/os: linux resources: @@ -29,6 +32,9 @@ catalog: pullPolicy: Always service: internalPort: 8080 + externalPort: metrics + # tlsSecret: catalog-operator-serving-cert + # clientCASecret: pprof-serving-cert nodeSelector: kubernetes.io/os: linux resources: diff --git a/staging/operator-lifecycle-manager/go.mod b/staging/operator-lifecycle-manager/go.mod index e760883893..c03eb7b657 100644 --- a/staging/operator-lifecycle-manager/go.mod +++ b/staging/operator-lifecycle-manager/go.mod @@ -25,7 +25,7 @@ require ( github.com/openshift/api v0.0.0-20200331152225-585af27e34fd github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0 github.com/operator-framework/api v0.10.1 - github.com/operator-framework/operator-registry v1.13.6 + github.com/operator-framework/operator-registry v1.17.5 github.com/otiai10/copy v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 @@ -60,11 +60,9 @@ replace ( // controller runtime github.com/openshift/api => github.com/openshift/api v0.0.0-20200331152225-585af27e34fd // release-4.5 github.com/openshift/client-go => github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0 // release-4.5 - - // pinned because latest etcd does not yet work with the latest grpc version (1.30.0) - go.etcd.io/etcd => go.etcd.io/etcd v0.5.0-alpha.5.0.20200520232829-54ba9589114f - google.golang.org/grpc => google.golang.org/grpc v1.27.0 - google.golang.org/grpc/examples => google.golang.org/grpc/examples v0.0.0-20200709232328-d8193ee9cc3e + // Patch for a race condition involving metadata-only + // informers until it can be resolved upstream: + sigs.k8s.io/controller-runtime v0.9.2 => github.com/benluddy/controller-runtime v0.9.3-0.20210720171926-9bcb99bd9bd3 // pinned because no tag supports 1.18 yet sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 diff --git a/staging/operator-lifecycle-manager/go.sum b/staging/operator-lifecycle-manager/go.sum index 685a6f6b2c..f0a51b880f 100644 --- a/staging/operator-lifecycle-manager/go.sum +++ b/staging/operator-lifecycle-manager/go.sum @@ -1,4 +1,5 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -76,6 +77,7 @@ github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= @@ -102,6 +104,7 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -110,7 +113,7 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -118,6 +121,7 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= @@ -129,6 +133,8 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/benluddy/controller-runtime v0.9.3-0.20210720171926-9bcb99bd9bd3 h1:XnmeUOSdoo764dm67j2pKUNU7MqzB6a303w4ML2EhB4= +github.com/benluddy/controller-runtime v0.9.3-0.20210720171926-9bcb99bd9bd3/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -169,6 +175,9 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -290,7 +299,13 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= @@ -310,6 +325,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -327,12 +343,21 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-bindata/go-bindata/v3 v3.1.3 h1:F0nVttLC3ws0ojc7p60veTurcOm//D4QBODNM7EGrCI= github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0 h1:4pl5BV4o7ZG/lterP4S6WzJ6xr49Ba5ET9ygheTYahk= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= +github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -346,9 +371,10 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= @@ -440,6 +466,7 @@ github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -449,7 +476,6 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-migrate/migrate/v4 v4.6.2 h1:LDDOHo/q1W5UDj6PbkxdCv7lv9yunyZHXvxuwDkGo3k= github.com/golang-migrate/migrate/v4 v4.6.2/go.mod h1:JYi6reN3+Z734VZ0akNuyOJNcrg45ZL7LDBMW3WGJL0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -524,7 +550,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= @@ -559,6 +584,10 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-health-probe v0.3.2/go.mod h1:izVOQ4RWbjUR6lm4nn+VLJyQ+FyaiGmprEYgI04Gs7U= +github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4= +github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= +github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c h1:fEE5/5VNnYUoBOj2I9TP8Jc+a7lge3QWn9DKE7NCwfc= +github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c/go.mod h1:ObS/W+h8RYb1Y7fYivughjxojTmIu5iAIjSrSLCLeqE= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= @@ -592,8 +621,7 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -609,9 +637,12 @@ github.com/itchyny/gojq v0.11.0 h1:z7HnaKZ6erVzxPo3BkhJBG7jHmzKdAW8Hcse5N6YAic= github.com/itchyny/gojq v0.11.0/go.mod h1:my6D2qN2Sm6qa+/5GsPDUZlCWGR+U8Qsa9he76sudv0= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -619,6 +650,8 @@ github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/joefitzgerald/rainbow-reporter v0.1.0 h1:AuMG652zjdzI0YCCnXAqATtRBpGXMcAnrajcaTrSeuo= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/joelanford/ignore v0.0.0-20210607151042-0d25dc18b62d h1:A2/B900ip/Z20TzkLeGRNy1s6J2HmH9AmGt+dHyqb4I= +github.com/joelanford/ignore v0.0.0-20210607151042-0d25dc18b62d/go.mod h1:7HQupe4vyNxMKXmM5DFuwXHsqwMyglcYmZBtlDPIcZ8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -640,6 +673,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= @@ -651,6 +685,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -677,6 +713,7 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -721,8 +758,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2 h1:g+4J5sZg6osfvEfkRZxJ1em0VT95/UOZgi/l7zi1/oE= github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU= -github.com/mikefarah/yq/v2 v2.4.1/go.mod h1:i8SYf1XdgUvY2OFwSqGAtWOOgimD2McJ6iutoxRm4k0= github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37 h1:lPmsut5Sk7eK2BmDXuvNEvMbT7MkAJBu64Yxr7iJ6nk= github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37/go.mod h1:dYWq+UWoFCDY1TndvFUQuhBbIYmZpjreC8adEAx93zE= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -777,7 +812,6 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -796,9 +830,9 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= @@ -808,8 +842,8 @@ github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= @@ -841,11 +875,11 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/operator-framework/api v0.3.7-0.20200602203552-431198de9fc2/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q= +github.com/operator-framework/api v0.7.1/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY= github.com/operator-framework/api v0.10.1 h1:2tBjIr4hRZ0iaJ4UuenVjocbo9xrJi1O409K/tlEddo= github.com/operator-framework/api v0.10.1/go.mod h1:tV0BUNvly7szq28ZPBXhjp1Sqg5yHCOeX19ui9K4vjI= -github.com/operator-framework/operator-registry v1.13.6 h1:h/dIjQQS7uneQNRifrSz7h0xg4Xyjg6C9f6XZofbMPg= -github.com/operator-framework/operator-registry v1.13.6/go.mod h1:YhnIzOVjRU2ZwZtzt+fjcjW8ujJaSFynBEu7QVKaSdU= +github.com/operator-framework/operator-registry v1.17.5 h1:LR8m1rFz5Gcyje8WK6iYt+gIhtzqo52zMRALdmTYHT0= +github.com/operator-framework/operator-registry v1.17.5/go.mod h1:sRQIgDMZZdUcmHltzyCnM6RUoDF+WS8Arj1BQIARDS8= github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= @@ -913,7 +947,6 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -973,7 +1006,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= @@ -1021,6 +1053,7 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= @@ -1031,7 +1064,6 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1052,12 +1084,12 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200520232829-54ba9589114f h1:pBCD+Z7cy5WPTq+R6MmJJvDRpn88cp7bmTypBsn91g4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200520232829-54ba9589114f/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU= @@ -1112,17 +1144,21 @@ go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0H go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1130,6 +1166,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -1146,8 +1183,9 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1160,6 +1198,7 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -1184,6 +1223,7 @@ golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1211,7 +1251,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1226,6 +1265,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= @@ -1249,6 +1290,7 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1315,6 +1357,8 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1346,9 +1390,11 @@ golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs= golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1373,7 +1419,6 @@ golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1395,6 +1440,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1407,7 +1453,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -1423,12 +1469,14 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= @@ -1457,13 +1505,32 @@ google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200701001935-0939c5918c31/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200709232328-d8193ee9cc3e/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1484,8 +1551,9 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1495,7 +1563,6 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8 gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= -gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1507,6 +1574,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1532,6 +1600,7 @@ gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.6.1 h1:TQ6q4pAatXr7qh2fbLcb0oNd0I3J7kv26oo5cExKTtc= helm.sh/helm/v3 v3.6.1/go.mod h1:mIIus8EOqj+obtycw3sidsR4ORr2aFDmXMSI3k+oeVY= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1540,13 +1609,17 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s= k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= k8s.io/api v0.22.0-beta.0 h1:Icf4R7+a0zV6GexTqOXlcLw4OojdQVZE263bd4ui8ms= k8s.io/api v0.22.0-beta.0/go.mod h1:qORVa/3lcMBQXyTmjzpk8IN3VRg619iFNr8d16VXkUQ= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= +k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk= +k8s.io/apiextensions-apiserver v0.20.6/go.mod h1:qO8YMqeMmZH+lV21LUNzV41vfpoE9QVAJRA+MNqj0mo= k8s.io/apiextensions-apiserver v0.21.0/go.mod h1:gsQGNtGkc/YoDG9loKI0V+oLZM4ljRPjc/sql5tmvzc= k8s.io/apiextensions-apiserver v0.21.1/go.mod h1:KESQFCGjqVcVsZ9g0xX5bacMjyX5emuWcS2arzdEouA= k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA= @@ -1554,26 +1627,32 @@ k8s.io/apiextensions-apiserver v0.22.0-beta.0 h1:0w1Z8vtPSH8j0S1ryaztOhwJ1C/4RWT k8s.io/apiextensions-apiserver v0.22.0-beta.0/go.mod h1:Gvf2TX9jNuFdzd+b39kVksy9pEq9rVexezn3SeJoLeI= k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= k8s.io/apimachinery v0.22.0-beta.0 h1:y7EnOS9n+ZhCrk9nlwW7kwIr67syKFkGWj/HPAiE+XI= k8s.io/apimachinery v0.22.0-beta.0/go.mod h1:AZCsxGyiXb/4yTvUIiY+4LESjQ12B77LFrbW2xd61is= k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/apiserver v0.21.0/go.mod h1:w2YSn4/WIwYuxG5zJmcqtRdtqgW/J2JRgFAqps3bBpg= k8s.io/apiserver v0.21.1/go.mod h1:nLLYZvMWn35glJ4/FZRhzLG/3MPxAaZTgV4FJZdr+tY= k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw= k8s.io/apiserver v0.22.0-beta.0 h1:pSXFpHIw3PBA4XNPiwkajD+XyOjJwqSGDCNR8d3orTA= k8s.io/apiserver v0.22.0-beta.0/go.mod h1:8nCnNsRYExj/mPHowit5UvsjbExm1YWojDs4ziem9q4= -k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= +k8s.io/cli-runtime v0.20.6/go.mod h1:JVERW478qcxWrUjJuWQSqyJeiz9QC4T6jmBznHFBC8w= k8s.io/cli-runtime v0.21.0 h1:/V2Kkxtf6x5NI2z+Sd/mIrq4FQyQ8jzZAUD6N5RnN7Y= k8s.io/cli-runtime v0.21.0/go.mod h1:XoaHP93mGPF37MkLbjGVYqg3S1MnsFdKtiA/RZzzxOo= k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs= k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= @@ -1581,20 +1660,24 @@ k8s.io/client-go v0.22.0-beta.0 h1:CMKtGoQPNjt70Ii94mvuRO/eJM1iXuRh+u3V9Fb1ZvM= k8s.io/client-go v0.22.0-beta.0/go.mod h1:h2hyZ6clrIgKFF4Upjz2Epnvo9cq8S7rZfWegGQkNb0= k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= k8s.io/code-generator v0.20.4/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= +k8s.io/code-generator v0.20.6/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU= k8s.io/code-generator v0.21.0/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q= k8s.io/code-generator v0.21.1/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q= k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U= k8s.io/code-generator v0.22.0-beta.0 h1:fwlAIV7nZmcjHkj4NmIbZCpJyUn2AQGXArvWYVxaND0= k8s.io/code-generator v0.22.0-beta.0/go.mod h1:lBZLjrQ5bAwlmQalYYgV0gH3G7SS+Z2d33ijEte85FY= -k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/component-base v0.21.0/go.mod h1:qvtjz6X0USWXbgmbfXR+Agik4RZ3jv2Bgr5QnZzdPYw= k8s.io/component-base v0.21.1/go.mod h1:NgzFZ2qu4m1juby4TnrmpR8adRk6ka62YdH5DkIIyKA= k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc= k8s.io/component-base v0.22.0-beta.0 h1:9FPWLb4nWVyKQDtj7NUEdm+nTSlEdKvLseLAeo10wXg= k8s.io/component-base v0.22.0-beta.0/go.mod h1:/sa2flwcRBe8XTwdrueKBHBrR+J+ptB669U5R4ytRsU= +k8s.io/component-helpers v0.20.6/go.mod h1:d4rFhZS/wxrZCxRiJJiWf1mVGVeMB5/ey3Yv8/rOp78= k8s.io/component-helpers v0.21.0/go.mod h1:tezqefP7lxfvJyR+0a+6QtVrkZ/wIkyMLK4WcQ3Cj8U= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1620,10 +1703,10 @@ k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iL k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210527164424-3c818078ee3d h1:lUK8GPtuJy8ClWZhuvKoaLdKGPLq9H1PxWp7VPBZBkU= k8s.io/kube-openapi v0.0.0-20210527164424-3c818078ee3d/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= +k8s.io/kubectl v0.20.6/go.mod h1:yTCGVrlkBuQhFbKA1R65+lQ9hH7XeyOqUd0FUPFicPg= k8s.io/kubectl v0.21.0 h1:WZXlnG/yjcE4LWO2g6ULjFxtzK6H1TKzsfaBFuVIhNg= k8s.io/kubectl v0.21.0/go.mod h1:EU37NukZRXn1TpAkMUoy8Z/B2u6wjHDS4aInsDzVvks= -k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= +k8s.io/metrics v0.20.6/go.mod h1:d+OAIaXutom9kGWcBit/M8OkDpIzBKTsm47+KcUt7VI= k8s.io/metrics v0.21.0/go.mod h1:L3Ji9EGPP1YBbfm9sPfEXSpnj8i24bfQbAFAsW0NueQ= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1641,11 +1724,9 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyz sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.20 h1:jLWvYcn/9pCws8p54so9QpxkkL0SWc2fTZTg77NRyhg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.20/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo= +sigs.k8s.io/controller-runtime v0.8.0/go.mod h1:v9Lbj5oX443uR7GXYY46E0EE2o7k2YxQ58GxVNeXSW4= sigs.k8s.io/controller-runtime v0.9.0/go.mod h1:TgkfvrhhEw3PlI0BRL/5xM+89y3/yc0ZDfdbTl84si8= -sigs.k8s.io/controller-runtime v0.9.2 h1:MnCAsopQno6+hI9SgJHKddzXpmv2wtouZz6931Eax+Q= -sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk= -sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI= +sigs.k8s.io/controller-tools v0.4.1/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU= sigs.k8s.io/controller-tools v0.6.0/go.mod h1:baRMVPrctU77F+rfAuH2uPqW93k6yQnZA2dhUOr7ihc= sigs.k8s.io/controller-tools v0.6.1 h1:nODRx2YrSNcaGd+90+CVC9SGEG6ygHlz3nSJmweR5as= sigs.k8s.io/controller-tools v0.6.1/go.mod h1:U6O1RF5w17iX2d+teSXELpJsdexmrTb126DMeJM8r+U= @@ -1662,6 +1743,7 @@ sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4 sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.1 h1:nYqY2A6oy37sKLYuSBXuQhbj4JVclzJK13BOIvJG5XU= sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= @@ -1669,4 +1751,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go b/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go index 67e86de300..962e8066dd 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go +++ b/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go @@ -100,9 +100,14 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string ImagePullSecrets: secrets, Containers: []corev1.Container{ { - Name: "extract", - Image: c.opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", cmRef.Namespace, "-c", cmRef.Name}, + Name: "extract", + Image: c.opmImage, + Command: []string{"opm", "alpha", "bundle", "extract", + "-m", "/bundle/", + "-n", cmRef.Namespace, + "-c", cmRef.Name, + "-z", + }, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, diff --git a/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker_test.go b/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker_test.go index 0149606048..5d8ff740ab 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker_test.go @@ -222,7 +222,7 @@ func TestConfigMapUnpacker(t *testing.T) { { Name: "extract", Image: opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash}, + Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash, "-z"}, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, @@ -399,7 +399,7 @@ func TestConfigMapUnpacker(t *testing.T) { { Name: "extract", Image: opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash}, + Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash, "-z"}, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, @@ -616,7 +616,7 @@ func TestConfigMapUnpacker(t *testing.T) { { Name: "extract", Image: opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash}, + Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash, "-z"}, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, @@ -828,7 +828,7 @@ func TestConfigMapUnpacker(t *testing.T) { { Name: "extract", Image: opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash}, + Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash, "-z"}, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, @@ -1010,7 +1010,7 @@ func TestConfigMapUnpacker(t *testing.T) { { Name: "extract", Image: opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash}, + Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash, "-z"}, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, @@ -1203,7 +1203,7 @@ func TestConfigMapUnpacker(t *testing.T) { { Name: "extract", Image: opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash}, + Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", "ns-a", "-c", pathHash, "-z"}, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go b/staging/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go index aa898c8a4e..8fb18ed92f 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go @@ -19,6 +19,7 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -68,17 +69,17 @@ func (r *AdoptionReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches(&source.Kind{Type: &appsv1.Deployment{}}, enqueueCSV). Watches(&source.Kind{Type: &corev1.Namespace{}}, enqueueCSV). Watches(&source.Kind{Type: &corev1.Service{}}, enqueueCSV). - Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueCSV). - Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueCSV). - Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueCSV). Watches(&source.Kind{Type: &apiextensionsv1.CustomResourceDefinition{}}, enqueueProviders). Watches(&source.Kind{Type: &apiregistrationv1.APIService{}}, enqueueCSV). Watches(&source.Kind{Type: &operatorsv1alpha1.Subscription{}}, enqueueCSV). Watches(&source.Kind{Type: &operatorsv2.OperatorCondition{}}, enqueueCSV). + Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueCSV, builder.OnlyMetadata). Complete(reconcile.Func(r.ReconcileClusterServiceVersion)) if err != nil { return err @@ -350,24 +351,7 @@ func (r *AdoptionReconciler) disownFromAll(ctx context.Context, component runtim func (r *AdoptionReconciler) adoptees(ctx context.Context, operator decorators.Operator, csv *operatorsv1alpha1.ClusterServiceVersion) ([]runtime.Object, error) { // Note: We need to figure out how to dynamically add new list types here (or some equivalent) in // order to support operators composed of custom resources. - componentLists := []runtime.Object{ - &appsv1.DeploymentList{}, - &corev1.ServiceList{}, - &corev1.NamespaceList{}, - &corev1.ServiceAccountList{}, - &corev1.SecretList{}, - &corev1.ConfigMapList{}, - &rbacv1.RoleList{}, - &rbacv1.RoleBindingList{}, - &rbacv1.ClusterRoleList{}, - &rbacv1.ClusterRoleBindingList{}, - &apiregistrationv1.APIServiceList{}, - &apiextensionsv1.CustomResourceDefinitionList{}, - &operatorsv1alpha1.SubscriptionList{}, - &operatorsv1alpha1.InstallPlanList{}, - &operatorsv1alpha1.ClusterServiceVersionList{}, - &operatorsv2.OperatorConditionList{}, - } + componentLists := componentLists() // Only resources that aren't already labelled are adoption candidates selector, err := operator.NonComponentSelector() @@ -403,7 +387,8 @@ func (r *AdoptionReconciler) adoptees(ctx context.Context, operator decorators.O // Pick up owned CRDs for _, provided := range csv.Spec.CustomResourceDefinitions.Owned { - crd := &apiextensionsv1.CustomResourceDefinition{} + crd := &metav1.PartialObjectMetadata{} + crd.SetGroupVersionKind(apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition")) if err := r.Get(ctx, types.NamespacedName{Name: provided.Name}, crd); err != nil { if !apierrors.IsNotFound(err) { // Inform requeue on transient error diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/not_found.go b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/not_found.go new file mode 100644 index 0000000000..8cf4a3b991 --- /dev/null +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/not_found.go @@ -0,0 +1,69 @@ +package catalog + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" +) + +// gvkNotFoundError is returned from installplan execution when a step contains a GVK that is not found on cluster. +type gvkNotFoundError struct { + gvk schema.GroupVersionKind + name string +} + +func (g gvkNotFoundError) Error() string { + return fmt.Sprintf("api-server resource not found installing %s %s: GroupVersionKind %s not found on the cluster. %s", g.gvk.Kind, g.name, g.gvk, + "This API may have been deprecated and removed, see https://kubernetes.io/docs/reference/using-api/deprecation-guide/ for more information.") +} + +type DiscoveryQuerier interface { + QueryForGVK() error +} + +type DiscoveryQuerierFunc func() error + +func (d DiscoveryQuerierFunc) QueryForGVK() error { + return d() +} + +type discoveryQuerier struct { + client discovery.DiscoveryInterface +} + +func newDiscoveryQuerier(client discovery.DiscoveryInterface) *discoveryQuerier { + return &discoveryQuerier{client: client} +} + +// WithStepResource returns a DiscoveryQuerier which uses discovery to query for supported APIs on the server based on the provided step's GVK. +func (d *discoveryQuerier) WithStepResource(stepResource operatorsv1alpha1.StepResource) DiscoveryQuerier { + var f DiscoveryQuerierFunc = func() error { + gvk := schema.GroupVersionKind{Group: stepResource.Group, Version: stepResource.Version, Kind: stepResource.Kind} + + resourceList, err := d.client.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) + if err != nil { + if errors.IsNotFound(err) { + return gvkNotFoundError{gvk: gvk, name: stepResource.Name} + } + return err + } + + if resourceList == nil { + return gvkNotFoundError{gvk: gvk, name: stepResource.Name} + } + + for _, resource := range resourceList.APIResources { + if resource.Kind == stepResource.Kind { + // this kind is supported for this particular GroupVersion + return nil + } + } + + return gvkNotFoundError{gvk: gvk, name: stepResource.Name} + } + return f +} diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go index efd1685b90..6d057af5f0 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go @@ -11,6 +11,8 @@ import ( "sync" "time" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + errorwrap "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/grpc/connectivity" @@ -115,7 +117,7 @@ type Operator struct { type CatalogSourceSyncFunc func(logger *logrus.Entry, in *v1alpha1.CatalogSource) (out *v1alpha1.CatalogSource, continueSync bool, syncError error) // NewOperator creates a new Catalog Operator. -func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clock, logger *logrus.Logger, resync time.Duration, configmapRegistryImage, utilImage string, operatorNamespace string, scheme *runtime.Scheme, installPlanTimeout time.Duration, bundleUnpackTimeout time.Duration) (*Operator, error) { +func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clock, logger *logrus.Logger, resync time.Duration, configmapRegistryImage, opmImage, utilImage string, operatorNamespace string, scheme *runtime.Scheme, installPlanTimeout time.Duration, bundleUnpackTimeout time.Duration) (*Operator, error) { resyncPeriod := queueinformer.ResyncWithJitter(resync, 0.2) config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) if err != nil { @@ -351,7 +353,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo bundle.WithPodLister(podInformer.Lister()), bundle.WithRoleLister(roleInformer.Lister()), bundle.WithRoleBindingLister(roleBindingInformer.Lister()), - bundle.WithOPMImage(configmapRegistryImage), + bundle.WithOPMImage(opmImage), bundle.WithUtilImage(utilImage), bundle.WithNow(op.now), bundle.WithUnpackTimeout(op.bundleUnpackTimeout), @@ -1774,6 +1776,8 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error { ensurer := newStepEnsurer(kubeclient, crclient, dynamicClient) r := newManifestResolver(plan.GetNamespace(), o.lister.CoreV1().ConfigMapLister(), o.logger) + discoveryQuerier := newDiscoveryQuerier(o.opClient.KubernetesInterface().Discovery()) + // CRDs should be installed via the default OLM (cluster-admin) client and not the scoped client specified by the AttenuatedServiceAccount // the StepBuilder is currently only implemented for CRD types // TODO give the StepBuilder both OLM and scoped clients when it supports new scoped types @@ -2173,6 +2177,14 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error { } return nil }(i, step); err != nil { + if k8serrors.IsNotFound(err) { + // Check for APIVersions present in the installplan steps that are not available on the server. + // The check is made via discovery per step in the plan. Transient communication failures to the api-server are handled by the plan retry logic. + notFoundErr := discoveryQuerier.WithStepResource(step.Resource).QueryForGVK() + if notFoundErr != nil { + return notFoundErr + } + } return err } } diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go index f5da96614a..024cb3dd96 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go @@ -1,6 +1,7 @@ package subscription import ( + "bytes" "context" "fmt" @@ -452,7 +453,7 @@ func (i *installPlanReferencedState) CheckInstallPlanStatus(now *metav1.Time, cl if cond.Reason == "" { cond.Reason = string(phase) } - + cond.Message = extractMessage(status) cond.Type = v1alpha1.SubscriptionInstallPlanPending cond.Status = corev1.ConditionTrue out.Status.SetCondition(cond) @@ -472,6 +473,7 @@ func (i *installPlanReferencedState) CheckInstallPlanStatus(now *metav1.Time, cl } cond.Type = v1alpha1.SubscriptionInstallPlanFailed + cond.Message = extractMessage(status) cond.Status = corev1.ConditionTrue out.Status.SetCondition(cond) @@ -508,6 +510,25 @@ func (i *installPlanReferencedState) CheckInstallPlanStatus(now *metav1.Time, cl return known, nil } +func extractMessage(status *v1alpha1.InstallPlanStatus) string { + if cond := status.GetCondition(v1alpha1.InstallPlanInstalled); cond.Status != corev1.ConditionUnknown && cond.Message != "" { + return cond.Message + } + + var b bytes.Buffer + for _, lookup := range status.BundleLookups { + if cond := lookup.GetCondition(v1alpha1.BundleLookupPending); cond.Status != corev1.ConditionUnknown && cond.Message != "" { + if b.Len() != 0 { + b.WriteString(" ") + } + b.WriteString(cond.Message) + b.WriteString(".") + } + } + + return b.String() +} + type installPlanKnownState struct { InstallPlanReferencedState } diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state_test.go index 3ba23f4fdf..afd0f576b4 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state_test.go @@ -1336,6 +1336,14 @@ func TestCheckInstallPlanStatus(t *testing.T) { now: &now, status: &v1alpha1.InstallPlanStatus{ Phase: v1alpha1.InstallPlanPhaseInstalling, + Conditions: []v1alpha1.InstallPlanCondition{ + { + Type: v1alpha1.InstallPlanInstalled, + Message: "no operatorgroup found that is managing this namespace", + Reason: v1alpha1.InstallPlanConditionReason("Installing"), + Status: corev1.ConditionFalse, + }, + }, }, }, want: want{ @@ -1346,7 +1354,7 @@ func TestCheckInstallPlanStatus(t *testing.T) { }, Status: v1alpha1.SubscriptionStatus{ Conditions: []v1alpha1.SubscriptionCondition{ - planPendingCondition(corev1.ConditionTrue, string(v1alpha1.InstallPlanPhaseInstalling), "", &now), + planPendingCondition(corev1.ConditionTrue, string(v1alpha1.InstallPlanPhaseInstalling), "no operatorgroup found that is managing this namespace", &now), }, LastUpdated: now, }, @@ -1535,6 +1543,86 @@ func TestCheckInstallPlanStatus(t *testing.T) { Reason: v1alpha1.InstallPlanReasonComponentFailed, }, }, + BundleLookups: []v1alpha1.BundleLookup{ + { + Conditions: []v1alpha1.BundleLookupCondition{ + { + Type: v1alpha1.BundleLookupPending, + Status: corev1.ConditionTrue, + Message: "unpack job not completed: Unpack pod(olm/c5a4) container(pull) is pending. Reason: ImagePullBackOff, Message: Back-off pulling image", + }, + }, + }, + }, + }, + }, + want: want{ + transitioned: newInstallPlanFailedState(&v1alpha1.Subscription{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sub", + Namespace: "ns", + }, + Status: v1alpha1.SubscriptionStatus{ + Conditions: []v1alpha1.SubscriptionCondition{ + planFailedCondition(corev1.ConditionTrue, string(v1alpha1.InstallPlanReasonComponentFailed), "unpack job not completed: Unpack pod(olm/c5a4) container(pull) is pending. Reason: ImagePullBackOff, Message: Back-off pulling image.", &now), + }, + LastUpdated: now, + }, + }), + }, + }, + { + description: "InstallPlanReferencedState/NoConditions/Failed/ToInstallPlanFailedState/Update/InstallPlanReasonComponentFailed/MultipleBundleConditions", + fields: fields{ + existingObjs: existingObjs{ + clientObjs: []runtime.Object{ + &v1alpha1.Subscription{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sub", + Namespace: "ns", + }, + }, + }, + }, + namespace: "ns", + state: newInstallPlanReferencedState(&v1alpha1.Subscription{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sub", + Namespace: "ns", + }, + }), + }, + args: args{ + now: &now, + status: &v1alpha1.InstallPlanStatus{ + Phase: v1alpha1.InstallPlanPhaseFailed, + Conditions: []v1alpha1.InstallPlanCondition{ + { + Type: v1alpha1.InstallPlanInstalled, + Status: corev1.ConditionFalse, + Reason: v1alpha1.InstallPlanReasonComponentFailed, + }, + }, + BundleLookups: []v1alpha1.BundleLookup{ + { + Conditions: []v1alpha1.BundleLookupCondition{ + { + Type: v1alpha1.BundleLookupPending, + Status: corev1.ConditionTrue, + Message: "encountered issue foo", + }, + }, + }, + { + Conditions: []v1alpha1.BundleLookupCondition{ + { + Type: v1alpha1.BundleLookupPending, + Status: corev1.ConditionTrue, + Message: "encountered issue bar", + }, + }, + }, + }, }, }, want: want{ @@ -1545,7 +1633,7 @@ func TestCheckInstallPlanStatus(t *testing.T) { }, Status: v1alpha1.SubscriptionStatus{ Conditions: []v1alpha1.SubscriptionCondition{ - planFailedCondition(corev1.ConditionTrue, string(v1alpha1.InstallPlanReasonComponentFailed), "", &now), + planFailedCondition(corev1.ConditionTrue, string(v1alpha1.InstallPlanReasonComponentFailed), "encountered issue foo. encountered issue bar.", &now), }, LastUpdated: now, }, diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/components.go b/staging/operator-lifecycle-manager/pkg/controller/operators/components.go new file mode 100644 index 0000000000..63c48429dc --- /dev/null +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/components.go @@ -0,0 +1,70 @@ +package operators + +import ( + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + operatorsv2 "github.com/operator-framework/api/pkg/operators/v2" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" +) + +func componentLists() []runtime.Object { + return []runtime.Object{ + &appsv1.DeploymentList{}, + &corev1.ServiceList{}, + &corev1.NamespaceList{}, + &apiregistrationv1.APIServiceList{}, + &apiextensionsv1.CustomResourceDefinitionList{}, + &operatorsv1alpha1.SubscriptionList{}, + &operatorsv1alpha1.InstallPlanList{}, + &operatorsv1alpha1.ClusterServiceVersionList{}, + &operatorsv2.OperatorConditionList{}, + + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "SecretList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ConfigMapList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ServiceAccountList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "RoleList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "RoleBindingList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "ClusterRoleList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "ClusterRoleBindingList", + }, + }, + } +} diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go b/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go index 40252daa5b..4046f7ee09 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/reference" @@ -344,6 +345,10 @@ func NewComponent(component runtime.Object, scheme *runtime.Scheme) (*Component, if err := scheme.Convert(component, u, nil); err != nil { return nil, err } + // GVK may have been lost from PartialObjectMetadata during conversion. + if gvk := component.GetObjectKind().GroupVersionKind(); gvk != schema.EmptyObjectKind.GroupVersionKind() { + u.SetGroupVersionKind(gvk) + } c := &Component{ Unstructured: u, diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator_test.go index d19d2cab16..3c6ee8dbf9 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator_test.go @@ -88,6 +88,7 @@ func TestAddComponents(t *testing.T) { scheme := runtime.NewScheme() require.NoError(t, k8sscheme.AddToScheme(scheme)) require.NoError(t, operatorsv1alpha1.AddToScheme(scheme)) + require.NoError(t, metav1.AddMetaToScheme(scheme)) type fields struct { operator *operatorsv1.Operator diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/clusteroperator_controller_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/clusteroperator_controller_test.go index 9b5c750ff5..6f00839d1b 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/clusteroperator_controller_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/clusteroperator_controller_test.go @@ -1,6 +1,8 @@ package openshift import ( + "fmt" + semver "github.com/blang/semver/v4" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -161,7 +163,7 @@ var _ = Describe("ClusterOperator controller", func() { withMax := func(version string) map[string]string { maxProperty := &api.Property{ Type: MaxOpenShiftVersionProperty, - Value: `"` + version + `"`, // Wrap in quotes so we don't break property marshaling + Value: version, } value, err := projection.PropertiesAnnotationFromPropertyList([]*api.Property{maxProperty}) Expect(err).ToNot(HaveOccurred()) @@ -170,7 +172,7 @@ var _ = Describe("ClusterOperator controller", func() { projection.PropertiesAnnotationKey: value, } } - incompatible.SetAnnotations(withMax(clusterVersion)) + incompatible.SetAnnotations(withMax(fmt.Sprintf(`"%s"`, clusterVersion))) // Wrap in quotes so we don't break property marshaling Eventually(func() error { return k8sClient.Create(ctx, incompatible) @@ -202,7 +204,7 @@ var _ = Describe("ClusterOperator controller", func() { // Set compatibility to the next minor version next := semver.MustParse(clusterVersion) Expect(next.IncrementMinor()).To(Succeed()) - incompatible.SetAnnotations(withMax(next.String())) + incompatible.SetAnnotations(withMax(fmt.Sprintf(`"%s"`, next.String()))) Eventually(func() error { return k8sClient.Update(ctx, incompatible) @@ -216,5 +218,32 @@ var _ = Describe("ClusterOperator controller", func() { Status: configv1.ConditionTrue, LastTransitionTime: fixedNow(), })) + + By("understanding unquoted short max versions; e.g. X.Y") + // Mimic common pipeline shorthand + v := semver.MustParse(clusterVersion) + short := fmt.Sprintf("%d.%d", v.Major, v.Minor) + incompatible.SetAnnotations(withMax(short)) + + Eventually(func() error { + return k8sClient.Update(ctx, incompatible) + }).Should(Succeed()) + + Eventually(func() ([]configv1.ClusterOperatorStatusCondition, error) { + err := k8sClient.Get(ctx, clusterOperatorName, co) + return co.Status.Conditions, err + }, timeout).Should(ContainElement(configv1.ClusterOperatorStatusCondition{ + Type: configv1.OperatorUpgradeable, + Status: configv1.ConditionFalse, + Reason: IncompatibleOperatorsInstalled, + Message: skews{ + { + namespace: ns.GetName(), + name: incompatible.GetName(), + maxOpenShiftVersion: short + ".0", + }, + }.String(), + LastTransitionTime: fixedNow(), + })) }) }) diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go b/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go index a30ab420f7..c7b0a00f2f 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go @@ -2,7 +2,6 @@ package openshift import ( "context" - "encoding/json" "fmt" "strings" @@ -190,12 +189,7 @@ func maxOpenShiftVersion(csv *operatorsv1alpha1.ClusterServiceVersion) (*semver. continue } - var value string - if err := json.Unmarshal([]byte(property.Value), &value); err != nil { - errs = append(errs, err) - continue - } - + value := strings.Trim(property.Value, "\"") if value == "" { continue } @@ -203,6 +197,7 @@ func maxOpenShiftVersion(csv *operatorsv1alpha1.ClusterServiceVersion) (*semver. version, err := semver.ParseTolerant(value) if err != nil { errs = append(errs, err) + continue } if max == nil { @@ -241,11 +236,7 @@ func notCopiedSelector() (labels.Selector, error) { if err != nil { return nil, err } - - selector := labels.NewSelector() - selector.Add(*requirement) - - return selector, nil + return labels.NewSelector().Add(*requirement), nil } func olmOperatorRelatedObjects(ctx context.Context, cli client.Client, namespace string) ([]configv1.ObjectReference, error) { diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers_test.go index 6ce1230834..3e6a12dd6f 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers_test.go @@ -2,12 +2,14 @@ package openshift import ( "context" + "fmt" "testing" semver "github.com/blang/semver/v4" configv1 "github.com/openshift/api/config/v1" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -340,6 +342,11 @@ func TestIncompatibleOperators(t *testing.T) { namespace: "default", maxOpenShiftVersion: "1.0.0", }, + { + name: "chestnut", + namespace: "default", + maxOpenShiftVersion: "1.0", + }, }, expect: expect{ err: false, @@ -349,6 +356,11 @@ func TestIncompatibleOperators(t *testing.T) { namespace: "default", maxOpenShiftVersion: "1.0.0", }, + { + name: "chestnut", + namespace: "default", + maxOpenShiftVersion: "1.0.0", + }, }, }, }, @@ -462,7 +474,10 @@ func TestIncompatibleOperators(t *testing.T) { func TestMaxOpenShiftVersion(t *testing.T) { mustParse := func(s string) *semver.Version { - version := semver.MustParse(s) + version, err := semver.ParseTolerant(s) + if err != nil { + panic(fmt.Sprintf("bad version given for test case: %s", err)) + } return &version } @@ -484,7 +499,7 @@ func TestMaxOpenShiftVersion(t *testing.T) { }, { description: "Nothing", - in: []string{""}, + in: []string{`""`}, expect: expect{ err: false, max: nil, @@ -493,8 +508,8 @@ func TestMaxOpenShiftVersion(t *testing.T) { { description: "Nothing/Mixed", in: []string{ - "", - "1.0.0", + `""`, + `"1.0.0"`, }, expect: expect{ err: false, @@ -503,7 +518,7 @@ func TestMaxOpenShiftVersion(t *testing.T) { }, { description: "Garbage", - in: []string{"bad_version"}, + in: []string{`"bad_version"`}, expect: expect{ err: true, max: nil, @@ -512,8 +527,8 @@ func TestMaxOpenShiftVersion(t *testing.T) { { description: "Garbage/Mixed", in: []string{ - "bad_version", - "1.0.0", + `"bad_version"`, + `"1.0.0"`, }, expect: expect{ err: true, @@ -522,7 +537,7 @@ func TestMaxOpenShiftVersion(t *testing.T) { }, { description: "Single", - in: []string{"1.0.0"}, + in: []string{`"1.0.0"`}, expect: expect{ err: false, max: mustParse("1.0.0"), @@ -531,8 +546,8 @@ func TestMaxOpenShiftVersion(t *testing.T) { { description: "Multiple", in: []string{ - "1.0.0", - "2.0.0", + `"1.0.0"`, + `"2.0.0"`, }, expect: expect{ err: false, @@ -542,8 +557,8 @@ func TestMaxOpenShiftVersion(t *testing.T) { { description: "Duplicates", in: []string{ - "1.0.0", - "1.0.0", + `"1.0.0"`, + `"1.0.0"`, }, expect: expect{ err: false, @@ -553,9 +568,9 @@ func TestMaxOpenShiftVersion(t *testing.T) { { description: "Duplicates/NonMax", in: []string{ - "1.0.0", - "1.0.0", - "2.0.0", + `"1.0.0"`, + `"1.0.0"`, + `"2.0.0"`, }, expect: expect{ err: false, @@ -565,21 +580,30 @@ func TestMaxOpenShiftVersion(t *testing.T) { { description: "Ambiguous", in: []string{ - "1.0.0", - "1.0.0+1", + `"1.0.0"`, + `"1.0.0+1"`, }, expect: expect{ err: true, max: nil, }, }, + { + // Ensure unquoted short strings are accepted; e.g. X.Y + description: "Unquoted/Short", + in: []string{"4.8"}, + expect: expect{ + err: false, + max: mustParse("4.8"), + }, + }, } { t.Run(tt.description, func(t *testing.T) { var properties []*api.Property for _, max := range tt.in { properties = append(properties, &api.Property{ Type: MaxOpenShiftVersionProperty, - Value: `"` + max + `"`, // Wrap in quotes so we don't break property marshaling + Value: max, }) } @@ -602,3 +626,25 @@ func TestMaxOpenShiftVersion(t *testing.T) { }) } } + +func TestNotCopiedSelector(t *testing.T) { + for _, tc := range []struct { + Labels labels.Set + Matches bool + }{ + { + Labels: labels.Set{operatorsv1alpha1.CopiedLabelKey: ""}, + Matches: false, + }, + { + Labels: labels.Set{}, + Matches: true, + }, + } { + t.Run(tc.Labels.String(), func(t *testing.T) { + selector, err := notCopiedSelector() + require.NoError(t, err) + require.Equal(t, tc.Matches, selector.Matches(tc.Labels)) + }) + } +} diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go b/staging/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go index 3153545ec4..62aa4e2d06 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go @@ -18,6 +18,7 @@ import ( kscheme "k8s.io/client-go/kubernetes/scheme" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -69,20 +70,22 @@ func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&operatorsv1.Operator{}). Watches(&source.Kind{Type: &appsv1.Deployment{}}, enqueueOperator). Watches(&source.Kind{Type: &corev1.Namespace{}}, enqueueOperator). - Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueOperator). - Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueOperator). - Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueOperator). Watches(&source.Kind{Type: &apiextensionsv1.CustomResourceDefinition{}}, enqueueOperator). Watches(&source.Kind{Type: &apiregistrationv1.APIService{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv1alpha1.Subscription{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv1alpha1.InstallPlan{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv1alpha1.ClusterServiceVersion{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv2.OperatorCondition{}}, enqueueOperator). - // TODO(njhale): Add WebhookConfigurations and ConfigMaps + // Metadata is sufficient to build component refs for + // GVKs that don't have a .status.conditions field. + Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueOperator, builder.OnlyMetadata). + // TODO(njhale): Add WebhookConfigurations Complete(r) } @@ -196,23 +199,7 @@ func (r *OperatorReconciler) updateComponents(ctx context.Context, operator *dec func (r *OperatorReconciler) listComponents(ctx context.Context, selector labels.Selector) ([]runtime.Object, error) { // Note: We need to figure out how to dynamically add new list types here (or some equivalent) in // order to support operators composed of custom resources. - componentLists := []runtime.Object{ - &appsv1.DeploymentList{}, - &corev1.NamespaceList{}, - &corev1.ServiceAccountList{}, - &corev1.SecretList{}, - &corev1.ConfigMapList{}, - &rbacv1.RoleList{}, - &rbacv1.RoleBindingList{}, - &rbacv1.ClusterRoleList{}, - &rbacv1.ClusterRoleBindingList{}, - &apiextensionsv1.CustomResourceDefinitionList{}, - &apiregistrationv1.APIServiceList{}, - &operatorsv1alpha1.SubscriptionList{}, - &operatorsv1alpha1.InstallPlanList{}, - &operatorsv1alpha1.ClusterServiceVersionList{}, - &operatorsv2.OperatorConditionList{}, - } + componentLists := componentLists() opt := client.MatchingLabelsSelector{Selector: selector} for _, list := range componentLists { diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/suite_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/suite_test.go index c1a90588a7..53f7d50fe6 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/suite_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/suite_test.go @@ -105,8 +105,8 @@ var _ = BeforeSuite(func() { Expect(cfg).ToNot(BeNil()) By("Setting up a controller manager") - err = AddToScheme(scheme) - Expect(err).ToNot(HaveOccurred()) + Expect(metav1.AddMetaToScheme(scheme)).To(Succeed()) + Expect(AddToScheme(scheme)).To(Succeed()) mgr, err := ctrl.NewManager(cfg, ctrl.Options{ MetricsBindAddress: "0", // Prevents conflicts with other test suites that might bind metrics Scheme: scheme, diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go index 1fd00375f3..0c017c7cc5 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go @@ -60,24 +60,30 @@ func NewBundleInstallableFromOperator(o *Operator) (BundleInstallable, error) { if src == nil { return BundleInstallable{}, fmt.Errorf("unable to resolve the source of bundle %s", o.Identifier()) } + id := bundleId(o.Identifier(), o.Channel(), src.Catalog) var constraints []solver.Constraint + if src.Catalog.Virtual() && src.Subscription == nil { + // CSVs already associated with a Subscription + // may be replaced, but freestanding CSVs must + // appear in any solution. + constraints = append(constraints, PrettyConstraint( + solver.Mandatory(), + fmt.Sprintf("clusterserviceversion %s exists and is not referenced by a subscription", o.Identifier()), + )) + } for _, p := range o.bundle.GetProperties() { if p.GetType() == operatorregistry.DeprecatedType { constraints = append(constraints, PrettyConstraint( solver.Prohibited(), - fmt.Sprintf("bundle %s is deprecated", bundleId(o.Identifier(), o.Channel(), src.Catalog)), + fmt.Sprintf("bundle %s is deprecated", id), )) break } } - return NewBundleInstallable(o.Identifier(), o.Channel(), src.Catalog, constraints...), nil -} - -func NewBundleInstallable(bundle, channel string, catalog registry.CatalogKey, constraints ...solver.Constraint) BundleInstallable { return BundleInstallable{ - identifier: bundleId(bundle, channel, catalog), + identifier: id, constraints: constraints, - } + }, nil } type GenericInstallable struct { diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go index 19a0b933ed..1c21425bf2 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go @@ -11,10 +11,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-registry/pkg/api" opregistry "github.com/operator-framework/operator-registry/pkg/registry" - - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" ) type APISet map[opregistry.APIKey]struct{} @@ -197,6 +196,7 @@ type OperatorSourceInfo struct { StartingCSV string Catalog registry.CatalogKey DefaultChannel bool + Subscription *v1alpha1.Subscription } func (i *OperatorSourceInfo) String() string { @@ -216,7 +216,6 @@ type OperatorSurface interface { SourceInfo() *OperatorSourceInfo Bundle() *api.Bundle Inline() bool - Dependencies() []*api.Dependency Properties() []*api.Property Skips() []string } @@ -229,7 +228,6 @@ type Operator struct { version *semver.Version bundle *api.Bundle sourceInfo *OperatorSourceInfo - dependencies []*api.Dependency properties []*api.Property skips []string } @@ -261,21 +259,27 @@ func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey reg // legacy support - if the api doesn't contain properties/dependencies, build them from required/provided apis properties := bundle.Properties if len(properties) == 0 { - properties, err = apisToProperties(provided) + properties, err = providedAPIsToProperties(provided) if err != nil { return nil, err } } - dependencies := bundle.Dependencies - if len(dependencies) == 0 { - dependencies, err = apisToDependencies(required) + if len(bundle.Dependencies) > 0 { + ps, err := legacyDependenciesToProperties(bundle.Dependencies) + if err != nil { + return nil, fmt.Errorf("failed to translate legacy dependencies to properties: %w", err) + } + properties = append(properties, ps...) + } else { + ps, err := requiredAPIsToProperties(required) if err != nil { return nil, err } + properties = append(properties, ps...) } // legacy support - if the grpc api doesn't contain required/provided apis, fallback to csv parsing - if len(required) == 0 && len(provided) == 0 && len(properties) == 0 && len(dependencies) == 0 { + if len(required) == 0 && len(provided) == 0 && len(properties) == 0 { // fallback to csv parsing if bundle.CsvJson == "" { if bundle.GetBundlePath() != "" { @@ -307,7 +311,6 @@ func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey reg requiredAPIs: required, bundle: bundle, sourceInfo: sourceInfo, - dependencies: dependencies, properties: properties, skips: bundle.Skips, }, nil @@ -338,15 +341,15 @@ func NewOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*Operator, requiredAPIs[opregistry.APIKey{Group: api.Group, Version: api.Version, Kind: api.Kind, Plural: api.Name}] = struct{}{} } - dependencies, err := apisToDependencies(requiredAPIs) + properties, err := providedAPIsToProperties(providedAPIs) if err != nil { return nil, err } - - properties, err := apisToProperties(providedAPIs) + dependencies, err := requiredAPIsToProperties(requiredAPIs) if err != nil { return nil, err } + properties = append(properties, dependencies...) return &Operator{ name: csv.GetName(), @@ -354,7 +357,6 @@ func NewOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*Operator, providedAPIs: providedAPIs, requiredAPIs: requiredAPIs, sourceInfo: &ExistingOperator, - dependencies: dependencies, properties: properties, }, nil } @@ -413,48 +415,48 @@ func (o *Operator) Inline() bool { return o.bundle != nil && o.bundle.GetBundlePath() == "" } -func (o *Operator) Dependencies() []*api.Dependency { - return o.dependencies -} - func (o *Operator) Properties() []*api.Property { return o.properties } func (o *Operator) DependencyPredicates() (predicates []OperatorPredicate, err error) { predicates = make([]OperatorPredicate, 0) - for _, d := range o.Dependencies() { - var p OperatorPredicate - if d == nil || d.Type == "" { - continue - } - p, err = PredicateForDependency(d) + for _, property := range o.Properties() { + predicate, err := PredicateForProperty(property) if err != nil { - return + return nil, err + } + if predicate == nil { + continue } - predicates = append(predicates, p) + predicates = append(predicates, predicate) } return } -// TODO: this should go in its own dependency/predicate builder package -// TODO: can we make this more extensible, i.e. via cue -func PredicateForDependency(dependency *api.Dependency) (OperatorPredicate, error) { - p, ok := predicates[dependency.Type] +func PredicateForProperty(property *api.Property) (OperatorPredicate, error) { + if property == nil { + return nil, nil + } + p, ok := predicates[property.Type] if !ok { - return nil, fmt.Errorf("no predicate for dependency type %s", dependency.Type) + return nil, nil } - return p(dependency.Value) + return p(property.Value) } var predicates = map[string]func(string) (OperatorPredicate, error){ - opregistry.GVKType: predicateForGVKDependency, - opregistry.PackageType: predicateForPackageDependency, - opregistry.LabelType: predicateForLabelDependency, + "olm.gvk.required": predicateForRequiredGVKProperty, + "olm.package.required": predicateForRequiredPackageProperty, + "olm.label.required": predicateForRequiredLabelProperty, } -func predicateForGVKDependency(value string) (OperatorPredicate, error) { - var gvk opregistry.GVKDependency +func predicateForRequiredGVKProperty(value string) (OperatorPredicate, error) { + var gvk struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + } if err := json.Unmarshal([]byte(value), &gvk); err != nil { return nil, err } @@ -465,44 +467,51 @@ func predicateForGVKDependency(value string) (OperatorPredicate, error) { }), nil } -func predicateForPackageDependency(value string) (OperatorPredicate, error) { - var pkg opregistry.PackageDependency +func predicateForRequiredPackageProperty(value string) (OperatorPredicate, error) { + var pkg struct { + PackageName string `json:"packageName"` + VersionRange string `json:"versionRange"` + } if err := json.Unmarshal([]byte(value), &pkg); err != nil { return nil, err } - ver, err := semver.ParseRange(pkg.Version) + ver, err := semver.ParseRange(pkg.VersionRange) if err != nil { return nil, err } - return And(WithPackage(pkg.PackageName), WithVersionInRange(ver)), nil } -func predicateForLabelDependency(value string) (OperatorPredicate, error) { - var label opregistry.LabelDependency +func predicateForRequiredLabelProperty(value string) (OperatorPredicate, error) { + var label struct { + Label string `json:"label"` + } if err := json.Unmarshal([]byte(value), &label); err != nil { return nil, err } - return WithLabel(label.Label), nil } -func apisToDependencies(apis APISet) (out []*api.Dependency, err error) { +func requiredAPIsToProperties(apis APISet) (out []*api.Property, err error) { if len(apis) == 0 { return } - out = make([]*api.Dependency, 0) + out = make([]*api.Property, 0) for a := range apis { - val, err := json.Marshal(opregistry.GVKDependency{ + val, err := json.Marshal(struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + }{ Group: a.Group, - Kind: a.Kind, Version: a.Version, + Kind: a.Kind, }) if err != nil { return nil, err } - out = append(out, &api.Dependency{ - Type: opregistry.GVKType, + out = append(out, &api.Property{ + Type: "olm.gvk.required", Value: string(val), }) } @@ -512,13 +521,13 @@ func apisToDependencies(apis APISet) (out []*api.Dependency, err error) { return nil, nil } -func apisToProperties(apis APISet) (out []*api.Property, err error) { +func providedAPIsToProperties(apis APISet) (out []*api.Property, err error) { out = make([]*api.Property, 0) for a := range apis { val, err := json.Marshal(opregistry.GVKProperty{ Group: a.Group, - Kind: a.Kind, Version: a.Version, + Kind: a.Kind, }) if err != nil { panic(err) @@ -533,3 +542,63 @@ func apisToProperties(apis APISet) (out []*api.Property, err error) { } return nil, nil } + +func legacyDependenciesToProperties(dependencies []*api.Dependency) ([]*api.Property, error) { + var result []*api.Property + for _, dependency := range dependencies { + switch dependency.Type { + case "olm.gvk": + type gvk struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + } + var vfrom gvk + if err := json.Unmarshal([]byte(dependency.Value), &vfrom); err != nil { + return nil, fmt.Errorf("failed to unmarshal legacy 'olm.gvk' dependency: %w", err) + } + vto := gvk{ + Group: vfrom.Group, + Version: vfrom.Version, + Kind: vfrom.Kind, + } + vb, err := json.Marshal(&vto) + if err != nil { + return nil, fmt.Errorf("unexpected error marshaling generated 'olm.package.required' property: %w", err) + } + result = append(result, &api.Property{ + Type: "olm.gvk.required", + Value: string(vb), + }) + case "olm.package": + var vfrom struct { + PackageName string `json:"packageName"` + VersionRange string `json:"version"` + } + if err := json.Unmarshal([]byte(dependency.Value), &vfrom); err != nil { + return nil, fmt.Errorf("failed to unmarshal legacy 'olm.package' dependency: %w", err) + } + vto := struct { + PackageName string `json:"packageName"` + VersionRange string `json:"versionRange"` + }{ + PackageName: vfrom.PackageName, + VersionRange: vfrom.VersionRange, + } + vb, err := json.Marshal(&vto) + if err != nil { + return nil, fmt.Errorf("unexpected error marshaling generated 'olm.package.required' property: %w", err) + } + result = append(result, &api.Property{ + Type: "olm.package.required", + Value: string(vb), + }) + case "olm.label": + result = append(result, &api.Property{ + Type: "olm.label.required", + Value: dependency.Value, + }) + } + } + return result, nil +} diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators_test.go b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators_test.go index 5cbbdeabcd..4f3897fb94 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/operators_test.go @@ -737,7 +737,7 @@ func TestAPIMultiOwnerSet_PopAPIKey(t *testing.T) { { name: "OneApi/OneOwner", s: map[opregistry.APIKey]OperatorSet{ - opregistry.APIKey{"g", "v", "k", "p"}: map[string]OperatorSurface{ + {Group: "g", Version: "v", Kind: "k", Plural: "p"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, }, }, @@ -745,7 +745,7 @@ func TestAPIMultiOwnerSet_PopAPIKey(t *testing.T) { { name: "OneApi/MultiOwner", s: map[opregistry.APIKey]OperatorSet{ - opregistry.APIKey{"g", "v", "k", "p"}: map[string]OperatorSurface{ + {Group: "g", Version: "v", Kind: "k", Plural: "p"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, "owner2": &Operator{name: "op2"}, }, @@ -754,11 +754,11 @@ func TestAPIMultiOwnerSet_PopAPIKey(t *testing.T) { { name: "MultipleApi/MultiOwner", s: map[opregistry.APIKey]OperatorSet{ - opregistry.APIKey{"g", "v", "k", "p"}: map[string]OperatorSurface{ + {Group: "g", Version: "v", Kind: "k", Plural: "p"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, "owner2": &Operator{name: "op2"}, }, - opregistry.APIKey{"g2", "v2", "k2", "p2"}: map[string]OperatorSurface{ + {Group: "g2", Version: "v2", Kind: "k2", Plural: "p2"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, "owner2": &Operator{name: "op2"}, }, @@ -797,7 +797,7 @@ func TestAPIMultiOwnerSet_PopAPIRequirers(t *testing.T) { { name: "OneApi/OneOwner", s: map[opregistry.APIKey]OperatorSet{ - opregistry.APIKey{"g", "v", "k", "p"}: map[string]OperatorSurface{ + {Group: "g", Version: "v", Kind: "k", Plural: "p"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, }, }, @@ -808,7 +808,7 @@ func TestAPIMultiOwnerSet_PopAPIRequirers(t *testing.T) { { name: "OneApi/MultiOwner", s: map[opregistry.APIKey]OperatorSet{ - opregistry.APIKey{"g", "v", "k", "p"}: map[string]OperatorSurface{ + {Group: "g", Version: "v", Kind: "k", Plural: "p"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, "owner2": &Operator{name: "op2"}, }, @@ -821,11 +821,11 @@ func TestAPIMultiOwnerSet_PopAPIRequirers(t *testing.T) { { name: "MultipleApi/MultiOwner", s: map[opregistry.APIKey]OperatorSet{ - opregistry.APIKey{"g", "v", "k", "p"}: map[string]OperatorSurface{ + {Group: "g", Version: "v", Kind: "k", Plural: "p"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, "owner2": &Operator{name: "op2"}, }, - opregistry.APIKey{"g2", "v2", "k2", "p2"}: map[string]OperatorSurface{ + {Group: "g2", Version: "v2", Kind: "k2", Plural: "p2"}: map[string]OperatorSurface{ "owner1": &Operator{name: "op1"}, "owner2": &Operator{name: "op2"}, }, @@ -879,7 +879,7 @@ func TestOperatorSourceInfo_String(t *testing.T) { i := &OperatorSourceInfo{ Package: tt.fields.Package, Channel: tt.fields.Channel, - Catalog: registry.CatalogKey{tt.fields.CatalogSource, tt.fields.CatalogSourceNamespace}, + Catalog: registry.CatalogKey{Name: tt.fields.CatalogSource, Namespace: tt.fields.CatalogSourceNamespace}, } if got := i.String(); got != tt.want { t.Errorf("OperatorSourceInfo.String() = %v, want %v", got, tt.want) @@ -889,7 +889,7 @@ func TestOperatorSourceInfo_String(t *testing.T) { } func TestNewOperatorFromBundle(t *testing.T) { - version := opver.OperatorVersion{semver.MustParse("0.1.0-abc")} + version := opver.OperatorVersion{Version: semver.MustParse("0.1.0-abc")} csv := v1alpha1.ClusterServiceVersion{ TypeMeta: metav1.TypeMeta{ Kind: v1alpha1.ClusterServiceVersionKind, @@ -1087,7 +1087,7 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{"source", "testNamespace"}, + Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1137,14 +1137,12 @@ func TestNewOperatorFromBundle(t *testing.T) { Type: "olm.gvk", Value: "{\"group\":\"apis.group.com\",\"kind\":\"OwnedAPI\",\"version\":\"v1\"}", }, - }, - dependencies: []*api.Dependency{ { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"crd.group.com\",\"kind\":\"RequiredCRD\",\"version\":\"v1\"}", }, { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"apis.group.com\",\"kind\":\"RequiredAPI\",\"version\":\"v1\"}", }, }, @@ -1152,7 +1150,7 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{"source", "testNamespace"}, + Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1172,7 +1170,7 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{"source", "testNamespace"}, + Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1221,14 +1219,12 @@ func TestNewOperatorFromBundle(t *testing.T) { Type: "olm.gvk", Value: "{\"group\":\"apis.group.com\",\"kind\":\"OwnedAPI\",\"version\":\"v1\"}", }, - }, - dependencies: []*api.Dependency{ { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"apis.group.com\",\"kind\":\"RequiredAPI\",\"version\":\"v1\"}", }, { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"crd.group.com\",\"kind\":\"RequiredCRD\",\"version\":\"v1\"}", }, }, @@ -1237,7 +1233,7 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{"source", "testNamespace"}, + Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1257,7 +1253,7 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{"source", "testNamespace"}, + Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, DefaultChannel: true, }, }, @@ -1278,7 +1274,7 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{"source", "testNamespace"}, + Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1302,14 +1298,12 @@ func TestNewOperatorFromBundle(t *testing.T) { Type: "olm.gvk", Value: "{\"group\":\"apis.group.com\",\"kind\":\"OwnedAPI\",\"version\":\"v1\"}", }, - }, - dependencies: []*api.Dependency{ { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"crd.group.com\",\"kind\":\"RequiredCRD\",\"version\":\"v1\"}", }, { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"apis.group.com\",\"kind\":\"RequiredAPI\",\"version\":\"v1\"}", }, }, @@ -1317,7 +1311,7 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{"source", "testNamespace"}, + Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1326,16 +1320,15 @@ func TestNewOperatorFromBundle(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got, err := NewOperatorFromBundle(tt.args.bundle, "", tt.args.sourceKey, tt.args.defaultChannel) require.Equal(t, tt.wantErr, err) - require.ElementsMatch(t, tt.want.dependencies, got.dependencies) - require.ElementsMatch(t, tt.want.properties, got.properties) - tt.want.properties, tt.want.dependencies, got.dependencies, got.properties = nil, nil, nil, nil + requirePropertiesEqual(t, tt.want.properties, got.properties) + tt.want.properties, got.properties = nil, nil require.Equal(t, tt.want, got) }) } } func TestNewOperatorFromCSV(t *testing.T) { - version := opver.OperatorVersion{semver.MustParse("0.1.0-abc")} + version := opver.OperatorVersion{Version: semver.MustParse("0.1.0-abc")} type args struct { csv *v1alpha1.ClusterServiceVersion } @@ -1455,13 +1448,13 @@ func TestNewOperatorFromCSV(t *testing.T) { {Group: "g", Version: "v1", Kind: "APIKind", Plural: "apikinds"}: {}, {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: {}, }, - dependencies: []*api.Dependency{ + properties: []*api.Property{ { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"g\",\"kind\":\"APIKind\",\"version\":\"v1\"}", }, { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"g\",\"kind\":\"CRDKind\",\"version\":\"v1\"}", }, }, @@ -1534,14 +1527,12 @@ func TestNewOperatorFromCSV(t *testing.T) { Type: "olm.gvk", Value: "{\"group\":\"g\",\"kind\":\"CRDOwnedKind\",\"version\":\"v1\"}", }, - }, - dependencies: []*api.Dependency{ { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"g2\",\"kind\":\"APIReqKind\",\"version\":\"v1\"}", }, { - Type: "olm.gvk", + Type: "olm.gvk.required", Value: "{\"group\":\"g2\",\"kind\":\"CRDReqKind\",\"version\":\"v1\"}", }, }, @@ -1554,10 +1545,34 @@ func TestNewOperatorFromCSV(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got, err := NewOperatorFromV1Alpha1CSV(tt.args.csv) require.Equal(t, tt.wantErr, err) - require.ElementsMatch(t, tt.want.dependencies, got.dependencies) - require.ElementsMatch(t, tt.want.properties, got.properties) - tt.want.properties, tt.want.dependencies, got.dependencies, got.properties = nil, nil, nil, nil + requirePropertiesEqual(t, tt.want.properties, got.properties) + tt.want.properties, got.properties = nil, nil require.Equal(t, tt.want, got) }) } } + +func requirePropertiesEqual(t *testing.T, a, b []*api.Property) { + type Property struct { + Type string + Value interface{} + } + nice := func(in *api.Property) Property { + var i interface{} + if err := json.Unmarshal([]byte(in.Value), &i); err != nil { + t.Fatalf("property value %q could not be unmarshaled as json: %s", in.Value, err) + } + return Property{ + Type: in.Type, + Value: i, + } + } + var l, r []Property + for _, p := range a { + l = append(l, nice(p)) + } + for _, p := range b { + r = append(r, nice(p)) + } + require.ElementsMatch(t, l, r) +} diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/projection/properties_test.go b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/projection/properties_test.go index 6c71a82a7f..acde5acd76 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/projection/properties_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/projection/properties_test.go @@ -54,6 +54,16 @@ func TestPropertiesAnnotationFromPropertyList(t *testing.T) { }, expected: `{"properties":[{"type":"string","value":"hello"},{"type":"number","value":5},{"type":"array","value":[1,"two",3,"four"]},{"type":"object","value":{"hello":{"worl":"d"}}}]}`, }, + { + name: "unquoted string", + properties: []*api.Property{ + { + Type: "version", + Value: "4.8", + }, + }, + expected: `{"properties":[{"type":"version","value":4.8}]}`, + }, } { t.Run(tc.name, func(t *testing.T) { actual, err := projection.PropertiesAnnotationFromPropertyList(tc.properties) @@ -120,12 +130,23 @@ func TestPropertyListFromPropertiesAnnotation(t *testing.T) { { Type: "array", Value: `[1,"two",3,"four"]`, - }, { + }, + { Type: "object", Value: `{"hello":{"worl":"d"}}`, }, }, }, + { + name: "unquoted string values", + annotation: `{"properties":[{"type": "version","value": 4.8}]}`, + expected: []*api.Property{ + { + Type: "version", + Value: "4.8", + }, + }, + }, } { t.Run(tc.name, func(t *testing.T) { actual, err := projection.PropertyListFromPropertiesAnnotation(tc.annotation) diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go index e2ee4a66e0..668f9be04f 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "sort" + "strings" "github.com/blang/semver/v4" "github.com/sirupsen/logrus" @@ -55,12 +56,16 @@ func (r *SatResolver) SolveOperators(namespaces []string, csvs []*v1alpha1.Clust startingCSVs := make(map[string]struct{}) // build a virtual catalog of all currently installed CSVs - existingSnapshot, existingInstallables, err := r.newSnapshotForNamespace(namespaces[0], subs, csvs) + existingSnapshot, err := r.newSnapshotForNamespace(namespaces[0], subs, csvs) if err != nil { return nil, err } namespacedCache := r.cache.Namespaced(namespaces...).WithExistingOperators(existingSnapshot) + _, existingInstallables, err := r.getBundleInstallables(registry.NewVirtualCatalogKey(namespaces[0]), existingSnapshot.Find(), namespacedCache, visited) + if err != nil { + return nil, err + } for _, i := range existingInstallables { installables[i.Identifier()] = i } @@ -233,7 +238,7 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu if i != len(bundles) && bundles[i].bundle.ChannelName == lastChannel { continue } - channel, err := r.sortChannel(bundles[lastIndex:i]) + channel, err := sortChannel(bundles[lastIndex:i]) if err != nil { return nil, err } @@ -248,7 +253,8 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu candidates := make([]*BundleInstallable, 0) for _, o := range Filter(sortedBundles, channelPredicates...) { predicates := append(cachePredicates, WithCSVName(o.Identifier())) - id, installable, err := r.getBundleInstallables(catalog, predicates, namespacedCache, visited) + stack := namespacedCache.Catalog(catalog).Find(predicates...) + id, installable, err := r.getBundleInstallables(catalog, stack, namespacedCache, visited) if err != nil { return nil, err } @@ -292,17 +298,10 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu return installables, nil } -func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, predicates []OperatorPredicate, namespacedCache MultiCatalogOperatorFinder, visited map[OperatorSurface]*BundleInstallable) (map[solver.Identifier]struct{}, map[solver.Identifier]*BundleInstallable, error) { +func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, bundleStack []*Operator, namespacedCache MultiCatalogOperatorFinder, visited map[OperatorSurface]*BundleInstallable) (map[solver.Identifier]struct{}, map[solver.Identifier]*BundleInstallable, error) { errs := make([]error, 0) installables := make(map[solver.Identifier]*BundleInstallable, 0) // all installables, including dependencies - var finder OperatorFinder = namespacedCache - if !catalog.Empty() { - finder = namespacedCache.Catalog(catalog) - } - - bundleStack := finder.Find(predicates...) - // track the first layer of installable ids var initial = make(map[*Operator]struct{}) for _, o := range bundleStack { @@ -353,11 +352,18 @@ func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, predica } sources[*si] = struct{}{} - sourcePredicate = Or(sourcePredicate, And( - WithPackage(si.Package), - WithChannel(si.Channel), - WithCatalog(si.Catalog), - )) + if si.Catalog.Virtual() { + sourcePredicate = Or(sourcePredicate, And( + WithCSVName(b.Identifier()), + WithCatalog(si.Catalog), + )) + } else { + sourcePredicate = Or(sourcePredicate, And( + WithPackage(si.Package), + WithChannel(si.Channel), + WithCatalog(si.Catalog), + )) + } } sortedBundles, err := r.sortBundles(namespacedCache.FindPreferred(&bundle.sourceInfo.Catalog, sourcePredicate)) if err != nil { @@ -441,15 +447,14 @@ func (r *SatResolver) inferProperties(csv *v1alpha1.ClusterServiceVersion, subs return properties, nil } -func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1.Subscription, csvs []*v1alpha1.ClusterServiceVersion) (*CatalogSnapshot, []solver.Installable, error) { - installables := make([]solver.Installable, 0) +func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1.Subscription, csvs []*v1alpha1.ClusterServiceVersion) (*CatalogSnapshot, error) { existingOperatorCatalog := registry.NewVirtualCatalogKey(namespace) // build a catalog snapshot of CSVs without subscriptions - csvsWithSubscriptions := make(map[*v1alpha1.ClusterServiceVersion]struct{}) + csvSubscriptions := make(map[*v1alpha1.ClusterServiceVersion]*v1alpha1.Subscription) for _, sub := range subs { for _, csv := range csvs { if csv.Name == sub.Status.InstalledCSV { - csvsWithSubscriptions[csv] = struct{}{} + csvSubscriptions[csv] = sub break } } @@ -457,17 +462,9 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 var csvsMissingProperties []*v1alpha1.ClusterServiceVersion standaloneOperators := make([]*Operator, 0) for _, csv := range csvs { - var constraints []solver.Constraint - if _, ok := csvsWithSubscriptions[csv]; !ok { - // CSVs already associated with a Subscription - // may be replaced, but freestanding CSVs must - // appear in any solution. - constraints = append(constraints, solver.Mandatory()) - } - op, err := NewOperatorFromV1Alpha1CSV(csv) if err != nil { - return nil, nil, err + return nil, err } if anno, ok := csv.GetAnnotations()[projection.PropertiesAnnotationKey]; !ok { @@ -478,13 +475,14 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 op.properties = append(op.properties, inferred...) } } else if props, err := projection.PropertyListFromPropertiesAnnotation(anno); err != nil { - return nil, nil, fmt.Errorf("failed to retrieve properties of csv %q: %w", csv.GetName(), err) + return nil, fmt.Errorf("failed to retrieve properties of csv %q: %w", csv.GetName(), err) } else { op.properties = props } op.sourceInfo = &OperatorSourceInfo{ - Catalog: existingOperatorCatalog, + Catalog: existingOperatorCatalog, + Subscription: csvSubscriptions[csv], } // Try to determine source package name from properties and add to SourceInfo. for _, p := range op.properties { @@ -501,10 +499,6 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 } standaloneOperators = append(standaloneOperators, op) - - // all standalone operators are mandatory - i := NewBundleInstallable(op.Identifier(), "", existingOperatorCatalog, constraints...) - installables = append(installables, &i) } if len(csvsMissingProperties) > 0 { @@ -515,7 +509,7 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 r.log.Infof("considered csvs without properties annotation during resolution: %v", names) } - return NewRunningOperatorSnapshot(r.log, existingOperatorCatalog, standaloneOperators), installables, nil + return NewRunningOperatorSnapshot(r.log, existingOperatorCatalog, standaloneOperators), nil } func (r *SatResolver) addInvariants(namespacedCache MultiCatalogOperatorFinder, installables map[solver.Identifier]solver.Installable) { @@ -617,7 +611,7 @@ func (r *SatResolver) sortBundles(bundles []*Operator) ([]*Operator, error) { return pi.Channel < pj.Channel }) for channel := range partitionedBundles[catalog] { - sorted, err := r.sortChannel(partitionedBundles[catalog][channel]) + sorted, err := sortChannel(partitionedBundles[catalog][channel]) if err != nil { return nil, err } @@ -633,13 +627,15 @@ func (r *SatResolver) sortBundles(bundles []*Operator) ([]*Operator, error) { return all, nil } -// sorts bundle in a channel by replaces -func (r *SatResolver) sortChannel(bundles []*Operator) ([]*Operator, error) { - if len(bundles) <= 1 { +// Sorts bundle in a channel by replaces. All entries in the argument +// are assumed to have the same Package and Channel. +func sortChannel(bundles []*Operator) ([]*Operator, error) { + if len(bundles) < 1 { return bundles, nil } - channel := []*Operator{} + packageName := bundles[0].Package() + channelName := bundles[0].Channel() bundleLookup := map[string]*Operator{} @@ -667,44 +663,60 @@ func (r *SatResolver) sortChannel(bundles []*Operator) ([]*Operator, error) { } } - // a bundle without a replacement is a channel head, but if we find more than one of those something is weird + // a bundle without a replacement is a channel head, but if we + // find more than one of those something is weird headCandidates := []*Operator{} for _, b := range bundles { if _, ok := replacedBy[b]; !ok { headCandidates = append(headCandidates, b) } } + if len(headCandidates) == 0 { + return nil, fmt.Errorf("no channel heads (entries not replaced by another entry) found in channel %q of package %q", channelName, packageName) + } - if len(headCandidates) > 1 { - var names []string - for _, v := range headCandidates { - names = append(names, v.Identifier()) + var chains [][]*Operator + for _, head := range headCandidates { + var chain []*Operator + visited := make(map[*Operator]struct{}) + current := head + for { + visited[current] = struct{}{} + if _, ok := skipped[current.Identifier()]; !ok { + chain = append(chain, current) + } + next, ok := replaces[current] + if !ok { + break + } + if _, ok := visited[next]; ok { + return nil, fmt.Errorf("a cycle exists in the chain of replacement beginning with %q in channel %q of package %q", head.Identifier(), channelName, packageName) + } + current = next } - return nil, fmt.Errorf("found multiple channel heads: %v, please check the `replaces`/`skipRange` fields of the operator bundles", names) - - } else if len(headCandidates) < 1 { - return nil, fmt.Errorf("head of channel not found") + chains = append(chains, chain) } - head := headCandidates[0] - current := head - skip := false - for { - if skip == false { - channel = append(channel, current) - } - skip = false - next, ok := replaces[current] - if !ok { - break - } - if _, ok := skipped[current.Identifier()]; ok { - skip = true + if len(chains) > 1 { + schains := make([]string, len(chains)) + for i, chain := range chains { + switch len(chain) { + case 0: + schains[i] = "[]" // Bug? + case 1: + schains[i] = chain[0].Identifier() + default: + schains[i] = fmt.Sprintf("%s...%s", chain[0].Identifier(), chain[len(chain)-1].Identifier()) + } } - current = next + return nil, fmt.Errorf("a unique replacement chain within a channel is required to determine the relative order between channel entries, but %d replacement chains were found in channel %q of package %q: %s", len(schains), channelName, packageName, strings.Join(schains, ", ")) } - // TODO: do we care if the channel doesn't include every bundle in the input? + if len(chains) == 0 { + // Bug? + return nil, fmt.Errorf("found no replacement chains in channel %q of package %q", channelName, packageName) + } - return channel, nil + // TODO: do we care if the channel doesn't include every bundle in the input? + return chains[0], nil } diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver_test.go b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver_test.go index 0472ef65fe..b954425930 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver_test.go @@ -1,6 +1,7 @@ package resolver import ( + "errors" "fmt" "testing" @@ -21,11 +22,11 @@ import ( ) func TestSolveOperators(t *testing.T) { - APISet := APISet{opregistry.APIKey{"g", "v", "k", "ks"}: struct{}{}} + APISet := APISet{opregistry.APIKey{Group: "g", Version: "v", Kind: "k", Plural: "ks"}: struct{}{}} Provides := APISet - namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + const namespace = "test-namespace" + catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -35,17 +36,11 @@ func TestSolveOperators(t *testing.T) { fakeNamespacedOperatorCache := NamespacedOperatorCache{ snapshots: map[registry.CatalogKey]*CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, + catalog: { + key: catalog, operators: []*Operator{ - genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), - genOperator("packageB.v1", "1.0.1", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), + genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), + genOperator("packageB.v1", "1.0.1", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), }, }, }, @@ -55,16 +50,44 @@ func TestSolveOperators(t *testing.T) { log: logrus.New(), } - operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) + operators, err := satResolver.SolveOperators([]string{namespace}, csvs, subs) assert.NoError(t, err) expected := OperatorSet{ - "packageA.v1": genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), - "packageB.v1": genOperator("packageB.v1", "1.0.1", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), + "packageB.v1": genOperator("packageB.v1", "1.0.1", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), } require.EqualValues(t, expected, operators) } +func TestDisjointChannelGraph(t *testing.T) { + const namespace = "test-namespace" + catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + + newSub := newSub(namespace, "packageA", "alpha", catalog) + subs := []*v1alpha1.Subscription{newSub} + + fakeNamespacedOperatorCache := NamespacedOperatorCache{ + snapshots: map[registry.CatalogKey]*CatalogSnapshot{ + catalog: { + key: catalog, + operators: []*Operator{ + genOperator("packageA.side1.v1", "0.0.1", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), + genOperator("packageA.side1.v2", "0.0.2", "packageA.side1.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), + genOperator("packageA.side2.v1", "1.0.0", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), + genOperator("packageA.side2.v2", "2.0.0", "packageA.side2.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), + }, + }, + }, + } + satResolver := SatResolver{ + cache: getFakeOperatorCache(fakeNamespacedOperatorCache), + log: logrus.New(), + } + + _, err := satResolver.SolveOperators([]string{namespace}, nil, subs) + require.Error(t, err, "a unique replacement chain within a channel is required to determine the relative order between channel entries, but 2 replacement chains were found in channel \"alpha\" of package \"packageA\": packageA.side1.v2...packageA.side1.v1, packageA.side2.v2...packageA.side2.v1") +} + func TestPropertiesAnnotationHonored(t *testing.T) { const ( namespace = "olm" @@ -117,16 +140,10 @@ func TestSolveOperators_MultipleChannels(t *testing.T) { fakeNamespacedOperatorCache := NamespacedOperatorCache{ snapshots: map[registry.CatalogKey]*CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, + catalog: { + key: catalog, operators: []*Operator{ - genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), + genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "beta", "community", "olm", nil, nil, nil, "", false), }, @@ -141,10 +158,9 @@ func TestSolveOperators_MultipleChannels(t *testing.T) { operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) assert.NoError(t, err) expected := OperatorSet{ - "packageA.v1": genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), "packageB.v1": genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), } - assert.Equal(t, 2, len(operators)) + assert.Len(t, operators, 1) for k, e := range expected { assert.EqualValues(t, e, operators[k]) } @@ -373,7 +389,7 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { Name: "community", }, operators: []*Operator{ - genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", namespace, nil, + genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", namespace, nil, nil, opToAddVersionDeps, "", false), }, }, @@ -415,7 +431,7 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { operators, err := satResolver.SolveOperators([]string{"olm"}, []*v1alpha1.ClusterServiceVersion{}, subs) assert.NoError(t, err) expected := OperatorSet{ - "packageA.v1": genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", + "packageA.v1": genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, opToAddVersionDeps, "", false), "packageB.v1": genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "high-priority-operator", "olm", nil, nil, nil, "", false), @@ -448,7 +464,7 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { operators, err = satResolver.SolveOperators([]string{"olm"}, []*v1alpha1.ClusterServiceVersion{}, subs) assert.NoError(t, err) expected = OperatorSet{ - "packageA.v1": genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", + "packageA.v1": genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, opToAddVersionDeps, "", false), "packageB.v1": genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "community-operator", "olm", nil, nil, nil, "", false), @@ -468,7 +484,7 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { Name: "community", }, operators: []*Operator{ - genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", namespace, nil, + genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", namespace, nil, nil, opToAddVersionDeps, "", false), genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "community", namespace, nil, nil, nil, "", false), @@ -482,7 +498,7 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { operators, err = satResolver.SolveOperators([]string{"olm"}, []*v1alpha1.ClusterServiceVersion{}, subs) assert.NoError(t, err) expected = OperatorSet{ - "packageA.v1": genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", + "packageA.v1": genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, opToAddVersionDeps, "", false), "packageB.v1": genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), @@ -1298,8 +1314,19 @@ func getFakeOperatorCache(fakedNamespacedOperatorCache NamespacedOperatorCache) func genOperator(name, version, replaces, pkg, channel, catalogName, catalogNamespace string, requiredAPIs, providedAPIs APISet, dependencies []*api.Dependency, defaultChannel string, deprecated bool) *Operator { semversion, _ := semver.Make(version) + properties := apiSetToProperties(providedAPIs, nil, deprecated) if len(dependencies) == 0 { - dependencies = apiSetToDependencies(requiredAPIs, nil) + ps, err := requiredAPIsToProperties(requiredAPIs) + if err != nil { + panic(err) + } + properties = append(properties, ps...) + } else { + ps, err := legacyDependenciesToProperties(dependencies) + if err != nil { + panic(err) + } + properties = append(properties, ps...) } o := &Operator{ name: name, @@ -1309,10 +1336,9 @@ func genOperator(name, version, replaces, pkg, channel, catalogName, catalogName PackageName: pkg, ChannelName: channel, Dependencies: dependencies, - Properties: apiSetToProperties(providedAPIs, nil, deprecated), + Properties: properties, }, - dependencies: dependencies, - properties: apiSetToProperties(providedAPIs, nil, deprecated), + properties: properties, sourceInfo: &OperatorSourceInfo{ Catalog: registry.CatalogKey{ Name: catalogName, @@ -1346,7 +1372,7 @@ func TestSolveOperators_WithoutDeprecated(t *testing.T) { catalog: { key: catalog, operators: []*Operator{ - genOperator("packageA.v1", "0.0.1", "packageA.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", true), + genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", true), }, }, }, @@ -1413,9 +1439,9 @@ func TestSolveOperators_WithSkipsAndStartingCSV(t *testing.T) { op1 := genOperator("packageA.v1", "1.0.0", "", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) op2 := genOperator("packageA.v2", "2.0.0", "packageA.v1", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) op3 := genOperator("packageA.v3", "3.0.0", "packageA.v2", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) - op4 := genOperator("packageA.v4", "4.0.0", "packageA.v2", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) + op4 := genOperator("packageA.v4", "4.0.0", "packageA.v3", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) op4.skips = []string{"packageA.v3"} - op5 := genOperator("packageA.v5", "5.0.0", "packageA.v1", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) + op5 := genOperator("packageA.v5", "5.0.0", "packageA.v4", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) op5.skips = []string{"packageA.v2", "packageA.v3", "packageA.v4"} op6 := genOperator("packageA.v6", "6.0.0", "packageA.v5", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) @@ -1451,26 +1477,20 @@ func TestSolveOperators_WithSkipsAndStartingCSV(t *testing.T) { } func TestSolveOperators_WithSkips(t *testing.T) { - namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + const namespace = "test-namespace" + catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} newSub := newSub(namespace, "packageB", "alpha", catalog) subs := []*v1alpha1.Subscription{newSub} - opB := genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false) - opB2 := genOperator("packageB.v2", "2.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false) + opB := genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false) + opB2 := genOperator("packageB.v2", "2.0.0", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false) opB2.skips = []string{"packageB.v1"} fakeNamespacedOperatorCache := NamespacedOperatorCache{ snapshots: map[registry.CatalogKey]*CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, + catalog: { + key: catalog, operators: []*Operator{ opB, opB2, }, @@ -1482,7 +1502,7 @@ func TestSolveOperators_WithSkips(t *testing.T) { log: logrus.New(), } - operators, err := satResolver.SolveOperators([]string{"olm"}, nil, subs) + operators, err := satResolver.SolveOperators([]string{namespace}, nil, subs) assert.NoError(t, err) expected := OperatorSet{ "packageB.v2": opB2, @@ -1490,6 +1510,77 @@ func TestSolveOperators_WithSkips(t *testing.T) { require.EqualValues(t, expected, operators) } +func TestSolveOperatorsWithSkipsPreventingSelection(t *testing.T) { + const namespace = "test-namespace" + catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + gvks := APISet{opregistry.APIKey{Group: "g", Version: "v", Kind: "k", Plural: "ks"}: struct{}{}} + + // Subscription candidate a-1 requires a GVK provided + // exclusively by b-1, but b-1 is skipped by b-3 and can't be + // chosen. + subs := []*v1alpha1.Subscription{newSub(namespace, "a", "channel", catalog)} + a1 := genOperator("a-1", "1.0.0", "", "a", "channel", catalog.Name, catalog.Namespace, gvks, nil, nil, "", false) + b3 := genOperator("b-3", "3.0.0", "b-2", "b", "channel", catalog.Name, catalog.Namespace, nil, nil, nil, "", false) + b3.skips = []string{"b-1"} + b2 := genOperator("b-2", "2.0.0", "b-1", "b", "channel", catalog.Name, catalog.Namespace, nil, nil, nil, "", false) + b1 := genOperator("b-1", "1.0.0", "", "b", "channel", catalog.Name, catalog.Namespace, nil, gvks, nil, "", false) + + logger, _ := test.NewNullLogger() + satResolver := SatResolver{ + cache: getFakeOperatorCache(NamespacedOperatorCache{ + snapshots: map[registry.CatalogKey]*CatalogSnapshot{ + catalog: { + key: catalog, + operators: []*Operator{a1, b3, b2, b1}, + }, + }, + }), + log: logger, + } + + _, err := satResolver.SolveOperators([]string{namespace}, nil, subs) + assert.IsType(t, solver.NotSatisfiable{}, err) +} + +func TestSolveOperatorsWithClusterServiceVersionHavingDependency(t *testing.T) { + const namespace = "test-namespace" + catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + + a1 := existingOperator(namespace, "a-1", "a", "default", "", nil, nil, nil, nil) + a1.Annotations = map[string]string{ + "operatorframework.io/properties": `{"properties":[{"type":"olm.package.required","value":{"packageName":"b","versionRange":"1.0.0"}}]}`, + } + + b1 := existingOperator(namespace, "b-1", "b", "default", "", nil, nil, nil, nil) + b1.Annotations = map[string]string{ + "operatorframework.io/properties": `{"properties":[{"type":"olm.package","value":{"packageName":"b","version":"1.0.0"}}]}`, + } + + csvs := []*v1alpha1.ClusterServiceVersion{a1, b1} + subs := []*v1alpha1.Subscription{ + existingSub(namespace, "b-1", "b", "default", catalog), + } + + log, _ := test.NewNullLogger() + r := SatResolver{ + cache: getFakeOperatorCache(NamespacedOperatorCache{ + snapshots: map[registry.CatalogKey]*CatalogSnapshot{ + catalog: { + key: catalog, + operators: []*Operator{ + genOperator("b-2", "2.0.0", "b-1", "b", "default", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), + }, + }, + }, + }), + log: log, + } + + operators, err := r.SolveOperators([]string{namespace}, csvs, subs) + assert.NoError(t, err) + require.Empty(t, operators) +} + func TestInferProperties(t *testing.T) { catalog := registry.CatalogKey{Namespace: "namespace", Name: "name"} @@ -1692,3 +1783,194 @@ func TestInferProperties(t *testing.T) { }) } } + +func TestSortChannel(t *testing.T) { + for _, tc := range []struct { + Name string + In []*Operator + Out []*Operator + Err error + }{ + { + Name: "wrinkle-free", + In: []*Operator{ + { + name: "b", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + { + name: "a", + replaces: "b", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + }, + Out: []*Operator{ + { + name: "a", + replaces: "b", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + { + name: "b", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + }, + }, + { + Name: "empty", + In: nil, + Out: nil, + }, + { + Name: "replacement cycle", + In: []*Operator{ + { + name: "a", + replaces: "b", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + { + name: "b", + replaces: "a", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + }, + Err: errors.New(`no channel heads (entries not replaced by another entry) found in channel "channel" of package "package"`), + }, + { + Name: "replacement cycle", + In: []*Operator{ + { + name: "a", + replaces: "b", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + { + name: "b", + replaces: "c", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + { + name: "c", + replaces: "b", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + }, + Err: errors.New(`a cycle exists in the chain of replacement beginning with "a" in channel "channel" of package "package"`), + }, + { + Name: "skipped and replaced entry omitted", + In: []*Operator{ + { + name: "a", + replaces: "b", + skips: []string{"b"}, + }, + { + name: "b", + }, + }, + Out: []*Operator{ + { + name: "a", + replaces: "b", + skips: []string{"b"}, + }, + }, + }, + { + Name: "skipped entry omitted", + In: []*Operator{ + { + name: "a", + replaces: "b", + skips: []string{"c"}, + }, + { + name: "b", + replaces: "c", + }, + { + name: "c", + }, + }, + Out: []*Operator{ + { + name: "a", + replaces: "b", + skips: []string{"c"}, + }, + { + name: "b", + replaces: "c", + }, + }, + }, + { + Name: "two replaces chains", + In: []*Operator{ + { + name: "a", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + { + name: "b", + replaces: "c", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + { + name: "c", + bundle: &api.Bundle{ + PackageName: "package", + ChannelName: "channel", + }, + }, + }, + Err: errors.New(`a unique replacement chain within a channel is required to determine the relative order between channel entries, but 2 replacement chains were found in channel "channel" of package "package": a, b...c`), + }, + } { + t.Run(tc.Name, func(t *testing.T) { + assert := assert.New(t) + actual, err := sortChannel(tc.In) + if tc.Err == nil { + assert.NoError(err) + } else { + assert.EqualError(err, tc.Err.Error()) + } + assert.Equal(tc.Out, actual) + }) + } +} diff --git a/staging/operator-lifecycle-manager/pkg/fakes/fake_registry_store.go b/staging/operator-lifecycle-manager/pkg/fakes/fake_registry_store.go index 8d27b36632..5894a426ae 100644 --- a/staging/operator-lifecycle-manager/pkg/fakes/fake_registry_store.go +++ b/staging/operator-lifecycle-manager/pkg/fakes/fake_registry_store.go @@ -57,6 +57,20 @@ type FakeQuery struct { result1 *api.Bundle result2 error } + GetBundlePathIfExistsStub func(context.Context, string) (string, error) + getBundlePathIfExistsMutex sync.RWMutex + getBundlePathIfExistsArgsForCall []struct { + arg1 context.Context + arg2 string + } + getBundlePathIfExistsReturns struct { + result1 string + result2 error + } + getBundlePathIfExistsReturnsOnCall map[int]struct { + result1 string + result2 error + } GetBundlePathsForPackageStub func(context.Context, string) ([]string, error) getBundlePathsForPackageMutex sync.RWMutex getBundlePathsForPackageArgsForCall []struct { @@ -331,6 +345,19 @@ type FakeQuery struct { result1 []string result2 error } + ListRegistryBundlesStub func(context.Context) ([]*registry.Bundle, error) + listRegistryBundlesMutex sync.RWMutex + listRegistryBundlesArgsForCall []struct { + arg1 context.Context + } + listRegistryBundlesReturns struct { + result1 []*registry.Bundle + result2 error + } + listRegistryBundlesReturnsOnCall map[int]struct { + result1 []*registry.Bundle + result2 error + } ListTablesStub func(context.Context) ([]string, error) listTablesMutex sync.RWMutex listTablesArgsForCall []struct { @@ -546,6 +573,70 @@ func (fake *FakeQuery) GetBundleForChannelReturnsOnCall(i int, result1 *api.Bund }{result1, result2} } +func (fake *FakeQuery) GetBundlePathIfExists(arg1 context.Context, arg2 string) (string, error) { + fake.getBundlePathIfExistsMutex.Lock() + ret, specificReturn := fake.getBundlePathIfExistsReturnsOnCall[len(fake.getBundlePathIfExistsArgsForCall)] + fake.getBundlePathIfExistsArgsForCall = append(fake.getBundlePathIfExistsArgsForCall, struct { + arg1 context.Context + arg2 string + }{arg1, arg2}) + fake.recordInvocation("GetBundlePathIfExists", []interface{}{arg1, arg2}) + fake.getBundlePathIfExistsMutex.Unlock() + if fake.GetBundlePathIfExistsStub != nil { + return fake.GetBundlePathIfExistsStub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + fakeReturns := fake.getBundlePathIfExistsReturns + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeQuery) GetBundlePathIfExistsCallCount() int { + fake.getBundlePathIfExistsMutex.RLock() + defer fake.getBundlePathIfExistsMutex.RUnlock() + return len(fake.getBundlePathIfExistsArgsForCall) +} + +func (fake *FakeQuery) GetBundlePathIfExistsCalls(stub func(context.Context, string) (string, error)) { + fake.getBundlePathIfExistsMutex.Lock() + defer fake.getBundlePathIfExistsMutex.Unlock() + fake.GetBundlePathIfExistsStub = stub +} + +func (fake *FakeQuery) GetBundlePathIfExistsArgsForCall(i int) (context.Context, string) { + fake.getBundlePathIfExistsMutex.RLock() + defer fake.getBundlePathIfExistsMutex.RUnlock() + argsForCall := fake.getBundlePathIfExistsArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeQuery) GetBundlePathIfExistsReturns(result1 string, result2 error) { + fake.getBundlePathIfExistsMutex.Lock() + defer fake.getBundlePathIfExistsMutex.Unlock() + fake.GetBundlePathIfExistsStub = nil + fake.getBundlePathIfExistsReturns = struct { + result1 string + result2 error + }{result1, result2} +} + +func (fake *FakeQuery) GetBundlePathIfExistsReturnsOnCall(i int, result1 string, result2 error) { + fake.getBundlePathIfExistsMutex.Lock() + defer fake.getBundlePathIfExistsMutex.Unlock() + fake.GetBundlePathIfExistsStub = nil + if fake.getBundlePathIfExistsReturnsOnCall == nil { + fake.getBundlePathIfExistsReturnsOnCall = make(map[int]struct { + result1 string + result2 error + }) + } + fake.getBundlePathIfExistsReturnsOnCall[i] = struct { + result1 string + result2 error + }{result1, result2} +} + func (fake *FakeQuery) GetBundlePathsForPackage(arg1 context.Context, arg2 string) ([]string, error) { fake.getBundlePathsForPackageMutex.Lock() ret, specificReturn := fake.getBundlePathsForPackageReturnsOnCall[len(fake.getBundlePathsForPackageArgsForCall)] @@ -1770,6 +1861,69 @@ func (fake *FakeQuery) ListPackagesReturnsOnCall(i int, result1 []string, result }{result1, result2} } +func (fake *FakeQuery) ListRegistryBundles(arg1 context.Context) ([]*registry.Bundle, error) { + fake.listRegistryBundlesMutex.Lock() + ret, specificReturn := fake.listRegistryBundlesReturnsOnCall[len(fake.listRegistryBundlesArgsForCall)] + fake.listRegistryBundlesArgsForCall = append(fake.listRegistryBundlesArgsForCall, struct { + arg1 context.Context + }{arg1}) + fake.recordInvocation("ListRegistryBundles", []interface{}{arg1}) + fake.listRegistryBundlesMutex.Unlock() + if fake.ListRegistryBundlesStub != nil { + return fake.ListRegistryBundlesStub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + fakeReturns := fake.listRegistryBundlesReturns + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeQuery) ListRegistryBundlesCallCount() int { + fake.listRegistryBundlesMutex.RLock() + defer fake.listRegistryBundlesMutex.RUnlock() + return len(fake.listRegistryBundlesArgsForCall) +} + +func (fake *FakeQuery) ListRegistryBundlesCalls(stub func(context.Context) ([]*registry.Bundle, error)) { + fake.listRegistryBundlesMutex.Lock() + defer fake.listRegistryBundlesMutex.Unlock() + fake.ListRegistryBundlesStub = stub +} + +func (fake *FakeQuery) ListRegistryBundlesArgsForCall(i int) context.Context { + fake.listRegistryBundlesMutex.RLock() + defer fake.listRegistryBundlesMutex.RUnlock() + argsForCall := fake.listRegistryBundlesArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeQuery) ListRegistryBundlesReturns(result1 []*registry.Bundle, result2 error) { + fake.listRegistryBundlesMutex.Lock() + defer fake.listRegistryBundlesMutex.Unlock() + fake.ListRegistryBundlesStub = nil + fake.listRegistryBundlesReturns = struct { + result1 []*registry.Bundle + result2 error + }{result1, result2} +} + +func (fake *FakeQuery) ListRegistryBundlesReturnsOnCall(i int, result1 []*registry.Bundle, result2 error) { + fake.listRegistryBundlesMutex.Lock() + defer fake.listRegistryBundlesMutex.Unlock() + fake.ListRegistryBundlesStub = nil + if fake.listRegistryBundlesReturnsOnCall == nil { + fake.listRegistryBundlesReturnsOnCall = make(map[int]struct { + result1 []*registry.Bundle + result2 error + }) + } + fake.listRegistryBundlesReturnsOnCall[i] = struct { + result1 []*registry.Bundle + result2 error + }{result1, result2} +} + func (fake *FakeQuery) ListTables(arg1 context.Context) ([]string, error) { fake.listTablesMutex.Lock() ret, specificReturn := fake.listTablesReturnsOnCall[len(fake.listTablesArgsForCall)] @@ -1842,6 +1996,8 @@ func (fake *FakeQuery) Invocations() map[string][][]interface{} { defer fake.getBundleMutex.RUnlock() fake.getBundleForChannelMutex.RLock() defer fake.getBundleForChannelMutex.RUnlock() + fake.getBundlePathIfExistsMutex.RLock() + defer fake.getBundlePathIfExistsMutex.RUnlock() fake.getBundlePathsForPackageMutex.RLock() defer fake.getBundlePathsForPackageMutex.RUnlock() fake.getBundleThatProvidesMutex.RLock() @@ -1880,6 +2036,8 @@ func (fake *FakeQuery) Invocations() map[string][][]interface{} { defer fake.listImagesMutex.RUnlock() fake.listPackagesMutex.RLock() defer fake.listPackagesMutex.RUnlock() + fake.listRegistryBundlesMutex.RLock() + defer fake.listRegistryBundlesMutex.RUnlock() fake.listTablesMutex.RLock() defer fake.listTablesMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} diff --git a/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cabundle_updater.go b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cabundle_updater.go new file mode 100644 index 0000000000..4fcee4e457 --- /dev/null +++ b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cabundle_updater.go @@ -0,0 +1,60 @@ +package filemonitor + +import ( + "crypto/x509" + "io/ioutil" + "sync" + + "github.com/fsnotify/fsnotify" + "github.com/sirupsen/logrus" +) + +type certPoolStore struct { + mutex sync.RWMutex + certpool *x509.CertPool + clientCAPath string +} + +func NewCertPoolStore(clientCAPath string) (*certPoolStore, error) { + pem, err := ioutil.ReadFile(clientCAPath) + if err != nil { + return nil, err + } + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(pem) + + return &certPoolStore{ + mutex: sync.RWMutex{}, + certpool: pool, + clientCAPath: clientCAPath, + }, nil +} + +func (c *certPoolStore) storeCABundle(clientCAPath string) error { + pem, err := ioutil.ReadFile(clientCAPath) + if err == nil { + c.mutex.Lock() + defer c.mutex.Unlock() + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(pem) + c.certpool = pool + } + return err +} + +func (c *certPoolStore) HandleCABundleUpdate(logger logrus.FieldLogger, event fsnotify.Event) { + switch op := event.Op; op { + case fsnotify.Create: + logger.Debugf("got fs event for %v", event.Name) + + if err := c.storeCABundle(c.clientCAPath); err != nil { + logger.Debugf("unable to reload ca bundle: %v", err) + } else { + logger.Debugf("successfully reload ca bundle: %v", err) + } + } +} + +func (c *certPoolStore) GetCertPool() *x509.CertPool { + return c.certpool +} diff --git a/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go index d7bb005ac2..9cf59c888c 100644 --- a/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go +++ b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go @@ -1,43 +1,38 @@ package filemonitor import ( - "context" "crypto/tls" "crypto/x509" - "fmt" - "path/filepath" "sync" "github.com/fsnotify/fsnotify" "github.com/sirupsen/logrus" ) -type keystore struct { +type certStore struct { mutex sync.RWMutex cert *tls.Certificate tlsCrtPath string tlsKeyPath string } -type getCertFn = func(*tls.ClientHelloInfo) (*tls.Certificate, error) - -// NewKeystore returns a store for storing the certificate data and the ability to retrieve it safely -func NewKeystore(tlsCrt, tlsKey string) *keystore { +// NewCertStore returns a store for storing the certificate data and the ability to retrieve it safely +func NewCertStore(tlsCrt, tlsKey string) (*certStore, error) { cert, err := tls.LoadX509KeyPair(tlsCrt, tlsKey) if err != nil { - panic(err) + return nil, err } - return &keystore{ + return &certStore{ mutex: sync.RWMutex{}, cert: &cert, tlsCrtPath: tlsCrt, tlsKeyPath: tlsKey, - } + }, nil } // HandleFilesystemUpdate is intended to be used as the OnUpdateFn for a watcher // and expects the certificate files to be in the same directory. -func (k *keystore) HandleFilesystemUpdate(logger *logrus.Logger, event fsnotify.Event) { +func (k *certStore) HandleFilesystemUpdate(logger logrus.FieldLogger, event fsnotify.Event) { switch op := event.Op; op { case fsnotify.Create: logger.Debugf("got fs event for %v", event.Name) @@ -57,7 +52,7 @@ func (k *keystore) HandleFilesystemUpdate(logger *logrus.Logger, event fsnotify. } } -func (k *keystore) storeCertificate(tlsCrt, tlsKey string) error { +func (k *certStore) storeCertificate(tlsCrt, tlsKey string) error { cert, err := tls.LoadX509KeyPair(tlsCrt, tlsKey) if err == nil { k.mutex.Lock() @@ -67,24 +62,8 @@ func (k *keystore) storeCertificate(tlsCrt, tlsKey string) error { return err } -func (k *keystore) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { +func (k *certStore) GetCertificate() *tls.Certificate { k.mutex.RLock() defer k.mutex.RUnlock() - return k.cert, nil -} - -// OLMGetCertRotationFn is a convenience function for OLM use only, but serves as an example for monitoring file system events -func OLMGetCertRotationFn(logger *logrus.Logger, tlsCertPath, tlsKeyPath string) (getCertFn, error) { - if filepath.Dir(tlsCertPath) != filepath.Dir(tlsKeyPath) { - return nil, fmt.Errorf("certificates expected to be in same directory %v vs %v", tlsCertPath, tlsKeyPath) - } - - keystore := NewKeystore(tlsCertPath, tlsKeyPath) - watcher, err := NewWatch(logger, []string{filepath.Dir(tlsCertPath)}, keystore.HandleFilesystemUpdate) - if err != nil { - return nil, err - } - watcher.Run(context.Background()) - - return keystore.GetCertificate, nil + return k.cert } diff --git a/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater_test.go b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater_test.go index 92cbb7af54..022b22fc30 100644 --- a/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater_test.go +++ b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater_test.go @@ -1,6 +1,7 @@ package filemonitor import ( + "context" "crypto/tls" "crypto/x509" "fmt" @@ -55,8 +56,18 @@ func TestOLMGetCertRotationFn(t *testing.T) { err = os.Symlink(filepath.Join("..", oldKey), loadKey) require.NoError(t, err) - tlsGetCertFn, err := OLMGetCertRotationFn(logger, loadCrt, loadKey) + certStore, err := NewCertStore(loadCrt, loadKey) + if err != nil { + require.NoError(t, err) + } + + tlsGetCertFn := func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { + return certStore.GetCertificate(), nil + } + + csw, err := NewWatch(logger, []string{filepath.Dir(loadCrt), filepath.Dir(loadKey)}, certStore.HandleFilesystemUpdate) require.NoError(t, err) + csw.Run(context.Background()) // find a free port to listen on and start server listener, err := net.Listen("tcp", "localhost:0") @@ -113,7 +124,8 @@ func TestOLMGetCertRotationFn(t *testing.T) { // sometimes the the filesystem operations need time to catch up so the server cert is updated err = wait.PollImmediate(500*time.Millisecond, 10*time.Second, func() (bool, error) { - currentCert, _ := tlsGetCertFn(nil) + currentCert, err := tlsGetCertFn(nil) + require.NoError(t, err) info, err := x509.ParseCertificate(currentCert.Certificate[0]) if err != nil { return false, err diff --git a/staging/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go index c3b04e9fc3..bfb42ffb06 100644 --- a/staging/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go +++ b/staging/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go @@ -10,12 +10,12 @@ import ( type watcher struct { notify *fsnotify.Watcher pathsToWatch []string - logger *logrus.Logger - onUpdateFn func(*logrus.Logger, fsnotify.Event) + logger logrus.FieldLogger + onUpdateFn func(logrus.FieldLogger, fsnotify.Event) } // NewWatch sets up monitoring on a slice of paths and will execute the update function to process each event -func NewWatch(logger *logrus.Logger, pathsToWatch []string, onUpdateFn func(*logrus.Logger, fsnotify.Event)) (*watcher, error) { +func NewWatch(logger logrus.FieldLogger, pathsToWatch []string, onUpdateFn func(logrus.FieldLogger, fsnotify.Event)) (*watcher, error) { notify, err := fsnotify.NewWatcher() if err != nil { return nil, err diff --git a/staging/operator-lifecycle-manager/pkg/lib/profile/profile.go b/staging/operator-lifecycle-manager/pkg/lib/profile/profile.go index 76c516cbf4..46cf07edb4 100644 --- a/staging/operator-lifecycle-manager/pkg/lib/profile/profile.go +++ b/staging/operator-lifecycle-manager/pkg/lib/profile/profile.go @@ -47,18 +47,28 @@ func RegisterHandlers(mux *http.ServeMux, options ...Option) { config.apply(options) if config.pprof { - mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.Handle("/debug/pprof/", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Index))) } if config.cmdline { - mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.Handle("/debug/pprof/cmdline", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Cmdline))) } if config.profile { - mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.Handle("/debug/pprof/profile", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Profile))) } if config.symbol { - mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.Handle("/debug/pprof/symbol", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Symbol))) } if config.trace { - mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + mux.Handle("/debug/pprof/trace", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Trace))) } } + +func requireVerifiedClientCertificate(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.TLS == nil || len(r.TLS.VerifiedChains) == 0 { + w.WriteHeader(http.StatusForbidden) + return + } + h.ServeHTTP(w, r) + }) +} diff --git a/staging/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go b/staging/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go index cef9db6719..02a66cb527 100644 --- a/staging/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go +++ b/staging/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go @@ -2,7 +2,6 @@ package queueinformer import ( "context" - "fmt" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -170,7 +169,6 @@ func (l LegacySyncHandler) ToSyncer() kubestate.Syncer { // ToSyncerWithDelete returns the Syncer equivalent of the given sync handler and delete function. func (l LegacySyncHandler) ToSyncerWithDelete(onDelete func(obj interface{})) kubestate.Syncer { var syncer kubestate.SyncFunc = func(ctx context.Context, event kubestate.ResourceEvent) error { - logrus.New().WithField("event", fmt.Sprintf("%+v", event)).Trace("legacy syncer received event") switch event.Type() { case kubestate.ResourceDeleted: if onDelete != nil { diff --git a/staging/operator-lifecycle-manager/pkg/lib/server/server.go b/staging/operator-lifecycle-manager/pkg/lib/server/server.go new file mode 100644 index 0000000000..c7307fc7b4 --- /dev/null +++ b/staging/operator-lifecycle-manager/pkg/lib/server/server.go @@ -0,0 +1,77 @@ +package server + +import ( + "context" + "crypto/tls" + "fmt" + "net/http" + "path/filepath" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" +) + +func GetListenAndServeFunc(logger *logrus.Logger, tlsCertPath, tlsKeyPath, clientCAPath *string) (func() error, error) { + mux := http.NewServeMux() + profile.RegisterHandlers(mux) + mux.Handle("/metrics", promhttp.Handler()) + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + s := http.Server{ + Handler: mux, + Addr: ":8080", + } + listenAndServe := s.ListenAndServe + + if *tlsCertPath != "" && *tlsKeyPath != "" { + logger.Info("TLS keys set, using https for metrics") + + certStore, err := filemonitor.NewCertStore(*tlsCertPath, *tlsKeyPath) + if err != nil { + return nil, fmt.Errorf("Certificate monitoring for metrics (https) failed: %v", err) + } + + csw, err := filemonitor.NewWatch(logger, []string{filepath.Dir(*tlsCertPath), filepath.Dir(*tlsKeyPath)}, certStore.HandleFilesystemUpdate) + if err != nil { + return nil, fmt.Errorf("error creating cert file watcher: %v", err) + } + csw.Run(context.Background()) + certPoolStore, err := filemonitor.NewCertPoolStore(*clientCAPath) + cpsw, err := filemonitor.NewWatch(logger, []string{filepath.Dir(*clientCAPath)}, certPoolStore.HandleCABundleUpdate) + if err != nil { + return nil, fmt.Errorf("error creating cert file watcher: %v", err) + } + cpsw.Run(context.Background()) + + s.Addr = ":8443" + s.TLSConfig = &tls.Config{ + GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { + return certStore.GetCertificate(), nil + }, + GetConfigForClient: func(_ *tls.ClientHelloInfo) (*tls.Config, error) { + var certs []tls.Certificate + if cert := certStore.GetCertificate(); cert != nil { + certs = append(certs, *cert) + } + return &tls.Config{ + Certificates: certs, + ClientCAs: certPoolStore.GetCertPool(), + ClientAuth: tls.VerifyClientCertIfGiven, + }, nil + }, + } + + listenAndServe = func() error { + return s.ListenAndServeTLS("", "") + } + } else if *tlsCertPath != "" || *tlsKeyPath != "" { + return nil, fmt.Errorf("both --tls-key and --tls-crt must be provided for TLS to be enabled") + } else { + logger.Info("TLS keys not set, using non-https for metrics") + } + return listenAndServe, nil +} diff --git a/staging/operator-lifecycle-manager/scripts/k8s_yaml_to_ansible_install.sh b/staging/operator-lifecycle-manager/scripts/k8s_yaml_to_ansible_install.sh deleted file mode 100755 index f1b1669977..0000000000 --- a/staging/operator-lifecycle-manager/scripts/k8s_yaml_to_ansible_install.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -# requires yq to be installed https://github.com/mikefarah/yq - -if [[ ${#@} < 2 ]]; then - echo "Usage: $0 manifests outfile" - echo "* manifests: directory of k8s manifests" - echo "* outfile: the ansible file to append" - exit 1 -fi - -manifests=$1 -outfile=$2 - -for filename in $manifests/*.yaml; do - kind=$(yq r "$filename" kind) - name=$(yq r "$filename" metadata.name) - cat <> $outfile - -- name: Apply $name $kind manifest - oc_obj: - state: present - kind: $kind - name: $name - namespace: operator-lifecycle-manager - files: - - "{{ mktemp.stdout }}/$(basename $filename)" -EOF -done diff --git a/staging/operator-lifecycle-manager/scripts/k8s_yaml_to_ansible_remove.sh b/staging/operator-lifecycle-manager/scripts/k8s_yaml_to_ansible_remove.sh deleted file mode 100755 index 7e92789720..0000000000 --- a/staging/operator-lifecycle-manager/scripts/k8s_yaml_to_ansible_remove.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -# requires yq to be installed https://github.com/mikefarah/yq - -if [[ ${#@} < 2 ]]; then - echo "Usage: $0 manifests outfile" - echo "* manifests: directory of k8s manifests" - echo "* outfile: the ansible file to append" - exit 1 -fi - -manifests=$1 -outfile=$2 - -for filename in $manifests/*.yaml; do - kind=$(yq r "$filename" kind) - name=$(yq r "$filename" metadata.name) - cat <> $outfile - -- name: Remove $name $kind manifest - oc_obj: - state: absent - kind: $kind - name: $name - namespace: operator-lifecycle-manager -EOF -done diff --git a/staging/operator-lifecycle-manager/scripts/kube_deps.sh b/staging/operator-lifecycle-manager/scripts/kube_deps.sh deleted file mode 100755 index fe07feaa8e..0000000000 --- a/staging/operator-lifecycle-manager/scripts/kube_deps.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -set -euo pipefail - -VERSION=${1#"v"} -if [ -z "$VERSION" ]; then - echo "Must specify version!" - exit 1 -fi -MODS=($( - curl -sS https://raw.githubusercontent.com/kubernetes/kubernetes/v${VERSION}/go.mod | - sed -n 's|.*k8s.io/\(.*\) => ./staging/src/k8s.io/.*|k8s.io/\1|p' -)) -for MOD in "${MODS[@]}"; do - V=$( - go mod download -json "${MOD}@kubernetes-${VERSION}" | - sed -n 's|.*"Version": "\(.*\)".*|\1|p' - ) - go mod edit "-replace=${MOD}=${MOD}@${V}" -done -go get "k8s.io/kubernetes@v${VERSION}" diff --git a/staging/operator-lifecycle-manager/scripts/pull_or_build_rh.sh b/staging/operator-lifecycle-manager/scripts/pull_or_build_rh.sh deleted file mode 100755 index dc8697bd62..0000000000 --- a/staging/operator-lifecycle-manager/scripts/pull_or_build_rh.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -if ! docker pull "quay.io/coreos/olm:$1-rhel" || ! docker pull "quay.io/coreos/catalog:$1-rhel"; then - docker build -t "quay.io/coreos/olm:$1-rhel" -t "quay.io/coreos/catalog:$1-rhel" . - docker push "quay.io/coreos/olm:$1-rhel" - docker push "quay.io/coreos/catalog:$1-rhel" -fi diff --git a/staging/operator-lifecycle-manager/test/e2e/deprecated_e2e_test.go b/staging/operator-lifecycle-manager/test/e2e/deprecated_e2e_test.go index f411d7a174..f9d66cbeb1 100644 --- a/staging/operator-lifecycle-manager/test/e2e/deprecated_e2e_test.go +++ b/staging/operator-lifecycle-manager/test/e2e/deprecated_e2e_test.go @@ -1,6 +1,89 @@ package e2e -// TODO +import ( + "context" + "time" -// v1beta1 CRD in installplan fails -// v1beta1 RBAC in an installplan fails + "github.com/blang/semver/v4" + . "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx" +) + +var missingAPI = `{"apiVersion":"verticalpodautoscalers.autoscaling.k8s.io/v1","kind":"VerticalPodAutoscaler","metadata":{"name":"my.thing","namespace":"foo"}}` + +var _ = Describe("Not found APIs", func() { + BeforeEach(func() { + csv := newCSV("test-csv", testNamespace, "", semver.Version{}, nil, nil, nil) + Expect(ctx.Ctx().Client().Create(context.TODO(), &csv)).To(Succeed()) + }) + AfterEach(func() { + TearDown(testNamespace) + }) + + When("objects with APIs that are not on-cluster are created in the installplan", func() { + // each entry is an installplan with a deprecated resource + type payload struct { + name string + ip *operatorsv1alpha1.InstallPlan + errMessage string + } + + var tableEntries []table.TableEntry + tableEntries = []table.TableEntry{ + table.Entry("contains an entry with a missing API not found on cluster ", payload{ + name: "installplan contains a missing API", + ip: &operatorsv1alpha1.InstallPlan{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: *namespace, // this is necessary due to ginkgo table semantics, see https://github.com/onsi/ginkgo/issues/378 + Name: "test-plan-api", + }, + Spec: operatorsv1alpha1.InstallPlanSpec{ + Approval: operatorsv1alpha1.ApprovalAutomatic, + Approved: true, + ClusterServiceVersionNames: []string{}, + }, + Status: operatorsv1alpha1.InstallPlanStatus{ + Phase: operatorsv1alpha1.InstallPlanPhaseInstalling, + CatalogSources: []string{}, + Plan: []*operatorsv1alpha1.Step{ + { + Resolving: "test-csv", + Status: operatorsv1alpha1.StepStatusUnknown, + Resource: operatorsv1alpha1.StepResource{ + Name: "my.thing", + Group: "verticalpodautoscalers.autoscaling.k8s.io", + Version: "v1", + Kind: "VerticalPodAutoscaler", + Manifest: missingAPI, + }, + }, + }, + }, + }, + errMessage: "api-server resource not found installing VerticalPodAutoscaler my.thing: GroupVersionKind " + + "verticalpodautoscalers.autoscaling.k8s.io/v1, Kind=VerticalPodAutoscaler not found on the cluster", + }), + } + + table.DescribeTable("the ip enters a failed state with a helpful error message", func(tt payload) { + Expect(ctx.Ctx().Client().Create(context.Background(), tt.ip)).To(Succeed()) + Expect(ctx.Ctx().Client().Status().Update(context.Background(), tt.ip)).To(Succeed()) + + // The IP sits in the Installing phase with the GVK missing error + Eventually(func() (*operatorsv1alpha1.InstallPlan, error) { + return tt.ip, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(tt.ip), tt.ip) + }).Should(And(HavePhase(operatorsv1alpha1.InstallPlanPhaseInstalling)), HaveMessage(tt.errMessage)) + + // Eventually the IP fails with the GVK missing error, after installplan retries, which is by default 1 minute. + Eventually(func() (*operatorsv1alpha1.InstallPlan, error) { + return tt.ip, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(tt.ip), tt.ip) + }, 2*time.Minute).Should(And(HavePhase(operatorsv1alpha1.InstallPlanPhaseFailed)), HaveMessage(tt.errMessage)) + }, tableEntries...) + }) +}) diff --git a/staging/operator-lifecycle-manager/test/e2e/installplan_e2e_test.go b/staging/operator-lifecycle-manager/test/e2e/installplan_e2e_test.go index 698e9309b1..9286b6086b 100644 --- a/staging/operator-lifecycle-manager/test/e2e/installplan_e2e_test.go +++ b/staging/operator-lifecycle-manager/test/e2e/installplan_e2e_test.go @@ -16,7 +16,6 @@ import ( "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gstruct" - "github.com/onsi/gomega/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" @@ -53,12 +52,6 @@ import ( ) var _ = Describe("Install Plan", func() { - HavePhase := func(goal operatorsv1alpha1.InstallPlanPhase) types.GomegaMatcher { - return WithTransform(func(plan *operatorsv1alpha1.InstallPlan) operatorsv1alpha1.InstallPlanPhase { - return plan.Status.Phase - }, Equal(goal)) - } - AfterEach(func() { TearDown(testNamespace) }) @@ -89,7 +82,7 @@ var _ = Describe("Install Plan", func() { BeforeEach(func() { counter = 0 - for _, metric := range getMetricsFromPod(ctx.Ctx().KubeClient(), getPodWithLabel(ctx.Ctx().KubeClient(), "app=catalog-operator"), "8081") { + for _, metric := range getMetricsFromPod(ctx.Ctx().KubeClient(), getPodWithLabel(ctx.Ctx().KubeClient(), "app=catalog-operator")) { if metric.Family == "installplan_warnings_total" { counter = metric.Value } @@ -189,7 +182,7 @@ var _ = Describe("Install Plan", func() { It("increments a metric counting the warning", func() { Eventually(func() []Metric { - return getMetricsFromPod(ctx.Ctx().KubeClient(), getPodWithLabel(ctx.Ctx().KubeClient(), "app=catalog-operator"), "8081") + return getMetricsFromPod(ctx.Ctx().KubeClient(), getPodWithLabel(ctx.Ctx().KubeClient(), "app=catalog-operator")) }).Should(ContainElement(LikeMetric( WithFamily("installplan_warnings_total"), WithValueGreaterThan(counter), diff --git a/staging/operator-lifecycle-manager/test/e2e/metrics_e2e_test.go b/staging/operator-lifecycle-manager/test/e2e/metrics_e2e_test.go index e733d637f7..56cef82d3e 100644 --- a/staging/operator-lifecycle-manager/test/e2e/metrics_e2e_test.go +++ b/staging/operator-lifecycle-manager/test/e2e/metrics_e2e_test.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "regexp" + "strconv" "strings" "sync" @@ -80,7 +81,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { It("generates csv_abnormal metric for OLM pod", func() { - Expect(getMetricsFromPod(c, getPodWithLabel(c, "app=olm-operator"), "8081")).To(And( + Expect(getMetricsFromPod(c, getPodWithLabel(c, "app=olm-operator"))).To(And( ContainElement(LikeMetric( WithFamily("csv_abnormal"), WithName(failingCSV.Name), @@ -108,7 +109,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { It("deletes its associated CSV metrics", func() { // Verify that when the csv has been deleted, it deletes the corresponding CSV metrics - Expect(getMetricsFromPod(c, getPodWithLabel(c, "app=olm-operator"), "8081")).ToNot(And( + Expect(getMetricsFromPod(c, getPodWithLabel(c, "app=olm-operator"))).ToNot(And( ContainElement(LikeMetric(WithFamily("csv_abnormal"), WithName(failingCSV.Name))), ContainElement(LikeMetric(WithFamily("csv_succeeded"), WithName(failingCSV.Name))), )) @@ -138,7 +139,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { // Verify metrics have been emitted for subscription Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(ContainElement(LikeMetric( WithFamily("subscription_sync_total"), WithName("metric-subscription-for-create"), @@ -153,7 +154,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { // Verify metrics have been emitted for dependency resolution Eventually(func() bool { return Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(ContainElement(LikeMetric( WithFamily("olm_resolution_duration_seconds"), WithLabel("outcome", "failed"), @@ -168,7 +169,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { BeforeEach(func() { subscriptionCleanup, subscription = createSubscription(GinkgoT(), crc, testNamespace, "metric-subscription-for-update", testPackageName, stableChannel, v1alpha1.ApprovalManual) Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(ContainElement(LikeMetric(WithFamily("subscription_sync_total"), WithLabel("name", "metric-subscription-for-update")))) Eventually(func() error { s, err := crc.OperatorsV1alpha1().Subscriptions(subscription.GetNamespace()).Get(context.TODO(), subscription.GetName(), metav1.GetOptions{}) @@ -189,7 +190,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { It("deletes the old Subscription metric and emits the new metric", func() { Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(And( Not(ContainElement(LikeMetric( WithFamily("subscription_sync_total"), @@ -223,7 +224,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { It("deletes the old subscription metric and emits the new metric(there is only one metric for the subscription)", func() { Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(And( Not(ContainElement(LikeMetric( WithFamily("subscription_sync_total"), @@ -253,7 +254,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { BeforeEach(func() { subscriptionCleanup, subscription = createSubscription(GinkgoT(), crc, testNamespace, "metric-subscription-for-delete", testPackageName, stableChannel, v1alpha1.ApprovalManual) Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(ContainElement(LikeMetric(WithFamily("subscription_sync_total"), WithLabel("name", "metric-subscription-for-delete")))) if subscriptionCleanup != nil { subscriptionCleanup() @@ -269,7 +270,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { It("deletes the Subscription metric", func() { Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).ShouldNot(ContainElement(LikeMetric(WithFamily("subscription_sync_total"), WithName("metric-subscription-for-delete")))) }) }) @@ -312,7 +313,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { }) It("emits metrics for the catalogSource", func() { Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(And( ContainElement(LikeMetric( WithFamily("catalog_source_count"), @@ -332,7 +333,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { }) It("deletes the metrics for the CatalogSource", func() { Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(And( Not(ContainElement(LikeMetric( WithFamily("catalogsource_ready"), @@ -356,7 +357,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { }) It("emits metrics for the CatlogSource with a Value greater than 0", func() { Eventually(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }).Should(And( ContainElement(LikeMetric( WithFamily("catalogsource_ready"), @@ -366,7 +367,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() { )), )) Consistently(func() []Metric { - return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081") + return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator")) }, "3m").Should(And( ContainElement(LikeMetric( WithFamily("catalogsource_ready"), @@ -395,7 +396,18 @@ func getPodWithLabel(client operatorclient.ClientInterface, label string) *corev return &podList.Items[0] } -func getMetricsFromPod(client operatorclient.ClientInterface, pod *corev1.Pod, port string) []Metric { +func extractMetricPortFromPod(pod *corev1.Pod) string { + for _, container := range pod.Spec.Containers { + for _, port := range container.Ports { + if port.Name == "metrics" { + return strconv.Itoa(int(port.ContainerPort)) + } + } + } + return "-1" +} + +func getMetricsFromPod(client operatorclient.ClientInterface, pod *corev1.Pod) []Metric { ctx.Ctx().Logf("querying pod %s/%s\n", pod.GetNamespace(), pod.GetName()) // assuming -tls-cert and -tls-key aren't used anywhere else as a parameter value @@ -417,14 +429,13 @@ func getMetricsFromPod(client operatorclient.ClientInterface, pod *corev1.Pod, p scheme = "http" } ctx.Ctx().Logf("Retrieving metrics using scheme %v\n", scheme) - mfs := make(map[string]*io_prometheus_client.MetricFamily) EventuallyWithOffset(1, func() error { raw, err := client.KubernetesInterface().CoreV1().RESTClient().Get(). Namespace(pod.GetNamespace()). Resource("pods"). SubResource("proxy"). - Name(net.JoinSchemeNamePort(scheme, pod.GetName(), port)). + Name(net.JoinSchemeNamePort(scheme, pod.GetName(), extractMetricPortFromPod(pod))). Suffix("metrics"). Do(context.Background()).Raw() if err != nil { diff --git a/staging/operator-lifecycle-manager/test/e2e/util_test.go b/staging/operator-lifecycle-manager/test/e2e/util_test.go index 284d14d7ee..18be4dddf3 100644 --- a/staging/operator-lifecycle-manager/test/e2e/util_test.go +++ b/staging/operator-lifecycle-manager/test/e2e/util_test.go @@ -35,7 +35,9 @@ import ( "k8s.io/client-go/rest" k8scontrollerclient "sigs.k8s.io/controller-runtime/pkg/client" + gtypes "github.com/onsi/gomega/types" "github.com/operator-framework/api/pkg/operators/v1alpha1" + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client" @@ -897,3 +899,15 @@ func deploymentReplicas(replicas int32) predicateFunc { func Apply(obj controllerclient.Object, changeFunc interface{}) func() error { return ctx.Ctx().SSAClient().Apply(context.Background(), obj, changeFunc) } + +func HavePhase(goal operatorsv1alpha1.InstallPlanPhase) gtypes.GomegaMatcher { + return WithTransform(func(plan *operatorsv1alpha1.InstallPlan) operatorsv1alpha1.InstallPlanPhase { + return plan.Status.Phase + }, Equal(goal)) +} + +func HaveMessage(goal string) gtypes.GomegaMatcher { + return WithTransform(func(plan *operatorsv1alpha1.InstallPlan) string { + return plan.Status.Message + }, ContainSubstring(goal)) +} diff --git a/staging/operator-registry/cmd/opm/alpha/cmd.go b/staging/operator-registry/cmd/opm/alpha/cmd.go index 9c06ae2cfd..42e50e3ddf 100644 --- a/staging/operator-registry/cmd/opm/alpha/cmd.go +++ b/staging/operator-registry/cmd/opm/alpha/cmd.go @@ -4,10 +4,7 @@ import ( "github.com/spf13/cobra" "github.com/operator-framework/operator-registry/cmd/opm/alpha/bundle" - initcmd "github.com/operator-framework/operator-registry/cmd/opm/alpha/init" - "github.com/operator-framework/operator-registry/cmd/opm/alpha/render" - "github.com/operator-framework/operator-registry/cmd/opm/alpha/serve" - "github.com/operator-framework/operator-registry/cmd/opm/alpha/validate" + "github.com/operator-framework/operator-registry/cmd/opm/alpha/list" ) func NewCmd() *cobra.Command { @@ -17,6 +14,6 @@ func NewCmd() *cobra.Command { Short: "Run an alpha subcommand", } - runCmd.AddCommand(bundle.NewCmd(), initcmd.NewCmd(), serve.NewCmd(), render.NewCmd(), validate.NewCmd()) + runCmd.AddCommand(bundle.NewCmd(), list.NewCmd()) return runCmd } diff --git a/staging/operator-registry/cmd/opm/alpha/list/cmd.go b/staging/operator-registry/cmd/opm/alpha/list/cmd.go new file mode 100644 index 0000000000..8309b089a1 --- /dev/null +++ b/staging/operator-registry/cmd/opm/alpha/list/cmd.go @@ -0,0 +1,107 @@ +package list + +import ( + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/operator-framework/operator-registry/internal/action" +) + +const humanReadabilityOnlyNote = `NOTE: This is meant to be used for convenience and human-readability only. The +CLI and output format are subject to change, so it is not recommended to depend +on the output in any programs or scripts. Use the "render" subcommand to do +more complex processing and automation.` + +func NewCmd() *cobra.Command { + list := &cobra.Command{ + Use: "list", + Short: "List contents of an index", + Long: `The list subcommands print the contents of an index. + +` + humanReadabilityOnlyNote, + } + list.AddCommand(newPackagesCmd(), newChannelsCmd(), newBundlesCmd()) + return list +} + +func newPackagesCmd() *cobra.Command { + logger := logrus.New() + + return &cobra.Command{ + Use: "packages ", + Short: "List packages in an index", + Long: `The "channels" command lists the channels from the specified index. + +` + humanReadabilityOnlyNote, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + lp := action.ListPackages{IndexReference: args[0]} + res, err := lp.Run(cmd.Context()) + if err != nil { + logger.Fatal(err) + } + if err := res.WriteColumns(os.Stdout); err != nil { + logger.Fatal(err) + } + return nil + }, + } +} + +func newChannelsCmd() *cobra.Command { + logger := logrus.New() + + return &cobra.Command{ + Use: "channels [packageName]", + Short: "List package channels in an index", + Long: `The "channels" command lists the channels from the specified index and package. + +` + humanReadabilityOnlyNote, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + lc := action.ListChannels{IndexReference: args[0]} + if len(args) > 1 { + lc.PackageName = args[1] + } + res, err := lc.Run(cmd.Context()) + if err != nil { + logger.Fatal(err) + } + if err := res.WriteColumns(os.Stdout); err != nil { + logger.Fatal(err) + } + return nil + }, + } +} + +func newBundlesCmd() *cobra.Command { + logger := logrus.New() + + return &cobra.Command{ + Use: "bundles ", + Short: "List package bundles in an index", + Long: `The "bundles" command lists the bundles from the specified index and package. +Bundles that exist in multiple channels are duplicated in the output (one +for each channel in which the bundle is present). + +` + humanReadabilityOnlyNote, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + lb := action.ListBundles{IndexReference: args[0]} + if len(args) > 1 { + lb.PackageName = args[1] + } + res, err := lb.Run(cmd.Context()) + if err != nil { + logger.Fatal(err) + } + if err := res.WriteColumns(os.Stdout); err != nil { + logger.Fatal(err) + } + return nil + }, + } +} diff --git a/staging/operator-registry/cmd/opm/alpha/init/cmd.go b/staging/operator-registry/cmd/opm/init/cmd.go similarity index 100% rename from staging/operator-registry/cmd/opm/alpha/init/cmd.go rename to staging/operator-registry/cmd/opm/init/cmd.go diff --git a/staging/operator-registry/cmd/opm/alpha/render/cmd.go b/staging/operator-registry/cmd/opm/render/cmd.go similarity index 100% rename from staging/operator-registry/cmd/opm/alpha/render/cmd.go rename to staging/operator-registry/cmd/opm/render/cmd.go diff --git a/staging/operator-registry/cmd/opm/root/cmd.go b/staging/operator-registry/cmd/opm/root/cmd.go index 272981bfd5..7b07ccbc75 100644 --- a/staging/operator-registry/cmd/opm/root/cmd.go +++ b/staging/operator-registry/cmd/opm/root/cmd.go @@ -6,7 +6,11 @@ import ( "github.com/operator-framework/operator-registry/cmd/opm/alpha" "github.com/operator-framework/operator-registry/cmd/opm/index" + initcmd "github.com/operator-framework/operator-registry/cmd/opm/init" "github.com/operator-framework/operator-registry/cmd/opm/registry" + "github.com/operator-framework/operator-registry/cmd/opm/render" + "github.com/operator-framework/operator-registry/cmd/opm/serve" + "github.com/operator-framework/operator-registry/cmd/opm/validate" "github.com/operator-framework/operator-registry/cmd/opm/version" ) @@ -23,7 +27,7 @@ func NewCmd() *cobra.Command { }, } - cmd.AddCommand(registry.NewOpmRegistryCmd(), alpha.NewCmd()) + cmd.AddCommand(registry.NewOpmRegistryCmd(), alpha.NewCmd(), initcmd.NewCmd(), serve.NewCmd(), render.NewCmd(), validate.NewCmd()) index.AddCommand(cmd) version.AddCommand(cmd) diff --git a/staging/operator-registry/cmd/opm/alpha/serve/serve.go b/staging/operator-registry/cmd/opm/serve/serve.go similarity index 100% rename from staging/operator-registry/cmd/opm/alpha/serve/serve.go rename to staging/operator-registry/cmd/opm/serve/serve.go diff --git a/staging/operator-registry/cmd/opm/alpha/validate/validate.go b/staging/operator-registry/cmd/opm/validate/validate.go similarity index 100% rename from staging/operator-registry/cmd/opm/alpha/validate/validate.go rename to staging/operator-registry/cmd/opm/validate/validate.go diff --git a/staging/operator-registry/internal/action/list.go b/staging/operator-registry/internal/action/list.go new file mode 100644 index 0000000000..972489226b --- /dev/null +++ b/staging/operator-registry/internal/action/list.go @@ -0,0 +1,227 @@ +package action + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "sort" + "strings" + "text/tabwriter" + + "github.com/operator-framework/api/pkg/operators/v1alpha1" + + "github.com/operator-framework/operator-registry/internal/declcfg" + "github.com/operator-framework/operator-registry/internal/model" + "github.com/operator-framework/operator-registry/internal/property" +) + +type ListPackages struct { + IndexReference string +} + +func (l *ListPackages) Run(ctx context.Context) (*ListPackagesResult, error) { + m, err := indexRefToModel(ctx, l.IndexReference) + if err != nil { + return nil, err + } + + pkgs := []model.Package{} + for _, pkg := range m { + pkgs = append(pkgs, *pkg) + } + sort.Slice(pkgs, func(i, j int) bool { + return pkgs[i].Name < pkgs[j].Name + }) + return &ListPackagesResult{Packages: pkgs}, nil +} + +type ListPackagesResult struct { + Packages []model.Package +} + +func (r *ListPackagesResult) WriteColumns(w io.Writer) error { + tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) + if _, err := fmt.Fprintln(tw, "NAME\tDISPLAY NAME\tDEFAULT CHANNEL"); err != nil { + return err + } + for _, pkg := range r.Packages { + if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", pkg.Name, getDisplayName(pkg), pkg.DefaultChannel.Name); err != nil { + return err + } + } + return tw.Flush() +} + +func getDisplayName(pkg model.Package) string { + if pkg.DefaultChannel == nil { + return "" + } + head, err := pkg.DefaultChannel.Head() + if err != nil || head == nil || head.CsvJSON == "" { + return "" + } + + csv := v1alpha1.ClusterServiceVersion{} + if err := json.Unmarshal([]byte(head.CsvJSON), &csv); err != nil { + return "" + } + return csv.Spec.DisplayName +} + +type ListChannels struct { + IndexReference string + PackageName string +} + +func (l *ListChannels) Run(ctx context.Context) (*ListChannelsResult, error) { + m, err := indexRefToModel(ctx, l.IndexReference) + if err != nil { + return nil, err + } + + pkgs, err := getPackages(m, l.PackageName) + if err != nil { + return nil, err + } + + channels := []model.Channel{} + for _, pkg := range pkgs { + for _, ch := range pkg.Channels { + channels = append(channels, *ch) + } + } + + sort.Slice(channels, func(i, j int) bool { + if channels[i].Package.Name != channels[j].Package.Name { + return channels[i].Package.Name < channels[j].Package.Name + } + return channels[i].Name < channels[j].Name + }) + return &ListChannelsResult{Channels: channels}, nil +} + +type ListChannelsResult struct { + Channels []model.Channel +} + +func (r *ListChannelsResult) WriteColumns(w io.Writer) error { + tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) + if _, err := fmt.Fprintln(tw, "PACKAGE\tCHANNEL\tHEAD"); err != nil { + return err + } + for _, ch := range r.Channels { + headStr := "" + head, err := ch.Head() + if err != nil { + headStr = fmt.Sprintf("ERROR: %s", err) + } else { + headStr = head.Name + } + if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", ch.Package.Name, ch.Name, headStr); err != nil { + return err + } + } + return tw.Flush() +} + +type ListBundles struct { + IndexReference string + PackageName string +} + +func (l *ListBundles) Run(ctx context.Context) (*ListBundlesResult, error) { + m, err := indexRefToModel(ctx, l.IndexReference) + if err != nil { + return nil, err + } + + pkgs, err := getPackages(m, l.PackageName) + if err != nil { + return nil, err + } + + bundles := []model.Bundle{} + for _, pkg := range pkgs { + for _, ch := range pkg.Channels { + for _, b := range ch.Bundles { + bundles = append(bundles, *b) + } + } + } + + sort.Slice(bundles, func(i, j int) bool { + if bundles[i].Package.Name != bundles[j].Package.Name { + return bundles[i].Package.Name < bundles[j].Package.Name + } + if bundles[i].Channel.Name != bundles[j].Channel.Name { + return bundles[i].Channel.Name < bundles[j].Channel.Name + } + return bundles[i].Name < bundles[j].Name + }) + return &ListBundlesResult{Bundles: bundles}, nil +} + +type ListBundlesResult struct { + Bundles []model.Bundle +} + +func (r *ListBundlesResult) WriteColumns(w io.Writer) error { + tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) + if _, err := fmt.Fprintln(tw, "PACKAGE\tCHANNEL\tBUNDLE\tREPLACES\tSKIPS\tSKIP RANGE\tIMAGE"); err != nil { + return err + } + for _, b := range r.Bundles { + skipRange, err := getSkipRange(b) + if err != nil { + return fmt.Errorf("get skipRange for bundle %q: %v", b.Name, err) + } + if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", b.Package.Name, b.Channel.Name, b.Name, b.Replaces, strings.Join(b.Skips, ","), skipRange, b.Image); err != nil { + return err + } + + } + return tw.Flush() +} + +func getSkipRange(b model.Bundle) (string, error) { + props, err := property.Parse(b.Properties) + if err != nil { + return "", err + } + if len(props.SkipRanges) > 1 { + return "", fmt.Errorf("multiple skip ranges not supported") + } + if len(props.SkipRanges) == 0 { + return "", nil + } + return string(props.SkipRanges[0]), nil +} + +func indexRefToModel(ctx context.Context, ref string) (model.Model, error) { + render := Render{ + Refs: []string{ref}, + AllowedRefMask: RefDCImage | RefDCDir | RefSqliteImage | RefSqliteFile, + } + cfg, err := render.Run(ctx) + if err != nil { + if errors.Is(err, &ErrNotAllowed{}) { + return nil, fmt.Errorf("cannot list non-index %q", ref) + } + return nil, err + } + + return declcfg.ConvertToModel(*cfg) +} + +func getPackages(m model.Model, packageName string) (model.Model, error) { + if packageName == "" { + return m, nil + } + pkg, ok := m[packageName] + if !ok { + return nil, fmt.Errorf("package %q not found", packageName) + } + return model.Model{packageName: pkg}, nil +} diff --git a/staging/operator-registry/internal/action/list_test.go b/staging/operator-registry/internal/action/list_test.go new file mode 100644 index 0000000000..87f53886e0 --- /dev/null +++ b/staging/operator-registry/internal/action/list_test.go @@ -0,0 +1,166 @@ +package action + +import ( + "bytes" + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestListPackages(t *testing.T) { + type spec struct { + name string + list ListPackages + expectedOut string + expectedErr string + } + + specs := []spec{ + { + name: "Success/ValidIndex", + list: ListPackages{IndexReference: "testdata/list-index"}, + expectedOut: `NAME DISPLAY NAME DEFAULT CHANNEL +bar Bar Operator beta +foo Foo Operator beta +`, + }, + { + name: "Error/UnknownIndex", + list: ListPackages{IndexReference: "unknown-index"}, + expectedErr: `render reference "unknown-index": error resolving name : object required`, + }, + } + for _, s := range specs { + t.Run(s.name, func(t *testing.T) { + res, err := s.list.Run(context.Background()) + if s.expectedErr != "" { + require.Nil(t, res) + require.EqualError(t, err, s.expectedErr) + } else { + require.NoError(t, err) + + buf := &bytes.Buffer{} + err = res.WriteColumns(buf) + require.NoError(t, err) + + require.Equal(t, s.expectedOut, buf.String()) + } + }) + } +} + +func TestListChannels(t *testing.T) { + type spec struct { + name string + list ListChannels + expectedOut string + expectedErr string + } + specs := []spec{ + { + name: "Success/WithPackage", + list: ListChannels{IndexReference: "testdata/list-index", PackageName: "foo"}, + expectedOut: `PACKAGE CHANNEL HEAD +foo beta foo.v0.2.0 +foo stable foo.v0.2.0 +`, + }, + { + name: "Success/WithoutPackage", + list: ListChannels{IndexReference: "testdata/list-index"}, + expectedOut: `PACKAGE CHANNEL HEAD +bar beta bar.v0.2.0 +bar stable bar.v0.2.0 +foo beta foo.v0.2.0 +foo stable foo.v0.2.0 +`, + }, + { + name: "Error/UnknownIndex", + list: ListChannels{IndexReference: "unknown-index"}, + expectedErr: `render reference "unknown-index": error resolving name : object required`, + }, + { + name: "Error/UnknownPackage", + list: ListChannels{IndexReference: "testdata/list-index", PackageName: "unknown"}, + expectedErr: `package "unknown" not found`, + }, + } + for _, s := range specs { + t.Run(s.name, func(t *testing.T) { + res, err := s.list.Run(context.Background()) + if s.expectedErr != "" { + require.Nil(t, res) + require.EqualError(t, err, s.expectedErr) + } else { + require.NoError(t, err) + + buf := &bytes.Buffer{} + err = res.WriteColumns(buf) + require.NoError(t, err) + + require.Equal(t, s.expectedOut, buf.String()) + } + }) + } +} + +func TestListBundles(t *testing.T) { + type spec struct { + name string + list ListBundles + expectedOut string + expectedErr string + } + specs := []spec{ + { + name: "Success/WithPackage", + list: ListBundles{IndexReference: "testdata/list-index", PackageName: "foo"}, + expectedOut: `PACKAGE CHANNEL BUNDLE REPLACES SKIPS SKIP RANGE IMAGE +foo beta foo.v0.1.0 <0.1.0 test.registry/foo-operator/foo-bundle:v0.1.0 +foo beta foo.v0.2.0 foo.v0.1.0 foo.v0.1.1,foo.v0.1.2 <0.2.0 test.registry/foo-operator/foo-bundle:v0.2.0 +foo stable foo.v0.2.0 foo.v0.1.0 foo.v0.1.1,foo.v0.1.2 <0.2.0 test.registry/foo-operator/foo-bundle:v0.2.0 +`, + }, + { + name: "Success/WithoutPackage", + list: ListBundles{IndexReference: "testdata/list-index"}, + expectedOut: `PACKAGE CHANNEL BUNDLE REPLACES SKIPS SKIP RANGE IMAGE +bar beta bar.v0.1.0 <0.1.0 test.registry/bar-operator/bar-bundle:v0.1.0 +bar beta bar.v0.2.0 bar.v0.1.0 bar.v0.1.1,bar.v0.1.2 <0.2.0 test.registry/bar-operator/bar-bundle:v0.2.0 +bar stable bar.v0.2.0 bar.v0.1.0 bar.v0.1.1,bar.v0.1.2 <0.2.0 test.registry/bar-operator/bar-bundle:v0.2.0 +foo beta foo.v0.1.0 <0.1.0 test.registry/foo-operator/foo-bundle:v0.1.0 +foo beta foo.v0.2.0 foo.v0.1.0 foo.v0.1.1,foo.v0.1.2 <0.2.0 test.registry/foo-operator/foo-bundle:v0.2.0 +foo stable foo.v0.2.0 foo.v0.1.0 foo.v0.1.1,foo.v0.1.2 <0.2.0 test.registry/foo-operator/foo-bundle:v0.2.0 +`, + }, + { + name: "Error/UnknownIndex", + list: ListBundles{IndexReference: "unknown-index"}, + expectedErr: `render reference "unknown-index": error resolving name : object required`, + }, + { + name: "Error/UnknownPackage", + list: ListBundles{IndexReference: "testdata/list-index", PackageName: "unknown"}, + expectedErr: `package "unknown" not found`, + }, + } + for _, s := range specs { + t.Run(s.name, func(t *testing.T) { + res, err := s.list.Run(context.Background()) + if s.expectedErr != "" { + require.Nil(t, res) + require.EqualError(t, err, s.expectedErr) + } else { + require.NoError(t, err) + + buf := &bytes.Buffer{} + err = res.WriteColumns(buf) + require.NoError(t, err) + + require.Equal(t, s.expectedOut, buf.String()) + } + }) + } +} diff --git a/staging/operator-registry/internal/action/render.go b/staging/operator-registry/internal/action/render.go index 64393c077a..2f4293a394 100644 --- a/staging/operator-registry/internal/action/render.go +++ b/staging/operator-registry/internal/action/render.go @@ -25,9 +25,34 @@ import ( "github.com/operator-framework/operator-registry/pkg/sqlite" ) +type RefType uint + +const ( + RefBundleImage RefType = 1 << iota + RefSqliteImage + RefSqliteFile + RefDCImage + RefDCDir + + RefAll = 0 +) + +func (r RefType) Allowed(refType RefType) bool { + return r == RefAll || r&refType == refType +} + +var _ error = &ErrNotAllowed{} + +type ErrNotAllowed struct{} + +func (_ *ErrNotAllowed) Error() string { + return "not allowed" +} + type Render struct { - Refs []string - Registry image.Registry + Refs []string + Registry image.Registry + AllowedRefMask RefType } func nullLogger() *logrus.Entry { @@ -48,26 +73,9 @@ func (r Render) Run(ctx context.Context) (*declcfg.DeclarativeConfig, error) { var cfgs []declcfg.DeclarativeConfig for _, ref := range r.Refs { - var ( - cfg *declcfg.DeclarativeConfig - err error - ) - if stat, serr := os.Stat(ref); serr == nil { - if stat.IsDir() { - cfg, err = declcfg.LoadFS(os.DirFS(ref)) - } else { - // The only supported file type is an sqlite DB file, - // since declarative configs will be in a directory. - if err := checkDBFile(ref); err != nil { - return nil, err - } - cfg, err = sqliteToDeclcfg(ctx, ref) - } - } else { - cfg, err = r.imageToDeclcfg(ctx, ref) - } + cfg, err := r.renderReference(ctx, ref) if err != nil { - return nil, fmt.Errorf("render reference %q: %v", ref, err) + return nil, fmt.Errorf("render reference %q: %w", ref, err) } renderBundleObjects(cfg) cfgs = append(cfgs, *cfg) @@ -96,6 +104,28 @@ func (r Render) createRegistry() (*containerdregistry.Registry, error) { return reg, nil } +func (r Render) renderReference(ctx context.Context, ref string) (*declcfg.DeclarativeConfig, error) { + if stat, serr := os.Stat(ref); serr == nil { + if stat.IsDir() { + if !r.AllowedRefMask.Allowed(RefDCDir) { + return nil, fmt.Errorf("cannot render DC directory: %w", &ErrNotAllowed{}) + } + return declcfg.LoadFS(os.DirFS(ref)) + } else { + // The only supported file type is an sqlite DB file, + // since declarative configs will be in a directory. + if err := checkDBFile(ref); err != nil { + return nil, err + } + if !r.AllowedRefMask.Allowed(RefSqliteFile) { + return nil, fmt.Errorf("cannot render sqlite file: %w", &ErrNotAllowed{}) + } + return sqliteToDeclcfg(ctx, ref) + } + } + return r.imageToDeclcfg(ctx, ref) +} + func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.DeclarativeConfig, error) { ref := image.SimpleReference(imageRef) if err := r.Registry.Pull(ctx, ref); err != nil { @@ -116,17 +146,26 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D var cfg *declcfg.DeclarativeConfig if dbFile, ok := labels[containertools.DbLocationLabel]; ok { + if !r.AllowedRefMask.Allowed(RefSqliteImage) { + return nil, fmt.Errorf("cannot render sqlite image: %w", &ErrNotAllowed{}) + } cfg, err = sqliteToDeclcfg(ctx, filepath.Join(tmpDir, dbFile)) if err != nil { return nil, err } } else if configsDir, ok := labels["operators.operatorframework.io.index.configs.v1"]; ok { // TODO(joelanford): Make a constant for above configs location label + if !r.AllowedRefMask.Allowed(RefDCImage) { + return nil, fmt.Errorf("cannot render DC image: %w", &ErrNotAllowed{}) + } cfg, err = declcfg.LoadFS(os.DirFS(filepath.Join(tmpDir, configsDir))) if err != nil { return nil, err } } else if _, ok := labels[bundle.PackageLabel]; ok { + if !r.AllowedRefMask.Allowed(RefBundleImage) { + return nil, fmt.Errorf("cannot render bundle image: %w", &ErrNotAllowed{}) + } img, err := registry.NewImageInput(ref, tmpDir) if err != nil { return nil, err diff --git a/staging/operator-registry/internal/action/render_test.go b/staging/operator-registry/internal/action/render_test.go index b9ed5be32d..f75ae54e91 100644 --- a/staging/operator-registry/internal/action/render_test.go +++ b/staging/operator-registry/internal/action/render_test.go @@ -3,6 +3,7 @@ package action_test import ( "context" "embed" + "errors" "io/fs" "os" "path/filepath" @@ -51,6 +52,14 @@ func TestRender(t *testing.T) { foov2crd, err = yaml.ToJSON(foov2crd) require.NoError(t, err) + dir := t.TempDir() + dbFile := filepath.Join(dir, "index.db") + imageMap := map[image.Reference]string{ + image.SimpleReference("test.registry/foo-operator/foo-bundle:v0.1.0"): "testdata/foo-bundle-v0.1.0", + image.SimpleReference("test.registry/foo-operator/foo-bundle:v0.2.0"): "testdata/foo-bundle-v0.2.0", + } + assert.NoError(t, generateSqliteFile(dbFile, imageMap)) + specs := []spec{ { name: "Success/SqliteIndexImage", @@ -74,6 +83,84 @@ func TestRender(t *testing.T) { Image: "test.registry/foo-operator/foo-bundle:v0.1.0", Properties: []property.Property{ property.MustBuildChannel("beta", ""), + property.MustBuildChannel("stable", ""), + property.MustBuildGVK("test.foo", "v1", "Foo"), + property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), + property.MustBuildPackage("foo", "0.1.0"), + property.MustBuildPackageRequired("bar", "v0.1.0"), + property.MustBuildSkipRange("<0.1.0"), + property.MustBuildBundleObjectData(foov1csv), + property.MustBuildBundleObjectData(foov1crd), + }, + RelatedImages: []declcfg.RelatedImage{ + { + Name: "operator", + Image: "test.registry/foo-operator/foo:v0.1.0", + }, + { + Image: "test.registry/foo-operator/foo-bundle:v0.1.0", + }, + }, + CsvJSON: string(foov1csv), + Objects: []string{string(foov1csv), string(foov1crd)}, + }, + { + Schema: "olm.bundle", + Name: "foo.v0.2.0", + Package: "foo", + Image: "test.registry/foo-operator/foo-bundle:v0.2.0", + Properties: []property.Property{ + property.MustBuildChannel("beta", "foo.v0.1.0"), + property.MustBuildChannel("stable", "foo.v0.1.0"), + property.MustBuildGVK("test.foo", "v1", "Foo"), + property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), + property.MustBuildPackage("foo", "0.2.0"), + property.MustBuildPackageRequired("bar", "v0.1.0"), + property.MustBuildSkipRange("<0.2.0"), + property.MustBuildSkips("foo.v0.1.1"), + property.MustBuildSkips("foo.v0.1.2"), + property.MustBuildBundleObjectData(foov2csv), + property.MustBuildBundleObjectData(foov2crd), + }, + RelatedImages: []declcfg.RelatedImage{ + { + Name: "operator", + Image: "test.registry/foo-operator/foo:v0.2.0", + }, + { + Image: "test.registry/foo-operator/foo-bundle:v0.2.0", + }, + }, + CsvJSON: string(foov2csv), + Objects: []string{string(foov2csv), string(foov2crd)}, + }, + }, + }, + assertion: require.NoError, + }, + { + name: "Success/SqliteFile", + render: action.Render{ + Refs: []string{dbFile}, + Registry: reg, + }, + expectCfg: &declcfg.DeclarativeConfig{ + Packages: []declcfg.Package{ + { + Schema: "olm.package", + Name: "foo", + DefaultChannel: "beta", + }, + }, + Bundles: []declcfg.Bundle{ + { + Schema: "olm.bundle", + Name: "foo.v0.1.0", + Package: "foo", + Image: "test.registry/foo-operator/foo-bundle:v0.1.0", + Properties: []property.Property{ + property.MustBuildChannel("beta", ""), + property.MustBuildChannel("stable", ""), property.MustBuildGVK("test.foo", "v1", "Foo"), property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), property.MustBuildPackage("foo", "0.1.0"), @@ -101,6 +188,7 @@ func TestRender(t *testing.T) { Image: "test.registry/foo-operator/foo-bundle:v0.2.0", Properties: []property.Property{ property.MustBuildChannel("beta", "foo.v0.1.0"), + property.MustBuildChannel("stable", "foo.v0.1.0"), property.MustBuildGVK("test.foo", "v1", "Foo"), property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), property.MustBuildPackage("foo", "0.2.0"), @@ -176,6 +264,83 @@ func TestRender(t *testing.T) { Image: "test.registry/foo-operator/foo-bundle:v0.2.0", Properties: []property.Property{ property.MustBuildChannel("beta", "foo.v0.1.0"), + property.MustBuildChannel("stable", "foo.v0.1.0"), + property.MustBuildGVK("test.foo", "v1", "Foo"), + property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), + property.MustBuildPackage("foo", "0.2.0"), + property.MustBuildPackageRequired("bar", "v0.1.0"), + property.MustBuildSkipRange("<0.2.0"), + property.MustBuildSkips("foo.v0.1.1"), + property.MustBuildSkips("foo.v0.1.2"), + property.MustBuildBundleObjectData(foov2csv), + property.MustBuildBundleObjectData(foov2crd), + }, + RelatedImages: []declcfg.RelatedImage{ + { + Name: "operator", + Image: "test.registry/foo-operator/foo:v0.2.0", + }, + { + Image: "test.registry/foo-operator/foo-bundle:v0.2.0", + }, + }, + CsvJSON: string(foov2csv), + Objects: []string{string(foov2csv), string(foov2crd)}, + }, + }, + }, + assertion: require.NoError, + }, + { + name: "Success/DeclcfgDirectory", + render: action.Render{ + Refs: []string{"testdata/foo-index-v0.2.0-declcfg"}, + Registry: reg, + }, + expectCfg: &declcfg.DeclarativeConfig{ + Packages: []declcfg.Package{ + { + Schema: "olm.package", + Name: "foo", + DefaultChannel: "beta", + }, + }, + Bundles: []declcfg.Bundle{ + { + Schema: "olm.bundle", + Name: "foo.v0.1.0", + Package: "foo", + Image: "test.registry/foo-operator/foo-bundle:v0.1.0", + Properties: []property.Property{ + property.MustBuildChannel("beta", ""), + property.MustBuildGVK("test.foo", "v1", "Foo"), + property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), + property.MustBuildPackage("foo", "0.1.0"), + property.MustBuildPackageRequired("bar", "v0.1.0"), + property.MustBuildSkipRange("<0.1.0"), + property.MustBuildBundleObjectData(foov1csv), + property.MustBuildBundleObjectData(foov1crd), + }, + RelatedImages: []declcfg.RelatedImage{ + { + Name: "operator", + Image: "test.registry/foo-operator/foo:v0.1.0", + }, + { + Image: "test.registry/foo-operator/foo-bundle:v0.1.0", + }, + }, + CsvJSON: string(foov1csv), + Objects: []string{string(foov1csv), string(foov1crd)}, + }, + { + Schema: "olm.bundle", + Name: "foo.v0.2.0", + Package: "foo", + Image: "test.registry/foo-operator/foo-bundle:v0.2.0", + Properties: []property.Property{ + property.MustBuildChannel("beta", "foo.v0.1.0"), + property.MustBuildChannel("stable", "foo.v0.1.0"), property.MustBuildGVK("test.foo", "v1", "Foo"), property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), property.MustBuildPackage("foo", "0.2.0"), @@ -217,6 +382,7 @@ func TestRender(t *testing.T) { Image: "test.registry/foo-operator/foo-bundle:v0.2.0", Properties: []property.Property{ property.MustBuildChannel("beta", "foo.v0.1.0"), + property.MustBuildChannel("stable", "foo.v0.1.0"), property.MustBuildGVK("test.foo", "v1", "Foo"), property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), property.MustBuildPackage("foo", "0.2.0"), @@ -247,111 +413,199 @@ func TestRender(t *testing.T) { } } -func TestRenderDBFile(t *testing.T) { +func TestAllowRefMask(t *testing.T) { + type spec struct { + name string + render action.Render + expectErr error + } - foov1csv, err := bundleImageV1.ReadFile("testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml") - require.NoError(t, err) - foov1crd, err := bundleImageV1.ReadFile("testdata/foo-bundle-v0.1.0/manifests/foos.test.foo.crd.yaml") - require.NoError(t, err) - foov2csv, err := bundleImageV2.ReadFile("testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml") - require.NoError(t, err) - foov2crd, err := bundleImageV2.ReadFile("testdata/foo-bundle-v0.2.0/manifests/foos.test.foo.crd.yaml") + reg, err := newRegistry() require.NoError(t, err) - foov1csv, err = yaml.ToJSON(foov1csv) - require.NoError(t, err) - foov1crd, err = yaml.ToJSON(foov1crd) - require.NoError(t, err) - foov2csv, err = yaml.ToJSON(foov2csv) - require.NoError(t, err) - foov2crd, err = yaml.ToJSON(foov2crd) - require.NoError(t, err) + dir := t.TempDir() + dbFile := filepath.Join(dir, "index.db") + imageMap := map[image.Reference]string{ + image.SimpleReference("test.registry/foo-operator/foo-bundle:v0.1.0"): "testdata/foo-bundle-v0.1.0", + image.SimpleReference("test.registry/foo-operator/foo-bundle:v0.2.0"): "testdata/foo-bundle-v0.2.0", + } + assert.NoError(t, generateSqliteFile(dbFile, imageMap)) - expectCfg := &declcfg.DeclarativeConfig{ - Packages: []declcfg.Package{ - { - Schema: "olm.package", - Name: "foo", - DefaultChannel: "beta", + specs := []spec{ + { + name: "SqliteImage/Allowed", + render: action.Render{ + Refs: []string{"test.registry/foo-operator/foo-index-sqlite:v0.2.0"}, + Registry: reg, + AllowedRefMask: action.RefSqliteImage, }, + expectErr: nil, }, - Bundles: []declcfg.Bundle{ - { - Schema: "olm.bundle", - Name: "foo.v0.1.0", - Package: "foo", - Image: "test.registry/foo-operator/foo-bundle:v0.1.0", - Properties: []property.Property{ - property.MustBuildChannel("beta", ""), - property.MustBuildGVK("test.foo", "v1", "Foo"), - property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("bar", "v0.1.0"), - property.MustBuildSkipRange("<0.1.0"), - property.MustBuildBundleObjectData(foov1csv), - property.MustBuildBundleObjectData(foov1crd), - }, - RelatedImages: []declcfg.RelatedImage{ - { - Name: "operator", - Image: "test.registry/foo-operator/foo:v0.1.0", - }, - { - Image: "test.registry/foo-operator/foo-bundle:v0.1.0", - }, - }, - CsvJSON: string(foov1csv), - Objects: []string{string(foov1csv), string(foov1crd)}, + { + name: "SqliteImage/NotAllowed", + render: action.Render{ + Refs: []string{"test.registry/foo-operator/foo-index-sqlite:v0.2.0"}, + Registry: reg, + AllowedRefMask: action.RefDCImage | action.RefDCDir | action.RefSqliteFile | action.RefBundleImage, }, - { - Schema: "olm.bundle", - Name: "foo.v0.2.0", - Package: "foo", - Image: "test.registry/foo-operator/foo-bundle:v0.2.0", - Properties: []property.Property{ - property.MustBuildChannel("beta", "foo.v0.1.0"), - property.MustBuildGVK("test.foo", "v1", "Foo"), - property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), - property.MustBuildPackage("foo", "0.2.0"), - property.MustBuildPackageRequired("bar", "v0.1.0"), - property.MustBuildSkipRange("<0.2.0"), - property.MustBuildSkips("foo.v0.1.1"), - property.MustBuildSkips("foo.v0.1.2"), - property.MustBuildBundleObjectData(foov2csv), - property.MustBuildBundleObjectData(foov2crd), - }, - RelatedImages: []declcfg.RelatedImage{ - { - Name: "operator", - Image: "test.registry/foo-operator/foo:v0.2.0", - }, - { - Image: "test.registry/foo-operator/foo-bundle:v0.2.0", - }, + expectErr: &action.ErrNotAllowed{}, + }, + { + name: "SqliteFile/Allowed", + render: action.Render{ + Refs: []string{dbFile}, + Registry: reg, + AllowedRefMask: action.RefSqliteFile, + }, + expectErr: nil, + }, + { + name: "SqliteFile/NotAllowed", + render: action.Render{ + Refs: []string{dbFile}, + Registry: reg, + AllowedRefMask: action.RefDCImage | action.RefDCDir | action.RefSqliteImage | action.RefBundleImage, + }, + expectErr: &action.ErrNotAllowed{}, + }, + { + name: "DeclcfgImage/Allowed", + render: action.Render{ + Refs: []string{"test.registry/foo-operator/foo-index-declcfg:v0.2.0"}, + Registry: reg, + AllowedRefMask: action.RefDCImage, + }, + expectErr: nil, + }, + { + name: "DeclcfgImage/NotAllowed", + render: action.Render{ + Refs: []string{"test.registry/foo-operator/foo-index-declcfg:v0.2.0"}, + Registry: reg, + AllowedRefMask: action.RefDCDir | action.RefSqliteImage | action.RefSqliteFile | action.RefBundleImage, + }, + expectErr: &action.ErrNotAllowed{}, + }, + { + name: "DeclcfgDir/Allowed", + render: action.Render{ + Refs: []string{"testdata/foo-index-v0.2.0-declcfg"}, + Registry: reg, + AllowedRefMask: action.RefDCDir, + }, + expectErr: nil, + }, + { + name: "DeclcfgDir/NotAllowed", + render: action.Render{ + Refs: []string{"testdata/foo-index-v0.2.0-declcfg"}, + Registry: reg, + AllowedRefMask: action.RefDCImage | action.RefSqliteImage | action.RefSqliteFile | action.RefBundleImage, + }, + expectErr: &action.ErrNotAllowed{}, + }, + { + name: "BundleImage/Allowed", + render: action.Render{ + Refs: []string{"test.registry/foo-operator/foo-bundle:v0.2.0"}, + Registry: reg, + AllowedRefMask: action.RefBundleImage, + }, + expectErr: nil, + }, + { + name: "BundleImage/NotAllowed", + render: action.Render{ + Refs: []string{"test.registry/foo-operator/foo-bundle:v0.2.0"}, + Registry: reg, + AllowedRefMask: action.RefDCImage | action.RefDCDir | action.RefSqliteImage | action.RefSqliteFile, + }, + expectErr: &action.ErrNotAllowed{}, + }, + { + name: "All/Allowed", + render: action.Render{ + Refs: []string{ + "test.registry/foo-operator/foo-index-sqlite:v0.2.0", + dbFile, + "test.registry/foo-operator/foo-index-declcfg:v0.2.0", + "testdata/foo-index-v0.2.0-declcfg", + "test.registry/foo-operator/foo-bundle:v0.2.0", }, - CsvJSON: string(foov2csv), - Objects: []string{string(foov2csv), string(foov2crd)}, + Registry: reg, }, + expectErr: nil, }, } - - dir := t.TempDir() - - dbFile := filepath.Join(dir, "index.db") - imageMap := map[image.Reference]string{ - image.SimpleReference("test.registry/foo-operator/foo-bundle:v0.1.0"): "testdata/foo-bundle-v0.1.0", - image.SimpleReference("test.registry/foo-operator/foo-bundle:v0.2.0"): "testdata/foo-bundle-v0.2.0", + for _, s := range specs { + t.Run(s.name, func(t *testing.T) { + _, err := s.render.Run(context.Background()) + require.True(t, errors.Is(err, s.expectErr), "expected error %#v to be %#v", err, s.expectErr) + }) } - assert.NoError(t, generateSqliteFile(dbFile, imageMap)) +} - render := action.Render{ - Refs: []string{dbFile}, - Registry: &image.MockRegistry{RemoteImages: map[image.Reference]*image.MockImage{}}, +func TestAllowRefMaskAllowed(t *testing.T) { + type spec struct { + name string + mask action.RefType + pass []action.RefType + fail []action.RefType + expect bool } - actualCfg, actualErr := render.Run(context.Background()) - assert.NoError(t, actualErr) - assert.Equal(t, expectCfg, actualCfg) + specs := []spec{ + { + name: "Mask/All", + mask: action.RefAll, + pass: []action.RefType{ + action.RefDCImage, + action.RefDCDir, + action.RefSqliteImage, + action.RefSqliteFile, + action.RefBundleImage, + }, + fail: []action.RefType{}, + }, + { + name: "Mask/One", + mask: action.RefDCImage, + pass: []action.RefType{ + action.RefDCImage, + }, + fail: []action.RefType{ + action.RefDCDir, + action.RefSqliteImage, + action.RefSqliteFile, + action.RefBundleImage, + }, + }, + { + name: "Mask/Some", + mask: action.RefDCImage | action.RefDCDir, + pass: []action.RefType{ + action.RefDCImage, + action.RefDCDir, + }, + fail: []action.RefType{ + action.RefSqliteImage, + action.RefSqliteFile, + action.RefBundleImage, + }, + }, + } + for _, s := range specs { + t.Run(s.name, func(t *testing.T) { + for _, c := range s.pass { + actual := s.mask.Allowed(c) + require.True(t, actual) + } + for _, c := range s.fail { + actual := s.mask.Allowed(c) + require.False(t, actual) + } + }) + } } //go:embed testdata/foo-bundle-v0.1.0/manifests/* diff --git a/staging/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml b/staging/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml index 830fb457af..189b6849eb 100644 --- a/staging/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml +++ b/staging/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml @@ -6,6 +6,7 @@ metadata: annotations: olm.skipRange: <0.1.0 spec: + displayName: "Foo Operator" customresourcedefinitions: owned: - group: test.foo diff --git a/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml b/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml index 607918ec91..d7e30bee03 100644 --- a/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml +++ b/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml @@ -6,6 +6,7 @@ metadata: annotations: olm.skipRange: <0.2.0 spec: + displayName: "Foo Operator" customresourcedefinitions: owned: - group: test.foo diff --git a/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml b/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml index 34187f125f..dc4cc05f68 100644 --- a/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml +++ b/staging/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml @@ -1,4 +1,4 @@ annotations: operators.operatorframework.io.bundle.package.v1: foo - operators.operatorframework.io.bundle.channels.v1: beta + operators.operatorframework.io.bundle.channels.v1: beta,stable operators.operatorframework.io.bundle.channel.default.v1: beta diff --git a/staging/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml b/staging/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml index da0b2131e5..2e108e380d 100644 --- a/staging/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml +++ b/staging/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml @@ -33,7 +33,7 @@ properties: value: <0.1.0 - type: olm.bundle.object value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sInJlbGF0ZWRJbWFnZXMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjEuMCIsIm5hbWUiOiJvcGVyYXRvciJ9XSwidmVyc2lvbiI6IjAuMS4wIn19 + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= - type: olm.bundle.object value: data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 @@ -51,6 +51,10 @@ properties: value: name: beta replaces: foo.v0.1.0 + - type: olm.channel + value: + name: stable + replaces: foo.v0.1.0 - type: olm.gvk value: group: test.foo @@ -77,7 +81,7 @@ properties: value: foo.v0.1.2 - type: olm.bundle.object value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sInJlbGF0ZWRJbWFnZXMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjIuMCIsIm5hbWUiOiJvcGVyYXRvciJ9XSwicmVwbGFjZXMiOiJmb28udjAuMS4wIiwic2tpcHMiOlsiZm9vLnYwLjEuMSIsImZvby52MC4xLjIiXSwidmVyc2lvbiI6IjAuMi4wIn19 + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMi4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJyZXBsYWNlcyI6ImZvby52MC4xLjAiLCJza2lwcyI6WyJmb28udjAuMS4xIiwiZm9vLnYwLjEuMiJdLCJ2ZXJzaW9uIjoiMC4yLjAifX0= - type: olm.bundle.object value: data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 diff --git a/staging/operator-registry/internal/action/testdata/list-index/bar/index.yaml b/staging/operator-registry/internal/action/testdata/list-index/bar/index.yaml new file mode 100644 index 0000000000..0d2a3610d7 --- /dev/null +++ b/staging/operator-registry/internal/action/testdata/list-index/bar/index.yaml @@ -0,0 +1,91 @@ +--- +schema: olm.package +name: bar +defaultChannel: beta +--- +schema: olm.bundle +package: bar +name: bar.v0.1.0 +image: test.registry/bar-operator/bar-bundle:v0.1.0 +properties: + - type: olm.channel + value: + name: beta + - type: olm.gvk + value: + group: test.bar + kind: Bar + version: v1 + - type: olm.gvk.required + value: + group: test.baz + kind: Baz + version: v1alpha1 + - type: olm.package + value: + packageName: bar + version: 0.1.0 + - type: olm.package.required + value: + packageName: baz + versionRange: v0.1.0 + - type: olm.skipRange + value: <0.1.0 + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJiYXIudjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmJhciIsImtpbmQiOiJCYXIiLCJuYW1lIjoiYmFycy50ZXN0LmJhciIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiQmFyIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Jhci1vcGVyYXRvci9iYXI6djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhcnMudGVzdC5iYXIifSwic3BlYyI6eyJncm91cCI6InRlc3QuYmFyIiwibmFtZXMiOnsia2luZCI6IkJhciIsInBsdXJhbCI6ImJhcnMifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 +relatedImages: + - image: test.registry/bar-operator/bar:v0.1.0 + name: operator + - image: test.registry/bar-operator/bar-bundle:v0.1.0 +--- +schema: olm.bundle +package: bar +name: bar.v0.2.0 +image: test.registry/bar-operator/bar-bundle:v0.2.0 +properties: + - type: olm.channel + value: + name: beta + replaces: bar.v0.1.0 + - type: olm.channel + value: + name: stable + replaces: bar.v0.1.0 + - type: olm.gvk + value: + group: test.bar + kind: Bar + version: v1 + - type: olm.gvk.required + value: + group: test.baz + kind: Baz + version: v1alpha1 + - type: olm.package + value: + packageName: bar + version: 0.2.0 + - type: olm.package.required + value: + packageName: baz + versionRange: v0.1.0 + - type: olm.skipRange + value: <0.2.0 + - type: olm.skips + value: bar.v0.1.1 + - type: olm.skips + value: bar.v0.1.2 + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJiYXIudjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmJhciIsImtpbmQiOiJCYXIiLCJuYW1lIjoiYmFycy50ZXN0LmJhciIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiQmFyIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Jhci1vcGVyYXRvci9iYXI6djAuMi4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJyZXBsYWNlcyI6ImJhci52MC4xLjAiLCJza2lwcyI6WyJiYXIudjAuMS4xIiwiYmFyLnYwLjEuMiJdLCJ2ZXJzaW9uIjoiMC4yLjAifX0= + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhcnMudGVzdC5iYXIifSwic3BlYyI6eyJncm91cCI6InRlc3QuYmFyIiwibmFtZXMiOnsia2luZCI6IkJhciIsInBsdXJhbCI6ImJhcnMifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 +relatedImages: + - image: test.registry/bar-operator/bar:v0.2.0 + name: operator + - image: test.registry/bar-operator/bar-bundle:v0.2.0 diff --git a/staging/operator-registry/internal/action/testdata/list-index/foo/index.yaml b/staging/operator-registry/internal/action/testdata/list-index/foo/index.yaml new file mode 100644 index 0000000000..2e108e380d --- /dev/null +++ b/staging/operator-registry/internal/action/testdata/list-index/foo/index.yaml @@ -0,0 +1,91 @@ +--- +schema: olm.package +name: foo +defaultChannel: beta +--- +schema: olm.bundle +package: foo +name: foo.v0.1.0 +image: test.registry/foo-operator/foo-bundle:v0.1.0 +properties: + - type: olm.channel + value: + name: beta + - type: olm.gvk + value: + group: test.foo + kind: Foo + version: v1 + - type: olm.gvk.required + value: + group: test.bar + kind: Bar + version: v1alpha1 + - type: olm.package + value: + packageName: foo + version: 0.1.0 + - type: olm.package.required + value: + packageName: bar + versionRange: v0.1.0 + - type: olm.skipRange + value: <0.1.0 + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 +relatedImages: + - image: test.registry/foo-operator/foo:v0.1.0 + name: operator + - image: test.registry/foo-operator/foo-bundle:v0.1.0 +--- +schema: olm.bundle +package: foo +name: foo.v0.2.0 +image: test.registry/foo-operator/foo-bundle:v0.2.0 +properties: + - type: olm.channel + value: + name: beta + replaces: foo.v0.1.0 + - type: olm.channel + value: + name: stable + replaces: foo.v0.1.0 + - type: olm.gvk + value: + group: test.foo + kind: Foo + version: v1 + - type: olm.gvk.required + value: + group: test.bar + kind: Bar + version: v1alpha1 + - type: olm.package + value: + packageName: foo + version: 0.2.0 + - type: olm.package.required + value: + packageName: bar + versionRange: v0.1.0 + - type: olm.skipRange + value: <0.2.0 + - type: olm.skips + value: foo.v0.1.1 + - type: olm.skips + value: foo.v0.1.2 + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMi4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJyZXBsYWNlcyI6ImZvby52MC4xLjAiLCJza2lwcyI6WyJmb28udjAuMS4xIiwiZm9vLnYwLjEuMiJdLCJ2ZXJzaW9uIjoiMC4yLjAifX0= + - type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 +relatedImages: + - image: test.registry/foo-operator/foo:v0.2.0 + name: operator + - image: test.registry/foo-operator/foo-bundle:v0.2.0 diff --git a/staging/operator-registry/internal/declcfg/helpers_test.go b/staging/operator-registry/internal/declcfg/helpers_test.go index d41fc99f75..748692a42a 100644 --- a/staging/operator-registry/internal/declcfg/helpers_test.go +++ b/staging/operator-registry/internal/declcfg/helpers_test.go @@ -125,6 +125,12 @@ func newTestBundle(packageName, version string, opts ...bundleOpt) Bundle { for _, opt := range opts { opt(&b) } + sort.Slice(b.Properties, func(i, j int) bool { + if b.Properties[i].Type != b.Properties[j].Type { + return b.Properties[i].Type < b.Properties[j].Type + } + return string(b.Properties[i].Value) < string(b.Properties[j].Value) + }) return b } diff --git a/staging/operator-registry/internal/declcfg/model_to_declcfg.go b/staging/operator-registry/internal/declcfg/model_to_declcfg.go index d3d4ed686d..df8357a917 100644 --- a/staging/operator-registry/internal/declcfg/model_to_declcfg.go +++ b/staging/operator-registry/internal/declcfg/model_to_declcfg.go @@ -68,6 +68,14 @@ func traverseModelChannels(mpkg model.Package) []Bundle { var out []Bundle for _, b := range bundles { b.Properties = property.Deduplicate(b.Properties) + + sort.Slice(b.Properties, func(i, j int) bool { + if b.Properties[i].Type != b.Properties[j].Type { + return b.Properties[i].Type < b.Properties[j].Type + } + return string(b.Properties[i].Value) < string(b.Properties[j].Value) + }) + out = append(out, *b) } return out diff --git a/staging/operator-registry/internal/declcfg/write_test.go b/staging/operator-registry/internal/declcfg/write_test.go index e48c139efd..4b69bc5b05 100644 --- a/staging/operator-registry/internal/declcfg/write_test.go +++ b/staging/operator-registry/internal/declcfg/write_test.go @@ -35,10 +35,9 @@ func TestWriteJSON(t *testing.T) { "image": "anakin-bundle:v0.0.1", "properties": [ { - "type": "olm.package", + "type": "olm.bundle.object", "value": { - "packageName": "anakin", - "version": "0.0.1" + "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" } }, { @@ -48,9 +47,9 @@ func TestWriteJSON(t *testing.T) { } }, { - "type": "olm.bundle.object", + "type": "olm.channel", "value": { - "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" + "name": "dark" } }, { @@ -60,9 +59,10 @@ func TestWriteJSON(t *testing.T) { } }, { - "type": "olm.channel", + "type": "olm.package", "value": { - "name": "dark" + "packageName": "anakin", + "version": "0.0.1" } } ], @@ -80,10 +80,9 @@ func TestWriteJSON(t *testing.T) { "image": "anakin-bundle:v0.1.0", "properties": [ { - "type": "olm.package", + "type": "olm.bundle.object", "value": { - "packageName": "anakin", - "version": "0.1.0" + "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" } }, { @@ -93,9 +92,10 @@ func TestWriteJSON(t *testing.T) { } }, { - "type": "olm.bundle.object", + "type": "olm.channel", "value": { - "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" + "name": "dark", + "replaces": "anakin.v0.0.1" } }, { @@ -106,10 +106,10 @@ func TestWriteJSON(t *testing.T) { } }, { - "type": "olm.channel", + "type": "olm.package", "value": { - "name": "dark", - "replaces": "anakin.v0.0.1" + "packageName": "anakin", + "version": "0.1.0" } } ], @@ -127,10 +127,9 @@ func TestWriteJSON(t *testing.T) { "image": "anakin-bundle:v0.1.1", "properties": [ { - "type": "olm.package", + "type": "olm.bundle.object", "value": { - "packageName": "anakin", - "version": "0.1.1" + "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" } }, { @@ -140,16 +139,17 @@ func TestWriteJSON(t *testing.T) { } }, { - "type": "olm.bundle.object", + "type": "olm.channel", "value": { - "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" + "name": "dark", + "replaces": "anakin.v0.0.1" } }, { - "type": "olm.channel", + "type": "olm.package", "value": { - "name": "dark", - "replaces": "anakin.v0.0.1" + "packageName": "anakin", + "version": "0.1.1" } }, { @@ -186,10 +186,9 @@ func TestWriteJSON(t *testing.T) { "image": "boba-fett-bundle:v1.0.0", "properties": [ { - "type": "olm.package", + "type": "olm.bundle.object", "value": { - "packageName": "boba-fett", - "version": "1.0.0" + "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" } }, { @@ -199,15 +198,16 @@ func TestWriteJSON(t *testing.T) { } }, { - "type": "olm.bundle.object", + "type": "olm.channel", "value": { - "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" + "name": "mando" } }, { - "type": "olm.channel", + "type": "olm.package", "value": { - "name": "mando" + "packageName": "boba-fett", + "version": "1.0.0" } } ], @@ -225,10 +225,9 @@ func TestWriteJSON(t *testing.T) { "image": "boba-fett-bundle:v2.0.0", "properties": [ { - "type": "olm.package", + "type": "olm.bundle.object", "value": { - "packageName": "boba-fett", - "version": "2.0.0" + "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" } }, { @@ -238,16 +237,17 @@ func TestWriteJSON(t *testing.T) { } }, { - "type": "olm.bundle.object", + "type": "olm.channel", "value": { - "data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0=" + "name": "mando", + "replaces": "boba-fett.v1.0.0" } }, { - "type": "olm.channel", + "type": "olm.package", "value": { - "name": "mando", - "replaces": "boba-fett.v1.0.0" + "packageName": "boba-fett", + "version": "2.0.0" } } ], @@ -305,22 +305,22 @@ image: anakin-bundle:v0.0.1 name: anakin.v0.0.1 package: anakin properties: -- type: olm.package +- type: olm.bundle.object value: - packageName: anakin - version: 0.0.1 + data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= - type: olm.bundle.object value: ref: objects/anakin.v0.0.1.csv.yaml -- type: olm.bundle.object +- type: olm.channel value: - data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= + name: dark - type: olm.channel value: name: light -- type: olm.channel +- type: olm.package value: - name: dark + packageName: anakin + version: 0.0.1 relatedImages: - image: anakin-bundle:v0.0.1 name: bundle @@ -330,24 +330,24 @@ image: anakin-bundle:v0.1.0 name: anakin.v0.1.0 package: anakin properties: -- type: olm.package - value: - packageName: anakin - version: 0.1.0 - type: olm.bundle.object value: - ref: objects/anakin.v0.1.0.csv.yaml + data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= - type: olm.bundle.object value: - data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= + ref: objects/anakin.v0.1.0.csv.yaml - type: olm.channel value: - name: light + name: dark replaces: anakin.v0.0.1 - type: olm.channel value: - name: dark + name: light replaces: anakin.v0.0.1 +- type: olm.package + value: + packageName: anakin + version: 0.1.0 relatedImages: - image: anakin-bundle:v0.1.0 name: bundle @@ -357,20 +357,20 @@ image: anakin-bundle:v0.1.1 name: anakin.v0.1.1 package: anakin properties: -- type: olm.package - value: - packageName: anakin - version: 0.1.1 - type: olm.bundle.object value: - ref: objects/anakin.v0.1.1.csv.yaml + data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= - type: olm.bundle.object value: - data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= + ref: objects/anakin.v0.1.1.csv.yaml - type: olm.channel value: name: dark replaces: anakin.v0.0.1 +- type: olm.package + value: + packageName: anakin + version: 0.1.1 - type: olm.skips value: anakin.v0.1.0 relatedImages: @@ -394,19 +394,19 @@ image: boba-fett-bundle:v1.0.0 name: boba-fett.v1.0.0 package: boba-fett properties: -- type: olm.package - value: - packageName: boba-fett - version: 1.0.0 - type: olm.bundle.object value: - ref: objects/boba-fett.v1.0.0.csv.yaml + data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= - type: olm.bundle.object value: - data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= + ref: objects/boba-fett.v1.0.0.csv.yaml - type: olm.channel value: name: mando +- type: olm.package + value: + packageName: boba-fett + version: 1.0.0 relatedImages: - image: boba-fett-bundle:v1.0.0 name: bundle @@ -416,20 +416,20 @@ image: boba-fett-bundle:v2.0.0 name: boba-fett.v2.0.0 package: boba-fett properties: -- type: olm.package - value: - packageName: boba-fett - version: 2.0.0 - type: olm.bundle.object value: - ref: objects/boba-fett.v2.0.0.csv.yaml + data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= - type: olm.bundle.object value: - data: eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0= + ref: objects/boba-fett.v2.0.0.csv.yaml - type: olm.channel value: name: mando replaces: boba-fett.v1.0.0 +- type: olm.package + value: + packageName: boba-fett + version: 2.0.0 relatedImages: - image: boba-fett-bundle:v2.0.0 name: bundle diff --git a/staging/operator-registry/internal/model/model.go b/staging/operator-registry/internal/model/model.go index e0abf6fbf5..78639d93d0 100644 --- a/staging/operator-registry/internal/model/model.go +++ b/staging/operator-registry/internal/model/model.go @@ -3,6 +3,7 @@ package model import ( "errors" "fmt" + "sort" "strings" "github.com/h2non/filetype" @@ -160,6 +161,7 @@ func (c Channel) Head() (*Bundle, error) { for _, head := range heads { headNames = append(headNames, head.Name) } + sort.Strings(headNames) return nil, fmt.Errorf("multiple channel heads found in graph: %s", strings.Join(headNames, ", ")) } return heads[0], nil @@ -269,9 +271,6 @@ type RelatedImage struct { func (i RelatedImage) Validate() error { result := newValidationError("invalid related image") - if i.Name == "" { - result.subErrors = append(result.subErrors, fmt.Errorf("name must be set")) - } if i.Image == "" { result.subErrors = append(result.subErrors, fmt.Errorf("image must be set")) } diff --git a/staging/operator-registry/internal/model/model_test.go b/staging/operator-registry/internal/model/model_test.go index 99a1799d05..7ff3cc34fb 100644 --- a/staging/operator-registry/internal/model/model_test.go +++ b/staging/operator-registry/internal/model/model_test.go @@ -97,7 +97,7 @@ func TestChannelHead(t *testing.T) { "anakin.v0.0.1": {Name: "anakin.v0.0.1", Replaces: "anakin.v0.0.3"}, "anakin.v0.0.3": head, }}, - assertion: require.Error, + assertion: hasError(`no channel head found in graph`), }, { name: "Error/MultipleChannelHeads", @@ -106,7 +106,7 @@ func TestChannelHead(t *testing.T) { "anakin.v0.0.3": head, "anakin.v0.0.4": {Name: "anakin.v0.0.4", Replaces: "anakin.v0.0.1"}, }}, - assertion: require.Error, + assertion: hasError(`multiple channel heads found in graph: anakin.v0.0.3, anakin.v0.0.4`), }, } for _, s := range specs { @@ -118,6 +118,32 @@ func TestChannelHead(t *testing.T) { } } +func hasError(expectedError string) require.ErrorAssertionFunc { + return func(t require.TestingT, actualError error, args ...interface{}) { + if stdt, ok := t.(*testing.T); ok { + stdt.Helper() + } + errsToCheck := []error{actualError} + for len(errsToCheck) > 0 { + var err error + err, errsToCheck = errsToCheck[0], errsToCheck[1:] + if err == nil { + continue + } + if verr, ok := err.(*validationError); ok { + if verr.message == expectedError { + return + } + errsToCheck = append(errsToCheck, verr.subErrors...) + } else if expectedError == err.Error() { + return + } + } + t.Errorf("expected error to be or contain suberror %q, got %v", expectedError, actualError) + t.FailNow() + } +} + func TestValidators(t *testing.T) { type spec struct { name string @@ -144,14 +170,14 @@ func TestValidators(t *testing.T) { v: Model{ "foo": pkg, }, - assertion: require.Error, + assertion: hasError(`package key "foo" does not match package name "anakin"`), }, { name: "Model/Error/InvalidPackage", v: Model{ pkgIncorrectDefaultChannel.Name: pkgIncorrectDefaultChannel, }, - assertion: require.Error, + assertion: hasError(`invalid package "anakin"`), }, { name: "Package/Success/Valid", @@ -161,23 +187,23 @@ func TestValidators(t *testing.T) { { name: "Package/Error/NoName", v: &Package{}, - assertion: require.Error, - }, - { - name: "Package/Error/InvalidIcon", - v: &Package{ - Name: "anakin", - Icon: &Icon{Data: mustBase64Decode(svgData)}, - }, - assertion: require.Error, + assertion: hasError("package name must not be empty"), }, + //{ + // name: "Package/Error/InvalidIcon", + // v: &Package{ + // Name: "anakin", + // Icon: &Icon{Data: mustBase64Decode(svgData)}, + // }, + // assertion: hasError("icon mediatype must be set if icon is defined"), + //}, { name: "Package/Error/NoChannels", v: &Package{ Name: "anakin", Icon: &Icon{Data: mustBase64Decode(svgData), MediaType: "image/svg+xml"}, }, - assertion: require.Error, + assertion: hasError("package must contain at least one channel"), }, { name: "Package/Error/NoDefaultChannel", @@ -186,7 +212,7 @@ func TestValidators(t *testing.T) { Icon: &Icon{Data: mustBase64Decode(svgData), MediaType: "image/svg+xml"}, Channels: map[string]*Channel{"light": ch}, }, - assertion: require.Error, + assertion: hasError("default channel must be set"), }, { name: "Package/Error/ChannelKeyNameMismatch", @@ -196,7 +222,7 @@ func TestValidators(t *testing.T) { DefaultChannel: ch, Channels: map[string]*Channel{"dark": ch}, }, - assertion: require.Error, + assertion: hasError(`channel key "dark" does not match channel name "light"`), }, { name: "Package/Error/InvalidChannel", @@ -206,7 +232,7 @@ func TestValidators(t *testing.T) { DefaultChannel: ch, Channels: map[string]*Channel{"light": {Name: "light"}}, }, - assertion: require.Error, + assertion: hasError(`invalid channel "light"`), }, { name: "Package/Error/InvalidChannelPackageLink", @@ -216,12 +242,12 @@ func TestValidators(t *testing.T) { DefaultChannel: ch, Channels: map[string]*Channel{"light": ch}, }, - assertion: require.Error, + assertion: hasError(`channel "light" not correctly linked to parent package`), }, { name: "Package/Error/DefaultChannelNotInChannelMap", v: pkgIncorrectDefaultChannel, - assertion: require.Error, + assertion: hasError(`default channel "not-found" not found in channels list`), }, { name: "Icon/Success/ValidSVG", @@ -258,7 +284,7 @@ func TestValidators(t *testing.T) { // Data: nil, // MediaType: "image/svg+xml", // }, - // assertion: require.Error, + // assertion: hasError(`icon data must be set if icon is defined`), //}, //{ // name: "Icon/Error/NoMediaType", @@ -266,7 +292,7 @@ func TestValidators(t *testing.T) { // Data: mustBase64Decode(svgData), // MediaType: "", // }, - // assertion: require.Error, + // assertion: hasError(`icon mediatype must be set if icon is defined`), //}, //{ // name: "Icon/Error/DataIsNotImage", @@ -274,7 +300,7 @@ func TestValidators(t *testing.T) { // Data: []byte("{}"), // MediaType: "application/json", // }, - // assertion: require.Error, + // assertion: hasError(`icon data is not an image`), //}, //{ // name: "Icon/Error/DataDoesNotMatchMediaType", @@ -282,7 +308,7 @@ func TestValidators(t *testing.T) { // Data: mustBase64Decode(svgData), // MediaType: "image/jpeg", // }, - // assertion: require.Error, + // assertion: hasError(`icon media type "image/jpeg" does not match detected media type "image/svg+xml"`), //}, { name: "Channel/Success/Valid", @@ -292,14 +318,14 @@ func TestValidators(t *testing.T) { { name: "Channel/Error/NoName", v: &Channel{}, - assertion: require.Error, + assertion: hasError(`channel name must not be empty`), }, { name: "Channel/Error/NoPackage", v: &Channel{ Name: "light", }, - assertion: require.Error, + assertion: hasError(`package must be set`), }, { name: "Channel/Error/NoBundles", @@ -307,7 +333,7 @@ func TestValidators(t *testing.T) { Package: pkg, Name: "light", }, - assertion: require.Error, + assertion: hasError(`channel must contain at least one bundle`), }, { name: "Channel/Error/InvalidHead", @@ -319,7 +345,7 @@ func TestValidators(t *testing.T) { "anakin.v0.0.1": {Name: "anakin.v0.0.1"}, }, }, - assertion: require.Error, + assertion: hasError(`multiple channel heads found in graph: anakin.v0.0.0, anakin.v0.0.1`), }, { name: "Channel/Error/BundleKeyNameMismatch", @@ -330,7 +356,7 @@ func TestValidators(t *testing.T) { "foo": {Name: "bar"}, }, }, - assertion: require.Error, + assertion: hasError(`bundle key "foo" does not match bundle name "bar"`), }, { name: "Channel/Error/InvalidBundle", @@ -341,7 +367,7 @@ func TestValidators(t *testing.T) { "anakin.v0.0.0": {Name: "anakin.v0.0.0"}, }, }, - assertion: require.Error, + assertion: hasError(`invalid bundle "anakin.v0.0.0"`), }, { name: "Channel/Error/InvalidBundleChannelLink", @@ -357,7 +383,7 @@ func TestValidators(t *testing.T) { }, }, }, - assertion: require.Error, + assertion: hasError(`bundle "anakin.v0.0.0" not correctly linked to parent channel`), }, { name: "Bundle/Success/Valid", @@ -411,7 +437,7 @@ func TestValidators(t *testing.T) { assertion: require.NoError, }, { - name: "Bundle/Error/NoBundleImage/NoBundleData", + name: "Bundle/Error/NoBundleImage", v: &Bundle{ Package: pkg, Channel: ch, @@ -423,19 +449,19 @@ func TestValidators(t *testing.T) { property.MustBuildChannel("light", "anakin.v0.0.1"), }, }, - assertion: require.Error, + assertion: hasError(`bundle image must be set`), }, { name: "Bundle/Error/NoName", v: &Bundle{}, - assertion: require.Error, + assertion: hasError(`name must be set`), }, { name: "Bundle/Error/NoChannel", v: &Bundle{ Name: "anakin.v0.1.0", }, - assertion: require.Error, + assertion: hasError(`channel must be set`), }, { name: "Bundle/Error/NoPackage", @@ -443,7 +469,7 @@ func TestValidators(t *testing.T) { Channel: ch, Name: "anakin.v0.1.0", }, - assertion: require.Error, + assertion: hasError(`package must be set`), }, { name: "Bundle/Error/WrongPackage", @@ -452,7 +478,7 @@ func TestValidators(t *testing.T) { Channel: ch, Name: "anakin.v0.1.0", }, - assertion: require.Error, + assertion: hasError(`package does not match channel's package`), }, { name: "Bundle/Error/InvalidProperty", @@ -461,9 +487,9 @@ func TestValidators(t *testing.T) { Channel: ch, Name: "anakin.v0.1.0", Replaces: "anakin.v0.0.1", - Properties: []property.Property{{Value: json.RawMessage("")}}, + Properties: []property.Property{{Type: "broken", Value: json.RawMessage("")}}, }, - assertion: require.Error, + assertion: hasError(`parse property[0] of type "broken": unexpected end of JSON input`), }, { name: "Bundle/Error/EmptySkipsValue", @@ -475,19 +501,7 @@ func TestValidators(t *testing.T) { Properties: []property.Property{{Type: "custom", Value: json.RawMessage("{}")}}, Skips: []string{""}, }, - assertion: require.Error, - }, - { - name: "Bundle/Error/SkipsNotInPackage", - v: &Bundle{ - Package: pkg, - Channel: ch, - Name: "anakin.v0.1.0", - Replaces: "anakin.v0.0.1", - Properties: []property.Property{{Type: "custom", Value: json.RawMessage("{}")}}, - Skips: []string{"foobar"}, - }, - assertion: require.Error, + assertion: hasError(`skip[0] is empty`), }, { name: "Bundle/Error/MissingPackage", @@ -504,7 +518,7 @@ func TestValidators(t *testing.T) { property.MustBuildChannel("dark", "anakin.v0.0.1"), }, }, - assertion: require.Error, + assertion: hasError(`must be exactly one property with type "olm.package"`), }, { name: "Bundle/Error/MultiplePackages", @@ -523,7 +537,7 @@ func TestValidators(t *testing.T) { property.MustBuildChannel("dark", "anakin.v0.0.1"), }, }, - assertion: require.Error, + assertion: hasError(`must be exactly one property with type "olm.package"`), }, { name: "RelatedImage/Success/Valid", @@ -533,21 +547,13 @@ func TestValidators(t *testing.T) { }, assertion: require.NoError, }, - { - name: "RelatedImage/Error/NoName", - v: RelatedImage{ - Name: "", - Image: "", - }, - assertion: require.Error, - }, { name: "RelatedImage/Error/NoImage", v: RelatedImage{ Name: "foo", Image: "", }, - assertion: require.Error, + assertion: hasError(`image must be set`), }, } for _, s := range specs { diff --git a/values.yaml b/values.yaml index aa68e15ded..7b476635ba 100644 --- a/values.yaml +++ b/values.yaml @@ -13,7 +13,9 @@ olm: ref: quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 pullPolicy: IfNotPresent service: - internalPort: 8080 + internalPort: 8443 + externalPort: 8443 + clientCASecret: olm-operator-serving-cert nodeSelector: kubernetes.io/os: linux node-role.kubernetes.io/master: "" @@ -29,19 +31,22 @@ olm: operator: Exists effect: NoExecute tolerationSeconds: 120 - tlsCertPath: /var/run/secrets/serving-cert/tls.crt - tlsKeyPath: /var/run/secrets/serving-cert/tls.key + tlsSecret: olm-operator-serving-cert resources: requests: cpu: 10m memory: 160Mi catalog: replicaCount: 1 + opmImageArgs: -opmImage=quay.io/operator-framework/configmap-operator-registry:latest image: ref: quay.io/operator-framework/olm@sha256:de396b540b82219812061d0d753440d5655250c621c753ed1dc67d6154741607 pullPolicy: IfNotPresent service: - internalPort: 8080 + internalPort: 8443 + externalPort: 8443 + clientCASecret: catalog-operator-serving-cert + tlsSecret: catalog-operator-serving-cert nodeSelector: kubernetes.io/os: linux node-role.kubernetes.io/master: "" diff --git a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go index 7eaae1e68c..e048d4988c 100644 --- a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go +++ b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go @@ -102,6 +102,9 @@ const ( // SubscriptionInstallPlanFailed indicates that the installation of a Subscription's InstallPlan has failed. SubscriptionInstallPlanFailed SubscriptionConditionType = "InstallPlanFailed" + + // SubscriptionResolutionFailed indicates that the dependency resolution in the namespace in which the subscription is created has failed + SubscriptionResolutionFailed SubscriptionConditionType = "ResolutionFailed" ) const ( diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/catalog/main.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/catalog/main.go index 4f945eb008..f8128a4c2c 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/catalog/main.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/catalog/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "crypto/tls" "flag" "fmt" "net/http" @@ -10,7 +9,6 @@ import ( "time" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" - "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" utilclock "k8s.io/apimachinery/pkg/util/clock" k8sscheme "k8s.io/client-go/kubernetes/scheme" @@ -18,10 +16,9 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals" "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version" @@ -31,7 +28,8 @@ const ( catalogNamespaceEnvVarName = "GLOBAL_CATALOG_NAMESPACE" defaultWakeupInterval = 15 * time.Minute defaultCatalogNamespace = "openshift-operator-lifecycle-manager" - defaultConfigMapServerImage = "quay.io/operatorframework/configmap-operator-registry:latest" + defaultConfigMapServerImage = "quay.io/operator-framework/configmap-operator-registry:latest" + defaultOPMImage = "quay.io/operator-framework/upstream-opm-builder:latest" defaultUtilImage = "quay.io/operator-framework/olm:latest" defaultOperatorName = "" ) @@ -50,6 +48,9 @@ var ( configmapServerImage = flag.String( "configmapServerImage", defaultConfigMapServerImage, "the image to use for serving the operator registry api for a configmap") + opmImage = flag.String( + "opmImage", defaultOPMImage, "the image to use for unpacking bundle content with opm") + utilImage = flag.String( "util-image", defaultUtilImage, "an image containing custom olm utilities") @@ -67,8 +68,9 @@ var ( tlsCertPath = flag.String( "tls-cert", "", "Path to use for certificate key (requires tls-key)") - profiling = flag.Bool( - "profiling", false, "serve profiling data (on port 8080)") + profiling = flag.Bool("profiling", false, "deprecated") + + clientCAPath = flag.String("client-ca", "", "path to watch for client ca bundle") installPlanTimeout = flag.Duration("install-plan-retry-timeout", 1*time.Minute, "time since first attempt at which plan execution errors are considered fatal") bundleUnpackTimeout = flag.Duration("bundle-unpack-timeout", 10*time.Minute, "The time limit for bundle unpacking, after which InstallPlan execution is considered to have failed. 0 is considered as having no timeout.") @@ -106,59 +108,16 @@ func main() { *catalogNamespace = catalogNamespaceEnvVarValue } - var useTLS bool - if *tlsCertPath != "" && *tlsKeyPath == "" || *tlsCertPath == "" && *tlsKeyPath != "" { - logger.Warn("both --tls-key and --tls-crt must be provided for TLS to be enabled, falling back to non-https") - } else if *tlsCertPath == "" && *tlsKeyPath == "" { - logger.Info("TLS keys not set, using non-https for metrics") - } else { - logger.Info("TLS keys set, using https for metrics") - useTLS = true - } - - // Serve a health check. - healthMux := http.NewServeMux() - healthMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - // Serve profiling if enabled - if *profiling { - logger.Infof("profiling enabled") - profile.RegisterHandlers(healthMux) + listenAndServe, err := server.GetListenAndServeFunc(logger, tlsCertPath, tlsKeyPath, clientCAPath) + if err != nil { + logger.Fatal("Error setting up health/metric/pprof service: %v", err) } - go http.ListenAndServe(":8080", healthMux) - - metricsMux := http.NewServeMux() - metricsMux.Handle("/metrics", promhttp.Handler()) - if useTLS { - tlsGetCertFn, err := filemonitor.OLMGetCertRotationFn(logger, *tlsCertPath, *tlsKeyPath) - if err != nil { - logger.Errorf("Certificate monitoring for metrics (https) failed: %v", err) + go func() { + if err := listenAndServe(); err != nil && err != http.ErrServerClosed { + logger.Error(err) } - - go func() { - httpsServer := &http.Server{ - Addr: ":8081", - Handler: metricsMux, - TLSConfig: &tls.Config{ - GetCertificate: tlsGetCertFn, - }, - } - err := httpsServer.ListenAndServeTLS("", "") - if err != nil { - logger.Errorf("Metrics (https) serving failed: %v", err) - } - }() - } else { - go func() { - err := http.ListenAndServe(":8081", metricsMux) - if err != nil { - logger.Errorf("Metrics (http) serving failed: %v", err) - } - }() - } + }() // create a config client for operator status config, err := clientcmd.BuildConfigFromFlags("", *kubeConfigPath) @@ -176,7 +135,7 @@ func main() { } // Create a new instance of the operator. - op, err := catalog.NewOperator(ctx, *kubeConfigPath, utilclock.RealClock{}, logger, *wakeupInterval, *configmapServerImage, *utilImage, *catalogNamespace, k8sscheme.Scheme, *installPlanTimeout, *bundleUnpackTimeout) + op, err := catalog.NewOperator(ctx, *kubeConfigPath, utilclock.RealClock{}, logger, *wakeupInterval, *configmapServerImage, *opmImage, *utilImage, *catalogNamespace, k8sscheme.Scheme, *installPlanTimeout, *bundleUnpackTimeout) if err != nil { log.Panicf("error configuring operator: %s", err.Error()) } diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/main.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/main.go index 087ebb74a8..f9bec9f689 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/main.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "crypto/tls" "flag" "fmt" "net/http" @@ -12,7 +11,6 @@ import ( configclientset "github.com/openshift/client-go/config/clientset/versioned" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" - "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" "github.com/spf13/pflag" v1 "k8s.io/api/core/v1" @@ -23,11 +21,10 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/openshift" "github.com/operator-framework/operator-lifecycle-manager/pkg/feature" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals" "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version" @@ -66,8 +63,9 @@ var ( tlsCertPath = pflag.String( "tls-cert", "", "Path to use for certificate key (requires tls-key)") - profiling = pflag.Bool( - "profiling", false, "serve profiling data (on port 8080)") + profiling = pflag.Bool("profiling", false, "deprecated") + + clientCAPath = pflag.String("client-ca", "", "path to watch for client ca bundle") namespace = pflag.String( "namespace", "", "namespace where cleanup runs") @@ -120,65 +118,17 @@ func main() { } logger.Infof("log level %s", logger.Level) - var useTLS bool - if *tlsCertPath != "" && *tlsKeyPath == "" || *tlsCertPath == "" && *tlsKeyPath != "" { - logger.Warn("both --tls-key and --tls-crt must be provided for TLS to be enabled, falling back to non-https") - } else if *tlsCertPath == "" && *tlsKeyPath == "" { - logger.Info("TLS keys not set, using non-https for metrics") - } else { - logger.Info("TLS keys set, using https for metrics") - useTLS = true - } - - // Serve a health check. - healthMux := http.NewServeMux() - healthMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - // Serve profiling if enabled - if *profiling { - logger.Infof("profiling enabled") - profile.RegisterHandlers(healthMux) + listenAndServe, err := server.GetListenAndServeFunc(logger, tlsCertPath, tlsKeyPath, clientCAPath) + if err != nil { + logger.Fatal("Error setting up health/metric/pprof service: %v", err) } go func() { - err := http.ListenAndServe(":8080", healthMux) - if err != nil { - logger.Errorf("Health serving failed: %v", err) + if err := listenAndServe(); err != nil && err != http.ErrServerClosed { + logger.Error(err) } }() - metricsMux := http.NewServeMux() - metricsMux.Handle("/metrics", promhttp.Handler()) - if useTLS { - tlsGetCertFn, err := filemonitor.OLMGetCertRotationFn(logger, *tlsCertPath, *tlsKeyPath) - if err != nil { - logger.Errorf("Certificate monitoring for metrics (https) failed: %v", err) - } - - go func() { - httpsServer := &http.Server{ - Addr: ":8081", - Handler: metricsMux, - TLSConfig: &tls.Config{ - GetCertificate: tlsGetCertFn, - }, - } - err := httpsServer.ListenAndServeTLS("", "") - if err != nil { - logger.Errorf("Metrics (https) serving failed: %v", err) - } - }() - } else { - go func() { - err := http.ListenAndServe(":8081", metricsMux) - if err != nil { - logger.Errorf("Metrics (http) serving failed: %v", err) - } - }() - } - mgr, err := Manager(ctx, *debug) if err != nil { logger.WithError(err).Fatalf("error configuring controller manager") diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/manager.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/manager.go index f01b6c77c6..5e8e33ab73 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/manager.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/cmd/olm/manager.go @@ -2,31 +2,62 @@ package main import ( "context" - "k8s.io/apimachinery/pkg/labels" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/selection" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/log/zap" + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators" "github.com/operator-framework/operator-lifecycle-manager/pkg/feature" ) +var ( + copiedLabelDoesNotExist labels.Selector +) + +func init() { + requirement, err := labels.NewRequirement(operatorsv1alpha1.CopiedLabelKey, selection.DoesNotExist, nil) + if err != nil { + panic(err) + } + copiedLabelDoesNotExist = labels.NewSelector().Add(*requirement) +} + func Manager(ctx context.Context, debug bool) (ctrl.Manager, error) { ctrl.SetLogger(zap.New(zap.UseDevMode(debug))) setupLog := ctrl.Log.WithName("setup").V(1) - // Setup a Manager + scheme := runtime.NewScheme() + if err := metav1.AddMetaToScheme(scheme); err != nil { + return nil, err + } + if err := operators.AddToScheme(scheme); err != nil { + // ctrl.NewManager needs the Scheme to be populated + // up-front so that the NewCache implementation we + // provide can configure custom cache behavior on + // non-core types. + return nil, err + } + setupLog.Info("configuring manager") mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, MetricsBindAddress: "0", // TODO(njhale): Enable metrics on non-conflicting port (not 8080) NewCache: cache.BuilderWithOptions(cache.Options{ SelectorsByObject: cache.SelectorsByObject{ &corev1.Secret{}: { Label: labels.SelectorFromValidatedSet(map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}), }, + &operatorsv1alpha1.ClusterServiceVersion{}: { + Label: copiedLabelDoesNotExist, + }, }, }), }) diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go index 67e86de300..962e8066dd 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go @@ -100,9 +100,14 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string ImagePullSecrets: secrets, Containers: []corev1.Container{ { - Name: "extract", - Image: c.opmImage, - Command: []string{"opm", "alpha", "bundle", "extract", "-m", "/bundle/", "-n", cmRef.Namespace, "-c", cmRef.Name}, + Name: "extract", + Image: c.opmImage, + Command: []string{"opm", "alpha", "bundle", "extract", + "-m", "/bundle/", + "-n", cmRef.Namespace, + "-c", cmRef.Name, + "-z", + }, Env: []corev1.EnvVar{ { Name: configmap.EnvContainerImage, diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go index aa898c8a4e..8fb18ed92f 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/adoption_controller.go @@ -19,6 +19,7 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -68,17 +69,17 @@ func (r *AdoptionReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches(&source.Kind{Type: &appsv1.Deployment{}}, enqueueCSV). Watches(&source.Kind{Type: &corev1.Namespace{}}, enqueueCSV). Watches(&source.Kind{Type: &corev1.Service{}}, enqueueCSV). - Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueCSV). - Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueCSV). - Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueCSV). - Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueCSV). Watches(&source.Kind{Type: &apiextensionsv1.CustomResourceDefinition{}}, enqueueProviders). Watches(&source.Kind{Type: &apiregistrationv1.APIService{}}, enqueueCSV). Watches(&source.Kind{Type: &operatorsv1alpha1.Subscription{}}, enqueueCSV). Watches(&source.Kind{Type: &operatorsv2.OperatorCondition{}}, enqueueCSV). + Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueCSV, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueCSV, builder.OnlyMetadata). Complete(reconcile.Func(r.ReconcileClusterServiceVersion)) if err != nil { return err @@ -350,24 +351,7 @@ func (r *AdoptionReconciler) disownFromAll(ctx context.Context, component runtim func (r *AdoptionReconciler) adoptees(ctx context.Context, operator decorators.Operator, csv *operatorsv1alpha1.ClusterServiceVersion) ([]runtime.Object, error) { // Note: We need to figure out how to dynamically add new list types here (or some equivalent) in // order to support operators composed of custom resources. - componentLists := []runtime.Object{ - &appsv1.DeploymentList{}, - &corev1.ServiceList{}, - &corev1.NamespaceList{}, - &corev1.ServiceAccountList{}, - &corev1.SecretList{}, - &corev1.ConfigMapList{}, - &rbacv1.RoleList{}, - &rbacv1.RoleBindingList{}, - &rbacv1.ClusterRoleList{}, - &rbacv1.ClusterRoleBindingList{}, - &apiregistrationv1.APIServiceList{}, - &apiextensionsv1.CustomResourceDefinitionList{}, - &operatorsv1alpha1.SubscriptionList{}, - &operatorsv1alpha1.InstallPlanList{}, - &operatorsv1alpha1.ClusterServiceVersionList{}, - &operatorsv2.OperatorConditionList{}, - } + componentLists := componentLists() // Only resources that aren't already labelled are adoption candidates selector, err := operator.NonComponentSelector() @@ -403,7 +387,8 @@ func (r *AdoptionReconciler) adoptees(ctx context.Context, operator decorators.O // Pick up owned CRDs for _, provided := range csv.Spec.CustomResourceDefinitions.Owned { - crd := &apiextensionsv1.CustomResourceDefinition{} + crd := &metav1.PartialObjectMetadata{} + crd.SetGroupVersionKind(apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition")) if err := r.Get(ctx, types.NamespacedName{Name: provided.Name}, crd); err != nil { if !apierrors.IsNotFound(err) { // Inform requeue on transient error diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/not_found.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/not_found.go new file mode 100644 index 0000000000..8cf4a3b991 --- /dev/null +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/not_found.go @@ -0,0 +1,69 @@ +package catalog + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" +) + +// gvkNotFoundError is returned from installplan execution when a step contains a GVK that is not found on cluster. +type gvkNotFoundError struct { + gvk schema.GroupVersionKind + name string +} + +func (g gvkNotFoundError) Error() string { + return fmt.Sprintf("api-server resource not found installing %s %s: GroupVersionKind %s not found on the cluster. %s", g.gvk.Kind, g.name, g.gvk, + "This API may have been deprecated and removed, see https://kubernetes.io/docs/reference/using-api/deprecation-guide/ for more information.") +} + +type DiscoveryQuerier interface { + QueryForGVK() error +} + +type DiscoveryQuerierFunc func() error + +func (d DiscoveryQuerierFunc) QueryForGVK() error { + return d() +} + +type discoveryQuerier struct { + client discovery.DiscoveryInterface +} + +func newDiscoveryQuerier(client discovery.DiscoveryInterface) *discoveryQuerier { + return &discoveryQuerier{client: client} +} + +// WithStepResource returns a DiscoveryQuerier which uses discovery to query for supported APIs on the server based on the provided step's GVK. +func (d *discoveryQuerier) WithStepResource(stepResource operatorsv1alpha1.StepResource) DiscoveryQuerier { + var f DiscoveryQuerierFunc = func() error { + gvk := schema.GroupVersionKind{Group: stepResource.Group, Version: stepResource.Version, Kind: stepResource.Kind} + + resourceList, err := d.client.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) + if err != nil { + if errors.IsNotFound(err) { + return gvkNotFoundError{gvk: gvk, name: stepResource.Name} + } + return err + } + + if resourceList == nil { + return gvkNotFoundError{gvk: gvk, name: stepResource.Name} + } + + for _, resource := range resourceList.APIResources { + if resource.Kind == stepResource.Kind { + // this kind is supported for this particular GroupVersion + return nil + } + } + + return gvkNotFoundError{gvk: gvk, name: stepResource.Name} + } + return f +} diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go index efd1685b90..6d057af5f0 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go @@ -11,6 +11,8 @@ import ( "sync" "time" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + errorwrap "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/grpc/connectivity" @@ -115,7 +117,7 @@ type Operator struct { type CatalogSourceSyncFunc func(logger *logrus.Entry, in *v1alpha1.CatalogSource) (out *v1alpha1.CatalogSource, continueSync bool, syncError error) // NewOperator creates a new Catalog Operator. -func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clock, logger *logrus.Logger, resync time.Duration, configmapRegistryImage, utilImage string, operatorNamespace string, scheme *runtime.Scheme, installPlanTimeout time.Duration, bundleUnpackTimeout time.Duration) (*Operator, error) { +func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clock, logger *logrus.Logger, resync time.Duration, configmapRegistryImage, opmImage, utilImage string, operatorNamespace string, scheme *runtime.Scheme, installPlanTimeout time.Duration, bundleUnpackTimeout time.Duration) (*Operator, error) { resyncPeriod := queueinformer.ResyncWithJitter(resync, 0.2) config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) if err != nil { @@ -351,7 +353,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo bundle.WithPodLister(podInformer.Lister()), bundle.WithRoleLister(roleInformer.Lister()), bundle.WithRoleBindingLister(roleBindingInformer.Lister()), - bundle.WithOPMImage(configmapRegistryImage), + bundle.WithOPMImage(opmImage), bundle.WithUtilImage(utilImage), bundle.WithNow(op.now), bundle.WithUnpackTimeout(op.bundleUnpackTimeout), @@ -1774,6 +1776,8 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error { ensurer := newStepEnsurer(kubeclient, crclient, dynamicClient) r := newManifestResolver(plan.GetNamespace(), o.lister.CoreV1().ConfigMapLister(), o.logger) + discoveryQuerier := newDiscoveryQuerier(o.opClient.KubernetesInterface().Discovery()) + // CRDs should be installed via the default OLM (cluster-admin) client and not the scoped client specified by the AttenuatedServiceAccount // the StepBuilder is currently only implemented for CRD types // TODO give the StepBuilder both OLM and scoped clients when it supports new scoped types @@ -2173,6 +2177,14 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error { } return nil }(i, step); err != nil { + if k8serrors.IsNotFound(err) { + // Check for APIVersions present in the installplan steps that are not available on the server. + // The check is made via discovery per step in the plan. Transient communication failures to the api-server are handled by the plan retry logic. + notFoundErr := discoveryQuerier.WithStepResource(step.Resource).QueryForGVK() + if notFoundErr != nil { + return notFoundErr + } + } return err } } diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go index f5da96614a..024cb3dd96 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription/state.go @@ -1,6 +1,7 @@ package subscription import ( + "bytes" "context" "fmt" @@ -452,7 +453,7 @@ func (i *installPlanReferencedState) CheckInstallPlanStatus(now *metav1.Time, cl if cond.Reason == "" { cond.Reason = string(phase) } - + cond.Message = extractMessage(status) cond.Type = v1alpha1.SubscriptionInstallPlanPending cond.Status = corev1.ConditionTrue out.Status.SetCondition(cond) @@ -472,6 +473,7 @@ func (i *installPlanReferencedState) CheckInstallPlanStatus(now *metav1.Time, cl } cond.Type = v1alpha1.SubscriptionInstallPlanFailed + cond.Message = extractMessage(status) cond.Status = corev1.ConditionTrue out.Status.SetCondition(cond) @@ -508,6 +510,25 @@ func (i *installPlanReferencedState) CheckInstallPlanStatus(now *metav1.Time, cl return known, nil } +func extractMessage(status *v1alpha1.InstallPlanStatus) string { + if cond := status.GetCondition(v1alpha1.InstallPlanInstalled); cond.Status != corev1.ConditionUnknown && cond.Message != "" { + return cond.Message + } + + var b bytes.Buffer + for _, lookup := range status.BundleLookups { + if cond := lookup.GetCondition(v1alpha1.BundleLookupPending); cond.Status != corev1.ConditionUnknown && cond.Message != "" { + if b.Len() != 0 { + b.WriteString(" ") + } + b.WriteString(cond.Message) + b.WriteString(".") + } + } + + return b.String() +} + type installPlanKnownState struct { InstallPlanReferencedState } diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/components.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/components.go new file mode 100644 index 0000000000..63c48429dc --- /dev/null +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/components.go @@ -0,0 +1,70 @@ +package operators + +import ( + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + operatorsv2 "github.com/operator-framework/api/pkg/operators/v2" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" +) + +func componentLists() []runtime.Object { + return []runtime.Object{ + &appsv1.DeploymentList{}, + &corev1.ServiceList{}, + &corev1.NamespaceList{}, + &apiregistrationv1.APIServiceList{}, + &apiextensionsv1.CustomResourceDefinitionList{}, + &operatorsv1alpha1.SubscriptionList{}, + &operatorsv1alpha1.InstallPlanList{}, + &operatorsv1alpha1.ClusterServiceVersionList{}, + &operatorsv2.OperatorConditionList{}, + + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "SecretList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ConfigMapList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ServiceAccountList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "RoleList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "RoleBindingList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "ClusterRoleList", + }, + }, + &metav1.PartialObjectMetadataList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "ClusterRoleBindingList", + }, + }, + } +} diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go index 40252daa5b..4046f7ee09 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/reference" @@ -344,6 +345,10 @@ func NewComponent(component runtime.Object, scheme *runtime.Scheme) (*Component, if err := scheme.Convert(component, u, nil); err != nil { return nil, err } + // GVK may have been lost from PartialObjectMetadata during conversion. + if gvk := component.GetObjectKind().GroupVersionKind(); gvk != schema.EmptyObjectKind.GroupVersionKind() { + u.SetGroupVersionKind(gvk) + } c := &Component{ Unstructured: u, diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go index a30ab420f7..c7b0a00f2f 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/openshift/helpers.go @@ -2,7 +2,6 @@ package openshift import ( "context" - "encoding/json" "fmt" "strings" @@ -190,12 +189,7 @@ func maxOpenShiftVersion(csv *operatorsv1alpha1.ClusterServiceVersion) (*semver. continue } - var value string - if err := json.Unmarshal([]byte(property.Value), &value); err != nil { - errs = append(errs, err) - continue - } - + value := strings.Trim(property.Value, "\"") if value == "" { continue } @@ -203,6 +197,7 @@ func maxOpenShiftVersion(csv *operatorsv1alpha1.ClusterServiceVersion) (*semver. version, err := semver.ParseTolerant(value) if err != nil { errs = append(errs, err) + continue } if max == nil { @@ -241,11 +236,7 @@ func notCopiedSelector() (labels.Selector, error) { if err != nil { return nil, err } - - selector := labels.NewSelector() - selector.Add(*requirement) - - return selector, nil + return labels.NewSelector().Add(*requirement), nil } func olmOperatorRelatedObjects(ctx context.Context, cli client.Client, namespace string) ([]configv1.ObjectReference, error) { diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go index 3153545ec4..62aa4e2d06 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go @@ -18,6 +18,7 @@ import ( kscheme "k8s.io/client-go/kubernetes/scheme" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -69,20 +70,22 @@ func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&operatorsv1.Operator{}). Watches(&source.Kind{Type: &appsv1.Deployment{}}, enqueueOperator). Watches(&source.Kind{Type: &corev1.Namespace{}}, enqueueOperator). - Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueOperator). - Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueOperator). - Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueOperator). - Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueOperator). Watches(&source.Kind{Type: &apiextensionsv1.CustomResourceDefinition{}}, enqueueOperator). Watches(&source.Kind{Type: &apiregistrationv1.APIService{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv1alpha1.Subscription{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv1alpha1.InstallPlan{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv1alpha1.ClusterServiceVersion{}}, enqueueOperator). Watches(&source.Kind{Type: &operatorsv2.OperatorCondition{}}, enqueueOperator). - // TODO(njhale): Add WebhookConfigurations and ConfigMaps + // Metadata is sufficient to build component refs for + // GVKs that don't have a .status.conditions field. + Watches(&source.Kind{Type: &corev1.ServiceAccount{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.Secret{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &corev1.ConfigMap{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.Role{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.RoleBinding{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRole{}}, enqueueOperator, builder.OnlyMetadata). + Watches(&source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, enqueueOperator, builder.OnlyMetadata). + // TODO(njhale): Add WebhookConfigurations Complete(r) } @@ -196,23 +199,7 @@ func (r *OperatorReconciler) updateComponents(ctx context.Context, operator *dec func (r *OperatorReconciler) listComponents(ctx context.Context, selector labels.Selector) ([]runtime.Object, error) { // Note: We need to figure out how to dynamically add new list types here (or some equivalent) in // order to support operators composed of custom resources. - componentLists := []runtime.Object{ - &appsv1.DeploymentList{}, - &corev1.NamespaceList{}, - &corev1.ServiceAccountList{}, - &corev1.SecretList{}, - &corev1.ConfigMapList{}, - &rbacv1.RoleList{}, - &rbacv1.RoleBindingList{}, - &rbacv1.ClusterRoleList{}, - &rbacv1.ClusterRoleBindingList{}, - &apiextensionsv1.CustomResourceDefinitionList{}, - &apiregistrationv1.APIServiceList{}, - &operatorsv1alpha1.SubscriptionList{}, - &operatorsv1alpha1.InstallPlanList{}, - &operatorsv1alpha1.ClusterServiceVersionList{}, - &operatorsv2.OperatorConditionList{}, - } + componentLists := componentLists() opt := client.MatchingLabelsSelector{Selector: selector} for _, list := range componentLists { diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go index 1fd00375f3..0c017c7cc5 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/installabletypes.go @@ -60,24 +60,30 @@ func NewBundleInstallableFromOperator(o *Operator) (BundleInstallable, error) { if src == nil { return BundleInstallable{}, fmt.Errorf("unable to resolve the source of bundle %s", o.Identifier()) } + id := bundleId(o.Identifier(), o.Channel(), src.Catalog) var constraints []solver.Constraint + if src.Catalog.Virtual() && src.Subscription == nil { + // CSVs already associated with a Subscription + // may be replaced, but freestanding CSVs must + // appear in any solution. + constraints = append(constraints, PrettyConstraint( + solver.Mandatory(), + fmt.Sprintf("clusterserviceversion %s exists and is not referenced by a subscription", o.Identifier()), + )) + } for _, p := range o.bundle.GetProperties() { if p.GetType() == operatorregistry.DeprecatedType { constraints = append(constraints, PrettyConstraint( solver.Prohibited(), - fmt.Sprintf("bundle %s is deprecated", bundleId(o.Identifier(), o.Channel(), src.Catalog)), + fmt.Sprintf("bundle %s is deprecated", id), )) break } } - return NewBundleInstallable(o.Identifier(), o.Channel(), src.Catalog, constraints...), nil -} - -func NewBundleInstallable(bundle, channel string, catalog registry.CatalogKey, constraints ...solver.Constraint) BundleInstallable { return BundleInstallable{ - identifier: bundleId(bundle, channel, catalog), + identifier: id, constraints: constraints, - } + }, nil } type GenericInstallable struct { diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go index 19a0b933ed..1c21425bf2 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/operators.go @@ -11,10 +11,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-registry/pkg/api" opregistry "github.com/operator-framework/operator-registry/pkg/registry" - - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" ) type APISet map[opregistry.APIKey]struct{} @@ -197,6 +196,7 @@ type OperatorSourceInfo struct { StartingCSV string Catalog registry.CatalogKey DefaultChannel bool + Subscription *v1alpha1.Subscription } func (i *OperatorSourceInfo) String() string { @@ -216,7 +216,6 @@ type OperatorSurface interface { SourceInfo() *OperatorSourceInfo Bundle() *api.Bundle Inline() bool - Dependencies() []*api.Dependency Properties() []*api.Property Skips() []string } @@ -229,7 +228,6 @@ type Operator struct { version *semver.Version bundle *api.Bundle sourceInfo *OperatorSourceInfo - dependencies []*api.Dependency properties []*api.Property skips []string } @@ -261,21 +259,27 @@ func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey reg // legacy support - if the api doesn't contain properties/dependencies, build them from required/provided apis properties := bundle.Properties if len(properties) == 0 { - properties, err = apisToProperties(provided) + properties, err = providedAPIsToProperties(provided) if err != nil { return nil, err } } - dependencies := bundle.Dependencies - if len(dependencies) == 0 { - dependencies, err = apisToDependencies(required) + if len(bundle.Dependencies) > 0 { + ps, err := legacyDependenciesToProperties(bundle.Dependencies) + if err != nil { + return nil, fmt.Errorf("failed to translate legacy dependencies to properties: %w", err) + } + properties = append(properties, ps...) + } else { + ps, err := requiredAPIsToProperties(required) if err != nil { return nil, err } + properties = append(properties, ps...) } // legacy support - if the grpc api doesn't contain required/provided apis, fallback to csv parsing - if len(required) == 0 && len(provided) == 0 && len(properties) == 0 && len(dependencies) == 0 { + if len(required) == 0 && len(provided) == 0 && len(properties) == 0 { // fallback to csv parsing if bundle.CsvJson == "" { if bundle.GetBundlePath() != "" { @@ -307,7 +311,6 @@ func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey reg requiredAPIs: required, bundle: bundle, sourceInfo: sourceInfo, - dependencies: dependencies, properties: properties, skips: bundle.Skips, }, nil @@ -338,15 +341,15 @@ func NewOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*Operator, requiredAPIs[opregistry.APIKey{Group: api.Group, Version: api.Version, Kind: api.Kind, Plural: api.Name}] = struct{}{} } - dependencies, err := apisToDependencies(requiredAPIs) + properties, err := providedAPIsToProperties(providedAPIs) if err != nil { return nil, err } - - properties, err := apisToProperties(providedAPIs) + dependencies, err := requiredAPIsToProperties(requiredAPIs) if err != nil { return nil, err } + properties = append(properties, dependencies...) return &Operator{ name: csv.GetName(), @@ -354,7 +357,6 @@ func NewOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*Operator, providedAPIs: providedAPIs, requiredAPIs: requiredAPIs, sourceInfo: &ExistingOperator, - dependencies: dependencies, properties: properties, }, nil } @@ -413,48 +415,48 @@ func (o *Operator) Inline() bool { return o.bundle != nil && o.bundle.GetBundlePath() == "" } -func (o *Operator) Dependencies() []*api.Dependency { - return o.dependencies -} - func (o *Operator) Properties() []*api.Property { return o.properties } func (o *Operator) DependencyPredicates() (predicates []OperatorPredicate, err error) { predicates = make([]OperatorPredicate, 0) - for _, d := range o.Dependencies() { - var p OperatorPredicate - if d == nil || d.Type == "" { - continue - } - p, err = PredicateForDependency(d) + for _, property := range o.Properties() { + predicate, err := PredicateForProperty(property) if err != nil { - return + return nil, err + } + if predicate == nil { + continue } - predicates = append(predicates, p) + predicates = append(predicates, predicate) } return } -// TODO: this should go in its own dependency/predicate builder package -// TODO: can we make this more extensible, i.e. via cue -func PredicateForDependency(dependency *api.Dependency) (OperatorPredicate, error) { - p, ok := predicates[dependency.Type] +func PredicateForProperty(property *api.Property) (OperatorPredicate, error) { + if property == nil { + return nil, nil + } + p, ok := predicates[property.Type] if !ok { - return nil, fmt.Errorf("no predicate for dependency type %s", dependency.Type) + return nil, nil } - return p(dependency.Value) + return p(property.Value) } var predicates = map[string]func(string) (OperatorPredicate, error){ - opregistry.GVKType: predicateForGVKDependency, - opregistry.PackageType: predicateForPackageDependency, - opregistry.LabelType: predicateForLabelDependency, + "olm.gvk.required": predicateForRequiredGVKProperty, + "olm.package.required": predicateForRequiredPackageProperty, + "olm.label.required": predicateForRequiredLabelProperty, } -func predicateForGVKDependency(value string) (OperatorPredicate, error) { - var gvk opregistry.GVKDependency +func predicateForRequiredGVKProperty(value string) (OperatorPredicate, error) { + var gvk struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + } if err := json.Unmarshal([]byte(value), &gvk); err != nil { return nil, err } @@ -465,44 +467,51 @@ func predicateForGVKDependency(value string) (OperatorPredicate, error) { }), nil } -func predicateForPackageDependency(value string) (OperatorPredicate, error) { - var pkg opregistry.PackageDependency +func predicateForRequiredPackageProperty(value string) (OperatorPredicate, error) { + var pkg struct { + PackageName string `json:"packageName"` + VersionRange string `json:"versionRange"` + } if err := json.Unmarshal([]byte(value), &pkg); err != nil { return nil, err } - ver, err := semver.ParseRange(pkg.Version) + ver, err := semver.ParseRange(pkg.VersionRange) if err != nil { return nil, err } - return And(WithPackage(pkg.PackageName), WithVersionInRange(ver)), nil } -func predicateForLabelDependency(value string) (OperatorPredicate, error) { - var label opregistry.LabelDependency +func predicateForRequiredLabelProperty(value string) (OperatorPredicate, error) { + var label struct { + Label string `json:"label"` + } if err := json.Unmarshal([]byte(value), &label); err != nil { return nil, err } - return WithLabel(label.Label), nil } -func apisToDependencies(apis APISet) (out []*api.Dependency, err error) { +func requiredAPIsToProperties(apis APISet) (out []*api.Property, err error) { if len(apis) == 0 { return } - out = make([]*api.Dependency, 0) + out = make([]*api.Property, 0) for a := range apis { - val, err := json.Marshal(opregistry.GVKDependency{ + val, err := json.Marshal(struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + }{ Group: a.Group, - Kind: a.Kind, Version: a.Version, + Kind: a.Kind, }) if err != nil { return nil, err } - out = append(out, &api.Dependency{ - Type: opregistry.GVKType, + out = append(out, &api.Property{ + Type: "olm.gvk.required", Value: string(val), }) } @@ -512,13 +521,13 @@ func apisToDependencies(apis APISet) (out []*api.Dependency, err error) { return nil, nil } -func apisToProperties(apis APISet) (out []*api.Property, err error) { +func providedAPIsToProperties(apis APISet) (out []*api.Property, err error) { out = make([]*api.Property, 0) for a := range apis { val, err := json.Marshal(opregistry.GVKProperty{ Group: a.Group, - Kind: a.Kind, Version: a.Version, + Kind: a.Kind, }) if err != nil { panic(err) @@ -533,3 +542,63 @@ func apisToProperties(apis APISet) (out []*api.Property, err error) { } return nil, nil } + +func legacyDependenciesToProperties(dependencies []*api.Dependency) ([]*api.Property, error) { + var result []*api.Property + for _, dependency := range dependencies { + switch dependency.Type { + case "olm.gvk": + type gvk struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + } + var vfrom gvk + if err := json.Unmarshal([]byte(dependency.Value), &vfrom); err != nil { + return nil, fmt.Errorf("failed to unmarshal legacy 'olm.gvk' dependency: %w", err) + } + vto := gvk{ + Group: vfrom.Group, + Version: vfrom.Version, + Kind: vfrom.Kind, + } + vb, err := json.Marshal(&vto) + if err != nil { + return nil, fmt.Errorf("unexpected error marshaling generated 'olm.package.required' property: %w", err) + } + result = append(result, &api.Property{ + Type: "olm.gvk.required", + Value: string(vb), + }) + case "olm.package": + var vfrom struct { + PackageName string `json:"packageName"` + VersionRange string `json:"version"` + } + if err := json.Unmarshal([]byte(dependency.Value), &vfrom); err != nil { + return nil, fmt.Errorf("failed to unmarshal legacy 'olm.package' dependency: %w", err) + } + vto := struct { + PackageName string `json:"packageName"` + VersionRange string `json:"versionRange"` + }{ + PackageName: vfrom.PackageName, + VersionRange: vfrom.VersionRange, + } + vb, err := json.Marshal(&vto) + if err != nil { + return nil, fmt.Errorf("unexpected error marshaling generated 'olm.package.required' property: %w", err) + } + result = append(result, &api.Property{ + Type: "olm.package.required", + Value: string(vb), + }) + case "olm.label": + result = append(result, &api.Property{ + Type: "olm.label.required", + Value: dependency.Value, + }) + } + } + return result, nil +} diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go index e2ee4a66e0..668f9be04f 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/resolver.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "sort" + "strings" "github.com/blang/semver/v4" "github.com/sirupsen/logrus" @@ -55,12 +56,16 @@ func (r *SatResolver) SolveOperators(namespaces []string, csvs []*v1alpha1.Clust startingCSVs := make(map[string]struct{}) // build a virtual catalog of all currently installed CSVs - existingSnapshot, existingInstallables, err := r.newSnapshotForNamespace(namespaces[0], subs, csvs) + existingSnapshot, err := r.newSnapshotForNamespace(namespaces[0], subs, csvs) if err != nil { return nil, err } namespacedCache := r.cache.Namespaced(namespaces...).WithExistingOperators(existingSnapshot) + _, existingInstallables, err := r.getBundleInstallables(registry.NewVirtualCatalogKey(namespaces[0]), existingSnapshot.Find(), namespacedCache, visited) + if err != nil { + return nil, err + } for _, i := range existingInstallables { installables[i.Identifier()] = i } @@ -233,7 +238,7 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu if i != len(bundles) && bundles[i].bundle.ChannelName == lastChannel { continue } - channel, err := r.sortChannel(bundles[lastIndex:i]) + channel, err := sortChannel(bundles[lastIndex:i]) if err != nil { return nil, err } @@ -248,7 +253,8 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu candidates := make([]*BundleInstallable, 0) for _, o := range Filter(sortedBundles, channelPredicates...) { predicates := append(cachePredicates, WithCSVName(o.Identifier())) - id, installable, err := r.getBundleInstallables(catalog, predicates, namespacedCache, visited) + stack := namespacedCache.Catalog(catalog).Find(predicates...) + id, installable, err := r.getBundleInstallables(catalog, stack, namespacedCache, visited) if err != nil { return nil, err } @@ -292,17 +298,10 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu return installables, nil } -func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, predicates []OperatorPredicate, namespacedCache MultiCatalogOperatorFinder, visited map[OperatorSurface]*BundleInstallable) (map[solver.Identifier]struct{}, map[solver.Identifier]*BundleInstallable, error) { +func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, bundleStack []*Operator, namespacedCache MultiCatalogOperatorFinder, visited map[OperatorSurface]*BundleInstallable) (map[solver.Identifier]struct{}, map[solver.Identifier]*BundleInstallable, error) { errs := make([]error, 0) installables := make(map[solver.Identifier]*BundleInstallable, 0) // all installables, including dependencies - var finder OperatorFinder = namespacedCache - if !catalog.Empty() { - finder = namespacedCache.Catalog(catalog) - } - - bundleStack := finder.Find(predicates...) - // track the first layer of installable ids var initial = make(map[*Operator]struct{}) for _, o := range bundleStack { @@ -353,11 +352,18 @@ func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, predica } sources[*si] = struct{}{} - sourcePredicate = Or(sourcePredicate, And( - WithPackage(si.Package), - WithChannel(si.Channel), - WithCatalog(si.Catalog), - )) + if si.Catalog.Virtual() { + sourcePredicate = Or(sourcePredicate, And( + WithCSVName(b.Identifier()), + WithCatalog(si.Catalog), + )) + } else { + sourcePredicate = Or(sourcePredicate, And( + WithPackage(si.Package), + WithChannel(si.Channel), + WithCatalog(si.Catalog), + )) + } } sortedBundles, err := r.sortBundles(namespacedCache.FindPreferred(&bundle.sourceInfo.Catalog, sourcePredicate)) if err != nil { @@ -441,15 +447,14 @@ func (r *SatResolver) inferProperties(csv *v1alpha1.ClusterServiceVersion, subs return properties, nil } -func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1.Subscription, csvs []*v1alpha1.ClusterServiceVersion) (*CatalogSnapshot, []solver.Installable, error) { - installables := make([]solver.Installable, 0) +func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1.Subscription, csvs []*v1alpha1.ClusterServiceVersion) (*CatalogSnapshot, error) { existingOperatorCatalog := registry.NewVirtualCatalogKey(namespace) // build a catalog snapshot of CSVs without subscriptions - csvsWithSubscriptions := make(map[*v1alpha1.ClusterServiceVersion]struct{}) + csvSubscriptions := make(map[*v1alpha1.ClusterServiceVersion]*v1alpha1.Subscription) for _, sub := range subs { for _, csv := range csvs { if csv.Name == sub.Status.InstalledCSV { - csvsWithSubscriptions[csv] = struct{}{} + csvSubscriptions[csv] = sub break } } @@ -457,17 +462,9 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 var csvsMissingProperties []*v1alpha1.ClusterServiceVersion standaloneOperators := make([]*Operator, 0) for _, csv := range csvs { - var constraints []solver.Constraint - if _, ok := csvsWithSubscriptions[csv]; !ok { - // CSVs already associated with a Subscription - // may be replaced, but freestanding CSVs must - // appear in any solution. - constraints = append(constraints, solver.Mandatory()) - } - op, err := NewOperatorFromV1Alpha1CSV(csv) if err != nil { - return nil, nil, err + return nil, err } if anno, ok := csv.GetAnnotations()[projection.PropertiesAnnotationKey]; !ok { @@ -478,13 +475,14 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 op.properties = append(op.properties, inferred...) } } else if props, err := projection.PropertyListFromPropertiesAnnotation(anno); err != nil { - return nil, nil, fmt.Errorf("failed to retrieve properties of csv %q: %w", csv.GetName(), err) + return nil, fmt.Errorf("failed to retrieve properties of csv %q: %w", csv.GetName(), err) } else { op.properties = props } op.sourceInfo = &OperatorSourceInfo{ - Catalog: existingOperatorCatalog, + Catalog: existingOperatorCatalog, + Subscription: csvSubscriptions[csv], } // Try to determine source package name from properties and add to SourceInfo. for _, p := range op.properties { @@ -501,10 +499,6 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 } standaloneOperators = append(standaloneOperators, op) - - // all standalone operators are mandatory - i := NewBundleInstallable(op.Identifier(), "", existingOperatorCatalog, constraints...) - installables = append(installables, &i) } if len(csvsMissingProperties) > 0 { @@ -515,7 +509,7 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 r.log.Infof("considered csvs without properties annotation during resolution: %v", names) } - return NewRunningOperatorSnapshot(r.log, existingOperatorCatalog, standaloneOperators), installables, nil + return NewRunningOperatorSnapshot(r.log, existingOperatorCatalog, standaloneOperators), nil } func (r *SatResolver) addInvariants(namespacedCache MultiCatalogOperatorFinder, installables map[solver.Identifier]solver.Installable) { @@ -617,7 +611,7 @@ func (r *SatResolver) sortBundles(bundles []*Operator) ([]*Operator, error) { return pi.Channel < pj.Channel }) for channel := range partitionedBundles[catalog] { - sorted, err := r.sortChannel(partitionedBundles[catalog][channel]) + sorted, err := sortChannel(partitionedBundles[catalog][channel]) if err != nil { return nil, err } @@ -633,13 +627,15 @@ func (r *SatResolver) sortBundles(bundles []*Operator) ([]*Operator, error) { return all, nil } -// sorts bundle in a channel by replaces -func (r *SatResolver) sortChannel(bundles []*Operator) ([]*Operator, error) { - if len(bundles) <= 1 { +// Sorts bundle in a channel by replaces. All entries in the argument +// are assumed to have the same Package and Channel. +func sortChannel(bundles []*Operator) ([]*Operator, error) { + if len(bundles) < 1 { return bundles, nil } - channel := []*Operator{} + packageName := bundles[0].Package() + channelName := bundles[0].Channel() bundleLookup := map[string]*Operator{} @@ -667,44 +663,60 @@ func (r *SatResolver) sortChannel(bundles []*Operator) ([]*Operator, error) { } } - // a bundle without a replacement is a channel head, but if we find more than one of those something is weird + // a bundle without a replacement is a channel head, but if we + // find more than one of those something is weird headCandidates := []*Operator{} for _, b := range bundles { if _, ok := replacedBy[b]; !ok { headCandidates = append(headCandidates, b) } } + if len(headCandidates) == 0 { + return nil, fmt.Errorf("no channel heads (entries not replaced by another entry) found in channel %q of package %q", channelName, packageName) + } - if len(headCandidates) > 1 { - var names []string - for _, v := range headCandidates { - names = append(names, v.Identifier()) + var chains [][]*Operator + for _, head := range headCandidates { + var chain []*Operator + visited := make(map[*Operator]struct{}) + current := head + for { + visited[current] = struct{}{} + if _, ok := skipped[current.Identifier()]; !ok { + chain = append(chain, current) + } + next, ok := replaces[current] + if !ok { + break + } + if _, ok := visited[next]; ok { + return nil, fmt.Errorf("a cycle exists in the chain of replacement beginning with %q in channel %q of package %q", head.Identifier(), channelName, packageName) + } + current = next } - return nil, fmt.Errorf("found multiple channel heads: %v, please check the `replaces`/`skipRange` fields of the operator bundles", names) - - } else if len(headCandidates) < 1 { - return nil, fmt.Errorf("head of channel not found") + chains = append(chains, chain) } - head := headCandidates[0] - current := head - skip := false - for { - if skip == false { - channel = append(channel, current) - } - skip = false - next, ok := replaces[current] - if !ok { - break - } - if _, ok := skipped[current.Identifier()]; ok { - skip = true + if len(chains) > 1 { + schains := make([]string, len(chains)) + for i, chain := range chains { + switch len(chain) { + case 0: + schains[i] = "[]" // Bug? + case 1: + schains[i] = chain[0].Identifier() + default: + schains[i] = fmt.Sprintf("%s...%s", chain[0].Identifier(), chain[len(chain)-1].Identifier()) + } } - current = next + return nil, fmt.Errorf("a unique replacement chain within a channel is required to determine the relative order between channel entries, but %d replacement chains were found in channel %q of package %q: %s", len(schains), channelName, packageName, strings.Join(schains, ", ")) } - // TODO: do we care if the channel doesn't include every bundle in the input? + if len(chains) == 0 { + // Bug? + return nil, fmt.Errorf("found no replacement chains in channel %q of package %q", channelName, packageName) + } - return channel, nil + // TODO: do we care if the channel doesn't include every bundle in the input? + return chains[0], nil } diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/cabundle_updater.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/cabundle_updater.go new file mode 100644 index 0000000000..4fcee4e457 --- /dev/null +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/cabundle_updater.go @@ -0,0 +1,60 @@ +package filemonitor + +import ( + "crypto/x509" + "io/ioutil" + "sync" + + "github.com/fsnotify/fsnotify" + "github.com/sirupsen/logrus" +) + +type certPoolStore struct { + mutex sync.RWMutex + certpool *x509.CertPool + clientCAPath string +} + +func NewCertPoolStore(clientCAPath string) (*certPoolStore, error) { + pem, err := ioutil.ReadFile(clientCAPath) + if err != nil { + return nil, err + } + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(pem) + + return &certPoolStore{ + mutex: sync.RWMutex{}, + certpool: pool, + clientCAPath: clientCAPath, + }, nil +} + +func (c *certPoolStore) storeCABundle(clientCAPath string) error { + pem, err := ioutil.ReadFile(clientCAPath) + if err == nil { + c.mutex.Lock() + defer c.mutex.Unlock() + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(pem) + c.certpool = pool + } + return err +} + +func (c *certPoolStore) HandleCABundleUpdate(logger logrus.FieldLogger, event fsnotify.Event) { + switch op := event.Op; op { + case fsnotify.Create: + logger.Debugf("got fs event for %v", event.Name) + + if err := c.storeCABundle(c.clientCAPath); err != nil { + logger.Debugf("unable to reload ca bundle: %v", err) + } else { + logger.Debugf("successfully reload ca bundle: %v", err) + } + } +} + +func (c *certPoolStore) GetCertPool() *x509.CertPool { + return c.certpool +} diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go index d7bb005ac2..9cf59c888c 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/cert_updater.go @@ -1,43 +1,38 @@ package filemonitor import ( - "context" "crypto/tls" "crypto/x509" - "fmt" - "path/filepath" "sync" "github.com/fsnotify/fsnotify" "github.com/sirupsen/logrus" ) -type keystore struct { +type certStore struct { mutex sync.RWMutex cert *tls.Certificate tlsCrtPath string tlsKeyPath string } -type getCertFn = func(*tls.ClientHelloInfo) (*tls.Certificate, error) - -// NewKeystore returns a store for storing the certificate data and the ability to retrieve it safely -func NewKeystore(tlsCrt, tlsKey string) *keystore { +// NewCertStore returns a store for storing the certificate data and the ability to retrieve it safely +func NewCertStore(tlsCrt, tlsKey string) (*certStore, error) { cert, err := tls.LoadX509KeyPair(tlsCrt, tlsKey) if err != nil { - panic(err) + return nil, err } - return &keystore{ + return &certStore{ mutex: sync.RWMutex{}, cert: &cert, tlsCrtPath: tlsCrt, tlsKeyPath: tlsKey, - } + }, nil } // HandleFilesystemUpdate is intended to be used as the OnUpdateFn for a watcher // and expects the certificate files to be in the same directory. -func (k *keystore) HandleFilesystemUpdate(logger *logrus.Logger, event fsnotify.Event) { +func (k *certStore) HandleFilesystemUpdate(logger logrus.FieldLogger, event fsnotify.Event) { switch op := event.Op; op { case fsnotify.Create: logger.Debugf("got fs event for %v", event.Name) @@ -57,7 +52,7 @@ func (k *keystore) HandleFilesystemUpdate(logger *logrus.Logger, event fsnotify. } } -func (k *keystore) storeCertificate(tlsCrt, tlsKey string) error { +func (k *certStore) storeCertificate(tlsCrt, tlsKey string) error { cert, err := tls.LoadX509KeyPair(tlsCrt, tlsKey) if err == nil { k.mutex.Lock() @@ -67,24 +62,8 @@ func (k *keystore) storeCertificate(tlsCrt, tlsKey string) error { return err } -func (k *keystore) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { +func (k *certStore) GetCertificate() *tls.Certificate { k.mutex.RLock() defer k.mutex.RUnlock() - return k.cert, nil -} - -// OLMGetCertRotationFn is a convenience function for OLM use only, but serves as an example for monitoring file system events -func OLMGetCertRotationFn(logger *logrus.Logger, tlsCertPath, tlsKeyPath string) (getCertFn, error) { - if filepath.Dir(tlsCertPath) != filepath.Dir(tlsKeyPath) { - return nil, fmt.Errorf("certificates expected to be in same directory %v vs %v", tlsCertPath, tlsKeyPath) - } - - keystore := NewKeystore(tlsCertPath, tlsKeyPath) - watcher, err := NewWatch(logger, []string{filepath.Dir(tlsCertPath)}, keystore.HandleFilesystemUpdate) - if err != nil { - return nil, err - } - watcher.Run(context.Background()) - - return keystore.GetCertificate, nil + return k.cert } diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go index c3b04e9fc3..bfb42ffb06 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor/watcher.go @@ -10,12 +10,12 @@ import ( type watcher struct { notify *fsnotify.Watcher pathsToWatch []string - logger *logrus.Logger - onUpdateFn func(*logrus.Logger, fsnotify.Event) + logger logrus.FieldLogger + onUpdateFn func(logrus.FieldLogger, fsnotify.Event) } // NewWatch sets up monitoring on a slice of paths and will execute the update function to process each event -func NewWatch(logger *logrus.Logger, pathsToWatch []string, onUpdateFn func(*logrus.Logger, fsnotify.Event)) (*watcher, error) { +func NewWatch(logger logrus.FieldLogger, pathsToWatch []string, onUpdateFn func(logrus.FieldLogger, fsnotify.Event)) (*watcher, error) { notify, err := fsnotify.NewWatcher() if err != nil { return nil, err diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile/profile.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile/profile.go index 76c516cbf4..46cf07edb4 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile/profile.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile/profile.go @@ -47,18 +47,28 @@ func RegisterHandlers(mux *http.ServeMux, options ...Option) { config.apply(options) if config.pprof { - mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.Handle("/debug/pprof/", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Index))) } if config.cmdline { - mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.Handle("/debug/pprof/cmdline", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Cmdline))) } if config.profile { - mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.Handle("/debug/pprof/profile", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Profile))) } if config.symbol { - mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.Handle("/debug/pprof/symbol", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Symbol))) } if config.trace { - mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + mux.Handle("/debug/pprof/trace", requireVerifiedClientCertificate(http.HandlerFunc(pprof.Trace))) } } + +func requireVerifiedClientCertificate(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.TLS == nil || len(r.TLS.VerifiedChains) == 0 { + w.WriteHeader(http.StatusForbidden) + return + } + h.ServeHTTP(w, r) + }) +} diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go index cef9db6719..02a66cb527 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer/queueinformer.go @@ -2,7 +2,6 @@ package queueinformer import ( "context" - "fmt" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -170,7 +169,6 @@ func (l LegacySyncHandler) ToSyncer() kubestate.Syncer { // ToSyncerWithDelete returns the Syncer equivalent of the given sync handler and delete function. func (l LegacySyncHandler) ToSyncerWithDelete(onDelete func(obj interface{})) kubestate.Syncer { var syncer kubestate.SyncFunc = func(ctx context.Context, event kubestate.ResourceEvent) error { - logrus.New().WithField("event", fmt.Sprintf("%+v", event)).Trace("legacy syncer received event") switch event.Type() { case kubestate.ResourceDeleted: if onDelete != nil { diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server/server.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server/server.go new file mode 100644 index 0000000000..c7307fc7b4 --- /dev/null +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server/server.go @@ -0,0 +1,77 @@ +package server + +import ( + "context" + "crypto/tls" + "fmt" + "net/http" + "path/filepath" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" +) + +func GetListenAndServeFunc(logger *logrus.Logger, tlsCertPath, tlsKeyPath, clientCAPath *string) (func() error, error) { + mux := http.NewServeMux() + profile.RegisterHandlers(mux) + mux.Handle("/metrics", promhttp.Handler()) + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + s := http.Server{ + Handler: mux, + Addr: ":8080", + } + listenAndServe := s.ListenAndServe + + if *tlsCertPath != "" && *tlsKeyPath != "" { + logger.Info("TLS keys set, using https for metrics") + + certStore, err := filemonitor.NewCertStore(*tlsCertPath, *tlsKeyPath) + if err != nil { + return nil, fmt.Errorf("Certificate monitoring for metrics (https) failed: %v", err) + } + + csw, err := filemonitor.NewWatch(logger, []string{filepath.Dir(*tlsCertPath), filepath.Dir(*tlsKeyPath)}, certStore.HandleFilesystemUpdate) + if err != nil { + return nil, fmt.Errorf("error creating cert file watcher: %v", err) + } + csw.Run(context.Background()) + certPoolStore, err := filemonitor.NewCertPoolStore(*clientCAPath) + cpsw, err := filemonitor.NewWatch(logger, []string{filepath.Dir(*clientCAPath)}, certPoolStore.HandleCABundleUpdate) + if err != nil { + return nil, fmt.Errorf("error creating cert file watcher: %v", err) + } + cpsw.Run(context.Background()) + + s.Addr = ":8443" + s.TLSConfig = &tls.Config{ + GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { + return certStore.GetCertificate(), nil + }, + GetConfigForClient: func(_ *tls.ClientHelloInfo) (*tls.Config, error) { + var certs []tls.Certificate + if cert := certStore.GetCertificate(); cert != nil { + certs = append(certs, *cert) + } + return &tls.Config{ + Certificates: certs, + ClientCAs: certPoolStore.GetCertPool(), + ClientAuth: tls.VerifyClientCertIfGiven, + }, nil + }, + } + + listenAndServe = func() error { + return s.ListenAndServeTLS("", "") + } + } else if *tlsCertPath != "" || *tlsKeyPath != "" { + return nil, fmt.Errorf("both --tls-key and --tls-crt must be provided for TLS to be enabled") + } else { + logger.Info("TLS keys not set, using non-https for metrics") + } + return listenAndServe, nil +} diff --git a/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/cmd.go b/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/cmd.go index 9c06ae2cfd..42e50e3ddf 100644 --- a/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/cmd.go +++ b/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/cmd.go @@ -4,10 +4,7 @@ import ( "github.com/spf13/cobra" "github.com/operator-framework/operator-registry/cmd/opm/alpha/bundle" - initcmd "github.com/operator-framework/operator-registry/cmd/opm/alpha/init" - "github.com/operator-framework/operator-registry/cmd/opm/alpha/render" - "github.com/operator-framework/operator-registry/cmd/opm/alpha/serve" - "github.com/operator-framework/operator-registry/cmd/opm/alpha/validate" + "github.com/operator-framework/operator-registry/cmd/opm/alpha/list" ) func NewCmd() *cobra.Command { @@ -17,6 +14,6 @@ func NewCmd() *cobra.Command { Short: "Run an alpha subcommand", } - runCmd.AddCommand(bundle.NewCmd(), initcmd.NewCmd(), serve.NewCmd(), render.NewCmd(), validate.NewCmd()) + runCmd.AddCommand(bundle.NewCmd(), list.NewCmd()) return runCmd } diff --git a/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/list/cmd.go b/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/list/cmd.go new file mode 100644 index 0000000000..8309b089a1 --- /dev/null +++ b/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/list/cmd.go @@ -0,0 +1,107 @@ +package list + +import ( + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/operator-framework/operator-registry/internal/action" +) + +const humanReadabilityOnlyNote = `NOTE: This is meant to be used for convenience and human-readability only. The +CLI and output format are subject to change, so it is not recommended to depend +on the output in any programs or scripts. Use the "render" subcommand to do +more complex processing and automation.` + +func NewCmd() *cobra.Command { + list := &cobra.Command{ + Use: "list", + Short: "List contents of an index", + Long: `The list subcommands print the contents of an index. + +` + humanReadabilityOnlyNote, + } + list.AddCommand(newPackagesCmd(), newChannelsCmd(), newBundlesCmd()) + return list +} + +func newPackagesCmd() *cobra.Command { + logger := logrus.New() + + return &cobra.Command{ + Use: "packages ", + Short: "List packages in an index", + Long: `The "channels" command lists the channels from the specified index. + +` + humanReadabilityOnlyNote, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + lp := action.ListPackages{IndexReference: args[0]} + res, err := lp.Run(cmd.Context()) + if err != nil { + logger.Fatal(err) + } + if err := res.WriteColumns(os.Stdout); err != nil { + logger.Fatal(err) + } + return nil + }, + } +} + +func newChannelsCmd() *cobra.Command { + logger := logrus.New() + + return &cobra.Command{ + Use: "channels [packageName]", + Short: "List package channels in an index", + Long: `The "channels" command lists the channels from the specified index and package. + +` + humanReadabilityOnlyNote, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + lc := action.ListChannels{IndexReference: args[0]} + if len(args) > 1 { + lc.PackageName = args[1] + } + res, err := lc.Run(cmd.Context()) + if err != nil { + logger.Fatal(err) + } + if err := res.WriteColumns(os.Stdout); err != nil { + logger.Fatal(err) + } + return nil + }, + } +} + +func newBundlesCmd() *cobra.Command { + logger := logrus.New() + + return &cobra.Command{ + Use: "bundles ", + Short: "List package bundles in an index", + Long: `The "bundles" command lists the bundles from the specified index and package. +Bundles that exist in multiple channels are duplicated in the output (one +for each channel in which the bundle is present). + +` + humanReadabilityOnlyNote, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + lb := action.ListBundles{IndexReference: args[0]} + if len(args) > 1 { + lb.PackageName = args[1] + } + res, err := lb.Run(cmd.Context()) + if err != nil { + logger.Fatal(err) + } + if err := res.WriteColumns(os.Stdout); err != nil { + logger.Fatal(err) + } + return nil + }, + } +} diff --git a/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/init/cmd.go b/vendor/github.com/operator-framework/operator-registry/cmd/opm/init/cmd.go similarity index 100% rename from vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/init/cmd.go rename to vendor/github.com/operator-framework/operator-registry/cmd/opm/init/cmd.go diff --git a/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/render/cmd.go b/vendor/github.com/operator-framework/operator-registry/cmd/opm/render/cmd.go similarity index 100% rename from vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/render/cmd.go rename to vendor/github.com/operator-framework/operator-registry/cmd/opm/render/cmd.go diff --git a/vendor/github.com/operator-framework/operator-registry/cmd/opm/root/cmd.go b/vendor/github.com/operator-framework/operator-registry/cmd/opm/root/cmd.go index 272981bfd5..7b07ccbc75 100644 --- a/vendor/github.com/operator-framework/operator-registry/cmd/opm/root/cmd.go +++ b/vendor/github.com/operator-framework/operator-registry/cmd/opm/root/cmd.go @@ -6,7 +6,11 @@ import ( "github.com/operator-framework/operator-registry/cmd/opm/alpha" "github.com/operator-framework/operator-registry/cmd/opm/index" + initcmd "github.com/operator-framework/operator-registry/cmd/opm/init" "github.com/operator-framework/operator-registry/cmd/opm/registry" + "github.com/operator-framework/operator-registry/cmd/opm/render" + "github.com/operator-framework/operator-registry/cmd/opm/serve" + "github.com/operator-framework/operator-registry/cmd/opm/validate" "github.com/operator-framework/operator-registry/cmd/opm/version" ) @@ -23,7 +27,7 @@ func NewCmd() *cobra.Command { }, } - cmd.AddCommand(registry.NewOpmRegistryCmd(), alpha.NewCmd()) + cmd.AddCommand(registry.NewOpmRegistryCmd(), alpha.NewCmd(), initcmd.NewCmd(), serve.NewCmd(), render.NewCmd(), validate.NewCmd()) index.AddCommand(cmd) version.AddCommand(cmd) diff --git a/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/serve/serve.go b/vendor/github.com/operator-framework/operator-registry/cmd/opm/serve/serve.go similarity index 100% rename from vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/serve/serve.go rename to vendor/github.com/operator-framework/operator-registry/cmd/opm/serve/serve.go diff --git a/vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/validate/validate.go b/vendor/github.com/operator-framework/operator-registry/cmd/opm/validate/validate.go similarity index 100% rename from vendor/github.com/operator-framework/operator-registry/cmd/opm/alpha/validate/validate.go rename to vendor/github.com/operator-framework/operator-registry/cmd/opm/validate/validate.go diff --git a/vendor/github.com/operator-framework/operator-registry/internal/action/list.go b/vendor/github.com/operator-framework/operator-registry/internal/action/list.go new file mode 100644 index 0000000000..972489226b --- /dev/null +++ b/vendor/github.com/operator-framework/operator-registry/internal/action/list.go @@ -0,0 +1,227 @@ +package action + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "sort" + "strings" + "text/tabwriter" + + "github.com/operator-framework/api/pkg/operators/v1alpha1" + + "github.com/operator-framework/operator-registry/internal/declcfg" + "github.com/operator-framework/operator-registry/internal/model" + "github.com/operator-framework/operator-registry/internal/property" +) + +type ListPackages struct { + IndexReference string +} + +func (l *ListPackages) Run(ctx context.Context) (*ListPackagesResult, error) { + m, err := indexRefToModel(ctx, l.IndexReference) + if err != nil { + return nil, err + } + + pkgs := []model.Package{} + for _, pkg := range m { + pkgs = append(pkgs, *pkg) + } + sort.Slice(pkgs, func(i, j int) bool { + return pkgs[i].Name < pkgs[j].Name + }) + return &ListPackagesResult{Packages: pkgs}, nil +} + +type ListPackagesResult struct { + Packages []model.Package +} + +func (r *ListPackagesResult) WriteColumns(w io.Writer) error { + tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) + if _, err := fmt.Fprintln(tw, "NAME\tDISPLAY NAME\tDEFAULT CHANNEL"); err != nil { + return err + } + for _, pkg := range r.Packages { + if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", pkg.Name, getDisplayName(pkg), pkg.DefaultChannel.Name); err != nil { + return err + } + } + return tw.Flush() +} + +func getDisplayName(pkg model.Package) string { + if pkg.DefaultChannel == nil { + return "" + } + head, err := pkg.DefaultChannel.Head() + if err != nil || head == nil || head.CsvJSON == "" { + return "" + } + + csv := v1alpha1.ClusterServiceVersion{} + if err := json.Unmarshal([]byte(head.CsvJSON), &csv); err != nil { + return "" + } + return csv.Spec.DisplayName +} + +type ListChannels struct { + IndexReference string + PackageName string +} + +func (l *ListChannels) Run(ctx context.Context) (*ListChannelsResult, error) { + m, err := indexRefToModel(ctx, l.IndexReference) + if err != nil { + return nil, err + } + + pkgs, err := getPackages(m, l.PackageName) + if err != nil { + return nil, err + } + + channels := []model.Channel{} + for _, pkg := range pkgs { + for _, ch := range pkg.Channels { + channels = append(channels, *ch) + } + } + + sort.Slice(channels, func(i, j int) bool { + if channels[i].Package.Name != channels[j].Package.Name { + return channels[i].Package.Name < channels[j].Package.Name + } + return channels[i].Name < channels[j].Name + }) + return &ListChannelsResult{Channels: channels}, nil +} + +type ListChannelsResult struct { + Channels []model.Channel +} + +func (r *ListChannelsResult) WriteColumns(w io.Writer) error { + tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) + if _, err := fmt.Fprintln(tw, "PACKAGE\tCHANNEL\tHEAD"); err != nil { + return err + } + for _, ch := range r.Channels { + headStr := "" + head, err := ch.Head() + if err != nil { + headStr = fmt.Sprintf("ERROR: %s", err) + } else { + headStr = head.Name + } + if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", ch.Package.Name, ch.Name, headStr); err != nil { + return err + } + } + return tw.Flush() +} + +type ListBundles struct { + IndexReference string + PackageName string +} + +func (l *ListBundles) Run(ctx context.Context) (*ListBundlesResult, error) { + m, err := indexRefToModel(ctx, l.IndexReference) + if err != nil { + return nil, err + } + + pkgs, err := getPackages(m, l.PackageName) + if err != nil { + return nil, err + } + + bundles := []model.Bundle{} + for _, pkg := range pkgs { + for _, ch := range pkg.Channels { + for _, b := range ch.Bundles { + bundles = append(bundles, *b) + } + } + } + + sort.Slice(bundles, func(i, j int) bool { + if bundles[i].Package.Name != bundles[j].Package.Name { + return bundles[i].Package.Name < bundles[j].Package.Name + } + if bundles[i].Channel.Name != bundles[j].Channel.Name { + return bundles[i].Channel.Name < bundles[j].Channel.Name + } + return bundles[i].Name < bundles[j].Name + }) + return &ListBundlesResult{Bundles: bundles}, nil +} + +type ListBundlesResult struct { + Bundles []model.Bundle +} + +func (r *ListBundlesResult) WriteColumns(w io.Writer) error { + tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) + if _, err := fmt.Fprintln(tw, "PACKAGE\tCHANNEL\tBUNDLE\tREPLACES\tSKIPS\tSKIP RANGE\tIMAGE"); err != nil { + return err + } + for _, b := range r.Bundles { + skipRange, err := getSkipRange(b) + if err != nil { + return fmt.Errorf("get skipRange for bundle %q: %v", b.Name, err) + } + if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", b.Package.Name, b.Channel.Name, b.Name, b.Replaces, strings.Join(b.Skips, ","), skipRange, b.Image); err != nil { + return err + } + + } + return tw.Flush() +} + +func getSkipRange(b model.Bundle) (string, error) { + props, err := property.Parse(b.Properties) + if err != nil { + return "", err + } + if len(props.SkipRanges) > 1 { + return "", fmt.Errorf("multiple skip ranges not supported") + } + if len(props.SkipRanges) == 0 { + return "", nil + } + return string(props.SkipRanges[0]), nil +} + +func indexRefToModel(ctx context.Context, ref string) (model.Model, error) { + render := Render{ + Refs: []string{ref}, + AllowedRefMask: RefDCImage | RefDCDir | RefSqliteImage | RefSqliteFile, + } + cfg, err := render.Run(ctx) + if err != nil { + if errors.Is(err, &ErrNotAllowed{}) { + return nil, fmt.Errorf("cannot list non-index %q", ref) + } + return nil, err + } + + return declcfg.ConvertToModel(*cfg) +} + +func getPackages(m model.Model, packageName string) (model.Model, error) { + if packageName == "" { + return m, nil + } + pkg, ok := m[packageName] + if !ok { + return nil, fmt.Errorf("package %q not found", packageName) + } + return model.Model{packageName: pkg}, nil +} diff --git a/vendor/github.com/operator-framework/operator-registry/internal/action/render.go b/vendor/github.com/operator-framework/operator-registry/internal/action/render.go index 64393c077a..2f4293a394 100644 --- a/vendor/github.com/operator-framework/operator-registry/internal/action/render.go +++ b/vendor/github.com/operator-framework/operator-registry/internal/action/render.go @@ -25,9 +25,34 @@ import ( "github.com/operator-framework/operator-registry/pkg/sqlite" ) +type RefType uint + +const ( + RefBundleImage RefType = 1 << iota + RefSqliteImage + RefSqliteFile + RefDCImage + RefDCDir + + RefAll = 0 +) + +func (r RefType) Allowed(refType RefType) bool { + return r == RefAll || r&refType == refType +} + +var _ error = &ErrNotAllowed{} + +type ErrNotAllowed struct{} + +func (_ *ErrNotAllowed) Error() string { + return "not allowed" +} + type Render struct { - Refs []string - Registry image.Registry + Refs []string + Registry image.Registry + AllowedRefMask RefType } func nullLogger() *logrus.Entry { @@ -48,26 +73,9 @@ func (r Render) Run(ctx context.Context) (*declcfg.DeclarativeConfig, error) { var cfgs []declcfg.DeclarativeConfig for _, ref := range r.Refs { - var ( - cfg *declcfg.DeclarativeConfig - err error - ) - if stat, serr := os.Stat(ref); serr == nil { - if stat.IsDir() { - cfg, err = declcfg.LoadFS(os.DirFS(ref)) - } else { - // The only supported file type is an sqlite DB file, - // since declarative configs will be in a directory. - if err := checkDBFile(ref); err != nil { - return nil, err - } - cfg, err = sqliteToDeclcfg(ctx, ref) - } - } else { - cfg, err = r.imageToDeclcfg(ctx, ref) - } + cfg, err := r.renderReference(ctx, ref) if err != nil { - return nil, fmt.Errorf("render reference %q: %v", ref, err) + return nil, fmt.Errorf("render reference %q: %w", ref, err) } renderBundleObjects(cfg) cfgs = append(cfgs, *cfg) @@ -96,6 +104,28 @@ func (r Render) createRegistry() (*containerdregistry.Registry, error) { return reg, nil } +func (r Render) renderReference(ctx context.Context, ref string) (*declcfg.DeclarativeConfig, error) { + if stat, serr := os.Stat(ref); serr == nil { + if stat.IsDir() { + if !r.AllowedRefMask.Allowed(RefDCDir) { + return nil, fmt.Errorf("cannot render DC directory: %w", &ErrNotAllowed{}) + } + return declcfg.LoadFS(os.DirFS(ref)) + } else { + // The only supported file type is an sqlite DB file, + // since declarative configs will be in a directory. + if err := checkDBFile(ref); err != nil { + return nil, err + } + if !r.AllowedRefMask.Allowed(RefSqliteFile) { + return nil, fmt.Errorf("cannot render sqlite file: %w", &ErrNotAllowed{}) + } + return sqliteToDeclcfg(ctx, ref) + } + } + return r.imageToDeclcfg(ctx, ref) +} + func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.DeclarativeConfig, error) { ref := image.SimpleReference(imageRef) if err := r.Registry.Pull(ctx, ref); err != nil { @@ -116,17 +146,26 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D var cfg *declcfg.DeclarativeConfig if dbFile, ok := labels[containertools.DbLocationLabel]; ok { + if !r.AllowedRefMask.Allowed(RefSqliteImage) { + return nil, fmt.Errorf("cannot render sqlite image: %w", &ErrNotAllowed{}) + } cfg, err = sqliteToDeclcfg(ctx, filepath.Join(tmpDir, dbFile)) if err != nil { return nil, err } } else if configsDir, ok := labels["operators.operatorframework.io.index.configs.v1"]; ok { // TODO(joelanford): Make a constant for above configs location label + if !r.AllowedRefMask.Allowed(RefDCImage) { + return nil, fmt.Errorf("cannot render DC image: %w", &ErrNotAllowed{}) + } cfg, err = declcfg.LoadFS(os.DirFS(filepath.Join(tmpDir, configsDir))) if err != nil { return nil, err } } else if _, ok := labels[bundle.PackageLabel]; ok { + if !r.AllowedRefMask.Allowed(RefBundleImage) { + return nil, fmt.Errorf("cannot render bundle image: %w", &ErrNotAllowed{}) + } img, err := registry.NewImageInput(ref, tmpDir) if err != nil { return nil, err diff --git a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml index 830fb457af..189b6849eb 100644 --- a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml +++ b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.1.0/manifests/foo.v0.1.0.csv.yaml @@ -6,6 +6,7 @@ metadata: annotations: olm.skipRange: <0.1.0 spec: + displayName: "Foo Operator" customresourcedefinitions: owned: - group: test.foo diff --git a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml index 607918ec91..d7e30bee03 100644 --- a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml +++ b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/manifests/foo.v0.2.0.csv.yaml @@ -6,6 +6,7 @@ metadata: annotations: olm.skipRange: <0.2.0 spec: + displayName: "Foo Operator" customresourcedefinitions: owned: - group: test.foo diff --git a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml index 34187f125f..dc4cc05f68 100644 --- a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml +++ b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-bundle-v0.2.0/metadata/annotations.yaml @@ -1,4 +1,4 @@ annotations: operators.operatorframework.io.bundle.package.v1: foo - operators.operatorframework.io.bundle.channels.v1: beta + operators.operatorframework.io.bundle.channels.v1: beta,stable operators.operatorframework.io.bundle.channel.default.v1: beta diff --git a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml index da0b2131e5..2e108e380d 100644 --- a/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml +++ b/vendor/github.com/operator-framework/operator-registry/internal/action/testdata/foo-index-v0.2.0-declcfg/foo/index.yaml @@ -33,7 +33,7 @@ properties: value: <0.1.0 - type: olm.bundle.object value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sInJlbGF0ZWRJbWFnZXMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjEuMCIsIm5hbWUiOiJvcGVyYXRvciJ9XSwidmVyc2lvbiI6IjAuMS4wIn19 + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= - type: olm.bundle.object value: data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 @@ -51,6 +51,10 @@ properties: value: name: beta replaces: foo.v0.1.0 + - type: olm.channel + value: + name: stable + replaces: foo.v0.1.0 - type: olm.gvk value: group: test.foo @@ -77,7 +81,7 @@ properties: value: foo.v0.1.2 - type: olm.bundle.object value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sInJlbGF0ZWRJbWFnZXMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjIuMCIsIm5hbWUiOiJvcGVyYXRvciJ9XSwicmVwbGFjZXMiOiJmb28udjAuMS4wIiwic2tpcHMiOlsiZm9vLnYwLjEuMSIsImZvby52MC4xLjIiXSwidmVyc2lvbiI6IjAuMi4wIn19 + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMi4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJyZXBsYWNlcyI6ImZvby52MC4xLjAiLCJza2lwcyI6WyJmb28udjAuMS4xIiwiZm9vLnYwLjEuMiJdLCJ2ZXJzaW9uIjoiMC4yLjAifX0= - type: olm.bundle.object value: data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 diff --git a/vendor/github.com/operator-framework/operator-registry/internal/declcfg/model_to_declcfg.go b/vendor/github.com/operator-framework/operator-registry/internal/declcfg/model_to_declcfg.go index d3d4ed686d..df8357a917 100644 --- a/vendor/github.com/operator-framework/operator-registry/internal/declcfg/model_to_declcfg.go +++ b/vendor/github.com/operator-framework/operator-registry/internal/declcfg/model_to_declcfg.go @@ -68,6 +68,14 @@ func traverseModelChannels(mpkg model.Package) []Bundle { var out []Bundle for _, b := range bundles { b.Properties = property.Deduplicate(b.Properties) + + sort.Slice(b.Properties, func(i, j int) bool { + if b.Properties[i].Type != b.Properties[j].Type { + return b.Properties[i].Type < b.Properties[j].Type + } + return string(b.Properties[i].Value) < string(b.Properties[j].Value) + }) + out = append(out, *b) } return out diff --git a/vendor/github.com/operator-framework/operator-registry/internal/model/model.go b/vendor/github.com/operator-framework/operator-registry/internal/model/model.go index e0abf6fbf5..78639d93d0 100644 --- a/vendor/github.com/operator-framework/operator-registry/internal/model/model.go +++ b/vendor/github.com/operator-framework/operator-registry/internal/model/model.go @@ -3,6 +3,7 @@ package model import ( "errors" "fmt" + "sort" "strings" "github.com/h2non/filetype" @@ -160,6 +161,7 @@ func (c Channel) Head() (*Bundle, error) { for _, head := range heads { headNames = append(headNames, head.Name) } + sort.Strings(headNames) return nil, fmt.Errorf("multiple channel heads found in graph: %s", strings.Join(headNames, ", ")) } return heads[0], nil @@ -269,9 +271,6 @@ type RelatedImage struct { func (i RelatedImage) Validate() error { result := newValidationError("invalid related image") - if i.Name == "" { - result.subErrors = append(result.subErrors, fmt.Errorf("name must be set")) - } if i.Image == "" { result.subErrors = append(result.subErrors, fmt.Errorf("image must be set")) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 5b27540840..dab38fd19b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -556,6 +556,7 @@ github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile github.com/operator-framework/operator-lifecycle-manager/pkg/lib/proxy github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer github.com/operator-framework/operator-lifecycle-manager/pkg/lib/scoped +github.com/operator-framework/operator-lifecycle-manager/pkg/lib/server github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals github.com/operator-framework/operator-lifecycle-manager/pkg/lib/time github.com/operator-framework/operator-lifecycle-manager/pkg/metrics @@ -572,20 +573,21 @@ github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/stor github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/version github.com/operator-framework/operator-lifecycle-manager/pkg/version github.com/operator-framework/operator-lifecycle-manager/util/cpb -# github.com/operator-framework/operator-registry v1.13.6 => ./staging/operator-registry +# github.com/operator-framework/operator-registry v1.17.5 => ./staging/operator-registry ## explicit github.com/operator-framework/operator-registry/cmd/configmap-server github.com/operator-framework/operator-registry/cmd/initializer github.com/operator-framework/operator-registry/cmd/opm github.com/operator-framework/operator-registry/cmd/opm/alpha github.com/operator-framework/operator-registry/cmd/opm/alpha/bundle -github.com/operator-framework/operator-registry/cmd/opm/alpha/init -github.com/operator-framework/operator-registry/cmd/opm/alpha/render -github.com/operator-framework/operator-registry/cmd/opm/alpha/serve -github.com/operator-framework/operator-registry/cmd/opm/alpha/validate +github.com/operator-framework/operator-registry/cmd/opm/alpha/list github.com/operator-framework/operator-registry/cmd/opm/index +github.com/operator-framework/operator-registry/cmd/opm/init github.com/operator-framework/operator-registry/cmd/opm/registry +github.com/operator-framework/operator-registry/cmd/opm/render github.com/operator-framework/operator-registry/cmd/opm/root +github.com/operator-framework/operator-registry/cmd/opm/serve +github.com/operator-framework/operator-registry/cmd/opm/validate github.com/operator-framework/operator-registry/cmd/opm/version github.com/operator-framework/operator-registry/cmd/registry-server github.com/operator-framework/operator-registry/internal/action