Skip to content

Commit

Permalink
Add a way to force plugin mode (helm version)
Browse files Browse the repository at this point in the history
  • Loading branch information
hypnoglow committed Jan 15, 2020
1 parent 95e8698 commit fb23362
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 6 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [0.9.1] - 2020-01-15

### Added

- `helm version` now has optional flag `--mode` that additionally prints the mode (Helm version) in which the plugin operates,
either v2 or v3.
- Added `HELM_S3_MODE` that can be used to forcefully change the mode (Helm version), in case when the plugin does not detect Helm version properly.

### Changed

- Changed the way the plugin detects Helm version. Now it parses `helm version` output instead of checking `helm env`
command existence.

## [0.9.0] - 2019-12-27

### Added
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,27 @@ for a CI that builds and pushes charts to your repository.
}
```

### Helm version mode

The plugin is able to detect if you are using Helm v2 or v3 automatically. If, for some reason, the plugin does not
detect Helm version properly, you can set `HELM_S3_MODE` environment variable to value `2` or `3` to force the mode.

Example:

# We have Helm version 3:
$ helm version --short
v3.0.2+g19e47ee

# For some reason, the plugin detects Helm version badly:
$ helm s3 version --mode
helm-s3 plugin version: 0.9.1
Helm version mode: v2

# Force the plugin to operate in v3 mode:
$ HELM_S3_MODE=3 helm s3 version --mode
helm-s3 plugin version: 0.9.1
Helm version mode: v3

## Usage

*Note: some Helm CLI commands in v3 are incompatible with v2. Example commands below are provided for v2. For commands
Expand Down
16 changes: 14 additions & 2 deletions cmd/helms3/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"os"
"time"

"github.com/hypnoglow/helm-s3/internal/helmutil"
"gopkg.in/alecthomas/kingpin.v2"

"github.com/hypnoglow/helm-s3/internal/helmutil"
)

var (
Expand Down Expand Up @@ -64,7 +65,9 @@ func main() {
}

cli := kingpin.New("helm s3", "")
cli.Command(actionVersion, "Show plugin version.")
versionCmd := cli.Command(actionVersion, "Show plugin version.")
versionMode := versionCmd.Flag("mode", "Also print Helm version mode in which the plugin operates, either v2 or v3. In case when the plugin does not detect Helm version properly, you can forcefully change the mode: set HELM_S3_MODE environment variable to either 2 or 3.").
Bool()

timeout := cli.Flag("timeout", helpFlagTimeout).
Default(defaultTimeoutString).
Expand Down Expand Up @@ -123,6 +126,15 @@ func main() {
var act Action
switch action {
case actionVersion:
if *versionMode {
mode := "v2"
if helmutil.IsHelm3() {
mode = "v3"
}
fmt.Printf("helm-s3 plugin version: %s\n", version)
fmt.Printf("Helm version mode: %s\n", mode)
return
}
fmt.Print(version)
return

Expand Down
32 changes: 32 additions & 0 deletions internal/helmutil/testing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,38 @@ func mockEnv(t *testing.T, name, value string) func() {
}
}

func mockEnvs(t *testing.T, nameValue ...string) func() {
if len(nameValue)%2 != 0 {
t.Fatal("mockEnvs: must have even number of arguments")
}

tearDowns := make([]func(), 0, len(nameValue)/2)
for i := 0; i < len(nameValue); i++ {
if i%2 == 1 {
continue
}

name := nameValue[i]
value := nameValue[i+1]

old := os.Getenv(name)

err := os.Setenv(name, value)
require.NoError(t, err)

tearDowns = append(tearDowns, func() {
err := os.Setenv(name, old)
require.NoError(t, err)
})
}

return func() {
for _, td := range tearDowns {
td()
}
}
}

func assertError(t *testing.T, err error, expected bool) {
if expected {
assert.Error(t, err)
Expand Down
26 changes: 22 additions & 4 deletions internal/helmutil/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@ package helmutil
import (
"os"
"os/exec"
"strings"
)

// IsHelm3 returns true if helm is version 3+.
func IsHelm3() bool {
// Support explicit mode configuration via environment variable.
switch strings.TrimSpace(os.Getenv("HELM_S3_MODE")) {
case "2", "v2":
return false
case "3", "v3":
return true
default:
// continue to other detection methods.
}

if os.Getenv("TILLER_HOST") != "" {
return false
}
Expand All @@ -17,12 +28,19 @@ func IsHelm3() bool {
// helm3Detected returns true if helm is v3.
var helm3Detected func() bool

func helmEnvCommand() bool {
cmd := exec.Command("helm", "env")
return cmd.Run() == nil
func helmVersionCommand() bool {
cmd := exec.Command("helm", "version", "--short", "--client")
out, err := cmd.CombinedOutput()
if err != nil {
// Should not happen in normal cases (when helm is properly installed).
// Anyway, for now fallback to v2 for backward compatibility for helm-s3 users that are still on v2.
return false
}

return strings.HasPrefix(string(out), "v3.")
}

// setupHelmVersionDetection sets up the command used to detect helm version.
func setupHelmVersionDetection() {
helm3Detected = helmEnvCommand
helm3Detected = helmVersionCommand
}
48 changes: 48 additions & 0 deletions internal/helmutil/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,54 @@ func TestIsHelm3(t *testing.T) {
setup func() func()
isHelm3 bool
}{
"HELM_S3_MODE is set to 2": {
setup: func() func() {
return mockEnv(t, "HELM_S3_MODE", "2")
},
isHelm3: false,
},
"HELM_S3_MODE is set to 3": {
setup: func() func() {
return mockEnv(t, "HELM_S3_MODE", "v3")
},
isHelm3: true,
},
"HELM_S3_MODE is set to any other value, TILLER_HOST is empty, helm command detects v2": {
setup: func() func() {
helm3Detected = func() bool {
return false
}
return mockEnvs(t,
"HELM_S3_MODE", "abc",
"TILLER_HOST", "",
)
},
isHelm3: false,
},
"HELM_S3_MODE is empty, TILLER_HOST is empty, helm command detects v2": {
setup: func() func() {
helm3Detected = func() bool {
return false
}
return mockEnvs(t,
"HELM_S3_MODE", "",
"TILLER_HOST", "",
)
},
isHelm3: false,
},
"HELM_S3_MODE is empty, TILLER_HOST is empty, helm command detects v3": {
setup: func() func() {
helm3Detected = func() bool {
return true
}
return mockEnvs(t,
"HELM_S3_MODE", "",
"TILLER_HOST", "",
)
},
isHelm3: true,
},
"TILLER_HOST is set": {
setup: func() func() {
return mockEnv(t, "TILLER_HOST", "1")
Expand Down

0 comments on commit fb23362

Please sign in to comment.