diff --git a/cmd/uninstall.go b/cmd/uninstall.go new file mode 100644 index 0000000..a5e57ef --- /dev/null +++ b/cmd/uninstall.go @@ -0,0 +1,147 @@ +package cmd + +import ( + "flag" + "reflect" + + "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/kuadrant/kuadrantctl/authorinomanifests" + "github.com/kuadrant/kuadrantctl/istiomanifests" + "github.com/kuadrant/kuadrantctl/kuadrantmanifests" + "github.com/kuadrant/kuadrantctl/pkg/utils" +) + +// unInstallCmd represents the uninstall command +var unInstallCmd = &cobra.Command{ + Use: "uninstall", + Short: "Uninstalling kuadrant from the cluster", + Long: "The uninstall command removes kuadrant manifest bundle from the cluster.", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + // Required to have controller-runtim config package read the kubeconfig arg + err := flag.CommandLine.Parse([]string{"-kubeconfig", installKubeConfig}) + if err != nil { + return err + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + return unInstallRun(cmd, args) + }, +} + +func unInstallRun(cmd *cobra.Command, args []string) error { + err := setupScheme() + if err != nil { + return err + } + + configuration, err := config.GetConfig() + if err != nil { + return err + } + + k8sClient, err := client.New(configuration, client.Options{Scheme: scheme.Scheme}) + if err != nil { + return err + } + + err = unDeployKuadrant(k8sClient) + if err != nil { + return err + } + + err = unDeployAuthorizationProvider(k8sClient) + if err != nil { + return err + } + + err = unDeployIngressProvider(k8sClient) + if err != nil { + return err + } + + logf.Log.Info("kuadrant successfully removed") + + return nil +} + +func unDeployKuadrant(k8sClient client.Client) error { + data, err := kuadrantmanifests.Content() + if err != nil { + return err + } + + if err = utils.DecodeFile(data, scheme.Scheme, delete(k8sClient)); err != nil { + return err + } + return nil +} + +func unDeployAuthorizationProvider(k8sClient client.Client) error { + data, err := authorinomanifests.Content() + if err != nil { + return err + } + + err = utils.DecodeFile(data, scheme.Scheme, delete(k8sClient)) + if err != nil { + return err + } + + return nil +} + +func unDeployIngressProvider(k8sClient client.Client) error { + manifests := []struct { + source func() ([]byte, error) + }{ + {istiomanifests.BaseContent}, + {istiomanifests.PilotContent}, + {istiomanifests.IngressGatewayContent}, + {istiomanifests.DefaultGatewayContent}, + } + + for _, manifest := range manifests { + data, err := manifest.source() + if err != nil { + return err + } + err = utils.DecodeFile(data, scheme.Scheme, delete(k8sClient)) + if err != nil { + return err + } + } + + return nil +} + +func delete(k8sClient client.Client) utils.DecodeCallback { + return func(obj runtime.Object) error { + if (obj.GetObjectKind().GroupVersionKind().GroupVersion() == corev1.SchemeGroupVersion && obj.GetObjectKind().GroupVersionKind().Kind == reflect.TypeOf(corev1.Namespace{}).Name()) || + obj.GetObjectKind().GroupVersionKind().Group == apiextensionsv1beta1.GroupName || obj.GetObjectKind().GroupVersionKind().Group == apiextensionsv1.GroupName { + // Omit Namespace and CRD's deletion inside the manifest data + return nil + } else { + return utils.DeleteK8SObject(k8sClient, obj) + } + } +} + +func init() { + logf.SetLogger(zap.New(zap.UseDevMode(true))) + + // TODO(eastizle): add context flag to switch between kubeconfig contexts + // It would require using config.GetConfigWithContext(context string) (*rest.Config, error) + unInstallCmd.PersistentFlags().StringVarP(&installKubeConfig, "kubeconfig", "", "", "Kubernetes configuration file") + rootCmd.AddCommand(unInstallCmd) +} diff --git a/pkg/utils/k8s_utils.go b/pkg/utils/k8s_utils.go index e93b6a2..a30ac9b 100644 --- a/pkg/utils/k8s_utils.go +++ b/pkg/utils/k8s_utils.go @@ -94,6 +94,22 @@ func CreateOnlyK8SObject(k8sClient client.Client, obj runtime.Object) error { return nil } +func DeleteK8SObject(k8sClient client.Client, obj runtime.Object) error { + k8sObj, ok := obj.(client.Object) + if !ok { + return errors.New("runtime.Object could not be casted to client.Object") + } + k8sObjKind := k8sObj.DeepCopyObject().GetObjectKind() + + err := k8sClient.Delete(context.Background(), k8sObj) + logf.Log.Info("delete resource", "GKV", k8sObjKind.GroupVersionKind(), "name", k8sObj.GetName(), "error", err) + if err != nil && !apierrors.IsNotFound(err) { + // Omit NotFound error + return err + } + return nil +} + // IsDeploymentAvailable returns true when the provided Deployment // has the "Available" condition set to true func IsDeploymentAvailable(dc *appsv1.Deployment) bool {