You've already forked sap-jenkins-library
							
							
				mirror of
				https://github.com/SAP/jenkins-library.git
				synced 2025-10-30 23:57:50 +02:00 
			
		
		
		
	feat: add step for Helm execution (#3419)
* Implement helm step * Create kubernetes package * Refactoring helm.go * Add package, test commands * Add test for helm package * Add tests for helm.go * Add tests for helm.go * Add tests for utils.go * Add tests for helmExecute.go * small fix * Add helm lint * small fix * small fix * Fix according to comments * Fix test * small fix * Add helm add function * Changes according to new comments * Add helm push * Add unit tests * Add tests for helmExecute * Add small fix * small fix * small fix * Move DeployUtilsBundle from kubernetesDeploy to kubernetes package * small fix * small fix * Add unit-tests * Fix * Update resources/metadata/helmExecute.yaml * Update resources/metadata/helmExecute.yaml * Add helm chart server parameterization * small fix * small fix Co-authored-by: “Vitalii <“vitalii.sidorov@sap.com”> Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										78
									
								
								cmd/helmExecute.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								cmd/helmExecute.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/kubernetes" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| ) | ||||
|  | ||||
| func helmExecute(config helmExecuteOptions, telemetryData *telemetry.CustomData) { | ||||
| 	utils := kubernetes.NewDeployUtilsBundle() | ||||
|  | ||||
| 	helmConfig := kubernetes.HelmExecuteOptions{ | ||||
| 		ChartPath:             config.ChartPath, | ||||
| 		DeploymentName:        config.DeploymentName, | ||||
| 		ContainerRegistryURL:  config.ContainerRegistryURL, | ||||
| 		Image:                 config.Image, | ||||
| 		ContainerImageName:    config.ContainerImageName, | ||||
| 		ContainerImageTag:     config.ContainerImageTag, | ||||
| 		Namespace:             config.Namespace, | ||||
| 		KubeContext:           config.KubeContext, | ||||
| 		KubeConfig:            config.KubeConfig, | ||||
| 		HelmDeployWaitSeconds: config.HelmDeployWaitSeconds, | ||||
| 		DryRun:                config.DryRun, | ||||
| 		PackageVersion:        config.PackageVersion, | ||||
| 		AppVersion:            config.AppVersion, | ||||
| 		DependencyUpdate:      config.DependencyUpdate, | ||||
| 		HelmValues:            config.HelmValues, | ||||
| 		FilterTest:            config.FilterTest, | ||||
| 		DumpLogs:              config.DumpLogs, | ||||
| 		ChartRepo:             config.ChartRepo, | ||||
| 		HelmRegistryUser:      config.HelmRegistryUser, | ||||
| 		HelmChartServer:       config.HelmChartServer, | ||||
| 	} | ||||
|  | ||||
| 	helmExecutor := kubernetes.NewHelmExecutor(helmConfig, utils, GeneralConfig.Verbose, log.Writer()) | ||||
|  | ||||
| 	// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end | ||||
| 	if err := runHelmExecute(config.HelmCommand, helmExecutor); err != nil { | ||||
| 		log.Entry().WithError(err).Fatalf("step execution failed: %v", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runHelmExecute(helmCommand string, helmExecutor kubernetes.HelmExecutor) error { | ||||
| 	switch helmCommand { | ||||
| 	case "upgrade": | ||||
| 		if err := helmExecutor.RunHelmUpgrade(); err != nil { | ||||
| 			return fmt.Errorf("failed to execute upgrade: %v", err) | ||||
| 		} | ||||
| 	case "lint": | ||||
| 		if err := helmExecutor.RunHelmLint(); err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm lint: %v", err) | ||||
| 		} | ||||
| 	case "install": | ||||
| 		if err := helmExecutor.RunHelmInstall(); err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm install: %v", err) | ||||
| 		} | ||||
| 	case "test": | ||||
| 		if err := helmExecutor.RunHelmTest(); err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm test: %v", err) | ||||
| 		} | ||||
| 	case "uninstall": | ||||
| 		if err := helmExecutor.RunHelmUninstall(); err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm uninstall: %v", err) | ||||
| 		} | ||||
| 	case "package": | ||||
| 		if err := helmExecutor.RunHelmPackage(); err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm package: %v", err) | ||||
| 		} | ||||
| 	case "push": | ||||
| 		if err := helmExecutor.RunHelmPush(); err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm push: %v", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										525
									
								
								cmd/helmExecute_generated.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										525
									
								
								cmd/helmExecute_generated.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,525 @@ | ||||
| // Code generated by piper's step-generator. DO NOT EDIT. | ||||
|  | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/config" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/splunk" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"github.com/SAP/jenkins-library/pkg/validation" | ||||
| 	"github.com/spf13/cobra" | ||||
| ) | ||||
|  | ||||
| type helmExecuteOptions struct { | ||||
| 	AdditionalParameters      []string `json:"additionalParameters,omitempty"` | ||||
| 	ChartPath                 string   `json:"chartPath,omitempty"` | ||||
| 	ContainerRegistryPassword string   `json:"containerRegistryPassword,omitempty"` | ||||
| 	ContainerImageName        string   `json:"containerImageName,omitempty"` | ||||
| 	ContainerImageTag         string   `json:"containerImageTag,omitempty"` | ||||
| 	ContainerRegistryURL      string   `json:"containerRegistryUrl,omitempty"` | ||||
| 	ContainerRegistryUser     string   `json:"containerRegistryUser,omitempty"` | ||||
| 	ContainerRegistrySecret   string   `json:"containerRegistrySecret,omitempty"` | ||||
| 	DeploymentName            string   `json:"deploymentName,omitempty"` | ||||
| 	HelmDeployWaitSeconds     int      `json:"helmDeployWaitSeconds,omitempty"` | ||||
| 	HelmValues                []string `json:"helmValues,omitempty"` | ||||
| 	Image                     string   `json:"image,omitempty"` | ||||
| 	KeepFailedDeployments     bool     `json:"keepFailedDeployments,omitempty"` | ||||
| 	KubeConfig                string   `json:"kubeConfig,omitempty"` | ||||
| 	KubeContext               string   `json:"kubeContext,omitempty"` | ||||
| 	Namespace                 string   `json:"namespace,omitempty"` | ||||
| 	DockerConfigJSON          string   `json:"dockerConfigJSON,omitempty"` | ||||
| 	HelmCommand               string   `json:"helmCommand,omitempty" validate:"possible-values=upgrade install lint test uninstall package push"` | ||||
| 	DryRun                    bool     `json:"dryRun,omitempty"` | ||||
| 	PackageVersion            string   `json:"packageVersion,omitempty"` | ||||
| 	AppVersion                string   `json:"appVersion,omitempty"` | ||||
| 	DependencyUpdate          bool     `json:"dependencyUpdate,omitempty"` | ||||
| 	DumpLogs                  bool     `json:"dumpLogs,omitempty"` | ||||
| 	FilterTest                string   `json:"filterTest,omitempty"` | ||||
| 	ChartRepo                 string   `json:"chartRepo,omitempty"` | ||||
| 	HelmRegistryUser          string   `json:"helmRegistryUser,omitempty"` | ||||
| 	HelmChartServer           string   `json:"helmChartServer,omitempty"` | ||||
| } | ||||
|  | ||||
| // HelmExecuteCommand Executes helm3 functionality as the package manager for Kubernetes. | ||||
| func HelmExecuteCommand() *cobra.Command { | ||||
| 	const STEP_NAME = "helmExecute" | ||||
|  | ||||
| 	metadata := helmExecuteMetadata() | ||||
| 	var stepConfig helmExecuteOptions | ||||
| 	var startTime time.Time | ||||
| 	var logCollector *log.CollectorHook | ||||
| 	var splunkClient *splunk.Splunk | ||||
| 	telemetryClient := &telemetry.Telemetry{} | ||||
|  | ||||
| 	var createHelmExecuteCmd = &cobra.Command{ | ||||
| 		Use:   STEP_NAME, | ||||
| 		Short: "Executes helm3 functionality as the package manager for Kubernetes.", | ||||
| 		Long: `Alpha version: please expect incompatible changes | ||||
|  | ||||
| Executes helm functionality as the package manager for Kubernetes. | ||||
|  | ||||
| * [Helm](https://helm.sh/)  is the package manager for Kubernetes. | ||||
| * [Helm documentation https://helm.sh/docs/intro/using_helm/ and best practies https://helm.sh/docs/chart_best_practices/conventions/] | ||||
| * [Helm Charts] (https://artifacthub.io/) | ||||
| ` + "`" + `` + "`" + `` + "`" + ` | ||||
| Available Commands: | ||||
|   install     install a chart | ||||
|   lint        examine a chart for possible issues | ||||
|   package     package a chart directory into a chart archive | ||||
|   repo        add, list, remove, update, and index chart repositories | ||||
|   test        run tests for a release | ||||
|   uninstall   uninstall a release | ||||
|   upgrade     upgrade a release | ||||
|   verify      verify that a chart at the given path has been signed and is valid | ||||
|   push        upload a chart to a registry | ||||
|  | ||||
|   also piper Execute step supports direct execution helm command via one flag. | ||||
| ` + "`" + `` + "`" + `` + "`" + ` | ||||
|  | ||||
| Note: piper supports only helm3 version, since helm2 is deprecated.`, | ||||
| 		PreRunE: func(cmd *cobra.Command, _ []string) error { | ||||
| 			startTime = time.Now() | ||||
| 			log.SetStepName(STEP_NAME) | ||||
| 			log.SetVerbose(GeneralConfig.Verbose) | ||||
|  | ||||
| 			GeneralConfig.GitHubAccessTokens = ResolveAccessTokens(GeneralConfig.GitHubTokens) | ||||
|  | ||||
| 			path, _ := os.Getwd() | ||||
| 			fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path} | ||||
| 			log.RegisterHook(fatalHook) | ||||
|  | ||||
| 			err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile) | ||||
| 			if err != nil { | ||||
| 				log.SetErrorCategory(log.ErrorConfiguration) | ||||
| 				return err | ||||
| 			} | ||||
| 			log.RegisterSecret(stepConfig.ContainerRegistryPassword) | ||||
| 			log.RegisterSecret(stepConfig.ContainerRegistryUser) | ||||
| 			log.RegisterSecret(stepConfig.KubeConfig) | ||||
| 			log.RegisterSecret(stepConfig.DockerConfigJSON) | ||||
|  | ||||
| 			if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 { | ||||
| 				sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID) | ||||
| 				log.RegisterHook(&sentryHook) | ||||
| 			} | ||||
|  | ||||
| 			if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 { | ||||
| 				splunkClient = &splunk.Splunk{} | ||||
| 				logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID} | ||||
| 				log.RegisterHook(logCollector) | ||||
| 			} | ||||
|  | ||||
| 			validation, err := validation.New(validation.WithJSONNamesForStructFields(), validation.WithPredefinedErrorMessages()) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if err = validation.ValidateStruct(stepConfig); err != nil { | ||||
| 				log.SetErrorCategory(log.ErrorConfiguration) | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		Run: func(_ *cobra.Command, _ []string) { | ||||
| 			stepTelemetryData := telemetry.CustomData{} | ||||
| 			stepTelemetryData.ErrorCode = "1" | ||||
| 			handler := func() { | ||||
| 				config.RemoveVaultSecretFiles() | ||||
| 				stepTelemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds()) | ||||
| 				stepTelemetryData.ErrorCategory = log.GetErrorCategory().String() | ||||
| 				stepTelemetryData.PiperCommitHash = GitCommit | ||||
| 				telemetryClient.SetData(&stepTelemetryData) | ||||
| 				telemetryClient.Send() | ||||
| 				if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 { | ||||
| 					splunkClient.Send(telemetryClient.GetData(), logCollector) | ||||
| 				} | ||||
| 			} | ||||
| 			log.DeferExitHandler(handler) | ||||
| 			defer handler() | ||||
| 			telemetryClient.Initialize(GeneralConfig.NoTelemetry, STEP_NAME) | ||||
| 			if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 { | ||||
| 				splunkClient.Initialize(GeneralConfig.CorrelationID, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Dsn, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Token, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Index, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.SendLogs) | ||||
| 			} | ||||
| 			helmExecute(stepConfig, &stepTelemetryData) | ||||
| 			stepTelemetryData.ErrorCode = "0" | ||||
| 			log.Entry().Info("SUCCESS") | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	addHelmExecuteFlags(createHelmExecuteCmd, &stepConfig) | ||||
| 	return createHelmExecuteCmd | ||||
| } | ||||
|  | ||||
| func addHelmExecuteFlags(cmd *cobra.Command, stepConfig *helmExecuteOptions) { | ||||
| 	cmd.Flags().StringSliceVar(&stepConfig.AdditionalParameters, "additionalParameters", []string{}, "Defines additional parameters for Helm like  \"helm install [NAME] [CHART] [flags]\".") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ChartPath, "chartPath", os.Getenv("PIPER_chartPath"), "Defines the chart path for helm.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ContainerRegistryPassword, "containerRegistryPassword", os.Getenv("PIPER_containerRegistryPassword"), "Password for container registry access - typically provided by the CI/CD environment.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ContainerImageName, "containerImageName", os.Getenv("PIPER_containerImageName"), "Name of the container which will be built - will be used together with `containerImageTag` instead of parameter `containerImage`") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ContainerImageTag, "containerImageTag", os.Getenv("PIPER_containerImageTag"), "Tag of the container which will be built - will be used together with `containerImageName` instead of parameter `containerImage`") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "http(s) url of the Container registry where the image to deploy is located.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ContainerRegistryUser, "containerRegistryUser", os.Getenv("PIPER_containerRegistryUser"), "Username for container registry access - typically provided by the CI/CD environment.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ContainerRegistrySecret, "containerRegistrySecret", `regsecret`, "Name of the container registry secret used for pulling containers from the registry.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DeploymentName, "deploymentName", os.Getenv("PIPER_deploymentName"), "Defines the name of the deployment. It is a mandatory parameter when deploying with helm.") | ||||
| 	cmd.Flags().IntVar(&stepConfig.HelmDeployWaitSeconds, "helmDeployWaitSeconds", 300, "Number of seconds before helm deploy returns.") | ||||
| 	cmd.Flags().StringSliceVar(&stepConfig.HelmValues, "helmValues", []string{}, "List of helm values as YAML file reference or URL (as per helm parameter description for `-f` / `--values`)") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Image, "image", os.Getenv("PIPER_image"), "Full name of the image to be deployed.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.KeepFailedDeployments, "keepFailedDeployments", false, "Defines whether a failed deployment will be purged") | ||||
| 	cmd.Flags().StringVar(&stepConfig.KubeConfig, "kubeConfig", os.Getenv("PIPER_kubeConfig"), "Defines the path to the \"kubeconfig\" file.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.KubeContext, "kubeContext", os.Getenv("PIPER_kubeContext"), "Defines the context to use from the \"kubeconfig\" file.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Namespace, "namespace", `default`, "Defines the target Kubernetes namespace for the deployment.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DockerConfigJSON, "dockerConfigJSON", os.Getenv("PIPER_dockerConfigJSON"), "Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).") | ||||
| 	cmd.Flags().StringVar(&stepConfig.HelmCommand, "helmCommand", os.Getenv("PIPER_helmCommand"), "Helm: defines the command `install`, `lint`, `package`, `test`, `upgrade` and etc.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.DryRun, "dryRun", false, "simulate execute command, like simulate an install") | ||||
| 	cmd.Flags().StringVar(&stepConfig.PackageVersion, "packageVersion", os.Getenv("PIPER_packageVersion"), "set the version on the chart to this semver version") | ||||
| 	cmd.Flags().StringVar(&stepConfig.AppVersion, "appVersion", os.Getenv("PIPER_appVersion"), "set the appVersion on the chart to this version") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.DependencyUpdate, "dependencyUpdate", false, "set the appVersion on the chart to this version") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.DumpLogs, "dumpLogs", false, "dump the logs from test pods (this runs after all tests are complete, but before any cleanup)") | ||||
| 	cmd.Flags().StringVar(&stepConfig.FilterTest, "filterTest", os.Getenv("PIPER_filterTest"), "specify tests by attribute (currently `name`) using attribute=value syntax or `!attribute=value` to exclude a test (can specify multiple or separate values with commas `name=test1,name=test2`)") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ChartRepo, "chartRepo", `https://charts.helm.sh/stable`, "set the chart repository") | ||||
| 	cmd.Flags().StringVar(&stepConfig.HelmRegistryUser, "helmRegistryUser", os.Getenv("PIPER_helmRegistryUser"), "set the user for login to helm registry") | ||||
| 	cmd.Flags().StringVar(&stepConfig.HelmChartServer, "helmChartServer", `localhost:5000`, "set chart server for pushing chart") | ||||
|  | ||||
| 	cmd.MarkFlagRequired("chartPath") | ||||
| 	cmd.MarkFlagRequired("containerRegistryUrl") | ||||
| 	cmd.MarkFlagRequired("image") | ||||
| 	cmd.MarkFlagRequired("helmCommand") | ||||
| } | ||||
|  | ||||
| // retrieve step metadata | ||||
| func helmExecuteMetadata() config.StepData { | ||||
| 	var theMetaData = config.StepData{ | ||||
| 		Metadata: config.StepMetadata{ | ||||
| 			Name:        "helmExecute", | ||||
| 			Aliases:     []config.Alias{}, | ||||
| 			Description: "Executes helm3 functionality as the package manager for Kubernetes.", | ||||
| 		}, | ||||
| 		Spec: config.StepSpec{ | ||||
| 			Inputs: config.StepInputs{ | ||||
| 				Secrets: []config.StepSecrets{ | ||||
| 					{Name: "dockerCredentialsId", Type: "jenkins"}, | ||||
| 					{Name: "dockerConfigJsonCredentialsId", Description: "Jenkins 'Secret file' credentials ID containing Docker config.json (with registry credential(s)).", Type: "jenkins"}, | ||||
| 				}, | ||||
| 				Resources: []config.StepResources{ | ||||
| 					{Name: "deployDescriptor", Type: "stash"}, | ||||
| 				}, | ||||
| 				Parameters: []config.StepParameters{ | ||||
| 					{ | ||||
| 						Name:        "additionalParameters", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "[]string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "helmDeploymentParameters"}}, | ||||
| 						Default:     []string{}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "chartPath", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   true, | ||||
| 						Aliases:     []config.Alias{{Name: "helmChartPath"}}, | ||||
| 						Default:     os.Getenv("PIPER_chartPath"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "containerRegistryPassword", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name:  "dockerCredentialsId", | ||||
| 								Param: "password", | ||||
| 								Type:  "secret", | ||||
| 							}, | ||||
|  | ||||
| 							{ | ||||
| 								Name:  "commonPipelineEnvironment", | ||||
| 								Param: "custom/repositoryPassword", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: false, | ||||
| 						Aliases:   []config.Alias{}, | ||||
| 						Default:   os.Getenv("PIPER_containerRegistryPassword"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "containerImageName", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "dockerImageName"}}, | ||||
| 						Default:     os.Getenv("PIPER_containerImageName"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "containerImageTag", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name:  "commonPipelineEnvironment", | ||||
| 								Param: "artifactVersion", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: false, | ||||
| 						Aliases:   []config.Alias{{Name: "artifactVersion"}}, | ||||
| 						Default:   os.Getenv("PIPER_containerImageTag"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "containerRegistryUrl", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name:  "commonPipelineEnvironment", | ||||
| 								Param: "container/registryUrl", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: true, | ||||
| 						Aliases:   []config.Alias{{Name: "dockerRegistryUrl"}}, | ||||
| 						Default:   os.Getenv("PIPER_containerRegistryUrl"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "containerRegistryUser", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name:  "dockerCredentialsId", | ||||
| 								Param: "username", | ||||
| 								Type:  "secret", | ||||
| 							}, | ||||
|  | ||||
| 							{ | ||||
| 								Name:  "commonPipelineEnvironment", | ||||
| 								Param: "custom/repositoryUsername", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: false, | ||||
| 						Aliases:   []config.Alias{}, | ||||
| 						Default:   os.Getenv("PIPER_containerRegistryUser"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "containerRegistrySecret", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     `regsecret`, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "deploymentName", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "helmDeploymentName"}}, | ||||
| 						Default:     os.Getenv("PIPER_deploymentName"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "helmDeployWaitSeconds", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "int", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     300, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "helmValues", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "[]string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     []string{}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "image", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name:  "commonPipelineEnvironment", | ||||
| 								Param: "container/imageNameTag", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: true, | ||||
| 						Aliases:   []config.Alias{{Name: "deployImage"}}, | ||||
| 						Default:   os.Getenv("PIPER_image"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "keepFailedDeployments", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "bool", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     false, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "kubeConfig", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name: "kubeConfigFileCredentialsId", | ||||
| 								Type: "secret", | ||||
| 							}, | ||||
|  | ||||
| 							{ | ||||
| 								Name:    "kubeConfigFileVaultSecretName", | ||||
| 								Type:    "vaultSecretFile", | ||||
| 								Default: "kube-config", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: false, | ||||
| 						Aliases:   []config.Alias{}, | ||||
| 						Default:   os.Getenv("PIPER_kubeConfig"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "kubeContext", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_kubeContext"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "namespace", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "helmDeploymentNamespace"}}, | ||||
| 						Default:     `default`, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "dockerConfigJSON", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name: "dockerConfigJsonCredentialsId", | ||||
| 								Type: "secret", | ||||
| 							}, | ||||
|  | ||||
| 							{ | ||||
| 								Name:    "dockerConfigFileVaultSecretName", | ||||
| 								Type:    "vaultSecretFile", | ||||
| 								Default: "docker-config", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: false, | ||||
| 						Aliases:   []config.Alias{}, | ||||
| 						Default:   os.Getenv("PIPER_dockerConfigJSON"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "helmCommand", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   true, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_helmCommand"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "dryRun", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "bool", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     false, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "packageVersion", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_packageVersion"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "appVersion", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_appVersion"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "dependencyUpdate", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "bool", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     false, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "dumpLogs", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "bool", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     false, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "filterTest", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_filterTest"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "chartRepo", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     `https://charts.helm.sh/stable`, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "helmRegistryUser", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_helmRegistryUser"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "helmChartServer", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     `localhost:5000`, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			Containers: []config.Container{ | ||||
| 				{Image: "dtzar/helm-kubectl:3.4.1", WorkingDir: "/config", Options: []config.Option{{Name: "-u", Value: "0"}}}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return theMetaData | ||||
| } | ||||
							
								
								
									
										17
									
								
								cmd/helmExecute_generated_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								cmd/helmExecute_generated_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestHelmExecuteCommand(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testCmd := HelmExecuteCommand() | ||||
|  | ||||
| 	// only high level testing performed - details are tested in step generation procedure | ||||
| 	assert.Equal(t, "helmExecute", testCmd.Use, "command name incorrect") | ||||
|  | ||||
| } | ||||
							
								
								
									
										271
									
								
								cmd/helmExecute_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								cmd/helmExecute_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,271 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/kubernetes/mocks" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestRunHelmUpgrade(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "upgrade", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "upgrade", | ||||
| 			}, | ||||
| 			methodError:    errors.New("some error"), | ||||
| 			expectedErrStr: "failed to execute upgrade: some error", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmUpgrade").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config.HelmCommand, helmExecute) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunHelmLint(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		expectedConfig []string | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "lint", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "lint", | ||||
| 			}, | ||||
| 			methodError:    errors.New("some error"), | ||||
| 			expectedErrStr: "failed to execute helm lint: some error", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmLint").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config.HelmCommand, helmExecute) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunHelmInstall(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		expectedConfig []string | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "install", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "install", | ||||
| 			}, | ||||
| 			methodError:    errors.New("some error"), | ||||
| 			expectedErrStr: "failed to execute helm install: some error", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmInstall").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config.HelmCommand, helmExecute) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunHelmTest(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "test", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "test", | ||||
| 			}, | ||||
| 			methodError:    errors.New("some error"), | ||||
| 			expectedErrStr: "failed to execute helm test: some error", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmTest").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config.HelmCommand, helmExecute) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunHelmUninstall(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "uninstall", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "uninstall", | ||||
| 			}, | ||||
| 			methodError:    errors.New("some error"), | ||||
| 			expectedErrStr: "failed to execute helm uninstall: some error", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmUninstall").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config.HelmCommand, helmExecute) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunHelmPackage(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "package", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "package", | ||||
| 			}, | ||||
| 			methodError:    errors.New("some error"), | ||||
| 			expectedErrStr: "failed to execute helm package: some error", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmPackage").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config.HelmCommand, helmExecute) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunHelmPush(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "push", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "push", | ||||
| 			}, | ||||
| 			methodError:    errors.New("some error"), | ||||
| 			expectedErrStr: "failed to execute helm push: some error", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmPush").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config.HelmCommand, helmExecute) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 	} | ||||
| } | ||||
| @@ -12,56 +12,14 @@ import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/command" | ||||
| 	"github.com/SAP/jenkins-library/pkg/docker" | ||||
| 	"github.com/SAP/jenkins-library/pkg/kubernetes" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperutils" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| ) | ||||
|  | ||||
| type kubernetesDeployUtils interface { | ||||
| 	SetEnv(env []string) | ||||
| 	Stdout(out io.Writer) | ||||
| 	Stderr(err io.Writer) | ||||
| 	RunExecutable(e string, p ...string) error | ||||
|  | ||||
| 	piperutils.FileUtils | ||||
| } | ||||
|  | ||||
| type kubernetesDeployUtilsBundle struct { | ||||
| 	*command.Command | ||||
| 	*piperutils.Files | ||||
| } | ||||
|  | ||||
| func newKubernetesDeployUtilsBundle() kubernetesDeployUtils { | ||||
| 	utils := kubernetesDeployUtilsBundle{ | ||||
| 		Command: &command.Command{ | ||||
| 			ErrorCategoryMapping: map[string][]string{ | ||||
| 				log.ErrorConfiguration.String(): { | ||||
| 					"Error: Get * no such host", | ||||
| 					"Error: path * not found", | ||||
| 					"Error: rendered manifests contain a resource that already exists.", | ||||
| 					"Error: unknown flag", | ||||
| 					"Error: UPGRADE FAILED: * failed to replace object: * is invalid", | ||||
| 					"Error: UPGRADE FAILED: * failed to create resource: * is invalid", | ||||
| 					"Error: UPGRADE FAILED: an error occurred * not found", | ||||
| 					"Error: UPGRADE FAILED: query: failed to query with labels:", | ||||
| 					"Invalid value: \"\": field is immutable", | ||||
| 				}, | ||||
| 				log.ErrorCustom.String(): { | ||||
| 					"Error: release * failed, * timed out waiting for the condition", | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Files: &piperutils.Files{}, | ||||
| 	} | ||||
| 	// reroute stderr output to logging framework, stdout will be used for command interactions | ||||
| 	utils.Stderr(log.Writer()) | ||||
| 	return &utils | ||||
| } | ||||
|  | ||||
| func kubernetesDeploy(config kubernetesDeployOptions, telemetryData *telemetry.CustomData) { | ||||
| 	utils := newKubernetesDeployUtilsBundle() | ||||
| 	utils := kubernetes.NewDeployUtilsBundle() | ||||
|  | ||||
| 	// error situations stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end | ||||
| 	err := runKubernetesDeploy(config, telemetryData, utils, log.Writer()) | ||||
| @@ -70,7 +28,7 @@ func kubernetesDeploy(config kubernetesDeployOptions, telemetryData *telemetry.C | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runKubernetesDeploy(config kubernetesDeployOptions, telemetryData *telemetry.CustomData, utils kubernetesDeployUtils, stdout io.Writer) error { | ||||
| func runKubernetesDeploy(config kubernetesDeployOptions, telemetryData *telemetry.CustomData, utils kubernetes.DeployUtils, stdout io.Writer) error { | ||||
| 	telemetryData.Custom1Label = "deployTool" | ||||
| 	telemetryData.Custom1 = config.DeployTool | ||||
|  | ||||
| @@ -82,7 +40,7 @@ func runKubernetesDeploy(config kubernetesDeployOptions, telemetryData *telemetr | ||||
| 	return fmt.Errorf("Failed to execute deployments") | ||||
| } | ||||
|  | ||||
| func runHelmDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils, stdout io.Writer) error { | ||||
| func runHelmDeploy(config kubernetesDeployOptions, utils kubernetes.DeployUtils, stdout io.Writer) error { | ||||
| 	if len(config.ChartPath) <= 0 { | ||||
| 		return fmt.Errorf("chart path has not been set, please configure chartPath parameter") | ||||
| 	} | ||||
| @@ -251,7 +209,7 @@ func runHelmDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils, | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func runKubectlDeploy(config kubernetesDeployOptions, utils kubernetesDeployUtils, stdout io.Writer) error { | ||||
| func runKubectlDeploy(config kubernetesDeployOptions, utils kubernetes.DeployUtils, stdout io.Writer) error { | ||||
| 	_, containerRegistry, err := splitRegistryURL(config.ContainerRegistryURL) | ||||
| 	if err != nil { | ||||
| 		log.Entry().WithError(err).Fatalf("Container registry url '%v' incorrect", config.ContainerRegistryURL) | ||||
| @@ -385,7 +343,7 @@ func splitFullImageName(image string) (imageName, tag string, err error) { | ||||
| 	return "", "", fmt.Errorf("Failed to split image name '%v'", image) | ||||
| } | ||||
|  | ||||
| func defineKubeSecretParams(config kubernetesDeployOptions, containerRegistry string, utils kubernetesDeployUtils) (error, []string) { | ||||
| func defineKubeSecretParams(config kubernetesDeployOptions, containerRegistry string, utils kubernetes.DeployUtils) (error, []string) { | ||||
| 	targetPath := "" | ||||
| 	if len(config.DockerConfigJSON) > 0 { | ||||
| 		// first enhance config.json with additional pipeline-related credentials if they have been provided | ||||
|   | ||||
| @@ -57,6 +57,7 @@ func GetAllStepMetadata() map[string]config.StepData { | ||||
| 		"golangBuild":                               golangBuildMetadata(), | ||||
| 		"gradleExecuteBuild":                        gradleExecuteBuildMetadata(), | ||||
| 		"hadolintExecute":                           hadolintExecuteMetadata(), | ||||
| 		"helmExecute":                               helmExecuteMetadata(), | ||||
| 		"influxWriteData":                           influxWriteDataMetadata(), | ||||
| 		"integrationArtifactDeploy":                 integrationArtifactDeployMetadata(), | ||||
| 		"integrationArtifactDownload":               integrationArtifactDownloadMetadata(), | ||||
|   | ||||
| @@ -95,6 +95,7 @@ func Execute() { | ||||
| 	rootCmd.AddCommand(UiVeri5ExecuteTestsCommand()) | ||||
| 	rootCmd.AddCommand(SonarExecuteScanCommand()) | ||||
| 	rootCmd.AddCommand(KubernetesDeployCommand()) | ||||
| 	rootCmd.AddCommand(HelmExecuteCommand()) | ||||
| 	rootCmd.AddCommand(XsDeployCommand()) | ||||
| 	rootCmd.AddCommand(GithubCheckBranchProtectionCommand()) | ||||
| 	rootCmd.AddCommand(GithubCommentIssueCommand()) | ||||
|   | ||||
							
								
								
									
										413
									
								
								pkg/kubernetes/helm.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										413
									
								
								pkg/kubernetes/helm.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,413 @@ | ||||
| package kubernetes | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| ) | ||||
|  | ||||
| // HelmExecutor is used for mock | ||||
| type HelmExecutor interface { | ||||
| 	RunHelmAdd() error | ||||
| 	RunHelmUpgrade() error | ||||
| 	RunHelmLint() error | ||||
| 	RunHelmInstall() error | ||||
| 	RunHelmUninstall() error | ||||
| 	RunHelmPackage() error | ||||
| 	RunHelmTest() error | ||||
| 	RunHelmRegistryLogin() error | ||||
| 	RunHelmRegistryLogout() error | ||||
| 	RunHelmPush() error | ||||
| } | ||||
|  | ||||
| // HelmExecute struct | ||||
| type HelmExecute struct { | ||||
| 	utils   DeployUtils | ||||
| 	config  HelmExecuteOptions | ||||
| 	verbose bool | ||||
| 	stdout  io.Writer | ||||
| } | ||||
|  | ||||
| // HelmExecuteOptions struct holds common parameters for functions RunHelm... | ||||
| type HelmExecuteOptions struct { | ||||
| 	AdditionalParameters      []string `json:"additionalParameters,omitempty"` | ||||
| 	ChartPath                 string   `json:"chartPath,omitempty"` | ||||
| 	ContainerRegistryPassword string   `json:"containerRegistryPassword,omitempty"` | ||||
| 	ContainerImageName        string   `json:"containerImageName,omitempty"` | ||||
| 	ContainerImageTag         string   `json:"containerImageTag,omitempty"` | ||||
| 	ContainerRegistryURL      string   `json:"containerRegistryUrl,omitempty"` | ||||
| 	ContainerRegistryUser     string   `json:"containerRegistryUser,omitempty"` | ||||
| 	ContainerRegistrySecret   string   `json:"containerRegistrySecret,omitempty"` | ||||
| 	DeploymentName            string   `json:"deploymentName,omitempty"` | ||||
| 	ForceUpdates              bool     `json:"forceUpdates,omitempty"` | ||||
| 	HelmDeployWaitSeconds     int      `json:"helmDeployWaitSeconds,omitempty"` | ||||
| 	HelmValues                []string `json:"helmValues,omitempty"` | ||||
| 	Image                     string   `json:"image,omitempty"` | ||||
| 	KeepFailedDeployments     bool     `json:"keepFailedDeployments,omitempty"` | ||||
| 	KubeConfig                string   `json:"kubeConfig,omitempty"` | ||||
| 	KubeContext               string   `json:"kubeContext,omitempty"` | ||||
| 	Namespace                 string   `json:"namespace,omitempty"` | ||||
| 	DockerConfigJSON          string   `json:"dockerConfigJSON,omitempty"` | ||||
| 	DryRun                    bool     `json:"dryRun,omitempty"` | ||||
| 	PackageVersion            string   `json:"packageVersion,omitempty"` | ||||
| 	AppVersion                string   `json:"appVersion,omitempty"` | ||||
| 	DependencyUpdate          bool     `json:"dependencyUpdate,omitempty"` | ||||
| 	DumpLogs                  bool     `json:"dumpLogs,omitempty"` | ||||
| 	FilterTest                string   `json:"filterTest,omitempty"` | ||||
| 	ChartRepo                 string   `json:"chartRepo,omitempty"` | ||||
| 	HelmRegistryUser          string   `json:"helmRegistryUser,omitempty"` | ||||
| 	HelmChartServer           string   `json:"helmChartServer,omitempty"` | ||||
| } | ||||
|  | ||||
| // NewHelmExecutor creates HelmExecute instance | ||||
| func NewHelmExecutor(config HelmExecuteOptions, utils DeployUtils, verbose bool, stdout io.Writer) HelmExecutor { | ||||
| 	return &HelmExecute{ | ||||
| 		config:  config, | ||||
| 		utils:   utils, | ||||
| 		verbose: verbose, | ||||
| 		stdout:  stdout, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // runHelmInit is used to set up env for executing helm command | ||||
| func (h *HelmExecute) runHelmInit() error { | ||||
| 	helmLogFields := map[string]interface{}{} | ||||
| 	helmLogFields["Chart Path"] = h.config.ChartPath | ||||
| 	helmLogFields["Namespace"] = h.config.Namespace | ||||
| 	helmLogFields["Deployment Name"] = h.config.DeploymentName | ||||
| 	helmLogFields["Context"] = h.config.KubeContext | ||||
| 	helmLogFields["Kubeconfig"] = h.config.KubeConfig | ||||
| 	log.Entry().WithFields(helmLogFields).Debug("Calling Helm") | ||||
|  | ||||
| 	helmEnv := []string{fmt.Sprintf("KUBECONFIG=%v", h.config.KubeConfig)} | ||||
|  | ||||
| 	log.Entry().Debugf("Helm SetEnv: %v", helmEnv) | ||||
| 	h.utils.SetEnv(helmEnv) | ||||
| 	h.utils.Stdout(h.stdout) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmAdd is used to add a chart repository | ||||
| func (h *HelmExecute) RunHelmAdd() error { | ||||
| 	helmParams := []string{ | ||||
| 		"repo", | ||||
| 		"add", | ||||
| 		"stable", | ||||
| 	} | ||||
|  | ||||
| 	helmParams = append(helmParams, h.config.ChartRepo) | ||||
| 	if h.verbose { | ||||
| 		helmParams = append(helmParams, "--debug") | ||||
| 	} | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm add ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm add call failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmUpgrade is used to upgrade a release | ||||
| func (h *HelmExecute) RunHelmUpgrade() error { | ||||
| 	err := h.runHelmInit() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	var containerInfo map[string]string | ||||
| 	if h.config.Image != "" && h.config.ContainerRegistryURL != "" { | ||||
| 		containerInfo, err = getContainerInfo(h.config) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to execute deployments") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	helmParams := []string{ | ||||
| 		"upgrade", | ||||
| 		h.config.DeploymentName, | ||||
| 		h.config.ChartPath, | ||||
| 	} | ||||
|  | ||||
| 	if h.verbose { | ||||
| 		helmParams = append(helmParams, "--debug") | ||||
| 	} | ||||
|  | ||||
| 	for _, v := range h.config.HelmValues { | ||||
| 		helmParams = append(helmParams, "--values", v) | ||||
| 	} | ||||
|  | ||||
| 	helmParams = append( | ||||
| 		helmParams, | ||||
| 		"--install", | ||||
| 		"--namespace", h.config.Namespace, | ||||
| 	) | ||||
|  | ||||
| 	if h.config.Image != "" && h.config.ContainerRegistryURL != "" { | ||||
| 		helmParams = append(helmParams, "--set", fmt.Sprintf("image.repository=%v/%v,image.tag=%v", | ||||
| 			containerInfo["containerRegistry"], containerInfo["containerImageName"], containerInfo["containerImageTag"])) | ||||
| 	} | ||||
|  | ||||
| 	if h.config.ForceUpdates { | ||||
| 		helmParams = append(helmParams, "--force") | ||||
| 	} | ||||
|  | ||||
| 	helmParams = append(helmParams, "--wait", "--timeout", fmt.Sprintf("%vs", h.config.HelmDeployWaitSeconds)) | ||||
|  | ||||
| 	if !h.config.KeepFailedDeployments { | ||||
| 		helmParams = append(helmParams, "--atomic") | ||||
| 	} | ||||
|  | ||||
| 	if len(h.config.AdditionalParameters) > 0 { | ||||
| 		helmParams = append(helmParams, h.config.AdditionalParameters...) | ||||
| 	} | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm upgrade ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm upgrade call failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmLint is used to examine a chart for possible issues | ||||
| func (h *HelmExecute) RunHelmLint() error { | ||||
| 	err := h.runHelmInit() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	helmParams := []string{ | ||||
| 		"lint", | ||||
| 		h.config.ChartPath, | ||||
| 	} | ||||
|  | ||||
| 	if h.verbose { | ||||
| 		helmParams = append(helmParams, "--debug") | ||||
| 	} | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm lint ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm lint call failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmInstall is used to install a chart | ||||
| func (h *HelmExecute) RunHelmInstall() error { | ||||
| 	if err := h.runHelmInit(); err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := h.RunHelmAdd(); err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	helmParams := []string{ | ||||
| 		"install", | ||||
| 		h.config.DeploymentName, | ||||
| 		h.config.ChartPath, | ||||
| 	} | ||||
| 	helmParams = append(helmParams, "--namespace", h.config.Namespace) | ||||
| 	helmParams = append(helmParams, "--create-namespace") | ||||
| 	if !h.config.KeepFailedDeployments { | ||||
| 		helmParams = append(helmParams, "--atomic") | ||||
| 	} | ||||
| 	if h.config.DryRun { | ||||
| 		helmParams = append(helmParams, "--dry-run") | ||||
| 	} | ||||
| 	helmParams = append(helmParams, "--wait", "--timeout", fmt.Sprintf("%vs", h.config.HelmDeployWaitSeconds)) | ||||
| 	for _, v := range h.config.HelmValues { | ||||
| 		helmParams = append(helmParams, "--values", v) | ||||
| 	} | ||||
| 	if len(h.config.AdditionalParameters) > 0 { | ||||
| 		helmParams = append(helmParams, h.config.AdditionalParameters...) | ||||
| 	} | ||||
| 	if h.verbose { | ||||
| 		helmParams = append(helmParams, "--debug") | ||||
| 	} | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm install ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm install call failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmUninstall is used to uninstall a chart | ||||
| func (h *HelmExecute) RunHelmUninstall() error { | ||||
| 	err := h.runHelmInit() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	helmParams := []string{ | ||||
| 		"uninstall", | ||||
| 		h.config.DeploymentName, | ||||
| 	} | ||||
| 	if len(h.config.Namespace) <= 0 { | ||||
| 		return fmt.Errorf("namespace has not been set, please configure namespace parameter") | ||||
| 	} | ||||
| 	helmParams = append(helmParams, "--namespace", h.config.Namespace) | ||||
| 	if h.config.HelmDeployWaitSeconds > 0 { | ||||
| 		helmParams = append(helmParams, "--wait", "--timeout", fmt.Sprintf("%vs", h.config.HelmDeployWaitSeconds)) | ||||
| 	} | ||||
| 	if h.config.DryRun { | ||||
| 		helmParams = append(helmParams, "--dry-run") | ||||
| 	} | ||||
| 	if h.verbose { | ||||
| 		helmParams = append(helmParams, "--debug") | ||||
| 	} | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm uninstall ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm uninstall call failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmPackage is used to package a chart directory into a chart archive | ||||
| func (h *HelmExecute) RunHelmPackage() error { | ||||
| 	err := h.runHelmInit() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	helmParams := []string{ | ||||
| 		"package", | ||||
| 		h.config.ChartPath, | ||||
| 	} | ||||
| 	if len(h.config.PackageVersion) > 0 { | ||||
| 		helmParams = append(helmParams, "--version", h.config.PackageVersion) | ||||
| 	} | ||||
| 	if h.config.DependencyUpdate { | ||||
| 		helmParams = append(helmParams, "--dependency-update") | ||||
| 	} | ||||
| 	if len(h.config.AppVersion) > 0 { | ||||
| 		helmParams = append(helmParams, "--app-version", h.config.AppVersion) | ||||
| 	} | ||||
| 	if h.verbose { | ||||
| 		helmParams = append(helmParams, "--debug") | ||||
| 	} | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm package ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm package call failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmTest is used to run tests for a release | ||||
| func (h *HelmExecute) RunHelmTest() error { | ||||
| 	err := h.runHelmInit() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	helmParams := []string{ | ||||
| 		"test", | ||||
| 		h.config.ChartPath, | ||||
| 	} | ||||
| 	if len(h.config.FilterTest) > 0 { | ||||
| 		helmParams = append(helmParams, "--filter", h.config.FilterTest) | ||||
| 	} | ||||
| 	if h.config.DumpLogs { | ||||
| 		helmParams = append(helmParams, "--logs") | ||||
| 	} | ||||
| 	if h.verbose { | ||||
| 		helmParams = append(helmParams, "--debug") | ||||
| 	} | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm test ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm test call failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmRegistryLogin is used to login private registry | ||||
| func (h *HelmExecute) RunHelmRegistryLogin() error { | ||||
| 	helmParams := []string{ | ||||
| 		"registry login", | ||||
| 	} | ||||
| 	helmParams = append(helmParams, "-u", h.config.HelmRegistryUser) | ||||
| 	helmParams = append(helmParams, h.config.HelmChartServer) | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm login ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm push login failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunHelmRegistryLogout is logout to login private registry | ||||
| func (h *HelmExecute) RunHelmRegistryLogout() error { | ||||
| 	helmParams := []string{ | ||||
| 		"registry logout", | ||||
| 	} | ||||
| 	helmParams = append(helmParams, h.config.HelmChartServer) | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm logout ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm push logout failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| //RunHelmPush is used to upload a chart to a registry | ||||
| func (h *HelmExecute) RunHelmPush() error { | ||||
| 	err := h.runHelmInit() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := h.RunHelmRegistryLogin(); err != nil { | ||||
| 		return fmt.Errorf("failed to execute registry login: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	helmParams := []string{ | ||||
| 		"push", | ||||
| 	} | ||||
| 	helmParams = append(helmParams, fmt.Sprintf("%v", h.config.DeploymentName+h.config.PackageVersion+".tgz")) | ||||
| 	helmParams = append(helmParams, fmt.Sprintf("%v", "oci://"+h.config.HelmChartServer+"/helm-charts")) | ||||
|  | ||||
| 	h.utils.Stdout(h.stdout) | ||||
| 	log.Entry().Info("Calling helm push ...") | ||||
| 	log.Entry().Debugf("Helm parameters: %v", helmParams) | ||||
| 	if err := h.utils.RunExecutable("helm", helmParams...); err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("Helm push call failed") | ||||
| 	} | ||||
|  | ||||
| 	if err := h.RunHelmRegistryLogout(); err != nil { | ||||
| 		return fmt.Errorf("failed to execute registry logout: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										413
									
								
								pkg/kubernetes/helm_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										413
									
								
								pkg/kubernetes/helm_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,413 @@ | ||||
| package kubernetes | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/mock" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| type helmMockUtilsBundle struct { | ||||
| 	*mock.FilesMock | ||||
| 	*mock.ExecMockRunner | ||||
| } | ||||
|  | ||||
| func newHelmMockUtilsBundle() helmMockUtilsBundle { | ||||
| 	utils := helmMockUtilsBundle{ExecMockRunner: &mock.ExecMockRunner{}} | ||||
| 	return utils | ||||
| } | ||||
|  | ||||
| func TestRunHelm(t *testing.T) { | ||||
|  | ||||
| 	t.Run("Helm add command", func(t *testing.T) { | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartRepo: "https://charts.helm.sh/stable", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"repo", "add", "stable", "https://charts.helm.sh/stable"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for i, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmAdd() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[i]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm upgrade command", func(t *testing.T) { | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					DeploymentName:        "test_deployment", | ||||
| 					ChartPath:             ".", | ||||
| 					Namespace:             "test_namespace", | ||||
| 					ForceUpdates:          true, | ||||
| 					HelmDeployWaitSeconds: 3456, | ||||
| 					AdditionalParameters:  []string{"additional parameter"}, | ||||
| 					ContainerRegistryURL:  "https://hub.docker.com/", | ||||
| 					Image:                 "dtzar/helm-kubectl:3.4.1", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"upgrade", "test_deployment", ".", "--install", "--namespace", "test_namespace", "--set", "image.repository=hub.docker.com/dtzar/helm-kubectl,image.tag=3.4.1", "--force", "--wait", "--timeout", "3456s", "--atomic", "additional parameter"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for i, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmUpgrade() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[i]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm lint command", func(t *testing.T) { | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath: ".", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"lint", "."}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for i, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmLint() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[i]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm install command", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config                HelmExecuteOptions | ||||
| 			expectedConfigInstall []string | ||||
| 			expectedConfigAdd     []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:             ".", | ||||
| 					DeploymentName:        "testPackage", | ||||
| 					Namespace:             "test-namespace", | ||||
| 					HelmDeployWaitSeconds: 525, | ||||
| 					ChartRepo:             "https://charts.helm.sh/stable", | ||||
| 				}, | ||||
| 				expectedConfigAdd:     []string{"repo", "add", "stable", "https://charts.helm.sh/stable"}, | ||||
| 				expectedConfigInstall: []string{"install", "testPackage", ".", "--namespace", "test-namespace", "--create-namespace", "--atomic", "--wait", "--timeout", "525s"}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:             ".", | ||||
| 					DeploymentName:        "testPackage", | ||||
| 					Namespace:             "test-namespace", | ||||
| 					HelmDeployWaitSeconds: 525, | ||||
| 					KeepFailedDeployments: false, | ||||
| 					DryRun:                true, | ||||
| 					AdditionalParameters:  []string{"--set-file my_script=dothings.sh"}, | ||||
| 					ChartRepo:             "https://charts.helm.sh/stable", | ||||
| 				}, | ||||
| 				expectedConfigAdd:     []string{"repo", "add", "stable", "https://charts.helm.sh/stable"}, | ||||
| 				expectedConfigInstall: []string{"install", "testPackage", ".", "--namespace", "test-namespace", "--create-namespace", "--atomic", "--dry-run", "--wait", "--timeout", "525s", "--set-file my_script=dothings.sh"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for _, testCase := range testTable { | ||||
| 			utils := newHelmMockUtilsBundle() | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmInstall() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfigAdd}, utils.Calls[0]) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfigInstall}, utils.Calls[1]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm uninstal command", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:      ".", | ||||
| 					DeploymentName: "testPackage", | ||||
| 					Namespace:      "test-namespace", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"uninstall", "testPackage", "--namespace", "test-namespace"}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:             ".", | ||||
| 					DeploymentName:        "testPackage", | ||||
| 					Namespace:             "test-namespace", | ||||
| 					HelmDeployWaitSeconds: 524, | ||||
| 					DryRun:                true, | ||||
| 				}, | ||||
| 				expectedConfig: []string{"uninstall", "testPackage", "--namespace", "test-namespace", "--wait", "--timeout", "524s", "--dry-run"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for i, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmUninstall() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[i]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm package command", func(t *testing.T) { | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:      ".", | ||||
| 					DeploymentName: "testPackage", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"package", "."}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:        ".", | ||||
| 					DeploymentName:   "testPackage", | ||||
| 					PackageVersion:   "1.2.3", | ||||
| 					DependencyUpdate: true, | ||||
| 					AppVersion:       "9.8.7", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"package", ".", "--version", "1.2.3", "--dependency-update", "--app-version", "9.8.7"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for i, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmPackage() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[i]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm test command", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:      ".", | ||||
| 					DeploymentName: "testPackage", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"test", "."}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:      ".", | ||||
| 					DeploymentName: "testPackage", | ||||
| 					FilterTest:     "name=test1,name=test2", | ||||
| 					DumpLogs:       true, | ||||
| 				}, | ||||
| 				expectedConfig: []string{"test", ".", "--filter", "name=test1,name=test2", "--logs"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for i, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmTest() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[i]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm unistall command(error processing)", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config        HelmExecuteOptions | ||||
| 			expectedError error | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:      ".", | ||||
| 					DeploymentName: "testPackage", | ||||
| 				}, | ||||
| 				expectedError: errors.New("namespace has not been set, please configure namespace parameter"), | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for _, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmUninstall() | ||||
| 			if testCase.expectedError != nil { | ||||
| 				assert.Error(t, err) | ||||
| 				assert.Equal(t, testCase.expectedError, err) | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm init", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config        HelmExecuteOptions | ||||
| 			expectedError error | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					ChartPath:      ".", | ||||
| 					Namespace:      "test-namespace", | ||||
| 					DeploymentName: "testPackage", | ||||
| 					KubeContext:    "kubeContext", | ||||
| 					KubeConfig:     "kubeConfig", | ||||
| 				}, | ||||
| 				expectedError: nil, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for _, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.runHelmInit() | ||||
| 			if testCase.expectedError != nil { | ||||
| 				assert.Error(t, err) | ||||
| 				assert.Equal(t, testCase.expectedError, err) | ||||
| 			} else { | ||||
| 				assert.NoError(t, err) | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm registry login command", func(t *testing.T) { | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					HelmRegistryUser: "helmRegistryUser", | ||||
| 					HelmChartServer:  "localhost:5000", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"registry login", "-u", "helmRegistryUser", "localhost:5000"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for i, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmRegistryLogin() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[i]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Helm registry login command", func(t *testing.T) { | ||||
| 		utils := newHelmMockUtilsBundle() | ||||
|  | ||||
| 		testTable := []struct { | ||||
| 			config         HelmExecuteOptions | ||||
| 			expectedConfig []string | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					DeploymentName:  "nginx", | ||||
| 					PackageVersion:  "2.3.4", | ||||
| 					HelmChartServer: "localhost:5000", | ||||
| 				}, | ||||
| 				expectedConfig: []string{"push", "nginx2.3.4.tgz", "oci://localhost:5000/helm-charts"}, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for _, testCase := range testTable { | ||||
| 			helmExecute := HelmExecute{ | ||||
| 				utils:   utils, | ||||
| 				config:  testCase.config, | ||||
| 				verbose: false, | ||||
| 				stdout:  log.Writer(), | ||||
| 			} | ||||
| 			err := helmExecute.RunHelmPush() | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, mock.ExecCall{Exec: "helm", Params: testCase.expectedConfig}, utils.Calls[1]) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| } | ||||
							
								
								
									
										150
									
								
								pkg/kubernetes/mocks/HelmExecutor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								pkg/kubernetes/mocks/HelmExecutor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| // Code generated by mockery v2.10.0. DO NOT EDIT. | ||||
|  | ||||
| package mocks | ||||
|  | ||||
| import mock "github.com/stretchr/testify/mock" | ||||
|  | ||||
| // HelmExecutor is an autogenerated mock type for the HelmExecutor type | ||||
| type HelmExecutor struct { | ||||
| 	mock.Mock | ||||
| } | ||||
|  | ||||
| // RunHelmAdd provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmAdd() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmInstall provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmInstall() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmLint provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmLint() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmPackage provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmPackage() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmPush provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmPush() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmRegistryLogin provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmRegistryLogin() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmRegistryLogout provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmRegistryLogout() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmTest provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmTest() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmUninstall provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmUninstall() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // RunHelmUpgrade provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmUpgrade() error { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
							
								
								
									
										89
									
								
								pkg/kubernetes/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								pkg/kubernetes/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| package kubernetes | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/command" | ||||
| 	"github.com/SAP/jenkins-library/pkg/docker" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperutils" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| ) | ||||
|  | ||||
| // DeployUtils interface | ||||
| type DeployUtils interface { | ||||
| 	SetEnv(env []string) | ||||
| 	Stdout(out io.Writer) | ||||
| 	Stderr(err io.Writer) | ||||
| 	RunExecutable(e string, p ...string) error | ||||
|  | ||||
| 	piperutils.FileUtils | ||||
| } | ||||
|  | ||||
| // deployUtilsBundle struct  for utils | ||||
| type deployUtilsBundle struct { | ||||
| 	*command.Command | ||||
| 	*piperutils.Files | ||||
| } | ||||
|  | ||||
| // NewDeployUtilsBundle initialize using deployUtilsBundle struct | ||||
| func NewDeployUtilsBundle() DeployUtils { | ||||
| 	utils := deployUtilsBundle{ | ||||
| 		Command: &command.Command{ | ||||
| 			ErrorCategoryMapping: map[string][]string{ | ||||
| 				log.ErrorConfiguration.String(): { | ||||
| 					"Error: Get * no such host", | ||||
| 					"Error: path * not found", | ||||
| 					"Error: rendered manifests contain a resource that already exists.", | ||||
| 					"Error: unknown flag", | ||||
| 					"Error: UPGRADE FAILED: * failed to replace object: * is invalid", | ||||
| 					"Error: UPGRADE FAILED: * failed to create resource: * is invalid", | ||||
| 					"Error: UPGRADE FAILED: an error occurred * not found", | ||||
| 					"Error: UPGRADE FAILED: query: failed to query with labels:", | ||||
| 					"Invalid value: \"\": field is immutable", | ||||
| 				}, | ||||
| 				log.ErrorCustom.String(): { | ||||
| 					"Error: release * failed, * timed out waiting for the condition", | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Files: &piperutils.Files{}, | ||||
| 	} | ||||
| 	// reroute stderr output to logging framework, stdout will be used for command interactions | ||||
| 	utils.Stderr(log.Writer()) | ||||
| 	return &utils | ||||
| } | ||||
|  | ||||
| func getContainerInfo(config HelmExecuteOptions) (map[string]string, error) { | ||||
| 	var err error | ||||
| 	containerRegistry, err := docker.ContainerRegistryFromURL(config.ContainerRegistryURL) | ||||
| 	if err != nil { | ||||
| 		log.Entry().WithError(err).Fatalf("Container registry url '%v' incorrect", config.ContainerRegistryURL) | ||||
| 	} | ||||
|  | ||||
| 	//support either image or containerImageName and containerImageTag | ||||
| 	containerInfo := map[string]string{ | ||||
| 		"containerImageName": "", | ||||
| 		"containerImageTag":  "", | ||||
| 		"containerRegistry":  containerRegistry, | ||||
| 	} | ||||
|  | ||||
| 	if len(config.Image) > 0 { | ||||
| 		ref, err := docker.ContainerImageNameTagFromImage(config.Image) | ||||
| 		if err != nil { | ||||
| 			log.Entry().WithError(err).Fatalf("Container image '%v' incorrect", config.Image) | ||||
| 		} | ||||
| 		parts := strings.Split(ref, ":") | ||||
| 		containerInfo["containerImageName"] = parts[0] | ||||
| 		containerInfo["containerImageTag"] = parts[1] | ||||
| 	} else if len(config.ContainerImageName) > 0 && len(config.ContainerImageTag) > 0 { | ||||
| 		containerInfo["containerImageName"] = config.ContainerImageName | ||||
| 		containerInfo["containerImageTag"] = config.ContainerImageTag | ||||
| 	} else { | ||||
| 		return nil, fmt.Errorf("image information not given - please either set image or containerImageName and containerImageTag") | ||||
| 	} | ||||
|  | ||||
| 	return containerInfo, nil | ||||
| } | ||||
							
								
								
									
										39
									
								
								pkg/kubernetes/utils_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								pkg/kubernetes/utils_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| package kubernetes | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestRunUtils(t *testing.T) { | ||||
| 	t.Run("Get container info", func(t *testing.T) { | ||||
| 		testTable := []struct { | ||||
| 			config                HelmExecuteOptions | ||||
| 			expectedContainerInfo map[string]string | ||||
| 			expectedError         error | ||||
| 		}{ | ||||
| 			{ | ||||
| 				config: HelmExecuteOptions{ | ||||
| 					Image:                "dtzar/helm-kubectl:3.4.1", | ||||
| 					ContainerImageName:   "", | ||||
| 					ContainerImageTag:    "", | ||||
| 					ContainerRegistryURL: "https://hub.docker.com/", | ||||
| 				}, | ||||
| 				expectedContainerInfo: map[string]string{ | ||||
| 					"containerImageName": "dtzar/helm-kubectl", | ||||
| 					"containerImageTag":  "3.4.1", | ||||
| 				}, | ||||
| 				expectedError: nil, | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		for _, testCase := range testTable { | ||||
| 			containerInfo, err := getContainerInfo(testCase.config) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, testCase.expectedContainerInfo["containerImageName"], containerInfo["containerImageName"]) | ||||
| 			assert.Equal(t, testCase.expectedContainerInfo["containerImageTag"], containerInfo["containerImageTag"]) | ||||
|  | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										328
									
								
								resources/metadata/helmExecute.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								resources/metadata/helmExecute.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,328 @@ | ||||
| metadata: | ||||
|   name: helmExecute | ||||
|   description: Executes helm3 functionality as the package manager for Kubernetes. | ||||
|   longDescription: |- | ||||
|     Alpha version: please expect incompatible changes | ||||
|  | ||||
|     Executes helm functionality as the package manager for Kubernetes. | ||||
|  | ||||
|     * [Helm](https://helm.sh/)  is the package manager for Kubernetes. | ||||
|     * [Helm documentation https://helm.sh/docs/intro/using_helm/ and best practies https://helm.sh/docs/chart_best_practices/conventions/] | ||||
|     * [Helm Charts] (https://artifacthub.io/) | ||||
|     ``` | ||||
|     Available Commands: | ||||
|       install     install a chart | ||||
|       lint        examine a chart for possible issues | ||||
|       package     package a chart directory into a chart archive | ||||
|       repo        add, list, remove, update, and index chart repositories | ||||
|       test        run tests for a release | ||||
|       uninstall   uninstall a release | ||||
|       upgrade     upgrade a release | ||||
|       verify      verify that a chart at the given path has been signed and is valid | ||||
|       push        upload a chart to a registry | ||||
|  | ||||
|       also piper Execute step supports direct execution helm command via one flag. | ||||
|     ``` | ||||
|  | ||||
|     Note: piper supports only helm3 version, since helm2 is deprecated. | ||||
| spec: | ||||
|   inputs: | ||||
|     secrets: | ||||
|       - name: dockerCredentialsId | ||||
|         type: jenkins | ||||
|       - name: dockerConfigJsonCredentialsId | ||||
|         description: Jenkins 'Secret file' credentials ID containing Docker config.json (with registry credential(s)). | ||||
|         type: jenkins | ||||
|     resources: | ||||
|       - name: deployDescriptor | ||||
|         type: stash | ||||
|     params: | ||||
|       - name: additionalParameters | ||||
|         aliases: | ||||
|           - name: helmDeploymentParameters | ||||
|         type: "[]string" | ||||
|         description: Defines additional parameters for Helm like  "helm install [NAME] [CHART] [flags]". | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: chartPath | ||||
|         aliases: | ||||
|           - name: helmChartPath | ||||
|         type: string | ||||
|         mandatory: true | ||||
|         description: Defines the chart path for helm. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: containerRegistryPassword | ||||
|         description: Password for container registry access - typically provided by the CI/CD environment. | ||||
|         type: string | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         secret: true | ||||
|         resourceRef: | ||||
|           - name: dockerCredentialsId | ||||
|             type: secret | ||||
|             param: password | ||||
|           - name: commonPipelineEnvironment | ||||
|             param: custom/repositoryPassword | ||||
|       - name: containerImageName | ||||
|         aliases: | ||||
|           - name: dockerImageName | ||||
|         type: string | ||||
|         description: Name of the container which will be built - will be used together with `containerImageTag` instead of parameter `containerImage` | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: containerImageTag | ||||
|         aliases: | ||||
|           - name: artifactVersion | ||||
|         type: string | ||||
|         description: Tag of the container which will be built - will be used together with `containerImageName` instead of parameter `containerImage` | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         resourceRef: | ||||
|           - name: commonPipelineEnvironment | ||||
|             param: artifactVersion | ||||
|       - name: containerRegistryUrl | ||||
|         aliases: | ||||
|           - name: dockerRegistryUrl | ||||
|         type: string | ||||
|         description: http(s) url of the Container registry where the image to deploy is located. | ||||
|         resourceRef: | ||||
|           - name: commonPipelineEnvironment | ||||
|             param: container/registryUrl | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         mandatory: true | ||||
|       - name: containerRegistryUser | ||||
|         description: Username for container registry access - typically provided by the CI/CD environment. | ||||
|         type: string | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         secret: true | ||||
|         resourceRef: | ||||
|           - name: dockerCredentialsId | ||||
|             type: secret | ||||
|             param: username | ||||
|           - name: commonPipelineEnvironment | ||||
|             param: custom/repositoryUsername | ||||
|       - name: containerRegistrySecret | ||||
|         description: Name of the container registry secret used for pulling containers from the registry. | ||||
|         longDescription: |- | ||||
|           Name of the container registry secret used for pulling containers from the registry. | ||||
|  | ||||
|           If `containerRegistryUser` and `containerRegistryPassword` are provided, a secret is created on the fly and the information is passed to the helm template.<br /> | ||||
|  | ||||
|           If neither `containerRegistryUser` nor `containerRegistryPassword` are provided, it is expected that a secret with the configured name exists in the target Kubernetes cluster.<br /> | ||||
|         type: string | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         default: regsecret | ||||
|       - name: deploymentName | ||||
|         aliases: | ||||
|           - name: helmDeploymentName | ||||
|         type: string | ||||
|         description: Defines the name of the deployment. It is a mandatory parameter when deploying with helm. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: helmDeployWaitSeconds | ||||
|         type: int | ||||
|         description: Number of seconds before helm deploy returns. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         default: 300 | ||||
|       - name: helmValues | ||||
|         type: "[]string" | ||||
|         description: List of helm values as YAML file reference or URL (as per helm parameter description for `-f` / `--values`) | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: image | ||||
|         aliases: | ||||
|           - name: deployImage | ||||
|         type: string | ||||
|         description: Full name of the image to be deployed. | ||||
|         resourceRef: | ||||
|           - name: commonPipelineEnvironment | ||||
|             param: container/imageNameTag | ||||
|         mandatory: true | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: keepFailedDeployments | ||||
|         type: bool | ||||
|         description: Defines whether a failed deployment will be purged | ||||
|         default: false | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: kubeConfig | ||||
|         type: string | ||||
|         description: Defines the path to the "kubeconfig" file. | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         secret: true | ||||
|         resourceRef: | ||||
|           - name: kubeConfigFileCredentialsId | ||||
|             type: secret | ||||
|           - type: vaultSecretFile | ||||
|             name: kubeConfigFileVaultSecretName | ||||
|             default: kube-config | ||||
|       - name: kubeContext | ||||
|         type: string | ||||
|         description: Defines the context to use from the "kubeconfig" file. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: namespace | ||||
|         aliases: | ||||
|           - name: helmDeploymentNamespace | ||||
|         type: string | ||||
|         description: Defines the target Kubernetes namespace for the deployment. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         default: default | ||||
|       - name: dockerConfigJSON | ||||
|         type: string | ||||
|         description: Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/). | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         secret: true | ||||
|         resourceRef: | ||||
|           - name: dockerConfigJsonCredentialsId | ||||
|             type: secret | ||||
|           - type: vaultSecretFile | ||||
|             name: dockerConfigFileVaultSecretName | ||||
|             default: docker-config | ||||
|       - name: helmCommand | ||||
|         type: string | ||||
|         description: "Helm: defines the command `install`, `lint`, `package`, `test`, `upgrade` and etc." | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         mandatory: true | ||||
|         possibleValues: | ||||
|           - upgrade | ||||
|           - install | ||||
|           - lint | ||||
|           - test | ||||
|           - uninstall | ||||
|           - package | ||||
|           - push | ||||
|       - name: dryRun | ||||
|         type: bool | ||||
|         description: simulate execute command, like simulate an install | ||||
|         default: false | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: packageVersion | ||||
|         type: string | ||||
|         description: set the version on the chart to this semver version | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: appVersion | ||||
|         type: string | ||||
|         description: set the appVersion on the chart to this version | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: dependencyUpdate | ||||
|         type: bool | ||||
|         description: set the appVersion on the chart to this version | ||||
|         default: false | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: dumpLogs | ||||
|         type: bool | ||||
|         description: dump the logs from test pods (this runs after all tests are complete, but before any cleanup) | ||||
|         default: false | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: filterTest | ||||
|         type: string | ||||
|         description: specify tests by attribute (currently `name`) using attribute=value syntax or `!attribute=value` to exclude a test (can specify multiple or separate values with commas `name=test1,name=test2`) | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: chartRepo | ||||
|         type: string | ||||
|         description: set the chart repository | ||||
|         default: "https://charts.helm.sh/stable" | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: helmRegistryUser | ||||
|         type: string | ||||
|         description: set the user for login to helm registry | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|       - name: helmChartServer | ||||
|         type: string | ||||
|         description: set chart server for pushing chart | ||||
|         default: "localhost:5000" | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|   containers: | ||||
|     - image: dtzar/helm-kubectl:3.4.1 | ||||
|       workingDir: /config | ||||
|       options: | ||||
|         - name: -u | ||||
|           value: "0" | ||||
		Reference in New Issue
	
	Block a user