You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by as...@apache.org on 2020/02/12 09:48:57 UTC

[camel-k] 02/06: Fix #1223: install and uninstall via OLM

This is an automated email from the ASF dual-hosted git repository.

astefanutti pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 8228f4b84b510b224d4b3504eec4e6a2b08b586a
Author: Nicola Ferraro <ni...@gmail.com>
AuthorDate: Mon Feb 10 17:15:26 2020 +0100

    Fix #1223: install and uninstall via OLM
---
 pkg/apis/addtoscheme_olm.go |  2 ++
 pkg/cmd/install.go          | 16 +++++++----
 pkg/cmd/uninstall.go        | 64 ++++++++++++++++++++++++++++++-----------
 pkg/util/olm/available.go   |  6 +++-
 pkg/util/olm/operator.go    | 69 +++++++++++++++++++++++++++++++++++++--------
 5 files changed, 121 insertions(+), 36 deletions(-)

diff --git a/pkg/apis/addtoscheme_olm.go b/pkg/apis/addtoscheme_olm.go
index a69aa84..d891e98 100644
--- a/pkg/apis/addtoscheme_olm.go
+++ b/pkg/apis/addtoscheme_olm.go
@@ -18,10 +18,12 @@ limitations under the License.
 package apis
 
 import (
+	olmv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1"
 	olmv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
 )
 
 func init() {
 	// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
 	AddToSchemes = append(AddToSchemes, olmv1alpha1.AddToScheme)
+	AddToSchemes = append(AddToSchemes, olmv1.AddToScheme)
 }
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index 8e9c669..292c9d9 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -134,7 +134,7 @@ type installCmdOptions struct {
 	Global            bool     `mapstructure:"global"`
 	KanikoBuildCache  bool     `mapstructure:"kaniko-build-cache"`
 	Save              bool     `mapstructure:"save"`
-	Olm               bool    `mapstructure:"olm"`
+	Olm               bool     `mapstructure:"olm"`
 	ClusterType       string   `mapstructure:"cluster-type"`
 	OutputFormat      string   `mapstructure:"output"`
 	RuntimeVersion    string   `mapstructure:"runtime-version"`
@@ -172,18 +172,22 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error {
 		if olmClient, err = clientProvider.Get(); err != nil {
 			return err
 		}
-		if installViaOLM, err = olm.IsAvailable(o.Context, olmClient); err != nil {
+		if installViaOLM, err = olm.IsAvailable(o.Context, olmClient, o.Namespace); err != nil {
 			return errors.Wrap(err, "error while checking OLM availability. Run with '--olm=false' to skip this check")
 		}
 
 		if installViaOLM {
-			fmt.Fprintln(cobraCmd.OutOrStdout(), "OLM is available in the cluster");
-			if err = olm.Install(o.Context, olmClient, o.Namespace, o.Global, o.olmOptions, collection); err != nil {
+			fmt.Fprintln(cobraCmd.OutOrStdout(), "OLM is available in the cluster")
+			var installed bool
+			if installed, err = olm.Install(o.Context, olmClient, o.Namespace, o.Global, o.olmOptions, collection); err != nil {
 				return err
 			}
+			if !installed {
+				fmt.Fprintln(cobraCmd.OutOrStdout(), "OLM resources are already available: skipping installation")
+			}
 		}
 
-		if err = install.WaitForAllCRDInstallation(o.Context, clientProvider, 90 * time.Second); err != nil {
+		if err = install.WaitForAllCRDInstallation(o.Context, clientProvider, 90*time.Second); err != nil {
 			return err
 		}
 	}
@@ -202,7 +206,7 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error {
 
 	if o.ClusterSetupOnly {
 		if collection == nil {
-			fmt.Fprintln(cobraCmd.OutOrStdout(),"Camel K cluster setup completed successfully")
+			fmt.Fprintln(cobraCmd.OutOrStdout(), "Camel K cluster setup completed successfully")
 		}
 	} else {
 		c, err := o.GetCmdClient()
diff --git a/pkg/cmd/uninstall.go b/pkg/cmd/uninstall.go
index 045b1b8..11462f8 100644
--- a/pkg/cmd/uninstall.go
+++ b/pkg/cmd/uninstall.go
@@ -18,9 +18,10 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
 	"fmt"
 
+	"github.com/apache/camel-k/pkg/util/olm"
+	"github.com/pkg/errors"
 	"k8s.io/client-go/kubernetes"
 
 	"github.com/apache/camel-k/pkg/client"
@@ -39,7 +40,7 @@ func newCmdUninstall(rootCmdOptions *RootCmdOptions) (*cobra.Command, *uninstall
 		Use:   "uninstall",
 		Short: "Uninstall Camel K from a Kubernetes cluster",
 		Long:  `Uninstalls Camel K from a Kubernetes or OpenShift cluster.`,
-		Run:   options.uninstall,
+		RunE:  options.uninstall,
 	}
 
 	cmd.Flags().BoolVar(&options.skipOperator, "skip-operator", false, "Do not uninstall the Camel-K Operator in the current namespace")
@@ -50,6 +51,11 @@ func newCmdUninstall(rootCmdOptions *RootCmdOptions) (*cobra.Command, *uninstall
 	cmd.Flags().BoolVar(&options.skipIntegrationPlatform, "skip-integration-platform", false, "Do not uninstall the Camel-K Integration Platform in the current namespace")
 	cmd.Flags().BoolVar(&options.skipServiceAccounts, "skip-service-accounts", false, "Do not uninstall the Camel-K Service Accounts in the current namespace")
 	cmd.Flags().BoolVar(&options.skipConfigMaps, "skip-config-maps", false, "Do not uninstall the Camel-K Config Maps in the current namespace")
+	cmd.Flags().BoolVar(&options.global, "global", false, "Indicates that a global installation is going to be uninstalled (affects OLM)")
+	cmd.Flags().BoolVar(&options.olmEnabled, "olm", true, "Try to uninstall via OLM (Operator Lifecycle Manager) if available")
+	cmd.Flags().StringVar(&options.olmOptions.OperatorName, "olm-operator-name", olm.DefaultOperatorName, "Name of the Camel K operator in the OLM source or marketplace")
+	cmd.Flags().StringVar(&options.olmOptions.Package, "olm-package", olm.DefaultPackage, "Name of the Camel K package in the OLM source or marketplace")
+	cmd.Flags().StringVar(&options.olmOptions.GlobalNamespace, "olm-global-namespace", olm.DefaultGlobalNamespace, "A namespace containing an OperatorGroup that defines global scope for the operator (used in combination with the --global flag)")
 
 	// completion support
 	configureBashAnnotationForFlag(
@@ -73,6 +79,10 @@ type uninstallCmdOptions struct {
 	skipIntegrationPlatform bool
 	skipServiceAccounts     bool
 	skipConfigMaps          bool
+	olmEnabled              bool
+	global                  bool
+
+	olmOptions olm.Options
 }
 
 var defaultListOptions = metav1.ListOptions{
@@ -80,33 +90,53 @@ var defaultListOptions = metav1.ListOptions{
 }
 
 // nolint: gocyclo
-func (o *uninstallCmdOptions) uninstall(_ *cobra.Command, _ []string) {
+func (o *uninstallCmdOptions) uninstall(cmd *cobra.Command, _ []string) error {
 	c, err := o.GetCmdClient()
 	if err != nil {
-		return
+		return err
+	}
+
+	uninstallViaOLM := false
+	if o.olmEnabled {
+		var err error
+		if uninstallViaOLM, err = olm.IsAvailable(o.Context, c, o.Namespace); err != nil {
+			return errors.Wrap(err, "error while checking OLM availability. Run with '--olm=false' to skip this check")
+		}
+
+		if uninstallViaOLM {
+			fmt.Fprintln(cmd.OutOrStdout(), "OLM is available in the cluster")
+			if err = olm.Uninstall(o.Context, c, o.Namespace, o.global, o.olmOptions); err != nil {
+				return err
+			}
+			where := fmt.Sprintf("from namespace %s", o.Namespace)
+			if o.global {
+				where = "globally"
+			}
+			fmt.Fprintf(cmd.OutOrStdout(), "Camel-K OLM service removed %s\n", where)
+		}
 	}
 
 	if !o.skipIntegrationPlatform {
 		if err = o.uninstallIntegrationPlatform(); err != nil {
-			fmt.Print(err)
-			return
+			return err
 		}
-		fmt.Printf("Camel-K Integration Platform removed from namespace %s\n", o.Namespace)
+		fmt.Fprintf(cmd.OutOrStdout(), "Camel-K Integration Platform removed from namespace %s\n", o.Namespace)
 	}
 
-	if err = o.uninstallClusterWideResources(c); err != nil {
-		fmt.Print(err)
-		return
-	}
-	fmt.Printf("Camel-K Cluster Wide Resources removed from namespace %s\n", o.Namespace)
+	if !uninstallViaOLM {
+		if err = o.uninstallClusterWideResources(c); err != nil {
+			return err
+		}
+		fmt.Fprintf(cmd.OutOrStdout(), "Camel-K Cluster Wide Resources removed from namespace %s\n", o.Namespace)
 
-	if !o.skipOperator {
-		if err = o.uninstallOperator(c); err != nil {
-			fmt.Print(err)
-			return
+		if !o.skipOperator {
+			if err = o.uninstallOperator(c); err != nil {
+				return err
+			}
+			fmt.Fprintf(cmd.OutOrStdout(), "Camel-K Operator removed from namespace %s\n", o.Namespace)
 		}
-		fmt.Printf("Camel-K Operator removed from namespace %s\n", o.Namespace)
 	}
+	return nil
 }
 
 func (o *uninstallCmdOptions) uninstallOperator(c client.Client) error {
diff --git a/pkg/util/olm/available.go b/pkg/util/olm/available.go
index 2d7636f..3c1d627 100644
--- a/pkg/util/olm/available.go
+++ b/pkg/util/olm/available.go
@@ -20,7 +20,9 @@ package olm
 import (
 	"context"
 
+	"github.com/apache/camel-k/pkg/client"
 	kubernetesutils "github.com/apache/camel-k/pkg/util/kubernetes"
+	olmv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1"
 	olmv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -30,7 +32,7 @@ import (
 // IsAvailable returns true if we are connected to a cluster with OLM installed
 //
 // This method should not be called from the operator, as it might require permissions that are not available.
-func IsAvailable(ctx context.Context, c kubernetes.Interface) (bool, error) {
+func IsAvailable(ctx context.Context, c client.Client, namespace string) (bool, error) {
 	// check some Knative APIs
 	for _, api := range getOLMGroupVersions() {
 		if installed, err := isAvailable(c, api); err != nil {
@@ -39,6 +41,7 @@ func IsAvailable(ctx context.Context, c kubernetes.Interface) (bool, error) {
 			return true, nil
 		}
 	}
+
 	return false, nil
 }
 
@@ -55,5 +58,6 @@ func isAvailable(c kubernetes.Interface, api schema.GroupVersion) (bool, error)
 func getOLMGroupVersions() []schema.GroupVersion {
 	return []schema.GroupVersion{
 		olmv1alpha1.SchemeGroupVersion,
+		olmv1.SchemeGroupVersion,
 	}
 }
diff --git a/pkg/util/olm/operator.go b/pkg/util/olm/operator.go
index c5bda90..5b23d14 100644
--- a/pkg/util/olm/operator.go
+++ b/pkg/util/olm/operator.go
@@ -19,11 +19,14 @@ package olm
 
 import (
 	"context"
+	"fmt"
 	"strings"
 
 	"github.com/apache/camel-k/pkg/client"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	olmv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1"
 	olmv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
+	"github.com/pkg/errors"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	runtime "sigs.k8s.io/controller-runtime/pkg/client"
 )
@@ -54,13 +57,13 @@ var DefaultGlobalNamespace = "openshift-operators"
 
 // Options contains information about an operator in OLM
 type Options struct {
-	OperatorName        string
-	Package             string
-	Channel             string
-	Source              string
-	SourceNamespace     string
-	StartingCSV         string
-	GlobalNamespace     string
+	OperatorName    string
+	Package         string
+	Channel         string
+	Source          string
+	SourceNamespace string
+	StartingCSV     string
+	GlobalNamespace string
 }
 
 // IsOperatorInstalled tells if a OLM CSV or a Subscription is already installed in the namespace
@@ -83,13 +86,13 @@ func IsOperatorInstalled(ctx context.Context, client client.Client, namespace st
 }
 
 // Install creates a subscription for the OLM package
-func Install(ctx context.Context, client client.Client, namespace string, global bool, options Options, collection *kubernetes.Collection) error {
+func Install(ctx context.Context, client client.Client, namespace string, global bool, options Options, collection *kubernetes.Collection) (bool, error) {
 	options = fillDefaults(options)
 	if installed, err := IsOperatorInstalled(ctx, client, namespace, global, options); err != nil {
-		return err
+		return false, err
 	} else if installed {
 		// Already installed
-		return nil
+		return false, nil
 	}
 
 	targetNamespace := namespace
@@ -113,9 +116,38 @@ func Install(ctx context.Context, client client.Client, namespace string, global
 	}
 	if collection != nil {
 		collection.Add(&sub)
-		return nil
+	} else {
+		if err := client.Create(ctx, &sub); err != nil {
+			return false, err
+		}
+	}
+
+	if !global {
+		group, err := findOperatorGroup(ctx, client, namespace, options)
+		if err != nil {
+			return false, err
+		}
+		if group == nil {
+			group = &olmv1.OperatorGroup{
+				ObjectMeta: v1.ObjectMeta{
+					Namespace:    namespace,
+					GenerateName: fmt.Sprintf("%s-", namespace),
+				},
+				Spec: olmv1.OperatorGroupSpec{
+					TargetNamespaces: []string{namespace},
+				},
+			}
+			if collection != nil {
+				collection.Add(group)
+			} else {
+				if err := client.Create(ctx, group); err != nil {
+					return false, errors.Wrap(err, fmt.Sprintf("namespace %s has no operator group defined and current user is not able to create it. " +
+						"Make sure you have the right roles to install operators from OLM", namespace))
+				}
+			}
+		}
 	}
-	return client.Create(ctx, &sub)
+	return true, nil
 }
 
 // Uninstall removes CSV and subscription from the namespace
@@ -175,6 +207,19 @@ func findCSV(ctx context.Context, client client.Client, namespace string, option
 	return nil, nil
 }
 
+func findOperatorGroup(ctx context.Context, client client.Client, namespace string, options Options) (*olmv1.OperatorGroup, error) {
+	opGroupList := olmv1.OperatorGroupList{}
+	if err := client.List(ctx, &opGroupList, runtime.InNamespace(namespace)); err != nil {
+		return nil, err
+	}
+
+	if len(opGroupList.Items) > 0 {
+		return &opGroupList.Items[0], nil
+	}
+
+	return nil, nil
+}
+
 func fillDefaults(o Options) Options {
 	if o.OperatorName == "" {
 		o.OperatorName = DefaultOperatorName