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 
			
		
		
		
	fix(jenkins): fix job invocation (#2868)
* update mock * update signarture * add test case * use latest gojenkins * add integration test * update mock * add todo * add job wrapper * add job mock * add test cases * refactor * cleanup * update integration test case
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							d8a8a73184
						
					
				
				
					commit
					824cd7d768
				
			
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @@ -11,7 +11,7 @@ require ( | ||||
| 	github.com/Masterminds/sprig v2.22.0+incompatible | ||||
| 	github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect | ||||
| 	github.com/bmatcuk/doublestar v1.3.2 | ||||
| 	github.com/bndr/gojenkins v1.1.0 | ||||
| 	github.com/bndr/gojenkins v1.1.1-0.20210520222939-90ed82bfdff6 | ||||
| 	github.com/elliotchance/orderedmap v1.3.0 | ||||
| 	github.com/evanphx/json-patch v4.9.0+incompatible | ||||
| 	github.com/getsentry/sentry-go v0.7.0 | ||||
|   | ||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @@ -244,8 +244,8 @@ github.com/bmatcuk/doublestar v1.3.2 h1:mzUncgFmpzNUhIITFqGdZ8nUU0O7JTJzRO8VdkeL | ||||
| github.com/bmatcuk/doublestar v1.3.2/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= | ||||
| github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= | ||||
| github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= | ||||
| github.com/bndr/gojenkins v1.1.0 h1:TWyJI6ST1qDAfH33DQb3G4mD8KkrBfyfSUoZBHQAvPI= | ||||
| github.com/bndr/gojenkins v1.1.0/go.mod h1:QeskxN9F/Csz0XV/01IC8y37CapKKWvOHa0UHLLX1fM= | ||||
| github.com/bndr/gojenkins v1.1.1-0.20210520222939-90ed82bfdff6 h1:yHK3nXjSRklq0SWhK6KW6kJtTebTUvVxN3gPmUtoVJ4= | ||||
| github.com/bndr/gojenkins v1.1.1-0.20210520222939-90ed82bfdff6/go.mod h1:QeskxN9F/Csz0XV/01IC8y37CapKKWvOHa0UHLLX1fM= | ||||
| github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= | ||||
| github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= | ||||
| github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= | ||||
|   | ||||
							
								
								
									
										49
									
								
								integration/integration_jenkins_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								integration/integration_jenkins_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| // can be execute with go test -tags=integration ./integration/... | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/jenkins" | ||||
| ) | ||||
|  | ||||
| func TestTriggerJob(t *testing.T) { | ||||
| 	t.Skip("no Jenkins instance for testing available yet") | ||||
| 	//TODO: check if testcontainers can be used | ||||
| 	// init | ||||
| 	ctx := context.Background() | ||||
| 	// ctx = context.WithValue(ctx, "debug", true) | ||||
|  | ||||
| 	// os.Setenv("PIPER_INTEGRATION_JENKINS_USER_NAME", "") | ||||
| 	// os.Setenv("PIPER_INTEGRATION_JENKINS_TOKEN", "") | ||||
| 	// os.Setenv("PIPER_INTEGRATION_JENKINS_HOST", "") | ||||
| 	// os.Setenv("PIPER_INTEGRATION_JENKINS_JOB_NAME", "") | ||||
|  | ||||
| 	host := os.Getenv("PIPER_INTEGRATION_JENKINS_HOST") | ||||
| 	user := os.Getenv("PIPER_INTEGRATION_JENKINS_USER_NAME") | ||||
| 	token := os.Getenv("PIPER_INTEGRATION_JENKINS_TOKEN") | ||||
| 	jobName := os.Getenv("PIPER_INTEGRATION_JENKINS_JOB_NAME") | ||||
| 	require.NotEmpty(t, host, "Jenkins host url is missing") | ||||
| 	require.NotEmpty(t, user, "Jenkins user name is missing") | ||||
| 	require.NotEmpty(t, token, "Jenkins token is missing") | ||||
| 	require.NotEmpty(t, jobName, "Jenkins job name is missing") | ||||
|  | ||||
| 	jenx, err := jenkins.Instance(ctx, http.DefaultClient, host, user, token) | ||||
| 	require.NotNil(t, jenx, "could not connect to Jenkins instance") | ||||
| 	require.NoError(t, err) | ||||
| 	// test | ||||
| 	job, getJobErr := jenkins.GetJob(ctx, jenx, jobName) | ||||
| 	build, triggerJobErr := jenkins.TriggerJob(ctx, jenx, job, nil) | ||||
| 	// asserts | ||||
| 	assert.NoError(t, getJobErr) | ||||
| 	assert.NoError(t, triggerJobErr) | ||||
| 	assert.NotNil(t, build) | ||||
| 	assert.True(t, build.IsRunning(ctx)) | ||||
| } | ||||
| @@ -7,13 +7,15 @@ import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/bndr/gojenkins" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| // Jenkins is an interface to abstract gojenkins.Jenkins. | ||||
| // mock generated with: mockery --name Jenkins --dir pkg/jenkins --output pkg/jenkins/mocks | ||||
| type Jenkins interface { | ||||
| 	GetJobObj(ctx context.Context, name string) *gojenkins.Job | ||||
| 	BuildJob(ctx context.Context, name string, params map[string]string) (int64, error) | ||||
| 	GetBuildFromQueueID(ctx context.Context, queueid int64) (*gojenkins.Build, error) | ||||
| 	GetBuildFromQueueID(ctx context.Context, job *gojenkins.Job, queueid int64) (*gojenkins.Build, error) | ||||
| } | ||||
|  | ||||
| // Instance connects to a Jenkins instance and returns a handler. | ||||
| @@ -23,12 +25,22 @@ func Instance(ctx context.Context, client *http.Client, jenkinsURL, user, token | ||||
| 		Init(ctx) | ||||
| } | ||||
|  | ||||
| // TriggerJob starts a build for a given job name. | ||||
| func TriggerJob(ctx context.Context, jenkins Jenkins, jobName string, parameters map[string]string) (*gojenkins.Build, error) { | ||||
| func GetJob(ctx context.Context, jenkins Jenkins, jobName string) (Job, error) { | ||||
| 	// get job id | ||||
| 	jobID := strings.ReplaceAll(jobName, "/", "/job/") | ||||
| 	// get job | ||||
| 	return &JobImpl{Job: jenkins.GetJobObj(ctx, jobID)}, nil | ||||
| } | ||||
|  | ||||
| // TriggerJob starts a build for a given job name. | ||||
| func TriggerJob(ctx context.Context, jenkins Jenkins, job Job, parameters map[string]string) (*gojenkins.Build, error) { | ||||
| 	// update job | ||||
| 	_, pollJobErr := job.Poll(ctx) | ||||
| 	if pollJobErr != nil { | ||||
| 		return nil, errors.Wrapf(pollJobErr, "failed to load job") | ||||
| 	} | ||||
| 	// start job | ||||
| 	queueID, startBuildErr := jenkins.BuildJob(ctx, jobID, parameters) | ||||
| 	queueID, startBuildErr := job.InvokeSimple(ctx, parameters) | ||||
| 	if startBuildErr != nil { | ||||
| 		return nil, startBuildErr | ||||
| 	} | ||||
| @@ -40,5 +52,5 @@ func TriggerJob(ctx context.Context, jenkins Jenkins, jobName string, parameters | ||||
| 	} | ||||
|  | ||||
| 	// get build | ||||
| 	return jenkins.GetBuildFromQueueID(ctx, queueID) | ||||
| 	return jenkins.GetBuildFromQueueID(ctx, job.GetJob(), queueID) | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,6 @@ package jenkins | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/jenkins/mocks" | ||||
| @@ -19,20 +18,39 @@ func TestInterfaceCompatibility(t *testing.T) { | ||||
|  | ||||
| func TestTriggerJob(t *testing.T) { | ||||
| 	ctx := context.Background() | ||||
| 	jobName := "ContinuousDelivery/piper-library" | ||||
| 	jobID := strings.ReplaceAll(jobName, "/", "/job/") | ||||
| 	jobParameters := map[string]string{} | ||||
|  | ||||
| 	t.Run("error - job not updated", func(t *testing.T) { | ||||
| 		// init | ||||
| 		jenkins := &mocks.Jenkins{} | ||||
| 		jenkins.Test(t) | ||||
| 		job := &mocks.Job{} | ||||
| 		job.Test(t) | ||||
| 		job. | ||||
| 			On("Poll", ctx).Return(404, fmt.Errorf(mock.Anything)) | ||||
| 		// test | ||||
| 		build, err := TriggerJob(ctx, jenkins, job, jobParameters) | ||||
| 		// asserts | ||||
| 		job.AssertExpectations(t) | ||||
| 		jenkins.AssertExpectations(t) | ||||
| 		assert.EqualError(t, err, fmt.Sprintf("failed to load job: %s", mock.Anything)) | ||||
| 		assert.Nil(t, build) | ||||
| 	}) | ||||
| 	t.Run("error - task not started", func(t *testing.T) { | ||||
| 		// init | ||||
| 		queueID := int64(0) | ||||
| 		jenkins := &mocks.Jenkins{} | ||||
| 		jenkins. | ||||
| 			On("BuildJob", ctx, jobID, map[string]string{}). | ||||
| 		jenkins.Test(t) | ||||
| 		job := &mocks.Job{} | ||||
| 		job.Test(t) | ||||
| 		job. | ||||
| 			On("Poll", ctx).Return(200, nil). | ||||
| 			On("InvokeSimple", ctx, map[string]string{}). | ||||
| 			Return(queueID, fmt.Errorf(mock.Anything)) | ||||
| 		// test | ||||
| 		build, err := TriggerJob(ctx, jenkins, jobName, jobParameters) | ||||
| 		build, err := TriggerJob(ctx, jenkins, job, jobParameters) | ||||
| 		// asserts | ||||
| 		job.AssertExpectations(t) | ||||
| 		jenkins.AssertExpectations(t) | ||||
| 		assert.EqualError(t, err, mock.Anything) | ||||
| 		assert.Nil(t, build) | ||||
| @@ -41,12 +59,17 @@ func TestTriggerJob(t *testing.T) { | ||||
| 		// init | ||||
| 		queueID := int64(0) | ||||
| 		jenkins := &mocks.Jenkins{} | ||||
| 		jenkins. | ||||
| 			On("BuildJob", ctx, jobID, map[string]string{}). | ||||
| 		jenkins.Test(t) | ||||
| 		job := &mocks.Job{} | ||||
| 		job.Test(t) | ||||
| 		job. | ||||
| 			On("Poll", ctx).Return(200, nil). | ||||
| 			On("InvokeSimple", ctx, jobParameters). | ||||
| 			Return(queueID, nil) | ||||
| 		// test | ||||
| 		build, err := TriggerJob(ctx, jenkins, jobName, jobParameters) | ||||
| 		build, err := TriggerJob(ctx, jenkins, job, jobParameters) | ||||
| 		// asserts | ||||
| 		job.AssertExpectations(t) | ||||
| 		jenkins.AssertExpectations(t) | ||||
| 		assert.EqualError(t, err, "unable to queue build") | ||||
| 		assert.Nil(t, build) | ||||
| @@ -56,14 +79,21 @@ func TestTriggerJob(t *testing.T) { | ||||
| 		queueID := int64(43) | ||||
| 		jenkins := &mocks.Jenkins{} | ||||
| 		jenkins.Test(t) | ||||
| 		jenkins. | ||||
| 			On("BuildJob", ctx, jobID, map[string]string{}). | ||||
| 		job := &mocks.Job{} | ||||
| 		job.Test(t) | ||||
| 		job. | ||||
| 			On("Poll", ctx).Return(200, nil). | ||||
| 			On("InvokeSimple", ctx, jobParameters). | ||||
| 			Return(queueID, nil). | ||||
| 			On("GetBuildFromQueueID", ctx, queueID). | ||||
| 			On("GetJob"). | ||||
| 			Return(&gojenkins.Job{}) | ||||
| 		jenkins. | ||||
| 			On("GetBuildFromQueueID", ctx, mock.Anything, queueID). | ||||
| 			Return(nil, fmt.Errorf(mock.Anything)) | ||||
| 		// test | ||||
| 		build, err := TriggerJob(ctx, jenkins, jobName, jobParameters) | ||||
| 		build, err := TriggerJob(ctx, jenkins, job, jobParameters) | ||||
| 		// asserts | ||||
| 		job.AssertExpectations(t) | ||||
| 		jenkins.AssertExpectations(t) | ||||
| 		assert.EqualError(t, err, mock.Anything) | ||||
| 		assert.Nil(t, build) | ||||
| @@ -73,13 +103,19 @@ func TestTriggerJob(t *testing.T) { | ||||
| 		queueID := int64(43) | ||||
| 		jenkins := &mocks.Jenkins{} | ||||
| 		jenkins.Test(t) | ||||
| 		jenkins. | ||||
| 			On("BuildJob", ctx, jobID, map[string]string{}). | ||||
| 		job := &mocks.Job{} | ||||
| 		job.Test(t) | ||||
| 		job. | ||||
| 			On("Poll", ctx).Return(200, nil). | ||||
| 			On("InvokeSimple", ctx, jobParameters). | ||||
| 			Return(queueID, nil). | ||||
| 			On("GetBuildFromQueueID", ctx, queueID). | ||||
| 			On("GetJob"). | ||||
| 			Return(&gojenkins.Job{}) | ||||
| 		jenkins. | ||||
| 			On("GetBuildFromQueueID", ctx, mock.Anything, queueID). | ||||
| 			Return(&gojenkins.Build{}, nil) | ||||
| 		// test | ||||
| 		build, err := TriggerJob(ctx, jenkins, jobName, jobParameters) | ||||
| 		build, err := TriggerJob(ctx, jenkins, job, jobParameters) | ||||
| 		// asserts | ||||
| 		jenkins.AssertExpectations(t) | ||||
| 		assert.NoError(t, err) | ||||
|   | ||||
							
								
								
									
										35
									
								
								pkg/jenkins/job.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								pkg/jenkins/job.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| package jenkins | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/bndr/gojenkins" | ||||
| ) | ||||
|  | ||||
| // Task is an interface to abstract gojenkins.Task. | ||||
| // mock generated with: mockery --name Job --dir pkg/jenkins --output pkg/jenkins/mocks | ||||
| type Job interface { | ||||
| 	Poll(context.Context) (int, error) | ||||
| 	InvokeSimple(ctx context.Context, params map[string]string) (int64, error) | ||||
| 	GetJob() *gojenkins.Job | ||||
| } | ||||
|  | ||||
| // JobImpl is a wrapper struct for gojenkins.Task that respects the Task interface. | ||||
| type JobImpl struct { | ||||
| 	Job *gojenkins.Job | ||||
| } | ||||
|  | ||||
| // Poll refers to the gojenkins.Job.Poll function. | ||||
| func (t *JobImpl) Poll(ctx context.Context) (int, error) { | ||||
| 	return t.Job.Poll(ctx) | ||||
| } | ||||
|  | ||||
| // InvokeSimple refers to the gojenkins.Job.InvokeSimple function. | ||||
| func (t *JobImpl) InvokeSimple(ctx context.Context, params map[string]string) (int64, error) { | ||||
| 	return t.Job.InvokeSimple(ctx, params) | ||||
| } | ||||
|  | ||||
| // GetJob returns wrapped gojenkins.Job. | ||||
| func (t *JobImpl) GetJob() *gojenkins.Job { | ||||
| 	return t.Job | ||||
| } | ||||
| @@ -36,13 +36,13 @@ func (_m *Jenkins) BuildJob(ctx context.Context, name string, params map[string] | ||||
| 	return r0, r1 | ||||
| } | ||||
|  | ||||
| // GetBuildFromQueueID provides a mock function with given fields: ctx, queueid | ||||
| func (_m *Jenkins) GetBuildFromQueueID(ctx context.Context, queueid int64) (*gojenkins.Build, error) { | ||||
| 	ret := _m.Called(ctx, queueid) | ||||
| // GetBuildFromQueueID provides a mock function with given fields: ctx, job, queueid | ||||
| func (_m *Jenkins) GetBuildFromQueueID(ctx context.Context, job *gojenkins.Job, queueid int64) (*gojenkins.Build, error) { | ||||
| 	ret := _m.Called(ctx, job, queueid) | ||||
|  | ||||
| 	var r0 *gojenkins.Build | ||||
| 	if rf, ok := ret.Get(0).(func(context.Context, int64) *gojenkins.Build); ok { | ||||
| 		r0 = rf(ctx, queueid) | ||||
| 	if rf, ok := ret.Get(0).(func(context.Context, *gojenkins.Job, int64) *gojenkins.Build); ok { | ||||
| 		r0 = rf(ctx, job, queueid) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*gojenkins.Build) | ||||
| @@ -50,11 +50,27 @@ func (_m *Jenkins) GetBuildFromQueueID(ctx context.Context, queueid int64) (*goj | ||||
| 	} | ||||
|  | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { | ||||
| 		r1 = rf(ctx, queueid) | ||||
| 	if rf, ok := ret.Get(1).(func(context.Context, *gojenkins.Job, int64) error); ok { | ||||
| 		r1 = rf(ctx, job, queueid) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
|  | ||||
| 	return r0, r1 | ||||
| } | ||||
|  | ||||
| // GetJobObj provides a mock function with given fields: ctx, name | ||||
| func (_m *Jenkins) GetJobObj(ctx context.Context, name string) *gojenkins.Job { | ||||
| 	ret := _m.Called(ctx, name) | ||||
|  | ||||
| 	var r0 *gojenkins.Job | ||||
| 	if rf, ok := ret.Get(0).(func(context.Context, string) *gojenkins.Job); ok { | ||||
| 		r0 = rf(ctx, name) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*gojenkins.Job) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|   | ||||
							
								
								
									
										74
									
								
								pkg/jenkins/mocks/Job.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								pkg/jenkins/mocks/Job.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| // Code generated by mockery v2.7.5. DO NOT EDIT. | ||||
|  | ||||
| package mocks | ||||
|  | ||||
| import ( | ||||
| 	context "context" | ||||
|  | ||||
| 	gojenkins "github.com/bndr/gojenkins" | ||||
|  | ||||
| 	mock "github.com/stretchr/testify/mock" | ||||
| ) | ||||
|  | ||||
| // Job is an autogenerated mock type for the Job type | ||||
| type Job struct { | ||||
| 	mock.Mock | ||||
| } | ||||
|  | ||||
| // GetJob provides a mock function with given fields: | ||||
| func (_m *Job) GetJob() *gojenkins.Job { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 *gojenkins.Job | ||||
| 	if rf, ok := ret.Get(0).(func() *gojenkins.Job); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*gojenkins.Job) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| // InvokeSimple provides a mock function with given fields: ctx, params | ||||
| func (_m *Job) InvokeSimple(ctx context.Context, params map[string]string) (int64, error) { | ||||
| 	ret := _m.Called(ctx, params) | ||||
|  | ||||
| 	var r0 int64 | ||||
| 	if rf, ok := ret.Get(0).(func(context.Context, map[string]string) int64); ok { | ||||
| 		r0 = rf(ctx, params) | ||||
| 	} else { | ||||
| 		r0 = ret.Get(0).(int64) | ||||
| 	} | ||||
|  | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(context.Context, map[string]string) error); ok { | ||||
| 		r1 = rf(ctx, params) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
|  | ||||
| 	return r0, r1 | ||||
| } | ||||
|  | ||||
| // Poll provides a mock function with given fields: _a0 | ||||
| func (_m *Job) Poll(_a0 context.Context) (int, error) { | ||||
| 	ret := _m.Called(_a0) | ||||
|  | ||||
| 	var r0 int | ||||
| 	if rf, ok := ret.Get(0).(func(context.Context) int); ok { | ||||
| 		r0 = rf(_a0) | ||||
| 	} else { | ||||
| 		r0 = ret.Get(0).(int) | ||||
| 	} | ||||
|  | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(context.Context) error); ok { | ||||
| 		r1 = rf(_a0) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
|  | ||||
| 	return r0, r1 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user