diff --git a/pkg/controller/jenkins/client/jenkins.go b/pkg/controller/jenkins/client/jenkins.go index 68808f5b..88feb682 100644 --- a/pkg/controller/jenkins/client/jenkins.go +++ b/pkg/controller/jenkins/client/jenkins.go @@ -51,6 +51,7 @@ type Jenkins interface { GetAllViews() ([]*gojenkins.View, error) CreateView(name string, viewType string) (*gojenkins.View, error) Poll() (int, error) + ExecuteScript(groovyScript string) (output string, err error) } type jenkins struct { diff --git a/pkg/controller/jenkins/client/mockgen.go b/pkg/controller/jenkins/client/mockgen.go index 7fb9e8dc..062b7bd0 100644 --- a/pkg/controller/jenkins/client/mockgen.go +++ b/pkg/controller/jenkins/client/mockgen.go @@ -603,3 +603,18 @@ func (mr *MockJenkinsMockRecorder) Poll() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Poll", reflect.TypeOf((*MockJenkins)(nil).Poll)) } + +// ExecuteScript mocks base method +func (m *MockJenkins) ExecuteScript(groovyScript string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteScript", groovyScript) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteScript indicates an expected call of ExecuteScript +func (mr *MockJenkinsMockRecorder) ExecuteScript(groovyScript interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteScript", reflect.TypeOf((*MockJenkins)(nil).ExecuteScript), groovyScript) +} diff --git a/pkg/controller/jenkins/client/script.go b/pkg/controller/jenkins/client/script.go new file mode 100644 index 00000000..6da73946 --- /dev/null +++ b/pkg/controller/jenkins/client/script.go @@ -0,0 +1,45 @@ +package client + +import ( + "fmt" + "net/http" + "strings" + "time" + + "github.com/bndr/gojenkins" + "github.com/pkg/errors" +) + +func (jenkins *jenkins) ExecuteScript(script string) (string, error) { + now := time.Now().Unix() + verifier := fmt.Sprintf("verifier-%d", now) + return jenkins.executeScript(script, verifier) +} + +func (jenkins *jenkins) executeScript(script string, verifier string) (string, error) { + output := "" + fullScript := fmt.Sprintf("%s\nprint println('%s')", script, verifier) + parameters := map[string]string{"script": fullScript} + + ar := gojenkins.NewAPIRequest("POST", "/scriptText", nil) + if err := jenkins.Requester.SetCrumb(ar); err != nil { + return output, err + } + ar.SetHeader("Content-Type", "application/x-www-form-urlencoded") + ar.Suffix = "" + + r, err := jenkins.Requester.Do(ar, &output, parameters) + if err != nil { + return "", errors.Wrap(err, "couldn't execute groovy script") + } + + if r.StatusCode != http.StatusOK { + return output, errors.Errorf("invalid status code '%d'", r.StatusCode) + } + + if !strings.Contains(output, verifier) { + return output, errors.New("script execution failed") + } + + return output, nil +} diff --git a/pkg/controller/jenkins/client/script_test.go b/pkg/controller/jenkins/client/script_test.go new file mode 100644 index 00000000..5f6cdd28 --- /dev/null +++ b/pkg/controller/jenkins/client/script_test.go @@ -0,0 +1,89 @@ +package client + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/bndr/gojenkins" + "github.com/stretchr/testify/assert" +) + +func Test_ExecuteScript(t *testing.T) { + verifier := "verifier-text" + t.Run("logs have verifier text", func(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) { + if strings.Contains(request.URL.Path, "/scriptText") { + _, _ = fmt.Fprint(responseWriter, "some output\n"+verifier) + return + } + responseWriter.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + client := ts.Client() + + jenkinsClient := &jenkins{} + jenkinsClient.Server = ts.URL + jenkinsClient.Requester = &gojenkins.Requester{ + Base: ts.URL, + SslVerify: true, + Client: client, + BasicAuth: &gojenkins.BasicAuth{Username: "unused", Password: "unused"}, + } + + script := "some groovy code" + logs, err := jenkinsClient.executeScript(script, verifier) + assert.NoError(t, err, logs) + }) + t.Run("logs don't have verifier text", func(t *testing.T) { + response := "some exception stack trace without verifier" + ts := httptest.NewTLSServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) { + if strings.Contains(request.URL.Path, "/scriptText") { + _, _ = fmt.Fprint(responseWriter, response) + return + } + responseWriter.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + client := ts.Client() + + jenkinsClient := &jenkins{} + jenkinsClient.Server = ts.URL + jenkinsClient.Requester = &gojenkins.Requester{ + Base: ts.URL, + SslVerify: true, + Client: client, + BasicAuth: &gojenkins.BasicAuth{Username: "unused", Password: "unused"}, + } + + script := "some groovy code" + logs, err := jenkinsClient.executeScript(script, verifier) + assert.EqualError(t, err, "script execution failed", logs) + assert.Equal(t, response, logs) + }) + t.Run("throw 500", func(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) { + responseWriter.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + client := ts.Client() + + jenkinsClient := &jenkins{} + jenkinsClient.Server = ts.URL + jenkinsClient.Requester = &gojenkins.Requester{ + Base: ts.URL, + SslVerify: true, + Client: client, + BasicAuth: &gojenkins.BasicAuth{Username: "unused", Password: "unused"}, + } + + script := "some groovy code" + logs, err := jenkinsClient.executeScript(script, verifier) + assert.EqualError(t, err, "invalid status code '500'", logs) + }) +}