Browse code

Add extended tests for pipeline examples

Moves common code out of Jenkins plugin test to utility package.
Adds tests to build pipeline to validate new pipeline examples.

Cesar Wong authored on 2016/12/22 09:52:56
Showing 7 changed files
... ...
@@ -34,9 +34,9 @@ objects:
34 34
           node {
35 35
             project = env.PROJECT_NAME
36 36
             stage("Initialize") {
37
-              sh "oc get route ${appName} -n ${project} -o jsonpath='{ .spec.to.weight }' > blueweight"
38
-              blueWeight = readFile('blueweight').trim()
39
-              if (blueWeight == "100") {
37
+              sh "oc get route ${appName} -n ${project} -o jsonpath='{ .spec.to.name }' > activeservice"
38
+              activeService = readFile('activeservice').trim()
39
+              if (activeService == "${appName}-blue") {
40 40
                 tag = "green"
41 41
                 altTag = "blue"
42 42
               }
... ...
@@ -55,7 +55,7 @@ objects:
55 55
             }
56 56
 
57 57
             stage("Test") {
58
-              input "Test deployment: http://${routeHost}. Approve?"
58
+              input message: "Test deployment: http://${routeHost}. Approve?", id: "approval"
59 59
             }
60 60
 
61 61
             stage("Go Live") {
... ...
@@ -4894,9 +4894,9 @@ objects:
4894 4894
           node {
4895 4895
             project = env.PROJECT_NAME
4896 4896
             stage("Initialize") {
4897
-              sh "oc get route ${appName} -n ${project} -o jsonpath='{ .spec.to.weight }' > blueweight"
4898
-              blueWeight = readFile('blueweight').trim()
4899
-              if (blueWeight == "100") {
4897
+              sh "oc get route ${appName} -n ${project} -o jsonpath='{ .spec.to.name }' > activeservice"
4898
+              activeService = readFile('activeservice').trim()
4899
+              if (activeService == "${appName}-blue") {
4900 4900
                 tag = "green"
4901 4901
                 altTag = "blue"
4902 4902
               }
... ...
@@ -4915,7 +4915,7 @@ objects:
4915 4915
             }
4916 4916
 
4917 4917
             stage("Test") {
4918
-              input "Test deployment: http://${routeHost}. Approve?"
4918
+              input message: "Test deployment: http://${routeHost}. Approve?", id: "approval"
4919 4919
             }
4920 4920
 
4921 4921
             stage("Go Live") {
... ...
@@ -2,52 +2,164 @@ package builds
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"strings"
6
+	"sync"
7
+	"time"
5 8
 
6 9
 	g "github.com/onsi/ginkgo"
7 10
 	o "github.com/onsi/gomega"
8 11
 
9 12
 	exutil "github.com/openshift/origin/test/extended/util"
13
+	"github.com/openshift/origin/test/extended/util/jenkins"
10 14
 )
11 15
 
12 16
 var _ = g.Describe("[builds][Slow] openshift pipeline build", func() {
13 17
 	defer g.GinkgoRecover()
14 18
 	var (
15
-		pipelineTemplatePath = exutil.FixturePath("testdata", "test-pipeline.json")
16
-		jenkinsTemplatePath  = exutil.FixturePath("..", "..", "examples", "jenkins", "jenkins-ephemeral-template.json")
17
-		oc                   = exutil.NewCLI("jenkins-pipeline", exutil.KubeConfigPath())
19
+		jenkinsTemplatePath       = exutil.FixturePath("..", "..", "examples", "jenkins", "jenkins-ephemeral-template.json")
20
+		mavenSlavePipelinePath    = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "maven-pipeline.yaml")
21
+		orchestrationPipelinePath = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "mapsapp-pipeline.yaml")
22
+		blueGreenPipelinePath     = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "bluegreen-pipeline.yaml")
23
+
24
+		oc = exutil.NewCLI("jenkins-pipeline", exutil.KubeConfigPath())
18 25
 	)
19 26
 	g.JustBeforeEach(func() {
20 27
 		g.By("waiting for builder service account")
21 28
 		err := exutil.WaitForBuilderAccount(oc.KubeClient().Core().ServiceAccounts(oc.Namespace()))
22 29
 		o.Expect(err).NotTo(o.HaveOccurred())
23 30
 	})
24
-	g.Context("Manual deploy the jenkins and trigger a jenkins pipeline build", func() {
25
-		g.It("JenkinsPipeline build should succeed when manual deploy the jenkins service", func() {
26
-			oc.SetOutputDir(exutil.TestContext.OutputDir)
27
-
31
+	g.Context("Pipeline with maven slave", func() {
32
+		g.It("Should build and complete successfully", func() {
33
+			// Deploy Jenkins
28 34
 			g.By(fmt.Sprintf("calling oc new-app -f %q", jenkinsTemplatePath))
29 35
 			err := oc.Run("new-app").Args("-f", jenkinsTemplatePath).Execute()
30 36
 			o.Expect(err).NotTo(o.HaveOccurred())
31 37
 
32
-			//wait for the jenkins deployment complete
33
-			g.By("waiting the jenkins service deployed")
34
-			err = exutil.WaitForADeploymentToComplete(oc.KubeClient().Core().ReplicationControllers(oc.Namespace()), "jenkins", oc)
35
-			if err != nil {
36
-				exutil.DumpDeploymentLogs("jenkins", oc)
37
-			}
38
+			// instantiate the template
39
+			g.By(fmt.Sprintf("calling oc new-app -f %q", mavenSlavePipelinePath))
40
+			err = oc.Run("new-app").Args("-f", mavenSlavePipelinePath).Execute()
41
+			o.Expect(err).NotTo(o.HaveOccurred())
42
+
43
+			// start the build
44
+			g.By("starting the pipeline build and waiting for it to complete")
45
+			br, _ := exutil.StartBuildAndWait(oc, "openshift-jee-sample")
46
+			br.AssertSuccess()
47
+
48
+			// wait for the service to be running
49
+			g.By("expecting the openshift-jee-sample service to be deployed and running")
50
+			_, err = exutil.GetEndpointAddress(oc, "openshift-jee-sample")
38 51
 			o.Expect(err).NotTo(o.HaveOccurred())
39
-			// create the pipeline build example
40
-			g.By(fmt.Sprintf("calling oc new-app -f %q", pipelineTemplatePath))
41
-			err = oc.Run("new-app").Args("-f", pipelineTemplatePath).Execute()
52
+		})
53
+	})
54
+
55
+	g.Context("Orchestration pipeline", func() {
56
+		g.It("Should build and complete successfully", func() {
57
+			// Deploy Jenkins
58
+			g.By(fmt.Sprintf("calling oc new-app -f %q", jenkinsTemplatePath))
59
+			err := oc.Run("new-app").Args("-f", jenkinsTemplatePath).Execute()
42 60
 			o.Expect(err).NotTo(o.HaveOccurred())
43 61
 
44
-			g.By("starting a pipeline build")
45
-			br, _ := exutil.StartBuildAndWait(oc, "sample-pipeline")
46
-			if !br.BuildSuccess {
47
-				exutil.DumpDeploymentLogs("jenkins", oc)
48
-			}
62
+			// instantiate the template
63
+			g.By(fmt.Sprintf("calling oc new-app -f %q", orchestrationPipelinePath))
64
+			err = oc.Run("new-app").Args("-f", orchestrationPipelinePath).Execute()
65
+			o.Expect(err).NotTo(o.HaveOccurred())
66
+
67
+			// start the build
68
+			g.By("starting the pipeline build and waiting for it to complete")
69
+			br, _ := exutil.StartBuildAndWait(oc, "mapsapp-pipeline")
49 70
 			br.AssertSuccess()
71
+
72
+			// wait for the service to be running
73
+			g.By("expecting the parksmap-web service to be deployed and running")
74
+			_, err = exutil.GetEndpointAddress(oc, "parksmap")
75
+			o.Expect(err).NotTo(o.HaveOccurred())
50 76
 		})
51 77
 	})
52 78
 
79
+	g.Context("Blue-green pipeline", func() {
80
+		g.It("Should build and complete successfully", func() {
81
+			// Deploy Jenkins without oauth
82
+			g.By(fmt.Sprintf("calling oc new-app -f %q -p ENABLE_OAUTH=false", jenkinsTemplatePath))
83
+			err := oc.Run("new-app").Args("-f", jenkinsTemplatePath, "-p", "ENABLE_OAUTH=false").Execute()
84
+			o.Expect(err).NotTo(o.HaveOccurred())
85
+
86
+			j := jenkins.NewRef(oc)
87
+
88
+			// instantiate the template
89
+			g.By(fmt.Sprintf("calling oc new-app -f %q", blueGreenPipelinePath))
90
+			err = oc.Run("new-app").Args("-f", blueGreenPipelinePath).Execute()
91
+			o.Expect(err).NotTo(o.HaveOccurred())
92
+
93
+			wg := &sync.WaitGroup{}
94
+			wg.Add(1)
95
+
96
+			// start the build
97
+			go func() {
98
+				g.By("starting the bluegreen pipeline build and waiting for it to complete")
99
+				br, _ := exutil.StartBuildAndWait(oc, "bluegreen-pipeline")
100
+				br.AssertSuccess()
101
+
102
+				g.By("verifying that the main route has been switched to green")
103
+				value, err := oc.Run("get").Args("route", "nodejs-mongodb-example", "-o", "jsonpath={ .spec.to.name }").Output()
104
+				o.Expect(err).NotTo(o.HaveOccurred())
105
+				activeRoute := strings.TrimSpace(value)
106
+				g.By("verifying that the active route is 'nodejs-mongodb-example-green'")
107
+				o.Expect(activeRoute).To(o.Equal("nodejs-mongodb-example-green"))
108
+				wg.Done()
109
+			}()
110
+
111
+			// wait for the green service to be available
112
+			g.By("waiting for the nodejs-mongodb-example-green service to be available")
113
+			_, err = exutil.GetEndpointAddress(oc, "nodejs-mongodb-example-green")
114
+			o.Expect(err).NotTo(o.HaveOccurred())
115
+
116
+			// approve the Jenkins pipeline
117
+			g.By("Waiting for the approval prompt")
118
+			jobName := oc.Namespace() + "-bluegreen-pipeline"
119
+			_, err = j.WaitForContent("Approve?", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
120
+			o.Expect(err).NotTo(o.HaveOccurred())
121
+
122
+			g.By("Approving the current build")
123
+			_, _, err = j.Post(nil, fmt.Sprintf("job/%s/lastBuild/input/Approval/proceedEmpty", jobName), "")
124
+			o.Expect(err).NotTo(o.HaveOccurred())
125
+
126
+			// wait for first build completion and verification
127
+			g.By("Waiting for the build to complete successfully")
128
+			wg.Wait()
129
+
130
+			wg = &sync.WaitGroup{}
131
+			wg.Add(1)
132
+
133
+			// start the build again
134
+			go func() {
135
+				g.By("starting the bluegreen pipeline build and waiting for it to complete")
136
+				br, _ := exutil.StartBuildAndWait(oc, "bluegreen-pipeline")
137
+				br.AssertSuccess()
138
+
139
+				g.By("verifying that the main route has been switched to blue")
140
+				value, err := oc.Run("get").Args("route", "nodejs-mongodb-example", "-o", "jsonpath={ .spec.to.name }").Output()
141
+				o.Expect(err).NotTo(o.HaveOccurred())
142
+				activeRoute := strings.TrimSpace(value)
143
+				g.By("verifying that the active route is 'nodejs-mongodb-example-blue'")
144
+				o.Expect(activeRoute).To(o.Equal("nodejs-mongodb-example-blue"))
145
+				wg.Done()
146
+			}()
147
+
148
+			// wait for the blue service to be available
149
+			g.By("waiting for the nodejs-mongodb-example-blue service to be available")
150
+			_, err = exutil.GetEndpointAddress(oc, "nodejs-mongodb-example-blue")
151
+			o.Expect(err).NotTo(o.HaveOccurred())
152
+
153
+			// approve the Jenkins pipeline
154
+			g.By("Waiting for the approval prompt")
155
+			_, err = j.WaitForContent("Approve?", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
156
+			o.Expect(err).NotTo(o.HaveOccurred())
157
+
158
+			g.By("Approving the current build")
159
+			_, _, err = j.Post(nil, fmt.Sprintf("job/%s/lastBuild/input/Approval/proceedEmpty", jobName), "")
160
+			o.Expect(err).NotTo(o.HaveOccurred())
161
+
162
+			wg.Wait()
163
+		})
164
+	})
53 165
 })
... ...
@@ -2,12 +2,10 @@ package image_ecosystem
2 2
 
3 3
 import (
4 4
 	"bytes"
5
-	"encoding/xml"
6 5
 	"fmt"
7
-	"io"
8
-	"io/ioutil"
9
-	"net/http"
10 6
 	"net/url"
7
+	"os"
8
+	"os/exec"
11 9
 	"regexp"
12 10
 	"strings"
13 11
 	"time"
... ...
@@ -18,333 +16,21 @@ import (
18 18
 	kapi "k8s.io/kubernetes/pkg/api"
19 19
 	"k8s.io/kubernetes/pkg/util/wait"
20 20
 
21
-	"os"
22
-	"os/exec"
23
-
24 21
 	exutil "github.com/openshift/origin/test/extended/util"
22
+	"github.com/openshift/origin/test/extended/util/jenkins"
25 23
 )
26 24
 
27
-type JenkinsRef struct {
28
-	oc   *exutil.CLI
29
-	host string
30
-	port string
31
-	// The namespace in which the Jenkins server is running
32
-	namespace string
33
-	password  string
34
-}
35
-
36 25
 const (
37 26
 	useLocalPluginSnapshotEnvVarName = "USE_SNAPSHOT_JENKINS_IMAGE"
38 27
 	disableJenkinsMemoryStats        = "DISABLE_JENKINS_MEMORY_MONITORING"
39 28
 	localPluginSnapshotImage         = "openshift/jenkins-plugin-snapshot-test:latest"
40 29
 )
41 30
 
42
-// Struct can be marshalled into XML to represent a Jenkins workflow job definition.
43
-type FlowDefinition struct {
44
-	XMLName          xml.Name `xml:"flow-definition"`
45
-	Plugin           string   `xml:"plugin,attr"`
46
-	KeepDependencies bool     `xml:"keepDependencies"`
47
-	Definition       Definition
48
-}
49
-
50
-type Definition struct {
51
-	XMLName xml.Name `xml:"definition"`
52
-	Class   string   `xml:"class,attr"`
53
-	Plugin  string   `xml:"plugin,attr"`
54
-	Script  string   `xml:"script"`
55
-}
56
-
57
-// Builds a URI for the Jenkins server.
58
-func (j *JenkinsRef) buildURI(resourcePathFormat string, a ...interface{}) string {
59
-	resourcePath := fmt.Sprintf(resourcePathFormat, a...)
60
-	return fmt.Sprintf("http://%s:%v/%s", j.host, j.port, resourcePath)
61
-}
62
-
63
-// Submits a GET request to this Jenkins server.
64
-// Returns a response body and status code or an error.
65
-func (j *JenkinsRef) getResource(resourcePathFormat string, a ...interface{}) (string, int, error) {
66
-	uri := j.buildURI(resourcePathFormat, a...)
67
-	ginkgolog("Retrieving Jenkins resource: %q", uri)
68
-	req, err := http.NewRequest("GET", uri, nil)
69
-	if err != nil {
70
-		return "", 0, fmt.Errorf("Unable to build request for uri %q: %v", uri, err)
71
-	}
72
-
73
-	// http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi
74
-	req.Close = true
75
-
76
-	req.SetBasicAuth("admin", j.password)
77
-	client := &http.Client{}
78
-	resp, err := client.Do(req)
79
-
80
-	if err != nil {
81
-		return "", 0, fmt.Errorf("Unable to GET uri %q: %v", uri, err)
82
-	}
83
-
84
-	defer resp.Body.Close()
85
-	status := resp.StatusCode
86
-
87
-	body, err := ioutil.ReadAll(resp.Body)
88
-	if err != nil {
89
-		return "", 0, fmt.Errorf("Error reading GET response %q: %v", uri, err)
90
-	}
91
-
92
-	return string(body), status, nil
93
-}
94
-
95
-// Sends a POST to the Jenkins server. If a body is specified, it should be XML.
96
-// Returns response body and status code or an error.
97
-func (j *JenkinsRef) postXML(reqBody io.Reader, resourcePathFormat string, a ...interface{}) (string, int, error) {
98
-	return j.post(reqBody, resourcePathFormat, "application/xml", a...)
99
-}
100
-
101
-// Sends a POST to the Jenkins server. Returns response body and status code or an error.
102
-func (j *JenkinsRef) post(reqBody io.Reader, resourcePathFormat, contentType string, a ...interface{}) (string, int, error) {
103
-	uri := j.buildURI(resourcePathFormat, a...)
104
-
105
-	req, err := http.NewRequest("POST", uri, reqBody)
106
-	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
107
-
108
-	// http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi
109
-	req.Close = true
110
-
111
-	if reqBody != nil {
112
-		req.Header.Set("Content-Type", contentType)
113
-		req.Header.Del("Expect") // jenkins will return 417 if we have an expect hdr
114
-	}
115
-	req.SetBasicAuth("admin", j.password)
116
-
117
-	client := &http.Client{}
118
-	resp, err := client.Do(req)
119
-	if err != nil {
120
-		return "", 0, fmt.Errorf("Error posting request to %q: %v", uri, err)
121
-	}
122
-
123
-	defer resp.Body.Close()
124
-	status := resp.StatusCode
125
-
126
-	body, err := ioutil.ReadAll(resp.Body)
127
-	if err != nil {
128
-		return "", 0, fmt.Errorf("Error reading post response body %q: %v", uri, err)
129
-	}
130
-
131
-	return string(body), status, nil
132
-}
133
-
134
-// Creates simple entry in the GinkgoWriter.
31
+// ginkgolog creates simple entry in the GinkgoWriter.
135 32
 func ginkgolog(format string, a ...interface{}) {
136 33
 	fmt.Fprintf(g.GinkgoWriter, format+"\n", a...)
137 34
 }
138 35
 
139
-// Repeatedly tries to GET a jenkins resource with an acceptable
140
-// HTTP status. Retries for the specified duration.
141
-func (j *JenkinsRef) getResourceWithStatus(validStatusList []int, timeout time.Duration, resourcePathFormat string, a ...interface{}) (string, int, error) {
142
-	var retBody string
143
-	var retStatus int
144
-	err := wait.Poll(10*time.Second, timeout, func() (bool, error) {
145
-		body, status, err := j.getResource(resourcePathFormat, a...)
146
-		if err != nil {
147
-			ginkgolog("Error accessing resource: %v", err)
148
-			return false, nil
149
-		}
150
-		var found bool
151
-		for _, s := range validStatusList {
152
-			if status == s {
153
-				found = true
154
-				break
155
-			}
156
-		}
157
-		if !found {
158
-			ginkgolog("Expected http status [%v] during GET by recevied [%v]", validStatusList, status)
159
-			return false, nil
160
-		}
161
-		retBody = body
162
-		retStatus = status
163
-		return true, nil
164
-	})
165
-	if err != nil {
166
-		uri := j.buildURI(resourcePathFormat, a...)
167
-		return "", retStatus, fmt.Errorf("Error waiting for status %v from resource path %s: %v", validStatusList, uri, err)
168
-	}
169
-	return retBody, retStatus, nil
170
-}
171
-
172
-// Waits for a particular HTTP status and HTML matching a particular
173
-// pattern to be returned by this Jenkins server. An error will be returned
174
-// if the condition is not matched within the timeout period.
175
-func (j *JenkinsRef) waitForContent(verificationRegEx string, verificationStatus int, timeout time.Duration, resourcePathFormat string, a ...interface{}) (string, error) {
176
-	var matchingContent = ""
177
-	err := wait.Poll(10*time.Second, timeout, func() (bool, error) {
178
-
179
-		content, _, err := j.getResourceWithStatus([]int{verificationStatus}, timeout, resourcePathFormat, a...)
180
-		if err != nil {
181
-			return false, nil
182
-		}
183
-
184
-		if len(verificationRegEx) > 0 {
185
-			re := regexp.MustCompile(verificationRegEx)
186
-			if re.MatchString(content) {
187
-				matchingContent = content
188
-				return true, nil
189
-			} else {
190
-				ginkgolog("Content did not match verification regex %q:\n %v", verificationRegEx, content)
191
-				return false, nil
192
-			}
193
-		} else {
194
-			matchingContent = content
195
-			return true, nil
196
-		}
197
-	})
198
-
199
-	if err != nil {
200
-		uri := j.buildURI(resourcePathFormat, a...)
201
-		return "", fmt.Errorf("Error waiting for status %v and verification regex %q from resource path %s: %v", verificationStatus, verificationRegEx, uri, err)
202
-	} else {
203
-		return matchingContent, nil
204
-	}
205
-}
206
-
207
-// Submits XML to create a named item on the Jenkins server.
208
-func (j *JenkinsRef) createItem(name string, itemDefXML string) {
209
-	g.By(fmt.Sprintf("Creating new jenkins item: %s", name))
210
-	_, status, err := j.postXML(bytes.NewBufferString(itemDefXML), "createItem?name=%s", name)
211
-	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
212
-	o.ExpectWithOffset(1, status).To(o.Equal(200))
213
-}
214
-
215
-type JobMon struct {
216
-	j               *JenkinsRef
217
-	lastBuildNumber string
218
-	buildNumber     string
219
-	jobName         string
220
-}
221
-
222
-// Returns the current buildNumber on the named project OR "new" if
223
-// there are no builds against a job yet.
224
-func (j *JenkinsRef) getJobBuildNumber(name string, timeout time.Duration) (string, error) {
225
-	body, status, err := j.getResourceWithStatus([]int{200, 404}, timeout, "job/%s/lastBuild/buildNumber", name)
226
-	if err != nil {
227
-		return "", err
228
-	}
229
-	if status != 200 {
230
-		return "new", nil
231
-	}
232
-	return body, nil
233
-}
234
-
235
-// Waits for the timestamp on the Jenkins job to change. Returns
236
-// and error if the timeout expires.
237
-func (jmon *JobMon) await(timeout time.Duration) error {
238
-	err := wait.Poll(10*time.Second, timeout, func() (bool, error) {
239
-
240
-		buildNumber, err := jmon.j.getJobBuildNumber(jmon.jobName, time.Minute)
241
-		o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
242
-
243
-		ginkgolog("Checking build number for job %q current[%v] vs last[%v]", jmon.jobName, buildNumber, jmon.lastBuildNumber)
244
-		if buildNumber == jmon.lastBuildNumber {
245
-			return false, nil
246
-		}
247
-
248
-		if jmon.buildNumber == "" {
249
-			jmon.buildNumber = buildNumber
250
-		}
251
-		body, status, err := jmon.j.getResource("job/%s/%s/api/json?depth=1", jmon.jobName, jmon.buildNumber)
252
-		o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
253
-		o.ExpectWithOffset(1, status).To(o.Equal(200))
254
-
255
-		body = strings.ToLower(body)
256
-		if strings.Contains(body, "\"building\":true") {
257
-			ginkgolog("Jenkins job %q still building:\n%s\n\n", jmon.jobName, body)
258
-			return false, nil
259
-		}
260
-
261
-		if strings.Contains(body, "\"result\":null") {
262
-			ginkgolog("Jenkins job %q still building result:\n%s\n\n", jmon.jobName, body)
263
-			return false, nil
264
-		}
265
-
266
-		ginkgolog("Jenkins job %q build complete:\n%s\n\n", jmon.jobName, body)
267
-		return true, nil
268
-	})
269
-	return err
270
-}
271
-
272
-// Triggers a named Jenkins job. The job can be monitored with the
273
-// returned object.
274
-func (j *JenkinsRef) startJob(jobName string) *JobMon {
275
-	lastBuildNumber, err := j.getJobBuildNumber(jobName, time.Minute)
276
-	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
277
-
278
-	jmon := &JobMon{
279
-		j:               j,
280
-		lastBuildNumber: lastBuildNumber,
281
-		buildNumber:     "",
282
-		jobName:         jobName,
283
-	}
284
-
285
-	ginkgolog("Current timestamp for [%s]: %q", jobName, jmon.lastBuildNumber)
286
-	g.By(fmt.Sprintf("Starting jenkins job: %s", jobName))
287
-	_, status, err := j.postXML(nil, "job/%s/build?delay=0sec", jobName)
288
-	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
289
-	o.ExpectWithOffset(1, status).To(o.Equal(201))
290
-
291
-	return jmon
292
-}
293
-
294
-// Returns the content of a Jenkins job XML file. Instances of the
295
-// string "PROJECT_NAME" are replaced with the specified namespace.
296
-// Variables named in the vars map will also be replaced with their
297
-// corresponding value.
298
-func (j *JenkinsRef) readJenkinsJobUsingVars(filename, namespace string, vars map[string]string) string {
299
-	pre := exutil.FixturePath("testdata", "jenkins-plugin", filename)
300
-	post := exutil.ArtifactPath(filename)
301
-
302
-	if vars == nil {
303
-		vars = map[string]string{}
304
-	}
305
-	vars["PROJECT_NAME"] = namespace
306
-	err := exutil.VarSubOnFile(pre, post, vars)
307
-	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
308
-
309
-	data, err := ioutil.ReadFile(post)
310
-	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
311
-	return string(data)
312
-}
313
-
314
-// Returns the content of a Jenkins job XML file. Instances of the
315
-// string "PROJECT_NAME" are replaced with the specified namespace.
316
-func (j *JenkinsRef) readJenkinsJob(filename, namespace string) string {
317
-	return j.readJenkinsJobUsingVars(filename, namespace, nil)
318
-}
319
-
320
-// Returns an XML string defining a Jenkins workflow/pipeline DSL job. Instances of the
321
-// string "PROJECT_NAME" are replaced with the specified namespace.
322
-func (j *JenkinsRef) buildDSLJob(namespace string, scriptLines ...string) (string, error) {
323
-	script := strings.Join(scriptLines, "\n")
324
-	script = strings.Replace(script, "PROJECT_NAME", namespace, -1)
325
-	fd := FlowDefinition{
326
-		Plugin: "workflow-job@2.7",
327
-		Definition: Definition{
328
-			Class:  "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition",
329
-			Plugin: "workflow-cps@2.18",
330
-			Script: script,
331
-		},
332
-	}
333
-	output, err := xml.MarshalIndent(fd, "  ", "    ")
334
-	ginkgolog("Formulated DSL Project XML:\n%s\n\n", output)
335
-	return string(output), err
336
-}
337
-
338
-// Returns the console logs of a particular buildNumber.
339
-func (j *JenkinsRef) getJobConsoleLogs(jobName, buildNumber string) (string, error) {
340
-	return j.waitForContent("", 200, 10*time.Minute, "job/%s/%s/consoleText", jobName, buildNumber)
341
-}
342
-
343
-// Returns the last build associated with a Jenkins job.
344
-func (j *JenkinsRef) getLastJobConsoleLogs(jobName string) (string, error) {
345
-	return j.getJobConsoleLogs(jobName, "lastBuild")
346
-}
347
-
348 36
 // Loads a Jenkins related template using new-app.
349 37
 func loadFixture(oc *exutil.CLI, filename string) {
350 38
 	resourcePath := exutil.FixturePath("testdata", "jenkins-plugin", filename)
... ...
@@ -385,33 +71,6 @@ func assertEnvVars(oc *exutil.CLI, buildPrefix string, varsToFind map[string]str
385 385
 	}
386 386
 }
387 387
 
388
-func getAdminPassword(oc *exutil.CLI) string {
389
-	envs, err := oc.Run("set").Args("env", "dc/jenkins", "--list").Output()
390
-	o.Expect(err).NotTo(o.HaveOccurred())
391
-	kvs := strings.Split(envs, "\n")
392
-	for _, kv := range kvs {
393
-		if strings.HasPrefix(kv, "JENKINS_PASSWORD=") {
394
-			s := strings.Split(kv, "=")
395
-			fmt.Fprintf(g.GinkgoWriter, "\nJenkins admin password %s\n", s[1])
396
-			return s[1]
397
-		}
398
-	}
399
-	return "password"
400
-}
401
-
402
-// Finds the pod running Jenkins
403
-func findJenkinsPod(oc *exutil.CLI) *kapi.Pod {
404
-	pods, err := exutil.GetDeploymentConfigPods(oc, "jenkins")
405
-	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
406
-
407
-	if pods == nil || pods.Items == nil {
408
-		g.Fail("No pods matching jenkins deploymentconfig in namespace " + oc.Namespace())
409
-	}
410
-
411
-	o.ExpectWithOffset(1, len(pods.Items)).To(o.Equal(1))
412
-	return &pods.Items[0]
413
-}
414
-
415 388
 // Stands up a simple pod which can be used for exec commands
416 389
 func initExecPod(oc *exutil.CLI) *kapi.Pod {
417 390
 	// Create a running pod in which we can execute our commands
... ...
@@ -456,7 +115,7 @@ var memoryOverragePattern = regexp.MustCompile(`\s+rss\s+5\d\d\d\d\d\d\d\d`)
456 456
 var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin", func() {
457 457
 	defer g.GinkgoRecover()
458 458
 	var oc = exutil.NewCLI("jenkins-plugin", exutil.KubeConfigPath())
459
-	var j *JenkinsRef
459
+	var j *jenkins.JenkinsRef
460 460
 	var dcLogFollow *exec.Cmd
461 461
 	var dcLogStdOut, dcLogStdErr *bytes.Buffer
462 462
 	var ticker *time.Ticker
... ...
@@ -467,12 +126,12 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
467 467
 			ticker.Stop()
468 468
 		}
469 469
 
470
-		oc.SetNamespace(j.namespace)
470
+		oc.SetNamespace(j.Namespace())
471 471
 		ginkgolog("Jenkins DC description follows. If there were issues, check to see if there were any restarts in the jenkins pod.")
472 472
 		exutil.DumpDeploymentLogs("jenkins", oc)
473 473
 
474 474
 		// Destroy the Jenkins namespace
475
-		oc.Run("delete").Args("project", j.namespace).Execute()
475
+		oc.Run("delete").Args("project", j.Namespace()).Execute()
476 476
 		if dcLogFollow != nil && dcLogStdOut != nil && dcLogStdErr != nil {
477 477
 			ginkgolog("Waiting for Jenkins DC log follow to terminate")
478 478
 			dcLogFollow.Process.Wait()
... ...
@@ -548,26 +207,10 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
548 548
 		err = exutil.WaitForADeploymentToComplete(oc.KubeClient().Core().ReplicationControllers(oc.Namespace()), "jenkins", oc)
549 549
 		o.Expect(err).NotTo(o.HaveOccurred())
550 550
 
551
-		g.By("get ip and port for jenkins service")
552
-		serviceIP, err := oc.Run("get").Args("svc", "jenkins", "--config", exutil.KubeConfigPath()).Template("{{.spec.clusterIP}}").Output()
553
-		o.Expect(err).NotTo(o.HaveOccurred())
554
-		port, err := oc.Run("get").Args("svc", "jenkins", "--config", exutil.KubeConfigPath()).Template("{{ $x := index .spec.ports 0}}{{$x.port}}").Output()
555
-		o.Expect(err).NotTo(o.HaveOccurred())
556
-
557
-		g.By("get admin password")
558
-		password := getAdminPassword(oc)
559
-		o.Expect(password).ShouldNot(o.BeEmpty())
560
-
561
-		j = &JenkinsRef{
562
-			oc:        oc,
563
-			host:      serviceIP,
564
-			port:      port,
565
-			namespace: jenkinsNamespace,
566
-			password:  password,
567
-		}
551
+		j = jenkins.NewRef(oc)
568 552
 
569 553
 		g.By("wait for jenkins to come up")
570
-		_, err = j.waitForContent("", 200, 10*time.Minute, "")
554
+		_, err = j.WaitForContent("", 200, 10*time.Minute, "")
571 555
 
572 556
 		if err != nil {
573 557
 			exutil.DumpDeploymentLogs("jenkins", oc)
... ...
@@ -578,11 +221,11 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
578 578
 		if useSnapshotImage {
579 579
 			g.By("verifying the test image is being used")
580 580
 			// for the test image, confirm that a snapshot version of the plugin is running in the jenkins image we'll test against
581
-			_, err = j.waitForContent(`About OpenShift Pipeline Jenkins Plugin ([0-9\.]+)-SNAPSHOT`, 200, 10*time.Minute, "/pluginManager/plugin/openshift-pipeline/thirdPartyLicenses")
581
+			_, err = j.WaitForContent(`About OpenShift Pipeline Jenkins Plugin ([0-9\.]+)-SNAPSHOT`, 200, 10*time.Minute, "/pluginManager/plugin/openshift-pipeline/thirdPartyLicenses")
582 582
 			o.Expect(err).NotTo(o.HaveOccurred())
583 583
 		}
584 584
 
585
-		jenkinsPod := findJenkinsPod(oc)
585
+		jenkinsPod := jenkins.FindJenkinsPod(oc)
586 586
 
587 587
 		if os.Getenv(disableJenkinsMemoryStats) == "" {
588 588
 			ticker = time.NewTicker(10 * time.Second)
... ...
@@ -630,7 +273,7 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
630 630
 		oc.SetNamespace(testNamespace)
631 631
 
632 632
 		g.By("set up policy for jenkins jobs in " + oc.Namespace())
633
-		err = oc.Run("policy").Args("add-role-to-user", "edit", "system:serviceaccount:"+j.namespace+":jenkins").Execute()
633
+		err = oc.Run("policy").Args("add-role-to-user", "edit", "system:serviceaccount:"+j.Namespace()+":jenkins").Execute()
634 634
 		o.Expect(err).NotTo(o.HaveOccurred())
635 635
 
636 636
 		// Populate shared Jenkins namespace with artifacts that can be used by all tests
... ...
@@ -646,10 +289,10 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
646 646
 		g.It("jenkins-plugin test trigger build including clone", func() {
647 647
 
648 648
 			jobName := "test-build-job"
649
-			data := j.readJenkinsJob("build-job.xml", oc.Namespace())
650
-			j.createItem(jobName, data)
651
-			jmon := j.startJob(jobName)
652
-			jmon.await(10 * time.Minute)
649
+			data := j.ReadJenkinsJob("build-job.xml", oc.Namespace())
650
+			j.CreateItem(jobName, data)
651
+			jmon := j.StartJob(jobName)
652
+			jmon.Await(10 * time.Minute)
653 653
 
654 654
 			// the build and deployment is by far the most time consuming portion of the test jenkins job;
655 655
 			// we leverage some of the openshift utilities for waiting for the deployment before we poll
... ...
@@ -661,7 +304,7 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
661 661
 			o.Expect(err).NotTo(o.HaveOccurred())
662 662
 
663 663
 			g.By("get build console logs and see if succeeded")
664
-			logs, err := j.waitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
664
+			logs, err := j.WaitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
665 665
 			ginkgolog("\n\nJenkins logs>\n%s\n\n", logs)
666 666
 			o.Expect(err).NotTo(o.HaveOccurred())
667 667
 
... ...
@@ -671,12 +314,12 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
671 671
 			o.Expect(strings.Contains(out, "Jenkins job")).To(o.BeTrue())
672 672
 
673 673
 			jobName = "test-build-clone-job"
674
-			data = j.readJenkinsJob("build-job-clone.xml", oc.Namespace())
675
-			j.createItem(jobName, data)
676
-			jmon = j.startJob(jobName)
677
-			jmon.await(10 * time.Minute)
674
+			data = j.ReadJenkinsJob("build-job-clone.xml", oc.Namespace())
675
+			j.CreateItem(jobName, data)
676
+			jmon = j.StartJob(jobName)
677
+			jmon.Await(10 * time.Minute)
678 678
 			g.By("get clone build console logs and see if succeeded")
679
-			logs, err = j.waitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
679
+			logs, err = j.WaitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
680 680
 			ginkgolog("\n\nJenkins logs>\n%s\n\n", logs)
681 681
 			o.Expect(err).NotTo(o.HaveOccurred())
682 682
 
... ...
@@ -693,10 +336,10 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
693 693
 		g.It("jenkins-plugin test trigger build with slave", func() {
694 694
 
695 695
 			jobName := "test-build-job-slave"
696
-			data := j.readJenkinsJob("build-job-slave.xml", oc.Namespace())
697
-			j.createItem(jobName, data)
698
-			jmon := j.startJob(jobName)
699
-			jmon.await(10 * time.Minute)
696
+			data := j.ReadJenkinsJob("build-job-slave.xml", oc.Namespace())
697
+			j.CreateItem(jobName, data)
698
+			jmon := j.StartJob(jobName)
699
+			jmon.Await(10 * time.Minute)
700 700
 
701 701
 			// the build and deployment is by far the most time consuming portion of the test jenkins job;
702 702
 			// we leverage some of the openshift utilities for waiting for the deployment before we poll
... ...
@@ -708,12 +351,12 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
708 708
 			o.Expect(err).NotTo(o.HaveOccurred())
709 709
 
710 710
 			g.By("get build console logs and see if succeeded")
711
-			logs, err := j.waitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
711
+			logs, err := j.WaitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
712 712
 			ginkgolog("\n\nJenkins logs>\n%s\n\n", logs)
713 713
 			o.Expect(err).NotTo(o.HaveOccurred())
714 714
 
715 715
 			g.By("get build console logs and confirm ran on slave")
716
-			logs, err = j.waitForContent("Building remotely on", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
716
+			logs, err = j.WaitForContent("Building remotely on", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
717 717
 			ginkgolog("\n\nJenkins logs>\n%s\n\n", logs)
718 718
 			o.Expect(err).NotTo(o.HaveOccurred())
719 719
 
... ...
@@ -723,17 +366,17 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
723 723
 
724 724
 			jobsToCreate := map[string]string{"test-create-obj": "create-job.xml", "test-delete-obj": "delete-job.xml", "test-delete-obj-labels": "delete-job-labels.xml", "test-delete-obj-keys": "delete-job-keys.xml"}
725 725
 			for jobName, jobConfig := range jobsToCreate {
726
-				data := j.readJenkinsJob(jobConfig, oc.Namespace())
727
-				j.createItem(jobName, data)
726
+				data := j.ReadJenkinsJob(jobConfig, oc.Namespace())
727
+				j.CreateItem(jobName, data)
728 728
 			}
729 729
 
730 730
 			jobsToRun := []apiObjJob{{"test-create-obj", true}, {"test-delete-obj", false}, {"test-create-obj", true}, {"test-delete-obj-labels", false}, {"test-create-obj", true}, {"test-delete-obj-keys", false}}
731 731
 			for _, job := range jobsToRun {
732
-				jmon := j.startJob(job.jobName)
733
-				jmon.await(10 * time.Minute)
732
+				jmon := j.StartJob(job.jobName)
733
+				jmon.Await(10 * time.Minute)
734 734
 
735 735
 				g.By("get build console logs and see if succeeded")
736
-				logs, err := j.waitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", job.jobName)
736
+				logs, err := j.WaitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", job.jobName)
737 737
 				ginkgolog("\n\nJenkins logs>\n%s\n\n", logs)
738 738
 				o.Expect(err).NotTo(o.HaveOccurred())
739 739
 				out, err := oc.Run("get").Args("bc", "forcepull-bldr").Output()
... ...
@@ -747,12 +390,12 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
747 747
 		g.It("jenkins-plugin test trigger build with envs", func() {
748 748
 
749 749
 			jobName := "test-build-with-env-job"
750
-			data := j.readJenkinsJob("build-with-env-job.xml", oc.Namespace())
751
-			j.createItem(jobName, data)
752
-			jmon := j.startJob(jobName)
753
-			jmon.await(10 * time.Minute)
750
+			data := j.ReadJenkinsJob("build-with-env-job.xml", oc.Namespace())
751
+			j.CreateItem(jobName, data)
752
+			jmon := j.StartJob(jobName)
753
+			jmon.Await(10 * time.Minute)
754 754
 
755
-			logs, err := j.getLastJobConsoleLogs(jobName)
755
+			logs, err := j.GetLastJobConsoleLogs(jobName)
756 756
 			ginkgolog("\n\nJenkins logs>\n%s\n\n", logs)
757 757
 			o.Expect(err).NotTo(o.HaveOccurred())
758 758
 
... ...
@@ -764,7 +407,7 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
764 764
 			o.Expect(err).NotTo(o.HaveOccurred())
765 765
 
766 766
 			g.By("get build console logs and see if succeeded")
767
-			_, err = j.waitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
767
+			_, err = j.WaitForContent("Finished: SUCCESS", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
768 768
 
769 769
 			assertEnvVars(oc, "frontend-", map[string]string{
770 770
 				"a": "b",
... ...
@@ -779,16 +422,16 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
779 779
 			buildsBefore, err := oc.Client().Builds(oc.Namespace()).List(kapi.ListOptions{})
780 780
 			o.Expect(err).NotTo(o.HaveOccurred())
781 781
 
782
-			data, err := j.buildDSLJob(oc.Namespace(),
782
+			data, err := j.BuildDSLJob(oc.Namespace(),
783 783
 				"node{",
784 784
 				"openshiftBuild( namespace:'PROJECT_NAME', bldCfg: 'frontend', env: [ [ name : 'a', value : 'b' ], [ name : 'C', value : 'D' ], [ name : 'e', value : '' ] ] )",
785 785
 				"}",
786 786
 			)
787 787
 
788 788
 			jobName := "test-build-dsl-job"
789
-			j.createItem(jobName, data)
790
-			monitor := j.startJob(jobName)
791
-			err = monitor.await(10 * time.Minute)
789
+			j.CreateItem(jobName, data)
790
+			monitor := j.StartJob(jobName)
791
+			err = monitor.Await(10 * time.Minute)
792 792
 			o.Expect(err).NotTo(o.HaveOccurred())
793 793
 
794 794
 			err = wait.Poll(10*time.Second, 10*time.Minute, func() (bool, error) {
... ...
@@ -801,7 +444,7 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
801 801
 			o.Expect(err).NotTo(o.HaveOccurred())
802 802
 			o.Expect(len(buildsAfter.Items)).To(o.Equal(len(buildsBefore.Items) + 1))
803 803
 
804
-			log, err := j.getLastJobConsoleLogs(jobName)
804
+			log, err := j.GetLastJobConsoleLogs(jobName)
805 805
 			ginkgolog("Job logs>>\n%s\n\n", log)
806 806
 
807 807
 			assertEnvVars(oc, "frontend-", map[string]string{
... ...
@@ -817,7 +460,7 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
817 817
 			targetPod := initExecPod(oc)
818 818
 			targetContainer := targetPod.Spec.Containers[0]
819 819
 
820
-			data, err := j.buildDSLJob(oc.Namespace(),
820
+			data, err := j.BuildDSLJob(oc.Namespace(),
821 821
 				"node{",
822 822
 				fmt.Sprintf("openshiftExec( namespace:'PROJECT_NAME', pod: '%s', command: [ 'echo', 'hello', 'world', '1' ] )", targetPod.Name),
823 823
 				fmt.Sprintf("openshiftExec( namespace:'PROJECT_NAME', pod: '%s', command: 'echo', arguments : [ 'hello', 'world', '2' ] )", targetPod.Name),
... ...
@@ -829,12 +472,12 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
829 829
 			)
830 830
 
831 831
 			jobName := "test-exec-dsl-job"
832
-			j.createItem(jobName, data)
833
-			monitor := j.startJob(jobName)
834
-			err = monitor.await(10 * time.Minute)
832
+			j.CreateItem(jobName, data)
833
+			monitor := j.StartJob(jobName)
834
+			err = monitor.Await(10 * time.Minute)
835 835
 			o.Expect(err).NotTo(o.HaveOccurred())
836 836
 
837
-			log, err := j.getLastJobConsoleLogs(jobName)
837
+			log, err := j.GetLastJobConsoleLogs(jobName)
838 838
 			ginkgolog("Job logs>>\n%s\n\n", log)
839 839
 
840 840
 			o.Expect(strings.Contains(log, "hello world 1")).To(o.BeTrue())
... ...
@@ -851,16 +494,16 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
851 851
 			targetContainer := targetPod.Spec.Containers[0]
852 852
 
853 853
 			jobName := "test-build-with-env-steps"
854
-			data := j.readJenkinsJobUsingVars("build-with-exec-steps.xml", oc.Namespace(), map[string]string{
854
+			data := j.ReadJenkinsJobUsingVars("build-with-exec-steps.xml", oc.Namespace(), map[string]string{
855 855
 				"POD_NAME":       targetPod.Name,
856 856
 				"CONTAINER_NAME": targetContainer.Name,
857 857
 			})
858 858
 
859
-			j.createItem(jobName, data)
860
-			jmon := j.startJob(jobName)
861
-			jmon.await(2 * time.Minute)
859
+			j.CreateItem(jobName, data)
860
+			jmon := j.StartJob(jobName)
861
+			jmon.Await(2 * time.Minute)
862 862
 
863
-			log, err := j.getLastJobConsoleLogs(jobName)
863
+			log, err := j.GetLastJobConsoleLogs(jobName)
864 864
 			ginkgolog("\n\nJenkins logs>\n%s\n\n", log)
865 865
 			o.Expect(err).NotTo(o.HaveOccurred())
866 866
 
... ...
@@ -868,16 +511,16 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
868 868
 
869 869
 			// Now run without specifying container
870 870
 			jobName = "test-build-with-env-steps-no-container"
871
-			data = j.readJenkinsJobUsingVars("build-with-exec-steps.xml", oc.Namespace(), map[string]string{
871
+			data = j.ReadJenkinsJobUsingVars("build-with-exec-steps.xml", oc.Namespace(), map[string]string{
872 872
 				"POD_NAME":       targetPod.Name,
873 873
 				"CONTAINER_NAME": "",
874 874
 			})
875 875
 
876
-			j.createItem(jobName, data)
877
-			jmon = j.startJob(jobName)
878
-			jmon.await(2 * time.Minute)
876
+			j.CreateItem(jobName, data)
877
+			jmon = j.StartJob(jobName)
878
+			jmon.Await(2 * time.Minute)
879 879
 
880
-			log, err = j.getLastJobConsoleLogs(jobName)
880
+			log, err = j.GetLastJobConsoleLogs(jobName)
881 881
 			ginkgolog("\n\nJenkins logs>\n%s\n\n", log)
882 882
 			o.Expect(err).NotTo(o.HaveOccurred())
883 883
 
... ...
@@ -898,13 +541,13 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
898 898
 			o.Expect(err).NotTo(o.HaveOccurred())
899 899
 
900 900
 			jobName := "test-multitag-job"
901
-			data := j.readJenkinsJob("multitag-job.xml", oc.Namespace())
902
-			j.createItem(jobName, data)
903
-			monitor := j.startJob(jobName)
904
-			err = monitor.await(10 * time.Minute)
901
+			data := j.ReadJenkinsJob("multitag-job.xml", oc.Namespace())
902
+			j.CreateItem(jobName, data)
903
+			monitor := j.StartJob(jobName)
904
+			err = monitor.Await(10 * time.Minute)
905 905
 			o.Expect(err).NotTo(o.HaveOccurred())
906 906
 
907
-			log, err := j.getLastJobConsoleLogs(jobName)
907
+			log, err := j.GetLastJobConsoleLogs(jobName)
908 908
 			ginkgolog("Job logs>>\n%s\n\n", log)
909 909
 
910 910
 			// Assert stream tagging results
... ...
@@ -966,14 +609,14 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
966 966
 
967 967
 			// Allow jenkins service account to edit the new namespace
968 968
 			oc.SetNamespace(anotherNamespace)
969
-			err = oc.Run("policy").Args("add-role-to-user", "edit", "system:serviceaccount:"+j.namespace+":jenkins").Execute()
969
+			err = oc.Run("policy").Args("add-role-to-user", "edit", "system:serviceaccount:"+j.Namespace()+":jenkins").Execute()
970 970
 			o.Expect(err).NotTo(o.HaveOccurred())
971 971
 
972 972
 			oc.SetNamespace(testNamespace)
973 973
 
974 974
 			ginkgolog("Using testNamespace: %q and currentNamespace: %q", testNamespace, oc.Namespace())
975 975
 
976
-			data, err := j.buildDSLJob(oc.Namespace(),
976
+			data, err := j.BuildDSLJob(oc.Namespace(),
977 977
 				"node{",
978 978
 				"openshiftTag( namespace:'PROJECT_NAME', srcStream: 'multitag', srcTag: 'orig', destStream: 'multitag', destTag: 'prod' )",
979 979
 				"openshiftTag( namespace:'PROJECT_NAME', srcStream: 'multitag', srcTag: 'orig', destStream: 'multitag2', destTag: 'prod1, prod2, prod3' )",
... ...
@@ -987,14 +630,14 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
987 987
 			)
988 988
 
989 989
 			jobName := "test-multitag-dsl-job"
990
-			j.createItem(jobName, data)
991
-			monitor := j.startJob(jobName)
992
-			err = monitor.await(10 * time.Minute)
990
+			j.CreateItem(jobName, data)
991
+			monitor := j.StartJob(jobName)
992
+			err = monitor.Await(10 * time.Minute)
993 993
 			o.Expect(err).NotTo(o.HaveOccurred())
994 994
 
995 995
 			time.Sleep(10 * time.Second)
996 996
 
997
-			log, err := j.getLastJobConsoleLogs(jobName)
997
+			log, err := j.GetLastJobConsoleLogs(jobName)
998 998
 			o.Expect(err).NotTo(o.HaveOccurred())
999 999
 			ginkgolog("Job logs>>\n%s\n\n", log)
1000 1000
 
... ...
@@ -1028,8 +671,8 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
1028 1028
 		testImageStreamSCM := func(jobXMLFile string) {
1029 1029
 			jobName := "test-imagestream-scm"
1030 1030
 			g.By("creating a jenkins job with an imagestream SCM")
1031
-			data := j.readJenkinsJob(jobXMLFile, oc.Namespace())
1032
-			j.createItem(jobName, data)
1031
+			data := j.ReadJenkinsJob(jobXMLFile, oc.Namespace())
1032
+			j.CreateItem(jobName, data)
1033 1033
 
1034 1034
 			// Because polling is enabled, a job should start automatically and fail
1035 1035
 			// Wait for it to run and fail
... ...
@@ -1038,7 +681,7 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
1038 1038
 			jobStatusURI := "api/xml?tree=%s&xpath=%s"
1039 1039
 			g.By("waiting for initial job to complete")
1040 1040
 			wait.Poll(10*time.Second, 10*time.Minute, func() (bool, error) {
1041
-				result, status, err := j.getResource(jobStatusURI, tree, xpath)
1041
+				result, status, err := j.GetResource(jobStatusURI, tree, xpath)
1042 1042
 				o.Expect(err).NotTo(o.HaveOccurred())
1043 1043
 				if status == 200 && strings.Contains(result, "red") {
1044 1044
 					return true, nil
... ...
@@ -1069,12 +712,12 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
1069 1069
 		g.It("jenkins-plugin test connection test", func() {
1070 1070
 
1071 1071
 			jobName := "test-build-job"
1072
-			data := j.readJenkinsJob("build-job.xml", oc.Namespace())
1073
-			j.createItem(jobName, data)
1072
+			data := j.ReadJenkinsJob("build-job.xml", oc.Namespace())
1073
+			j.CreateItem(jobName, data)
1074 1074
 
1075 1075
 			g.By("trigger test connection logic, check for success")
1076 1076
 			testConnectionBody := bytes.NewBufferString("apiURL=&authToken=")
1077
-			result, code, err := j.post(testConnectionBody, "job/test-build-job/descriptorByName/com.openshift.jenkins.plugins.pipeline.OpenShiftBuilder/testConnection", "application/x-www-form-urlencoded")
1077
+			result, code, err := j.Post(testConnectionBody, "job/test-build-job/descriptorByName/com.openshift.jenkins.plugins.pipeline.OpenShiftBuilder/testConnection", "application/x-www-form-urlencoded")
1078 1078
 			if code != 200 {
1079 1079
 				err = fmt.Errorf("Expected return code of 200")
1080 1080
 			}
... ...
@@ -1085,7 +728,7 @@ var _ = g.Describe("[image_ecosystem][jenkins][Slow] openshift pipeline plugin",
1085 1085
 
1086 1086
 			g.By("trigger test connection logic, check for failure")
1087 1087
 			testConnectionBody = bytes.NewBufferString("apiURL=https%3A%2F%2F1.2.3.4&authToken=")
1088
-			result, code, err = j.post(testConnectionBody, "job/test-build-job/descriptorByName/com.openshift.jenkins.plugins.pipeline.OpenShiftBuilder/testConnection", "application/x-www-form-urlencoded")
1088
+			result, code, err = j.Post(testConnectionBody, "job/test-build-job/descriptorByName/com.openshift.jenkins.plugins.pipeline.OpenShiftBuilder/testConnection", "application/x-www-form-urlencoded")
1089 1089
 			if code != 200 {
1090 1090
 				err = fmt.Errorf("Expected return code of 200")
1091 1091
 			}
1092 1092
deleted file mode 100644
... ...
@@ -1,88 +0,0 @@
1
-{
2
-  "kind": "Template",
3
-  "apiVersion": "v1",
4
-  "metadata": {
5
-    "name": "jenkins-pipeline-example",
6
-    "annotations": {
7
-      "description": "This is used to test the jenkins pipeline strategy",
8
-      "iconClass": "icon-jenkins"
9
-    }
10
-  },
11
-  "objects": [
12
-    {
13
-      "kind": "BuildConfig",
14
-      "apiVersion": "v1",
15
-      "metadata": {
16
-        "name": "sample-pipeline",
17
-        "labels": {
18
-          "name": "sample-pipeline"
19
-        },
20
-        "annotations": {
21
-          "pipeline.alpha.openshift.io/uses": "[{\"name\": \"frontend\", \"namespace\": \"\", \"kind\": \"DeploymentConfig\"}]"
22
-        }
23
-      },
24
-      "spec": {
25
-        "strategy": {
26
-          "type": "JenkinsPipeline",
27
-          "jenkinsPipelineStrategy": {
28
-            "jenkinsfile": "node{\nstage 'build'\nopenshiftBuild(buildConfig: 'ruby-sample-build', showBuildLogs: 'true')\n}"
29
-          }
30
-        }
31
-      }
32
-    },
33
-    {
34
-      "kind": "ImageStream",
35
-      "apiVersion": "v1",
36
-      "metadata": {
37
-        "name": "origin-ruby-sample"
38
-      },
39
-      "spec": {},
40
-      "status": {
41
-        "dockerImageRepository": ""
42
-      }
43
-    },
44
-    {
45
-      "kind": "BuildConfig",
46
-      "apiVersion": "v1",
47
-      "metadata": {
48
-        "name": "ruby-sample-build",
49
-        "labels": {
50
-          "name": "ruby-sample-build"
51
-        }
52
-      },
53
-      "spec": {
54
-        "source": {
55
-          "type": "Git",
56
-          "git": {
57
-            "uri": "https://github.com/openshift/ruby-hello-world.git"
58
-          }
59
-        },
60
-        "strategy": {
61
-          "type": "Source",
62
-          "sourceStrategy": {
63
-            "from": {
64
-              "kind": "ImageStreamTag",
65
-              "name": "ruby:2.2",
66
-              "namespace": "openshift"
67
-            }
68
-          }
69
-        },
70
-        "output": {
71
-          "to": {
72
-            "kind": "ImageStreamTag",
73
-            "name": "origin-ruby-sample:latest"
74
-          }
75
-        },
76
-        "resources": {}
77
-      },
78
-      "status": {
79
-        "lastVersion": 0
80
-      }
81
-    }
82
-  ],
83
-  "parameters": [
84
-  ],
85
-  "labels": {
86
-    "template": "jenkins-pipeline-test"
87
-  }
88
-}
89 1
new file mode 100644
... ...
@@ -0,0 +1,55 @@
0
+package jenkins
1
+
2
+import (
3
+	"strings"
4
+	"time"
5
+
6
+	o "github.com/onsi/gomega"
7
+
8
+	"k8s.io/kubernetes/pkg/util/wait"
9
+)
10
+
11
+// JobMon is a Jenkins job monitor
12
+type JobMon struct {
13
+	j               *JenkinsRef
14
+	lastBuildNumber string
15
+	buildNumber     string
16
+	jobName         string
17
+}
18
+
19
+// Await waits for the timestamp on the Jenkins job to change. Returns
20
+// and error if the timeout expires.
21
+func (jmon *JobMon) Await(timeout time.Duration) error {
22
+	err := wait.Poll(10*time.Second, timeout, func() (bool, error) {
23
+
24
+		buildNumber, err := jmon.j.GetJobBuildNumber(jmon.jobName, time.Minute)
25
+		o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
26
+
27
+		ginkgolog("Checking build number for job %q current[%v] vs last[%v]", jmon.jobName, buildNumber, jmon.lastBuildNumber)
28
+		if buildNumber == jmon.lastBuildNumber {
29
+			return false, nil
30
+		}
31
+
32
+		if jmon.buildNumber == "" {
33
+			jmon.buildNumber = buildNumber
34
+		}
35
+		body, status, err := jmon.j.GetResource("job/%s/%s/api/json?depth=1", jmon.jobName, jmon.buildNumber)
36
+		o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
37
+		o.ExpectWithOffset(1, status).To(o.Equal(200))
38
+
39
+		body = strings.ToLower(body)
40
+		if strings.Contains(body, "\"building\":true") {
41
+			ginkgolog("Jenkins job %q still building:\n%s\n\n", jmon.jobName, body)
42
+			return false, nil
43
+		}
44
+
45
+		if strings.Contains(body, "\"result\":null") {
46
+			ginkgolog("Jenkins job %q still building result:\n%s\n\n", jmon.jobName, body)
47
+			return false, nil
48
+		}
49
+
50
+		ginkgolog("Jenkins job %q build complete:\n%s\n\n", jmon.jobName, body)
51
+		return true, nil
52
+	})
53
+	return err
54
+}
0 55
new file mode 100644
... ...
@@ -0,0 +1,349 @@
0
+package jenkins
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/xml"
5
+	"fmt"
6
+	"io"
7
+	"io/ioutil"
8
+	"net/http"
9
+	"regexp"
10
+	"strings"
11
+	"time"
12
+
13
+	g "github.com/onsi/ginkgo"
14
+	o "github.com/onsi/gomega"
15
+
16
+	kapi "k8s.io/kubernetes/pkg/api"
17
+	"k8s.io/kubernetes/pkg/util/wait"
18
+
19
+	exutil "github.com/openshift/origin/test/extended/util"
20
+)
21
+
22
+// JenkinsRef represents a Jenkins instance running on an OpenShift server
23
+type JenkinsRef struct {
24
+	oc   *exutil.CLI
25
+	host string
26
+	port string
27
+	// The namespace in which the Jenkins server is running
28
+	namespace string
29
+	password  string
30
+}
31
+
32
+// FlowDefinition can be marshalled into XML to represent a Jenkins workflow job definition.
33
+type FlowDefinition struct {
34
+	XMLName          xml.Name `xml:"flow-definition"`
35
+	Plugin           string   `xml:"plugin,attr"`
36
+	KeepDependencies bool     `xml:"keepDependencies"`
37
+	Definition       Definition
38
+}
39
+
40
+// Definition is part of a FlowDefinition
41
+type Definition struct {
42
+	XMLName xml.Name `xml:"definition"`
43
+	Class   string   `xml:"class,attr"`
44
+	Plugin  string   `xml:"plugin,attr"`
45
+	Script  string   `xml:"script"`
46
+}
47
+
48
+// ginkgolog creates simple entry in the GinkgoWriter.
49
+func ginkgolog(format string, a ...interface{}) {
50
+	fmt.Fprintf(g.GinkgoWriter, format+"\n", a...)
51
+}
52
+
53
+// NewRef creates a jenkins reference from an OC client
54
+func NewRef(oc *exutil.CLI) *JenkinsRef {
55
+	g.By("get ip and port for jenkins service")
56
+	serviceIP, err := oc.Run("get").Args("svc", "jenkins", "--config", exutil.KubeConfigPath()).Template("{{.spec.clusterIP}}").Output()
57
+	o.Expect(err).NotTo(o.HaveOccurred())
58
+	port, err := oc.Run("get").Args("svc", "jenkins", "--config", exutil.KubeConfigPath()).Template("{{ $x := index .spec.ports 0}}{{$x.port}}").Output()
59
+	o.Expect(err).NotTo(o.HaveOccurred())
60
+
61
+	g.By("get admin password")
62
+	password := GetAdminPassword(oc)
63
+	o.Expect(password).ShouldNot(o.BeEmpty())
64
+
65
+	j := &JenkinsRef{
66
+		oc:        oc,
67
+		host:      serviceIP,
68
+		port:      port,
69
+		namespace: oc.Namespace(),
70
+		password:  password,
71
+	}
72
+	return j
73
+}
74
+
75
+// Namespace returns the Jenkins namespace
76
+func (j *JenkinsRef) Namespace() string {
77
+	return j.namespace
78
+}
79
+
80
+// BuildURI builds a URI for the Jenkins server.
81
+func (j *JenkinsRef) BuildURI(resourcePathFormat string, a ...interface{}) string {
82
+	resourcePath := fmt.Sprintf(resourcePathFormat, a...)
83
+	return fmt.Sprintf("http://%s:%v/%s", j.host, j.port, resourcePath)
84
+}
85
+
86
+// GetResource submits a GET request to this Jenkins server.
87
+// Returns a response body and status code or an error.
88
+func (j *JenkinsRef) GetResource(resourcePathFormat string, a ...interface{}) (string, int, error) {
89
+	uri := j.BuildURI(resourcePathFormat, a...)
90
+	ginkgolog("Retrieving Jenkins resource: %q", uri)
91
+	req, err := http.NewRequest("GET", uri, nil)
92
+	if err != nil {
93
+		return "", 0, fmt.Errorf("Unable to build request for uri %q: %v", uri, err)
94
+	}
95
+
96
+	// http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi
97
+	req.Close = true
98
+
99
+	req.SetBasicAuth("admin", j.password)
100
+	client := &http.Client{}
101
+	resp, err := client.Do(req)
102
+
103
+	if err != nil {
104
+		return "", 0, fmt.Errorf("Unable to GET uri %q: %v", uri, err)
105
+	}
106
+
107
+	defer resp.Body.Close()
108
+	status := resp.StatusCode
109
+
110
+	body, err := ioutil.ReadAll(resp.Body)
111
+	if err != nil {
112
+		return "", 0, fmt.Errorf("Error reading GET response %q: %v", uri, err)
113
+	}
114
+
115
+	return string(body), status, nil
116
+}
117
+
118
+// Post sends a POST to the Jenkins server. Returns response body and status code or an error.
119
+func (j *JenkinsRef) Post(reqBody io.Reader, resourcePathFormat, contentType string, a ...interface{}) (string, int, error) {
120
+	uri := j.BuildURI(resourcePathFormat, a...)
121
+
122
+	req, err := http.NewRequest("POST", uri, reqBody)
123
+	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
124
+
125
+	// http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi
126
+	req.Close = true
127
+
128
+	if reqBody != nil {
129
+		req.Header.Set("Content-Type", contentType)
130
+		req.Header.Del("Expect") // jenkins will return 417 if we have an expect hdr
131
+	}
132
+	req.SetBasicAuth("admin", j.password)
133
+
134
+	client := &http.Client{}
135
+	ginkgolog("Posting to Jenkins resource: %q", uri)
136
+	resp, err := client.Do(req)
137
+	if err != nil {
138
+		return "", 0, fmt.Errorf("Error posting request to %q: %v", uri, err)
139
+	}
140
+
141
+	defer resp.Body.Close()
142
+	status := resp.StatusCode
143
+
144
+	body, err := ioutil.ReadAll(resp.Body)
145
+	if err != nil {
146
+		return "", 0, fmt.Errorf("Error reading Post response body %q: %v", uri, err)
147
+	}
148
+
149
+	return string(body), status, nil
150
+}
151
+
152
+// PostXML sends a POST to the Jenkins server. If a body is specified, it should be XML.
153
+// Returns response body and status code or an error.
154
+func (j *JenkinsRef) PostXML(reqBody io.Reader, resourcePathFormat string, a ...interface{}) (string, int, error) {
155
+	return j.Post(reqBody, resourcePathFormat, "application/xml", a...)
156
+}
157
+
158
+// GetResourceWithStatus repeatedly tries to GET a jenkins resource with an acceptable
159
+// HTTP status. Retries for the specified duration.
160
+func (j *JenkinsRef) GetResourceWithStatus(validStatusList []int, timeout time.Duration, resourcePathFormat string, a ...interface{}) (string, int, error) {
161
+	var retBody string
162
+	var retStatus int
163
+	err := wait.Poll(10*time.Second, timeout, func() (bool, error) {
164
+		body, status, err := j.GetResource(resourcePathFormat, a...)
165
+		if err != nil {
166
+			ginkgolog("Error accessing resource: %v", err)
167
+			return false, nil
168
+		}
169
+		var found bool
170
+		for _, s := range validStatusList {
171
+			if status == s {
172
+				found = true
173
+				break
174
+			}
175
+		}
176
+		if !found {
177
+			ginkgolog("Expected http status [%v] during GET by recevied [%v]", validStatusList, status)
178
+			return false, nil
179
+		}
180
+		retBody = body
181
+		retStatus = status
182
+		return true, nil
183
+	})
184
+	if err != nil {
185
+		uri := j.BuildURI(resourcePathFormat, a...)
186
+		return "", retStatus, fmt.Errorf("Error waiting for status %v from resource path %s: %v", validStatusList, uri, err)
187
+	}
188
+	return retBody, retStatus, nil
189
+}
190
+
191
+// WaitForContent waits for a particular HTTP status and HTML matching a particular
192
+// pattern to be returned by this Jenkins server. An error will be returned
193
+// if the condition is not matched within the timeout period.
194
+func (j *JenkinsRef) WaitForContent(verificationRegEx string, verificationStatus int, timeout time.Duration, resourcePathFormat string, a ...interface{}) (string, error) {
195
+	var matchingContent = ""
196
+	err := wait.Poll(10*time.Second, timeout, func() (bool, error) {
197
+
198
+		content, _, err := j.GetResourceWithStatus([]int{verificationStatus}, timeout, resourcePathFormat, a...)
199
+		if err != nil {
200
+			return false, nil
201
+		}
202
+
203
+		if len(verificationRegEx) > 0 {
204
+			re := regexp.MustCompile(verificationRegEx)
205
+			if re.MatchString(content) {
206
+				matchingContent = content
207
+				return true, nil
208
+			} else {
209
+				ginkgolog("Content did not match verification regex %q:\n %v", verificationRegEx, content)
210
+				return false, nil
211
+			}
212
+		} else {
213
+			matchingContent = content
214
+			return true, nil
215
+		}
216
+	})
217
+
218
+	if err != nil {
219
+		uri := j.BuildURI(resourcePathFormat, a...)
220
+		return "", fmt.Errorf("Error waiting for status %v and verification regex %q from resource path %s: %v", verificationStatus, verificationRegEx, uri, err)
221
+	} else {
222
+		return matchingContent, nil
223
+	}
224
+}
225
+
226
+// CreateItem submits XML to create a named item on the Jenkins server.
227
+func (j *JenkinsRef) CreateItem(name string, itemDefXML string) {
228
+	g.By(fmt.Sprintf("Creating new jenkins item: %s", name))
229
+	_, status, err := j.PostXML(bytes.NewBufferString(itemDefXML), "CreateItem?name=%s", name)
230
+	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
231
+	o.ExpectWithOffset(1, status).To(o.Equal(200))
232
+}
233
+
234
+// GetJobBuildNumber returns the current buildNumber on the named project OR "new" if
235
+// there are no builds against a job yet.
236
+func (j *JenkinsRef) GetJobBuildNumber(name string, timeout time.Duration) (string, error) {
237
+	body, status, err := j.GetResourceWithStatus([]int{200, 404}, timeout, "job/%s/lastBuild/buildNumber", name)
238
+	if err != nil {
239
+		return "", err
240
+	}
241
+	if status != 200 {
242
+		return "new", nil
243
+	}
244
+	return body, nil
245
+}
246
+
247
+// StartJob triggers a named Jenkins job. The job can be monitored with the
248
+// returned object.
249
+func (j *JenkinsRef) StartJob(jobName string) *JobMon {
250
+	lastBuildNumber, err := j.GetJobBuildNumber(jobName, time.Minute)
251
+	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
252
+
253
+	jmon := &JobMon{
254
+		j:               j,
255
+		lastBuildNumber: lastBuildNumber,
256
+		buildNumber:     "",
257
+		jobName:         jobName,
258
+	}
259
+
260
+	ginkgolog("Current timestamp for [%s]: %q", jobName, jmon.lastBuildNumber)
261
+	g.By(fmt.Sprintf("Starting jenkins job: %s", jobName))
262
+	_, status, err := j.PostXML(nil, "job/%s/build?delay=0sec", jobName)
263
+	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
264
+	o.ExpectWithOffset(1, status).To(o.Equal(201))
265
+
266
+	return jmon
267
+}
268
+
269
+// ReadJenkinsJobUsingVars returns the content of a Jenkins job XML file. Instances of the
270
+// string "PROJECT_NAME" are replaced with the specified namespace.
271
+// Variables named in the vars map will also be replaced with their
272
+// corresponding value.
273
+func (j *JenkinsRef) ReadJenkinsJobUsingVars(filename, namespace string, vars map[string]string) string {
274
+	pre := exutil.FixturePath("testdata", "jenkins-plugin", filename)
275
+	post := exutil.ArtifactPath(filename)
276
+
277
+	if vars == nil {
278
+		vars = map[string]string{}
279
+	}
280
+	vars["PROJECT_NAME"] = namespace
281
+	err := exutil.VarSubOnFile(pre, post, vars)
282
+	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
283
+
284
+	data, err := ioutil.ReadFile(post)
285
+	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
286
+	return string(data)
287
+}
288
+
289
+// ReadJenkinsJob returns the content of a Jenkins job XML file. Instances of the
290
+// string "PROJECT_NAME" are replaced with the specified namespace.
291
+func (j *JenkinsRef) ReadJenkinsJob(filename, namespace string) string {
292
+	return j.ReadJenkinsJobUsingVars(filename, namespace, nil)
293
+}
294
+
295
+// BuildDSLJob returns an XML string defining a Jenkins workflow/pipeline DSL job. Instances of the
296
+// string "PROJECT_NAME" are replaced with the specified namespace.
297
+func (j *JenkinsRef) BuildDSLJob(namespace string, scriptLines ...string) (string, error) {
298
+	script := strings.Join(scriptLines, "\n")
299
+	script = strings.Replace(script, "PROJECT_NAME", namespace, -1)
300
+	fd := FlowDefinition{
301
+		Plugin: "workflow-job@2.7",
302
+		Definition: Definition{
303
+			Class:  "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition",
304
+			Plugin: "workflow-cps@2.18",
305
+			Script: script,
306
+		},
307
+	}
308
+	output, err := xml.MarshalIndent(fd, "  ", "    ")
309
+	ginkgolog("Formulated DSL Project XML:\n%s\n\n", output)
310
+	return string(output), err
311
+}
312
+
313
+// GetJobConsoleLogs returns the console logs of a particular buildNumber.
314
+func (j *JenkinsRef) GetJobConsoleLogs(jobName, buildNumber string) (string, error) {
315
+	return j.WaitForContent("", 200, 10*time.Minute, "job/%s/%s/consoleText", jobName, buildNumber)
316
+}
317
+
318
+// GetLastJobConsoleLogs returns the last build associated with a Jenkins job.
319
+func (j *JenkinsRef) GetLastJobConsoleLogs(jobName string) (string, error) {
320
+	return j.GetJobConsoleLogs(jobName, "lastBuild")
321
+}
322
+
323
+func GetAdminPassword(oc *exutil.CLI) string {
324
+	envs, err := oc.Run("set").Args("env", "dc/jenkins", "--list").Output()
325
+	o.Expect(err).NotTo(o.HaveOccurred())
326
+	kvs := strings.Split(envs, "\n")
327
+	for _, kv := range kvs {
328
+		if strings.HasPrefix(kv, "JENKINS_PASSWORD=") {
329
+			s := strings.Split(kv, "=")
330
+			fmt.Fprintf(g.GinkgoWriter, "\nJenkins admin password %s\n", s[1])
331
+			return s[1]
332
+		}
333
+	}
334
+	return "password"
335
+}
336
+
337
+// Finds the pod running Jenkins
338
+func FindJenkinsPod(oc *exutil.CLI) *kapi.Pod {
339
+	pods, err := exutil.GetDeploymentConfigPods(oc, "jenkins")
340
+	o.ExpectWithOffset(1, err).NotTo(o.HaveOccurred())
341
+
342
+	if pods == nil || pods.Items == nil {
343
+		g.Fail("No pods matching jenkins deploymentconfig in namespace " + oc.Namespace())
344
+	}
345
+
346
+	o.ExpectWithOffset(1, len(pods.Items)).To(o.Equal(1))
347
+	return &pods.Items[0]
348
+}