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