From 912d9cebe298808a1ffb4fe49744de8c24f5e873 Mon Sep 17 00:00:00 2001 From: Rashmi Gottipati Date: Wed, 2 Sep 2020 13:19:28 -0400 Subject: [PATCH] Verify install plan generation and approve install plan for subscription --- internal/cmd/operator-sdk/run/cmd.go | 2 +- internal/olm/operator/bundle/install.go | 2 +- internal/olm/operator/registry/catalog.go | 5 -- .../olm/operator/registry/olm_resources.go | 11 +++ .../operator/registry/operator_installer.go | 77 ++++++++++++++++--- 5 files changed, 80 insertions(+), 17 deletions(-) diff --git a/internal/cmd/operator-sdk/run/cmd.go b/internal/cmd/operator-sdk/run/cmd.go index 539a245d23..5fb87cda1b 100644 --- a/internal/cmd/operator-sdk/run/cmd.go +++ b/internal/cmd/operator-sdk/run/cmd.go @@ -34,7 +34,7 @@ Currently only the package manifests format is supported via the 'packagemanifes cmd.AddCommand( // TODO(joelanford): enable bundle command when implementation is complete - //bundle.NewCmd(), + // bundle.NewCmd(cfg), packagemanifests.NewCmd(cfg), ) diff --git a/internal/olm/operator/bundle/install.go b/internal/olm/operator/bundle/install.go index 367610e861..a2ab4c7319 100644 --- a/internal/olm/operator/bundle/install.go +++ b/internal/olm/operator/bundle/install.go @@ -79,7 +79,7 @@ func (i *Install) setup(ctx context.Context) error { i.OperatorInstaller.CatalogSourceName = fmt.Sprintf("%s-catalog", i.OperatorInstaller.PackageName) i.OperatorInstaller.StartingCSV = csv.Name i.OperatorInstaller.Channel = strings.Split(labels["operators.operatorframework.io.bundle.channels.v1"], ",")[0] - + i.IndexImageCatalogCreator.BundleImage = i.BundleImage i.IndexImageCatalogCreator.PackageName = i.OperatorInstaller.PackageName i.IndexImageCatalogCreator.InjectBundles = []string{i.BundleImage} i.IndexImageCatalogCreator.InjectBundleMode = "replaces" diff --git a/internal/olm/operator/registry/catalog.go b/internal/olm/operator/registry/catalog.go index 06b91c341a..c12d4b5b3c 100644 --- a/internal/olm/operator/registry/catalog.go +++ b/internal/olm/operator/registry/catalog.go @@ -23,8 +23,3 @@ import ( type CatalogCreator interface { CreateCatalog(ctx context.Context, name string) (*v1alpha1.CatalogSource, error) } - -// TODO: modify this as necessary. -type InstallPlanApprover interface { - Approve(ctx context.Context, name string) error -} diff --git a/internal/olm/operator/registry/olm_resources.go b/internal/olm/operator/registry/olm_resources.go index 4a1f439058..61255efdd6 100644 --- a/internal/olm/operator/registry/olm_resources.go +++ b/internal/olm/operator/registry/olm_resources.go @@ -48,6 +48,17 @@ func withPackageChannel(pkgName, channelName, startingCSV string) func(*v1alpha1 } } +// withInstallPlanApproval sets the Subscription's install plan approval field +// to manual +func withInstallPlanApproval(approval v1alpha1.Approval) func(*v1alpha1.Subscription) { + return func(sub *v1alpha1.Subscription) { + if sub.Spec == nil { + sub.Spec = &v1alpha1.SubscriptionSpec{} + } + sub.Spec.InstallPlanApproval = approval + } +} + // newSubscription creates a new Subscription for a CSV with a name derived // from csvName, the CSV's objectmeta.name, in namespace. opts will be applied // to the Subscription object. diff --git a/internal/olm/operator/registry/operator_installer.go b/internal/olm/operator/registry/operator_installer.go index f77571f81a..92e1a95bb3 100644 --- a/internal/olm/operator/registry/operator_installer.go +++ b/internal/olm/operator/registry/operator_installer.go @@ -26,6 +26,7 @@ import ( log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" olmclient "github.com/operator-framework/operator-sdk/internal/olm/client" @@ -73,16 +74,20 @@ func (o OperatorInstaller) InstallOperator(ctx context.Context) (*v1alpha1.Clust return nil, err } + var subscription *v1alpha1.Subscription // Create Subscription - if err = o.createSubscription(ctx, cs); err != nil { + if subscription, err = o.createSubscription(ctx, cs); err != nil { return nil, err } - // Approve Install Plan (if necessary) - if approver, ok := o.CatalogCreator.(InstallPlanApprover); ok { - if err = approver.Approve(ctx, o.PackageName); err != nil { - return nil, err - } + // Wait for the Install Plan to be generated + if err = o.waitForInstallPlan(ctx, subscription); err != nil { + return nil, err + } + + // Approve Install Plan for the subscription + if err = o.approveInstallPlan(ctx, subscription); err != nil { + return nil, err } // Wait for successfully installed CSV @@ -191,15 +196,17 @@ func (o OperatorInstaller) getOperatorGroup(ctx context.Context) (*v1.OperatorGr return &ogList.Items[0], true, nil } -func (o OperatorInstaller) createSubscription(ctx context.Context, cs *v1alpha1.CatalogSource) error { +func (o OperatorInstaller) createSubscription(ctx context.Context, cs *v1alpha1.CatalogSource) (*v1alpha1.Subscription, error) { sub := newSubscription(o.StartingCSV, o.cfg.Namespace, withPackageChannel(o.PackageName, o.Channel, o.StartingCSV), - withCatalogSource(cs.GetName(), o.cfg.Namespace)) + withCatalogSource(cs.GetName(), o.cfg.Namespace), + withInstallPlanApproval(v1alpha1.ApprovalManual)) + log.Info("Creating Subscription") if err := o.cfg.Client.Create(ctx, sub); err != nil { - return fmt.Errorf("error creating OperatorGroup: %w", err) + return nil, fmt.Errorf("error creating subscription: %w", err) } - return nil + return sub, nil } func (o OperatorInstaller) getInstalledCSV(ctx context.Context) (*v1alpha1.ClusterServiceVersion, error) { @@ -226,3 +233,53 @@ func (o OperatorInstaller) getInstalledCSV(ctx context.Context) (*v1alpha1.Clust } return csv, nil } + +// approveInstallPlan approves the install plan for a subscription, which will +// generate a CSV +func (o OperatorInstaller) approveInstallPlan(ctx context.Context, sub *v1alpha1.Subscription) error { + ip := v1alpha1.InstallPlan{} + + ipKey := types.NamespacedName{ + Name: sub.Status.InstallPlanRef.Name, + Namespace: sub.Status.InstallPlanRef.Namespace, + } + + if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + if err := o.cfg.Client.Get(ctx, ipKey, &ip); err != nil { + return fmt.Errorf("error in getting install plan: %v", err) + } + // approve the install plan by setting Approved to true + ip.Spec.Approved = true + if err := o.cfg.Client.Update(ctx, &ip); err != nil { + return fmt.Errorf("error in approving install plan: %v", err) + } + return nil + }); err != nil { + return err + } + + return nil +} + +// waitForInstallPlan verifies if an Install Plan exists through subscription status +func (o OperatorInstaller) waitForInstallPlan(ctx context.Context, sub *v1alpha1.Subscription) error { + subKey := types.NamespacedName{ + Namespace: sub.GetNamespace(), + Name: sub.GetName(), + } + + ipCheck := wait.ConditionFunc(func() (done bool, err error) { + if err := o.cfg.Client.Get(ctx, subKey, sub); err != nil { + return false, err + } + if sub.Status.InstallPlanRef != nil { + return true, nil + } + return false, nil + }) + + if err := wait.PollImmediateUntil(200*time.Millisecond, ipCheck, ctx.Done()); err != nil { + return fmt.Errorf("install plan is not available for the subscription %s: %v", sub.Name, err) + } + return nil +}