orch 0.0.1
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 +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +44 -0
- data/Rakefile +2 -0
- data/bin/orch +5 -0
- data/examples/env-chronos.yml +46 -0
- data/examples/simple-chronos.yml +17 -0
- data/examples/simple-marathon.yml +28 -0
- data/lib/config.rb +63 -0
- data/lib/deploy.rb +121 -0
- data/lib/orch/version.rb +3 -0
- data/lib/orch.rb +97 -0
- data/lib/parse.rb +209 -0
- data/orch.gemspec +27 -0
- metadata +116 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 2f67ca3214ddbc6302dde59fc294da08fd0dbd2b
|
|
4
|
+
data.tar.gz: 572ce735ddf78eebbd1ace4c6d0b33a05892d37f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e890d242ed98f9986b2a2eced37f4798aa85baba10192ac3bc5baeae214611c84043fef017163388267fdd5f8807241eb2efda216e284849546acb205154af43
|
|
7
|
+
data.tar.gz: 876005978425fba1769c54227c1473dddae4e83e5f210d98fcb5e5aa5e49f10d2d56454f31ff763841f7d83e0e771432405a81c1b15d45ee608f29e249fdeef1
|
data/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
.bundle
|
|
4
|
+
.config
|
|
5
|
+
.yardoc
|
|
6
|
+
Gemfile.lock
|
|
7
|
+
InstalledFiles
|
|
8
|
+
_yardoc
|
|
9
|
+
coverage
|
|
10
|
+
doc/
|
|
11
|
+
lib/bundler/man
|
|
12
|
+
pkg
|
|
13
|
+
rdoc
|
|
14
|
+
spec/reports
|
|
15
|
+
test/tmp
|
|
16
|
+
test/version_tmp
|
|
17
|
+
tmp
|
|
18
|
+
*.bundle
|
|
19
|
+
*.so
|
|
20
|
+
*.o
|
|
21
|
+
*.a
|
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2015 Ray Johnson
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Orch
|
|
2
|
+
|
|
3
|
+
orch - uses a Yaml file to specify jobs that can be delpoyed to Mesos' Marathon and Chronos frameworks.
|
|
4
|
+
|
|
5
|
+
## Installing this tool
|
|
6
|
+
|
|
7
|
+
Run the following command:
|
|
8
|
+
```
|
|
9
|
+
$> gem install orch
|
|
10
|
+
```
|
|
11
|
+
(you might need to sudo)
|
|
12
|
+
|
|
13
|
+
Once the gem is installed, the command orch will be in your $PATH
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Type "orch help" to get the list of commands and "orch help <command>" to get list of options for a given command.
|
|
18
|
+
|
|
19
|
+
The general usecase is to run "orch deploy <job_spec>" to deploy a configuration to to Marathon or Chronos. A given job_spec can contain specifications for many jobs and for multiple environments. Various options to the deploy command gives control to exactly what is deployed.
|
|
20
|
+
|
|
21
|
+
## Job spec format
|
|
22
|
+
|
|
23
|
+
TODO: describe the file
|
|
24
|
+
|
|
25
|
+
## Configuration Options
|
|
26
|
+
|
|
27
|
+
Run the following command to interactively create a config file.
|
|
28
|
+
```
|
|
29
|
+
$> orch config
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The file ~/.orch/config.yaml would contain values for "chronos_url" and "marathon_url". You can also pass --chronos_url or --marathon_url options to the "orch deploy" command to override what is in the config file.
|
|
33
|
+
|
|
34
|
+
## Examples
|
|
35
|
+
|
|
36
|
+
TODO: show some examples
|
|
37
|
+
|
|
38
|
+
## Contributing
|
|
39
|
+
|
|
40
|
+
1. Fork it ( https://github.com/[my-github-username]/orch/fork )
|
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
42
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
44
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/orch
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
default_job: &DEFAULT_JOB
|
|
3
|
+
kind: Chronos
|
|
4
|
+
environment: override me
|
|
5
|
+
cronos_spec: &DEFAULT_SPEC
|
|
6
|
+
name: "ray-test-{{ENV}}"
|
|
7
|
+
schedule: "R/2015-01-25T21:00/PT24H"
|
|
8
|
+
scheduleTimeZone: "PST"
|
|
9
|
+
owner: "rjohnson@yp.com"
|
|
10
|
+
container: &DEFAULT_CONTAINER
|
|
11
|
+
type: "DOCKER"
|
|
12
|
+
image: "busybox"
|
|
13
|
+
network: "BRIDGE"
|
|
14
|
+
cpus: "0.5"
|
|
15
|
+
mem: "512"
|
|
16
|
+
uris: []
|
|
17
|
+
environmentVariables:
|
|
18
|
+
-
|
|
19
|
+
name: "LOG_LOCATION"
|
|
20
|
+
value: "/var/{{ENV}}"
|
|
21
|
+
forcePullImage: false
|
|
22
|
+
command: "echo \"log here: $LOG_LOCATION\"; echo \"ENV: $DEPLOY_ENV\";sleep 10; echo bye"
|
|
23
|
+
|
|
24
|
+
version: alpha1
|
|
25
|
+
environments_var: DEPLOY_ENV
|
|
26
|
+
environments:
|
|
27
|
+
- dev
|
|
28
|
+
- test
|
|
29
|
+
- prod
|
|
30
|
+
applications:
|
|
31
|
+
- <<: *DEFAULT_JOB
|
|
32
|
+
environment: dev
|
|
33
|
+
cronos_spec:
|
|
34
|
+
<<: *DEFAULT_SPEC
|
|
35
|
+
schedule: "R/2015-08-01T21:00/PT24H"
|
|
36
|
+
forcePullImage: true
|
|
37
|
+
- <<: *DEFAULT_JOB
|
|
38
|
+
environment: test
|
|
39
|
+
cronos_spec:
|
|
40
|
+
<<: *DEFAULT_SPEC
|
|
41
|
+
schedule: "R/2015-08-01T21:10/PT24H"
|
|
42
|
+
- <<: *DEFAULT_JOB
|
|
43
|
+
environment: prod
|
|
44
|
+
cronos_spec:
|
|
45
|
+
<<: *DEFAULT_SPEC
|
|
46
|
+
schedule: "R/2015-08-01T21:30/PT24H"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
version: alpha1
|
|
2
|
+
applications:
|
|
3
|
+
- kind: Chronos
|
|
4
|
+
cronos_spec:
|
|
5
|
+
name: "ray-test"
|
|
6
|
+
schedule: "R/2015-01-25T21:00/PT24H"
|
|
7
|
+
scheduleTimeZone: "PST"
|
|
8
|
+
owner: "rjohnson@yp.com"
|
|
9
|
+
container:
|
|
10
|
+
type: "DOCKER"
|
|
11
|
+
image: "busybox"
|
|
12
|
+
network: "BRIDGE"
|
|
13
|
+
cpus: "0.5"
|
|
14
|
+
mem: "512"
|
|
15
|
+
uris: []
|
|
16
|
+
forcePullImage: true
|
|
17
|
+
command: "echo hello; sleep 10; echo bye"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
version: alpha1
|
|
2
|
+
environments_var: DEPLOY_ENV
|
|
3
|
+
environments:
|
|
4
|
+
- dev
|
|
5
|
+
- test
|
|
6
|
+
- prod
|
|
7
|
+
applications:
|
|
8
|
+
- kind: Marathon
|
|
9
|
+
environment: dev
|
|
10
|
+
marathon_spec:
|
|
11
|
+
id: "ray-web-{{ENV}}"
|
|
12
|
+
cpus: 0.1
|
|
13
|
+
mem: 300
|
|
14
|
+
instances: 10
|
|
15
|
+
env:
|
|
16
|
+
FOO: "bar"
|
|
17
|
+
container:
|
|
18
|
+
type: "DOCKER"
|
|
19
|
+
docker:
|
|
20
|
+
image: "nginx"
|
|
21
|
+
network: "BRIDGE"
|
|
22
|
+
portMappings:
|
|
23
|
+
-
|
|
24
|
+
containerPort: 80
|
|
25
|
+
hostPort: 0
|
|
26
|
+
protocol: "tcp"
|
|
27
|
+
forcePullImage: true
|
|
28
|
+
|
data/lib/config.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# config
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
module Orch
|
|
7
|
+
class Config
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
|
|
11
|
+
@config_path = "#{Dir.home}/.orch/config.yml"
|
|
12
|
+
if File.file?(@config_path)
|
|
13
|
+
@APP_CONFIG = YAML.load_file(@config_path)
|
|
14
|
+
else
|
|
15
|
+
@APP_CONFIG = nil
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def chronos_url
|
|
20
|
+
if @options.has_key?("chronos_url")
|
|
21
|
+
# If passed in on command line override what is in config file
|
|
22
|
+
url = @options["chronos_url"]
|
|
23
|
+
return url
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if @APP_CONFIG.nil? || @APP_CONFIG["chronos_url"].nil?
|
|
27
|
+
puts "chronos_url not specified, use --chronos_url or set in ~/.orch/config.yml"
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return @APP_CONFIG["chronos_url"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def marathon_url
|
|
35
|
+
if @options.has_key?("marathon_url")
|
|
36
|
+
# If passed in on command line override what is in config file
|
|
37
|
+
url = @options["marathon_url"]
|
|
38
|
+
return url
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if @APP_CONFIG.nil? || @APP_CONFIG["marathon_url"].nil?
|
|
42
|
+
puts "marathon_url not specified, use --marathon_url or set in ~/.orch/config.yml"
|
|
43
|
+
exit 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
return @APP_CONFIG["marathon_url"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def setup_config(marathon_url, chronos_url)
|
|
50
|
+
|
|
51
|
+
if @APP_CONFIG.nil?
|
|
52
|
+
@APP_CONFIG = {}
|
|
53
|
+
end
|
|
54
|
+
@APP_CONFIG['marathon_url'] = marathon_url
|
|
55
|
+
@APP_CONFIG['chronos_url'] = chronos_url
|
|
56
|
+
|
|
57
|
+
if ! File.directory?("#{Dir.home}/.orch")
|
|
58
|
+
Dir.mkdir("#{Dir.home}/.orch")
|
|
59
|
+
end
|
|
60
|
+
File.open(@config_path, 'w') {|f| f.write @APP_CONFIG.to_yaml}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/deploy.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
require 'config'
|
|
6
|
+
|
|
7
|
+
class String
|
|
8
|
+
def numeric?
|
|
9
|
+
Float(self) != nil rescue false
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module Orch
|
|
14
|
+
class Deploy
|
|
15
|
+
def initialize(options)
|
|
16
|
+
# TODO: get chronos and marathon urls from diffferent ways: param, env, .config
|
|
17
|
+
@config = Orch::Config.new(options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deploy_chronos(json_payload)
|
|
21
|
+
uri = URI(@config.chronos_url)
|
|
22
|
+
json_headers = {"Content-Type" => "application/json",
|
|
23
|
+
"Accept" => "application/json"}
|
|
24
|
+
|
|
25
|
+
# curl -L -H 'Content-Type: application/json' -X POST -d @$schedule_file $CHRONOS_URL/scheduler/iso8601
|
|
26
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
27
|
+
response = http.post("/scheduler/iso8601", json_payload, json_headers)
|
|
28
|
+
|
|
29
|
+
if response.code != 204.to_s
|
|
30
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# TODO: handle error codes better?
|
|
34
|
+
|
|
35
|
+
return response
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def verify_chronos(json_payload)
|
|
39
|
+
spec = Hashie::Mash.new(JSON.parse(json_payload))
|
|
40
|
+
|
|
41
|
+
uri = URI(@config.chronos_url)
|
|
42
|
+
json_headers = {"Content-Type" => "application/json",
|
|
43
|
+
"Accept" => "application/json"}
|
|
44
|
+
|
|
45
|
+
# curl -L -H 'Content-Type: application/json' -X POST -d @$schedule_file $CHRONOS_URL/scheduler/iso8601
|
|
46
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
47
|
+
response = http.get("/scheduler/jobs", json_headers)
|
|
48
|
+
|
|
49
|
+
if response.code != 200.to_s
|
|
50
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
array = JSON.parse(response.body).map { |hash| Hashie::Mash.new(hash) }
|
|
54
|
+
|
|
55
|
+
jobFound = false
|
|
56
|
+
array.each do |job|
|
|
57
|
+
if job.name == spec.name
|
|
58
|
+
jobFound = true
|
|
59
|
+
foundDiffs = find_diffs(spec, job)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
if !jobFound
|
|
64
|
+
puts "job \"#{spec.name}\" not currently deployed"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# TODO: handle error codes better?
|
|
68
|
+
|
|
69
|
+
return response
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def find_diffs(spec, job)
|
|
73
|
+
foundDiff = false
|
|
74
|
+
|
|
75
|
+
spec.each_key do |key|
|
|
76
|
+
if spec[key].is_a?(Hash)
|
|
77
|
+
if find_diffs(spec[key], job[key]) == true
|
|
78
|
+
foundDiff = true
|
|
79
|
+
end
|
|
80
|
+
next
|
|
81
|
+
end
|
|
82
|
+
specVal = spec[key]
|
|
83
|
+
jobVal = job[key]
|
|
84
|
+
if spec[key].to_s.numeric?
|
|
85
|
+
specVal = Float(spec[key])
|
|
86
|
+
jobVal = Float(job[key])
|
|
87
|
+
else
|
|
88
|
+
specVal = spec[key]
|
|
89
|
+
jobVal = job[key]
|
|
90
|
+
end
|
|
91
|
+
if specVal != jobVal
|
|
92
|
+
puts "#{key}= spec:#{spec[key]}, server:#{job[key]}"
|
|
93
|
+
foundDiff = true
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
return foundDiff
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def deploy_marathon(json_payload)
|
|
101
|
+
uri = URI(@config.marathon_url)
|
|
102
|
+
json_headers = {"Content-Type" => "application/json",
|
|
103
|
+
"Accept" => "application/json"}
|
|
104
|
+
|
|
105
|
+
# curl -L -H 'Content-Type: application/json' -X POST -d @$schedule_file $CHRONOS_URL/scheduler/iso8601
|
|
106
|
+
puts uri
|
|
107
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
108
|
+
response = http.put("/v2/apps/#{json_payload["id"]}", json_payload, json_headers)
|
|
109
|
+
|
|
110
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
|
111
|
+
|
|
112
|
+
if response.code == 204.to_s
|
|
113
|
+
puts "success"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# TODO: handle error codes
|
|
117
|
+
|
|
118
|
+
return response
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/orch/version.rb
ADDED
data/lib/orch.rb
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require "orch/version"
|
|
2
|
+
require 'hashie'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'thor'
|
|
5
|
+
require "yaml"
|
|
6
|
+
require "parse"
|
|
7
|
+
require "deploy"
|
|
8
|
+
|
|
9
|
+
module Orch
|
|
10
|
+
class Application < Thor
|
|
11
|
+
|
|
12
|
+
desc 'config', 'Interactive way to build ~/.orch/config.yml file'
|
|
13
|
+
def config
|
|
14
|
+
config = Orch::Config.new(options)
|
|
15
|
+
|
|
16
|
+
if File.file?("#{Dir.home}/.orch/config.yml")
|
|
17
|
+
say "This will over-write your existing ~/.orch/config.yaml file", :yellow
|
|
18
|
+
answer = yes? "Proceed?", :yellow
|
|
19
|
+
if answer == false
|
|
20
|
+
exit 0
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
say("Enter values to construct a ~/.orch/config.yaml file")
|
|
24
|
+
marathon_url = ask("Marathon URL: ")
|
|
25
|
+
chronos_url = ask("Chronos URL: ")
|
|
26
|
+
|
|
27
|
+
config.setup_config(marathon_url, chronos_url)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
option :deploy_type, :default => 'all',
|
|
31
|
+
:desc => 'chronos, marathon, all'
|
|
32
|
+
option :env_type, :default => 'all',
|
|
33
|
+
:desc => 'a valid environment defined in your config or all to deploy to all environments'
|
|
34
|
+
option :subst,
|
|
35
|
+
:desc => 'KEY=VALUE substitute KEY with VALUE globaly in your config'
|
|
36
|
+
option :show_json, :default => false,
|
|
37
|
+
:desc => 'show the json result that would be sent to Chronos or Marathon'
|
|
38
|
+
desc 'verify PATH', 'Checks basic syntax and does not deploy'
|
|
39
|
+
def verify(file_name)
|
|
40
|
+
parser = Orch::Parse.new(file_name, options)
|
|
41
|
+
result = parser.parse(true)
|
|
42
|
+
puts "Number of configs found: #{result.length}"
|
|
43
|
+
deploy = Orch::Deploy.new(options)
|
|
44
|
+
result.each do |app|
|
|
45
|
+
if parser.hasEnvironments
|
|
46
|
+
puts "Name: #{app[:name]}, Type: #{app[:type]}, Would deploy: #{app[:deploy]}, Environment: #{app[:environment]}"
|
|
47
|
+
else
|
|
48
|
+
puts "Name: #{app[:name]}, Type: #{app[:type]}, Would deploy: #{app[:deploy]}"
|
|
49
|
+
end
|
|
50
|
+
if options[:show_json]
|
|
51
|
+
pretty = JSON.pretty_generate(app[:json])
|
|
52
|
+
puts "JSON: #{pretty}"
|
|
53
|
+
end
|
|
54
|
+
if app[:type] == "Chronos"
|
|
55
|
+
deploy.verify_chronos(app[:json].to_json)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
option :deploy_type, :default => 'all',
|
|
61
|
+
:desc => 'chronos, marathon, all'
|
|
62
|
+
option :env_type, :default => 'all',
|
|
63
|
+
:desc => 'a valid environment defined in your config or all to deploy to all environments'
|
|
64
|
+
option :chronos_url,
|
|
65
|
+
:desc => 'url to chronos master'
|
|
66
|
+
option :marathon_url,
|
|
67
|
+
:desc => 'url to marathon master'
|
|
68
|
+
option :subst,
|
|
69
|
+
:desc => 'KEY=VALUE substitute KEY with VALUE globaly in your config'
|
|
70
|
+
desc 'deploy PATH', 'Deploys config to mesos frameworks.'
|
|
71
|
+
def deploy(file_name)
|
|
72
|
+
parser = Orch::Parse.new(file_name, options)
|
|
73
|
+
result = parser.parse(false)
|
|
74
|
+
|
|
75
|
+
if result.length == 0
|
|
76
|
+
puts "nothing found to deploy"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
deploy = Orch::Deploy.new(options)
|
|
80
|
+
result.each do |app|
|
|
81
|
+
if !app[:deploy]
|
|
82
|
+
puts "skipping app: #{app[:name]}"
|
|
83
|
+
next
|
|
84
|
+
end
|
|
85
|
+
puts "deploying #{app[:name]} to #{app[:type]}"
|
|
86
|
+
#puts "#{app[:json]}" - should I support show_json here as well?
|
|
87
|
+
if app[:type] == "Chronos"
|
|
88
|
+
deploy.deploy_chronos(app[:json].to_json)
|
|
89
|
+
end
|
|
90
|
+
if app[:type] == "Marathon"
|
|
91
|
+
deploy.deploy_marathon(app[:json].to_json)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/parse.rb
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Orch
|
|
5
|
+
class Parse
|
|
6
|
+
attr_reader :hasEnvironments
|
|
7
|
+
|
|
8
|
+
def initialize(path, options)
|
|
9
|
+
if ! File.file?(path)
|
|
10
|
+
puts "file does not exist: #{path}"
|
|
11
|
+
exit 1
|
|
12
|
+
end
|
|
13
|
+
# TODO: try resecue for any syntax issues in yaml file
|
|
14
|
+
yaml = ::YAML.load_file(path)
|
|
15
|
+
#puts yaml.to_json
|
|
16
|
+
|
|
17
|
+
@options = options
|
|
18
|
+
|
|
19
|
+
@spec = Hashie::Mash.new(yaml)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def parse(dry_run)
|
|
23
|
+
@dry_run = dry_run
|
|
24
|
+
check_option_syntax
|
|
25
|
+
spec = @spec
|
|
26
|
+
|
|
27
|
+
# Check for valid version paramter
|
|
28
|
+
if spec.version.nil?
|
|
29
|
+
puts "required field version was not found"
|
|
30
|
+
exit 1
|
|
31
|
+
end
|
|
32
|
+
if spec.version != "alpha1"
|
|
33
|
+
puts "unsupported orch version specified: #{spec.version}"
|
|
34
|
+
puts "application only understands version alpha1"
|
|
35
|
+
exit 1
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Does this config support environments
|
|
39
|
+
@hasEnvironments = false
|
|
40
|
+
defined_environments = {}
|
|
41
|
+
if (! spec.environments.nil?)
|
|
42
|
+
@hasEnvironments = true
|
|
43
|
+
spec.environments.each do |e|
|
|
44
|
+
defined_environments[e] = e
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if (! spec.environments_var.nil?)
|
|
49
|
+
@environments_var = spec.environments_var
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if spec.applications.nil?
|
|
53
|
+
puts "required section applications: must have at least one application defined"
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
results = []
|
|
58
|
+
spec.applications.each do |app|
|
|
59
|
+
# Check for valid kind paramter
|
|
60
|
+
if app.kind.nil?
|
|
61
|
+
puts "required field kind was not found"
|
|
62
|
+
exit 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if !(app.kind == "Chronos" || app.kind == "Marathon")
|
|
67
|
+
puts "unsupported kind specified: #{app.kind} - must be: Chronos | Marathon"
|
|
68
|
+
exit 1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if @hasEnvironments
|
|
72
|
+
if !defined_environments.has_key?(app.environment)
|
|
73
|
+
puts "environment \"#{app.environment}\" not defined in environments"
|
|
74
|
+
exit 1
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if (app.kind == "Chronos")
|
|
79
|
+
chronos_spec = parse_chronos(app)
|
|
80
|
+
|
|
81
|
+
result = {:name => chronos_spec["name"], :type => app.kind, :deploy => should_deploy?(app), :json => chronos_spec}
|
|
82
|
+
if @hasEnvironments
|
|
83
|
+
result[:environment] = app.environment
|
|
84
|
+
end
|
|
85
|
+
results << result
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
if (app.kind == "Marathon")
|
|
89
|
+
marathon_spec = parse_marathon(app)
|
|
90
|
+
|
|
91
|
+
result = {:name => marathon_spec["id"], :type => app.kind, :deploy => should_deploy?(app), :json => marathon_spec}
|
|
92
|
+
if @hasEnvironments
|
|
93
|
+
result[:environment] = app.environment
|
|
94
|
+
end
|
|
95
|
+
results << result
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
return results
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def parse_chronos(app)
|
|
103
|
+
# TODO: check if it exists
|
|
104
|
+
cronos_spec = app.cronos_spec
|
|
105
|
+
|
|
106
|
+
# This adds environment vaiables to the spec that conform to our docker-wrapper security hack
|
|
107
|
+
if ! app.vault.nil?
|
|
108
|
+
# TODO: check that an environment was defined
|
|
109
|
+
if (! @hasEnvironments) || app.environment.nil?
|
|
110
|
+
puts "the vault feature requires an environment to be set"
|
|
111
|
+
exit 1
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
count = 1
|
|
115
|
+
cronos_spec.environmentVariables ||= []
|
|
116
|
+
app.vault.each do |vault_key|
|
|
117
|
+
# Add an environment
|
|
118
|
+
pair = {"name" => "VAULT_KEY_#{count}", "value" => vault_key}
|
|
119
|
+
count += 1
|
|
120
|
+
cronos_spec.environmentVariables << pair
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if (! @environments_var.nil?)
|
|
125
|
+
pair = {"name" => @environments_var, "value" => app.environment}
|
|
126
|
+
cronos_spec.environmentVariables << pair
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Do subst processing
|
|
130
|
+
spec_str = do_subst(cronos_spec, app)
|
|
131
|
+
cronos_spec = JSON.parse(spec_str)
|
|
132
|
+
|
|
133
|
+
return cronos_spec
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def parse_marathon(app)
|
|
137
|
+
# TODO: check if it exists
|
|
138
|
+
marathon_spec = app.marathon_spec
|
|
139
|
+
|
|
140
|
+
# This adds environment vaiables to the spec that conform to our docker-wrapper security hack
|
|
141
|
+
if ! app.vault.nil?
|
|
142
|
+
# TODO: check that an environment was defined
|
|
143
|
+
if (! @hasEnvironments) || app.environment.nil?
|
|
144
|
+
puts "the vault feature requires an environment to be set"
|
|
145
|
+
exit 1
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
count = 1
|
|
149
|
+
marathon_spec.env ||= {}
|
|
150
|
+
app.vault.each do |vault_key|
|
|
151
|
+
# Add an environment
|
|
152
|
+
marathon_spec.env["VAULT_KEY_#{count}"] = vault_key
|
|
153
|
+
count += 1
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
if (! @environments_var.nil?)
|
|
158
|
+
marathon_spec.env[@environments_var] = app.environment
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
spec_str = do_subst(marathon_spec, app)
|
|
162
|
+
marathon_spec = JSON.parse(spec_str)
|
|
163
|
+
|
|
164
|
+
return marathon_spec
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def should_deploy?(app)
|
|
168
|
+
result = true
|
|
169
|
+
if (app.kind == "Marathon") && @options[:deploy_type] == 'chronos'
|
|
170
|
+
result = false
|
|
171
|
+
end
|
|
172
|
+
if (app.kind == "Chronos") && @options[:deploy_type] == 'marathon'
|
|
173
|
+
result = false
|
|
174
|
+
end
|
|
175
|
+
if @options[:env_type] != 'all'
|
|
176
|
+
if @options[:env_type] != app.environment
|
|
177
|
+
result = false
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
return result
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def do_subst(spec, app)
|
|
185
|
+
# {{ENV}} is a special value
|
|
186
|
+
spec_str = spec.to_json.to_s.gsub(/{{ENV}}/, app.environment)
|
|
187
|
+
|
|
188
|
+
if ! @options[:subst].nil?
|
|
189
|
+
@options[:subst].split(",").each do |x|
|
|
190
|
+
pair = x.split("=")
|
|
191
|
+
spec_str = spec_str.gsub(/{{#{pair[0]}}}/, pair[1])
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
return spec_str
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def check_option_syntax
|
|
199
|
+
if @dry_run
|
|
200
|
+
return
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if ! ['chronos', 'marathon', 'all'].include?(@options[:deploy_type])
|
|
204
|
+
puts "value of --deploy-type was #{@options[:deploy_type]}, must be chronos, marathon or all"
|
|
205
|
+
exit 1
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
data/orch.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'orch/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "orch"
|
|
8
|
+
spec.version = Orch::VERSION
|
|
9
|
+
spec.authors = ["Ray Johnson"]
|
|
10
|
+
spec.email = ["rjohnson@yp.com"]
|
|
11
|
+
spec.summary = %q{orch uses yaml to deploy to Mesos' Marathon and Chronos frameworks}
|
|
12
|
+
spec.description = %q{orch uses yaml to deploy to Mesos' Marathon and Chronos frameworks}
|
|
13
|
+
spec.homepage = "https://github.com/rayjohnson/orch"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
22
|
+
spec.add_development_dependency "rake"
|
|
23
|
+
|
|
24
|
+
spec.add_dependency 'thor', '~> 0.18'
|
|
25
|
+
spec.add_dependency 'hashie'
|
|
26
|
+
|
|
27
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: orch
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ray Johnson
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-08-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.6'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.6'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: thor
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ~>
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.18'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.18'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: hashie
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - '>='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
description: orch uses yaml to deploy to Mesos' Marathon and Chronos frameworks
|
|
70
|
+
email:
|
|
71
|
+
- rjohnson@yp.com
|
|
72
|
+
executables:
|
|
73
|
+
- orch
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- .gitignore
|
|
78
|
+
- Gemfile
|
|
79
|
+
- LICENSE.txt
|
|
80
|
+
- README.md
|
|
81
|
+
- Rakefile
|
|
82
|
+
- bin/orch
|
|
83
|
+
- examples/env-chronos.yml
|
|
84
|
+
- examples/simple-chronos.yml
|
|
85
|
+
- examples/simple-marathon.yml
|
|
86
|
+
- lib/config.rb
|
|
87
|
+
- lib/deploy.rb
|
|
88
|
+
- lib/orch.rb
|
|
89
|
+
- lib/orch/version.rb
|
|
90
|
+
- lib/parse.rb
|
|
91
|
+
- orch.gemspec
|
|
92
|
+
homepage: https://github.com/rayjohnson/orch
|
|
93
|
+
licenses:
|
|
94
|
+
- MIT
|
|
95
|
+
metadata: {}
|
|
96
|
+
post_install_message:
|
|
97
|
+
rdoc_options: []
|
|
98
|
+
require_paths:
|
|
99
|
+
- lib
|
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - '>='
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '0'
|
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - '>='
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
requirements: []
|
|
111
|
+
rubyforge_project:
|
|
112
|
+
rubygems_version: 2.4.6
|
|
113
|
+
signing_key:
|
|
114
|
+
specification_version: 4
|
|
115
|
+
summary: orch uses yaml to deploy to Mesos' Marathon and Chronos frameworks
|
|
116
|
+
test_files: []
|