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 
			
		
		
		
	ADO - Vault Secret Rotation (#3084)
* Implemented vault secret rotation for ADO * Added tests * Fixed issues
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							3921c563c9
						
					
				
				
					commit
					d8d533b154
				
			| @@ -2,11 +2,13 @@ package cmd | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/hashicorp/vault/api" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/ado" | ||||
| 	"github.com/SAP/jenkins-library/pkg/jenkins" | ||||
| 	"github.com/SAP/jenkins-library/pkg/vault" | ||||
|  | ||||
| @@ -93,6 +95,7 @@ func runVaultRotateSecretID(utils vaultRotateSecretIDUtils) error { | ||||
|  | ||||
| 	if err = utils.UpdateSecretInStore(config, newSecretID); err != nil { | ||||
| 		log.Entry().WithError(err).Warnf("Could not write secret back to secret store %s", config.SecretStore) | ||||
| 		return err | ||||
| 	} | ||||
| 	log.Entry().Infof("Secret has been successfully updated in secret store %s", config.SecretStore) | ||||
| 	return nil | ||||
| @@ -111,6 +114,25 @@ func writeVaultSecretIDToStore(config *vaultRotateSecretIdOptions, secretID stri | ||||
| 		credManager := jenkins.NewCredentialsManager(instance) | ||||
| 		credential := jenkins.StringCredentials{ID: config.VaultAppRoleSecretTokenCredentialsID, Secret: secretID} | ||||
| 		return jenkins.UpdateCredential(ctx, credManager, config.JenkinsCredentialDomain, credential) | ||||
| 	case "ado": | ||||
| 		adoBuildClient, err := ado.NewBuildClient(config.AdoOrganization, config.AdoPersonalAccessToken, config.AdoProject, config.AdoPipelineID) | ||||
| 		if err != nil { | ||||
| 			log.Entry().Warn("Could not write secret ID back to Azure DevOps") | ||||
| 			return err | ||||
| 		} | ||||
| 		variables := []ado.Variable{ | ||||
| 			{ | ||||
| 				Name:     config.VaultAppRoleSecretTokenCredentialsID, | ||||
| 				Value:    secretID, | ||||
| 				IsSecret: true, | ||||
| 			}, | ||||
| 		} | ||||
| 		if err := adoBuildClient.UpdateVariables(variables); err != nil { | ||||
| 			log.Entry().Warn("Could not write secret ID back to Azure DevOps") | ||||
| 			return err | ||||
| 		} | ||||
| 	default: | ||||
| 		return fmt.Errorf("error: invalid secret store: %s", config.SecretStore) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -24,6 +24,10 @@ type vaultRotateSecretIdOptions struct { | ||||
| 	VaultServerURL                       string `json:"vaultServerUrl,omitempty"` | ||||
| 	VaultNamespace                       string `json:"vaultNamespace,omitempty"` | ||||
| 	DaysBeforeExpiry                     int    `json:"daysBeforeExpiry,omitempty"` | ||||
| 	AdoOrganization                      string `json:"adoOrganization,omitempty"` | ||||
| 	AdoPersonalAccessToken               string `json:"adoPersonalAccessToken,omitempty"` | ||||
| 	AdoProject                           string `json:"adoProject,omitempty"` | ||||
| 	AdoPipelineID                        int    `json:"adoPipelineId,omitempty"` | ||||
| } | ||||
|  | ||||
| // VaultRotateSecretIdCommand Rotate vault AppRole Secret ID | ||||
| @@ -58,6 +62,7 @@ func VaultRotateSecretIdCommand() *cobra.Command { | ||||
| 			log.RegisterSecret(stepConfig.JenkinsURL) | ||||
| 			log.RegisterSecret(stepConfig.JenkinsUsername) | ||||
| 			log.RegisterSecret(stepConfig.JenkinsToken) | ||||
| 			log.RegisterSecret(stepConfig.AdoPersonalAccessToken) | ||||
|  | ||||
| 			if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 { | ||||
| 				sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID) | ||||
| @@ -109,10 +114,14 @@ func addVaultRotateSecretIdFlags(cmd *cobra.Command, stepConfig *vaultRotateSecr | ||||
| 	cmd.Flags().StringVar(&stepConfig.JenkinsCredentialDomain, "jenkinsCredentialDomain", `_`, "The jenkins credential domain which should be used") | ||||
| 	cmd.Flags().StringVar(&stepConfig.JenkinsUsername, "jenkinsUsername", os.Getenv("PIPER_jenkinsUsername"), "The jenkins username") | ||||
| 	cmd.Flags().StringVar(&stepConfig.JenkinsToken, "jenkinsToken", os.Getenv("PIPER_jenkinsToken"), "The jenkins token") | ||||
| 	cmd.Flags().StringVar(&stepConfig.VaultAppRoleSecretTokenCredentialsID, "vaultAppRoleSecretTokenCredentialsId", os.Getenv("PIPER_vaultAppRoleSecretTokenCredentialsId"), "The Jenkins credential ID for the Vault AppRole Secret ID credential") | ||||
| 	cmd.Flags().StringVar(&stepConfig.VaultAppRoleSecretTokenCredentialsID, "vaultAppRoleSecretTokenCredentialsId", os.Getenv("PIPER_vaultAppRoleSecretTokenCredentialsId"), "The Jenkins credential ID or Azure DevOps variable name for the Vault AppRole Secret ID credential") | ||||
| 	cmd.Flags().StringVar(&stepConfig.VaultServerURL, "vaultServerUrl", os.Getenv("PIPER_vaultServerUrl"), "The URL for the Vault server to use") | ||||
| 	cmd.Flags().StringVar(&stepConfig.VaultNamespace, "vaultNamespace", os.Getenv("PIPER_vaultNamespace"), "The vault namespace that should be used (optional)") | ||||
| 	cmd.Flags().IntVar(&stepConfig.DaysBeforeExpiry, "daysBeforeExpiry", 15, "The amount of days before expiry until the secret ID gets rotated") | ||||
| 	cmd.Flags().StringVar(&stepConfig.AdoOrganization, "adoOrganization", os.Getenv("PIPER_adoOrganization"), "The Azure DevOps organization name") | ||||
| 	cmd.Flags().StringVar(&stepConfig.AdoPersonalAccessToken, "adoPersonalAccessToken", os.Getenv("PIPER_adoPersonalAccessToken"), "The Azure DevOps personal access token") | ||||
| 	cmd.Flags().StringVar(&stepConfig.AdoProject, "adoProject", os.Getenv("PIPER_adoProject"), "The Azure DevOps project ID. Project name also can be used") | ||||
| 	cmd.Flags().IntVar(&stepConfig.AdoPipelineID, "adoPipelineId", 0, "The Azure DevOps pipeline ID. Also called as definition ID") | ||||
|  | ||||
| 	cmd.MarkFlagRequired("vaultAppRoleSecretTokenCredentialsId") | ||||
| 	cmd.MarkFlagRequired("vaultServerUrl") | ||||
| @@ -228,6 +237,48 @@ func vaultRotateSecretIdMetadata() config.StepData { | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     15, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "adoOrganization", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_adoOrganization"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name: "adoPersonalAccessToken", | ||||
| 						ResourceRef: []config.ResourceReference{ | ||||
| 							{ | ||||
| 								Name:  "", | ||||
| 								Paths: []string{"$(vaultPath)/jenkins", "$(vaultBasePath)/$(vaultPipelineName)/jenkins", "$(vaultBasePath)/GROUP-SECRETS/jenkins"}, | ||||
| 								Type:  "vaultSecret", | ||||
| 							}, | ||||
| 						}, | ||||
| 						Scope:     []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:      "string", | ||||
| 						Mandatory: false, | ||||
| 						Aliases:   []config.Alias{}, | ||||
| 						Default:   os.Getenv("PIPER_adoPersonalAccessToken"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "adoProject", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     os.Getenv("PIPER_adoProject"), | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "adoPipelineId", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "int", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     0, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @@ -27,6 +27,7 @@ require ( | ||||
| 	github.com/google/go-containerregistry v0.1.3 | ||||
| 	github.com/google/go-github/v32 v32.1.0 | ||||
| 	github.com/google/uuid v1.1.2 | ||||
| 	github.com/gorilla/websocket v1.4.2 // indirect | ||||
| 	github.com/hashicorp/go-retryablehttp v0.6.7 | ||||
| 	github.com/hashicorp/vault v1.7.2 | ||||
| 	github.com/hashicorp/vault/api v1.1.0 | ||||
| @@ -38,6 +39,7 @@ require ( | ||||
| 	github.com/magicsong/color-glog v0.0.1 // indirect | ||||
| 	github.com/magicsong/sonargo v0.0.1 | ||||
| 	github.com/mailru/easyjson v0.7.6 // indirect | ||||
| 	github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 | ||||
| 	github.com/motemen/go-nuts v0.0.0-20200601065735-3df31f16cb2f | ||||
| 	github.com/piper-validation/fortify-client-go v0.0.0-20210114140201-1261216783c6 | ||||
| 	github.com/pkg/errors v0.9.1 | ||||
|   | ||||
							
								
								
									
										5
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								go.sum
									
									
									
									
									
								
							| @@ -731,8 +731,9 @@ github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYb | ||||
| github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= | ||||
| github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
| github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= | ||||
| github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= | ||||
| github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= | ||||
| github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= | ||||
| github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= | ||||
| @@ -1131,6 +1132,8 @@ github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1 | ||||
| github.com/michaelklishin/rabbit-hole v0.0.0-20191008194146-93d9988f0cd5 h1:uA3b4GgZMZxAJsTkd+CVQ85b7KBlD7HLpd/FfTNlGN0= | ||||
| github.com/michaelklishin/rabbit-hole v0.0.0-20191008194146-93d9988f0cd5/go.mod h1:+pmbihVqjC3GPdfWv1V2TnRSuVvwrWLKfEP/MZVB/Wc= | ||||
| github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= | ||||
| github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 h1:YH424zrwLTlyHSH/GzLMJeu5zhYVZSx5RQxGKm1h96s= | ||||
| github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= | ||||
| github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | ||||
| github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= | ||||
| github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= | ||||
|   | ||||
							
								
								
									
										109
									
								
								pkg/ado/ado.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								pkg/ado/ado.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| package ado | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/microsoft/azure-devops-go-api/azuredevops" | ||||
| 	"github.com/microsoft/azure-devops-go-api/azuredevops/build" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| const azureUrl = "https://dev.azure.com" | ||||
|  | ||||
| type BuildClient interface { | ||||
| 	UpdateVariables(variables []Variable) error | ||||
| } | ||||
|  | ||||
| type BuildClientImpl struct { | ||||
| 	ctx         context.Context | ||||
| 	buildClient build.Client | ||||
| 	project     string | ||||
| 	pipelineID  int | ||||
| } | ||||
|  | ||||
| type Variable struct { | ||||
| 	Name          string | ||||
| 	Value         string | ||||
| 	IsSecret      bool | ||||
| 	AllowOverride bool | ||||
| } | ||||
|  | ||||
| //UpdateVariables updates variables in build definition or creates them if they are missing | ||||
| func (bc *BuildClientImpl) UpdateVariables(variables []Variable) error { | ||||
| 	if len(variables) == 0 { | ||||
| 		return errors.New("error: slice variables must not be empty") | ||||
| 	} | ||||
| 	getDefinitionArgs := build.GetDefinitionArgs{ | ||||
| 		Project:      &bc.project, | ||||
| 		DefinitionId: &bc.pipelineID, | ||||
| 	} | ||||
|  | ||||
| 	// Get a build definition | ||||
| 	buildDefinition, err := bc.buildClient.GetDefinition(bc.ctx, getDefinitionArgs) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrapf(err, "error: get definition failed") | ||||
| 	} | ||||
|  | ||||
| 	buildDefinitionVars := map[string]build.BuildDefinitionVariable{} | ||||
| 	if buildDefinition.Variables != nil { | ||||
| 		buildDefinitionVars = *buildDefinition.Variables | ||||
| 	} | ||||
|  | ||||
| 	for _, variable := range variables { | ||||
| 		buildDefinitionVars[variable.Name] = build.BuildDefinitionVariable{ | ||||
| 			Value:         &variable.Value, | ||||
| 			IsSecret:      &variable.IsSecret, | ||||
| 			AllowOverride: &variable.AllowOverride, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	buildDefinition.Variables = &buildDefinitionVars | ||||
|  | ||||
| 	updateDefinitionArgs := build.UpdateDefinitionArgs{ | ||||
| 		Definition:   buildDefinition, | ||||
| 		Project:      &bc.project, | ||||
| 		DefinitionId: &bc.pipelineID, | ||||
| 	} | ||||
|  | ||||
| 	_, err = bc.buildClient.UpdateDefinition(bc.ctx, updateDefinitionArgs) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrapf(err, "error: update definition failed") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| //NewBuildClient Create a client to interact with the Build area | ||||
| func NewBuildClient(organization string, personalAccessToken string, project string, pipelineID int) (BuildClient, error) { | ||||
| 	if organization == "" { | ||||
| 		return nil, errors.New("error: organization must not be empty") | ||||
| 	} | ||||
| 	if personalAccessToken == "" { | ||||
| 		return nil, errors.New("error: personal access token must not be empty") | ||||
| 	} | ||||
| 	if project == "" { | ||||
| 		return nil, errors.New("error: project must not be empty") | ||||
| 	} | ||||
|  | ||||
| 	organizationUrl := fmt.Sprintf("%s/%s", azureUrl, organization) | ||||
| 	// Create a connection to your organization | ||||
| 	connection := azuredevops.NewPatConnection(organizationUrl, personalAccessToken) | ||||
|  | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	// Create a client to interact with the Core area | ||||
| 	buildClient, err := build.NewClient(ctx, connection) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	buildClientImpl := &BuildClientImpl{ | ||||
| 		ctx:         ctx, | ||||
| 		buildClient: buildClient, | ||||
| 		project:     project, | ||||
| 		pipelineID:  pipelineID, | ||||
| 	} | ||||
|  | ||||
| 	return buildClientImpl, nil | ||||
| } | ||||
							
								
								
									
										100
									
								
								pkg/ado/ado_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								pkg/ado/ado_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| package ado | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/ado/mocks" | ||||
| 	"github.com/microsoft/azure-devops-go-api/azuredevops/build" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| ) | ||||
|  | ||||
| func TestUpdateVariables(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	ctx := context.Background() | ||||
| 	const secretName = "test-secret" | ||||
| 	const secretValue = "secret-value" | ||||
| 	const projectID = "some-id" | ||||
| 	const pipelineID = 1 | ||||
| 	testErr := errors.New("error") | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		name                  string | ||||
| 		variables             []Variable | ||||
| 		getDefinitionError    error | ||||
| 		updateDefinitionError error | ||||
| 		isErrorExpected       bool | ||||
| 		errorStr              string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:                  "Test update secret - successful", | ||||
| 			variables:             []Variable{{Name: secretName, Value: secretValue, IsSecret: true}}, | ||||
| 			getDefinitionError:    nil, | ||||
| 			updateDefinitionError: nil, | ||||
| 			isErrorExpected:       false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                  "Failed get definition", | ||||
| 			variables:             []Variable{{Name: secretName, Value: secretValue, IsSecret: true}}, | ||||
| 			getDefinitionError:    testErr, | ||||
| 			updateDefinitionError: nil, | ||||
| 			isErrorExpected:       true, | ||||
| 			errorStr:              "get definition failed", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                  "Failed update definition", | ||||
| 			variables:             []Variable{{Name: secretName, Value: secretValue, IsSecret: true}}, | ||||
| 			getDefinitionError:    nil, | ||||
| 			updateDefinitionError: testErr, | ||||
| 			isErrorExpected:       true, | ||||
| 			errorStr:              "update definition failed", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                  "Slice variables is empty", | ||||
| 			variables:             []Variable{}, | ||||
| 			getDefinitionError:    nil, | ||||
| 			updateDefinitionError: nil, | ||||
| 			isErrorExpected:       true, | ||||
| 			errorStr:              "slice variables must not be empty", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			buildClientMock := &mocks.Client{} | ||||
| 			buildClientMock.On("GetDefinition", ctx, mock.Anything).Return( | ||||
| 				func(ctx context.Context, getDefinitionArgs build.GetDefinitionArgs) *build.BuildDefinition { | ||||
| 					return &build.BuildDefinition{} | ||||
| 				}, | ||||
| 				func(ctx context.Context, getDefinitionArgs build.GetDefinitionArgs) error { | ||||
| 					return tt.getDefinitionError | ||||
| 				}, | ||||
| 			) | ||||
|  | ||||
| 			buildClientMock.On("UpdateDefinition", ctx, mock.Anything).Return( | ||||
| 				func(ctx context.Context, updateDefinitionArgs build.UpdateDefinitionArgs) *build.BuildDefinition { | ||||
| 					return &build.BuildDefinition{} | ||||
| 				}, | ||||
| 				func(ctx context.Context, updateDefinitionArgs build.UpdateDefinitionArgs) error { | ||||
| 					return tt.updateDefinitionError | ||||
| 				}, | ||||
| 			) | ||||
|  | ||||
| 			buildClientImpl := BuildClientImpl{ | ||||
| 				ctx:         ctx, | ||||
| 				buildClient: buildClientMock, | ||||
| 				project:     projectID, | ||||
| 				pipelineID:  pipelineID, | ||||
| 			} | ||||
|  | ||||
| 			err := buildClientImpl.UpdateVariables(tt.variables) | ||||
| 			if tt.isErrorExpected { | ||||
| 				assert.Contains(t, err.Error(), tt.errorStr) | ||||
| 			} else { | ||||
| 				assert.NoError(t, err) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1769
									
								
								pkg/ado/mocks/Client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1769
									
								
								pkg/ado/mocks/Client.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -15,6 +15,7 @@ spec: | ||||
|         default: "jenkins" | ||||
|         possibleValues: | ||||
|           - jenkins | ||||
|           - ado | ||||
|       - name: jenkinsUrl | ||||
|         type: string | ||||
|         description: "The jenkins url" | ||||
| @@ -73,7 +74,7 @@ spec: | ||||
|               - $(vaultBasePath)/GROUP-SECRETS/jenkins | ||||
|       - name: vaultAppRoleSecretTokenCredentialsId | ||||
|         type: string | ||||
|         description: The Jenkins credential ID for the Vault AppRole Secret ID credential | ||||
|         description: The Jenkins credential ID or Azure DevOps variable name for the Vault AppRole Secret ID credential | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
| @@ -105,3 +106,39 @@ spec: | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         default: 15 | ||||
|       - name: adoOrganization | ||||
|         type: string | ||||
|         scope: | ||||
|           - GENERAL | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         description: The Azure DevOps organization name | ||||
|       - name: adoPersonalAccessToken | ||||
|         type: string | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         description: The Azure DevOps personal access token | ||||
|         secret: true | ||||
|         resourceRef: | ||||
|           - type: vaultSecret | ||||
|             paths: | ||||
|               - $(vaultPath)/jenkins | ||||
|               - $(vaultBasePath)/$(vaultPipelineName)/jenkins | ||||
|               - $(vaultBasePath)/GROUP-SECRETS/jenkins | ||||
|       - name: adoProject | ||||
|         type: string | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         description: The Azure DevOps project ID. Project name also can be used | ||||
|       - name: adoPipelineId | ||||
|         type: int | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         description: The Azure DevOps pipeline ID. Also called as definition ID | ||||
|   | ||||
		Reference in New Issue
	
	Block a user