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 
			
		
		
		
	Cleanup outdated blue green support for cf native build tools (#4965)
* Remove blue green deployment support for cf native build tools * Empty for testing * Remove obsolete dependency * feedback from code review * Fix IT's run * Add test
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							e2f1c13b75
						
					
				
				
					commit
					4a4c13ff03
				
			| @@ -2,14 +2,11 @@ package cmd | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| @@ -19,7 +16,6 @@ import ( | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperutils" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"github.com/SAP/jenkins-library/pkg/yaml" | ||||
| 	"github.com/elliotchance/orderedmap" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| @@ -57,10 +53,6 @@ func cfLogout(c command.ExecRunner) error { | ||||
| 	return cf.Logout() | ||||
| } | ||||
|  | ||||
| const defaultSmokeTestScript = `#!/usr/bin/env bash | ||||
| # this is simply testing if the application root returns HTTP STATUS_CODE | ||||
| curl -so /dev/null -w '%{response_code}' https://$1 | grep $STATUS_CODE` | ||||
|  | ||||
| func cloudFoundryDeploy(config cloudFoundryDeployOptions, telemetryData *telemetry.CustomData, influxData *cloudFoundryDeployInflux) { | ||||
| 	// for command execution use Command | ||||
| 	c := command.Command{} | ||||
| @@ -226,46 +218,33 @@ func handleMTADeployment(config *cloudFoundryDeployOptions, command command.Exec | ||||
| } | ||||
|  | ||||
| type deployConfig struct { | ||||
| 	DeployCommand   string | ||||
| 	DeployOptions   []string | ||||
| 	AppName         string | ||||
| 	ManifestFile    string | ||||
| 	SmokeTestScript []string | ||||
| 	DeployCommand string | ||||
| 	DeployOptions []string | ||||
| 	AppName       string | ||||
| 	ManifestFile  string | ||||
| } | ||||
|  | ||||
| func handleCFNativeDeployment(config *cloudFoundryDeployOptions, command command.ExecRunner) error { | ||||
|  | ||||
| 	deployType, err := checkAndUpdateDeployTypeForNotSupportedManifest(config) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	var deployCommand string | ||||
| 	var smokeTestScript []string | ||||
| 	var deployOptions []string | ||||
| 	var err error | ||||
|  | ||||
| 	// deploy command will be provided by the prepare functions below | ||||
|  | ||||
| 	if deployType == "blue-green" { | ||||
| 		log.Entry().Warn("[WARN] Blue-green deployment type is deprecated for cf native builds " + | ||||
| 			"and will be completely removed by 15.06.2024" + | ||||
| 	if config.DeployType == "blue-green" { | ||||
| 		return fmt.Errorf("Blue-green deployment type is deprecated for cf native builds." + | ||||
| 			"Instead set parameter `cfNativeDeployParameters: '--strategy rolling'`. " + | ||||
| 			"Please refer to the Cloud Foundry documentation for further information: " + | ||||
| 			"https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html." + | ||||
| 			"Or alternatively, switch to mta build tool. Please refer to mta build tool" + | ||||
| 			"documentation for further information: https://sap.github.io/cloud-mta-build-tool/configuration/.") | ||||
| 		deployCommand, deployOptions, smokeTestScript, err = prepareBlueGreenCfNativeDeploy(config) | ||||
| 	} else if config.DeployType == "standard" { | ||||
| 		deployCommand, deployOptions, err = prepareCfPushCfNativeDeploy(config) | ||||
| 		if err != nil { | ||||
| 			return errors.Wrapf(err, "Cannot prepare cf native deployment. DeployType '%s'", deployType) | ||||
| 		} | ||||
| 	} else if deployType == "standard" { | ||||
| 		deployCommand, deployOptions, smokeTestScript, err = prepareCfPushCfNativeDeploy(config) | ||||
| 		if err != nil { | ||||
| 			return errors.Wrapf(err, "Cannot prepare cf push native deployment. DeployType '%s'", deployType) | ||||
| 			return errors.Wrapf(err, "Cannot prepare cf push native deployment. DeployType '%s'", config.DeployType) | ||||
| 		} | ||||
| 	} else { | ||||
| 		return fmt.Errorf("Invalid deploy type received: '%s'. Supported values: %v", deployType, []string{"blue-green", "standard"}) | ||||
| 		return fmt.Errorf("Invalid deploy type received: '%s'. Supported value: standard", config.DeployType) | ||||
| 	} | ||||
|  | ||||
| 	appName, err := getAppName(config) | ||||
| @@ -281,22 +260,18 @@ func handleCFNativeDeployment(config *cloudFoundryDeployOptions, command command | ||||
| 	log.Entry().Infof("cfManifestVariables: '%v'", config.ManifestVariables) | ||||
| 	log.Entry().Infof("cfManifestVariablesFiles: '%v'", config.ManifestVariablesFiles) | ||||
| 	log.Entry().Infof("cfdeployDockerImage: '%s'", config.DeployDockerImage) | ||||
| 	log.Entry().Infof("smokeTestScript: '%s'", config.SmokeTestScript) | ||||
|  | ||||
| 	additionalEnvironment := []string{ | ||||
| 		"STATUS_CODE=" + strconv.FormatInt(int64(config.SmokeTestStatusCode), 10), | ||||
| 	} | ||||
| 	var additionalEnvironment []string | ||||
|  | ||||
| 	if len(config.DockerPassword) > 0 { | ||||
| 		additionalEnvironment = append(additionalEnvironment, "CF_DOCKER_PASSWORD="+config.DockerPassword) | ||||
| 		additionalEnvironment = []string{("CF_DOCKER_PASSWORD=" + config.DockerPassword)} | ||||
| 	} | ||||
|  | ||||
| 	myDeployConfig := deployConfig{ | ||||
| 		DeployCommand:   deployCommand, | ||||
| 		DeployOptions:   deployOptions, | ||||
| 		AppName:         config.AppName, | ||||
| 		ManifestFile:    config.Manifest, | ||||
| 		SmokeTestScript: smokeTestScript, | ||||
| 		DeployCommand: deployCommand, | ||||
| 		DeployOptions: deployOptions, | ||||
| 		AppName:       config.AppName, | ||||
| 		ManifestFile:  config.Manifest, | ||||
| 	} | ||||
|  | ||||
| 	log.Entry().Infof("DeployConfig: %v", myDeployConfig) | ||||
| @@ -323,55 +298,19 @@ func deployCfNative(deployConfig deployConfig, config *cloudFoundryDeployOptions | ||||
| 		deployStatement = append(deployStatement, deployConfig.ManifestFile) | ||||
| 	} | ||||
|  | ||||
| 	if len(config.DeployDockerImage) > 0 && config.DeployType != "blue-green" { | ||||
| 	if len(config.DeployDockerImage) > 0 { | ||||
| 		deployStatement = append(deployStatement, "--docker-image", config.DeployDockerImage) | ||||
| 	} | ||||
|  | ||||
| 	if len(config.DockerUsername) > 0 && config.DeployType != "blue-green" { | ||||
| 	if len(config.DockerUsername) > 0 { | ||||
| 		deployStatement = append(deployStatement, "--docker-username", config.DockerUsername) | ||||
| 	} | ||||
|  | ||||
| 	if len(deployConfig.SmokeTestScript) > 0 { | ||||
| 		deployStatement = append(deployStatement, deployConfig.SmokeTestScript...) | ||||
| 	} | ||||
|  | ||||
| 	if len(config.CfNativeDeployParameters) > 0 { | ||||
| 		deployStatement = append(deployStatement, strings.Fields(config.CfNativeDeployParameters)...) | ||||
| 	} | ||||
|  | ||||
| 	stopOldAppIfRunning := func(_cmd command.ExecRunner) error { | ||||
|  | ||||
| 		if config.KeepOldInstance && config.DeployType == "blue-green" { | ||||
| 			oldAppName := deployConfig.AppName + "-old" | ||||
|  | ||||
| 			var buff bytes.Buffer | ||||
|  | ||||
| 			_cmd.Stdout(&buff) | ||||
|  | ||||
| 			defer func() { | ||||
| 				_cmd.Stdout(log.Writer()) | ||||
| 			}() | ||||
|  | ||||
| 			err := _cmd.RunExecutable("cf", "stop", oldAppName) | ||||
|  | ||||
| 			if err != nil { | ||||
|  | ||||
| 				cfStopLog := buff.String() | ||||
|  | ||||
| 				if !strings.Contains(cfStopLog, oldAppName+" not found") { | ||||
| 					return fmt.Errorf("Could not stop application '%s'. Error: %s", oldAppName, cfStopLog) | ||||
| 				} | ||||
| 				log.Entry().Infof("Cannot stop application '%s' since this appliation was not found.", oldAppName) | ||||
|  | ||||
| 			} else { | ||||
| 				log.Entry().Infof("Old application '%s' has been stopped.", oldAppName) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return cfDeploy(config, deployStatement, additionalEnvironment, stopOldAppIfRunning, cmd) | ||||
| 	return cfDeploy(config, deployStatement, additionalEnvironment, cmd) | ||||
| } | ||||
|  | ||||
| func getManifest(name string) (cloudfoundry.Manifest, error) { | ||||
| @@ -392,9 +331,7 @@ func getAppName(config *cloudFoundryDeployOptions) (string, error) { | ||||
| 	if len(config.AppName) > 0 { | ||||
| 		return config.AppName, nil | ||||
| 	} | ||||
| 	if config.DeployType == "blue-green" { | ||||
| 		return "", fmt.Errorf("Blue-green plugin requires app name to be passed (see https://github.com/bluemixgaragelondon/cf-blue-green-deploy/issues/27)") | ||||
| 	} | ||||
|  | ||||
| 	manifestFile, err := getManifestFileName(config) | ||||
|  | ||||
| 	fileExists, err := fileUtils.FileExists(manifestFile) | ||||
| @@ -438,153 +375,12 @@ func getAppName(config *cloudFoundryDeployOptions) (string, error) { | ||||
| 	return name, nil | ||||
| } | ||||
|  | ||||
| func handleSmokeTestScript(smokeTestScript string) ([]string, error) { | ||||
|  | ||||
| 	if smokeTestScript == "blueGreenCheckScript.sh" { | ||||
| 		// what should we do if there is already a script with the given name? Should we really overwrite ... | ||||
| 		err := fileUtils.FileWrite(smokeTestScript, []byte(defaultSmokeTestScript), 0755) | ||||
| 		if err != nil { | ||||
| 			return []string{}, fmt.Errorf("failed to write default smoke-test script: %w", err) | ||||
| 		} | ||||
| 		log.Entry().Debugf("smoke test script '%s' has been written.", smokeTestScript) | ||||
| 	} | ||||
|  | ||||
| 	if len(smokeTestScript) > 0 { | ||||
| 		err := fileUtils.Chmod(smokeTestScript, 0755) | ||||
| 		if err != nil { | ||||
| 			return []string{}, fmt.Errorf("failed to make smoke-test script executable: %w", err) | ||||
| 		} | ||||
| 		pwd, err := fileUtils.Getwd() | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return []string{}, fmt.Errorf("failed to get current working directory for execution of smoke-test script: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		return []string{"--smoke-test", filepath.Join(pwd, smokeTestScript)}, nil | ||||
| 	} | ||||
| 	return []string{}, nil | ||||
| } | ||||
|  | ||||
| func prepareBlueGreenCfNativeDeploy(config *cloudFoundryDeployOptions) (string, []string, []string, error) { | ||||
|  | ||||
| 	smokeTest, err := handleSmokeTestScript(config.SmokeTestScript) | ||||
| 	if err != nil { | ||||
| 		return "", []string{}, []string{}, err | ||||
| 	} | ||||
|  | ||||
| 	var deployOptions = []string{} | ||||
|  | ||||
| 	if !config.KeepOldInstance { | ||||
| 		deployOptions = append(deployOptions, "--delete-old-apps") | ||||
| 	} | ||||
|  | ||||
| 	manifestFile, err := getManifestFileName(config) | ||||
|  | ||||
| 	manifestFileExists, err := fileUtils.FileExists(manifestFile) | ||||
| 	if err != nil { | ||||
| 		return "", []string{}, []string{}, errors.Wrapf(err, "Cannot check if file '%s' exists", manifestFile) | ||||
| 	} | ||||
|  | ||||
| 	if !manifestFileExists { | ||||
|  | ||||
| 		log.Entry().Infof("Manifest file '%s' does not exist", manifestFile) | ||||
|  | ||||
| 	} else { | ||||
|  | ||||
| 		manifestVariables, err := toStringInterfaceMap(toParameterMap(config.ManifestVariables)) | ||||
| 		if err != nil { | ||||
| 			return "", []string{}, []string{}, errors.Wrapf(err, "Cannot prepare manifest variables: '%v'", config.ManifestVariables) | ||||
| 		} | ||||
|  | ||||
| 		manifestVariablesFiles, err := validateManifestVariablesFiles(config.ManifestVariablesFiles) | ||||
| 		if err != nil { | ||||
| 			return "", []string{}, []string{}, errors.Wrapf(err, "Cannot validate manifest variables files '%v'", config.ManifestVariablesFiles) | ||||
| 		} | ||||
|  | ||||
| 		modified, err := _replaceVariables(manifestFile, manifestVariables, manifestVariablesFiles) | ||||
| 		if err != nil { | ||||
| 			return "", []string{}, []string{}, errors.Wrap(err, "Cannot prepare manifest file") | ||||
| 		} | ||||
|  | ||||
| 		if modified { | ||||
| 			log.Entry().Infof("Manifest file '%s' has been updated (variable substitution)", manifestFile) | ||||
| 		} else { | ||||
| 			log.Entry().Infof("Manifest file '%s' has not been updated (no variable substitution)", manifestFile) | ||||
| 		} | ||||
|  | ||||
| 		err = handleLegacyCfManifest(manifestFile) | ||||
| 		if err != nil { | ||||
| 			return "", []string{}, []string{}, errors.Wrapf(err, "Cannot handle legacy manifest '%s'", manifestFile) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return "blue-green-deploy", deployOptions, smokeTest, nil | ||||
| } | ||||
|  | ||||
| // validateManifestVariablesFiles: in case the only provided file is 'manifest-variables.yml' and this file does not | ||||
| // exist we ignore that file. For any other file there is no check if that file exists. In case several files are | ||||
| // provided we also do not check for the default file 'manifest-variables.yml' | ||||
| func validateManifestVariablesFiles(manifestVariablesFiles []string) ([]string, error) { | ||||
|  | ||||
| 	const defaultManifestVariableFileName = "manifest-variables.yml" | ||||
| 	if len(manifestVariablesFiles) == 1 && manifestVariablesFiles[0] == defaultManifestVariableFileName { | ||||
| 		// we have only the default file. Most likely this is not configured, but we simply have the default. | ||||
| 		// In case this file does not exist we ignore that file. | ||||
| 		exists, err := fileUtils.FileExists(defaultManifestVariableFileName) | ||||
| 		if err != nil { | ||||
| 			return []string{}, errors.Wrapf(err, "Cannot check if file '%s' exists", defaultManifestVariableFileName) | ||||
| 		} | ||||
| 		if !exists { | ||||
| 			return []string{}, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return manifestVariablesFiles, nil | ||||
| } | ||||
|  | ||||
| func toParameterMap(parameters []string) (*orderedmap.OrderedMap, error) { | ||||
|  | ||||
| 	parameterMap := orderedmap.NewOrderedMap() | ||||
|  | ||||
| 	for _, p := range parameters { | ||||
| 		keyVal := strings.Split(p, "=") | ||||
| 		if len(keyVal) != 2 { | ||||
| 			return nil, fmt.Errorf("Invalid parameter provided (expected format <key>=<val>: '%s'", p) | ||||
| 		} | ||||
| 		parameterMap.Set(keyVal[0], keyVal[1]) | ||||
| 	} | ||||
| 	return parameterMap, nil | ||||
| } | ||||
|  | ||||
| func handleLegacyCfManifest(manifestFile string) error { | ||||
| 	manifest, err := _getManifest(manifestFile) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = manifest.Transform() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if manifest.IsModified() { | ||||
|  | ||||
| 		err = manifest.WriteManifest() | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		log.Entry().Infof("Manifest file '%s' was in legacy format has been transformed and updated.", manifestFile) | ||||
| 	} else { | ||||
| 		log.Entry().Debugf("Manifest file '%s' was not in legacy format. No transformation needed, no update performed.", manifestFile) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func prepareCfPushCfNativeDeploy(config *cloudFoundryDeployOptions) (string, []string, []string, error) { | ||||
| func prepareCfPushCfNativeDeploy(config *cloudFoundryDeployOptions) (string, []string, error) { | ||||
|  | ||||
| 	deployOptions := []string{} | ||||
| 	varOptions, err := _getVarsOptions(config.ManifestVariables) | ||||
| 	if err != nil { | ||||
| 		return "", []string{}, []string{}, errors.Wrapf(err, "Cannot prepare var-options: '%v'", config.ManifestVariables) | ||||
| 		return "", []string{}, errors.Wrapf(err, "Cannot prepare var-options: '%v'", config.ManifestVariables) | ||||
| 	} | ||||
|  | ||||
| 	varFileOptions, err := _getVarsFileOptions(config.ManifestVariablesFiles) | ||||
| @@ -594,73 +390,14 @@ func prepareCfPushCfNativeDeploy(config *cloudFoundryDeployOptions) (string, []s | ||||
| 				log.Entry().Warningf("We skip adding not-existing file '%s' as a vars-file to the cf create-service-push call", missingVarFile) | ||||
| 			} | ||||
| 		} else { | ||||
| 			return "", []string{}, []string{}, errors.Wrapf(err, "Cannot prepare var-file-options: '%v'", config.ManifestVariablesFiles) | ||||
| 			return "", []string{}, errors.Wrapf(err, "Cannot prepare var-file-options: '%v'", config.ManifestVariablesFiles) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	deployOptions = append(deployOptions, varOptions...) | ||||
| 	deployOptions = append(deployOptions, varFileOptions...) | ||||
|  | ||||
| 	return "push", deployOptions, []string{}, nil | ||||
| } | ||||
|  | ||||
| func toStringInterfaceMap(in *orderedmap.OrderedMap, err error) (map[string]interface{}, error) { | ||||
|  | ||||
| 	out := map[string]interface{}{} | ||||
|  | ||||
| 	if err == nil { | ||||
| 		for _, key := range in.Keys() { | ||||
| 			if k, ok := key.(string); ok { | ||||
| 				val, exists := in.Get(key) | ||||
| 				if exists { | ||||
| 					out[k] = val | ||||
| 				} else { | ||||
| 					return nil, fmt.Errorf("No entry found for '%v'", key) | ||||
| 				} | ||||
| 			} else { | ||||
| 				return nil, fmt.Errorf("Cannot cast key '%v' to string", key) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return out, err | ||||
| } | ||||
|  | ||||
| func checkAndUpdateDeployTypeForNotSupportedManifest(config *cloudFoundryDeployOptions) (string, error) { | ||||
|  | ||||
| 	manifestFile, err := getManifestFileName(config) | ||||
|  | ||||
| 	manifestFileExists, err := fileUtils.FileExists(manifestFile) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	if config.DeployType == "blue-green" && manifestFileExists { | ||||
|  | ||||
| 		manifest, _ := _getManifest(manifestFile) | ||||
|  | ||||
| 		apps, err := manifest.GetApplications() | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return "", fmt.Errorf("failed to obtain applications from manifest: %w", err) | ||||
| 		} | ||||
| 		if len(apps) > 1 { | ||||
| 			return "", fmt.Errorf("Your manifest contains more than one application. For blue green deployments your manifest file may contain only one application") | ||||
| 		} | ||||
|  | ||||
| 		hasNoRouteProperty, err := manifest.ApplicationHasProperty(0, "no-route") | ||||
| 		if err != nil { | ||||
| 			return "", errors.Wrap(err, "Failed to obtain 'no-route' property from manifest") | ||||
| 		} | ||||
| 		if len(apps) == 1 && hasNoRouteProperty { | ||||
|  | ||||
| 			const deployTypeStandard = "standard" | ||||
| 			log.Entry().Warningf("Blue green deployment is not possible for application without route. Using deployment type '%s' instead.", deployTypeStandard) | ||||
| 			return deployTypeStandard, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return config.DeployType, nil | ||||
| 	return "push", deployOptions, nil | ||||
| } | ||||
|  | ||||
| func deployMta(config *cloudFoundryDeployOptions, mtarFilePath string, command command.ExecRunner) error { | ||||
| @@ -706,7 +443,7 @@ func deployMta(config *cloudFoundryDeployOptions, mtarFilePath string, command c | ||||
|  | ||||
| 	cfDeployParams = append(cfDeployParams, extFileParams...) | ||||
|  | ||||
| 	err := cfDeploy(config, cfDeployParams, nil, nil, command) | ||||
| 	err := cfDeploy(config, cfDeployParams, nil, command) | ||||
|  | ||||
| 	for _, extFile := range extFiles { | ||||
| 		renameError := fileUtils.FileRename(extFile+".original", extFile) | ||||
| @@ -833,7 +570,6 @@ func cfDeploy( | ||||
| 	config *cloudFoundryDeployOptions, | ||||
| 	cfDeployParams []string, | ||||
| 	additionalEnvironment []string, | ||||
| 	postDeployAction func(command command.ExecRunner) error, | ||||
| 	command command.ExecRunner) error { | ||||
|  | ||||
| 	const cfLogFile = "cf.log" | ||||
| @@ -883,10 +619,6 @@ func cfDeploy( | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err == nil && postDeployAction != nil { | ||||
| 		err = postDeployAction(command) | ||||
| 	} | ||||
|  | ||||
| 	if loginPerformed { | ||||
|  | ||||
| 		logoutErr := _cfLogout(command) | ||||
|   | ||||
| @@ -42,8 +42,6 @@ type cloudFoundryDeployOptions struct { | ||||
| 	MtaPath                  string                 `json:"mtaPath,omitempty"` | ||||
| 	Org                      string                 `json:"org,omitempty"` | ||||
| 	Password                 string                 `json:"password,omitempty"` | ||||
| 	SmokeTestScript          string                 `json:"smokeTestScript,omitempty"` | ||||
| 	SmokeTestStatusCode      int                    `json:"smokeTestStatusCode,omitempty"` | ||||
| 	Space                    string                 `json:"space,omitempty"` | ||||
| 	Username                 string                 `json:"username,omitempty"` | ||||
| } | ||||
| @@ -214,10 +212,10 @@ func addCloudFoundryDeployFlags(cmd *cobra.Command, stepConfig *cloudFoundryDepl | ||||
| 	cmd.Flags().StringVar(&stepConfig.DeployDockerImage, "deployDockerImage", os.Getenv("PIPER_deployDockerImage"), "Docker image deployments are supported (via manifest file in general)[https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#docker]. If no manifest is used, this parameter defines the image to be deployed. The specified name of the image is passed to the `--docker-image` parameter of the cf CLI and must adhere it's naming pattern (e.g. REPO/IMAGE:TAG). See (cf CLI documentation)[https://docs.cloudfoundry.org/devguide/deploy-apps/push-docker.html] for details. Note: The used Docker registry must be visible for the targeted Cloud Foundry instance.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DeployTool, "deployTool", os.Getenv("PIPER_deployTool"), "Defines the tool which should be used for deployment.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.BuildTool, "buildTool", os.Getenv("PIPER_buildTool"), "Defines the tool which is used for building the artifact. If provided, `deployTool` is automatically derived from it. For MTA projects, `deployTool` defaults to `mtaDeployPlugin`. For other projects `cf_native` will be used.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DeployType, "deployType", `standard`, "Defines the type of deployment, either `standard` deployment which results in a system downtime or a zero-downtime `blue-green` deployment. If 'cf_native' as deployTool and 'blue-green' as deployType is used in combination, your manifest.yaml may only contain one application. If this application has the option 'no-route' active the deployType will be changed to 'standard'.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DeployType, "deployType", `standard`, "Defines the type of deployment, for example, `standard` deployment which results in a system downtime, `blue-green` deployment which results in zero downtime for mta deploy tool. - For mta build tool, possible values are `standard`, `blue-green` or `bg-deploy`. - For cf native build tools, possible value is `standard`. To eliminate system downtime, an alternative is to pass '--strategy rolling' to the parameter `cfNativeDeployParameters`.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DockerPassword, "dockerPassword", os.Getenv("PIPER_dockerPassword"), "If the specified image in `deployDockerImage` is contained in a Docker registry, which requires authorization, this defines the password to be used.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.DockerUsername, "dockerUsername", os.Getenv("PIPER_dockerUsername"), "If the specified image in `deployDockerImage` is contained in a Docker registry, which requires authorization, this defines the username to be used.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.KeepOldInstance, "keepOldInstance", false, "In case of a `blue-green` deployment the old instance will be deleted by default. If this option is set to true the old instance will remain stopped in the Cloud Foundry space.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.KeepOldInstance, "keepOldInstance", false, "If this option is set to true the old instance will remain stopped in the Cloud Foundry space.\"") | ||||
| 	cmd.Flags().StringVar(&stepConfig.LoginParameters, "loginParameters", os.Getenv("PIPER_loginParameters"), "Addition command line options for cf login command. No escaping/quoting is performed. Not recommended for productive environments.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Manifest, "manifest", os.Getenv("PIPER_manifest"), "Defines the manifest to be used for deployment to Cloud Foundry.") | ||||
| 	cmd.Flags().StringSliceVar(&stepConfig.ManifestVariables, "manifestVariables", []string{}, "Defines a list of variables in the form `key=value` which are used for variable substitution within the file given by manifest.") | ||||
| @@ -228,8 +226,6 @@ func addCloudFoundryDeployFlags(cmd *cobra.Command, stepConfig *cloudFoundryDepl | ||||
| 	cmd.Flags().StringVar(&stepConfig.MtaPath, "mtaPath", os.Getenv("PIPER_mtaPath"), "Defines the path to *.mtar for deployment with the mtaDeployPlugin") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Org, "org", os.Getenv("PIPER_org"), "Cloud Foundry target organization.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password") | ||||
| 	cmd.Flags().StringVar(&stepConfig.SmokeTestScript, "smokeTestScript", `blueGreenCheckScript.sh`, "Allows to specify a script which performs a check during blue-green deployment. The script gets the FQDN as parameter and returns `exit code 0` in case check returned `smokeTestStatusCode`. More details can be found [here](https://github.com/bluemixgaragelondon/cf-blue-green-deploy#how-to-use). Currently this option is only considered for deployTool `cf_native`.") | ||||
| 	cmd.Flags().IntVar(&stepConfig.SmokeTestStatusCode, "smokeTestStatusCode", 200, "Expected status code returned by the check.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Space, "space", os.Getenv("PIPER_space"), "Cloud Foundry target space") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User name used for deployment") | ||||
|  | ||||
| @@ -514,24 +510,6 @@ func cloudFoundryDeployMetadata() config.StepData { | ||||
| 						Aliases:   []config.Alias{}, | ||||
| 						Default:   os.Getenv("PIPER_password"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "smokeTestScript", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     `blueGreenCheckScript.sh`, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "smokeTestStatusCode", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"}, | ||||
| 						Type:        "int", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     200, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "space", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
|   | ||||
| @@ -6,7 +6,6 @@ package cmd | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| @@ -77,8 +76,7 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		Username:            "me", | ||||
| 		Password:            "******", | ||||
| 		APIEndpoint:         "https://examples.sap.com/cf", | ||||
| 		SmokeTestStatusCode: 200,            // default | ||||
| 		Manifest:            "manifest.yml", //default | ||||
| 		Manifest:            "manifest.yml", // default | ||||
| 		MtaDeployParameters: "-f",           // default | ||||
| 		DeployType:          "standard",     // default | ||||
| 	} | ||||
| @@ -167,73 +165,6 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		assert.EqualError(t, err, "Your application name 'a_z' contains a '_' (underscore) which is not allowed, only letters, dashes and numbers can be used. Please change the name to fit this requirement(s). For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings.") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Manifest substitution", func(t *testing.T) { | ||||
|  | ||||
| 		defer func() { | ||||
| 			cleanup() | ||||
| 			_replaceVariables = func(manifest string, replacements map[string]interface{}, replacementsFiles []string) (bool, error) { | ||||
| 				return false, nil | ||||
| 			} | ||||
| 		}() | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		var manifestForSubstitution string | ||||
| 		var replacements map[string]interface{} | ||||
| 		var replacementFiles []string | ||||
|  | ||||
| 		defer prepareDefaultManifestMocking("substitute-manifest.yml", []string{"testAppName"})() | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.AppName = "myApp" | ||||
| 		config.Manifest = "substitute-manifest.yml" | ||||
|  | ||||
| 		_replaceVariables = func(manifest string, _replacements map[string]interface{}, _replacementsFiles []string) (bool, error) { | ||||
| 			manifestForSubstitution = manifest | ||||
| 			replacements = _replacements | ||||
| 			replacementFiles = _replacementsFiles | ||||
| 			return false, nil | ||||
| 		} | ||||
|  | ||||
| 		t.Run("straight forward", func(t *testing.T) { | ||||
|  | ||||
| 			defer func() { | ||||
| 				config.ManifestVariables = []string{} | ||||
| 				config.ManifestVariablesFiles = []string{} | ||||
| 			}() | ||||
|  | ||||
| 			config.ManifestVariables = []string{"k1=v1"} | ||||
| 			config.ManifestVariablesFiles = []string{"myVars.yml"} | ||||
|  | ||||
| 			err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 			if assert.NoError(t, err) { | ||||
| 				assert.Equal(t, "substitute-manifest.yml", manifestForSubstitution) | ||||
| 				assert.Equal(t, map[string]interface{}{"k1": "v1"}, replacements) | ||||
| 				assert.Equal(t, []string{"myVars.yml"}, replacementFiles) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("empty", func(t *testing.T) { | ||||
|  | ||||
| 			defer func() { | ||||
| 				config.ManifestVariables = []string{} | ||||
| 				config.ManifestVariablesFiles = []string{} | ||||
| 			}() | ||||
|  | ||||
| 			config.ManifestVariables = []string{} | ||||
| 			config.ManifestVariablesFiles = []string{} | ||||
|  | ||||
| 			err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 			if assert.NoError(t, err) { | ||||
| 				assert.Equal(t, "substitute-manifest.yml", manifestForSubstitution) | ||||
| 				assert.Equal(t, map[string]interface{}{}, replacements) | ||||
| 				assert.Equal(t, []string{}, replacementFiles) | ||||
| 			} | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Invalid deploytool", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
| @@ -279,7 +210,6 @@ func TestCfDeployment(t *testing.T) { | ||||
| 			t.Run("check environment variables", func(t *testing.T) { | ||||
| 				assert.Contains(t, s.Env, "CF_HOME=/home/me1") | ||||
| 				assert.Contains(t, s.Env, "CF_PLUGIN_HOME=/home/me2") | ||||
| 				assert.Contains(t, s.Env, "STATUS_CODE=200") | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
| @@ -404,55 +334,7 @@ func TestCfDeployment(t *testing.T) { | ||||
| 			}) | ||||
|  | ||||
| 			t.Run("check environment variables", func(t *testing.T) { | ||||
| 				//REVISIT: in the corresponding groovy test we checked for "${'********'}" | ||||
| 				// I don't understand why, but we should discuss ... | ||||
| 				assert.Contains(t, s.Env, "CF_DOCKER_PASSWORD=********") | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("deploy cf native blue green with manifest and docker credentials", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		// Blue Green Deploy cf cli plugin does not support --docker-username and --docker-image parameters | ||||
| 		// docker username and docker image have to be set in the manifest file | ||||
| 		// if a private docker repository is used the CF_DOCKER_PASSWORD env variable must be set | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.DockerUsername = "test_cf_docker" | ||||
| 		config.DockerPassword = "********" | ||||
| 		config.AppName = "testAppName" | ||||
|  | ||||
| 		defer prepareDefaultManifestMocking("manifest.yml", []string{"testAppName"})() | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.NoError(t, err) { | ||||
|  | ||||
| 			t.Run("check shell calls", func(t *testing.T) { | ||||
|  | ||||
| 				withLoginAndLogout(t, func(t *testing.T) { | ||||
|  | ||||
| 					assert.Equal(t, []mock.ExecCall{ | ||||
| 						{Exec: "cf", Params: []string{"version"}}, | ||||
| 						{Exec: "cf", Params: []string{"plugins"}}, | ||||
| 						{Exec: "cf", Params: []string{ | ||||
| 							"blue-green-deploy", | ||||
| 							"testAppName", | ||||
| 							"--delete-old-apps", | ||||
| 							"-f", | ||||
| 							"manifest.yml", | ||||
| 						}}, | ||||
| 					}, s.Calls) | ||||
| 				}) | ||||
| 			}) | ||||
|  | ||||
| 			t.Run("check environment variables", func(t *testing.T) { | ||||
| 				//REVISIT: in the corresponding groovy test we checked for "${'********'}" | ||||
| 				// REVISIT: in the corresponding groovy test we checked for "${'********'}" | ||||
| 				// I don't understand why, but we should discuss ... | ||||
| 				assert.Contains(t, s.Env, "CF_DOCKER_PASSWORD=********") | ||||
| 			}) | ||||
| @@ -503,8 +385,8 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		config.Manifest = "" | ||||
| 		config.AppName = "" | ||||
|  | ||||
| 		//app name does not need to be set if it can be found in the manifest.yml | ||||
| 		//manifest name does not need to be set- the default manifest.yml will be used if not set | ||||
| 		// app name does not need to be set if it can be found in the manifest.yml | ||||
| 		// manifest name does not need to be set- the default manifest.yml will be used if not set | ||||
| 		defer prepareDefaultManifestMocking("manifest.yml", []string{"newAppName"})() | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
| @@ -530,6 +412,61 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("cf native deploy fail when deployType is blue-green", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.Manifest = "" | ||||
| 		config.AppName = "" | ||||
|  | ||||
| 		// app name does not need to be set if it can be found in the manifest.yml | ||||
| 		// manifest name does not need to be set- the default manifest.yml will be used if not set | ||||
| 		defer prepareDefaultManifestMocking("manifest.yml", []string{"newAppName"})() | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.EqualError(t, err, "Blue-green deployment type is deprecated for cf native builds."+ | ||||
| 			"Instead set parameter `cfNativeDeployParameters: '--strategy rolling'`. "+ | ||||
| 			"Please refer to the Cloud Foundry documentation for further information: "+ | ||||
| 			"https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html."+ | ||||
| 			"Or alternatively, switch to mta build tool. Please refer to mta build tool"+ | ||||
| 			"documentation for further information: https://sap.github.io/cloud-mta-build-tool/configuration/.") { | ||||
|  | ||||
| 			t.Run("check shell calls", func(t *testing.T) { | ||||
| 				noopCfAPICalls(t, s) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("cf native deploy fail when unknown deployType is set", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue" | ||||
| 		config.Manifest = "" | ||||
| 		config.AppName = "" | ||||
|  | ||||
| 		// app name does not need to be set if it can be found in the manifest.yml | ||||
| 		// manifest name does not need to be set- the default manifest.yml will be used if not set | ||||
| 		defer prepareDefaultManifestMocking("manifest.yml", []string{"newAppName"})() | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.EqualError(t, err, "Invalid deploy type received: 'blue'. Supported value: standard") { | ||||
|  | ||||
| 			t.Run("check shell calls", func(t *testing.T) { | ||||
| 				noopCfAPICalls(t, s) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("deploy cf native without app name", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
| @@ -553,131 +490,12 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	// tests from groovy checking for keep old instances are already contained above. Search for '--delete-old-apps' | ||||
|  | ||||
| 	t.Run("deploy cf native blue green keep old instance", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.Manifest = "test-manifest.yml" | ||||
| 		config.AppName = "myTestApp" | ||||
| 		config.KeepOldInstance = true | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.NoError(t, err) { | ||||
|  | ||||
| 			t.Run("check shell calls", func(t *testing.T) { | ||||
|  | ||||
| 				withLoginAndLogout(t, func(t *testing.T) { | ||||
|  | ||||
| 					assert.Equal(t, []mock.ExecCall{ | ||||
| 						{Exec: "cf", Params: []string{"version"}}, | ||||
| 						{Exec: "cf", Params: []string{"plugins"}}, | ||||
| 						{Exec: "cf", Params: []string{ | ||||
| 							"blue-green-deploy", | ||||
| 							"myTestApp", | ||||
| 							"-f", | ||||
| 							"test-manifest.yml", | ||||
| 						}}, | ||||
| 						{Exec: "cf", Params: []string{ | ||||
| 							"stop", | ||||
| 							"myTestApp-old", | ||||
| 							// MIGRATE FFROM GROOVY: in contrast to groovy there is not redirect of everything &> to a file since we | ||||
| 							// read the stream directly now. | ||||
| 						}}, | ||||
| 					}, s.Calls) | ||||
| 				}) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("cf deploy blue green multiple applications", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.Manifest = "test-manifest.yml" | ||||
| 		config.AppName = "myTestApp" | ||||
|  | ||||
| 		defer prepareDefaultManifestMocking("test-manifest.yml", []string{"app1", "app2"})() | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.EqualError(t, err, "Your manifest contains more than one application. For blue green deployments your manifest file may contain only one application") { | ||||
| 			t.Run("check shell calls", func(t *testing.T) { | ||||
| 				noopCfAPICalls(t, s) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("cf native deploy blue green with no route", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.Manifest = "test-manifest.yml" | ||||
| 		config.AppName = "myTestApp" | ||||
|  | ||||
| 		defer func() { | ||||
| 			_ = filesMock.FileRemove("test-manifest.yml") | ||||
| 			_getManifest = getManifest | ||||
| 		}() | ||||
|  | ||||
| 		filesMock.AddFile("test-manifest.yml", []byte("Content does not matter")) | ||||
|  | ||||
| 		_getManifest = func(name string) (cloudfoundry.Manifest, error) { | ||||
| 			return manifestMock{ | ||||
| 					manifestFileName: "test-manifest.yml", | ||||
| 					apps: []map[string]interface{}{ | ||||
| 						{ | ||||
| 							"name":     "app1", | ||||
| 							"no-route": true, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				nil | ||||
| 		} | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.NoError(t, err) { | ||||
|  | ||||
| 			t.Run("check shell calls", func(t *testing.T) { | ||||
|  | ||||
| 				withLoginAndLogout(t, func(t *testing.T) { | ||||
|  | ||||
| 					assert.Equal(t, []mock.ExecCall{ | ||||
| 						{Exec: "cf", Params: []string{"version"}}, | ||||
| 						{Exec: "cf", Params: []string{"plugins"}}, | ||||
| 						{Exec: "cf", Params: []string{ | ||||
| 							"push", | ||||
| 							"myTestApp", | ||||
| 							"-f", | ||||
| 							"test-manifest.yml", | ||||
| 						}}, | ||||
| 					}, s.Calls) | ||||
| 				}) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("cf native deployment failure", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.DeployType = "standard" | ||||
| 		config.Manifest = "test-manifest.yml" | ||||
| 		config.AppName = "myTestApp" | ||||
|  | ||||
| @@ -685,7 +503,7 @@ func TestCfDeployment(t *testing.T) { | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		s.ShouldFailOnCommand = map[string]error{"cf.*deploy.*": fmt.Errorf("cf deploy failed")} | ||||
| 		s.ShouldFailOnCommand = map[string]error{"cf.*push.*": fmt.Errorf("cf deploy failed")} | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.EqualError(t, err, "cf deploy failed") { | ||||
| @@ -702,7 +520,7 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.DeployType = "standard" | ||||
| 		config.Manifest = "test-manifest.yml" | ||||
| 		config.AppName = "myTestApp" | ||||
|  | ||||
| @@ -739,8 +557,6 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	// TODO testCfNativeBlueGreenKeepOldInstanceShouldThrowErrorOnStopError | ||||
|  | ||||
| 	t.Run("cf native deploy standard should not stop instance", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
| @@ -783,45 +599,6 @@ func TestCfDeployment(t *testing.T) { | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("testCfNativeWithoutAppNameBlueGreen", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
|  | ||||
| 		config.DeployTool = "cf_native" | ||||
| 		config.DeployType = "blue-green" | ||||
| 		config.Manifest = "test-manifest.yml" | ||||
|  | ||||
| 		defer func() { | ||||
| 			_ = filesMock.FileRemove("test-manifest.yml") | ||||
| 			_getManifest = getManifest | ||||
| 		}() | ||||
|  | ||||
| 		filesMock.AddFile("test-manifest.yml", []byte("The content does not matter")) | ||||
|  | ||||
| 		_getManifest = func(name string) (cloudfoundry.Manifest, error) { | ||||
| 			return manifestMock{ | ||||
| 					manifestFileName: "test-manifest.yml", | ||||
| 					apps: []map[string]interface{}{ | ||||
| 						{ | ||||
| 							"there-is": "no-app-name", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				nil | ||||
| 		} | ||||
|  | ||||
| 		s := mock.ExecMockRunner{} | ||||
|  | ||||
| 		err := runCloudFoundryDeploy(&config, nil, nil, &s) | ||||
|  | ||||
| 		if assert.EqualError(t, err, "Blue-green plugin requires app name to be passed (see https://github.com/bluemixgaragelondon/cf-blue-green-deploy/issues/27)") { | ||||
|  | ||||
| 			t.Run("check shell calls", func(t *testing.T) { | ||||
| 				noopCfAPICalls(t, s) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	// TODO add test for testCfNativeFailureInShellCall | ||||
|  | ||||
| 	t.Run("deploytool mtaDeployPlugin blue green", func(t *testing.T) { | ||||
| @@ -1019,10 +796,6 @@ func TestCfDeployment(t *testing.T) { | ||||
|  | ||||
| 	// TODO: testCfPushDeploymentWithoutVariableSubstitution is already handled above (?) | ||||
|  | ||||
| 	// TODO: testCfBlueGreenDeploymentWithVariableSubstitution variable substitution is not handled at the moment (pr pending). | ||||
| 	// but anyway we should not test the full cycle here, but only that the variables substitution tool is called in the appropriate way. | ||||
| 	// variable substitution should be tested at the variables substitution tool itself (yaml util) | ||||
|  | ||||
| 	t.Run("deploytool mtaDeployPlugin", func(t *testing.T) { | ||||
|  | ||||
| 		defer cleanup() | ||||
| @@ -1144,135 +917,6 @@ func TestMtarLookup(t *testing.T) { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestSmokeTestScriptHandling(t *testing.T) { | ||||
|  | ||||
| 	filesMock := mock.FilesMock{} | ||||
| 	filesMock.AddDir("/home/me") | ||||
| 	err := filesMock.Chdir("/home/me") | ||||
| 	assert.NoError(t, err) | ||||
| 	filesMock.AddFileWithMode("mySmokeTestScript.sh", []byte("Content does not matter"), 0644) | ||||
| 	fileUtils = &filesMock | ||||
|  | ||||
| 	var canExec os.FileMode = 0755 | ||||
|  | ||||
| 	t.Run("non default existing smoke test file", func(t *testing.T) { | ||||
|  | ||||
| 		parts, err := handleSmokeTestScript("mySmokeTestScript.sh") | ||||
| 		if assert.NoError(t, err) { | ||||
| 			// when the none-default file name is provided the file must already exist | ||||
| 			// in the project sources. | ||||
| 			assert.False(t, filesMock.HasWrittenFile("mySmokeTestScript.sh")) | ||||
| 			info, e := filesMock.Stat("mySmokeTestScript.sh") | ||||
| 			if assert.NoError(t, e) { | ||||
| 				assert.Equal(t, canExec, info.Mode()) | ||||
| 			} | ||||
|  | ||||
| 			assert.Equal(t, []string{ | ||||
| 				"--smoke-test", | ||||
| 				filepath.FromSlash("/home/me/mySmokeTestScript.sh"), | ||||
| 			}, parts) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("non default not existing smoke test file", func(t *testing.T) { | ||||
|  | ||||
| 		parts, err := handleSmokeTestScript("notExistingSmokeTestScript.sh") | ||||
| 		if assert.EqualError(t, err, "failed to make smoke-test script executable: chmod: notExistingSmokeTestScript.sh: No such file or directory") { | ||||
| 			assert.False(t, filesMock.HasWrittenFile("notExistingSmokeTestScript.sh")) | ||||
| 			assert.Equal(t, []string{}, parts) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("default smoke test file", func(t *testing.T) { | ||||
|  | ||||
| 		parts, err := handleSmokeTestScript("blueGreenCheckScript.sh") | ||||
|  | ||||
| 		if assert.NoError(t, err) { | ||||
|  | ||||
| 			info, e := filesMock.Stat("blueGreenCheckScript.sh") | ||||
| 			if assert.NoError(t, e) { | ||||
| 				assert.Equal(t, canExec, info.Mode()) | ||||
| 			} | ||||
|  | ||||
| 			// in this case we provide the file. We overwrite in case there is already such a file ... | ||||
| 			assert.True(t, filesMock.HasWrittenFile("blueGreenCheckScript.sh")) | ||||
|  | ||||
| 			content, e := filesMock.FileRead("blueGreenCheckScript.sh") | ||||
|  | ||||
| 			if assert.NoError(t, e) { | ||||
| 				assert.Equal(t, "#!/usr/bin/env bash\n# this is simply testing if the application root returns HTTP STATUS_CODE\ncurl -so /dev/null -w '%{response_code}' https://$1 | grep $STATUS_CODE", string(content)) | ||||
| 			} | ||||
|  | ||||
| 			assert.Equal(t, []string{ | ||||
| 				"--smoke-test", | ||||
| 				filepath.FromSlash("/home/me/blueGreenCheckScript.sh"), | ||||
| 			}, parts) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestDefaultManifestVariableFilesHandling(t *testing.T) { | ||||
|  | ||||
| 	filesMock := mock.FilesMock{} | ||||
| 	filesMock.AddDir("/home/me") | ||||
| 	err := filesMock.Chdir("/home/me") | ||||
| 	assert.NoError(t, err) | ||||
| 	fileUtils = &filesMock | ||||
|  | ||||
| 	t.Run("default manifest variable file is the only one and exists", func(t *testing.T) { | ||||
| 		defer func() { | ||||
| 			_ = filesMock.FileRemove("manifest-variables.yml") | ||||
| 		}() | ||||
| 		filesMock.AddFile("manifest-variables.yml", []byte("Content does not matter")) | ||||
|  | ||||
| 		manifestFiles, err := validateManifestVariablesFiles( | ||||
| 			[]string{ | ||||
| 				"manifest-variables.yml", | ||||
| 			}, | ||||
| 		) | ||||
|  | ||||
| 		if assert.NoError(t, err) { | ||||
| 			assert.Equal(t, | ||||
| 				[]string{ | ||||
| 					"manifest-variables.yml", | ||||
| 				}, manifestFiles) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("default manifest variable file is the only one and does not exist", func(t *testing.T) { | ||||
|  | ||||
| 		manifestFiles, err := validateManifestVariablesFiles( | ||||
| 			[]string{ | ||||
| 				"manifest-variables.yml", | ||||
| 			}, | ||||
| 		) | ||||
|  | ||||
| 		if assert.NoError(t, err) { | ||||
| 			assert.Equal(t, []string{}, manifestFiles) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("default manifest variable file among others remains if it does not exist", func(t *testing.T) { | ||||
|  | ||||
| 		// in this case we might fail later. | ||||
|  | ||||
| 		manifestFiles, err := validateManifestVariablesFiles( | ||||
| 			[]string{ | ||||
| 				"manifest-variables.yml", | ||||
| 				"a-second-file.yml", | ||||
| 			}, | ||||
| 		) | ||||
|  | ||||
| 		if assert.NoError(t, err) { | ||||
| 			// the order in which the files are returned is significant. | ||||
| 			assert.Equal(t, []string{ | ||||
| 				"manifest-variables.yml", | ||||
| 				"a-second-file.yml", | ||||
| 			}, manifestFiles) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestExtensionDescriptorsWithMinusE(t *testing.T) { | ||||
|  | ||||
| 	t.Run("ExtensionDescriptorsWithMinusE", func(t *testing.T) { | ||||
|   | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @@ -22,7 +22,6 @@ require ( | ||||
| 	github.com/buildpacks/lifecycle v0.18.4 | ||||
| 	github.com/cloudevents/sdk-go/v2 v2.10.1 | ||||
| 	github.com/docker/cli v24.0.6+incompatible | ||||
| 	github.com/elliotchance/orderedmap v1.4.0 | ||||
| 	github.com/evanphx/json-patch v5.7.0+incompatible | ||||
| 	github.com/getsentry/sentry-go v0.26.0 | ||||
| 	github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @@ -279,8 +279,6 @@ github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU | ||||
| github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= | ||||
| github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= | ||||
| github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= | ||||
| github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A= | ||||
| github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= | ||||
| github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= | ||||
| github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= | ||||
| github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= | ||||
|   | ||||
| @@ -136,10 +136,10 @@ spec: | ||||
|       - name: deployType | ||||
|         type: string | ||||
|         description: | ||||
|           "Defines the type of deployment, either `standard` deployment which results in a system | ||||
|           downtime or a zero-downtime `blue-green` deployment. If 'cf_native' as deployTool and 'blue-green' | ||||
|           as deployType is used in combination, your manifest.yaml may only contain one application. | ||||
|           If this application has the option 'no-route' active the deployType will be changed to 'standard'." | ||||
|           "Defines the type of deployment, for example, `standard` deployment which results in a system | ||||
|           downtime, `blue-green` deployment which results in zero downtime for mta deploy tool. | ||||
|            - For mta build tool, possible values are `standard`, `blue-green` or `bg-deploy`. | ||||
|            - For cf native build tools, possible value is `standard`. To eliminate system downtime, an alternative is to pass '--strategy rolling' to the parameter `cfNativeDeployParameters`." | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
| @@ -180,7 +180,6 @@ spec: | ||||
|       - name: keepOldInstance | ||||
|         type: bool | ||||
|         description: | ||||
|           "In case of a `blue-green` deployment the old instance will be deleted by default. | ||||
|           If this option is set to true the old instance will remain stopped in the Cloud Foundry space." | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
| @@ -339,31 +338,6 @@ spec: | ||||
|           - type: vaultSecret | ||||
|             default: cloudfoundry-$(org)-$(space) | ||||
|             name: cloudfoundryVaultSecretName | ||||
|       - name: smokeTestScript | ||||
|         type: string | ||||
|         description: | ||||
|           "Allows to specify a script which performs a check during blue-green deployment. | ||||
|           The script gets the FQDN as parameter and returns `exit code 0` in case check returned | ||||
|           `smokeTestStatusCode`. | ||||
|           More details can be found [here](https://github.com/bluemixgaragelondon/cf-blue-green-deploy#how-to-use). | ||||
|           Currently this option is only considered for deployTool `cf_native`." | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|           - GENERAL | ||||
|         mandatory: false | ||||
|         default: "blueGreenCheckScript.sh" | ||||
|       - name: smokeTestStatusCode | ||||
|         type: int | ||||
|         description: "Expected status code returned by the check." | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|           - GENERAL | ||||
|         mandatory: false | ||||
|         default: 200 | ||||
|       - name: space | ||||
|         type: string | ||||
|         description: "Cloud Foundry target space" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user