orch 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +127 -1
- data/examples/Examples.md +4 -2
- data/examples/bamboo-ex.yml +47 -0
- data/examples/env-chronos.yml +7 -14
- data/examples/env-marathon.yml +44 -0
- data/examples/simple-chronos.yml +19 -3
- data/examples/simple-marathon.yml +2 -8
- data/lib/bamboo.rb +112 -0
- data/lib/{deploy.rb → chronos.rb} +58 -64
- data/lib/config.rb +30 -3
- data/lib/marathon.rb +150 -0
- data/lib/orch/version.rb +1 -1
- data/lib/orch.rb +108 -14
- data/lib/parse.rb +85 -39
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4fc678adcfcacd47d4c3424fffae37cfba36f7f1
|
|
4
|
+
data.tar.gz: deba3fb34b70a3b486c400b71ff395b869fa0a9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf766eea2fd37950ac0fa9fcbe2528e4b51748570f0ae104cc73796bc667403d994e64e310c964fd882bfb2adca49aaaaefe4e412e39df7bf2c7ddcf08ef6b95
|
|
7
|
+
data.tar.gz: 5f1e9ce4837fb1a4ca65cefc4fc12005ffb0d6f94d98a3f58c45b059b972725120310374df0cc02cc7bcce63f447e7e1cfe06e5938bc52257c6d55aa8bccca08
|
data/README.md
CHANGED
|
@@ -20,7 +20,133 @@ The general usecase is to run "orch deploy _job_spec_" to deploy a configuration
|
|
|
20
20
|
|
|
21
21
|
## Job spec format
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
The orch Yaml format is really a wrapper for multiple Marathon or Chronos job descriptions. The format is based on yaml. To get a basic understanding of Yaml files check out: http://www.yaml.org/start.html
|
|
24
|
+
|
|
25
|
+
The basic orch syntax is as follows:
|
|
26
|
+
```
|
|
27
|
+
version: alpha1
|
|
28
|
+
applications:
|
|
29
|
+
- kind: Chronos
|
|
30
|
+
chronos_spec:
|
|
31
|
+
...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The **version:** field must currently always be alpha1. Eventually, this tool may need to support a revised format and this can be used to support multiple versions.
|
|
35
|
+
|
|
36
|
+
The **applications:** field contains an array of Chronos or Marathon configurations. Each array must have a **kind:** field with a value of _Chronos_ or _Marathon_ which simply tells orch what type of config this is. Depending on the value of **kind:** a field of **chronos_spec:** or **marathon_spec:** is also required.
|
|
37
|
+
|
|
38
|
+
The values of **chronos_spec:** should simply be the yaml equivelent of the chronos json messages. You can find documentation for chronos json format here: https://mesos.github.io/chronos/docs/api.html
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Likewise, the values of **marathon_spec:** should simply be the yaml equivelent of the marathon json messages. You can find documentation for marathon json format here: https://mesosphere.github.io/marathon/docs/rest-api.html
|
|
42
|
+
|
|
43
|
+
So far all we have is a different way to specify a job. The real power of orch will come from using meta data to act on these configs in more interesting ways...
|
|
44
|
+
|
|
45
|
+
### Deployment vars
|
|
46
|
+
|
|
47
|
+
The **deploy_vars:** field allows you to define some values that can be used to drive deployment of the same config for multiple uses. Often it is useful to have *dev*, *test* & *prod* environments for our application. Generally we want to use the same specification for all environments but we need a way to differentiate them and allow the app at run time to know what environment it is running in.
|
|
48
|
+
|
|
49
|
+
Let's start with an example:
|
|
50
|
+
```
|
|
51
|
+
deploy_vars:
|
|
52
|
+
DEPLOY_ENV:
|
|
53
|
+
- dev
|
|
54
|
+
- test
|
|
55
|
+
- prod
|
|
56
|
+
```
|
|
57
|
+
This example will define an environment variable named **DEPLOY_ENV**. (You can name it whatever you want.) We are defining it here to have values of *dev*, *test*, and *prod*.
|
|
58
|
+
|
|
59
|
+
You then would need to specify that variable in your application sections like this:
|
|
60
|
+
```
|
|
61
|
+
version: alpha1
|
|
62
|
+
deploy_vars:
|
|
63
|
+
DEPLOY_ENV:
|
|
64
|
+
- dev
|
|
65
|
+
- test
|
|
66
|
+
- prod
|
|
67
|
+
applications:
|
|
68
|
+
- kind: Chronos
|
|
69
|
+
DEPLOY_ENV: dev
|
|
70
|
+
chronos_spec:
|
|
71
|
+
name: "myapp-{{DEPLOY_ENV}}"
|
|
72
|
+
...
|
|
73
|
+
- kind: Chronos
|
|
74
|
+
DEPLOY_ENV: prod
|
|
75
|
+
chronos_spec:
|
|
76
|
+
name: "myapp-{{DEPLOY_ENV}}"
|
|
77
|
+
...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
When orch runs it will insert the environment variable into the *env* or *environmentVariables* sections of your spec. However, you can also use the **--deploy-env** command line option to tell orch to only deploy a paticular environment. You can also use the substitution feature to use the value of your environment varaible in other parts of the config like name. *(e.g. `name: "myapp-{{DEPLOY_ENV}}"` would substitute to `name: "myapp-dev"` for the dev environment)*
|
|
81
|
+
|
|
82
|
+
### Alternative way to set environment variables
|
|
83
|
+
|
|
84
|
+
Chronos and Marathon use different syntx to specify environment variables. Also, you may want to specify certain variables consistently across several applications in your orch spec. The **env:** field provides a way to do that with a nice clean syntax.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
The env field could be used at the top level of the spec to be used across all applications in the spec:
|
|
88
|
+
```
|
|
89
|
+
version: alpha1
|
|
90
|
+
env:
|
|
91
|
+
GOOGLE_URL: http://www.google.com
|
|
92
|
+
YP_URL: http://www.yp.com
|
|
93
|
+
applications:
|
|
94
|
+
...
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or you can also specify it at the application level as an alternative syntax to specifying it in the *marathon_spec:* or *chronos_spec:* sections - like this:
|
|
98
|
+
```
|
|
99
|
+
version: alpha1
|
|
100
|
+
env:
|
|
101
|
+
GOOGLE_URL: http://www.google.com
|
|
102
|
+
YP_URL: http://www.yp.com
|
|
103
|
+
applications:
|
|
104
|
+
- kind: Marathon
|
|
105
|
+
env:
|
|
106
|
+
GOOGLE_URL: http://www.google.com
|
|
107
|
+
YP_URL: http://www.yp.com
|
|
108
|
+
marathon_spec:
|
|
109
|
+
...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Lower level definitions of an env var will supeceed higher level version. That is if you specify an env value in the *marathon_spec* or *chronos_spec* level it will override anything set at the *app* or *orch* level.
|
|
113
|
+
### Yaml anchors and aliases
|
|
114
|
+
|
|
115
|
+
The nice thing about using Yaml as the spec format is the ability to use Yaml's anchors (&) and alias (*) syntax. This allows you to specify sections of your configuration that can be reused across multiple jobs. Typically if you have a job for multiple deployment environments you what them all to have the same spec - except perhaps override one or two things. (Like an envionment value or the docker image.)
|
|
116
|
+
|
|
117
|
+
We will refer you to other documentation on the internet on how to use Yaml anchors and aliases. However, you can take a look at a couple of the [provided examples](examples/Examples.md) to see how you might use Yaml to its fullest.
|
|
118
|
+
|
|
119
|
+
### Substition
|
|
120
|
+
|
|
121
|
+
A primary use case of **orch** is to use the tool from within a Makefile. You may want to subsitite parts of you configuration from vairables in your Makefile. In the *Deployment vars* section we showed you how you can use deployment vars as substitution values in other parts of the configuration.
|
|
122
|
+
|
|
123
|
+
**Orch** also provides the *--subst* option to provide a way to pass additional substitution vars when running your configuration. A common use case might be to pass in the tag for a Docker image that is managed by your Makefile. Here is an example:
|
|
124
|
+
```
|
|
125
|
+
todo: provide an example
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
TODO: finish this section
|
|
129
|
+
|
|
130
|
+
### Bamboo
|
|
131
|
+
|
|
132
|
+
[Bamboo](https://github.com/QubitProducts/bamboo) is a tool that creates an HAProxy for providing an easier way to get to a farm of web servers hosted by Marathon. You define the Marathon id of the application you want to load balance and an acl to tell HAProxy what url to direct to your marathon application.
|
|
133
|
+
|
|
134
|
+
Orch provides a way to also configure your Bamboo server from your orch config. Here is an example:
|
|
135
|
+
```
|
|
136
|
+
version: alpha1
|
|
137
|
+
applications:
|
|
138
|
+
- kind: Marathon
|
|
139
|
+
marathon_spec:
|
|
140
|
+
...
|
|
141
|
+
bamboo_spec:
|
|
142
|
+
acl: "hdr(host) -i test-web.int.yp.com"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
An additional *bamboo_spec:* field is added to the Marathon application at the same level as the *marathon_spec:* field. The *bamboo_spec:* field contains one field called *acl:*. The simply has a acl rule for HAProxy. See Bamboo documentation for more details.
|
|
146
|
+
|
|
147
|
+
Note: you will also need to set the **bamboo_url:** feild of your configutation.
|
|
148
|
+
|
|
149
|
+
### TODO: section on vault
|
|
24
150
|
|
|
25
151
|
## Configuration Options
|
|
26
152
|
|
data/examples/Examples.md
CHANGED
|
@@ -5,6 +5,8 @@ This directory contains several examples to show various orch features.
|
|
|
5
5
|
|
|
6
6
|
| File | Description
|
|
7
7
|
|-----------------------|------------------------------------------------------------------------|
|
|
8
|
-
| shimple-chronos.yml | A very simple chronos spec for a daily recurring job
|
|
9
|
-
| env-chronos.yml | Shows how to set up a chronos job for multiple deployment environments.
|
|
8
|
+
| shimple-chronos.yml | A very simple chronos spec for a daily recurring job. It also has a simple example of a dependant job.
|
|
9
|
+
| env-chronos.yml | Shows how to set up a chronos job for multiple deployment environments. It also gives a good example of how to use Yaml anchors, the use of multiple deploy_vars: fields, and how to use deploy_vars fields in substitutions.
|
|
10
10
|
| simple-marathon.yml | A simple example of a Marathon job using orch
|
|
11
|
+
| env-marathon.yml | A more complex marathon example showing multiple deployment environments.
|
|
12
|
+
| bamboo-ex.yml | Example of using Marathon with Bamboo.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
default_job: &DEFAULT_JOB
|
|
2
|
+
kind: Marathon
|
|
3
|
+
DEPLOY_ENV: dev
|
|
4
|
+
bamboo_spec:
|
|
5
|
+
acl: "hdr(host) -i test-web-{{DEPLOY_ENV}}.ypec.int.yp.com"
|
|
6
|
+
marathon_spec: &DEFAULT_SPEC
|
|
7
|
+
id: "test-bamboo-{{DEPLOY_ENV}}"
|
|
8
|
+
cpus: 0.1
|
|
9
|
+
mem: 300
|
|
10
|
+
instances: 3
|
|
11
|
+
container: &DEFAULT_CONTAINER
|
|
12
|
+
type: "DOCKER"
|
|
13
|
+
docker: &DEFAULT_DOCKER
|
|
14
|
+
image: "nginx"
|
|
15
|
+
network: "BRIDGE"
|
|
16
|
+
portMappings:
|
|
17
|
+
-
|
|
18
|
+
containerPort: 80
|
|
19
|
+
hostPort: 0
|
|
20
|
+
protocol: "tcp"
|
|
21
|
+
forcePullImage: false
|
|
22
|
+
|
|
23
|
+
version: alpha1
|
|
24
|
+
deploy_vars:
|
|
25
|
+
DEPLOY_ENV:
|
|
26
|
+
- dev
|
|
27
|
+
- test
|
|
28
|
+
- prod
|
|
29
|
+
env:
|
|
30
|
+
DEBUG_FLAG: true
|
|
31
|
+
applications:
|
|
32
|
+
- <<: *DEFAULT_JOB
|
|
33
|
+
DEPLOY_ENV: dev
|
|
34
|
+
marathon_spec:
|
|
35
|
+
<<: *DEFAULT_SPEC
|
|
36
|
+
container:
|
|
37
|
+
<<: *DEFAULT_CONTAINER
|
|
38
|
+
docker:
|
|
39
|
+
<<: *DEFAULT_DOCKER
|
|
40
|
+
forcePullImage: true
|
|
41
|
+
- <<: *DEFAULT_JOB
|
|
42
|
+
DEPLOY_ENV: test
|
|
43
|
+
- <<: *DEFAULT_JOB
|
|
44
|
+
DEPLOY_ENV: prod
|
|
45
|
+
env:
|
|
46
|
+
DEBUG_FLAG: false
|
|
47
|
+
|
data/examples/env-chronos.yml
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
default_job: &DEFAULT_JOB
|
|
3
3
|
kind: Chronos
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
name: "ray-test-{{DEPLOY_ENV}}"
|
|
4
|
+
chronos_spec: &DEFAULT_SPEC
|
|
5
|
+
name: "ray-test-{{DEPLOY_ENV}}-{{COLO}}"
|
|
7
6
|
schedule: "R/2015-01-25T21:00/PT24H"
|
|
8
7
|
scheduleTimeZone: "PST"
|
|
9
8
|
owner: "rjohnson@yp.com"
|
|
@@ -18,11 +17,10 @@ default_job: &DEFAULT_JOB
|
|
|
18
17
|
-
|
|
19
18
|
name: "LOG_LOCATION"
|
|
20
19
|
value: "/var/{{DEPLOY_ENV}}"
|
|
21
|
-
forcePullImage: false
|
|
22
20
|
command: "echo \"log here: $LOG_LOCATION\"; echo \"ENV: $DEPLOY_ENV\";sleep 10; echo bye"
|
|
23
21
|
|
|
24
22
|
version: alpha1
|
|
25
|
-
|
|
23
|
+
deploy_vars:
|
|
26
24
|
DEPLOY_ENV:
|
|
27
25
|
- dev
|
|
28
26
|
- test
|
|
@@ -32,31 +30,26 @@ environment_vars:
|
|
|
32
30
|
- east
|
|
33
31
|
applications:
|
|
34
32
|
- <<: *DEFAULT_JOB
|
|
35
|
-
environment: dev
|
|
36
33
|
DEPLOY_ENV: dev
|
|
37
34
|
COLO: east
|
|
38
|
-
|
|
35
|
+
chronos_spec:
|
|
39
36
|
<<: *DEFAULT_SPEC
|
|
40
37
|
schedule: "R/2015-08-01T21:00/PT24H"
|
|
41
|
-
forcePullImage: true
|
|
42
38
|
- <<: *DEFAULT_JOB
|
|
43
|
-
environment: test
|
|
44
39
|
DEPLOY_ENV: test
|
|
45
40
|
COLO: east
|
|
46
|
-
|
|
41
|
+
chronos_spec:
|
|
47
42
|
<<: *DEFAULT_SPEC
|
|
48
43
|
schedule: "R/2015-08-01T21:10/PT24H"
|
|
49
44
|
- <<: *DEFAULT_JOB
|
|
50
|
-
environment: prod
|
|
51
45
|
DEPLOY_ENV: prod
|
|
52
46
|
COLO: east
|
|
53
|
-
|
|
47
|
+
chronos_spec:
|
|
54
48
|
<<: *DEFAULT_SPEC
|
|
55
49
|
schedule: "R/2015-08-01T21:30/PT24H"
|
|
56
50
|
- <<: *DEFAULT_JOB
|
|
57
|
-
environment: prod
|
|
58
51
|
DEPLOY_ENV: prod
|
|
59
52
|
COLO: west
|
|
60
|
-
|
|
53
|
+
chronos_spec:
|
|
61
54
|
<<: *DEFAULT_SPEC
|
|
62
55
|
schedule: "R/2015-08-01T21:30/PT24H"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
default_job: &DEFAULT_JOB
|
|
2
|
+
kind: Marathon
|
|
3
|
+
DEPLOY_ENV: dev
|
|
4
|
+
marathon_spec: &DEFAULT_SPEC
|
|
5
|
+
id: "test-web-{{DEPLOY_ENV}}"
|
|
6
|
+
cpus: 0.1
|
|
7
|
+
mem: 300
|
|
8
|
+
instances: 3
|
|
9
|
+
container: &DEFAULT_CONTAINER
|
|
10
|
+
type: "DOCKER"
|
|
11
|
+
docker: &DEFAULT_DOCKER
|
|
12
|
+
image: "nginx"
|
|
13
|
+
network: "BRIDGE"
|
|
14
|
+
portMappings:
|
|
15
|
+
-
|
|
16
|
+
containerPort: 80
|
|
17
|
+
hostPort: 0
|
|
18
|
+
protocol: "tcp"
|
|
19
|
+
forcePullImage: false
|
|
20
|
+
|
|
21
|
+
version: alpha1
|
|
22
|
+
deploy_vars:
|
|
23
|
+
DEPLOY_ENV:
|
|
24
|
+
- dev
|
|
25
|
+
- test
|
|
26
|
+
- prod
|
|
27
|
+
env:
|
|
28
|
+
DEBUG_FLAG: true
|
|
29
|
+
applications:
|
|
30
|
+
- <<: *DEFAULT_JOB
|
|
31
|
+
DEPLOY_ENV: dev
|
|
32
|
+
marathon_spec:
|
|
33
|
+
<<: *DEFAULT_SPEC
|
|
34
|
+
container:
|
|
35
|
+
<<: *DEFAULT_CONTAINER
|
|
36
|
+
docker:
|
|
37
|
+
<<: *DEFAULT_DOCKER
|
|
38
|
+
forcePullImage: true
|
|
39
|
+
- <<: *DEFAULT_JOB
|
|
40
|
+
DEPLOY_ENV: test
|
|
41
|
+
- <<: *DEFAULT_JOB
|
|
42
|
+
DEPLOY_ENV: prod
|
|
43
|
+
env:
|
|
44
|
+
DEBUG_FLAG: false
|
data/examples/simple-chronos.yml
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
version: alpha1
|
|
2
2
|
applications:
|
|
3
3
|
- kind: Chronos
|
|
4
|
-
|
|
5
|
-
name: "
|
|
6
|
-
schedule: "R/2015-01-25T21:00/
|
|
4
|
+
chronos_spec:
|
|
5
|
+
name: "orch-test-schedule"
|
|
6
|
+
schedule: "R/2015-01-25T21:00/PT03H"
|
|
7
7
|
scheduleTimeZone: "PST"
|
|
8
8
|
owner: "rjohnson@yp.com"
|
|
9
9
|
container:
|
|
@@ -15,3 +15,19 @@ applications:
|
|
|
15
15
|
uris: []
|
|
16
16
|
forcePullImage: true
|
|
17
17
|
command: "echo hello; sleep 10; echo bye"
|
|
18
|
+
- kind: Chronos
|
|
19
|
+
chronos_spec:
|
|
20
|
+
name: "orch-test-dependant-schedule"
|
|
21
|
+
parents:
|
|
22
|
+
- orch-test-schedule
|
|
23
|
+
scheduleTimeZone: "PST"
|
|
24
|
+
owner: "rjohnson@yp.com"
|
|
25
|
+
container:
|
|
26
|
+
type: "DOCKER"
|
|
27
|
+
image: "busybox"
|
|
28
|
+
network: "BRIDGE"
|
|
29
|
+
cpus: "0.5"
|
|
30
|
+
mem: "512"
|
|
31
|
+
uris: []
|
|
32
|
+
forcePullImage: true
|
|
33
|
+
command: "echo run-dependant-job; sleep 10; echo bye"
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
version: alpha1
|
|
2
|
-
environment_vars:
|
|
3
|
-
DEPLOY_ENV:
|
|
4
|
-
- dev
|
|
5
|
-
- test
|
|
6
|
-
- prod
|
|
7
2
|
applications:
|
|
8
3
|
- kind: Marathon
|
|
9
4
|
DEPLOY_ENV: dev
|
|
10
5
|
marathon_spec:
|
|
11
|
-
id: "
|
|
6
|
+
id: "test-web-app"
|
|
12
7
|
cpus: 0.1
|
|
13
8
|
mem: 300
|
|
14
|
-
instances:
|
|
9
|
+
instances: 2
|
|
15
10
|
env:
|
|
16
11
|
FOO: "bar"
|
|
17
12
|
container:
|
|
@@ -24,5 +19,4 @@ applications:
|
|
|
24
19
|
containerPort: 80
|
|
25
20
|
hostPort: 0
|
|
26
21
|
protocol: "tcp"
|
|
27
|
-
forcePullImage: true
|
|
28
22
|
|
data/lib/bamboo.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Chronos interface object
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
|
|
6
|
+
require 'config'
|
|
7
|
+
|
|
8
|
+
class String
|
|
9
|
+
def numeric?
|
|
10
|
+
Float(self) != nil rescue false
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Orch
|
|
15
|
+
class Bamboo
|
|
16
|
+
def initialize(options)
|
|
17
|
+
@config = Orch::Config.new(options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deploy(app_id, bamboo_spec)
|
|
21
|
+
uri = URI(@config.bamboo_url)
|
|
22
|
+
json_headers = {"Content-Type" => "application/json",
|
|
23
|
+
"Accept" => "application/json"}
|
|
24
|
+
|
|
25
|
+
# Create the real json
|
|
26
|
+
bamboo_json = {}
|
|
27
|
+
bamboo_json["id"] = (app_id[0] == '/') ? app_id : ("/" + app_id)
|
|
28
|
+
bamboo_json["acl"] = bamboo_spec["acl"]
|
|
29
|
+
|
|
30
|
+
# curl -i -X PUT -d '{"id":"/ExampleAppGroup/app1", "acl":"path_beg -i /group/app-1"}' http://localhost:8000/api/services//ExampleAppGroup/app1
|
|
31
|
+
# {"/adam-web-dev":{"Id":"/adam-web-dev","Acl":"hdr(host) -i adam-web-dev.ypec.int.yp.com"
|
|
32
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
33
|
+
response = http.put("/api/services/#{app_id}", bamboo_json.to_json, json_headers)
|
|
34
|
+
|
|
35
|
+
if response.code == 200.to_s
|
|
36
|
+
puts "successfully created bamboo spec for marathon job: #{app_id}"
|
|
37
|
+
else
|
|
38
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
return response
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete(app_id)
|
|
45
|
+
uri = URI(@config.chronos_url)
|
|
46
|
+
json_headers = {"Content-Type" => "application/json",
|
|
47
|
+
"Accept" => "application/json"}
|
|
48
|
+
|
|
49
|
+
puts "curl -i -X DELETE #{uri}/api/services/#{app_id}"
|
|
50
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
51
|
+
response = http.delete("/api/services/#{app_id}", json_headers)
|
|
52
|
+
|
|
53
|
+
if response.code != 200.to_s
|
|
54
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# TODO: handle error codes better?
|
|
58
|
+
|
|
59
|
+
return response
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def verify(app_id, spec)
|
|
63
|
+
if @config.check_for_bamboo_url == false
|
|
64
|
+
puts "no bamboo_url - can not verify with server"
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
uri = URI(@config.bamboo_url)
|
|
69
|
+
json_headers = {"Content-Type" => "application/json",
|
|
70
|
+
"Accept" => "application/json"}
|
|
71
|
+
|
|
72
|
+
# TODO: will this work or do I need to parse through all services like chronos
|
|
73
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
74
|
+
response = http.get("/api/services", json_headers)
|
|
75
|
+
|
|
76
|
+
if response.code != 200.to_s
|
|
77
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
78
|
+
foundDiffs = true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
allJobs = Hashie::Mash.new(JSON.parse(response.body))
|
|
82
|
+
|
|
83
|
+
# Bamboo api only returns all items
|
|
84
|
+
jobFound = false
|
|
85
|
+
allJobs.each do |key, job|
|
|
86
|
+
if key == app_id
|
|
87
|
+
jobFound = true
|
|
88
|
+
# Bamboo returns keys with different case then you send them!
|
|
89
|
+
# Right now the only field to compar is Acl so we only check it by hand
|
|
90
|
+
if spec["acl"] != job["Acl"]
|
|
91
|
+
printf "difference for field: acl\n"
|
|
92
|
+
printf " spec: #{spec["acl"]}\n"
|
|
93
|
+
printf " server: #{job["Acl"]}\n"
|
|
94
|
+
foundDiffs = true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if !jobFound
|
|
102
|
+
puts "bamboo spec for marathon job \"#{app_id}\" not currently deployed"
|
|
103
|
+
foundDiffs = true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# TODO: handle error codes better?
|
|
107
|
+
|
|
108
|
+
return foundDiffs
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# Chronos interface object
|
|
1
2
|
require 'json'
|
|
2
3
|
require 'net/http'
|
|
3
4
|
require 'uri'
|
|
@@ -11,20 +12,27 @@ class String
|
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
module Orch
|
|
14
|
-
class
|
|
15
|
+
class Chronos
|
|
15
16
|
def initialize(options)
|
|
16
17
|
# TODO: get chronos and marathon urls from diffferent ways: param, env, .config
|
|
17
18
|
@config = Orch::Config.new(options)
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def
|
|
21
|
+
def deploy(json_payload)
|
|
21
22
|
uri = URI(@config.chronos_url)
|
|
22
23
|
json_headers = {"Content-Type" => "application/json",
|
|
23
24
|
"Accept" => "application/json"}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
path = nil
|
|
27
|
+
path = "/scheduler/iso8601" unless json_payload["schedule"].nil?
|
|
28
|
+
path = "/scheduler/dependency" unless json_payload["parents"].nil?
|
|
29
|
+
if path.nil?
|
|
30
|
+
puts "neither schedule nor parents fields defined for Chronos job"
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
|
|
26
34
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
27
|
-
response = http.post(
|
|
35
|
+
response = http.post(path, json_payload, json_headers)
|
|
28
36
|
|
|
29
37
|
if response.code != 204.to_s
|
|
30
38
|
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
@@ -35,7 +43,25 @@ module Orch
|
|
|
35
43
|
return response
|
|
36
44
|
end
|
|
37
45
|
|
|
38
|
-
def
|
|
46
|
+
def delete(name)
|
|
47
|
+
uri = URI(@config.chronos_url)
|
|
48
|
+
json_headers = {"Content-Type" => "application/json",
|
|
49
|
+
"Accept" => "application/json"}
|
|
50
|
+
|
|
51
|
+
# curl -L -X DELETE chronos-node:8080/scheduler/job/request_event_counter_hourly
|
|
52
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
53
|
+
response = http.delete("/scheduler/job/#{name}", json_headers)
|
|
54
|
+
|
|
55
|
+
if response.code != 204.to_s
|
|
56
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# TODO: handle error codes better?
|
|
60
|
+
|
|
61
|
+
return response
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def verify(json_payload)
|
|
39
65
|
if @config.check_for_chronos_url == false
|
|
40
66
|
puts "no chronos_url - can not verify with server"
|
|
41
67
|
return
|
|
@@ -47,16 +73,17 @@ module Orch
|
|
|
47
73
|
json_headers = {"Content-Type" => "application/json",
|
|
48
74
|
"Accept" => "application/json"}
|
|
49
75
|
|
|
50
|
-
# curl -L -H 'Content-Type: application/json' -X POST -d @$schedule_file $CHRONOS_URL/scheduler/iso8601
|
|
51
76
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
52
|
-
response = http.get("/scheduler/jobs", json_headers)
|
|
77
|
+
response = http.get("/scheduler/jobs/search?name=#{spec.name}", json_headers)
|
|
53
78
|
|
|
54
79
|
if response.code != 200.to_s
|
|
55
80
|
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
81
|
+
foundDiffs = true
|
|
56
82
|
end
|
|
57
83
|
|
|
58
84
|
array = JSON.parse(response.body).map { |hash| Hashie::Mash.new(hash) }
|
|
59
85
|
|
|
86
|
+
# Chronos search API could return more than one item - make sure we find the exact match
|
|
60
87
|
jobFound = false
|
|
61
88
|
array.each do |job|
|
|
62
89
|
if job.name == spec.name
|
|
@@ -67,11 +94,12 @@ module Orch
|
|
|
67
94
|
|
|
68
95
|
if !jobFound
|
|
69
96
|
puts "job \"#{spec.name}\" not currently deployed"
|
|
97
|
+
foundDiffs = true
|
|
70
98
|
end
|
|
71
99
|
|
|
72
100
|
# TODO: handle error codes better?
|
|
73
101
|
|
|
74
|
-
return
|
|
102
|
+
return foundDiffs
|
|
75
103
|
end
|
|
76
104
|
|
|
77
105
|
def find_diffs(spec, job)
|
|
@@ -84,7 +112,16 @@ module Orch
|
|
|
84
112
|
end
|
|
85
113
|
next
|
|
86
114
|
end
|
|
87
|
-
|
|
115
|
+
if spec[key].is_a?(Array)
|
|
116
|
+
if spec[key].length != job[key].length
|
|
117
|
+
printf "difference for field: #{key} - length of array is different\n"
|
|
118
|
+
printf " spec: #{spec[key].to_json}\n"
|
|
119
|
+
printf " server: #{job[key].to_json}\n"
|
|
120
|
+
foundDiff = true
|
|
121
|
+
next
|
|
122
|
+
end
|
|
123
|
+
# TODO: not sure how to compare arrays
|
|
124
|
+
end
|
|
88
125
|
specVal = spec[key]
|
|
89
126
|
jobVal = job[key]
|
|
90
127
|
if spec[key].to_s.numeric?
|
|
@@ -94,66 +131,23 @@ module Orch
|
|
|
94
131
|
specVal = spec[key]
|
|
95
132
|
jobVal = job[key]
|
|
96
133
|
end
|
|
134
|
+
# Chronos changes the case of the Docker argument for some reason
|
|
135
|
+
if key == "type"
|
|
136
|
+
specVal = specVal.upcase
|
|
137
|
+
jobVal = jobVal.upcase
|
|
138
|
+
end
|
|
97
139
|
if specVal != jobVal
|
|
98
|
-
|
|
140
|
+
if foundDiff == false
|
|
141
|
+
puts "Differences found in job"
|
|
142
|
+
end
|
|
143
|
+
printf "difference for field: #{key}\n"
|
|
144
|
+
printf " spec: #{specVal}\n"
|
|
145
|
+
printf " server: #{jobVal}\n"
|
|
99
146
|
foundDiff = true
|
|
100
147
|
end
|
|
101
148
|
end
|
|
102
149
|
|
|
103
150
|
return foundDiff
|
|
104
151
|
end
|
|
105
|
-
|
|
106
|
-
def deploy_marathon(json_payload)
|
|
107
|
-
uri = URI(@config.marathon_url)
|
|
108
|
-
json_headers = {"Content-Type" => "application/json",
|
|
109
|
-
"Accept" => "application/json"}
|
|
110
|
-
puts uri
|
|
111
|
-
|
|
112
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
113
|
-
response = http.put("/v2/apps/#{json_payload["id"]}", json_payload, json_headers)
|
|
114
|
-
|
|
115
|
-
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
116
|
-
|
|
117
|
-
if response.code == 204.to_s
|
|
118
|
-
puts "success"
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# TODO: handle error codes
|
|
122
|
-
|
|
123
|
-
return response
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def verify_marathon(json_payload)
|
|
127
|
-
if @config.check_for_marathon_url == false
|
|
128
|
-
puts "no marathon_url - can not verify with server"
|
|
129
|
-
return
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
spec = Hashie::Mash.new(JSON.parse(json_payload))
|
|
133
|
-
|
|
134
|
-
uri = URI(@config.marathon_url)
|
|
135
|
-
json_headers = {"Content-Type" => "application/json",
|
|
136
|
-
"Accept" => "application/json"}
|
|
137
|
-
|
|
138
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
139
|
-
response = http.get("/v2/apps/#{spec.id}", json_headers)
|
|
140
|
-
|
|
141
|
-
# puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
142
|
-
|
|
143
|
-
if response.code == 404.to_s
|
|
144
|
-
puts "job: #{spec.id} - is not deployed"
|
|
145
|
-
return
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
if response.code == 200.to_s
|
|
149
|
-
job = Hashie::Mash.new(JSON.parse(response.body))
|
|
150
|
-
foundDiffs = find_diffs(spec, job.app)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# TODO: handle error codes
|
|
154
|
-
|
|
155
|
-
return response
|
|
156
|
-
end
|
|
157
|
-
|
|
158
152
|
end
|
|
159
|
-
end
|
|
153
|
+
end
|
data/lib/config.rb
CHANGED
|
@@ -32,6 +32,14 @@ module Orch
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def check_for_bamboo_url
|
|
36
|
+
if (! @options.has_key?("bamboo_url")) && (@APP_CONFIG.nil? || @APP_CONFIG["bamboo_url"].nil?)
|
|
37
|
+
return false
|
|
38
|
+
else
|
|
39
|
+
return true
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
35
43
|
def chronos_url
|
|
36
44
|
if @options.has_key?("chronos_url")
|
|
37
45
|
# If passed in on command line override what is in config file
|
|
@@ -62,13 +70,32 @@ module Orch
|
|
|
62
70
|
return @APP_CONFIG["marathon_url"]
|
|
63
71
|
end
|
|
64
72
|
|
|
65
|
-
def
|
|
73
|
+
def bamboo_url
|
|
74
|
+
if @options.has_key?("bamboo_url")
|
|
75
|
+
# If passed in on command line override what is in config file
|
|
76
|
+
url = @options["bamboo_url"]
|
|
77
|
+
return url
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if @APP_CONFIG.nil? || @APP_CONFIG["bamboo_url"].nil?
|
|
81
|
+
puts "bamboo_url not specified, use --bamboo_url or set in ~/.orch/config.yml"
|
|
82
|
+
exit 1
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return @APP_CONFIG["bamboo_url"]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def setup_config(settings)
|
|
66
89
|
|
|
67
90
|
if @APP_CONFIG.nil?
|
|
68
91
|
@APP_CONFIG = {}
|
|
69
92
|
end
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
|
|
94
|
+
settings.each do |key, value|
|
|
95
|
+
if settings[key] != ""
|
|
96
|
+
@APP_CONFIG[key] = value
|
|
97
|
+
end
|
|
98
|
+
end
|
|
72
99
|
|
|
73
100
|
if ! File.directory?("#{Dir.home}/.orch")
|
|
74
101
|
Dir.mkdir("#{Dir.home}/.orch")
|
data/lib/marathon.rb
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Marathon interface object
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
|
|
6
|
+
require 'config'
|
|
7
|
+
|
|
8
|
+
class String
|
|
9
|
+
def numeric?
|
|
10
|
+
Float(self) != nil rescue false
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Orch
|
|
15
|
+
class Marathon
|
|
16
|
+
def initialize(options)
|
|
17
|
+
# TODO: get chronos and marathon urls from diffferent ways: param, env, .config
|
|
18
|
+
@config = Orch::Config.new(options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def deploy(app_id, json_payload)
|
|
22
|
+
uri = URI(@config.marathon_url)
|
|
23
|
+
json_headers = {"Content-Type" => "application/json",
|
|
24
|
+
"Accept" => "application/json"}
|
|
25
|
+
|
|
26
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
27
|
+
response = http.put("/v2/apps/#{app_id}", json_payload, json_headers)
|
|
28
|
+
|
|
29
|
+
# TODO: should we do anyting with version or deploymentId that gets returned?
|
|
30
|
+
if response.code == 201.to_s
|
|
31
|
+
puts "successfully created marathon job: #{app_id}"
|
|
32
|
+
elsif response.code == 200.to_s
|
|
33
|
+
puts "successfully updated marathon job: #{app_id}"
|
|
34
|
+
else
|
|
35
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return response
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def delete(id)
|
|
42
|
+
uri = URI(@config.marathon_url)
|
|
43
|
+
json_headers = {"Content-Type" => "application/json",
|
|
44
|
+
"Accept" => "application/json"}
|
|
45
|
+
|
|
46
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
47
|
+
response = http.delete("/v2/apps/#{id}", json_headers)
|
|
48
|
+
|
|
49
|
+
if response.code == 200.to_s
|
|
50
|
+
puts "successfully deleted #{id}"
|
|
51
|
+
elsif response.code == 404.to_s
|
|
52
|
+
puts "job: #{id} - does not exist to delete"
|
|
53
|
+
else
|
|
54
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return response
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def verify(json_payload)
|
|
61
|
+
if @config.check_for_marathon_url == false
|
|
62
|
+
puts "no marathon_url - can not verify with server"
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
spec = Hashie::Mash.new(JSON.parse(json_payload))
|
|
67
|
+
|
|
68
|
+
uri = URI(@config.marathon_url)
|
|
69
|
+
json_headers = {"Content-Type" => "application/json",
|
|
70
|
+
"Accept" => "application/json"}
|
|
71
|
+
|
|
72
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
73
|
+
response = http.get("/v2/apps/#{spec.id}", json_headers)
|
|
74
|
+
|
|
75
|
+
if response.code == 200.to_s
|
|
76
|
+
job = Hashie::Mash.new(JSON.parse(response.body))
|
|
77
|
+
foundDiffs = find_diffs(spec, job.app)
|
|
78
|
+
elsif response.code == 404.to_s
|
|
79
|
+
puts "job: #{spec.id} - not defined in Marathon"
|
|
80
|
+
foundDiffs = true
|
|
81
|
+
else
|
|
82
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
83
|
+
foundDiffs = true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
return foundDiffs
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def restart(app_id)
|
|
90
|
+
# POST /v2/apps/{appId}/restart: Rolling restart of all tasks of the given app
|
|
91
|
+
uri = URI(@config.marathon_url)
|
|
92
|
+
json_headers = {"Content-Type" => "application/json",
|
|
93
|
+
"Accept" => "application/json"}
|
|
94
|
+
|
|
95
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
96
|
+
response = http.post("/v2/apps/#{app_id}/restart", {}.to_json, json_headers)
|
|
97
|
+
|
|
98
|
+
if response.code == 200.to_s
|
|
99
|
+
puts "success"
|
|
100
|
+
else
|
|
101
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
return response
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def find_diffs(spec, job)
|
|
108
|
+
foundDiff = false
|
|
109
|
+
|
|
110
|
+
spec.each_key do |key|
|
|
111
|
+
if spec[key].is_a?(Hash)
|
|
112
|
+
if find_diffs(spec[key], job[key]) == true
|
|
113
|
+
foundDiff = true
|
|
114
|
+
end
|
|
115
|
+
next
|
|
116
|
+
end
|
|
117
|
+
if spec[key].is_a?(Array)
|
|
118
|
+
if spec[key].length != job[key].length
|
|
119
|
+
printf "difference for field: #{key} - length of array is different\n"
|
|
120
|
+
printf " spec: #{spec[key].to_json}\n"
|
|
121
|
+
printf " server: #{job[key].to_json}\n"
|
|
122
|
+
foundDiff = true
|
|
123
|
+
end
|
|
124
|
+
# TODO: this will not work if arrays are not in same order
|
|
125
|
+
spec[key].zip(job[key]).each do |subSpec, subJob|
|
|
126
|
+
if find_diffs(subSpec, subJob) == true
|
|
127
|
+
foundDiff = true
|
|
128
|
+
end
|
|
129
|
+
next
|
|
130
|
+
end
|
|
131
|
+
next
|
|
132
|
+
end
|
|
133
|
+
specVal = spec[key]
|
|
134
|
+
jobVal = job[key]
|
|
135
|
+
if spec[key].to_s.numeric?
|
|
136
|
+
specVal = Float(spec[key])
|
|
137
|
+
jobVal = Float(job[key])
|
|
138
|
+
end
|
|
139
|
+
if specVal != jobVal
|
|
140
|
+
printf "difference for field: #{key}\n"
|
|
141
|
+
printf " spec: #{specVal}\n"
|
|
142
|
+
printf " server: #{jobVal}\n"
|
|
143
|
+
foundDiff = true
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
return foundDiff
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
data/lib/orch/version.rb
CHANGED
data/lib/orch.rb
CHANGED
|
@@ -4,7 +4,9 @@ require 'json'
|
|
|
4
4
|
require 'thor'
|
|
5
5
|
require "yaml"
|
|
6
6
|
require "parse"
|
|
7
|
-
require "
|
|
7
|
+
require "chronos"
|
|
8
|
+
require "marathon"
|
|
9
|
+
require "bamboo"
|
|
8
10
|
|
|
9
11
|
module Orch
|
|
10
12
|
class Application < Thor
|
|
@@ -21,16 +23,18 @@ module Orch
|
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
say("Enter values to construct a ~/.orch/config.yaml file")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
settings = {}
|
|
27
|
+
settings["marathon_url"] = ask("Marathon URL: ")
|
|
28
|
+
settings["chronos_url"] = ask("Chronos URL: ")
|
|
29
|
+
settings["bamboo_url"] = ask("Bamboo URL: ")
|
|
26
30
|
|
|
27
|
-
config.setup_config(
|
|
31
|
+
config.setup_config(settings)
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
option :deploy_kind, :default => 'all',
|
|
31
35
|
:desc => 'deploys only the given application kind: chronos, marathon, all'
|
|
32
|
-
option :
|
|
33
|
-
:desc => '
|
|
36
|
+
option :deploy_var, :default => 'all',
|
|
37
|
+
:desc => 'DEPLOY_VAR=VALUE deploys only if a deploy_var matches the given value'
|
|
34
38
|
option :subst,
|
|
35
39
|
:desc => 'KEY=VALUE substitute KEY with VALUE globaly in your config'
|
|
36
40
|
option :show_json, :default => false,
|
|
@@ -42,7 +46,10 @@ module Orch
|
|
|
42
46
|
parser = Orch::Parse.new(file_name, options)
|
|
43
47
|
result = parser.parse(true)
|
|
44
48
|
puts "Number of configs found: #{result.length}"
|
|
45
|
-
|
|
49
|
+
|
|
50
|
+
marathon = Orch::Marathon.new(options)
|
|
51
|
+
chronos = Orch::Chronos.new(options)
|
|
52
|
+
bamboo = Orch::Bamboo.new(options)
|
|
46
53
|
result.each do |app|
|
|
47
54
|
printf "Name: %s, Type: %s, Deploy?: %s", app[:name], app[:type], app[:deploy]
|
|
48
55
|
app[:env_vars].each do |key, value|
|
|
@@ -53,23 +60,33 @@ module Orch
|
|
|
53
60
|
pretty = JSON.pretty_generate(app[:json])
|
|
54
61
|
puts "JSON: #{pretty}"
|
|
55
62
|
end
|
|
63
|
+
foundDiffs = false
|
|
56
64
|
if (app[:type] == "Chronos") && (options[:server_verify] == true)
|
|
57
|
-
|
|
65
|
+
foundDiffs = chronos.verify(app[:json].to_json)
|
|
58
66
|
end
|
|
59
67
|
if (app[:type] == "Marathon") && (options[:server_verify] == true)
|
|
60
|
-
|
|
68
|
+
foundDiffs = marathon.verify(app[:json].to_json)
|
|
69
|
+
if app[:bamboo_spec]
|
|
70
|
+
bambooDiffs = bamboo.verify(app[:name], app[:bamboo_spec])
|
|
71
|
+
foundDiffs = foundDiffs || bambooDiffs
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
if (!foundDiffs) && (options[:server_verify] == true)
|
|
75
|
+
puts "No differences with server found"
|
|
61
76
|
end
|
|
62
77
|
end
|
|
63
78
|
end
|
|
64
79
|
|
|
65
80
|
option :deploy_kind, :default => 'all',
|
|
66
81
|
:desc => 'deploys only the given application kind: chronos, marathon, all'
|
|
67
|
-
option :
|
|
68
|
-
:desc => '
|
|
82
|
+
option :deploy_var, :default => 'all',
|
|
83
|
+
:desc => 'DEPLOY_VAR=VALUE deploys only if a deploy_var matches the given value'
|
|
69
84
|
option :chronos_url,
|
|
70
85
|
:desc => 'url to chronos master'
|
|
71
86
|
option :marathon_url,
|
|
72
87
|
:desc => 'url to marathon master'
|
|
88
|
+
option :bamboo_url,
|
|
89
|
+
:desc => 'url to bamboo server'
|
|
73
90
|
option :subst,
|
|
74
91
|
:desc => 'KEY=VALUE substitute KEY with VALUE globaly in your config'
|
|
75
92
|
desc 'deploy PATH', 'Deploys config to mesos frameworks.'
|
|
@@ -81,7 +98,9 @@ module Orch
|
|
|
81
98
|
puts "nothing found to deploy"
|
|
82
99
|
end
|
|
83
100
|
|
|
84
|
-
|
|
101
|
+
marathon = Orch::Marathon.new(options)
|
|
102
|
+
chronos = Orch::Chronos.new(options)
|
|
103
|
+
bamboo = Orch::Bamboo.new(options)
|
|
85
104
|
result.each do |app|
|
|
86
105
|
if !app[:deploy]
|
|
87
106
|
puts "skipping app: #{app[:name]}"
|
|
@@ -90,13 +109,88 @@ module Orch
|
|
|
90
109
|
puts "deploying #{app[:name]} to #{app[:type]}"
|
|
91
110
|
#puts "#{app[:json]}" - should I support show_json here as well?
|
|
92
111
|
if app[:type] == "Chronos"
|
|
93
|
-
deploy
|
|
112
|
+
chronos.deploy(app[:json].to_json)
|
|
94
113
|
end
|
|
95
114
|
if app[:type] == "Marathon"
|
|
96
|
-
deploy
|
|
115
|
+
marathon.deploy(app[:name], app[:json].to_json)
|
|
116
|
+
if app[:bamboo_spec]
|
|
117
|
+
bamboo.deploy(app[:name], app[:bamboo_spec])
|
|
118
|
+
end
|
|
97
119
|
end
|
|
98
120
|
end
|
|
99
121
|
end
|
|
100
122
|
|
|
123
|
+
option :deploy_kind, :default => 'all',
|
|
124
|
+
:desc => 'deletes only the given application kind: chronos, marathon, all'
|
|
125
|
+
option :deploy_var, :default => 'all',
|
|
126
|
+
:desc => 'DEPLOY_VAR=VALUE deletes only if a deploy_var matches the given value'
|
|
127
|
+
option :chronos_url,
|
|
128
|
+
:desc => 'url to chronos master'
|
|
129
|
+
option :marathon_url,
|
|
130
|
+
:desc => 'url to marathon master'
|
|
131
|
+
option :bamboo_url,
|
|
132
|
+
:desc => 'url to bamboo server'
|
|
133
|
+
option :subst,
|
|
134
|
+
:desc => 'KEY=VALUE substitute KEY with VALUE globaly in your config'
|
|
135
|
+
desc 'delete PATH', 'Deletes config from mesos frameworks.'
|
|
136
|
+
def delete(file_name)
|
|
137
|
+
parser = Orch::Parse.new(file_name, options)
|
|
138
|
+
result = parser.parse(false)
|
|
139
|
+
|
|
140
|
+
if result.length == 0
|
|
141
|
+
puts "nothing found to delete"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
marathon = Orch::Marathon.new(options)
|
|
145
|
+
chronos = Orch::Chronos.new(options)
|
|
146
|
+
bamboo = Orch::Bamboo.new(options)
|
|
147
|
+
|
|
148
|
+
result.each do |app|
|
|
149
|
+
if !app[:deploy]
|
|
150
|
+
puts "skipping app: #{app[:name]}"
|
|
151
|
+
next
|
|
152
|
+
end
|
|
153
|
+
puts "deleting #{app[:name]} from #{app[:type]}"
|
|
154
|
+
if app[:type] == "Chronos"
|
|
155
|
+
chronos.delete(app[:name])
|
|
156
|
+
end
|
|
157
|
+
if app[:type] == "Marathon"
|
|
158
|
+
marathon.delete(app[:name])
|
|
159
|
+
if app[:bamboo_spec]
|
|
160
|
+
bamboo.delete(app[:name])
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
option :deploy_kind, :default => 'all',
|
|
167
|
+
:desc => 'deletes only the given application kind: chronos, marathon, all'
|
|
168
|
+
option :deploy_var, :default => 'all',
|
|
169
|
+
:desc => 'DEPLOY_VAR=VALUE deletes only if a deploy_var matches the given value'
|
|
170
|
+
option :marathon_url,
|
|
171
|
+
:desc => 'url to marathon master'
|
|
172
|
+
option :subst,
|
|
173
|
+
:desc => 'KEY=VALUE substitute KEY with VALUE globaly in your config'
|
|
174
|
+
desc 'restart PATH', 'Restarts specified application(s) on server'
|
|
175
|
+
def restart(file_name)
|
|
176
|
+
parser = Orch::Parse.new(file_name, options)
|
|
177
|
+
result = parser.parse(false)
|
|
178
|
+
|
|
179
|
+
if result.length == 0
|
|
180
|
+
puts "nothing found to restart"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
marathon = Orch::Marathon.new(options)
|
|
184
|
+
result.each do |app|
|
|
185
|
+
if !app[:deploy] || (app[:type] == "Chronos")
|
|
186
|
+
puts "skipping app: #{app[:name]}"
|
|
187
|
+
next
|
|
188
|
+
end
|
|
189
|
+
puts "restarting #{app[:name]} on #{app[:type]}"
|
|
190
|
+
if app[:type] == "Marathon"
|
|
191
|
+
marathon.restart(app[:name])
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
101
195
|
end
|
|
102
196
|
end
|
data/lib/parse.rb
CHANGED
|
@@ -9,14 +9,13 @@ module Orch
|
|
|
9
9
|
puts "file does not exist: #{path}"
|
|
10
10
|
exit 1
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
begin
|
|
14
14
|
yaml = ::YAML.load_file(path)
|
|
15
15
|
rescue Psych::SyntaxError => e
|
|
16
16
|
puts "error parsing yaml file #{e}"
|
|
17
17
|
exit 1
|
|
18
18
|
end
|
|
19
|
-
#puts yaml.to_json
|
|
20
19
|
|
|
21
20
|
@options = options
|
|
22
21
|
|
|
@@ -42,6 +41,11 @@ module Orch
|
|
|
42
41
|
# Check for vault vars
|
|
43
42
|
env_var_values = parse_vault_vars(@spec)
|
|
44
43
|
|
|
44
|
+
# Check for env values at spec level
|
|
45
|
+
if ! spec.env.nil?
|
|
46
|
+
env_var_values = env_var_values.merge(spec.env)
|
|
47
|
+
end
|
|
48
|
+
|
|
45
49
|
if spec.applications.nil?
|
|
46
50
|
puts "required section applications: must have at least one application defined"
|
|
47
51
|
exit 1
|
|
@@ -60,39 +64,51 @@ module Orch
|
|
|
60
64
|
exit 1
|
|
61
65
|
end
|
|
62
66
|
|
|
63
|
-
# Generate any
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
# Generate any deploy variables that need to be merged in
|
|
68
|
+
deploy_vars = parse_deploy_vars(app)
|
|
69
|
+
app_var_values = env_var_values.merge(deploy_vars)
|
|
70
|
+
|
|
71
|
+
# Check for env values at app level
|
|
72
|
+
if ! app.env.nil?
|
|
73
|
+
app_var_values = app_var_values.merge(app.env)
|
|
74
|
+
end
|
|
66
75
|
|
|
67
76
|
if (app.kind == "Chronos")
|
|
68
|
-
chronos_spec = parse_chronos(app,
|
|
77
|
+
chronos_spec = parse_chronos(app, app_var_values)
|
|
69
78
|
|
|
70
|
-
result = {:name => chronos_spec["name"], :type => app.kind, :deploy => should_deploy?(app), :env_vars =>
|
|
79
|
+
result = {:name => chronos_spec["name"], :type => app.kind, :deploy => should_deploy?(app), :env_vars => deploy_vars, :json => chronos_spec}
|
|
71
80
|
results << result
|
|
72
81
|
end
|
|
73
82
|
|
|
74
83
|
if (app.kind == "Marathon")
|
|
75
|
-
marathon_spec = parse_marathon(app,
|
|
84
|
+
marathon_spec = parse_marathon(app, app_var_values)
|
|
85
|
+
|
|
86
|
+
result = {:name => marathon_spec["id"], :type => app.kind, :deploy => should_deploy?(app), :env_vars => deploy_vars, :json => marathon_spec}
|
|
87
|
+
|
|
88
|
+
if app.bamboo_spec
|
|
89
|
+
bamboo_spec = parse_bamboo(app, app_var_values)
|
|
90
|
+
result[:bamboo_spec] = bamboo_spec
|
|
91
|
+
end
|
|
76
92
|
|
|
77
|
-
result = {:name => marathon_spec["id"], :type => app.kind, :deploy => should_deploy?(app), :env_vars => env_var_values, :json => marathon_spec}
|
|
78
93
|
results << result
|
|
79
94
|
end
|
|
95
|
+
|
|
80
96
|
end
|
|
81
97
|
|
|
82
98
|
return results
|
|
83
99
|
end
|
|
84
100
|
|
|
85
|
-
def
|
|
101
|
+
def parse_deploy_vars(app)
|
|
86
102
|
result = {}
|
|
87
|
-
if (! @spec.
|
|
88
|
-
@spec.
|
|
103
|
+
if (! @spec.deploy_vars.nil?)
|
|
104
|
+
@spec.deploy_vars.each do |key, value|
|
|
89
105
|
if app[key].nil?
|
|
90
106
|
puts "environments_var #{key} specified - but not included in app"
|
|
91
107
|
# TODO: would be nice to put the app name...
|
|
92
108
|
exit 1
|
|
93
109
|
end
|
|
94
|
-
if ! @spec.
|
|
95
|
-
puts "#{key} value \"#{app[key]}\" not in #{@spec.
|
|
110
|
+
if ! @spec.deploy_vars[key].include? app[key]
|
|
111
|
+
puts "#{key} value \"#{app[key]}\" not in #{@spec.deploy_vars[key].to_s}"
|
|
96
112
|
exit 1
|
|
97
113
|
end
|
|
98
114
|
result[key] = app[key]
|
|
@@ -115,29 +131,50 @@ module Orch
|
|
|
115
131
|
end
|
|
116
132
|
|
|
117
133
|
def parse_chronos(app, env_var_values)
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
if app.chronos_spec.nil?
|
|
135
|
+
puts "App of kind: Chronos requires a 'chronos_spec:' field"
|
|
136
|
+
exit 1
|
|
137
|
+
end
|
|
138
|
+
chronos_spec = app.chronos_spec
|
|
120
139
|
|
|
121
|
-
#
|
|
122
|
-
|
|
140
|
+
# Override out high-level env vars with any chronos_spec level vars
|
|
141
|
+
env_vars = env_var_values
|
|
142
|
+
(chronos_spec.environmentVariables || []).each do |x|
|
|
143
|
+
env_vars[x["name"]] = x["value"]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Rewrite the environmentVariables from the hash
|
|
147
|
+
chronos_spec.environmentVariables = []
|
|
148
|
+
env_vars.each do |key, value|
|
|
123
149
|
pair = {"name" => key, "value" => value}
|
|
124
|
-
|
|
150
|
+
chronos_spec.environmentVariables << pair
|
|
125
151
|
end
|
|
126
152
|
|
|
127
153
|
# Do subst processing
|
|
128
|
-
spec_str = do_subst(
|
|
129
|
-
|
|
154
|
+
spec_str = do_subst(chronos_spec, app)
|
|
155
|
+
chronos_spec = JSON.parse(spec_str)
|
|
130
156
|
|
|
131
|
-
return
|
|
157
|
+
return chronos_spec
|
|
132
158
|
end
|
|
133
159
|
|
|
134
160
|
def parse_marathon(app, env_var_values)
|
|
135
|
-
|
|
161
|
+
if app.marathon_spec.nil?
|
|
162
|
+
puts "App of kind: Marathon requires a 'marathon_spec:' field"
|
|
163
|
+
exit 1
|
|
164
|
+
end
|
|
136
165
|
marathon_spec = app.marathon_spec
|
|
137
166
|
|
|
138
|
-
# Augment any spec environment variables with meta values
|
|
167
|
+
# Augment any spec environment variables with meta values - but don't overwite
|
|
168
|
+
marathon_spec.env = {} unless marathon_spec.env
|
|
139
169
|
env_var_values.each do |key, value|
|
|
140
|
-
marathon_spec.env[key] = value
|
|
170
|
+
marathon_spec.env[key] = value.to_s unless marathon_spec.env[key]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if marathon_spec.id
|
|
174
|
+
marathon_spec.id = (marathon_spec.id[0] == '/') ? marathon_spec.id : ("/" + marathon_spec.id)
|
|
175
|
+
else
|
|
176
|
+
puts "id: is a required field for a marathon spec"
|
|
177
|
+
exit 1
|
|
141
178
|
end
|
|
142
179
|
|
|
143
180
|
spec_str = do_subst(marathon_spec, app)
|
|
@@ -146,25 +183,34 @@ module Orch
|
|
|
146
183
|
return marathon_spec
|
|
147
184
|
end
|
|
148
185
|
|
|
186
|
+
def parse_bamboo(app, env_var_values)
|
|
187
|
+
# Do any substs
|
|
188
|
+
spec_str = do_subst(app.bamboo_spec, app)
|
|
189
|
+
bamboo_spec = JSON.parse(spec_str)
|
|
190
|
+
|
|
191
|
+
if bamboo_spec['acl'].nil?
|
|
192
|
+
puts "required field 'acl:' missing from bamboo_spec"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
return bamboo_spec
|
|
196
|
+
end
|
|
197
|
+
|
|
149
198
|
def should_deploy?(app)
|
|
150
199
|
result = true
|
|
151
|
-
if
|
|
152
|
-
result = false
|
|
153
|
-
end
|
|
154
|
-
if (app.kind == "Chronos") && @options[:deploy_kind] == 'marathon'
|
|
200
|
+
if @options[:deploy_kind] != 'all' && app.kind != @options[:deploy_kind]
|
|
155
201
|
result = false
|
|
156
202
|
end
|
|
157
203
|
|
|
158
|
-
if @options[:
|
|
159
|
-
@options[:
|
|
204
|
+
if @options[:deploy_var] != 'all'
|
|
205
|
+
@options[:deploy_var].split(",").each do |x|
|
|
160
206
|
pair = x.split("=")
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if app[
|
|
164
|
-
puts "environment var of '#{
|
|
207
|
+
deployVar = pair[0]
|
|
208
|
+
deployVal = pair[1]
|
|
209
|
+
if app[deployVar].nil?
|
|
210
|
+
puts "environment var of '#{deployVar}' not found in app"
|
|
165
211
|
exit 1
|
|
166
212
|
end
|
|
167
|
-
if app[
|
|
213
|
+
if app[deployVar] != deployVal
|
|
168
214
|
result = false
|
|
169
215
|
end
|
|
170
216
|
end
|
|
@@ -176,9 +222,9 @@ module Orch
|
|
|
176
222
|
def do_subst(spec, app)
|
|
177
223
|
spec_str = spec.to_json.to_s
|
|
178
224
|
|
|
179
|
-
# Subst any of the
|
|
180
|
-
if (! @spec.
|
|
181
|
-
@spec.
|
|
225
|
+
# Subst any of the deploy_vars values
|
|
226
|
+
if (! @spec.deploy_vars.nil?)
|
|
227
|
+
@spec.deploy_vars.each do |key, value|
|
|
182
228
|
spec_str = spec_str.gsub(/{{#{key}}}/, app[key])
|
|
183
229
|
end
|
|
184
230
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: orch
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ray Johnson
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-08-
|
|
11
|
+
date: 2015-08-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -81,11 +81,15 @@ files:
|
|
|
81
81
|
- Rakefile
|
|
82
82
|
- bin/orch
|
|
83
83
|
- examples/Examples.md
|
|
84
|
+
- examples/bamboo-ex.yml
|
|
84
85
|
- examples/env-chronos.yml
|
|
86
|
+
- examples/env-marathon.yml
|
|
85
87
|
- examples/simple-chronos.yml
|
|
86
88
|
- examples/simple-marathon.yml
|
|
89
|
+
- lib/bamboo.rb
|
|
90
|
+
- lib/chronos.rb
|
|
87
91
|
- lib/config.rb
|
|
88
|
-
- lib/
|
|
92
|
+
- lib/marathon.rb
|
|
89
93
|
- lib/orch.rb
|
|
90
94
|
- lib/orch/version.rb
|
|
91
95
|
- lib/parse.rb
|