Skip to content

Commit b680ccc

Browse files
committed
Merge pull request apache#9 from mesosphere/btm-demo-branch
Fix python env, marathon definition, app labels
2 parents 8010c11 + 0a1b8ba commit b680ccc

File tree

5 files changed

+156
-99
lines changed

5 files changed

+156
-99
lines changed

README.md

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
# cd-demo
22
A continuous delivery demo using Jenkins on DCOS.
33

4-
This demo is a Python script that performs the following sequence of actions when run with the `install` command:
4+
This demo is a Python script that, when run with the `install` command, will:
55

66
1. Installs Jenkins if it isn't already available.
7+
8+
Running the `pipeline` command will:
9+
710
2. Sets up a series of build jobs, the necessary credentials and a [Build Pipeline](https://wiki.jenkins-ci.org/display/JENKINS/Build+Pipeline+Plugin) view to demonstrate a basic continuous delivery pipeline. Jenkins will:
811
+ Spin up a new Jenkins slave using the Mesos plugin. This slave runs inside a Docker container on one of our DCOS agents.
912
+ Clone the git repository
1013
+ Build a Docker container based off the [Jekyll Docker image](https://hub.docker.com/r/jekyll/jekyll/) that includes the content stored in [/site](/site) and push it to DockerHub.
1114
+ Run the newly created container and a [Linkchecker container](https://github.com/mesosphere/docker-containers/blob/master/utils/linkchecker/Dockerfile) that runs a basic integration test against the container, checking that the webserver comes up correctly and that all links being served are valid (i.e. no 404s).
1215
+ Manually trigger a Marathon deployment of the newly created container to the DCOS base Marathon instance. If the application already exists, Marathon will simply upgrade it.
13-
+ Make the application available on a public slave at port 80.
16+
+ Make the application available on a public slave at port 80 using Marathon-lb.
17+
18+
When run with the `dynamic-slaves` command, it will:
19+
1420
3. Creates 50 build jobs that take a random amount of time between 1 and 2 minutes. These jobs will randomly fail.
1521
+ The Mesos plugin will spin up build slaves on demand for these jobs, using as much capacity as your cluster has available.
1622
+ When these jobs are finished, the Jenkins tasks will terminate and the resources will be relinquished back to other users of your cluster.
@@ -31,38 +37,64 @@ When run with the `uninstall` command, it will:
3137
```
3238
git clone https://github.com/mesosphere/cd-demo.git
3339
```
40+
2. Create a branch, this is mandatory:
41+
42+
```
43+
git checkout -b my-demo-branch
44+
git push origin my-demo-branch
45+
```
3446
2. [Set up the DCOS CLI](https://docs.mesosphere.com/administration/introcli/cli/) locally.
35-
3. Ensure you have a DCOS cluster available. 1 node will work but more than 1 node is preferable to demonstrate build parallelism. If you already had the CLI installed, make sure you set the new cluster URL and authenticate against it:
47+
48+
3. Ensure you have a DCOS cluster available. 1 node will work but more than 1 node is preferable to demonstrate build parallelism. If you already had the CLI installed, make you sure you set the new cluster URL and authenticate against it (else the script will complain):
3649
3750
```
3851
dcos config set core.dcos_url http://my.dcos.cluster/
3952
dcos auth login
4053
```
54+
4. Export the password to an environment variable (ideally put it in your ~/.bashrc). You will need to replace the password here with the password for the `cddemo` user with permission to push to `mesosphere/cd-demo-app` (or your own repo, if you override the `--org` and `--username` flags later):
55+
56+
```
57+
export PASSWORD=mypass123
58+
```
4159
4260
### Running Demo
4361
44-
1. Run the demo script. You will need to replace the password here with the password for the `cddemo` user with permission to push to `mesosphere/cd-demo-app`:
62+
1. Run the install command. This is mainly a wrapper for the `dcos package install` command but will also check to see if you're authenticated.
63+
64+
```
65+
python bin/demo.py install http://my.dcos.cluster/
66+
```
67+
68+
NOTE: You must use the domain name for your cluster; the IP address will fail.
69+
70+
2. Check that the Jenkins UI is running before proceeding. You can now run either the pipeline demo or the dynamic slaves demo. To run the pipeline demo, grab the ELB address (`Public Slave`), and make sure to specify the branch to run against:
4571
4672
```
47-
bin/demo.py install --branch=my-demo-branch --password=mypass123 http://my.dcos.cluster/
73+
python bin/demo.py pipeline --branch=my-demo-branch --password=$PASSWORD http://my.elb/ http://my.dcos.cluster/
4874
```
49-
NOTE: Depending on your environment you may need to prepend the above command with `python` Also you must use the domain name for your cluster; the IP address will fail.
50-
51-
2. The script will install Jenkins and pause. Check that the Jenkins UI is running before hitting enter to proceed.
52-
3. The script will now use the Jenkins HTTP API to install jobs, necessary credentials and a view. It will automatically trigger the initial build before pausing.
53-
4. Navigate to the Jenkins UI to see the builds in progress. After a few seconds, you should see a build executor spinning up on Mesos. If you navigate to the configured view, you'll see the pipeline in progress.
54-
5. Once the tests have completed successfully, you will need to manually deploy the build using the button in the bottom right of the "deploy" box on the view.
55-
![deploy](/img/manual-deploy.png)
56-
6. The deploy will happen almost instantaneously. After a few seconds, you should be able to load the application by navigating to your public slave's IP address in your browser.
75+
76+
3. The script will first install Marathon-lb if it looks like it isn't available. It will also update the `marathon.json` in the branch you specified to include the ELB hostname so that Marathon-lb can route to it.
77+
4. The script will then use the Jenkins HTTP API to install jobs, necessary credentials and a view. It will automatically trigger the initial build before finishing.
78+
5. Navigate to the Jenkins UI to see the builds in progress. After a few seconds, you should see a build executor spinning up on Mesos. If you navigate to the configured view, you'll see the pipeline in progress.
79+
6. The deploy will happen almost instantaneously. After a few seconds, you should be able to load the application by navigating to the ELB hostname you provided earlier in your browser.
5780
![deployed-app](/img/deployed-jekyll-app.png)
58-
7. Hit Enter to proceed to the next step of the demo. It will create 50 jobs that will randomly fail.
81+
7. Now let's run the dynamic slaves demo. It will create 50 jobs that will randomly fail.
82+
83+
```
84+
python bin/demo.py dynamic-slaves http://my.dcos.cluster/
85+
```
86+
5987
8. Navigate back to the Jenkins and/or DCOS UI to show build slaves spinning up manually.
60-
9. Hit enter to complete the demo.
6188
6289
### Uninstalling
6390
6491
1. Simply run the uninstall command to remove any persisted configuration and to uninstall the DCOS service itself. This will allow you to run multiple demos on the same cluster but you should recycle clusters if the version of the Jenkins package has changed (to ensure plugins are upgraded):
65-
bin/demo.py uninstall http://my.dcos.cluster/
92+
93+
```
94+
python bin/demo.py uninstall http://my.dcos.cluster/
95+
```
96+
97+
Alternatively, run the cleanup command instead to just remove jobs and to avoid having to re-install Jenkins.
6698
6799
## Advanced Usage
68100
@@ -72,27 +104,20 @@ By default, this script assumes you will be pushing to the [mesosphere/cd-demo-a
72104
73105
1. Create a public repo called `cd-demo-app` under your example organisation.
74106
2. Create a Docker Hub user that has credentials to push to this repository.
75-
3. Run the demo script, passing in the credentials:
107+
3. Run the pipeline demo, passing in the credentials:
76108
77109
```
78-
bin/demo.py install --branch=my-demo-branch --org=myorg --username=myuser --password=mypass123 http://my.dcos.cluster/
110+
python bin/demo.py pipeline --branch=my-demo-branch --org=myorg --username=myuser --password=$PASSWORD http://my.elb/ http://my.dcos.cluster/
79111
```
80112
81113
### Build on Commit
82114
83-
If you'd like to demonstrate the build running automatically with every commit:
84-
85-
1. By default this build operates off the `demo` branch. However, it's recommended that you create your own branches for demo purposes to avoid collisions. Create a new branch in this repository and push it up to origin:
115+
1. Run the demo to completion with the `--branch` parameter to monitor your branch. The pipeline will continue to monitor your branch after the script finishes:
86116
87117
```
88-
git checkout -b my-demo-branch
89-
git push origin my-demo-branch
118+
python bin/demo.py pipeline --branch=my-demo-branch --password=$PASSWORD http://my.elb/ http://my.dcos.cluster/
90119
```
91-
2. Run the demo to completion with the `--branch` parameter to monitor your branch. The pipeline will continue to monitor your branch after the script finishes:
92120
93-
```
94-
bin/demo.py install --branch=my-demo-branch --password=mypass123 http://my.dcos.cluster/
95-
```
96121
3. Create a new blog post with today's date, open it up in your text editor and make whatever changes you'd like to:
97122
98123
```
@@ -115,12 +140,12 @@ To demonstrate how you can install multiple Jenkins instances side by side on DC
115140
1. Create one instance:
116141
117142
```
118-
bin/demo.py install --name=jenkins-1 --password=mypass123 http://my.dcos.cluster/
143+
bin/demo.py install --name=jenkins-1 http://my.dcos.cluster/
119144
```
120145
2. Open a new terminal tab and create a second instance and so on:
121146
122147
```
123-
bin/demo.py install --name=jenkins-2 --password=mypass123 http://my.dcos.cluster/
148+
bin/demo.py install --name=jenkins-2 http://my.dcos.cluster/
124149
```
125150
3. You can uninstall these in the same way:
126151
@@ -129,10 +154,6 @@ To demonstrate how you can install multiple Jenkins instances side by side on DC
129154
bin/demo.py uninstall --name=jenkins-2 http://my.dcos.cluster/
130155
```
131156
132-
### Skipping Demos
133-
134-
Only want to run one of the demos? Simply specify `--no-pipeline` to skip the continuous delivery demo, or `--no-dynamic-slaves` to skip the dynamic slaves demo.
135-
136157
## TODO
137158
138159
+ This script is currently untested on Windows.

bin/demo.py

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,45 @@
1-
#!/usr/bin/python
1+
#!/usr/bin/env python
22
"""demo.py
33
44
Usage:
5-
demo.py install [--name=<name>] (--no-pipeline | [--branch=<branch>] [--org=<org>] [--username=<user>] --password=<pass>) [--no-dynamic-slaves] [--builds=<n>] <dcos_url>
5+
demo.py install [--name=<name>] <dcos_url>
6+
demo.py pipeline [--org=<org>] [--username=<user>] --branch=<branch> --password=<pass> <elb_url> <dcos_url>
7+
demo.py dynamic-slaves [--builds=<n>] <dcos_url>
68
demo.py cleanup [--name=<name>] [--builds=<n>] <dcos_url>
79
demo.py uninstall [--name=<name>] [--builds=<n>] <dcos_url>
810
911
Options:
1012
--name=<name> Jenkins instance name to use [default: jenkins].
11-
--no-pipeline Don't run continuous delivery demo.
12-
--branch=<branch> Git branch for continuous delivery demo [default: demo].
13+
--branch=<branch> Git branch for continuous delivery demo.
1314
--org=<org> Docker Hub organisation where repo lives [default: mesosphere].
1415
--username=<user> Docker Hub username to push image with [default: cddemo].
1516
--password=<pass> Docker Hub password to push image with.
16-
--no-dynamic-slaves Don't run dynamic slaves demo.
1717
--builds=<n> Number of builds to create [default: 50].
1818
1919
This script is used to demonstrate various features of Jenkins on the DCOS.
2020
2121
Pre-requisites:
2222
+ A DCOS CLI is configured and available on the PATH of the host system
23-
+ A running DCOS cluster greater than version 1.4. It currently does not
24-
work with clusters that have authentication enabled.
23+
+ A running DCOS cluster greater than version 1.7.0.
2524
+ Python dependencies are installed (pip install -r requirements.txt)
2625
2726
The continuous delivery demo will create a build pipeline that will deploy a Docker
2827
container to the DCOS Marathon.
2928
3029
The dynamic slaves demo will create 50 (by default) "freestyle" Jenkins jobs.
3130
Each of these jobs will appear as a separate Jenkins build, and will randomly
32-
pass or fail. The duration of each job will be between 120 and 240 seconds.
31+
password or fail. The duration of each job will be between 120 and 240 seconds.
3332
"""
3433

3534
import json
3635
import os
3736
import random
37+
import requests
3838
import shutil
39-
from subprocess import call, CalledProcessError, check_output
4039

41-
import requests
4240
from docopt import docopt
41+
from subprocess import call, CalledProcessError, check_output
42+
from urlparse import urlparse
4343

4444
def log(message):
4545
print "[demo] {}".format(message)
@@ -85,24 +85,58 @@ def rename(path, jenkins_name):
8585
json.dump(config, f, indent=4)
8686

8787
def install(dcos_url, jenkins_name, jenkins_url):
88-
log ("Installing Jenkins with name {}".format(jenkins_name))
88+
log ("Installing Jenkins with name {}.".format(jenkins_name))
8989
shutil.copyfile("conf/jenkins.json", "tmp/jenkins.json")
9090
rename("tmp/jenkins.json", jenkins_name)
9191
command = "dcos package install --yes --options=tmp/jenkins.json jenkins"
9292
print ("\n> " + command)
9393
if call (['dcos', 'package', 'install', '--yes', '--options=tmp/jenkins.json', 'jenkins']) != 0:
9494
log_and_exit ("Failed to install Jenkins.")
95-
print("\n[demo] Jenkins has been installed! Wait for it to come up before proceeding at: {}".format(jenkins_url))
96-
raw_input("[demo] Press [Enter] to continue, or ^C to cancel...")
95+
log("Jenkins has been installed! Wait for it to come up before proceeding at: {}".format(jenkins_url))
9796

9897
def verify(jenkins_url):
9998
r = requests.get(jenkins_url, headers=auth_func({}))
10099
if r.status_code != 200:
101-
log ("Couldn't find a Jenkins instance running at {}".format(jenkins_url))
100+
log ("Couldn't find a Jenkins instance running at {}.".format(jenkins_url))
102101
return False
103102
log ("Jenkins is up and running! Got Jenkins version {}".format(r.headers['x-jenkins']))
104103
return True
105104

105+
def install_marathon_lb(elb_url):
106+
log("Checking to see if Marathon-lb is installed.")
107+
r = requests.get(elb_url)
108+
if r.status_code == 503:
109+
log ("Couldn't find a Marathon-lb instance running at {}.".format(elb_url))
110+
log ("Installing Marathon-lb.")
111+
command = "dcos package install --yes marathon-lb"
112+
print ("\n> " + command)
113+
if call (['dcos', 'package', 'install', '--yes', 'marathon-lb']) != 0:
114+
log ("Failed to install Marathon-lb.")
115+
else:
116+
log("Marathon-lb has been installed!")
117+
else:
118+
log ("Marathon-lb already seems to be running at {}. Not installing Marathon-lb.".format(elb_url))
119+
120+
def strip_to_hostname(url):
121+
parsed_url = urlparse(url)
122+
return parsed_url.netloc
123+
124+
def update_marathon_json(elb_url):
125+
with open('marathon.json', 'r+') as f:
126+
marathon = json.load(f)
127+
marathon['labels']['HAPROXY_0_VHOST'] = elb_url
128+
f.seek(0)
129+
json.dump(marathon, f, indent=4, sort_keys=True, separators=(',', ': '))
130+
131+
def update_and_push_marathon_json(elb_url, branch):
132+
elb_hostname = strip_to_hostname(elb_url)
133+
update_marathon_json(elb_hostname)
134+
if call (['git', 'commit', '-a', '-m', 'Update marathon.json with ELB URL']) != 0:
135+
log_and_exit ("Failed to commit updated marathon.json.")
136+
if call (['git', 'push', 'origin', branch]) != 0:
137+
log_and_exit ("Failed to push updated marathon.json.")
138+
log ("Updated marathon.json with ELB hostname {}.".format(elb_hostname))
139+
106140
def create_credentials(jenkins_url, id, username, password):
107141
credential = { 'credentials' : { 'scope' : 'GLOBAL', 'id' : id, 'username' : username, 'password' : password, 'description' : id, '$class' : 'com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl'} }
108142
data = {'json' : json.dumps(credential) }
@@ -116,8 +150,9 @@ def create_job(jenkins_url, job_name, job_config):
116150
post_url = "{}/createItem?name={}".format(jenkins_url, job_name)
117151
r = requests.post(post_url, headers=headers, data=job_config)
118152
if r.status_code != 200:
119-
log_and_exit ("Failed to create job {} at {}".format(job_name, jenkins_url))
120-
log ("Job {} created successfully.".format(job_name))
153+
log ("Failed to create job {} at {}.".format(job_name, jenkins_url))
154+
r.raise_for_status()
155+
log ("Job {} created successfully".format(job_name))
121156

122157
def create_view(jenkins_url, view_name, view_config):
123158
log ("Creating view")
@@ -151,7 +186,7 @@ def delete_view(jenkins_url, view_name):
151186
def remove_temp_dir():
152187
shutil.rmtree("tmp", ignore_errors=True)
153188

154-
def demo_pipeline(jenkins_url, dcos_url, name, branch, org, username, password):
189+
def demo_pipeline(jenkins_url, dcos_url, elb_url, name, branch, org, username, password):
155190
log ("Creating demo pipeline.")
156191
create_credentials(jenkins_url, 'docker-hub-credentials', username, password)
157192
with open("jobs/build-cd-demo/config.xml") as build_job:
@@ -171,7 +206,7 @@ def demo_pipeline(jenkins_url, dcos_url, name, branch, org, username, password):
171206
create_view(jenkins_url, "cd-demo-pipeline", view_config)
172207
trigger_build(jenkins_url, "build-cd-demo")
173208
log ("Created demo pipeline.")
174-
raw_input("[demo] Press [Enter] to continue, or ^C to cancel...")
209+
log("Once deployed, your application should be available at {}.".format(elb_url))
175210

176211
def demo_dynamic_slaves(jenkins_url, builds):
177212
log ("Creating {} freestyle Jenkins jobs.".format(builds))
@@ -187,7 +222,6 @@ def demo_dynamic_slaves(jenkins_url, builds):
187222
trigger_build(jenkins_url, job_name, parameter_string)
188223
log ("Job {} created successfully. Duration: {}. Result: {}. Triggering build.".format(job_name, duration, result))
189224
log ("Created {} freestyle Jenkins jobs.".format(builds))
190-
raw_input("[demo] Press [Enter] to continue, or ^C to cancel...")
191225

192226
def cleanup_pipeline_jobs (jenkins_url):
193227
log ("Cleaning up demo pipeline.")
@@ -223,26 +257,30 @@ def uninstall(dcos_url, jenkins_name):
223257
jenkins_name = arguments['--name'].lower()
224258
builds = int(arguments['--builds'])
225259
dcos_url = arguments['<dcos_url>']
260+
elb_url = arguments['<elb_url>'] #TODO: FIX ME
226261
jenkins_url = '{}service/{}/'.format(dcos_url, jenkins_name)
227262

228-
check_and_set_token(jenkins_url)
229263
config_dcos_cli(dcos_url)
264+
check_and_set_token(jenkins_url)
265+
230266
try:
231267
if arguments['install']:
232268
make_temp_dir()
233269
if not verify(jenkins_url):
234270
install(dcos_url, jenkins_name, jenkins_url)
235-
if not arguments['--no-pipeline']:
236-
branch = arguments['--branch'].lower()
237-
if branch == 'master':
238-
log_and_exit ("Cannot run demo against the master branch.")
239-
org = arguments['--org']
240-
username = arguments['--username']
241-
password = arguments['--password']
242-
demo_pipeline(jenkins_url, dcos_url, jenkins_name, branch, org, username, password)
243-
if not arguments['--no-dynamic-slaves']:
244-
demo_dynamic_slaves(jenkins_url, builds)
245271
remove_temp_dir()
272+
elif arguments['pipeline']:
273+
branch = arguments['--branch'].lower()
274+
if branch == 'master':
275+
log_and_exit ("Cannot run demo against the master branch.")
276+
org = arguments['--org']
277+
username = arguments['--username']
278+
password = arguments['--password']
279+
install_marathon_lb(elb_url)
280+
update_and_push_marathon_json(elb_url, branch)
281+
demo_pipeline(jenkins_url, dcos_url, elb_url, jenkins_name, branch, org, username, password)
282+
elif arguments['dynamic-slaves']:
283+
demo_dynamic_slaves(jenkins_url, builds)
246284
elif arguments['cleanup']:
247285
cleanup(jenkins_url, builds)
248286
elif arguments['uninstall']:

img/manual-deploy.png

-26.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)