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 
			
		
		
		
	integrationArtifactTransport Command (#4131)
* integrationArtifactTransport Command * CodeReview Fix * CodeReview Fix * codereview fix * Update documentation/docs/steps/integrationArtifactTransport.md Co-authored-by: Srinikitha Kondreddy <srinikitha.kondreddy@sap.com> * Update documentation/docs/steps/integrationArtifactTransport.md Co-authored-by: Srinikitha Kondreddy <srinikitha.kondreddy@sap.com> * CodeReview Fixes * CodeReview FIxes * CodeReview Fix * Doc Fixes * Update documentation/docs/steps/integrationArtifactTransport.md Co-authored-by: Linda Siebert <39100394+LindaSieb@users.noreply.github.com> * Doc fixes * Doc Fixes * CodeReview Fixes * Doc Fixes Co-authored-by: Linda Siebert <linda.siebert@sap.com> Co-authored-by: Srinikitha Kondreddy <srinikitha.kondreddy@sap.com> Co-authored-by: Linda Siebert <39100394+LindaSieb@users.noreply.github.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							4ae97a8a73
						
					
				
				
					commit
					a65df9ced6
				
			
							
								
								
									
										227
									
								
								cmd/integrationArtifactTransport.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								cmd/integrationArtifactTransport.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,227 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"math/rand" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/Jeffail/gabs/v2" | ||||
| 	"github.com/SAP/jenkins-library/pkg/apim" | ||||
| 	piperhttp "github.com/SAP/jenkins-library/pkg/http" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| func integrationArtifactTransport(config integrationArtifactTransportOptions, telemetryData *telemetry.CustomData) { | ||||
| 	httpClient := &piperhttp.Client{} | ||||
| 	err := runIntegrationArtifactTransport(&config, telemetryData, httpClient) | ||||
| 	if err != nil { | ||||
| 		log.Entry().WithError(err).Fatal("step execution failed") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runIntegrationArtifactTransport(config *integrationArtifactTransportOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) error { | ||||
| 	apimData := apim.Bundle{APIServiceKey: config.CasServiceKey, Client: httpClient} | ||||
| 	err := apim.Utils.InitAPIM(&apimData) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return CreateIntegrationArtifactTransportRequest(config, apimData) | ||||
| } | ||||
|  | ||||
| //CreateIntegrationArtifactTransportRequest - Create a transport request for Integration Package | ||||
| func CreateIntegrationArtifactTransportRequest(config *integrationArtifactTransportOptions, apistruct apim.Bundle) error { | ||||
| 	httpMethod := http.MethodPost | ||||
| 	httpClient := apistruct.Client | ||||
| 	createTransportRequestURL := fmt.Sprintf("%s/v1/contentResources/export", apistruct.Host) | ||||
| 	header := make(http.Header) | ||||
| 	header.Add("content-type", "application/json") | ||||
| 	payload, jsonError := GetCPITransportReqPayload(config) | ||||
| 	if jsonError != nil { | ||||
| 		return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.IntegrationPackageID) | ||||
| 	} | ||||
|  | ||||
| 	createTransportRequestResp, httpErr := httpClient.SendRequest(httpMethod, createTransportRequestURL, payload, header, nil) | ||||
|  | ||||
| 	if httpErr != nil { | ||||
| 		return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error", httpMethod, createTransportRequestURL) | ||||
| 	} | ||||
|  | ||||
| 	if createTransportRequestResp != nil && createTransportRequestResp.Body != nil { | ||||
| 		defer createTransportRequestResp.Body.Close() | ||||
| 	} | ||||
|  | ||||
| 	if createTransportRequestResp == nil { | ||||
| 		return errors.Errorf("did not retrieve a HTTP response") | ||||
| 	} | ||||
|  | ||||
| 	if createTransportRequestResp.StatusCode == http.StatusAccepted { | ||||
| 		log.Entry(). | ||||
| 			WithField("IntegrationPackageID", config.IntegrationPackageID). | ||||
| 			Info("successfully created the integration package transport request") | ||||
|  | ||||
| 		bodyText, readErr := ioutil.ReadAll(createTransportRequestResp.Body) | ||||
| 		if readErr != nil { | ||||
| 			return errors.Wrap(readErr, "HTTP response body could not be read") | ||||
| 		} | ||||
| 		jsonResponse, parsingErr := gabs.ParseJSON([]byte(bodyText)) | ||||
| 		if parsingErr != nil { | ||||
| 			return errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText)) | ||||
| 		} | ||||
| 		processId := jsonResponse.Path("processId").Data().(string) | ||||
|  | ||||
| 		if processId != "" { | ||||
| 			error := pollTransportStatus(processId, retryCount, config, httpClient, apistruct.Host) | ||||
| 			return error | ||||
| 		} | ||||
| 		return errors.New("Invalid process id") | ||||
| 	} | ||||
| 	responseBody, readErr := ioutil.ReadAll(createTransportRequestResp.Body) | ||||
|  | ||||
| 	if readErr != nil { | ||||
| 		return errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", createTransportRequestResp.StatusCode) | ||||
| 	} | ||||
| 	log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code : %v", string(responseBody), createTransportRequestResp.StatusCode) | ||||
| 	return errors.Errorf("integration flow deployment failed, response Status code: %v", createTransportRequestResp.StatusCode) | ||||
| } | ||||
|  | ||||
| //pollTransportStatus - Poll the integration package transport processing, return status or error details | ||||
| func pollTransportStatus(processId string, remainingRetries int, config *integrationArtifactTransportOptions, httpClient piperhttp.Sender, apiHost string) error { | ||||
|  | ||||
| 	if remainingRetries <= 0 { | ||||
| 		return errors.New("failed to start integration artifact after retrying several times") | ||||
| 	} | ||||
| 	transportStatus, err := getIntegrationTransportProcessingStatus(config, httpClient, apiHost, processId) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	//with specific delay between each retry | ||||
| 	if (transportStatus == "RUNNING") || (transportStatus == "INITIAL") { | ||||
| 		// Calling Sleep method | ||||
| 		sleepTime := int(retryCount * 3) | ||||
| 		time.Sleep(time.Duration(sleepTime) * time.Second) | ||||
| 		remainingRetries-- | ||||
| 		return pollTransportStatus(processId, retryCount, config, httpClient, apiHost) | ||||
| 	} | ||||
|  | ||||
| 	//if artifact transport completed, then just return | ||||
| 	if transportStatus == "FINISHED" { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	//if error return immediately with error details | ||||
| 	if transportStatus == "ERROR" || transportStatus == "ABORTED" { | ||||
| 		resp, err := getIntegrationTransportError(config, httpClient, apiHost, processId) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return errors.New(resp) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| //GetJSONPayload -return http payload as byte array | ||||
| func GetCPITransportReqPayload(config *integrationArtifactTransportOptions) (*bytes.Buffer, error) { | ||||
| 	jsonObj := gabs.New() | ||||
| 	jsonObj.Set(rand.Intn(5000), "id") | ||||
| 	jsonObj.Set("MonitoringTeam", "requestor") | ||||
| 	jsonObj.Set("1.0.0", "version") | ||||
| 	jsonObj.Set("TransportManagementService", "exportMode") | ||||
| 	jsonObj.Set("MTAR", "exportMediaType") | ||||
| 	jsonObj.Set("Integration Artifact transport request for TransportManagementService", "description") | ||||
| 	jsonResourceObj := gabs.New() | ||||
| 	jsonResourceObj.Set(config.IntegrationPackageID, "id") | ||||
| 	jsonResourceObj.Set(config.ResourceID, "resourceID") | ||||
| 	jsonResourceObj.Set("d9c3fe08ceeb47a2991e53049f2ed766", "contentType") | ||||
| 	jsonResourceObj.Set("package", "subType") | ||||
| 	jsonResourceObj.Set(config.Name, "name") | ||||
| 	jsonResourceObj.Set("CloudIntegration", "type") | ||||
| 	jsonResourceObj.Set(config.Version, "version") | ||||
| 	jsonObj.ArrayAppend(jsonResourceObj, "contentResources") | ||||
|  | ||||
| 	jsonBody, jsonErr := json.Marshal(jsonObj) | ||||
|  | ||||
| 	if jsonErr != nil { | ||||
| 		return nil, errors.Wrapf(jsonErr, "Transport request payload is invalid for integration package artifact %q", config.IntegrationPackageID) | ||||
| 	} | ||||
| 	return bytes.NewBuffer(jsonBody), nil | ||||
| } | ||||
|  | ||||
| //getIntegrationTransportProcessingStatus - Get integration package transport request processing Status | ||||
| func getIntegrationTransportProcessingStatus(config *integrationArtifactTransportOptions, httpClient piperhttp.Sender, apiHost string, processId string) (string, error) { | ||||
| 	httpMethod := "GET" | ||||
| 	header := make(http.Header) | ||||
| 	header.Add("content-type", "application/json") | ||||
| 	header.Add("Accept", "application/json") | ||||
| 	transportProcStatusURL := fmt.Sprintf("%s/v1/operations/%s", apiHost, processId) | ||||
| 	transportProcStatusResp, httpErr := httpClient.SendRequest(httpMethod, transportProcStatusURL, nil, header, nil) | ||||
|  | ||||
| 	if transportProcStatusResp != nil && transportProcStatusResp.Body != nil { | ||||
| 		defer transportProcStatusResp.Body.Close() | ||||
| 	} | ||||
|  | ||||
| 	if transportProcStatusResp == nil { | ||||
| 		return "", errors.Errorf("did not retrieve a HTTP response: %v", httpErr) | ||||
| 	} | ||||
|  | ||||
| 	if (transportProcStatusResp.StatusCode == http.StatusOK) || (transportProcStatusResp.StatusCode == http.StatusAccepted) { | ||||
| 		log.Entry(). | ||||
| 			WithField("IntegrationPackageID", config.IntegrationPackageID). | ||||
| 			Info("successfully processed the integration package transport response status") | ||||
|  | ||||
| 		bodyText, readErr := ioutil.ReadAll(transportProcStatusResp.Body) | ||||
| 		if readErr != nil { | ||||
| 			return "", errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", transportProcStatusResp.StatusCode) | ||||
| 		} | ||||
| 		jsonResponse, parsingErr := gabs.ParseJSON([]byte(bodyText)) | ||||
| 		if parsingErr != nil { | ||||
| 			return "", errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText)) | ||||
| 		} | ||||
| 		contentTransporStatus := jsonResponse.Path("state").Data().(string) | ||||
| 		return contentTransporStatus, nil | ||||
| 	} | ||||
| 	if httpErr != nil { | ||||
| 		return getHTTPErrorMessage(httpErr, transportProcStatusResp, httpMethod, transportProcStatusURL) | ||||
| 	} | ||||
| 	return "", errors.Errorf("failed to get transport request processing status, response Status code: %v", transportProcStatusResp.StatusCode) | ||||
| } | ||||
|  | ||||
| //getTransportError - Get integration package transport failures error details | ||||
| func getIntegrationTransportError(config *integrationArtifactTransportOptions, httpClient piperhttp.Sender, apiHost string, processId string) (string, error) { | ||||
| 	httpMethod := "GET" | ||||
| 	header := make(http.Header) | ||||
| 	header.Add("content-type", "application/json") | ||||
| 	errorStatusURL := fmt.Sprintf("%s/v1/operations/%s/logs", apiHost, processId) | ||||
| 	errorStatusResp, httpErr := httpClient.SendRequest(httpMethod, errorStatusURL, nil, header, nil) | ||||
|  | ||||
| 	if errorStatusResp != nil && errorStatusResp.Body != nil { | ||||
| 		defer errorStatusResp.Body.Close() | ||||
| 	} | ||||
|  | ||||
| 	if errorStatusResp == nil { | ||||
| 		return "", errors.Errorf("did not retrieve a HTTP response: %v", httpErr) | ||||
| 	} | ||||
|  | ||||
| 	if errorStatusResp.StatusCode == http.StatusOK { | ||||
| 		log.Entry(). | ||||
| 			WithField("IntegrationPackageId", config.IntegrationPackageID). | ||||
| 			Info("Successfully retrieved deployment failures error details") | ||||
| 		responseBody, readErr := ioutil.ReadAll(errorStatusResp.Body) | ||||
| 		if readErr != nil { | ||||
| 			return "", errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", errorStatusResp.StatusCode) | ||||
| 		} | ||||
| 		log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), errorStatusResp.StatusCode) | ||||
| 		errorDetails := string(responseBody) | ||||
| 		return errorDetails, nil | ||||
| 	} | ||||
| 	if httpErr != nil { | ||||
| 		return getHTTPErrorMessage(httpErr, errorStatusResp, httpMethod, errorStatusURL) | ||||
| 	} | ||||
| 	return "", errors.Errorf("failed to get Integration Package transport error details, response Status code: %v", errorStatusResp.StatusCode) | ||||
| } | ||||
							
								
								
									
										203
									
								
								cmd/integrationArtifactTransport_generated.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								cmd/integrationArtifactTransport_generated.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,203 @@ | ||||
| // Code generated by piper's step-generator. DO NOT EDIT. | ||||
|  | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/config" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/splunk" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"github.com/SAP/jenkins-library/pkg/validation" | ||||
| 	"github.com/spf13/cobra" | ||||
| ) | ||||
|  | ||||
| type integrationArtifactTransportOptions struct { | ||||
| 	CasServiceKey        string `json:"casServiceKey,omitempty"` | ||||
| 	IntegrationPackageID string `json:"integrationPackageId,omitempty"` | ||||
| 	ResourceID           string `json:"resourceID,omitempty"` | ||||
| 	Name                 string `json:"name,omitempty"` | ||||
| 	Version              string `json:"version,omitempty"` | ||||
| } | ||||
|  | ||||
| // IntegrationArtifactTransportCommand Integration Package transport using the SAP Content Agent Service | ||||
| func IntegrationArtifactTransportCommand() *cobra.Command { | ||||
| 	const STEP_NAME = "integrationArtifactTransport" | ||||
|  | ||||
| 	metadata := integrationArtifactTransportMetadata() | ||||
| 	var stepConfig integrationArtifactTransportOptions | ||||
| 	var startTime time.Time | ||||
| 	var logCollector *log.CollectorHook | ||||
| 	var splunkClient *splunk.Splunk | ||||
| 	telemetryClient := &telemetry.Telemetry{} | ||||
|  | ||||
| 	var createIntegrationArtifactTransportCmd = &cobra.Command{ | ||||
| 		Use:   STEP_NAME, | ||||
| 		Short: "Integration Package transport using the SAP Content Agent Service", | ||||
| 		Long:  `With this step you can trigger an Integration Package transport from SAP Integration Suite using SAP Content Agent Service and SAP Cloud Transport Management Service. For more information about doing an Integration Package transport using SAP Content Agent Service see the documentation [here](https://help.sap.com/docs/CONTENT_AGENT_SERVICE/ae1a4f2d150d468d9ff56e13f9898e07/8e274fdd41da45a69ff919c0af8c6127.html).`, | ||||
| 		PreRunE: func(cmd *cobra.Command, _ []string) error { | ||||
| 			startTime = time.Now() | ||||
| 			log.SetStepName(STEP_NAME) | ||||
| 			log.SetVerbose(GeneralConfig.Verbose) | ||||
|  | ||||
| 			GeneralConfig.GitHubAccessTokens = ResolveAccessTokens(GeneralConfig.GitHubTokens) | ||||
|  | ||||
| 			path, _ := os.Getwd() | ||||
| 			fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path} | ||||
| 			log.RegisterHook(fatalHook) | ||||
|  | ||||
| 			err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile) | ||||
| 			if err != nil { | ||||
| 				log.SetErrorCategory(log.ErrorConfiguration) | ||||
| 				return err | ||||
| 			} | ||||
| 			log.RegisterSecret(stepConfig.CasServiceKey) | ||||
|  | ||||
| 			if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 { | ||||
| 				sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID) | ||||
| 				log.RegisterHook(&sentryHook) | ||||
| 			} | ||||
|  | ||||
| 			if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 { | ||||
| 				splunkClient = &splunk.Splunk{} | ||||
| 				logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID} | ||||
| 				log.RegisterHook(logCollector) | ||||
| 			} | ||||
|  | ||||
| 			if err = log.RegisterANSHookIfConfigured(GeneralConfig.CorrelationID); err != nil { | ||||
| 				log.Entry().WithError(err).Warn("failed to set up SAP Alert Notification Service log hook") | ||||
| 			} | ||||
|  | ||||
| 			validation, err := validation.New(validation.WithJSONNamesForStructFields(), validation.WithPredefinedErrorMessages()) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if err = validation.ValidateStruct(stepConfig); err != nil { | ||||
| 				log.SetErrorCategory(log.ErrorConfiguration) | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		Run: func(_ *cobra.Command, _ []string) { | ||||
| 			stepTelemetryData := telemetry.CustomData{} | ||||
| 			stepTelemetryData.ErrorCode = "1" | ||||
| 			handler := func() { | ||||
| 				config.RemoveVaultSecretFiles() | ||||
| 				stepTelemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds()) | ||||
| 				stepTelemetryData.ErrorCategory = log.GetErrorCategory().String() | ||||
| 				stepTelemetryData.PiperCommitHash = GitCommit | ||||
| 				telemetryClient.SetData(&stepTelemetryData) | ||||
| 				telemetryClient.Send() | ||||
| 				if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 { | ||||
| 					splunkClient.Send(telemetryClient.GetData(), logCollector) | ||||
| 				} | ||||
| 			} | ||||
| 			log.DeferExitHandler(handler) | ||||
| 			defer handler() | ||||
| 			telemetryClient.Initialize(GeneralConfig.NoTelemetry, STEP_NAME) | ||||
| 			if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 { | ||||
| 				splunkClient.Initialize(GeneralConfig.CorrelationID, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Dsn, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Token, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Index, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.SendLogs) | ||||
| 			} | ||||
| 			integrationArtifactTransport(stepConfig, &stepTelemetryData) | ||||
| 			stepTelemetryData.ErrorCode = "0" | ||||
| 			log.Entry().Info("SUCCESS") | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	addIntegrationArtifactTransportFlags(createIntegrationArtifactTransportCmd, &stepConfig) | ||||
| 	return createIntegrationArtifactTransportCmd | ||||
| } | ||||
|  | ||||
| func addIntegrationArtifactTransportFlags(cmd *cobra.Command, stepConfig *integrationArtifactTransportOptions) { | ||||
| 	cmd.Flags().StringVar(&stepConfig.CasServiceKey, "casServiceKey", os.Getenv("PIPER_casServiceKey"), "Service key JSON string to access the CAS service instance") | ||||
| 	cmd.Flags().StringVar(&stepConfig.IntegrationPackageID, "integrationPackageId", os.Getenv("PIPER_integrationPackageId"), "Specifies the ID of the integration package artifact.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ResourceID, "resourceID", os.Getenv("PIPER_resourceID"), "Specifies the technical ID of the integration package artifact.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Name, "name", os.Getenv("PIPER_name"), "Specifies the name of the integration package artifact.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.Version, "version", os.Getenv("PIPER_version"), "Specifies the version of the Integration Package artifact.") | ||||
|  | ||||
| 	cmd.MarkFlagRequired("casServiceKey") | ||||
| 	cmd.MarkFlagRequired("integrationPackageId") | ||||
| 	cmd.MarkFlagRequired("resourceID") | ||||
| 	cmd.MarkFlagRequired("name") | ||||
| 	cmd.MarkFlagRequired("version") | ||||
| } | ||||
|  | ||||
| // retrieve step metadata | ||||
| func integrationArtifactTransportMetadata() config.StepData { | ||||
| 	var theMetaData = config.StepData{ | ||||
| 		Metadata: config.StepMetadata{ | ||||
| 			Name:        "integrationArtifactTransport", | ||||
| 			Aliases:     []config.Alias{}, | ||||
| 			Description: "Integration Package transport using the SAP Content Agent Service", | ||||
| 		}, | ||||
| 		Spec: config.StepSpec{ | ||||
| 			Inputs: config.StepInputs{ | ||||
| 				Secrets: []config.StepSecrets{ | ||||
| 					{Name: "casApiServiceKeyCredentialsId", Description: "Jenkins secret text credential ID containing the service key to the CAS service instance", Type: "jenkins"}, | ||||
| 				}, | ||||
| 				Parameters: []config.StepParameters{ | ||||
| 					{ | ||||
| 						Name: "casServiceKey", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name:  "casApiServiceKeyCredentialsId", | ||||
| 								Param: "casServiceKey", | ||||
| 								Type:  "secret", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"PARAMETERS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: true, | ||||
| 						Aliases:   []config.Alias{}, | ||||
| 						Default:   os.Getenv("PIPER_casServiceKey"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "integrationPackageId", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "GENERAL", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   true, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_integrationPackageId"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "resourceID", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "GENERAL", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   true, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_resourceID"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "name", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "GENERAL", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   true, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_name"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "version", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "GENERAL", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   true, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_version"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return theMetaData | ||||
| } | ||||
							
								
								
									
										17
									
								
								cmd/integrationArtifactTransport_generated_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								cmd/integrationArtifactTransport_generated_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestIntegrationArtifactTransportCommand(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testCmd := IntegrationArtifactTransportCommand() | ||||
|  | ||||
| 	// only high level testing performed - details are tested in step generation procedure | ||||
| 	assert.Equal(t, "integrationArtifactTransport", testCmd.Use, "command name incorrect") | ||||
|  | ||||
| } | ||||
							
								
								
									
										128
									
								
								cmd/integrationArtifactTransport_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								cmd/integrationArtifactTransport_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/apim" | ||||
| 	apimhttp "github.com/SAP/jenkins-library/pkg/apim" | ||||
| 	"github.com/SAP/jenkins-library/pkg/mock" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| type integrationArtifactTransportMockUtils struct { | ||||
| 	*mock.ExecMockRunner | ||||
| 	*mock.FilesMock | ||||
| } | ||||
|  | ||||
| func newIntegrationArtifactTransportTestsUtils() integrationArtifactTransportMockUtils { | ||||
| 	utils := integrationArtifactTransportMockUtils{ | ||||
| 		ExecMockRunner: &mock.ExecMockRunner{}, | ||||
| 		FilesMock:      &mock.FilesMock{}, | ||||
| 	} | ||||
| 	return utils | ||||
| } | ||||
|  | ||||
| func TestRunIntegrationArtifactTransport(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	t.Run("Create Transport Request Successful test", func(t *testing.T) { | ||||
| 		config := getDefaultOptionsForIntegrationArtifactTransport() | ||||
| 		httpClientMock := &apimhttp.HttpMockAPIM{StatusCode: 202, ResponseBody: `{"processId": "100", "state": "FINISHED"}`} | ||||
| 		apim := apim.Bundle{APIServiceKey: config.CasServiceKey, Client: httpClientMock} | ||||
| 		// test | ||||
| 		err := CreateIntegrationArtifactTransportRequest(&config, apim) | ||||
| 		// assert | ||||
| 		if assert.NoError(t, err) { | ||||
| 			t.Run("check url", func(t *testing.T) { | ||||
| 				assert.Equal(t, "/v1/operations/100", httpClientMock.URL) | ||||
| 			}) | ||||
| 			t.Run("check method", func(t *testing.T) { | ||||
| 				assert.Equal(t, "GET", httpClientMock.Method) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("getIntegrationTransportProcessingStatus successful test", func(t *testing.T) { | ||||
| 		config := getDefaultOptionsForIntegrationArtifactTransport() | ||||
| 		httpClientMock := &apimhttp.HttpMockAPIM{StatusCode: 200, ResponseBody: `{"state": "FINISHED"}`} | ||||
| 		// test | ||||
| 		resp, err := getIntegrationTransportProcessingStatus(&config, httpClientMock, "demo", "100") | ||||
|  | ||||
| 		// assert | ||||
| 		assert.Equal(t, "FINISHED", resp) | ||||
| 		assert.NoError(t, err) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("getIntegrationTransportError successful test", func(t *testing.T) { | ||||
| 		config := getDefaultOptionsForIntegrationArtifactTransport() | ||||
| 		httpClientMock := &apimhttp.HttpMockAPIM{StatusCode: 200, ResponseBody: `{ "logs": [] }`} | ||||
| 		// test | ||||
| 		resp, err := getIntegrationTransportError(&config, httpClientMock, "demo", "100") | ||||
|  | ||||
| 		// assert | ||||
| 		assert.Equal(t, "{ \"logs\": [] }", resp) | ||||
| 		// assert | ||||
| 		if assert.NoError(t, err) { | ||||
| 			t.Run("check url", func(t *testing.T) { | ||||
| 				assert.Equal(t, "demo/v1/operations/100/logs", httpClientMock.URL) | ||||
| 			}) | ||||
| 			t.Run("check method", func(t *testing.T) { | ||||
| 				assert.Equal(t, "GET", httpClientMock.Method) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("GetCPITransportReqPayload successful test", func(t *testing.T) { | ||||
| 		config := getDefaultOptionsForIntegrationArtifactTransport() | ||||
| 		// test | ||||
| 		resp, err := GetCPITransportReqPayload(&config) | ||||
| 		fmt.Println(resp.String()) | ||||
| 		// assert | ||||
| 		expJson := `{"contentType":"d9c3fe08ceeb47a2991e53049f2ed766","id":"TestTransport","name":"TestTransport","resourceID":"d9c3fe08ceeb47a2991e53049f2ed766","subType":"package","type":"CloudIntegration","version":"1.0"}` | ||||
| 		actJson := resp.String() | ||||
| 		assert.Contains(t, actJson, expJson) | ||||
| 		assert.NoError(t, err) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Create Transport Request negative test1", func(t *testing.T) { | ||||
| 		config := getDefaultOptionsForIntegrationArtifactTransport() | ||||
| 		httpClientMock := &apimhttp.HttpMockAPIM{StatusCode: 202, ResponseBody: `{"processId": ""}`} | ||||
| 		apim := apim.Bundle{APIServiceKey: config.CasServiceKey, Client: httpClientMock} | ||||
| 		// test | ||||
| 		err := CreateIntegrationArtifactTransportRequest(&config, apim) | ||||
| 		assert.Equal(t, "/v1/contentResources/export", httpClientMock.URL) | ||||
| 		assert.Equal(t, "POST", httpClientMock.Method) | ||||
| 		assert.Error(t, err) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Create Transport Request negative test2", func(t *testing.T) { | ||||
| 		config := getDefaultOptionsForIntegrationArtifactTransport() | ||||
| 		httpClientMock := &apimhttp.HttpMockAPIM{StatusCode: 400, ResponseBody: ``} | ||||
| 		apim := apim.Bundle{APIServiceKey: config.CasServiceKey, Client: httpClientMock} | ||||
| 		// test | ||||
| 		err := CreateIntegrationArtifactTransportRequest(&config, apim) | ||||
| 		assert.EqualError(t, err, "HTTP POST request to /v1/contentResources/export failed with error: Bad Request") | ||||
| 	}) | ||||
|  | ||||
| } | ||||
|  | ||||
| func getDefaultOptionsForIntegrationArtifactTransport() integrationArtifactTransportOptions { | ||||
|  | ||||
| 	apiServiceKey := `{ | ||||
| 		"oauth": { | ||||
| 			"url": "https://demo", | ||||
| 			"clientid": "sb-2d0622c9", | ||||
| 			"clientsecret": "edb5c506=", | ||||
| 			"tokenurl": "https://demo/oauth/token" | ||||
| 		} | ||||
| 	}` | ||||
|  | ||||
| 	return integrationArtifactTransportOptions{ | ||||
| 		CasServiceKey:        apiServiceKey, | ||||
| 		IntegrationPackageID: "TestTransport", | ||||
| 		ResourceID:           "d9c3fe08ceeb47a2991e53049f2ed766", | ||||
| 		Name:                 "TestTransport", | ||||
| 		Version:              "1.0", | ||||
| 	} | ||||
| } | ||||
| @@ -75,6 +75,7 @@ func GetAllStepMetadata() map[string]config.StepData { | ||||
| 		"integrationArtifactGetMplStatus":           integrationArtifactGetMplStatusMetadata(), | ||||
| 		"integrationArtifactGetServiceEndpoint":     integrationArtifactGetServiceEndpointMetadata(), | ||||
| 		"integrationArtifactResource":               integrationArtifactResourceMetadata(), | ||||
| 		"integrationArtifactTransport":              integrationArtifactTransportMetadata(), | ||||
| 		"integrationArtifactTriggerIntegrationTest": integrationArtifactTriggerIntegrationTestMetadata(), | ||||
| 		"integrationArtifactUnDeploy":               integrationArtifactUnDeployMetadata(), | ||||
| 		"integrationArtifactUpdateConfiguration":    integrationArtifactUpdateConfigurationMetadata(), | ||||
|   | ||||
| @@ -192,6 +192,7 @@ func Execute() { | ||||
| 	rootCmd.AddCommand(AnsSendEventCommand()) | ||||
| 	rootCmd.AddCommand(ApiProviderListCommand()) | ||||
| 	rootCmd.AddCommand(TmsUploadCommand()) | ||||
| 	rootCmd.AddCommand(IntegrationArtifactTransportCommand()) | ||||
|  | ||||
| 	addRootFlags(rootCmd) | ||||
|  | ||||
|   | ||||
							
								
								
									
										45
									
								
								documentation/docs/steps/integrationArtifactTransport.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								documentation/docs/steps/integrationArtifactTransport.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| # ${docGenStepName} | ||||
|  | ||||
| ## ${docGenDescription} | ||||
|  | ||||
| With this step, you can transport Integration Packages from SAP Integration Suite across various landscapes using SAP Content Agent Service. | ||||
|  | ||||
| SAP Integration Suite provides the ability to transport its content to other services. SAP Content Agent service enables you to assemble the content from various content providers (including SAP Integration Suite) in MTAR format. Later, this content is either available for download or can be exported to a configured transport queue, such as SAP Cloud Transport Management. This step, integrationArtifactTransport, only supports transporting Integration Packages from SAP Integration Suite. For more information on | ||||
| configurations required for SAP Integration Suite, see [Content Assembly for SAP Integration Suite](https://help.sap.com/docs/CONTENT_AGENT_SERVICE/ae1a4f2d150d468d9ff56e13f9898e07/8e274fdd41da45a69ff919c0af8c6127.html) | ||||
|  | ||||
| To use the integrationArtifactTransport step, proceed as follows: | ||||
|  | ||||
| * [Create SAP Content Agent Service Destination](https://help.sap.com/docs/CONTENT_AGENT_SERVICE/ae1a4f2d150d468d9ff56e13f9898e07/a4da0c26ced74bbfbc60e7f607dc05ab.html). | ||||
| * [Create Cloud Integration Destination](https://help.sap.com/docs/CONTENT_AGENT_SERVICE/ae1a4f2d150d468d9ff56e13f9898e07/c17c4004049d4d9dba373d72ce5610cd.html). | ||||
| * [Create SAP Cloud Transport Management Destination](https://help.sap.com/docs/CONTENT_AGENT_SERVICE/ae1a4f2d150d468d9ff56e13f9898e07/b44463a657fa4be48ea2525b7eb6e7de.html). | ||||
| * Transport Cloud Integration Content with SAP Content Agent Service as explained in the blog [TMS – Transport SAP Cloud Integration (CI/CPI) Content with Transport Management Service (TMS) and Content Agent Service (CAS)](https://blogs.sap.com/2022/03/25/transport-sap-cloud-integration-ci-cpi-content-with-transport-management-service-tms-and-content-agent-service-cas/) | ||||
| * integrationArtifactTransport step only supports Integration Package transport | ||||
|  | ||||
| ## Prerequisites | ||||
|  | ||||
| ## ${docGenParameters} | ||||
|  | ||||
| ## ${docGenConfiguration} | ||||
|  | ||||
| ## ${docJenkinsPluginDependencies} | ||||
|  | ||||
| ## Example | ||||
|  | ||||
| Configuration example for a `Jenkinsfile`: | ||||
|  | ||||
| ```groovy | ||||
| integrationArtifactTransport script: this | ||||
| ``` | ||||
|  | ||||
| Configuration example for a YAML file (for example `.pipeline/config.yaml`): | ||||
|  | ||||
| ```yaml | ||||
| steps: | ||||
|   <...> | ||||
|   integrationArtifactTransport: | ||||
|     casApiServiceKeyCredentialsId: 'MY_API_SERVICE_KEY' | ||||
|     integrationPackageId: MY_INTEGRATION_PACKAGE_ID | ||||
|     resourceID: MY_INTEGRATION_RESOURCE_ID | ||||
|     name: MY_INTEGRATION_PACKAGE_NAME | ||||
|     version: MY_INTEGRATION_PACKAGE_VERSION | ||||
| ``` | ||||
| @@ -126,6 +126,7 @@ nav: | ||||
|         - integrationArtifactGetMplStatus: steps/integrationArtifactGetMplStatus.md | ||||
|         - integrationArtifactGetServiceEndpoint: steps/integrationArtifactGetServiceEndpoint.md | ||||
|         - integrationArtifactResource: steps/integrationArtifactResource.md | ||||
|         - integrationArtifactTransport: steps/integrationArtifactTransport.md         | ||||
|         - integrationArtifactUnDeploy: steps/integrationArtifactUnDeploy.md | ||||
|         - integrationArtifactUpdateConfiguration: steps/integrationArtifactUpdateConfiguration.md | ||||
|         - integrationArtifactUpload: steps/integrationArtifactUpload.md | ||||
|   | ||||
							
								
								
									
										60
									
								
								resources/metadata/integrationArtifactTransport.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								resources/metadata/integrationArtifactTransport.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| metadata: | ||||
|   name: integrationArtifactTransport | ||||
|   description: Integration Package transport using the SAP Content Agent Service | ||||
|   longDescription: | | ||||
|     With this step you can trigger an Integration Package transport from SAP Integration Suite using SAP Content Agent Service and SAP Cloud Transport Management Service. For more information about doing an Integration Package transport using SAP Content Agent Service see the documentation [here](https://help.sap.com/docs/CONTENT_AGENT_SERVICE/ae1a4f2d150d468d9ff56e13f9898e07/8e274fdd41da45a69ff919c0af8c6127.html). | ||||
|  | ||||
| spec: | ||||
|   inputs: | ||||
|     secrets: | ||||
|       - name: casApiServiceKeyCredentialsId | ||||
|         description: Jenkins secret text credential ID containing the service key to the CAS service instance | ||||
|         type: jenkins | ||||
|     params: | ||||
|       - name: casServiceKey | ||||
|         type: string | ||||
|         description: Service key JSON string to access the CAS service instance | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|         mandatory: true | ||||
|         secret: true | ||||
|         resourceRef: | ||||
|           - name: casApiServiceKeyCredentialsId | ||||
|             type: secret | ||||
|             param: casServiceKey | ||||
|       - name: integrationPackageId | ||||
|         type: string | ||||
|         description: Specifies the ID of the integration package artifact. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - GENERAL | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         mandatory: true | ||||
|       - name: resourceID | ||||
|         type: string | ||||
|         description: Specifies the technical ID of the integration package artifact. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - GENERAL | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         mandatory: true | ||||
|       - name: name | ||||
|         type: string | ||||
|         description: Specifies the name of the integration package artifact. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - GENERAL | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         mandatory: true | ||||
|       - name: version | ||||
|         type: string | ||||
|         description: Specifies the version of the Integration Package artifact. | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - GENERAL | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         mandatory: true | ||||
| @@ -193,6 +193,7 @@ public class CommonStepsTest extends BasePiperTest{ | ||||
|         'integrationArtifactGetServiceEndpoint', //implementing new golang pattern without fields | ||||
|         'integrationArtifactDownload', //implementing new golang pattern without fields | ||||
|         'integrationArtifactUpload', //implementing new golang pattern without fields | ||||
|         'integrationArtifactTransport', //implementing new golang pattern without fields           | ||||
|         'integrationArtifactTriggerIntegrationTest', //implementing new golang pattern without fields | ||||
|         'integrationArtifactUnDeploy', //implementing new golang pattern without fields | ||||
|         'integrationArtifactResource', //implementing new golang pattern without fields | ||||
| @@ -223,7 +224,7 @@ public class CommonStepsTest extends BasePiperTest{ | ||||
|         'azureBlobUpload', | ||||
|         'awsS3Upload', | ||||
|         'ansSendEvent', | ||||
|         'apiProviderList', //implementing new golang pattern without fields | ||||
|         'apiProviderList', //implementing new golang pattern without fields     | ||||
|     ] | ||||
|  | ||||
|     @Test | ||||
|   | ||||
							
								
								
									
										11
									
								
								vars/integrationArtifactTransport.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vars/integrationArtifactTransport.groovy
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import groovy.transform.Field | ||||
|  | ||||
| @Field String STEP_NAME = getClass().getName() | ||||
| @Field String METADATA_FILE = 'metadata/integrationArtifactTransport.yaml' | ||||
|  | ||||
| void call(Map parameters = [:]) { | ||||
|     List credentials = [ | ||||
|         [type: 'token', id: 'casApiServiceKeyCredentialsId', env: ['PIPER_casServiceKey']] | ||||
|     ] | ||||
|     piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user