Add subcommand init for checks and installs helmfile deps (#389)
* Add subcommand init for checks and installs helmfile deps Signed-off-by: xiaomudk <xiaomudk@gmail.com>
This commit is contained in:
parent
008e92de37
commit
6dcde20d7a
|
|
@ -132,3 +132,25 @@ jobs:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
EXTRA_HELMFILE_FLAGS: ${{ matrix.extra-helmfile-flags }}
|
EXTRA_HELMFILE_FLAGS: ${{ matrix.extra-helmfile-flags }}
|
||||||
run: make integration
|
run: make integration
|
||||||
|
e2e_tests:
|
||||||
|
needs: tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install package
|
||||||
|
run: |
|
||||||
|
sudo apt-get -y install expect
|
||||||
|
- name: Download built binaries
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: built-binaries-${{ github.run_id }}
|
||||||
|
- name: Extract tar to get built binaries
|
||||||
|
run: tar -xvf built-binaries.tar
|
||||||
|
- name: Display built binaries
|
||||||
|
run: ls -l helmfile diff-yamls yamldiff
|
||||||
|
- name: Run helmfile init
|
||||||
|
env:
|
||||||
|
TERM: xterm
|
||||||
|
run: bash test/e2e/helmfile-init/init_linux.sh
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/helmfile/helmfile/pkg/app"
|
||||||
|
"github.com/helmfile/helmfile/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewInitCmd helmfile checks and installs deps
|
||||||
|
func NewInitCmd(globalCfg *config.GlobalImpl) *cobra.Command {
|
||||||
|
options := config.NewInitOptions()
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "init",
|
||||||
|
Short: "Initialize the helmfile, includes version checking and installation of helm and plug-ins",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
initImpl := config.NewInitImpl(globalCfg, options)
|
||||||
|
err := config.NewCLIConfigImpl(initImpl.GlobalImpl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := initImpl.ValidateConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := app.New(initImpl)
|
||||||
|
return toCLIError(initImpl.GlobalImpl, a.Init(initImpl))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
f := cmd.Flags()
|
||||||
|
f.BoolVar(&options.Force, "force", false, "Do not prompt, install dependencies required by helmfile")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
@ -81,6 +81,7 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
|
NewInitCmd(globalImpl),
|
||||||
NewApplyCmd(globalImpl),
|
NewApplyCmd(globalImpl),
|
||||||
NewBuildCmd(globalImpl),
|
NewBuildCmd(globalImpl),
|
||||||
NewCacheCmd(globalImpl),
|
NewCacheCmd(globalImpl),
|
||||||
|
|
|
||||||
|
|
@ -505,6 +505,7 @@ Available Commands:
|
||||||
diff Diff releases defined in state file
|
diff Diff releases defined in state file
|
||||||
fetch Fetch charts from state file
|
fetch Fetch charts from state file
|
||||||
help Help about any command
|
help Help about any command
|
||||||
|
init Initialize the helmfile, includes version checking and installation of helm and plug-ins
|
||||||
lint Lint charts from state file (helm lint)
|
lint Lint charts from state file (helm lint)
|
||||||
list List releases defined in state file
|
list List releases defined in state file
|
||||||
repos Repos releases defined in state file
|
repos Repos releases defined in state file
|
||||||
|
|
@ -543,6 +544,11 @@ Flags:
|
||||||
Use "helmfile [command] --help" for more information about a command.
|
Use "helmfile [command] --help" for more information about a command.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### init
|
||||||
|
|
||||||
|
The `helmfile init` sub-command checks the dependencies required for helmfile operation, such as `helm`, `helm diff plugin`, `helm secrets plugin`, `helm helm-git plugin`, `helm s3 plugin`. When it does not exist or the version is too low, it can be installed automatically.
|
||||||
|
|
||||||
|
|
||||||
### sync
|
### sync
|
||||||
|
|
||||||
The `helmfile sync` sub-command sync your cluster state as described in your `helmfile`. The default helmfile is `helmfile.yaml`, but any YAML file can be passed by specifying a `--file path/to/your/yaml/file` flag.
|
The `helmfile sync` sub-command sync your cluster state as described in your `helmfile`. The default helmfile is `helmfile.yaml`, but any YAML file can be passed by specifying a `--file path/to/your/yaml/file` flag.
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,14 @@ func Init(app *App) *App {
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) Init(c InitConfigProvider) error {
|
||||||
|
runner := &helmexec.ShellRunner{
|
||||||
|
Logger: a.Logger,
|
||||||
|
}
|
||||||
|
helmfileInit := NewHelmfileInit(a.OverrideHelmBinary, c, a.Logger, runner)
|
||||||
|
return helmfileInit.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) Deps(c DepsConfigProvider) error {
|
func (a *App) Deps(c DepsConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) (_ bool, errs []error) {
|
return a.ForEachState(func(run *Run) (_ bool, errs []error) {
|
||||||
prepErr := run.withPreparedCharts("deps", state.ChartPrepareOptions{
|
prepErr := run.withPreparedCharts("deps", state.ChartPrepareOptions{
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,10 @@ type ListConfigProvider interface {
|
||||||
|
|
||||||
type CacheConfigProvider interface{}
|
type CacheConfigProvider interface{}
|
||||||
|
|
||||||
|
type InitConfigProvider interface {
|
||||||
|
Force() bool
|
||||||
|
}
|
||||||
|
|
||||||
// when enable reuse-values, reuse the last release's values and merge in any overrides values.
|
// when enable reuse-values, reuse the last release's values and merge in any overrides values.
|
||||||
type valuesControlMode interface {
|
type valuesControlMode interface {
|
||||||
ReuseValues() bool
|
ReuseValues() bool
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,238 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"helm.sh/helm/v3/pkg/cli"
|
||||||
|
|
||||||
|
"github.com/helmfile/helmfile/pkg/helmexec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
HelmRequiredVersion = "v2.10.0"
|
||||||
|
HelmRecommendedVersion = "v3.10.1"
|
||||||
|
HelmDiffRecommendedVersion = "v3.4.0"
|
||||||
|
HelmSecretsRecommendedVersion = "v4.1.1"
|
||||||
|
HelmGitRecommendedVersion = "v0.12.0"
|
||||||
|
HelmS3RecommendedVersion = "v0.14.0"
|
||||||
|
HelmInstallCommand = "https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
manuallyInstallCode = 1
|
||||||
|
windowPackageManagers = map[string]string{
|
||||||
|
"scoop": fmt.Sprintf("scoop install helm@%s", strings.TrimLeft(HelmRecommendedVersion, "v")),
|
||||||
|
"choco": fmt.Sprintf("choco install kubernetes-helm --version %s", strings.TrimLeft(HelmRecommendedVersion, "v")),
|
||||||
|
}
|
||||||
|
helmPlugins = []helmRecommendedPlugin{
|
||||||
|
{
|
||||||
|
name: "diff",
|
||||||
|
version: HelmDiffRecommendedVersion,
|
||||||
|
repo: "https://github.com/databus23/helm-diff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "secrets",
|
||||||
|
version: HelmSecretsRecommendedVersion,
|
||||||
|
repo: "https://github.com/jkroepke/helm-secrets",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "s3",
|
||||||
|
version: HelmS3RecommendedVersion,
|
||||||
|
repo: "https://github.com/hypnoglow/helm-s3.git",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "helm-git",
|
||||||
|
version: HelmGitRecommendedVersion,
|
||||||
|
repo: "https://github.com/aslafy-z/helm-git.git",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type helmRecommendedPlugin struct {
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
repo string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelmfileInit struct {
|
||||||
|
helmBinary string
|
||||||
|
configProvider InitConfigProvider
|
||||||
|
logger *zap.SugaredLogger
|
||||||
|
runner helmexec.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadfile(filepath string, url string) error {
|
||||||
|
file, err := os.Create(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = file.Close() }()
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode/100 != 2 {
|
||||||
|
return fmt.Errorf("download %s error, code: %d", url, resp.StatusCode)
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
_, err = io.Copy(file, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHelmfileInit(helmBinary string, c InitConfigProvider, logger *zap.SugaredLogger, runner helmexec.Runner) *HelmfileInit {
|
||||||
|
return &HelmfileInit{helmBinary: helmBinary, configProvider: c, logger: logger, runner: runner}
|
||||||
|
}
|
||||||
|
func (h *HelmfileInit) UpdateHelm() error {
|
||||||
|
return h.InstallHelm()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HelmfileInit) installHelmOnWindows() error {
|
||||||
|
for name, command := range windowPackageManagers {
|
||||||
|
_, err := exec.LookPath(name)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = h.WhetherContinue(fmt.Sprintf("use: '%s'", command))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = h.runner.Execute("cmd", []string{
|
||||||
|
"/c",
|
||||||
|
command,
|
||||||
|
}, nil, true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Error{msg: "windows platform, please install helm manually, installation steps: https://helm.sh/docs/intro/install/", code: &manuallyInstallCode}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HelmfileInit) InstallHelm() error {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return h.installHelmOnWindows()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.WhetherContinue(fmt.Sprintf("use: '%s'", HelmInstallCommand))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
getHelmScript, err := os.CreateTemp("", "get-helm-3.sh")
|
||||||
|
defer func() {
|
||||||
|
_ = getHelmScript.Close()
|
||||||
|
_ = os.Remove(getHelmScript.Name())
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = downloadfile(getHelmScript.Name(), HelmInstallCommand)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = h.runner.Execute("bash", []string{
|
||||||
|
getHelmScript.Name(),
|
||||||
|
"--version",
|
||||||
|
HelmRecommendedVersion,
|
||||||
|
}, nil, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.helmBinary = DefaultHelmBinary
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HelmfileInit) WhetherContinue(ask string) error {
|
||||||
|
if h.configProvider.Force() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
askYes := AskForConfirmation(ask)
|
||||||
|
if !askYes {
|
||||||
|
return &Error{msg: "cancel automatic installation, please install manually", code: &manuallyInstallCode}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HelmfileInit) CheckHelmPlugins() error {
|
||||||
|
settings := cli.New()
|
||||||
|
helm := helmexec.New(h.helmBinary, false, h.logger, "", h.runner)
|
||||||
|
for _, p := range helmPlugins {
|
||||||
|
pluginVersion, err := helmexec.GetPluginVersion(p.name, settings.PluginsDirectory)
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "not installed") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.WhetherContinue(fmt.Sprintf("The helm plugin %s is not installed, do you need to install it", p.name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = helm.AddPlugin(p.name, p.repo, p.version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pluginVersion, _ = helmexec.GetPluginVersion(p.name, settings.PluginsDirectory)
|
||||||
|
}
|
||||||
|
requiredVersion, _ := semver.NewVersion(p.version)
|
||||||
|
if pluginVersion.LessThan(requiredVersion) {
|
||||||
|
err = h.WhetherContinue(fmt.Sprintf("The helm plugin %s version is too low, do you need to update it", p.name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = helm.UpdatePlugin(p.name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HelmfileInit) CheckHelm() error {
|
||||||
|
helmExits := true
|
||||||
|
_, err := exec.LookPath(h.helmBinary)
|
||||||
|
if err != nil {
|
||||||
|
helmExits = false
|
||||||
|
}
|
||||||
|
if !helmExits {
|
||||||
|
h.logger.Info("helm not found, needs to be installed")
|
||||||
|
err = h.InstallHelm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
helmversion, err := helmexec.GetHelmVersion(h.helmBinary, h.runner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
requiredHelmVersion, _ := semver.NewVersion(HelmRequiredVersion)
|
||||||
|
if helmversion.LessThan(requiredHelmVersion) {
|
||||||
|
h.logger.Infof("helm version is too low, the current version is %s, the required version is %s", helmversion, requiredHelmVersion)
|
||||||
|
err = h.UpdateHelm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (h *HelmfileInit) Initialize() error {
|
||||||
|
err := h.CheckHelm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = h.CheckHelmPlugins()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.logger.Info("helmfile initialization completed!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDownloadfile(t *testing.T) {
|
||||||
|
var ts *httptest.Server
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
handler func(http.ResponseWriter, *http.Request)
|
||||||
|
url string
|
||||||
|
filepath string
|
||||||
|
wantContent string
|
||||||
|
wantError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "download success",
|
||||||
|
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, "helmfile")
|
||||||
|
},
|
||||||
|
wantContent: "helmfile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "download 404",
|
||||||
|
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(404)
|
||||||
|
fmt.Fprint(w, "not found")
|
||||||
|
},
|
||||||
|
wantError: "download .*? error, code: 404",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "download 500",
|
||||||
|
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
fmt.Fprint(w, "server error")
|
||||||
|
},
|
||||||
|
wantError: "download .*? error, code: 500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "download path error",
|
||||||
|
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, "helmfile")
|
||||||
|
},
|
||||||
|
filepath: "abc/down.txt",
|
||||||
|
wantError: "open .*? no such file or directory",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
downfile := path.Join(dir, "down.txt")
|
||||||
|
if c.filepath != "" {
|
||||||
|
downfile = path.Join(dir, c.filepath)
|
||||||
|
}
|
||||||
|
|
||||||
|
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.handler(w, r)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
url := ts.URL
|
||||||
|
if c.url != "" {
|
||||||
|
url = c.url
|
||||||
|
}
|
||||||
|
err := downloadfile(downfile, url)
|
||||||
|
if c.wantError != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("download got no error, want error: %v", c.wantError)
|
||||||
|
} else if matched, regexErr := regexp.MatchString(c.wantError, err.Error()); regexErr != nil || !matched {
|
||||||
|
t.Errorf("download got error: %v, want error: %v", err, c.wantError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content, err := os.ReadFile(downfile)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("read download file error: %v", err)
|
||||||
|
}
|
||||||
|
if string(content) != c.wantContent {
|
||||||
|
t.Errorf("download file content got: %v, want content: %v", string(content), c.wantContent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
// InitOptions is the options for the init command
|
||||||
|
type InitOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInitOptions creates a new InitOptions
|
||||||
|
func NewInitOptions() *InitOptions {
|
||||||
|
return &InitOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitImpl is impl for InitOptions
|
||||||
|
type InitImpl struct {
|
||||||
|
*GlobalImpl
|
||||||
|
*InitOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInitImpl creates a new InitImpl
|
||||||
|
func NewInitImpl(g *GlobalImpl, b *InitOptions) *InitImpl {
|
||||||
|
return &InitImpl{
|
||||||
|
GlobalImpl: g,
|
||||||
|
InitOptions: b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force returns the Force.
|
||||||
|
func (b *InitImpl) Force() bool {
|
||||||
|
return b.InitOptions.Force
|
||||||
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ func parseHelmVersion(versionStr string) (semver.Version, error) {
|
||||||
return *ver, nil
|
return *ver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHelmVersion(helmBinary string, runner Runner) (semver.Version, error) {
|
func GetHelmVersion(helmBinary string, runner Runner) (semver.Version, error) {
|
||||||
// Autodetect from `helm version`
|
// Autodetect from `helm version`
|
||||||
outBytes, err := runner.Execute(helmBinary, []string{"version", "--client", "--short"}, nil, false)
|
outBytes, err := runner.Execute(helmBinary, []string{"version", "--client", "--short"}, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -115,7 +115,7 @@ func redactedURL(chart string) string {
|
||||||
// nolint: golint
|
// nolint: golint
|
||||||
func New(helmBinary string, enableLiveOutput bool, logger *zap.SugaredLogger, kubeContext string, runner Runner) *execer {
|
func New(helmBinary string, enableLiveOutput bool, logger *zap.SugaredLogger, kubeContext string, runner Runner) *execer {
|
||||||
// TODO: proper error handling
|
// TODO: proper error handling
|
||||||
version, err := getHelmVersion(helmBinary, runner)
|
version, err := GetHelmVersion(helmBinary, runner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -521,6 +521,20 @@ func (helm *execer) TestRelease(context HelmContext, name string, flags ...strin
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (helm *execer) AddPlugin(name, path, version string) error {
|
||||||
|
helm.logger.Infof("Install helm plugin %v", name)
|
||||||
|
out, err := helm.exec([]string{"plugin", "install", path, "--version", version}, map[string]string{}, nil)
|
||||||
|
helm.info(out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (helm *execer) UpdatePlugin(name string) error {
|
||||||
|
helm.logger.Infof("Update helm plugin %v", name)
|
||||||
|
out, err := helm.exec([]string{"plugin", "update", name}, map[string]string{}, nil)
|
||||||
|
helm.info(out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (helm *execer) exec(args []string, env map[string]string, overrideEnableLiveOutput *bool) ([]byte, error) {
|
func (helm *execer) exec(args []string, env map[string]string, overrideEnableLiveOutput *bool) ([]byte, error) {
|
||||||
cmdargs := args
|
cmdargs := args
|
||||||
if len(helm.extra) > 0 {
|
if len(helm.extra) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# vim: set tabstop=4 shiftwidth=4
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# IMPORTS -----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# determine working directory to use to relative paths irrespective of starting directory
|
||||||
|
dir="${BASH_SOURCE%/*}"
|
||||||
|
if [[ ! -d "${dir}" ]]; then dir="${PWD}"; fi
|
||||||
|
|
||||||
|
. "${dir}/../../integration/lib/output.sh"
|
||||||
|
|
||||||
|
helmfile="./helmfile"
|
||||||
|
helm_dir="${PWD}/${dir}/.helm"
|
||||||
|
helm=`which helm`
|
||||||
|
export HELM_DATA_HOME="${helm_dir}/data"
|
||||||
|
export HELM_HOME="${HELM_DATA_HOME}"
|
||||||
|
export HELM_PLUGINS="${HELM_DATA_HOME}/plugins"
|
||||||
|
export HELM_CONFIG_HOME="${helm_dir}/config"
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
set +e
|
||||||
|
info "Deleting ${helm_dir}"
|
||||||
|
rm -rf ${helm_dir} # remove helm data so reinstalling plugins does not fail
|
||||||
|
}
|
||||||
|
|
||||||
|
function removehelm() {
|
||||||
|
[ -f $helm ] && rm -rf $helm
|
||||||
|
}
|
||||||
|
|
||||||
|
set -e
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
removehelm
|
||||||
|
|
||||||
|
expect <<EOF
|
||||||
|
set timeout -1
|
||||||
|
spawn ${helmfile} init
|
||||||
|
expect {
|
||||||
|
"*y/n" {send "y\r";exp_continue}
|
||||||
|
eof
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
helm plugin ls | grep diff || fail "helmfile init run fail"
|
||||||
|
|
||||||
|
all_tests_passed
|
||||||
Loading…
Reference in New Issue