From f03cdcdbd525623d9e85b873a582390e66a4d56e Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Mon, 8 Jul 2019 14:50:03 -0400 Subject: [PATCH] feat(init): add init container to olm operator that can clean previous deployments CVO doesn't delete manifests during upgrades, so we need to clean up after older releases. --- Dockerfile | 1 + cmd/init/main.go | 128 ++++++++++++++++++ cmd/olm/main.go | 3 +- ...000_50_olm_07-olm-operator.deployment.yaml | 14 ++ e2e.Dockerfile | 1 + local.Dockerfile | 1 + ...000_50_olm_07-olm-operator.deployment.yaml | 14 ++ pkg/controller/operators/olm/operator.go | 70 +++++++--- 8 files changed, 211 insertions(+), 21 deletions(-) create mode 100644 cmd/init/main.go diff --git a/Dockerfile b/Dockerfile index 6c2f4bad41d..2e40452054a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,6 +32,7 @@ LABEL io.openshift.release.operator=true COPY --from=builder /build/bin/olm /bin/olm COPY --from=builder /build/bin/catalog /bin/catalog COPY --from=builder /build/bin/package-server /bin/package-server +COPY --from=builder /build/bin/init /bin/init # This image doesn't need to run as root user. USER 1001 diff --git a/cmd/init/main.go b/cmd/init/main.go new file mode 100644 index 00000000000..a1e63df0657 --- /dev/null +++ b/cmd/init/main.go @@ -0,0 +1,128 @@ +package main + +import ( + "context" + "flag" + "time" + + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client" + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals" +) + +var ( + kubeConfigPath = flag.String( + "kubeconfig", "", "absolute path to the kubeconfig file") + namespace = flag.String( + "namespace", "", "namespace where init runs") + crc versioned.Interface + c operatorclient.ClientInterface +) + +const ( + pollInterval = 1 * time.Second + pollDuration = 5 * time.Minute +) + +type checkResourceFunc func() error +type deleteResourceFunc func() error + +func main() { + // Get exit signal context + ctx, cancel := context.WithCancel(signals.Context()) + defer cancel() + + logger := log.New() + + // Parse the command-line flags. + flag.Parse() + + if client, err := client.NewClient(*kubeConfigPath); err != nil { + logger.WithError(err).Fatalf("error configuring client") + } else { + crc = client + } + + c = operatorclient.NewClientFromConfig(*kubeConfigPath, logger) + + if err := waitForDelete(checkCatalogSource("olm-operators"), deleteCatalogSource("olm-operators")); err != nil { + log.WithError(err).Fatal("couldn't clean previous release") + } + + if err := waitForDelete(checkConfigMap("olm-operators"), deleteConfigMap("olm-operators")); err != nil { + log.WithError(err).Fatal("couldn't clean previous release") + } + + if err := waitForDelete(checkSubscription("packageserver"), deleteSubscription("packageserver")); err != nil { + log.WithError(err).Fatal("couldn't clean previous release") + } + + ctx.Done() +} + +func checkSubscription(name string) checkResourceFunc { + return func() error { + _, err := crc.OperatorsV1alpha1().Subscriptions(*namespace).Get(name, metav1.GetOptions{}) + return err + } +} + +func deleteSubscription(name string) deleteResourceFunc { + return func() error { + return crc.OperatorsV1alpha1().Subscriptions(*namespace).Delete(name, metav1.NewDeleteOptions(0)) + } +} + +func checkConfigMap(name string) checkResourceFunc { + return func() error { + _, err := c.KubernetesInterface().CoreV1().ConfigMaps(*namespace).Get(name, metav1.GetOptions{}) + return err + } +} + +func deleteConfigMap(name string) deleteResourceFunc { + return func() error { + return c.KubernetesInterface().CoreV1().ConfigMaps(*namespace).Delete(name, metav1.NewDeleteOptions(0)) + } +} + +func checkCatalogSource(name string) checkResourceFunc { + return func() error { + _, err := crc.OperatorsV1alpha1().CatalogSources(*namespace).Get(name, metav1.GetOptions{}) + return err + } +} + +func deleteCatalogSource(name string) deleteResourceFunc { + return func() error { + return crc.OperatorsV1alpha1().CatalogSources(*namespace).Delete(name, metav1.NewDeleteOptions(0)) + } +} + +func waitForDelete(checkResource checkResourceFunc, deleteResource deleteResourceFunc) error { + if err := checkResource(); err != nil && errors.IsNotFound(err) { + return nil + } + if err := deleteResource(); err != nil { + return err + } + var err error + err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { + err := checkResource() + if errors.IsNotFound(err) { + return true, nil + } + if err != nil { + return false, err + } + return false, nil + }) + + return err +} diff --git a/cmd/olm/main.go b/cmd/olm/main.go index 04c44ff128b..5c78c8a64e2 100644 --- a/cmd/olm/main.go +++ b/cmd/olm/main.go @@ -176,7 +176,8 @@ func main() { olm.WithRestConfig(config), ) if err != nil { - log.Fatalf("error configuring operator: %s", err.Error()) + log.WithError(err).Fatalf("error configuring operator") + return } op.Run(ctx) diff --git a/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml b/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml index e66980658bd..2fc8ecf88be 100644 --- a/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml +++ b/deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml @@ -21,6 +21,20 @@ spec: {{- if and .Values.installType (eq .Values.installType "ocp") }} priorityClassName: "system-cluster-critical" {{- end }} + initContainers: + - name: init + command: + - /bin/init + - -namespace + - ${NAMESPACE} + image: {{ .Values.olm.image.ref }} + imagePullPolicy: {{ .Values.olm.image.pullPolicy }} + terminationMessagePolicy: FallbackToLogsOnError + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace containers: - name: olm-operator command: diff --git a/e2e.Dockerfile b/e2e.Dockerfile index 4acf1763d85..e2130b8f400 100644 --- a/e2e.Dockerfile +++ b/e2e.Dockerfile @@ -21,6 +21,7 @@ WORKDIR / COPY --from=builder /go/src/github.com/operator-framework/operator-lifecycle-manager/bin/olm /bin/olm COPY --from=builder /go/src/github.com/operator-framework/operator-lifecycle-manager/bin/catalog /bin/catalog COPY --from=builder /go/src/github.com/operator-framework/operator-lifecycle-manager/bin/package-server /bin/package-server +COPY --from=builder /go/src/github.com/operator-framework/operator-lifecycle-manager/build/bin/init /bin/init EXPOSE 8080 EXPOSE 5443 CMD ["/bin/olm"] diff --git a/local.Dockerfile b/local.Dockerfile index e0963b70819..5c9faa9acdb 100644 --- a/local.Dockerfile +++ b/local.Dockerfile @@ -3,6 +3,7 @@ WORKDIR / COPY olm /bin/olm COPY catalog /bin/catalog COPY package-server /bin/package-server +COPY init /bin/init EXPOSE 8080 EXPOSE 5443 CMD ["/bin/olm"] diff --git a/manifests/0000_50_olm_07-olm-operator.deployment.yaml b/manifests/0000_50_olm_07-olm-operator.deployment.yaml index 9e72fc0d9d9..9988b7b27b1 100644 --- a/manifests/0000_50_olm_07-olm-operator.deployment.yaml +++ b/manifests/0000_50_olm_07-olm-operator.deployment.yaml @@ -19,6 +19,20 @@ spec: spec: serviceAccountName: olm-operator-serviceaccount priorityClassName: "system-cluster-critical" + initContainers: + - name: init + command: + - /bin/init + - -namespace + - ${NAMESPACE} + image: quay.io/operator-framework/olm@sha256:f965474776bada158e4bf7be5c84b54460843e7478f06060990d2fdeb31b0b90 + imagePullPolicy: IfNotPresent + terminationMessagePolicy: FallbackToLogsOnError + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace containers: - name: olm-operator command: diff --git a/pkg/controller/operators/olm/operator.go b/pkg/controller/operators/olm/operator.go index 8af92f307d3..ae98343510c 100644 --- a/pkg/controller/operators/olm/operator.go +++ b/pkg/controller/operators/olm/operator.go @@ -7,9 +7,9 @@ import ( "strings" "time" - log "github.com/sirupsen/logrus" v1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1" "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" extinf "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -51,8 +51,6 @@ var ( ErrAPIServiceOwnerConflict = errors.New("unable to adopt APIService") ) -var timeNow = func() metav1.Time { return metav1.NewTime(time.Now().UTC()) } - type Operator struct { queueinformer.Operator @@ -152,8 +150,12 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(csvQueueInformer) - csvInformer.Informer().AddIndexers(cache.Indexers{index.MetaLabelIndexFuncKey: index.MetaLabelIndexFunc}) + if err := op.RegisterQueueInformer(csvQueueInformer); err != nil { + return nil, err + } + if err := csvInformer.Informer().AddIndexers(cache.Indexers{index.MetaLabelIndexFuncKey: index.MetaLabelIndexFunc}); err != nil { + return nil, err + } csvIndexer := csvInformer.Informer().GetIndexer() op.csvIndexers[namespace] = csvIndexer @@ -170,7 +172,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(csvCopyQueueInformer) + if err := op.RegisterQueueInformer(csvCopyQueueInformer); err != nil { + return nil, err + } // Register separate queue for gcing csvs csvGCQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), fmt.Sprintf("%s/csv-gc", namespace)) @@ -185,7 +189,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(csvGCQueueInformer) + if err := op.RegisterQueueInformer(csvGCQueueInformer); err != nil { + return nil, err + } // Wire OperatorGroup reconciliation operatorGroupInformer := extInformerFactory.Operators().V1().OperatorGroups() @@ -202,7 +208,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(operatorGroupQueueInformer) + if err := op.RegisterQueueInformer(operatorGroupQueueInformer); err != nil { + return nil, err + } // Wire Deployments k8sInformerFactory := informers.NewSharedInformerFactoryWithOptions(op.opClient.KubernetesInterface(), config.resyncPeriod, informers.WithNamespace(namespace)) @@ -217,7 +225,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(depQueueInformer) + if err := op.RegisterQueueInformer(depQueueInformer); err != nil { + return nil, err + } // Set up RBAC informers roleInformer := k8sInformerFactory.Rbac().V1().Roles() @@ -231,7 +241,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(roleQueueInformer) + if err := op.RegisterQueueInformer(roleQueueInformer); err != nil { + return nil, err + } roleBindingInformer := k8sInformerFactory.Rbac().V1().RoleBindings() op.lister.RbacV1().RegisterRoleBindingLister(namespace, roleBindingInformer.Lister()) @@ -244,7 +256,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(roleBindingQueueInformer) + if err := op.RegisterQueueInformer(roleBindingQueueInformer); err != nil { + return nil, err + } // Register Secret QueueInformer secretInformer := k8sInformerFactory.Core().V1().Secrets() @@ -258,7 +272,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(secretQueueInformer) + if err := op.RegisterQueueInformer(secretQueueInformer); err != nil { + return nil, err + } // Register Service QueueInformer serviceInformer := k8sInformerFactory.Core().V1().Services() @@ -272,7 +288,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(serviceQueueInformer) + if err := op.RegisterQueueInformer(serviceQueueInformer); err != nil { + return nil, err + } // Register ServiceAccount QueueInformer serviceAccountInformer := k8sInformerFactory.Core().V1().ServiceAccounts() @@ -283,7 +301,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat queueinformer.WithInformer(serviceAccountInformer.Informer()), queueinformer.WithSyncer(k8sSyncer), ) - op.RegisterQueueInformer(serviceAccountQueueInformer) + if err := op.RegisterQueueInformer(serviceAccountQueueInformer); err != nil { + return nil, err + } } k8sInformerFactory := informers.NewSharedInformerFactory(op.opClient.KubernetesInterface(), config.resyncPeriod) @@ -298,7 +318,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(clusterRoleQueueInformer) + if err := op.RegisterQueueInformer(clusterRoleQueueInformer); err != nil { + return nil, err + } clusterRoleBindingInformer := k8sInformerFactory.Rbac().V1().ClusterRoleBindings() op.lister.RbacV1().RegisterClusterRoleBindingLister(clusterRoleBindingInformer.Lister()) @@ -311,7 +333,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(clusterRoleBindingQueueInformer) + if err := op.RegisterQueueInformer(clusterRoleBindingQueueInformer); err != nil { + return nil, err + } // register namespace queueinformer namespaceInformer := k8sInformerFactory.Core().V1().Namespaces() @@ -331,7 +355,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(namespaceQueueInformer) + if err := op.RegisterQueueInformer(namespaceQueueInformer); err != nil { + return nil, err + } // Register APIService QueueInformer apiServiceInformer := kagg.NewSharedInformerFactory(op.opClient.ApiregistrationV1Interface(), config.resyncPeriod).Apiregistration().V1().APIServices() @@ -346,7 +372,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(apiServiceQueueInformer) + if err := op.RegisterQueueInformer(apiServiceQueueInformer); err != nil { + return nil, err + } // Register CustomResourceDefinition QueueInformer crdInformer := extinf.NewSharedInformerFactory(op.opClient.ApiextensionsV1beta1Interface(), config.resyncPeriod).Apiextensions().V1beta1().CustomResourceDefinitions() @@ -360,7 +388,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat if err != nil { return nil, err } - op.RegisterQueueInformer(crdQueueInformer) + if err := op.RegisterQueueInformer(crdQueueInformer); err != nil { + return nil, err + } return op, nil } @@ -1426,7 +1456,7 @@ func (a *Operator) apiServiceOwnerConflicts(csv *v1alpha1.ClusterServiceVersion) continue } - adoptable, err := a.isAPIServiceAdoptable(csv, apiService) + adoptable, err := a.isAPIServiceAdoptable(csv, apiService) if err != nil { a.logger.WithFields(log.Fields{"obj": "apiService", "labels": apiService.GetLabels()}).Errorf("adoption check failed - %v", err) }