From 0deaadea3d25c540314e35dd3b508f8d396a036e Mon Sep 17 00:00:00 2001 From: Nicola Ferraro Date: Fri, 21 Feb 2020 09:35:27 +0100 Subject: [PATCH] Fix #1286: overwrite resources when --force option is passed to kamel install --- pkg/cmd/install.go | 13 ++-- pkg/controller/integrationplatform/create.go | 4 +- pkg/install/builder.go | 4 +- pkg/install/common.go | 62 ++++++++++---------- pkg/install/operator.go | 32 +++++----- pkg/install/secret.go | 4 +- 6 files changed, 61 insertions(+), 58 deletions(-) diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go index 8b973b8d88..b0441dcb15 100644 --- a/pkg/cmd/install.go +++ b/pkg/cmd/install.go @@ -60,6 +60,9 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) (*cobra.Command, *installCmdO return err } if err := options.install(cmd, args); err != nil { + if k8serrors.IsAlreadyExists(err) { + return errors.Wrap(err, "Camel K seems already installed (use the --force option to overwrite existing resources)") + } return err } return nil @@ -73,6 +76,7 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) (*cobra.Command, *installCmdO cmd.Flags().Bool("skip-cluster-setup", false, "Skip the cluster-setup phase") cmd.Flags().Bool("example", false, "Install example integration") cmd.Flags().Bool("global", false, "Configure the operator to watch all namespaces. No integration platform is created.") + cmd.Flags().Bool("force", false, "Force replacement of configuration resources when already present.") cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml") cmd.Flags().String("organization", "", "A organization on the Docker registry that can be used to publish images") @@ -137,6 +141,7 @@ type installCmdOptions struct { Global bool `mapstructure:"global"` KanikoBuildCache bool `mapstructure:"kaniko-build-cache"` Save bool `mapstructure:"save"` + Force bool `mapstructure:"force"` Olm bool `mapstructure:"olm"` ClusterType string `mapstructure:"cluster-type"` OutputFormat string `mapstructure:"output"` @@ -238,7 +243,7 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { Global: o.Global, ClusterType: o.ClusterType, } - err = install.OperatorOrCollect(o.Context, c, cfg, collection) + err = install.OperatorOrCollect(o.Context, c, cfg, collection, o.Force) if err != nil { return err } @@ -250,7 +255,7 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { if o.registryAuth.IsSet() { regData := o.registryAuth regData.Registry = o.registry.Address - generatedSecretName, err = install.RegistrySecretOrCollect(o.Context, c, namespace, regData, collection) + generatedSecretName, err = install.RegistrySecretOrCollect(o.Context, c, namespace, regData, collection, o.Force) if err != nil { return err } @@ -346,14 +351,14 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { // to be created in other namespaces. // In OLM mode, the operator is installed in an external namespace, so it's ok to install the platform locally. if !o.Global || installViaOLM { - err = install.RuntimeObjectOrCollect(o.Context, c, namespace, collection, platform) + err = install.RuntimeObjectOrCollect(o.Context, c, namespace, collection, o.Force, platform) if err != nil { return err } } if o.ExampleSetup { - err = install.ExampleOrCollect(o.Context, c, namespace, collection) + err = install.ExampleOrCollect(o.Context, c, namespace, collection, o.Force) if err != nil { return err } diff --git a/pkg/controller/integrationplatform/create.go b/pkg/controller/integrationplatform/create.go index 7d387a94e9..e8efebac62 100644 --- a/pkg/controller/integrationplatform/create.go +++ b/pkg/controller/integrationplatform/create.go @@ -49,7 +49,7 @@ func (action *createAction) Handle(ctx context.Context, platform *v1.Integration for _, k := range deploy.Resources("/") { if strings.HasPrefix(k, "camel-catalog-") { action.L.Infof("Installing camel catalog: %s", k) - err := install.Resources(ctx, action.client, platform.Namespace, install.IdentityResourceCustomizer, k) + err := install.Resources(ctx, action.client, platform.Namespace, true, install.IdentityResourceCustomizer, k) if err != nil { return nil, err } @@ -73,7 +73,7 @@ func (action *createAction) Handle(ctx context.Context, platform *v1.Integration if len(res) > 0 { action.L.Info("Installing custom platform resources") - err := install.Resources(ctx, action.client, platform.Namespace, install.IdentityResourceCustomizer, res...) + err := install.Resources(ctx, action.client, platform.Namespace, true, install.IdentityResourceCustomizer, res...) if err != nil { return nil, err } diff --git a/pkg/install/builder.go b/pkg/install/builder.go index cd906c0bba..d9f0d17df9 100644 --- a/pkg/install/builder.go +++ b/pkg/install/builder.go @@ -43,7 +43,7 @@ func BuilderServiceAccountRoles(ctx context.Context, c client.Client, namespace } func installBuilderServiceAccountRolesOpenshift(ctx context.Context, c client.Client, namespace string) error { - return ResourcesOrCollect(ctx, c, namespace, nil, IdentityResourceCustomizer, + return ResourcesOrCollect(ctx, c, namespace, nil, true, IdentityResourceCustomizer, "builder-service-account.yaml", "builder-role-openshift.yaml", "builder-role-binding.yaml", @@ -51,7 +51,7 @@ func installBuilderServiceAccountRolesOpenshift(ctx context.Context, c client.Cl } func installBuilderServiceAccountRolesKubernetes(ctx context.Context, c client.Client, namespace string) error { - return ResourcesOrCollect(ctx, c, namespace, nil, IdentityResourceCustomizer, + return ResourcesOrCollect(ctx, c, namespace, nil, true, IdentityResourceCustomizer, "builder-service-account.yaml", "builder-role-kubernetes.yaml", "builder-role-binding.yaml", diff --git a/pkg/install/common.go b/pkg/install/common.go index 79414c2a9b..e1328a19b0 100644 --- a/pkg/install/common.go +++ b/pkg/install/common.go @@ -42,14 +42,14 @@ var IdentityResourceCustomizer = func(object runtime.Object) runtime.Object { } // Resources installs named resources from the project resource directory -func Resources(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, names ...string) error { - return ResourcesOrCollect(ctx, c, namespace, nil, customizer, names...) +func Resources(ctx context.Context, c client.Client, namespace string, force bool, customizer ResourceCustomizer, names ...string) error { + return ResourcesOrCollect(ctx, c, namespace, nil, force, customizer, names...) } // ResourcesOrCollect -- -func ResourcesOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, customizer ResourceCustomizer, names ...string) error { +func ResourcesOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, force bool, customizer ResourceCustomizer, names ...string) error { for _, name := range names { - if err := ResourceOrCollect(ctx, c, namespace, collection, customizer, name); err != nil { + if err := ResourceOrCollect(ctx, c, namespace, collection, force, customizer, name); err != nil { return err } } @@ -57,27 +57,27 @@ func ResourcesOrCollect(ctx context.Context, c client.Client, namespace string, } // Resource installs a single named resource from the project resource directory -func Resource(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, name string) error { - return ResourceOrCollect(ctx, c, namespace, nil, customizer, name) +func Resource(ctx context.Context, c client.Client, namespace string, force bool, customizer ResourceCustomizer, name string) error { + return ResourceOrCollect(ctx, c, namespace, nil, force, customizer, name) } // ResourceOrCollect -- -func ResourceOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, customizer ResourceCustomizer, name string) error { +func ResourceOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, force bool, customizer ResourceCustomizer, name string) error { obj, err := kubernetes.LoadResourceFromYaml(c.GetScheme(), deploy.ResourceAsString(name)) if err != nil { return err } - return RuntimeObjectOrCollect(ctx, c, namespace, collection, customizer(obj)) + return RuntimeObjectOrCollect(ctx, c, namespace, collection, force, customizer(obj)) } // RuntimeObject installs a single runtime object -func RuntimeObject(ctx context.Context, c client.Client, namespace string, obj runtime.Object) error { - return RuntimeObjectOrCollect(ctx, c, namespace, nil, obj) +func RuntimeObject(ctx context.Context, c client.Client, namespace string, force bool, obj runtime.Object) error { + return RuntimeObjectOrCollect(ctx, c, namespace, nil, force, obj) } // RuntimeObjectOrCollect -- -func RuntimeObjectOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, obj runtime.Object) error { +func RuntimeObjectOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, force bool, obj runtime.Object) error { if collection != nil { // Adding to the collection before setting the namespace collection.Add(obj) @@ -88,31 +88,29 @@ func RuntimeObjectOrCollect(ctx context.Context, c client.Client, namespace stri metaObject.SetNamespace(namespace) } - err := c.Create(ctx, obj) - if err != nil && errors.IsAlreadyExists(err) { - // Don't recreate Service object - if obj.GetObjectKind().GroupVersionKind().Kind == "Service" { - return nil - } - // Don't recreate integration kits, platforms, etc - if obj.GetObjectKind().GroupVersionKind().Kind == v1.IntegrationKitKind { - return nil - } - if obj.GetObjectKind().GroupVersionKind().Kind == v1.IntegrationPlatformKind { - return nil - } - if obj.GetObjectKind().GroupVersionKind().Kind == v1.CamelCatalogKind { - return nil + if obj.GetObjectKind().GroupVersionKind().Kind == "PersistentVolumeClaim" { + if err := c.Create(ctx, obj); err != nil && !errors.IsAlreadyExists(err) { + return err } - if obj.GetObjectKind().GroupVersionKind().Kind == v1.BuildKind { - return nil + } + + if force { + if err := kubernetes.ReplaceResource(ctx, c, obj); err != nil { + return err } - if obj.GetObjectKind().GroupVersionKind().Kind == "PersistentVolumeClaim" { - return nil + // For some resources, also reset the status + if obj.GetObjectKind().GroupVersionKind().Kind == v1.IntegrationKitKind || + obj.GetObjectKind().GroupVersionKind().Kind == v1.BuildKind || + obj.GetObjectKind().GroupVersionKind().Kind == v1.IntegrationPlatformKind { + if err := c.Status().Update(ctx, obj); err != nil { + return err + } } - return c.Update(ctx, obj) + return nil } - return err + + // Just try to create them + return c.Create(ctx, obj) } func isOpenShift(c kube.Interface, clusterType string) (bool, error) { diff --git a/pkg/install/operator.go b/pkg/install/operator.go index 02a01ca0a5..e61f13f4cd 100644 --- a/pkg/install/operator.go +++ b/pkg/install/operator.go @@ -45,12 +45,12 @@ type OperatorConfiguration struct { } // Operator installs the operator resources in the given namespace -func Operator(ctx context.Context, c client.Client, cfg OperatorConfiguration) error { - return OperatorOrCollect(ctx, c, cfg, nil) +func Operator(ctx context.Context, c client.Client, cfg OperatorConfiguration, force bool) error { + return OperatorOrCollect(ctx, c, cfg, nil, force) } // OperatorOrCollect installs the operator resources or adds them to the collector if present -func OperatorOrCollect(ctx context.Context, c client.Client, cfg OperatorConfiguration, collection *kubernetes.Collection) error { +func OperatorOrCollect(ctx context.Context, c client.Client, cfg OperatorConfiguration, collection *kubernetes.Collection, force bool) error { customizer := func(o runtime.Object) runtime.Object { if cfg.CustomImage != "" { if d, ok := o.(*appsv1.Deployment); ok { @@ -108,11 +108,11 @@ func OperatorOrCollect(ctx context.Context, c client.Client, cfg OperatorConfigu return err } if isOpenshift { - if err := installOpenshift(ctx, c, cfg.Namespace, customizer, collection); err != nil { + if err := installOpenshift(ctx, c, cfg.Namespace, customizer, collection, force); err != nil { return err } } else { - if err := installKubernetes(ctx, c, cfg.Namespace, customizer, collection); err != nil { + if err := installKubernetes(ctx, c, cfg.Namespace, customizer, collection, force); err != nil { return err } } @@ -122,13 +122,13 @@ func OperatorOrCollect(ctx context.Context, c client.Client, cfg OperatorConfigu return err } if isKnative { - return installKnative(ctx, c, cfg.Namespace, customizer, collection) + return installKnative(ctx, c, cfg.Namespace, customizer, collection, force) } return nil } -func installOpenshift(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection) error { - return ResourcesOrCollect(ctx, c, namespace, collection, customizer, +func installOpenshift(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection, force bool) error { + return ResourcesOrCollect(ctx, c, namespace, collection, force, customizer, "operator-service-account.yaml", "operator-role-openshift.yaml", "operator-role-binding.yaml", @@ -136,8 +136,8 @@ func installOpenshift(ctx context.Context, c client.Client, namespace string, cu ) } -func installKubernetes(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection) error { - return ResourcesOrCollect(ctx, c, namespace, collection, customizer, +func installKubernetes(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection, force bool) error { + return ResourcesOrCollect(ctx, c, namespace, collection, force, customizer, "operator-service-account.yaml", "operator-role-kubernetes.yaml", "operator-role-binding.yaml", @@ -145,8 +145,8 @@ func installKubernetes(ctx context.Context, c client.Client, namespace string, c ) } -func installKnative(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection) error { - return ResourcesOrCollect(ctx, c, namespace, collection, customizer, +func installKnative(ctx context.Context, c client.Client, namespace string, customizer ResourceCustomizer, collection *kubernetes.Collection, force bool) error { + return ResourcesOrCollect(ctx, c, namespace, collection, force, customizer, "operator-role-knative.yaml", "operator-role-binding-knative.yaml", ) @@ -195,13 +195,13 @@ func PlatformOrCollect(ctx context.Context, c client.Client, clusterType string, } // Example -- -func Example(ctx context.Context, c client.Client, namespace string) error { - return ExampleOrCollect(ctx, c, namespace, nil) +func Example(ctx context.Context, c client.Client, namespace string, force bool) error { + return ExampleOrCollect(ctx, c, namespace, nil, force) } // ExampleOrCollect -- -func ExampleOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection) error { - return ResourcesOrCollect(ctx, c, namespace, collection, IdentityResourceCustomizer, +func ExampleOrCollect(ctx context.Context, c client.Client, namespace string, collection *kubernetes.Collection, force bool) error { + return ResourcesOrCollect(ctx, c, namespace, collection, force, IdentityResourceCustomizer, "cr-example.yaml", ) } diff --git a/pkg/install/secret.go b/pkg/install/secret.go index 026b52048f..70a7536f50 100644 --- a/pkg/install/secret.go +++ b/pkg/install/secret.go @@ -30,7 +30,7 @@ import ( const registrySecretName = "camel-k-registry-secret" // RegistrySecretOrCollect generates a secret from auth settings and creates it on the cluster (or appends it to the collection) -func RegistrySecretOrCollect(ctx context.Context, c client.Client, namespace string, auth registry.Auth, collection *kubernetes.Collection) (string, error) { +func RegistrySecretOrCollect(ctx context.Context, c client.Client, namespace string, auth registry.Auth, collection *kubernetes.Collection, force bool) (string, error) { secretData, err := auth.GenerateDockerConfig() if err != nil { return "", err @@ -51,7 +51,7 @@ func RegistrySecretOrCollect(ctx context.Context, c client.Client, namespace str }, } - if err := RuntimeObjectOrCollect(ctx, c, namespace, collection, ®istrySecret); err != nil { + if err := RuntimeObjectOrCollect(ctx, c, namespace, collection, force, ®istrySecret); err != nil { return "", err } return registrySecretName, nil