Skip to content
This repository has been archived by the owner on Jun 29, 2022. It is now read-only.

Commit

Permalink
Merge pull request #268 from kinvolk/surajssd/uninstall-component
Browse files Browse the repository at this point in the history
Add support for `lokoctl component delete`
  • Loading branch information
surajssd authored Apr 23, 2020
2 parents 5034672 + 7b2f936 commit a6c6db6
Show file tree
Hide file tree
Showing 23 changed files with 259 additions and 5 deletions.
184 changes: 184 additions & 0 deletions cli/cmd/component-delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// Copyright 2020 The Lokomotive Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"fmt"
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/kinvolk/lokomotive/pkg/components"
"github.com/kinvolk/lokomotive/pkg/components/util"
"github.com/kinvolk/lokomotive/pkg/k8sutil"
)

var componentDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete an installed component",
Long: `Delete a component.
When run with no arguments, all components listed in the configuration are deleted.`,
Run: runDelete,
}

var deleteNamespace bool

// nolint:gochecknoinits
func init() {
componentCmd.AddCommand(componentDeleteCmd)
pf := componentDeleteCmd.PersistentFlags()
pf.BoolVarP(&deleteNamespace, "delete-namespace", "", false, "Delete namespace with component.")
}

func runDelete(cmd *cobra.Command, args []string) {
contextLogger := log.WithFields(log.Fields{
"command": "lokoctl component delete",
"args": args,
})

lokoCfg, diags := getLokoConfig()
if len(diags) > 0 {
contextLogger.Fatal(diags)
}

componentsToDelete := make([]string, len(args))
copy(componentsToDelete, args)

if len(args) == 0 {
componentsToDelete = make([]string, len(lokoCfg.RootConfig.Components))

for i, component := range lokoCfg.RootConfig.Components {
componentsToDelete[i] = component.Name
}
}

componentsObjects := make([]components.Component, len(componentsToDelete))

for i, componentName := range componentsToDelete {
compObj, err := components.Get(componentName)
if err != nil {
contextLogger.Fatal(err)
}

componentsObjects[i] = compObj
}

if !askForConfirmation(
fmt.Sprintf(
"The following components will be deleted:\n\t%s\n\nAre you sure you want to proceed?",
strings.Join(componentsToDelete, "\n\t"),
),
) {
contextLogger.Info("Components deletion cancelled.")
return
}

kubeconfig, err := getKubeconfig()
if err != nil {
contextLogger.Fatalf("Error in finding kubeconfig file: %s", err)
}

if err := deleteComponents(kubeconfig, componentsObjects...); err != nil {
contextLogger.Fatal(err)
}
}

func deleteComponents(kubeconfig string, componentObjects ...components.Component) error {
for _, compObj := range componentObjects {
fmt.Printf("Deleting component '%s'...\n", compObj.Metadata().Name)

if err := deleteHelmRelease(compObj, kubeconfig, deleteNamespace); err != nil {
return err
}

fmt.Printf("Successfully deleted component %q!\n", compObj.Metadata().Name)
}

// Add a line to distinguish between info logs and errors, if any.
fmt.Println()

return nil
}

// deleteComponent deletes a component.
func deleteHelmRelease(c components.Component, kubeconfig string, deleteNSBool bool) error {
name := c.Metadata().Name
if name == "" {
// This should never fail in real user usage, if this does that means the component was not
// created with all the needed information.
panic(fmt.Errorf("component name is empty"))
}

ns := c.Metadata().Namespace
if ns == "" {
// This should never fail in real user usage, if this does that means the component was not
// created with all the needed information.
panic(fmt.Errorf("component %s namespace is empty", name))
}

cfg, err := util.HelmActionConfig(ns, kubeconfig)
if err != nil {
return fmt.Errorf("failed preparing helm client: %w", err)
}

history := action.NewHistory(cfg)
// Check if the component's release exists. If it does only then proceed to delete.
//
// Note: It is assumed that this call will return error only when the release does not exist.
// The error check is ignored to make `lokoctl component delete ..` idempotent.
// We rely on the fact that the 'component name' == 'release name'. Since component's name is
// hardcoded and unlikely to change release name won't change as well. And they will be
// consistent if installed by lokoctl. So it is highly unlikely that following call will return
// any other error than "release not found".
if _, err := history.Run(name); err == nil {
uninstall := action.NewUninstall(cfg)

// Ignore the err when we have deleted the release already or it does not exist for some reason.
if _, err := uninstall.Run(name); err != nil {
return err
}
}

if deleteNSBool {
if err := deleteNS(ns, kubeconfig); err != nil {
return err
}
}

return nil
}

func deleteNS(ns string, kubeconfig string) error {
cs, err := k8sutil.NewClientset(kubeconfig)
if err != nil {
return err
}

// Delete the manually created namespace which was not created by helm.
if err = cs.CoreV1().Namespaces().Delete(ns, &metav1.DeleteOptions{}); err != nil {
// Ignore error when the namespace does not exist.
if errors.IsNotFound(err) {
return nil
}

return err
}

return nil
}
1 change: 1 addition & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package cmd has code for all the subcommands of lokoctl.
package cmd

import (
Expand Down
12 changes: 7 additions & 5 deletions cli/cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ package cmd

import (
"fmt"
"os"
"path/filepath"

"github.com/hashicorp/hcl/v2"
"github.com/kinvolk/lokomotive/pkg/backend"
"github.com/kinvolk/lokomotive/pkg/config"
"github.com/kinvolk/lokomotive/pkg/platform"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
"path/filepath"

"github.com/kinvolk/lokomotive/pkg/backend"
"github.com/kinvolk/lokomotive/pkg/config"
"github.com/kinvolk/lokomotive/pkg/platform"
)

// getConfiguredBackend loads a backend from the given configuration file.
Expand Down
1 change: 1 addition & 0 deletions docs/cli/lokoctl_component.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Manage components

* [lokoctl](lokoctl.md) - Manage Lokomotive clusters
* [lokoctl component apply](lokoctl_component_apply.md) - Deploy or update a component
* [lokoctl component delete](lokoctl_component_delete.md) - Delete an installed component
* [lokoctl component list](lokoctl_component_list.md) - List all available components
* [lokoctl component render-manifest](lokoctl_component_render-manifest.md) - Print the manifests for a component

32 changes: 32 additions & 0 deletions docs/cli/lokoctl_component_delete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## lokoctl component delete

Delete an installed component

### Synopsis

Delete a component.
When run with no arguments, all components listed in the configuration are deleted.

```
lokoctl component delete [flags]
```

### Options

```
--delete-namespace Delete namespace with component.
-h, --help help for delete
```

### Options inherited from parent commands

```
--kubeconfig string Path to kubeconfig file, taken from the asset dir if not given, and finally falls back to ~/.kube/config
--lokocfg string Path to lokocfg directory or file (default "./")
--lokocfg-vars string Path to lokocfg.vars file (default "./lokocfg.vars")
```

### SEE ALSO

* [lokoctl component](lokoctl_component.md) - Manage components

1 change: 1 addition & 0 deletions pkg/components/cert-manager/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: c.Namespace,
Helm: components.HelmMetadata{
// Cert-manager registers admission webhooks, so we should wait for the webhook to
Expand Down
1 change: 1 addition & 0 deletions pkg/components/cluster-autoscaler/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: c.Namespace,
}
}
1 change: 1 addition & 0 deletions pkg/components/contour/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: "projectcontour",
}
}
1 change: 1 addition & 0 deletions pkg/components/dex/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ func createSecretManifest(path string) (string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: name,
}
}
1 change: 1 addition & 0 deletions pkg/components/external-dns/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: c.Namespace,
}
}
1 change: 1 addition & 0 deletions pkg/components/flatcar-linux-update-operator/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: componentName,
Namespace: "reboot-coordinator",
}
}
1 change: 1 addition & 0 deletions pkg/components/gangway/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: name,
}
}
1 change: 1 addition & 0 deletions pkg/components/httpbin/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: name,
}
}
1 change: 1 addition & 0 deletions pkg/components/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package components
// Metadata is a struct which represents basic information about the component.
// It may contain information like name, version, dependencies, namespace, source etc.
type Metadata struct {
Name string
Namespace string
Helm HelmMetadata
}
Expand Down
1 change: 1 addition & 0 deletions pkg/components/metallb/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: "metallb-system",
}
}
1 change: 1 addition & 0 deletions pkg/components/metrics-server/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: c.Namespace,
}
}
1 change: 1 addition & 0 deletions pkg/components/openebs-operator/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: "openebs",
}
}
1 change: 1 addition & 0 deletions pkg/components/openebs-storage-class/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
// Return the same namespace which the openebs-operator component is using.
Namespace: "openebs",
}
Expand Down
1 change: 1 addition & 0 deletions pkg/components/prometheus-operator/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: c.Namespace,
Helm: components.HelmMetadata{
// Prometheus-operator registers admission webhooks, so we should wait for the webhook to
Expand Down
1 change: 1 addition & 0 deletions pkg/components/rook-ceph/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: c.Namespace,
}
}
1 change: 1 addition & 0 deletions pkg/components/rook/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func (c *component) RenderManifests() (map[string]string, error) {

func (c *component) Metadata() components.Metadata {
return components.Metadata{
Name: name,
Namespace: c.Namespace,
}
}
Loading

0 comments on commit a6c6db6

Please sign in to comment.