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 
			
		
		
		
	New step mavenExecuteIntegration (#1829)
Co-authored-by: Florian Wilhelm <florian.wilhelm02@sap.com>
This commit is contained in:
		
							
								
								
									
										106
									
								
								cmd/mavenExecuteIntegration.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								cmd/mavenExecuteIntegration.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/SAP/jenkins-library/pkg/command" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/maven" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperutils" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"io" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| ) | ||||
|  | ||||
| type mavenExecuteIntegrationUtils interface { | ||||
| 	Stdout(out io.Writer) | ||||
| 	Stderr(err io.Writer) | ||||
| 	RunExecutable(e string, p ...string) error | ||||
|  | ||||
| 	FileExists(filename string) (bool, error) | ||||
| } | ||||
|  | ||||
| type mavenExecuteIntegrationUtilsBundle struct { | ||||
| 	*command.Command | ||||
| 	*piperutils.Files | ||||
| } | ||||
|  | ||||
| func newMavenExecuteIntegrationUtils() mavenExecuteIntegrationUtils { | ||||
| 	utils := mavenExecuteIntegrationUtilsBundle{ | ||||
| 		Command: &command.Command{}, | ||||
| 		Files:   &piperutils.Files{}, | ||||
| 	} | ||||
| 	utils.Stdout(log.Writer()) | ||||
| 	utils.Stderr(log.Writer()) | ||||
| 	return &utils | ||||
| } | ||||
|  | ||||
| func mavenExecuteIntegration(config mavenExecuteIntegrationOptions, _ *telemetry.CustomData) { | ||||
| 	utils := newMavenExecuteIntegrationUtils() | ||||
| 	err := runMavenExecuteIntegration(&config, utils) | ||||
| 	if err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("step execution failed") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runMavenExecuteIntegration(config *mavenExecuteIntegrationOptions, utils mavenExecuteIntegrationUtils) error { | ||||
| 	pomPath := filepath.Join("integration-tests", "pom.xml") | ||||
| 	hasIntegrationTestsModule, _ := utils.FileExists(pomPath) | ||||
| 	if !hasIntegrationTestsModule { | ||||
| 		return fmt.Errorf("maven module 'integration-tests' does not exist in project structure") | ||||
| 	} | ||||
|  | ||||
| 	if err := validateForkCount(config.ForkCount); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	retryDefine := fmt.Sprintf("-Dsurefire.rerunFailingTestsCount=%v", config.Retry) | ||||
| 	forkCountDefine := fmt.Sprintf("-Dsurefire.forkCount=%v", config.ForkCount) | ||||
|  | ||||
| 	mavenOptions := maven.ExecuteOptions{ | ||||
| 		PomPath:             pomPath, | ||||
| 		M2Path:              config.M2Path, | ||||
| 		ProjectSettingsFile: config.ProjectSettingsFile, | ||||
| 		GlobalSettingsFile:  config.GlobalSettingsFile, | ||||
| 		Goals:               []string{"org.jacoco:jacoco-maven-plugin:prepare-agent", "test"}, | ||||
| 		Defines:             []string{retryDefine, forkCountDefine}, | ||||
| 	} | ||||
|  | ||||
| 	_, err := maven.Execute(&mavenOptions, utils) | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func validateForkCount(value string) error { | ||||
| 	var err error | ||||
|  | ||||
| 	if strings.HasSuffix(value, "C") { | ||||
| 		value := strings.TrimSuffix(value, "C") | ||||
| 		for _, c := range value { | ||||
| 			if !unicode.IsDigit(c) && c != '.' { | ||||
| 				err = fmt.Errorf("only integers or floats allowed with 'C' suffix") | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if err == nil { | ||||
| 			_, err = strconv.ParseFloat(value, 64) | ||||
| 		} | ||||
| 	} else { | ||||
| 		for _, c := range value { | ||||
| 			if !unicode.IsDigit(c) { | ||||
| 				err = fmt.Errorf("only integers allowed without 'C' suffix") | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if err == nil { | ||||
| 			_, err = strconv.ParseInt(value, 10, 64) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("invalid forkCount parameter '%v': %w, please see https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#forkCount for details", value, err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										153
									
								
								cmd/mavenExecuteIntegration_generated.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								cmd/mavenExecuteIntegration_generated.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| // 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/telemetry" | ||||
| 	"github.com/spf13/cobra" | ||||
| ) | ||||
|  | ||||
| type mavenExecuteIntegrationOptions struct { | ||||
| 	Retry                       int    `json:"retry,omitempty"` | ||||
| 	ForkCount                   string `json:"forkCount,omitempty"` | ||||
| 	ProjectSettingsFile         string `json:"projectSettingsFile,omitempty"` | ||||
| 	GlobalSettingsFile          string `json:"globalSettingsFile,omitempty"` | ||||
| 	M2Path                      string `json:"m2Path,omitempty"` | ||||
| 	LogSuccessfulMavenTransfers bool   `json:"logSuccessfulMavenTransfers,omitempty"` | ||||
| } | ||||
|  | ||||
| // MavenExecuteIntegrationCommand This step will execute backend integration tests via the Jacoco Maven-plugin. | ||||
| func MavenExecuteIntegrationCommand() *cobra.Command { | ||||
| 	const STEP_NAME = "mavenExecuteIntegration" | ||||
|  | ||||
| 	metadata := mavenExecuteIntegrationMetadata() | ||||
| 	var stepConfig mavenExecuteIntegrationOptions | ||||
| 	var startTime time.Time | ||||
|  | ||||
| 	var createMavenExecuteIntegrationCmd = &cobra.Command{ | ||||
| 		Use:   STEP_NAME, | ||||
| 		Short: "This step will execute backend integration tests via the Jacoco Maven-plugin.", | ||||
| 		Long: `If the project contains a Maven module named "integration-tests", this step will execute | ||||
| the integration tests via the Jacoco Maven-plugin.`, | ||||
| 		PreRunE: func(cmd *cobra.Command, _ []string) error { | ||||
| 			startTime = time.Now() | ||||
| 			log.SetStepName(STEP_NAME) | ||||
| 			log.SetVerbose(GeneralConfig.Verbose) | ||||
|  | ||||
| 			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 | ||||
| 			} | ||||
|  | ||||
| 			if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 { | ||||
| 				sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID) | ||||
| 				log.RegisterHook(&sentryHook) | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		Run: func(_ *cobra.Command, _ []string) { | ||||
| 			telemetryData := telemetry.CustomData{} | ||||
| 			telemetryData.ErrorCode = "1" | ||||
| 			handler := func() { | ||||
| 				telemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds()) | ||||
| 				telemetry.Send(&telemetryData) | ||||
| 			} | ||||
| 			log.DeferExitHandler(handler) | ||||
| 			defer handler() | ||||
| 			telemetry.Initialize(GeneralConfig.NoTelemetry, STEP_NAME) | ||||
| 			mavenExecuteIntegration(stepConfig, &telemetryData) | ||||
| 			telemetryData.ErrorCode = "0" | ||||
| 			log.Entry().Info("SUCCESS") | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	addMavenExecuteIntegrationFlags(createMavenExecuteIntegrationCmd, &stepConfig) | ||||
| 	return createMavenExecuteIntegrationCmd | ||||
| } | ||||
|  | ||||
| func addMavenExecuteIntegrationFlags(cmd *cobra.Command, stepConfig *mavenExecuteIntegrationOptions) { | ||||
| 	cmd.Flags().IntVar(&stepConfig.Retry, "retry", 1, "The number of times that integration tests will be retried before failing the step. Note: This will consume more time for the step execution.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ForkCount, "forkCount", `1C`, "The number of JVM processes that are spawned to run the tests in parallel in case of using a maven based project structure. For more details visit the Surefire documentation at https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#forkCount.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Path to the mvn settings file that should be used as project settings file.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.GlobalSettingsFile, "globalSettingsFile", os.Getenv("PIPER_globalSettingsFile"), "Path to the mvn settings file that should be used as global settings file.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.M2Path, "m2Path", os.Getenv("PIPER_m2Path"), "Path to the location of the local repository that should be used.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.LogSuccessfulMavenTransfers, "logSuccessfulMavenTransfers", false, "Configures maven to log successful downloads. This is set to `false` by default to reduce the noise in build logs.") | ||||
|  | ||||
| } | ||||
|  | ||||
| // retrieve step metadata | ||||
| func mavenExecuteIntegrationMetadata() config.StepData { | ||||
| 	var theMetaData = config.StepData{ | ||||
| 		Metadata: config.StepMetadata{ | ||||
| 			Name:    "mavenExecuteIntegration", | ||||
| 			Aliases: []config.Alias{{Name: "mavenExecute", Deprecated: false}}, | ||||
| 		}, | ||||
| 		Spec: config.StepSpec{ | ||||
| 			Inputs: config.StepInputs{ | ||||
| 				Parameters: []config.StepParameters{ | ||||
| 					{ | ||||
| 						Name:        "retry", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STEPS", "STAGES"}, | ||||
| 						Type:        "int", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "forkCount", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STEPS", "STAGES"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "projectSettingsFile", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "STEPS", "STAGES", "PARAMETERS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "maven/projectSettingsFile"}}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "globalSettingsFile", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "STEPS", "STAGES", "PARAMETERS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "maven/globalSettingsFile"}}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "m2Path", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "STEPS", "STAGES", "PARAMETERS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "maven/m2Path"}}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "logSuccessfulMavenTransfers", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "STEPS", "STAGES", "PARAMETERS"}, | ||||
| 						Type:        "bool", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{{Name: "maven/logSuccessfulMavenTransfers"}}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return theMetaData | ||||
| } | ||||
							
								
								
									
										16
									
								
								cmd/mavenExecuteIntegration_generated_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								cmd/mavenExecuteIntegration_generated_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestMavenExecuteIntegrationCommand(t *testing.T) { | ||||
|  | ||||
| 	testCmd := MavenExecuteIntegrationCommand() | ||||
|  | ||||
| 	// only high level testing performed - details are tested in step generation procudure | ||||
| 	assert.Equal(t, "mavenExecuteIntegration", testCmd.Use, "command name incorrect") | ||||
|  | ||||
| } | ||||
							
								
								
									
										133
									
								
								cmd/mavenExecuteIntegration_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								cmd/mavenExecuteIntegration_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"github.com/SAP/jenkins-library/pkg/mock" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| type mavenExecuteIntegrationTestUtilsBundle struct { | ||||
| 	*mock.ExecMockRunner | ||||
| 	*mock.FilesMock | ||||
| } | ||||
|  | ||||
| func TestIntegrationTestModuleDoesNotExist(t *testing.T) { | ||||
| 	utils := newMavenIntegrationTestsUtilsBundle() | ||||
| 	config := mavenExecuteIntegrationOptions{} | ||||
|  | ||||
| 	err := runMavenExecuteIntegration(&config, utils) | ||||
|  | ||||
| 	assert.EqualError(t, err, "maven module 'integration-tests' does not exist in project structure") | ||||
| } | ||||
|  | ||||
| func TestHappyPathIntegrationTests(t *testing.T) { | ||||
| 	utils := newMavenIntegrationTestsUtilsBundle() | ||||
| 	utils.FilesMock.AddFile("integration-tests/pom.xml", []byte(`<project> </project>`)) | ||||
|  | ||||
| 	config := mavenExecuteIntegrationOptions{ | ||||
| 		Retry:     2, | ||||
| 		ForkCount: "1C", | ||||
| 	} | ||||
|  | ||||
| 	err := runMavenExecuteIntegration(&config, utils) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error %s", err) | ||||
| 	} | ||||
|  | ||||
| 	expectedParameters1 := []string{ | ||||
| 		"--file", | ||||
| 		"integration-tests/pom.xml", | ||||
| 		"-Dsurefire.rerunFailingTestsCount=2", | ||||
| 		"-Dsurefire.forkCount=1C", | ||||
| 		"-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn", | ||||
| 		"--batch-mode", | ||||
| 		"org.jacoco:jacoco-maven-plugin:prepare-agent", | ||||
| 		"test", | ||||
| 	} | ||||
|  | ||||
| 	assert.Equal(t, mock.ExecCall{Exec: "mvn", Params: expectedParameters1}, utils.ExecMockRunner.Calls[0]) | ||||
| } | ||||
|  | ||||
| func TestInvalidForkCountParam(t *testing.T) { | ||||
| 	// init | ||||
| 	utils := newMavenIntegrationTestsUtilsBundle() | ||||
| 	utils.FilesMock.AddFile("integration-tests/pom.xml", []byte(`<project> </project>`)) | ||||
|  | ||||
| 	// test | ||||
| 	err := runMavenExecuteIntegration(&mavenExecuteIntegrationOptions{ForkCount: "4.2"}, utils) | ||||
|  | ||||
| 	// assert | ||||
| 	if assert.Error(t, err) { | ||||
| 		assert.Contains(t, err.Error(), "invalid forkCount parameter") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestValidateForkCount(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		name          string | ||||
| 		testValue     string | ||||
| 		expectedError string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:          "valid integer", | ||||
| 			testValue:     "2", | ||||
| 			expectedError: "", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "zero is valid", | ||||
| 			testValue:     "0", | ||||
| 			expectedError: "", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "valid floating point", | ||||
| 			testValue:     "2.5C", | ||||
| 			expectedError: "", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "valid integer with C", | ||||
| 			testValue:     "2C", | ||||
| 			expectedError: "", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "invalid floating point", | ||||
| 			testValue:     "1.2", | ||||
| 			expectedError: "invalid forkCount parameter", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "invalid", | ||||
| 			testValue:     "C1", | ||||
| 			expectedError: "invalid forkCount parameter", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "another invalid", | ||||
| 			testValue:     "1 C", | ||||
| 			expectedError: "invalid forkCount parameter", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "invalid float", | ||||
| 			testValue:     "1..2C", | ||||
| 			expectedError: "invalid forkCount parameter", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, testCase := range testCases { | ||||
| 		t.Run(testCase.name, func(t *testing.T) { | ||||
| 			err := validateForkCount(testCase.testValue) | ||||
| 			if testCase.expectedError == "" { | ||||
| 				assert.NoError(t, err) | ||||
| 			} else if assert.Error(t, err) { | ||||
| 				assert.Contains(t, err.Error(), testCase.expectedError) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newMavenIntegrationTestsUtilsBundle() mavenExecuteIntegrationTestUtilsBundle { | ||||
| 	utilsBundle := mavenExecuteIntegrationTestUtilsBundle{ | ||||
| 		ExecMockRunner: &mock.ExecMockRunner{}, | ||||
| 		FilesMock:      &mock.FilesMock{}, | ||||
| 	} | ||||
| 	return utilsBundle | ||||
| } | ||||
| @@ -81,6 +81,7 @@ func Execute() { | ||||
| 	rootCmd.AddCommand(MavenExecuteCommand()) | ||||
| 	rootCmd.AddCommand(CloudFoundryCreateServiceKeyCommand()) | ||||
| 	rootCmd.AddCommand(MavenBuildCommand()) | ||||
| 	rootCmd.AddCommand(MavenExecuteIntegrationCommand()) | ||||
| 	rootCmd.AddCommand(MavenExecuteStaticCodeChecksCommand()) | ||||
| 	rootCmd.AddCommand(NexusUploadCommand()) | ||||
| 	rootCmd.AddCommand(AbapEnvironmentRunATCCheckCommand()) | ||||
|   | ||||
							
								
								
									
										7
									
								
								documentation/docs/steps/mavenExecuteIntegration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								documentation/docs/steps/mavenExecuteIntegration.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| # ${docGenStepName} | ||||
|  | ||||
| ## ${docGenDescription} | ||||
|  | ||||
| ## ${docGenParameters} | ||||
|  | ||||
| ## ${docGenConfiguration} | ||||
| @@ -24,6 +24,16 @@ func TestMavenBuildCloudSdkSpringProject(t *testing.T) { | ||||
| 	container.assertHasOutput(t, "BUILD SUCCESS") | ||||
| 	container.assertHasFile(t, "/project/application/target/cloud-sdk-spring-archetype-application.jar") | ||||
| 	container.assertHasFile(t, "/tmp/.m2/repository") | ||||
| 
 | ||||
| 	err = container.whenRunningPiperCommand("mavenExecuteIntegration", "") | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Calling piper command filed %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	container.assertHasOutput(t, "INFO mydemo.HelloWorldControllerTest - Starting HelloWorldControllerTest") | ||||
| 	container.assertHasOutput(t, "Tests run: 1, Failures: 0, Errors: 0, Skipped: 0") | ||||
| 
 | ||||
| 	container.assertHasFile(t, "/project/integration-tests/target/coverage-reports/jacoco.exec") | ||||
| } | ||||
| 
 | ||||
| func TestMavenBuildCloudSdkTomeeProject(t *testing.T) { | ||||
| @@ -44,4 +54,14 @@ func TestMavenBuildCloudSdkTomeeProject(t *testing.T) { | ||||
| 	container.assertHasFile(t, "/project/application/target/cloud-sdk-tomee-archetype-application-classes.jar") | ||||
| 	container.assertHasFile(t, "/project/application/target/cloud-sdk-tomee-archetype-application.war") | ||||
| 	container.assertHasFile(t, "/tmp/.m2/repository") | ||||
| 
 | ||||
| 	err = container.whenRunningPiperCommand("mavenExecuteIntegration", "") | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Calling piper command filed %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	container.assertHasOutput(t, "(prepare-agent) @ cloud-sdk-tomee-archetype-integration-tests") | ||||
| 	container.assertHasOutput(t, "Tests run: 1, Failures: 0, Errors: 0, Skipped: 0") | ||||
| 
 | ||||
| 	container.assertHasFile(t, "/project/integration-tests/target/coverage-reports/jacoco.exec") | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| general: | ||||
| steps: | ||||
|   mavenBuild: | ||||
|   maven: | ||||
|     globalSettingsFile: settings.xml | ||||
| steps: | ||||
| stages: | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| general: | ||||
| steps: | ||||
|   mavenBuild: | ||||
|   maven: | ||||
|     globalSettingsFile: settings.xml | ||||
| steps: | ||||
| stages: | ||||
|   | ||||
							
								
								
									
										85
									
								
								resources/metadata/mavenExecuteIntegration.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								resources/metadata/mavenExecuteIntegration.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| metadata: | ||||
|   name: mavenExecuteIntegration | ||||
|   aliases: | ||||
|     - name: mavenExecute | ||||
|       deprecated: false | ||||
|   description: "This step will execute backend integration tests via the Jacoco Maven-plugin." | ||||
|   longDescription: | | ||||
|     If the project contains a Maven module named "integration-tests", this step will execute | ||||
|     the integration tests via the Jacoco Maven-plugin. | ||||
| spec: | ||||
|   inputs: | ||||
|     params: | ||||
|       - name: retry | ||||
|         type: int | ||||
|         description: "The number of times that integration tests will be retried before failing the step. | ||||
|           Note: This will consume more time for the step execution." | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STEPS | ||||
|           - STAGES | ||||
|         default: 1 | ||||
|       - name: forkCount | ||||
|         type: string | ||||
|         description: "The number of JVM processes that are spawned to run the tests in parallel in case of | ||||
|           using a maven based project structure. | ||||
|           For more details visit the Surefire documentation at | ||||
|           https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#forkCount." | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STEPS | ||||
|           - STAGES | ||||
|         default: "1C" | ||||
|  | ||||
|       # Global maven settings, should be added to all maven steps | ||||
|       - name: projectSettingsFile | ||||
|         type: string | ||||
|         description: "Path to the mvn settings file that should be used as project settings file." | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - STEPS | ||||
|           - STAGES | ||||
|           - PARAMETERS | ||||
|         aliases: | ||||
|           - name: maven/projectSettingsFile | ||||
|       - name: globalSettingsFile | ||||
|         type: string | ||||
|         description: "Path to the mvn settings file that should be used as global settings file." | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - STEPS | ||||
|           - STAGES | ||||
|           - PARAMETERS | ||||
|         aliases: | ||||
|           - name: maven/globalSettingsFile | ||||
|       - name: m2Path | ||||
|         type: string | ||||
|         description: "Path to the location of the local repository that should be used." | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - STEPS | ||||
|           - STAGES | ||||
|           - PARAMETERS | ||||
|         aliases: | ||||
|           - name: maven/m2Path | ||||
|       - name: logSuccessfulMavenTransfers | ||||
|         type: bool | ||||
|         description: "Configures maven to log successful downloads. | ||||
|           This is set to `false` by default to reduce the noise in build logs." | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - STEPS | ||||
|           - STAGES | ||||
|           - PARAMETERS | ||||
|         default: false | ||||
|         aliases: | ||||
|           - name: maven/logSuccessfulMavenTransfers | ||||
|  | ||||
|   containers: | ||||
|     - name: mvn | ||||
|       image: maven:3.6-jdk-8 | ||||
|       imagePullPolicy: Never | ||||
|  | ||||
|   # This declaration is necessary in order to return any sidecar configuration in the context config. | ||||
|   sidecars: | ||||
|     - name: '' | ||||
| @@ -4,31 +4,33 @@ package com.sap.piper | ||||
| class DownloadCacheUtils { | ||||
|  | ||||
|     static Map injectDownloadCacheInParameters(Script script, Map parameters, BuildTool buildTool) { | ||||
|         if (DownloadCacheUtils.isEnabled(script)) { | ||||
|         if (!isEnabled(script)) { | ||||
|             return parameters | ||||
|         } | ||||
|  | ||||
|             if (!parameters.dockerOptions) { | ||||
|                 parameters.dockerOptions = [] | ||||
|             } | ||||
|             if (parameters.dockerOptions instanceof CharSequence) { | ||||
|                 parameters.dockerOptions = [parameters.dockerOptions] | ||||
|         if (!parameters.dockerOptions) { | ||||
|             parameters.dockerOptions = [] | ||||
|         } | ||||
|         if (parameters.dockerOptions instanceof CharSequence) { | ||||
|             parameters.dockerOptions = [parameters.dockerOptions] | ||||
|         } | ||||
|  | ||||
|         if (!(parameters.dockerOptions instanceof List)) { | ||||
|             throw new IllegalArgumentException("Unexpected type for dockerOptions. Expected was either a list or a string. Actual type was: '${parameters.dockerOptions.getClass()}'") | ||||
|         } | ||||
|         parameters.dockerOptions.add(getDockerOptions(script)) | ||||
|  | ||||
|         if (buildTool == BuildTool.MAVEN || buildTool == BuildTool.MTA) { | ||||
|             String globalSettingsFile = getGlobalMavenSettingsForDownloadCache(script) | ||||
|             if (parameters.globalSettingsFile && parameters.globalSettingsFile != globalSettingsFile) { | ||||
|                 throw new IllegalArgumentException("You can not specify the parameter globalSettingsFile if the download cache is active") | ||||
|             } | ||||
|  | ||||
|             if (!(parameters.dockerOptions instanceof List)) { | ||||
|                 throw new IllegalArgumentException("Unexpected type for dockerOptions. Expected was either a list or a string. Actual type was: '${parameters.dockerOptions.getClass()}'") | ||||
|             } | ||||
|             parameters.dockerOptions.add(DownloadCacheUtils.getDockerOptions(script)) | ||||
|             parameters.globalSettingsFile = globalSettingsFile | ||||
|         } | ||||
|  | ||||
|             if (buildTool == BuildTool.MAVEN || buildTool == BuildTool.MTA) { | ||||
|                 if (parameters.globalSettingsFile) { | ||||
|                     throw new IllegalArgumentException("You can not specify the parameter globalSettingsFile if the download cache is active") | ||||
|                 } | ||||
|  | ||||
|                 parameters.globalSettingsFile = DownloadCacheUtils.getGlobalMavenSettingsForDownloadCache(script) | ||||
|             } | ||||
|  | ||||
|             if (buildTool == BuildTool.NPM || buildTool == buildTool.MTA) { | ||||
|                 parameters['defaultNpmRegistry'] = DownloadCacheUtils.getNpmRegistryUri(script) | ||||
|             } | ||||
|         if (buildTool == BuildTool.NPM || buildTool == BuildTool.MTA) { | ||||
|             parameters['defaultNpmRegistry'] = getNpmRegistryUri(script) | ||||
|         } | ||||
|  | ||||
|         return parameters | ||||
| @@ -47,6 +49,15 @@ class DownloadCacheUtils { | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         // Do not enable the DL-cache when a sidecar image is specified. | ||||
|         // This is necessary because it is currently not possible to connect a container to multiple networks. | ||||
|         // Can be removed when docker plugin supports multiple networks and jenkins-library implemented that feature | ||||
|         // See also https://github.com/SAP/jenkins-library/issues/1864 | ||||
|         if (script.env.SIDECAR_IMAGE) { | ||||
|             script.echo "Download cache disabled while running with sidecar image (${script.env.SIDECAR_IMAGE})" | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         return (networkName() && hostname()) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -134,6 +134,7 @@ public class CommonStepsTest extends BasePiperTest{ | ||||
|         'malwareExecuteScan', //implementing new golang pattern without fields | ||||
|         'mavenBuild', //implementing new golang pattern without fields | ||||
|         'mavenExecute', //implementing new golang pattern without fields | ||||
|         'mavenExecuteIntegration', //implementing new golang pattern without fields | ||||
|         'mavenExecuteStaticCodeChecks', //implementing new golang pattern without fields | ||||
|         'mtaBuild', //implementing new golang pattern without fields | ||||
|         'nexusUpload', //implementing new golang pattern without fields | ||||
|   | ||||
							
								
								
									
										72
									
								
								test/groovy/MavenExecuteIntegrationTest.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								test/groovy/MavenExecuteIntegrationTest.groovy
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| import groovy.json.JsonSlurper | ||||
| import org.junit.Before | ||||
| import org.junit.Rule | ||||
| import org.junit.Test | ||||
| import org.junit.rules.ExpectedException | ||||
| import org.junit.rules.RuleChain | ||||
| import util.* | ||||
|  | ||||
| import static org.hamcrest.Matchers.* | ||||
| import static org.junit.Assert.assertThat | ||||
|  | ||||
| class MavenExecuteIntegrationTest extends BasePiperTest { | ||||
|     private ExpectedException exception = ExpectedException.none() | ||||
|  | ||||
|     private JenkinsCredentialsRule credentialsRule = new JenkinsCredentialsRule(this) | ||||
|     private JenkinsShellCallRule shellCallRule = new JenkinsShellCallRule(this) | ||||
|     private JenkinsStepRule stepRule = new JenkinsStepRule(this) | ||||
|     private JenkinsWriteFileRule writeFileRule = new JenkinsWriteFileRule(this) | ||||
|  | ||||
|     private List withEnvArgs = [] | ||||
|  | ||||
|     @Rule | ||||
|     public RuleChain rules = Rules | ||||
|         .getCommonRules(this) | ||||
|         .around(exception) | ||||
|         .around(new JenkinsReadYamlRule(this)) | ||||
|         .around(credentialsRule) | ||||
|         .around(new JenkinsReadJsonRule(this)) | ||||
|         .around(shellCallRule) | ||||
|         .around(stepRule) | ||||
|         .around(writeFileRule) | ||||
|         .around(new JenkinsFileExistsRule(this, [])) | ||||
|  | ||||
|     @Before | ||||
|     void init() { | ||||
|         helper.registerAllowedMethod("readJSON", [Map], { m -> | ||||
|             if (m.text instanceof String) | ||||
|                 return new JsonSlurper().parseText(m.text as String) | ||||
|         }) | ||||
|         helper.registerAllowedMethod("withEnv", [List, Closure], { arguments, closure -> | ||||
|             arguments.each {arg -> | ||||
|                 withEnvArgs.add(arg.toString()) | ||||
|             } | ||||
|             return closure() | ||||
|         }) | ||||
|         shellCallRule.setReturnValue( | ||||
|             './piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/mavenExecuteIntegration.yaml\'', | ||||
|             '{"verbose": false}' | ||||
|         ) | ||||
|  | ||||
|         helper.registerAllowedMethod('fileExists', [String], {return true}) | ||||
|         helper.registerAllowedMethod('findFiles', [Map], {return null}) | ||||
|         helper.registerAllowedMethod('testsPublishResults', [Map], {return null}) | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     void testWithSidecar() { | ||||
|         stepRule.step.mavenExecuteIntegration( | ||||
|             juStabUtils: utils, | ||||
|             jenkinsUtilsStub: jenkinsUtils, | ||||
|             testParam: 'This is test content', | ||||
|             sidecarImage: 'some/image', | ||||
|             script: nullScript, | ||||
|         ) | ||||
|         // asserts | ||||
|         assertThat(writeFileRule.files['.pipeline/tmp/metadata/mavenExecuteIntegration.yaml'] as String, | ||||
|             containsString('name: mavenExecuteIntegration')) | ||||
|         assertThat(withEnvArgs[0], allOf(startsWith('PIPER_parametersJSON'), | ||||
|             containsString('"testParam":"This is test content"'))) | ||||
|         assertThat(shellCallRule.shell[1] as String, is('./piper mavenExecuteIntegration')) | ||||
|     } | ||||
| } | ||||
| @@ -316,6 +316,14 @@ boolean isContainerDefined(config) { | ||||
|         return false | ||||
|     } | ||||
|  | ||||
|     if (env.SIDECAR_IMAGE != config.sidecarImage) { | ||||
|         // If a sidecar image has been configured for the current stage, | ||||
|         // then piperStageWrapper will have set the env.SIDECAR_IMAGE variable. | ||||
|         // If the current step overrides the stage's sidecar image, | ||||
|         // then a new Pod needs to be spawned. | ||||
|         return false | ||||
|     } | ||||
|  | ||||
|     return containerMap.get(env.POD_NAME).containsKey(config.dockerImage) | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										31
									
								
								vars/mavenExecuteIntegration.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vars/mavenExecuteIntegration.groovy
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import com.sap.piper.BuildTool | ||||
| import com.sap.piper.DownloadCacheUtils | ||||
| import groovy.transform.Field | ||||
|  | ||||
| import static com.sap.piper.Prerequisites.checkScript | ||||
|  | ||||
| @Field String STEP_NAME = getClass().getName() | ||||
| @Field String METADATA_FILE = 'metadata/mavenExecuteIntegration.yaml' | ||||
|  | ||||
| @Field Set GENERAL_CONFIG_KEYS = [] | ||||
| @Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS | ||||
| @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([ | ||||
|     /** | ||||
|      * Specify a glob pattern where test result files will be located. | ||||
|      */ | ||||
|     'reportLocationPattern', | ||||
| ]) | ||||
|  | ||||
| //Metadata maintained in file project://resources/metadata/mavenExecuteIntegration.yaml | ||||
|  | ||||
| void call(Map parameters = [:]) { | ||||
|     final script = checkScript(this, parameters) ?: this | ||||
|     parameters = DownloadCacheUtils.injectDownloadCacheInParameters(script, parameters, BuildTool.MAVEN) | ||||
|  | ||||
|     try { | ||||
|         List credentials = [] | ||||
|         piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials) | ||||
|     } finally { | ||||
|         testsPublishResults(script: script, junit: [allowEmptyResults: true, pattern: parameters.reportLocationPattern]) | ||||
|     } | ||||
| } | ||||
| @@ -48,7 +48,7 @@ void call(Map parameters = [:], String stepName, String metadataFile, List crede | ||||
|             } | ||||
|  | ||||
|             // prepare stashes | ||||
|             // first eliminate empty stahes | ||||
|             // first eliminate empty stashes | ||||
|             config.stashContent = utils.unstashAll(config.stashContent) | ||||
|             // then make sure that commonPipelineEnvironment, config, ... is also available when step stashing is active | ||||
|             if (config.stashContent?.size() > 0) { | ||||
|   | ||||
| @@ -31,20 +31,38 @@ void call(Map parameters = [:], body) { | ||||
|  | ||||
|     stageLocking(config) { | ||||
|         def containerMap = ContainerMap.instance.getMap().get(stageName) ?: [:] | ||||
|         List environment = [] | ||||
|         if (config.sidecarImage) { | ||||
|             echo "sidecarImage configured for stage '${stageName}': '${config.sidecarImage}'" | ||||
|             environment.add("SIDECAR_IMAGE=${config.sidecarImage}") | ||||
|         } | ||||
|         if (Boolean.valueOf(env.ON_K8S) && (containerMap.size() > 0 || config.runStageInPod)) { | ||||
|             withEnv(["POD_NAME=${stageName}"]) { | ||||
|             environment.add("POD_NAME=${stageName}") | ||||
|             withEnv(environment) { | ||||
|                 dockerExecuteOnKubernetes(script: script, containerMap: containerMap, stageName: stageName) { | ||||
|                     executeStage(script, body, stageName, config, utils, parameters.telemetryDisabled) | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             node(config.nodeLabel) { | ||||
|                 executeStage(script, body, stageName, config, utils, parameters.telemetryDisabled) | ||||
|             withEnvWrapper(environment) { | ||||
|                 node(config.nodeLabel) { | ||||
|                     executeStage(script, body, stageName, config, utils, parameters.telemetryDisabled) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| private void withEnvWrapper(List environment, Closure body) { | ||||
|     if (environment) { | ||||
|         withEnv(environment) { | ||||
|             body() | ||||
|         } | ||||
|     } else { | ||||
|         body() | ||||
|     } | ||||
| } | ||||
|  | ||||
| private void stageLocking(Map config, Closure body) { | ||||
|     if (config.stageLocking) { | ||||
|         String resource = config.lockingResourceGroup?:env.JOB_NAME | ||||
|   | ||||
		Reference in New Issue
	
	Block a user