Browse code

Merge pull request #12330 from csrwng/pipeline_ext_tests

Merged by openshift-bot

OpenShift Bot authored on 2017/01/04 03:13:04
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
+}