Merge lp:~pfalcon/linaro-android-build-tools/cmdline-client into lp:linaro-android-build-tools
- cmdline-client
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 671 |
Proposed branch: | lp:~pfalcon/linaro-android-build-tools/cmdline-client |
Merge into: | lp:linaro-android-build-tools |
Diff against target: |
271 lines (+261/-0) 2 files modified
utils/cmdline-client/README (+62/-0) utils/cmdline-client/android-build-client (+199/-0) |
To merge this branch: | bzr merge lp:~pfalcon/linaro-android-build-tools/cmdline-client |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Sokolovsky | Approve | ||
Milo Casagrande (community) | Needs Fixing | ||
Review via email: mp+182969@code.launchpad.net |
Commit message
Description of the change
android-build command line client. This implements https:/
Paul Sokolovsky (pfalcon) wrote : | # |
Hello Milo,
Thanks for comments!
On Fri, 30 Aug 2013 14:31:38 -0000
Milo Casagrande <email address hidden> wrote:
> Review: Needs Fixing
>
> Hey Paul,
>
> following my comments, there's some typos that need fixing and a
> couple of suggestion/
> cleaned the typos it should be ready to go.
>
> +Basic android-
> +======
> +
> +This tool provides proof-of-concept command-line client for Linaro
> Android +Build Jenkins master (android-
> alternative for +existing Web UI interface. One particular feature
> the command-line client +has and Web UI lacks is support for creating
> completely private builds (such +builds are not availabel via Web UI
> at all). At the same time, this tool is +so far in the proof of
> concept stage and provides only basic job management +actions
> (namely, create a job and schedule its build). If you find this tools
>
> s/this tools/this tool
fixed
>
> +useful, please share you comments and suggestion using this
> bugtracker:
>
> s/you comments/your comments
fixed
>
> +
> +https:/
> +
> +to help its improvement and evolution.
> +
> +
> +Quick start
> +-----------
> +
> +1. You should have access to Jenkins at:
> +
> +http://
> +
> +(Generally available for Linaro Android team members).
> +
> +2. Download android-
> only
>
> s/to depend/it depends
You probably mean replacing the whole starting phrase? Nope, that
loses meaning. It not just randomly depends only on stdlib, it's
purposedly written to ;-).
>
> +on Python standard library, an easy way to get it is to download
> seperate +file via Launchpad BZR viewer:
> +
> +wget <fill in link once branch lands>
> +chmod +x android-
> +
> +3. Look up your Jenkins API token by visiting
> +https:/
> +"Show API Token..." button.
> +
> +4. Run:
> +
> +./android-
> +
> +Enter you Jenkins username and API token. Note that this will cache
> these
>
> s/you Jenkins/your Jenkins
fixed
>
> +credentials in your home dir, so use this only on your personal
> well-protected +workstation. The alternative is to use --user switch
> and input API key +interactively.
> +
> +5. To create a new job, prepare an Android job config (see
> +https:/
> for more +info) in a file. Run:
> +
> +./android-
> +
> +Note that job name should conform to Android Build Service naming
> +converntions, see documentation link above for more info. To create
> a job +private for particular group, pass --private=<group> switch to
> create command.
>
> s/job private/private job
Nope, again changes meaning ;-). "job private for particular group" ==
"job which is private for particular group"
>
> +
> +6. To schedule a build, run:
> +
> +./android-
> +
> +
> +Run ./android-
- 663. By Paul Sokolovsky
-
Fixed typos, thanks Milo for review.
- 664. By Paul Sokolovsky
-
Make --help show which commands are available.
- 665. By Paul Sokolovsky
-
Print error messages to stderr.
- 666. By Paul Sokolovsky
-
Fix typo.
Paul Sokolovsky (pfalcon) wrote : | # |
<pfalcon> Gwaihir: thanks for review on https:/
<Gwaihir> pfalcon, sure, lets not block on that, it is an open source license after all, non GPL compatible, but OSI approved... will raise this with Alan and see if we need to use just one license for all our projects or if it is fine to use others as long as they are open source
Preview Diff
1 | === added directory 'utils/cmdline-client' |
2 | === added file 'utils/cmdline-client/README' |
3 | --- utils/cmdline-client/README 1970-01-01 00:00:00 +0000 |
4 | +++ utils/cmdline-client/README 2013-09-03 10:45:47 +0000 |
5 | @@ -0,0 +1,62 @@ |
6 | +Basic android-build.linaro.org command-line client |
7 | +================================================== |
8 | + |
9 | +This tool provides proof-of-concept command-line client for Linaro Android |
10 | +Build Jenkins master (android-build.linaro.org), as an alternative for |
11 | +existing Web UI interface. One particular feature the command-line client |
12 | +has and Web UI lacks is support for creating completely private builds (such |
13 | +builds are not availabel via Web UI at all). At the same time, this tool is |
14 | +so far in the proof of concept stage and provides only basic job management |
15 | +actions (namely, create a job and schedule its build). If you find this tool |
16 | +useful, please share your comments and suggestion using this bugtracker: |
17 | + |
18 | +https://bugs.launchpad.net/linaro-android-infrastructure |
19 | + |
20 | +to help its improvement and evolution. |
21 | + |
22 | + |
23 | +Quick start |
24 | +----------- |
25 | + |
26 | +1. You should have access to Jenkins at: |
27 | + |
28 | +http://android-build.linaro.org/jenkins/ |
29 | + |
30 | +(Generally available for Linaro Android team members). |
31 | + |
32 | +2. Download android-build-client tool. As it is written to depend only |
33 | +on Python standard library, an easy way to get it is to download seperate |
34 | +file via Launchpad BZR viewer: |
35 | + |
36 | +wget <fill in link once branch lands> |
37 | +chmod +x android-build-client |
38 | + |
39 | +3. Look up your Jenkins API token by visiting |
40 | +https://android-build.linaro.org/jenkins/me/configure and clicking |
41 | +"Show API Token..." button. |
42 | + |
43 | +4. Run: |
44 | + |
45 | +./android-build-client authorize |
46 | + |
47 | +Enter yous Jenkins username and API token. Note that this will cache these |
48 | +credentials in your home dir, so use this only on your personal well-protected |
49 | +workstation. The alternative is to use --user switch and input API key |
50 | +interactively. |
51 | + |
52 | +5. To create a new job, prepare an Android job config (see |
53 | +https://wiki.linaro.org/Platform/Android/LinaroAndroidBuildService for more |
54 | +info) in a file. Run: |
55 | + |
56 | +./android-build-client create <job_name> <job_config_file> |
57 | + |
58 | +Note that job name should conform to Android Build Service naming |
59 | +converntions, see documentation link above for more info. To create a job |
60 | +private for a particular group, pass --private=<group> switch to create command. |
61 | + |
62 | +6. To schedule a build, run: |
63 | + |
64 | +./android-build-client build <job_name> |
65 | + |
66 | + |
67 | +Run ./android-build-client --help to see all options. |
68 | |
69 | === added file 'utils/cmdline-client/android-build-client' |
70 | --- utils/cmdline-client/android-build-client 1970-01-01 00:00:00 +0000 |
71 | +++ utils/cmdline-client/android-build-client 2013-09-03 10:45:47 +0000 |
72 | @@ -0,0 +1,199 @@ |
73 | +#!/usr/bin/env python |
74 | +############################################################################### |
75 | +# Copyright (c) 2013 Linaro |
76 | +# All rights reserved. This program and the accompanying materials |
77 | +# are made available under the terms of the Eclipse Public License v1.0 |
78 | +# which accompanies this distribution, and is available at |
79 | +# http://www.eclipse.org/legal/epl-v10.html |
80 | +############################################################################### |
81 | + |
82 | +import base64 |
83 | +import json |
84 | +import os |
85 | +import sys |
86 | +import urllib2 |
87 | +import optparse |
88 | +import getpass |
89 | +from xml.dom import minidom |
90 | + |
91 | +PRIVATE_ACL = """\ |
92 | + <hudson.security.AuthorizationMatrixProperty> |
93 | + <permission>hudson.model.Item.Build:%(group)s</permission> |
94 | + <permission>hudson.model.Item.Configure:%(group)s</permission> |
95 | + <permission>hudson.model.Item.Read:%(group)s</permission> |
96 | + <permission>hudson.model.Item.Cancel:%(group)s</permission> |
97 | + <permission>hudson.model.Item.Discover:%(group)s</permission> |
98 | + </hudson.security.AuthorizationMatrixProperty> |
99 | +""" |
100 | + |
101 | + |
102 | +class Jenkins(object): |
103 | + |
104 | + def __init__(self, base_url, username, passwd): |
105 | + self.base = base_url |
106 | + self.auth_headers = { |
107 | + 'Authorization': 'Basic %s' % ( |
108 | + base64.b64encode('%s:%s' % (username, passwd)))} |
109 | + self.csrf = None |
110 | + |
111 | + def jenkins_rest(self, jenkins_path, data=None, extra_headers=None): |
112 | + """Make an authenticated request to jenkins. |
113 | + |
114 | + @param jenkins_path: The path on the Jenkins instance to make the |
115 | + request to. |
116 | + @param data: Data to include in the request (if this is not None the |
117 | + request will be a POST). |
118 | + @param extra_headers: A dictionary of extra headers that will passed in |
119 | + addition to Authorization. |
120 | + @raises urllib2.HTTPError: If the response is not a HTTP 200. |
121 | + @returns: the body of the response. |
122 | + """ |
123 | + headers = self.auth_headers.copy() |
124 | + if extra_headers: |
125 | + headers.update(extra_headers) |
126 | + url = self.base + jenkins_path |
127 | + print url |
128 | + req = urllib2.Request( |
129 | + url, data, headers) |
130 | + resp = urllib2.urlopen(req) |
131 | + data = resp.read() |
132 | + resp.close() |
133 | + return data |
134 | + |
135 | + def get_csrf_headers(self): |
136 | + if self.csrf is None: |
137 | + try: |
138 | + crumb_data = self.jenkins_rest('crumbIssuer/api/json') |
139 | + data = json.loads(crumb_data) |
140 | + self.csrf = {data['crumbRequestField']: data['crumb']} |
141 | + except urllib2.HTTPError: |
142 | + # Ignore errors in case CSRF protection is not enabled |
143 | + self.csrf = {} |
144 | + return self.csrf |
145 | + |
146 | + def jenkins_rest_post(self, jenkins_path, data, extra_headers=None): |
147 | + if extra_headers is None: |
148 | + extra_headers = {} |
149 | + extra_headers.update(self.get_csrf_headers()) |
150 | + return self.jenkins_rest(jenkins_path, data, extra_headers) |
151 | + |
152 | + def get_job_config(self, job_name): |
153 | + return self.jenkins_rest('job/' + job_name + '/config.xml') |
154 | + |
155 | + def post_config(self, url, config_xml): |
156 | + return self.jenkins_rest_post(url, config_xml, |
157 | + {'Content-Type': 'text/xml'}) |
158 | + |
159 | + def set_job_config(self, job_name, config_xml): |
160 | + return self.post_config('job/' + job_name + '/config.xml', config_xml) |
161 | + |
162 | + def create_job(self, job_name, config_xml): |
163 | + return self.post_config('createItem?name=' + job_name, config_xml) |
164 | + |
165 | + def build_job(self, job_name): |
166 | + self.jenkins_rest_post( |
167 | + 'job/' + job_name + '/buildWithParameters?delay=0sec', '') |
168 | + |
169 | + |
170 | +def job2user_group(job_group): |
171 | + if job_group == "linaro-android": |
172 | + return "linaro-android-builders" |
173 | + return job_group |
174 | + |
175 | + |
176 | +def error(msg): |
177 | + print >>sys.stderr, msg |
178 | + sys.exit(1) |
179 | + |
180 | + |
181 | +def main(): |
182 | + global options |
183 | + optparser = optparse.OptionParser(usage="%prog authorize|create|build <args>...") |
184 | + optparser.add_option("--url", |
185 | + default="https://android-build.linaro.org/jenkins", |
186 | + help="Jenkins base url, default: %default") |
187 | + optparser.add_option("--user", |
188 | + help="Jenkins username, default: $USER") |
189 | + optparser.add_option("--apikey-file", metavar="FILE", |
190 | + help="File holding Jenkins API key") |
191 | + optparser.add_option("--cred-file", metavar="FILE", |
192 | + help="File holding Jenkins username:API key pair") |
193 | + optparser.add_option("--private", metavar="GROUP", |
194 | + help="Create private job accessible to GROUP") |
195 | + |
196 | + options, args = optparser.parse_args(sys.argv[1:]) |
197 | + if len(args) < 1: |
198 | + optparser.error("Wrong number of arguments") |
199 | + |
200 | + config_dir = os.path.expanduser("~/.config/android-build-client") |
201 | + |
202 | + password = None |
203 | + if options.cred_file: |
204 | + options.user, password = open(options.cred_file).read().strip().split(":") |
205 | + elif options.apikey_file: |
206 | + password = open(options.passwd_file).read().strip() |
207 | + elif os.path.exists(config_dir + "/cred"): |
208 | + options.user, password = open(config_dir + "/cred").read().strip().split(":") |
209 | + print "INFO: Using cached authorization for: %s" % options.user |
210 | + elif args[0] != "authorize": |
211 | + password = getpass.getpass("API Token:") |
212 | + |
213 | + if options.url[-1] != '/': |
214 | + options.url += '/' |
215 | + |
216 | + j = Jenkins(options.url, options.user, password) |
217 | + |
218 | + if args[0] == "authorize": |
219 | + if len(args) != 1: |
220 | + optparser.error("Usage: authorize") |
221 | + import getpass |
222 | + user = raw_input("Enter user name: ") |
223 | + while True: |
224 | + key = getpass.getpass("API key: ") |
225 | + if len(key) == 32: |
226 | + break |
227 | + print "This does not look like Jenkisn API key, please try again" |
228 | + try: |
229 | + os.makedirs(config_dir) |
230 | + except OSError: |
231 | + pass |
232 | + f = open(config_dir + "/cred", "w") |
233 | + print >>f, "%s:%s" % (user, key) |
234 | + f.close() |
235 | + print "Credentials cached for future use" |
236 | + elif args[0] == "create": |
237 | + if len(args) != 3: |
238 | + optparser.error("Usage: create <job_name> <build_config_file>") |
239 | + job_group, job_subname = args[1].split("/") |
240 | + template = j.get_job_config("template_" + job2user_group(job_group)) |
241 | + dom = minidom.parseString(template) |
242 | + nodes = dom.getElementsByTagName("hudson.model.StringParameterDefinition") |
243 | + assert len(nodes) == 1 |
244 | + n = nodes[0].getElementsByTagName("defaultValue")[0] |
245 | + build_config = open(args[2]).read() |
246 | + n.childNodes[0].data = base64.encodestring(build_config) |
247 | + |
248 | + if options.private: |
249 | + n = dom.getElementsByTagName("properties")[0] |
250 | + c = n.getElementsByTagName("hudson.security.AuthorizationMatrixProperty")[0] |
251 | + acl_dom = minidom.parseString(PRIVATE_ACL % {"group": options.private}) |
252 | + n.replaceChild(acl_dom.documentElement, c) |
253 | + |
254 | + job_conf = dom.toxml() |
255 | + jenkins_job = args[1].replace("/", "_") |
256 | + try: |
257 | + j.create_job(jenkins_job, job_conf) |
258 | + except urllib2.HTTPError: |
259 | + error("Error creating job '%s' (job exists?)" % jenkins_job) |
260 | + print "Job created successfully: %s/job/%s/" % (options.url, jenkins_job) |
261 | + elif args[0] == "build": |
262 | + if len(args) != 2: |
263 | + optparser.error("Usage: build <job_name>") |
264 | + j.build_job(args[1].replace("/", "_")) |
265 | + print "Build queued" |
266 | + else: |
267 | + optparser.error("Unknown command '%s'" % args[0]) |
268 | + |
269 | + |
270 | +if __name__ == "__main__": |
271 | + main() |
Hey Paul,
following my comments, there's some typos that need fixing and a couple of suggestion/ thoughts.
Marking as fix needed, but once you cleaned the typos it should be ready to go.
+Basic android- build.linaro. org command-line client ======= ======= ======= ======= ======= ======= == build.linaro. org), as an alternative for
+======
+
+This tool provides proof-of-concept command-line client for Linaro Android
+Build Jenkins master (android-
+existing Web UI interface. One particular feature the command-line client
+has and Web UI lacks is support for creating completely private builds (such
+builds are not availabel via Web UI at all). At the same time, this tool is
+so far in the proof of concept stage and provides only basic job management
+actions (namely, create a job and schedule its build). If you find this tools
s/this tools/this tool
+useful, please share you comments and suggestion using this bugtracker:
s/you comments/your comments
+ /bugs.launchpad .net/linaro- android- infrastructure android- build.linaro. org/jenkins/ build-client tool. As it is written to depend only
+https:/
+
+to help its improvement and evolution.
+
+
+Quick start
+-----------
+
+1. You should have access to Jenkins at:
+
+http://
+
+(Generally available for Linaro Android team members).
+
+2. Download android-
s/to depend/it depends
+on Python standard library, an easy way to get it is to download seperate build-client /android- build.linaro. org/jenkins/ me/configure and clicking build-client authorize
+file via Launchpad BZR viewer:
+
+wget <fill in link once branch lands>
+chmod +x android-
+
+3. Look up your Jenkins API token by visiting
+https:/
+"Show API Token..." button.
+
+4. Run:
+
+./android-
+
+Enter you Jenkins username and API token. Note that this will cache these
s/you Jenkins/your Jenkins
+credentials in your home dir, so use this only on your personal well-protected /wiki.linaro. org/Platform/ Android/ LinaroAndroidBu ildService for more build-client create <job_name> <job_config_file>
+workstation. The alternative is to use --user switch and input API key
+interactively.
+
+5. To create a new job, prepare an Android job config (see
+https:/
+info) in a file. Run:
+
+./android-
+
+Note that job name should conform to Android Build Service naming
+converntions, see documentation link above for more info. To create a job
+private for particular group, pass --private=<group> switch to create command.
s/job private/private job
+ build-client build <job_name> build-client --help to see all options.
+6. To schedule a build, run:
+
+./android-
+
+
+Run ./android-
I tried it, but it does not show me all the available "commands", just the normale options.
It's early in the development stage, but it might be worth to take a look at how lava-tool is handling the commands definition.
There's a lot more code to write to achieve that, but it is a well structured pattern. Just food for thoughts, and something to keep in mind.
=== added file 'utils/ cmdline- client/ android- build-client' client/ android- build-client 1970-01-01 00:00:00 +0000 client/ android- build-client 2013-08-29 17:39:17 +0000
--- utils/cmdline-
+++ utils/cmdline-
@@ -0,0 +1,199 @@
+#!/usr/bin/env py...