Skip to content
This repository has been archived by the owner on Jul 17, 2024. It is now read-only.

Commit

Permalink
Add clean up capability (#33)
Browse files Browse the repository at this point in the history
* Add clean up capability

It cleans up Helm v2:
- Configuration (Helm home)
- Release data
- Tiller deployment

Signed-off-by: Martin Hickey <[email protected]>

* Update after review comments

#33 (review)
#33 (comment)

Signed-off-by: Martin Hickey <[email protected]>
  • Loading branch information
hickeyma authored Sep 18, 2019
1 parent f428eda commit 802b44a
Show file tree
Hide file tree
Showing 12 changed files with 579 additions and 221 deletions.
45 changes: 39 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

![diagram](./helm-2to3.png)

**Helm plugin which migrates Helm v2 configuration and releases in-place to Helm v3**
**Helm plugin which migrates and cleans up Helm v2 configuration and releases in-place to Helm v3**

## Usage

Expand Down Expand Up @@ -55,15 +55,48 @@ $ helm 2to3 convert [flags] RELEASE
Flags:

```
--dry-run simulate a convert
-h, --help help for convert
--delete-v2-releases v2 releases are deleted after migration. By default, the v2 releases are retained
-l, --label string label to select tiller resources by (default "OWNER=TILLER")
--dry-run simulate a convert
-h, --help help for convert
--delete-v2-releases v2 releases are deleted after migration. By default, the v2 releases are retained
-l, --label string label to select tiller resources by (default "OWNER=TILLER")
-s, --release-storage string v2 release storage type/object. It can be 'secrets' or 'configmaps'. This is only used with the 'tiller-out-cluster' flag (default "secrets")
-t, --tiller-ns string namespace of Tiller (default "kube-system")
-t, --tiller-ns string namespace of Tiller (default "kube-system")
--tiller-out-cluster when Tiller is not running in the cluster e.g. Tillerless
```

### Clean up Helm v2 data

Clean up Helm v2 configuration, release data and Tiller deployment:

```console
$ helm 2to3 cleanup [flags]

Flags:
--dry-run simulate a command
-h, --help help for cleanup
-l, --label string label to select tiller resources by (default "OWNER=TILLER")
-s, --release-storage string v2 release storage type/object. It can be 'secrets' or 'configmaps'. This is only used with the 'tiller-out-cluster' flag (default "secrets")
-t, --tiller-ns string namespace of Tiller (default "kube-system")
--tiller-out-cluster when Tiller is not running in the cluster e.g. Tillerless
```

It will clean:
- Configuration (Helm home directory)
- v2 release data
- Tiller deployment

For cleanup it uses the default Helm v2 home folder.
To override this folder you need to set the environment variable `HELM_V2_HOME`:

```console
$ export HELM_V2_HOME=$PWD/.helm2
$ helm 2to3 cleanup
```

*Warning:* The `cleanup` command will remove the Helm v2 Configuration, Release Data and Tiller Deployment.
It cleans up all releases managed by Helm v2. It will not be possible to restore them if you haven't made a backup of the releases.
Helm v2 will not be usable afterwards.

## Install

Based on the version in `plugin.yaml`, release binary will be downloaded from GitHub:
Expand Down
130 changes: 130 additions & 0 deletions cmd/cleanup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
Copyright The Helm 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 (
"bufio"
"fmt"
"io"
"os"
"strings"

"github.com/pkg/errors"
"github.com/spf13/cobra"

"helm-2to3/pkg/v2"
)

/*var (
settings *EnvSettings
)*/

func newCleanupCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "cleanup",
Short: "cleanup Helm v2 configuration, release data and Tiller deployment",
Args: func(cmd *cobra.Command, args []string) error {
return nil
},
RunE: runCleanup,
}

flags := cmd.Flags()
settings.AddFlags(flags)

return cmd
}

func runCleanup(cmd *cobra.Command, args []string) error {
return Cleanup()
}

// Cleanup will delete all release data for in specified namespace and owner label. It will remove
// the Tiller server deployed as per namespace and owner label. It is also delete the Helm gv2 home directory
// which contains the Helm configuration. Helm v2 will be unusable after this operation.
func Cleanup() error {
if settings.dryRun {
fmt.Printf("NOTE: This is in dry-run mode, the following actions will not be executed.\n")
fmt.Printf("Run without --dry-run to take the actions described below:\n\n")
}

fmt.Printf("WARNING: Helm v2 Configuration, Release Data and Tiller Deployment will be removed.\n")
fmt.Printf("This will clean up all releases managed by Helm v2. It will not be possible to restore them if you haven't made a backup of the releases.\n")
fmt.Printf("Helm v2 will not be usable afterwards.\n\n")

doCleanup, err := askConfirmation()
if err != nil {
return err
}
if !doCleanup {
return fmt.Errorf("Cleanup will not proceed as the user didn't answer (Y|y) in order to continue")
}

fmt.Printf("\nHelm v2 data will be cleaned up.\n")

fmt.Printf("[Helm 2] Releases will be deleted.\n")
retrieveOptions := v2.RetrieveOptions{
ReleaseName: "",
TillerNamespace: settings.tillerNamespace,
TillerLabel: settings.label,
TillerOutCluster: settings.tillerOutCluster,
StorageType: settings.releaseStorage,
}
err = v2.DeleteAllReleaseVersions(retrieveOptions, settings.dryRun)
if err != nil {
return err
}
if !settings.dryRun {
fmt.Printf("[Helm 2] Releases deleted.\n")
}

if !settings.tillerOutCluster {
fmt.Printf("[Helm 2] Tiller service in \"%s\" namespace will be removed.\n", settings.tillerNamespace)
err = v2.RemoveTiller(settings.tillerNamespace, settings.dryRun)
if err != nil {
return err
}
if !settings.dryRun {
fmt.Printf("[Helm 2] Tiller service in \"%s\" namespace removed.\n", settings.tillerNamespace)
}
}

err = v2.RemoveHomeFolder(settings.dryRun)
if err != nil {
return err
}

if !settings.dryRun {
fmt.Printf("Helm v2 data was cleaned up successfully.\n")
}
return nil
}

func askConfirmation() (bool, error) {
fmt.Printf("[Cleanup/confirm] Are you sure you want to cleanup Helm v2 data? [y/N]: ")

scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if err := scanner.Err(); err != nil {
return false, errors.Wrap(err, "couldn't read from standard input")
}
answer := scanner.Text()
if strings.ToLower(answer) == "y" || strings.ToLower(answer) == "yes" {
return true, nil
}
return false, nil
}
48 changes: 18 additions & 30 deletions cmd/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,7 @@ import (
)

var (
tillerNamespace string
label string
releaseStorage string
dryRun bool
deletev2Releases bool
tillerOutCluster bool
)

func newConvertCmd(out io.Writer) *cobra.Command {
Expand All @@ -53,20 +48,17 @@ func newConvertCmd(out io.Writer) *cobra.Command {
}

flags := cmd.Flags()
flags.StringVarP(&tillerNamespace, "tiller-ns", "t", "kube-system", "namespace of Tiller")
flags.StringVarP(&label, "label", "l", "OWNER=TILLER", "label to select tiller resources by")
flags.BoolVar(&dryRun, "dry-run", false, "simulate a convert")
settings.AddFlags(flags)

flags.BoolVar(&deletev2Releases, "delete-v2-releases", false, "v2 releases are deleted after migration. By default, the v2 releases are retained")
flags.BoolVar(&tillerOutCluster, "tiller-out-cluster", false, "when Tiller is not running in the cluster e.g. Tillerless")
flags.StringVarP(&releaseStorage, "release-storage", "s", "secrets", "v2 release storage type/object. It can be 'secrets' or 'configmaps'. This is only used with the 'tiller-out-cluster' flag")

return cmd

}

func run(cmd *cobra.Command, args []string) error {
releaseName := args[0]
if releaseStorage != "configmaps" && releaseStorage != "secrets" {
if settings.releaseStorage != "configmaps" && settings.releaseStorage != "secrets" {
return errors.New("release-storage flag needs to be 'configmaps' or 'secrets'")
}
return Convert(releaseName)
Expand All @@ -75,9 +67,9 @@ func run(cmd *cobra.Command, args []string) error {
// Convert converts Helm 2 release into Helm 3 release. It maps the Helm v2 release versions
// of the release into Helm v3 equivalent and stores the release versions. The underlying Kubernetes resources
// are untouched. Note: The namespaces of each release version need to exist in the Kubernetes cluster.
// The Helm 2 release is retained by default, unless the '--deletev2Releases' flag is set.
// The Helm 2 release is retained by default, unless the '--delete-v2-releases' flag is set.
func Convert(releaseName string) error {
if dryRun {
if settings.dryRun {
fmt.Printf("NOTE: This is in dry-run mode, the following actions will not be executed.\n")
fmt.Printf("Run without --dry-run to take the actions described below:\n\n")
}
Expand All @@ -88,10 +80,10 @@ func Convert(releaseName string) error {

retrieveOptions := v2.RetrieveOptions{
ReleaseName: releaseName,
TillerNamespace: tillerNamespace,
TillerLabel: label,
TillerOutCluster: tillerOutCluster,
StorageType: releaseStorage,
TillerNamespace: settings.tillerNamespace,
TillerLabel: settings.label,
TillerOutCluster: settings.tillerOutCluster,
StorageType: settings.releaseStorage,
}
v2Releases, err := v2.GetReleaseVersions(retrieveOptions)
if err != nil {
Expand All @@ -101,36 +93,36 @@ func Convert(releaseName string) error {
versions := []int32{}
for i := len(v2Releases) - 1; i >= 0; i-- {
v2Release := v2Releases[i]
version := v2Release.Version
fmt.Printf("[Helm 3] ReleaseVersion \"%s\" will be created.\n", getReleaseVersionName(releaseName, version))
if !dryRun {
relVerName := v2.GetReleaseVersionName(releaseName, v2Release.Version)
fmt.Printf("[Helm 3] ReleaseVersion \"%s\" will be created.\n", relVerName)
if !settings.dryRun {
if err := createV3ReleaseVersion(v2Release); err != nil {
return err
}
fmt.Printf("[Helm 3] ReleaseVersion \"%s\" created.\n", getReleaseVersionName(releaseName, version))
fmt.Printf("[Helm 3] ReleaseVersion \"%s\" created.\n", relVerName)
}
versions = append(versions, version)
versions = append(versions, v2Release.Version)
}
if !dryRun {
if !settings.dryRun {
fmt.Printf("[Helm 3] Release \"%s\" created.\n", releaseName)
}

if deletev2Releases {
fmt.Printf("[Helm 2] Release \"%s\" will be deleted.\n", releaseName)
deleteOptions := v2.DeleteOptions{
DryRun: dryRun,
DryRun: settings.dryRun,
Versions: versions,
}
if err := v2.DeleteReleaseVersions(retrieveOptions, deleteOptions); err != nil {
return err
}
if !dryRun {
if !settings.dryRun {
fmt.Printf("[Helm 2] Release \"%s\" deleted.\n", releaseName)

fmt.Printf("Release \"%s\" was converted successfully from Helm 2 to Helm 3.\n", releaseName)
}
} else {
if !dryRun {
if !settings.dryRun {
fmt.Printf("Release \"%s\" was converted successfully from Helm 2 to Helm 3. Note: the v2 releases still remain and should be removed to avoid conflicts with the migrated v3 releases.\n", releaseName)
}
}
Expand All @@ -145,7 +137,3 @@ func createV3ReleaseVersion(v2Release *v2rel.Release) error {
}
return v3.StoreRelease(v3Release)
}

func getReleaseVersionName(releaseName string, releaseVersion int32) string {
return fmt.Sprintf("%s.v%d", releaseName, releaseVersion)
}
43 changes: 43 additions & 0 deletions cmd/environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Copyright The Helm 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 (
"github.com/spf13/pflag"
)

type EnvSettings struct {
tillerNamespace string
releaseStorage string
label string
dryRun bool
tillerOutCluster bool
}

func New() *EnvSettings {
envSettings := EnvSettings{}
return &envSettings
}

// AddFlags binds flags to the given flagset.
func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringVarP(&s.tillerNamespace, "tiller-ns", "t", "kube-system", "namespace of Tiller")
fs.BoolVar(&s.dryRun, "dry-run", false, "simulate a command")
fs.StringVarP(&s.label, "label", "l", "OWNER=TILLER", "label to select tiller resources by")
fs.BoolVar(&s.tillerOutCluster, "tiller-out-cluster", false, "when Tiller is not running in the cluster e.g. Tillerless")
fs.StringVarP(&s.releaseStorage, "release-storage", "s", "secrets", "v2 release storage type/object. It can be 'secrets' or 'configmaps'. This is only used with the 'tiller-out-cluster' flag")
}
Loading

0 comments on commit 802b44a

Please sign in to comment.