hudson-remote-cli 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,144 @@
1
+ module Hudson
2
+ class Job < HudsonObject
3
+ attr_reader :name
4
+
5
+ class << self
6
+ def create(name, config_path)
7
+ name.strip!
8
+
9
+ config = File.open(config_path).read
10
+
11
+ res = send_xml_post_request(
12
+ uri_for(@@api_create_item, {:name => name}),
13
+ config
14
+ )
15
+ raise(APIError, "Error creating job #{name}: #{res.body}") if
16
+ res.class != Net::HTTPOK
17
+
18
+ Job.new(name)
19
+ end
20
+ end
21
+
22
+ def initialize(name)
23
+ name.strip!
24
+ raise "no job named #{name}!" unless Hudson.jobs.include?(name)
25
+ @name = name
26
+ load_json_api
27
+ end
28
+
29
+ def load_json_api
30
+ @api_job = "job/#{@name}/api/json"
31
+ @api_job_config = "job/#{@name}/config.xml"
32
+ @api_job_description = "job/#{@name}/description"
33
+ @api_job_build = "job/#{@name}/build"
34
+ @api_job_withparam_build = "job/#{@name}/buildWithParameters"
35
+ @api_job_delete = "job/#{@name}/doDelete"
36
+ @api_job_disable = "job/#{@name}/disable"
37
+ @api_job_enable = "job/#{@name}/enable"
38
+ end
39
+
40
+ def api(keys = nil, refresh = true)
41
+ api_base(@api_job, keys, refresh)
42
+ end
43
+
44
+ def active?
45
+ api('color')['color'].include?('anime')
46
+ end
47
+
48
+ def buildable?
49
+ api('buildable')['buildable']
50
+ end
51
+
52
+ def last_success_build
53
+ res = api('lastSuccessfulBuild[number]')['lastSuccessfulBuild']
54
+ res.nil? ? 0 : res['number']
55
+ end
56
+
57
+ def last_build
58
+ res = api('lastBuild[number]')['lastBuild']
59
+ res.nil? ? 0 : res['number']
60
+ end
61
+
62
+ def builds
63
+ res = api('builds[number]')['builds']
64
+ res.nil? ? [] : res.map { |b| b['number'] }
65
+ end
66
+
67
+ def update(config)
68
+ @config = File.open(config).read unless config.nil?
69
+ res = send_xml_post_request(uri_for(@api_job_config), @config)
70
+ @config = nil
71
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
72
+ end
73
+
74
+ def config
75
+ return @config unless @config.nil?
76
+ @config = get(uri_for(@api_job_config))
77
+ @config
78
+ end
79
+
80
+ def description
81
+ return @description unless @description.nil?
82
+ @description = get(uri_for(@api_job_description))
83
+ @description
84
+ end
85
+
86
+ def description=(description)
87
+ @description = description
88
+ send_post_request(uri_for(@api_job_description), {
89
+ :description => @description
90
+ })
91
+ end
92
+
93
+ def copy(name = nil)
94
+ name = "copy_of_#{@name}" if name.nil?
95
+ name.strip!
96
+ res = send_post_request(uri_for(@@api_create_item), {
97
+ :name => name,
98
+ :mode => 'copy',
99
+ :from => @name
100
+ })
101
+ raise(APIError, "Error copying job #{@name}: #{res.body}") if
102
+ res.class != Net::HTTPFound
103
+ Job.new(name)
104
+ end
105
+
106
+ def disable
107
+ res = send_post_request(uri_for(@api_job_disable))
108
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
109
+ end
110
+
111
+ def enable
112
+ res = send_post_request(uri_for(@api_job_enable))
113
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
114
+ end
115
+
116
+ def delete
117
+ res = send_post_request(uri_for(@api_job_delete))
118
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
119
+ end
120
+
121
+ def build(param = {})
122
+ response = nil
123
+ if param.size.eql? 0
124
+ response = send_post_request(uri_for(@api_job_build), {:delay => '0sec'})
125
+ else
126
+ param[:delay] = '0sec' unless param.has_key? :delay
127
+ response = send_post_request(uri_for(@api_job_withparam_build), param)
128
+ end
129
+ response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
130
+ end
131
+
132
+ def build!(param = {})
133
+ build(param)
134
+ wait_for_build
135
+ end
136
+
137
+ def wait_for_build(poll_freq = 10)
138
+ loop do
139
+ break if !active? and !BuildQueue.list.include?(@name)
140
+ sleep poll_freq
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,18 @@
1
+ module Hudson
2
+ class System < HudsonObject
3
+ class << self
4
+ def api(keys = nil, refresh = false)
5
+ api_base(@@api_hudson, keys, refresh)
6
+ end
7
+
8
+ def overallLoad(keys = nil, refresh = false)
9
+ api_base(@@api_overallLoad, keys, refresh)
10
+ end
11
+
12
+ def jobs(*param)
13
+ jobs = api('jobs[name]', true)
14
+ jobs['jobs'].map { |j| j['name'] }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Hudson
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,118 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'rack/utils'
4
+ require 'zlib'
5
+ require 'json'
6
+ require 'hudson-remote-cli/version'
7
+ require 'hudson-remote-cli/config'
8
+
9
+ module Hudson
10
+ autoload :System, 'hudson-remote-cli/system'
11
+ autoload :BuildQueue, 'hudson-remote-cli/build_queue'
12
+ autoload :Job, 'hudson-remote-cli/job'
13
+ autoload :Build, 'hudson-remote-cli/build'
14
+
15
+ class APIError < StandardError; end
16
+
17
+ class HudsonObject
18
+ def self.uri_for(path, query = {})
19
+ uri = URI.parse(URI.encode(File.join(Hudson[:url], path)))
20
+ uri.query = Rack::Utils.build_query(query)
21
+ uri
22
+ end
23
+
24
+ def uri_for(path, query = {})
25
+ self.class.uri_for(path, query)
26
+ end
27
+
28
+ def self.load_json_api
29
+ @@_api_cache = Hash.new
30
+
31
+ @@api_hudson = 'api/json'
32
+ @@api_create_item = 'createItem'
33
+ @@api_overallLoad = 'overallLoad/api/json'
34
+ end
35
+
36
+ load_json_api
37
+
38
+ def self.hudson_request(uri, request)
39
+ Net::HTTP.start(uri.host, uri.port) do |http|
40
+ http = Net::HTTP.new(uri.host, uri.port)
41
+ if uri.scheme == 'https'
42
+ http.use_ssl = true
43
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
44
+ end
45
+ http.request(request)
46
+ end
47
+ end
48
+
49
+ def self.get(uri)
50
+ request = Net::HTTP::Get.new(uri.request_uri)
51
+ request.basic_auth(Hudson[:user], Hudson[:password]) if
52
+ Hudson[:user] and Hudson[:password]
53
+ request['Content-Type'] = 'text/html'
54
+ response = hudson_request(uri, request)
55
+
56
+ if response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
57
+ encoding = response.get_fields("Content-Encoding")
58
+ if encoding and encoding.include?("gzip")
59
+ return Zlib::GzipReader.new(StringIO.new(response.body)).read
60
+ else
61
+ return response.body
62
+ end
63
+ else
64
+ $stderr.puts response.body
65
+ raise APIError, "Error retrieving #{uri.request_uri}"
66
+ end
67
+ end
68
+
69
+ def get(uri)
70
+ self.class.get(uri)
71
+ end
72
+
73
+ def self.send_post_request(uri, data={})
74
+ request = Net::HTTP::Post.new(uri.request_uri)
75
+ request.basic_auth(Hudson[:user], Hudson[:password]) if
76
+ Hudson[:user] and Hudson[:password]
77
+ request.set_form_data(data)
78
+ hudson_request(uri, request)
79
+ end
80
+
81
+ def send_post_request(uri, data={})
82
+ self.class.send_post_request(uri, data)
83
+ end
84
+
85
+ def self.send_xml_post_request(uri, xml, data=nil)
86
+ request = Net::HTTP::Post.new(uri.request_uri)
87
+ request['Content-Type'] = 'text/xml'
88
+ request.basic_auth(Hudson[:user], Hudson[:password]) if
89
+ Hudson[:user] and Hudson[:password]
90
+ request.set_form_data(data) if data
91
+ request.body = xml
92
+ hudson_request(uri,request)
93
+ end
94
+
95
+ def send_xml_post_request(uri, xml, data=nil)
96
+ self.class.send_xml_post_request(uri, xml, data)
97
+ end
98
+
99
+ def self.api_base(api, keys = nil, refresh = false)
100
+ cache_key = [api, keys.nil? ? '__ALL__' : keys].join '_'
101
+
102
+ return @@_api_cache[cache_key] if
103
+ @@_api_cache.has_key?(cache_key) and not refresh
104
+
105
+ json = get(uri_for(api, keys.nil? ? {} : {:tree => keys}))
106
+ @@_api_cache[cache_key] = JSON.load(json)
107
+ @@_api_cache[cache_key]
108
+ end
109
+
110
+ def api_base(api, keys = nil, refresh = false)
111
+ self.class.api_base(api, keys, refresh)
112
+ end
113
+ end
114
+
115
+ def self.method_missing(method, *args, &block)
116
+ Hudson::System.send(method, *args, block)
117
+ end
118
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ module Hudson
4
+ describe BuildQueue do
5
+ describe '#api' do
6
+ it 'should be a Hash' do
7
+ api = BuildQueue.api
8
+ api.class.should eq(Hash)
9
+ end
10
+
11
+ it 'should be only jobs[name, color]' do
12
+ jobs = BuildQueue.api('items[task[name]]')
13
+ jobs.keys.should eq(['items'])
14
+ end
15
+ end
16
+ end
17
+ end
data/spec/config.xml ADDED
@@ -0,0 +1,258 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <project>
3
+ <actions/>
4
+ <description></description>
5
+ <project-properties class="java.util.concurrent.ConcurrentHashMap">
6
+ <entry>
7
+ <string>blockBuildWhenDownstreamBuilding</string>
8
+ <boolean-property>
9
+ <originalValue class="boolean">false</originalValue>
10
+ <propertyOverridden>false</propertyOverridden>
11
+ </boolean-property>
12
+ </entry>
13
+ <entry>
14
+ <string>org-hudsonci-plugins-snapshotmonitor-SnapshotTrigger</string>
15
+ <trigger-property>
16
+ <propertyOverridden>false</propertyOverridden>
17
+ </trigger-property>
18
+ </entry>
19
+ <entry>
20
+ <string>org-hudsonci-plugins-snapshotmonitor-WatchedDependenciesProperty</string>
21
+ <base-property>
22
+ <propertyOverridden>false</propertyOverridden>
23
+ </base-property>
24
+ </entry>
25
+ <entry>
26
+ <string>logRotator</string>
27
+ <log-rotator-property>
28
+ <propertyOverridden>false</propertyOverridden>
29
+ </log-rotator-property>
30
+ </entry>
31
+ <entry>
32
+ <string>au-com-centrumsystems-hudson-plugin-buildpipeline-trigger-BuildPipelineTrigger</string>
33
+ <external-property>
34
+ <propertyOverridden>false</propertyOverridden>
35
+ <modified>false</modified>
36
+ </external-property>
37
+ </entry>
38
+ <entry>
39
+ <string>blockBuildWhenUpstreamBuilding</string>
40
+ <boolean-property>
41
+ <originalValue class="boolean">false</originalValue>
42
+ <propertyOverridden>false</propertyOverridden>
43
+ </boolean-property>
44
+ </entry>
45
+ <entry>
46
+ <string>scmCheckoutRetryCount</string>
47
+ <integer-property>
48
+ <propertyOverridden>false</propertyOverridden>
49
+ </integer-property>
50
+ </entry>
51
+ <entry>
52
+ <string>hudson-plugins-promoted_builds-JobPropertyImpl</string>
53
+ <base-property>
54
+ <propertyOverridden>false</propertyOverridden>
55
+ </base-property>
56
+ </entry>
57
+ <entry>
58
+ <string>org-hudsonci-maven-plugin-dependencymonitor-DependencyNotifier</string>
59
+ <external-property>
60
+ <propertyOverridden>false</propertyOverridden>
61
+ <modified>false</modified>
62
+ </external-property>
63
+ </entry>
64
+ <entry>
65
+ <string>hudson-plugins-git-GitPublisher</string>
66
+ <external-property>
67
+ <propertyOverridden>false</propertyOverridden>
68
+ <modified>false</modified>
69
+ </external-property>
70
+ </entry>
71
+ <entry>
72
+ <string>org-hudsonci-maven-plugin-artifactrecorder-ArtifactArchiver</string>
73
+ <external-property>
74
+ <propertyOverridden>false</propertyOverridden>
75
+ <modified>false</modified>
76
+ </external-property>
77
+ </entry>
78
+ <entry>
79
+ <string>hudson-tasks-Mailer</string>
80
+ <external-property>
81
+ <propertyOverridden>false</propertyOverridden>
82
+ <modified>false</modified>
83
+ </external-property>
84
+ </entry>
85
+ <entry>
86
+ <string>cleanWorkspaceRequired</string>
87
+ <boolean-property>
88
+ <originalValue class="boolean">false</originalValue>
89
+ <propertyOverridden>false</propertyOverridden>
90
+ </boolean-property>
91
+ </entry>
92
+ <entry>
93
+ <string>hudson-tasks-test-AggregatedTestResultPublisher</string>
94
+ <external-property>
95
+ <propertyOverridden>false</propertyOverridden>
96
+ <modified>false</modified>
97
+ </external-property>
98
+ </entry>
99
+ <entry>
100
+ <string>hudson-tasks-junit-JUnitResultArchiver</string>
101
+ <external-property>
102
+ <propertyOverridden>false</propertyOverridden>
103
+ <modified>false</modified>
104
+ </external-property>
105
+ </entry>
106
+ <entry>
107
+ <string>hudson-tasks-Fingerprinter</string>
108
+ <external-property>
109
+ <propertyOverridden>false</propertyOverridden>
110
+ <modified>false</modified>
111
+ </external-property>
112
+ </entry>
113
+ <entry>
114
+ <string>hudson-plugins-ansicolor-AnsiColorBuildWrapper</string>
115
+ <external-property>
116
+ <propertyOverridden>false</propertyOverridden>
117
+ <modified>true</modified>
118
+ </external-property>
119
+ </entry>
120
+ <entry>
121
+ <string>hudson-plugins-downstream_ext-DownstreamTrigger</string>
122
+ <external-property>
123
+ <propertyOverridden>false</propertyOverridden>
124
+ <modified>false</modified>
125
+ </external-property>
126
+ </entry>
127
+ <entry>
128
+ <string>hudson-tasks-JavadocArchiver</string>
129
+ <external-property>
130
+ <propertyOverridden>false</propertyOverridden>
131
+ <modified>false</modified>
132
+ </external-property>
133
+ </entry>
134
+ <entry>
135
+ <string>parametersDefinitionProperties</string>
136
+ <copy-write-list-property>
137
+ <originalValue class="hudson.util.CopyOnWriteList"/>
138
+ <propertyOverridden>false</propertyOverridden>
139
+ </copy-write-list-property>
140
+ </entry>
141
+ <entry>
142
+ <string>customWorkspace</string>
143
+ <string-property>
144
+ <propertyOverridden>false</propertyOverridden>
145
+ </string-property>
146
+ </entry>
147
+ <entry>
148
+ <string>quietPeriod</string>
149
+ <integer-property>
150
+ <propertyOverridden>false</propertyOverridden>
151
+ </integer-property>
152
+ </entry>
153
+ <entry>
154
+ <string>hudson-tasks-BuildTrigger</string>
155
+ <external-property>
156
+ <propertyOverridden>false</propertyOverridden>
157
+ <modified>false</modified>
158
+ </external-property>
159
+ </entry>
160
+ <entry>
161
+ <string>hudson-triggers-SCMTrigger</string>
162
+ <trigger-property>
163
+ <propertyOverridden>false</propertyOverridden>
164
+ </trigger-property>
165
+ </entry>
166
+ <entry>
167
+ <string>builders</string>
168
+ <describable-list-property>
169
+ <originalValue class="hudson.util.DescribableList">
170
+ <hudson.tasks.Shell>
171
+ <command>echo nice
172
+ sleep 10</command>
173
+ </hudson.tasks.Shell>
174
+ </originalValue>
175
+ <propertyOverridden>false</propertyOverridden>
176
+ </describable-list-property>
177
+ </entry>
178
+ <entry>
179
+ <string>jdk</string>
180
+ <string-property>
181
+ <propertyOverridden>false</propertyOverridden>
182
+ </string-property>
183
+ </entry>
184
+ <entry>
185
+ <string>appointedNode</string>
186
+ <base-property>
187
+ <propertyOverridden>false</propertyOverridden>
188
+ </base-property>
189
+ </entry>
190
+ <entry>
191
+ <string>hudson-plugins-perforce-PerforceTagNotifier</string>
192
+ <external-property>
193
+ <propertyOverridden>false</propertyOverridden>
194
+ <modified>false</modified>
195
+ </external-property>
196
+ </entry>
197
+ <entry>
198
+ <string>concurrentBuild</string>
199
+ <boolean-property>
200
+ <originalValue class="boolean">false</originalValue>
201
+ <propertyOverridden>false</propertyOverridden>
202
+ </boolean-property>
203
+ </entry>
204
+ <entry>
205
+ <string>hudson-tasks-ArtifactArchiver</string>
206
+ <external-property>
207
+ <propertyOverridden>false</propertyOverridden>
208
+ <modified>false</modified>
209
+ </external-property>
210
+ </entry>
211
+ <entry>
212
+ <string>org-hudsonci-maven-plugin-dependencymonitor-DependencyTrigger</string>
213
+ <trigger-property>
214
+ <propertyOverridden>false</propertyOverridden>
215
+ </trigger-property>
216
+ </entry>
217
+ <entry>
218
+ <string>scm</string>
219
+ <scm-property>
220
+ <originalValue class="hudson.scm.NullSCM"/>
221
+ <propertyOverridden>false</propertyOverridden>
222
+ </scm-property>
223
+ </entry>
224
+ <entry>
225
+ <string>hudson-triggers-TimerTrigger</string>
226
+ <trigger-property>
227
+ <propertyOverridden>false</propertyOverridden>
228
+ </trigger-property>
229
+ </entry>
230
+ <entry>
231
+ <string>org-hudsonci-maven-plugin-artifactrecorder-ArtifactFingerprinter</string>
232
+ <external-property>
233
+ <propertyOverridden>false</propertyOverridden>
234
+ <modified>false</modified>
235
+ </external-property>
236
+ </entry>
237
+ </project-properties>
238
+ <keepDependencies>false</keepDependencies>
239
+ <createdBy>im-op</createdBy>
240
+ <creationTime>1375924970937</creationTime>
241
+ <properties/>
242
+ <cascadingChildrenNames class="java.util.concurrent.CopyOnWriteArraySet"/>
243
+ <cascading-job-properties class="java.util.concurrent.CopyOnWriteArraySet">
244
+ <string>org-hudsonci-plugins-snapshotmonitor-WatchedDependenciesProperty</string>
245
+ <string>hudson-plugins-promoted_builds-JobPropertyImpl</string>
246
+ </cascading-job-properties>
247
+ <scm class="hudson.scm.NullSCM"/>
248
+ <canRoam>false</canRoam>
249
+ <disabled>false</disabled>
250
+ <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
251
+ <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
252
+ <triggers class="vector"/>
253
+ <concurrentBuild>false</concurrentBuild>
254
+ <cleanWorkspaceRequired>false</cleanWorkspaceRequired>
255
+ <builders/>
256
+ <publishers/>
257
+ <buildWrappers/>
258
+ </project>
data/spec/job_spec.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ module Hudson
4
+ describe Job, Build do
5
+ let (:job_name) { 'hudson-remote-cli-test' }
6
+ let (:job_conf) { File.join(File.dirname(__FILE__), 'config.xml') }
7
+
8
+ describe '#create' do
9
+ it 'should be a new job in test hudson' do
10
+ Hudson::Job.create job_name, job_conf
11
+ Hudson.jobs.include?(job_name).should be_true
12
+ end
13
+ end
14
+
15
+ describe 'Job Actions' do
16
+ before(:each) do
17
+ @job = Hudson::Job.new(job_name)
18
+ end
19
+
20
+ it 'should be job_name' do
21
+ @job.name.should eq(job_name)
22
+ end
23
+
24
+ context 'job enable/disable test' do
25
+ after(:each) { @job.enable }
26
+
27
+ it 'should be enabled' do
28
+ @job.enable
29
+ @job.buildable?.should be_true
30
+ end
31
+
32
+ it 'should be disabled' do
33
+ @job.disable
34
+ @job.buildable?.should be_false
35
+ end
36
+ end
37
+
38
+ context 'job description set/get' do
39
+ it 'shoud be "test success"' do
40
+ @job.description = 'test success'
41
+ @job.description.should eq('test success')
42
+ end
43
+ end
44
+
45
+ context 'job build/active/lastnumber test' do
46
+ before(:each) do
47
+ @job.wait_for_build
48
+ @last_number = @job.last_build
49
+ end
50
+
51
+ it 'should be trigger a build' do
52
+ @job.build!
53
+ last_number = @job.last_build
54
+ (last_number - @last_number).should eq(1)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe 'build test' do
60
+ before(:each) do
61
+ @build = Hudson::Build.new(job_name)
62
+ end
63
+
64
+ it 'should be 1' do
65
+ @build.number.should eq(1)
66
+ end
67
+
68
+ it 'should be suit for the time in record' do
69
+ (10 - (@build.end_time - @build.start_time)).should < 2
70
+ end
71
+ end
72
+
73
+ describe '#delete' do
74
+ it 'should be no test_job more' do
75
+ @job = Hudson::Job.new(job_name)
76
+ @job.delete
77
+ Hudson.jobs.include?(job_name).should be_false
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,6 @@
1
+ require 'hudson-remote-cli'
2
+ require 'rspec'
3
+
4
+ Hudson[:url] = 'http://localhost:8235/hudson'
5
+ Hudson[:user] = 'test'
6
+ Hudson[:password] = 'test'
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module Hudson
4
+ describe System do
5
+ describe '#api' do
6
+ it 'should be a Hash' do
7
+ api = Hudson.api
8
+ api.class.should eq(Hash)
9
+ end
10
+
11
+ it 'should be only jobs[name, color]' do
12
+ jobs = Hudson.api('jobs[name,color]')
13
+ jobs.keys.should eq(['jobs'])
14
+ end
15
+ end
16
+
17
+ describe '#overallLoad' do
18
+ it 'should be 4 keys at least' do
19
+ api = Hudson.overallLoad
20
+ (%w{busyExecutors queueLength totalExecutors totalQueueLength} -
21
+ api.keys).should eq([])
22
+ end
23
+ end
24
+ end
25
+ end