provisioning 0.0.1.alpha.3 → 0.0.1.alpha.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -26
- data/lib/provisioning.rb +6 -0
- data/lib/provisioning/cli.rb +81 -37
- data/lib/provisioning/compute/aws.rb +7 -8
- data/lib/provisioning/compute/base.rb +23 -0
- data/lib/provisioning/compute/digitalocean.rb +5 -7
- data/lib/provisioning/core.rb +15 -0
- data/lib/provisioning/dns/aws.rb +54 -0
- data/lib/provisioning/dns/base.rb +25 -0
- data/lib/provisioning/dns/digitalocean.rb +22 -13
- data/lib/provisioning/platform/base.rb +31 -0
- data/lib/provisioning/platform/dokku.rb +28 -26
- data/lib/provisioning/platform/flynn.rb +74 -0
- data/lib/provisioning/version.rb +1 -1
- metadata +29 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 933806e5f54e7f91d0c47aa40f1ccebebdc2623c
|
4
|
+
data.tar.gz: a4178fef2186d84051a44985379023c56626d700
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd72fdf87aa3ae6189da7fd8bb10aa207fd4785a480f54dd64caede766abe0ac342bcccc5bc993e60a28cf0abb2dffbc57bbcc95233b9325467835ee9b2f91f3
|
7
|
+
data.tar.gz: 8caf109b025a5b3339b072f87fb90f41b609f87e3e7572ccc94572c066db23c114e5fd65d976ade15221531177fa7ceed211569f11922d7c24dd51396487d252
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Open PaaS provisioning.
|
|
6
6
|
This gem will install a `provision` command for provisioning an open
|
7
7
|
platform-as-a-service (PaaS) from a JSON manifest file.
|
8
8
|
|
9
|
-
Currently limited to dokku apps on AWS or DigitalOcean servers.
|
9
|
+
Currently limited to dokku or flynn apps on AWS or DigitalOcean servers.
|
10
10
|
|
11
11
|
|
12
12
|
Installation
|
@@ -16,7 +16,7 @@ Installation
|
|
16
16
|
|
17
17
|
Alternatively you can build the gem from its repository:
|
18
18
|
|
19
|
-
$ git clone git://github.com/vinc/provisioning.git
|
19
|
+
$ git clone git://github.com/vinc/provisioning.rb.git
|
20
20
|
$ cd provisioning
|
21
21
|
$ gem build provisioning.gemspec
|
22
22
|
$ gem install provisioning-0.0.1.gem
|
@@ -25,33 +25,31 @@ Alternatively you can build the gem from its repository:
|
|
25
25
|
Usage
|
26
26
|
-----
|
27
27
|
|
28
|
-
|
28
|
+
Provision a manifest file:
|
29
29
|
|
30
|
-
$ provision manifest.json
|
30
|
+
$ provision manifest.sample.json
|
31
|
+
Reading provisioning manifest file 'manifest.sample.json'
|
32
|
+
==> Provisioning digitalocean compute
|
31
33
|
Uploading SSH key to DigitalOcean
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
Creating domain '
|
36
|
-
Configue '
|
37
|
-
- ns1.
|
38
|
-
- ns2.
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
Installing dokku v0.10.4 on '192.168.13.37'
|
47
|
-
Run `gem install dokku-cli` to get dokku client on your machine
|
48
|
-
|
49
|
-
Creating dokku app 'example' on '192.168.13.37'
|
50
|
-
|
34
|
+
Creating server 'dokku1.sfo1.example.net'
|
35
|
+
==> Provisioning digitalocean dns
|
36
|
+
Creating zone 'sfo1.example.net'
|
37
|
+
Creating domain record A '@' to '104.131.186.241'
|
38
|
+
Configue 'sfo1.example.net' with the following DNS servers:
|
39
|
+
- ns1.example.net
|
40
|
+
- ns2.example.net
|
41
|
+
Creating domain record CNAME '*' to 'sfo1.example.net.'
|
42
|
+
Configue 'example.com' to point to 'sfo1.example.net'
|
43
|
+
Configue 'www.example.com' to point to 'sfo1.example.net'
|
44
|
+
==> Provisioning dokku platform
|
45
|
+
Installing dokku v0.10.4 on '104.131.186.241'
|
46
|
+
Creating dokku app 'example' on '104.131.186.241'
|
47
|
+
Run `gem install dokku-cli` to get dokku client on your computer
|
51
48
|
Adding dokku to git remotes
|
49
|
+
Uploading SSH key to DigitalOcean
|
52
50
|
Run `git push dokku master` to deploy your code
|
53
51
|
|
54
|
-
|
52
|
+
Sample manifest file (in JSON format)
|
55
53
|
|
56
54
|
```json
|
57
55
|
{
|
@@ -63,8 +61,8 @@ Provisioning manifest json file:
|
|
63
61
|
},
|
64
62
|
"platform": {
|
65
63
|
"provider": "dokku",
|
66
|
-
"
|
67
|
-
"
|
64
|
+
"version": "v0.10.4",
|
65
|
+
"domain": "sfo1.example.net"
|
68
66
|
},
|
69
67
|
"compute": {
|
70
68
|
"provider": "digitalocean",
|
data/lib/provisioning.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require "object"
|
2
|
+
require "provisioning/core"
|
2
3
|
require "provisioning/cli"
|
4
|
+
require "provisioning/compute/base"
|
3
5
|
require "provisioning/compute/aws"
|
4
6
|
require "provisioning/compute/digitalocean"
|
5
7
|
require "provisioning/console"
|
8
|
+
require "provisioning/dns/base"
|
9
|
+
require "provisioning/dns/aws"
|
6
10
|
require "provisioning/dns/digitalocean"
|
11
|
+
require "provisioning/platform/base"
|
7
12
|
require "provisioning/platform/dokku"
|
13
|
+
require "provisioning/platform/flynn"
|
8
14
|
require "provisioning/public_key"
|
9
15
|
require "provisioning/version"
|
10
16
|
|
data/lib/provisioning/cli.rb
CHANGED
@@ -26,10 +26,11 @@ module Provisioning
|
|
26
26
|
set_instance_variable_from_manifest(%w[compute provider])
|
27
27
|
set_instance_variable_from_manifest(%w[dns provider])
|
28
28
|
|
29
|
-
@
|
30
|
-
@server_hostname = [@platform_provider, @platform_domain].join(".")
|
29
|
+
@servers = []
|
31
30
|
|
32
|
-
|
31
|
+
key_path = File.join(@env["HOME"], ".ssh/id_rsa.pub")
|
32
|
+
key_body = @env["SSH_PUBLIC_KEY"] || File.open(key_path).read
|
33
|
+
@ssh_key = PublicKey.new(key_body)
|
33
34
|
end
|
34
35
|
|
35
36
|
def parse_opts(args)
|
@@ -48,58 +49,84 @@ module Provisioning
|
|
48
49
|
provision_dns
|
49
50
|
provision_platform
|
50
51
|
add_git_remote
|
52
|
+
# TODO: return false when an error occur
|
51
53
|
end
|
52
54
|
|
53
55
|
def provision_compute
|
54
|
-
|
55
|
-
compute = klass.new(@manifest["compute"], @opts, @env)
|
56
|
+
compute = provider("compute")
|
56
57
|
|
57
58
|
compute.upload_ssh_key(@ssh_key)
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
n = (@manifest["compute"]["count"] || 1).to_i
|
61
|
+
@servers = n.times.map do |i|
|
62
|
+
compute.find_or_create_server(
|
63
|
+
name: "#{@platform_provider}#{i + 1}.#{@platform_domain}",
|
64
|
+
ssh_key: @ssh_key
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
@servers.each do |server|
|
69
|
+
server.wait_for { ready? }
|
70
|
+
end
|
71
|
+
sleep 5
|
65
72
|
end
|
66
73
|
|
67
74
|
def provision_dns
|
68
|
-
|
69
|
-
dns = klass.new(@manifest["dns"], @opts, @env)
|
75
|
+
dns = provider("dns")
|
70
76
|
|
71
|
-
|
77
|
+
cluster = @servers.map(&:public_ip_address)
|
78
|
+
|
79
|
+
if @dns_provider == "digitalocean"
|
80
|
+
dns.create_zone(@platform_domain, cluster.shift)
|
81
|
+
else
|
82
|
+
dns.create_zone(@platform_domain)
|
83
|
+
end
|
72
84
|
Console.success("Configue '#{@platform_domain}' with the following DNS servers:")
|
73
|
-
dns.
|
85
|
+
dns.get_name_servers(@platform_domain).each do |hostname|
|
74
86
|
Console.success(" - #{hostname}")
|
75
87
|
end
|
76
88
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
89
|
+
if cluster.count > 0
|
90
|
+
dns.create_record(@platform_domain,
|
91
|
+
type: "A",
|
92
|
+
name: [@platform_domain, ""].join("."),
|
93
|
+
value: cluster
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
@servers.each_with_index do |server, i|
|
98
|
+
dns.create_record(@platform_domain,
|
99
|
+
type: "A",
|
100
|
+
name: "#{@platform_provider}#{i + 1}.#{@platform_domain}",
|
101
|
+
value: server.public_ip_address
|
102
|
+
)
|
103
|
+
end
|
83
104
|
|
84
|
-
|
85
|
-
|
105
|
+
# Wilcard subdomains
|
106
|
+
dns.create_record(@platform_domain,
|
86
107
|
type: "CNAME",
|
87
|
-
name: @
|
88
|
-
|
108
|
+
name: ["*", @platform_domain, ""].join("."),
|
109
|
+
value: [@platform_domain, ""].join(".")
|
89
110
|
)
|
90
111
|
|
91
112
|
@manifest["app"]["domains"].each do |app_domain|
|
92
|
-
Console.success("Configue '#{app_domain}' to point to '#{@
|
113
|
+
Console.success("Configue '#{app_domain}' to point to '#{@platform_domain}'")
|
93
114
|
end
|
94
115
|
end
|
95
116
|
|
96
117
|
def provision_platform
|
97
|
-
|
98
|
-
|
118
|
+
platform = provider("platform")
|
119
|
+
|
120
|
+
@servers.each do |server|
|
121
|
+
platform.setup(
|
122
|
+
address: server.public_ip_address,
|
123
|
+
user: @compute_provider == "aws" ? "ubuntu" : "root"
|
124
|
+
)
|
125
|
+
end
|
99
126
|
|
100
|
-
platform.setup(address: @server_address, domain: @platform_domain)
|
101
127
|
platform.create_app(@manifest["app"])
|
102
128
|
|
129
|
+
# TODO: add `platform.get_post_install_instructions`
|
103
130
|
case @platform_provider
|
104
131
|
when "dokku"
|
105
132
|
Console.success("Run `gem install dokku-cli` to get dokku client on your computer")
|
@@ -119,11 +146,14 @@ module Provisioning
|
|
119
146
|
else
|
120
147
|
case @platform_provider
|
121
148
|
when "dokku"
|
122
|
-
url = "#{@platform_provider}@#{@
|
149
|
+
url = "#{@platform_provider}@#{@platform_domain}:#{@app_name}"
|
150
|
+
git.add_remote(@platform_provider, url)
|
151
|
+
when "flynn"
|
152
|
+
url = "https://git.#{@platform_domain}/#{@app_name}.git"
|
123
153
|
git.add_remote(@platform_provider, url)
|
124
154
|
end
|
125
155
|
end
|
126
|
-
Console.success("Run `git push
|
156
|
+
Console.success("Run `git push #{@platform_provider} master` to deploy your code")
|
127
157
|
end
|
128
158
|
end
|
129
159
|
|
@@ -131,19 +161,16 @@ module Provisioning
|
|
131
161
|
Console.info("Reading provisioning manifest file '#{filename}'")
|
132
162
|
begin
|
133
163
|
json = JSON.parse(File.open(filename).read)
|
134
|
-
rescue Errno::ENOENT
|
164
|
+
rescue Errno::ENOENT
|
135
165
|
Console.error("Could not read provisioning manifest file '#{filename}'")
|
166
|
+
rescue JSON::ParserError
|
167
|
+
Console.error("Could not parse provisioning manifest file '#{filename}'")
|
136
168
|
end
|
137
169
|
json["manifest"]
|
138
170
|
end
|
139
171
|
|
140
172
|
private
|
141
173
|
|
142
|
-
def set_instance_variable_from_manifest(keys)
|
143
|
-
name = "@" + keys.join("_")
|
144
|
-
instance_variable_set(name, dig_manifest(keys))
|
145
|
-
end
|
146
|
-
|
147
174
|
def dig_manifest(keys)
|
148
175
|
path = []
|
149
176
|
hash = @manifest
|
@@ -153,5 +180,22 @@ module Provisioning
|
|
153
180
|
end
|
154
181
|
hash
|
155
182
|
end
|
183
|
+
|
184
|
+
def set_instance_variable_from_manifest(keys)
|
185
|
+
name = "@" + keys.join("_")
|
186
|
+
instance_variable_set(name, dig_manifest(keys))
|
187
|
+
end
|
188
|
+
|
189
|
+
def provider(type)
|
190
|
+
provider = instance_variable_get("@#{type}_provider")
|
191
|
+
|
192
|
+
Console.info("==> Provisioning #{provider} #{type}")
|
193
|
+
|
194
|
+
klass = Provisioning.
|
195
|
+
const_get(type.capitalize).
|
196
|
+
const_get(provider.capitalize)
|
197
|
+
|
198
|
+
klass.new(@manifest[type], @opts, @env)
|
199
|
+
end
|
156
200
|
end
|
157
201
|
end
|
@@ -4,18 +4,16 @@ require "provisioning"
|
|
4
4
|
|
5
5
|
module Provisioning
|
6
6
|
module Compute
|
7
|
-
class Aws
|
8
|
-
KEY_NAME = "provisioning key".freeze
|
9
|
-
|
7
|
+
class Aws < Base
|
10
8
|
def initialize(config, opts, env)
|
11
9
|
@config = config
|
12
10
|
@opts = opts
|
13
11
|
Fog.mock! if @opts[:mock]
|
14
12
|
@client = Fog::Compute.new(
|
15
13
|
provider: "aws",
|
16
|
-
region:
|
17
|
-
aws_access_key_id:
|
18
|
-
aws_secret_access_key:
|
14
|
+
region: fetch("region", config: @config),
|
15
|
+
aws_access_key_id: fetch("AWS_ACCESS_KEY_ID", env: env),
|
16
|
+
aws_secret_access_key: fetch("AWS_SECRET_ACCESS_KEY", env: env)
|
19
17
|
)
|
20
18
|
end
|
21
19
|
|
@@ -38,9 +36,10 @@ module Provisioning
|
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
39
|
+
# TODO: set instance name instead of tagging it
|
41
40
|
@client.servers.create(
|
42
|
-
image_id:
|
43
|
-
flavor_id:
|
41
|
+
image_id: fetch("image_id", config: @config),
|
42
|
+
flavor_id: fetch("flavor_id", config: @config),
|
44
43
|
tags: { name: name },
|
45
44
|
key_name: KEY_NAME
|
46
45
|
)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "provisioning"
|
2
|
+
|
3
|
+
module Provisioning
|
4
|
+
module Compute
|
5
|
+
class Base
|
6
|
+
include Provisioning::Core
|
7
|
+
|
8
|
+
KEY_NAME = "provisioning key".freeze
|
9
|
+
|
10
|
+
def initialize(config, opts, env)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def upload_ssh_key(ssh_key)
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_or_create_server(name:, ssh_key:)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -4,16 +4,14 @@ require "provisioning"
|
|
4
4
|
|
5
5
|
module Provisioning
|
6
6
|
module Compute
|
7
|
-
class Digitalocean
|
8
|
-
KEY_NAME = "provisioning key".freeze
|
9
|
-
|
7
|
+
class Digitalocean < Base
|
10
8
|
def initialize(config, opts, env)
|
11
9
|
@config = config
|
12
10
|
@opts = opts
|
13
11
|
Fog.mock! if @opts[:mock]
|
14
12
|
@client = Fog::Compute.new(
|
15
13
|
provider: :digitalocean,
|
16
|
-
digitalocean_token:
|
14
|
+
digitalocean_token: fetch("DIGITALOCEAN_TOKEN", env: env)
|
17
15
|
)
|
18
16
|
end
|
19
17
|
|
@@ -41,9 +39,9 @@ module Provisioning
|
|
41
39
|
|
42
40
|
@client.servers.create(
|
43
41
|
name: name,
|
44
|
-
region:
|
45
|
-
image:
|
46
|
-
size:
|
42
|
+
region: fetch("region", config: @config),
|
43
|
+
image: fetch("image", config: @config),
|
44
|
+
size: fetch("size", config: @config),
|
47
45
|
ssh_keys: [ssh_key.fingerprint]
|
48
46
|
)
|
49
47
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "provisioning"
|
2
|
+
|
3
|
+
module Provisioning
|
4
|
+
module Core
|
5
|
+
def fetch(key, default = nil, env: nil, config: nil)
|
6
|
+
if env
|
7
|
+
env[key] || default || Console.error("Could not find '#{key}' in environment")
|
8
|
+
elsif config
|
9
|
+
config[key] || default || Console.error("Could not find '#{key}' in manifest")
|
10
|
+
else
|
11
|
+
# TODO: env or config required
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "fog/aws"
|
2
|
+
|
3
|
+
require "provisioning"
|
4
|
+
|
5
|
+
module Provisioning
|
6
|
+
module Dns
|
7
|
+
class Aws < Base
|
8
|
+
def initialize(config, opts, env)
|
9
|
+
@config = config
|
10
|
+
@opts = opts
|
11
|
+
Fog.mock! if @opts[:mock]
|
12
|
+
@client = Fog::DNS.new(
|
13
|
+
provider: "aws",
|
14
|
+
aws_access_key_id: fetch("AWS_ACCESS_KEY_ID", env: env),
|
15
|
+
aws_secret_access_key: fetch("AWS_SECRET_ACCESS_KEY", env: env)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_zone(domain)
|
20
|
+
Console.info("Creating zone '#{domain}'")
|
21
|
+
|
22
|
+
zone = get_zone(domain)
|
23
|
+
if zone
|
24
|
+
Console.warning("Domain already exists, skipping")
|
25
|
+
else
|
26
|
+
zone = @client.zones.create(domain: domain)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_record(domain, type:, name:, value:)
|
31
|
+
value = value.is_a?(Array) ? value : [value]
|
32
|
+
value_to_s = value.join(", ")
|
33
|
+
Console.info("Creating domain record #{type} '#{name}' to '#{value_to_s}'")
|
34
|
+
|
35
|
+
zone = get_zone(domain)
|
36
|
+
if zone.records.get(name, type).try(:value) == value
|
37
|
+
Console.warning("Record already exists, skipping")
|
38
|
+
else
|
39
|
+
zone.records.create(type: type, name: name, value: value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_name_servers(domain)
|
44
|
+
get_zone(domain).reload.nameservers || []
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def get_zone(domain)
|
50
|
+
@client.zones.all.find { |z| z.domain == domain + "." }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "provisioning"
|
2
|
+
|
3
|
+
module Provisioning
|
4
|
+
module Dns
|
5
|
+
class Base
|
6
|
+
include Provisioning::Core
|
7
|
+
|
8
|
+
def initialize(config, opts, env)
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_zone(domain)
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_record(domain, type:, name:, data:)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_name_servers(domain)
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,24 +3,25 @@ require "droplet_kit"
|
|
3
3
|
require "provisioning"
|
4
4
|
|
5
5
|
module Provisioning
|
6
|
-
module
|
7
|
-
class Digitalocean
|
6
|
+
module Dns
|
7
|
+
class Digitalocean < Base
|
8
8
|
def initialize(config, opts, env)
|
9
9
|
@config = config
|
10
10
|
@opts = opts
|
11
11
|
@client = DropletKit::Client.new(
|
12
|
-
access_token:
|
12
|
+
access_token: fetch("DIGITALOCEAN_TOKEN", env: env)
|
13
13
|
) unless @opts[:mock]
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
Console.info("Creating
|
16
|
+
def create_zone(name, address)
|
17
|
+
Console.info("Creating zone '#{name}'")
|
18
18
|
|
19
19
|
disable_verbose
|
20
20
|
return if @opts[:mock]
|
21
21
|
if @client.domains.all.map(&:name).include?(name)
|
22
22
|
Console.warning("Domain already exists, skipping")
|
23
23
|
else
|
24
|
+
Console.info("Creating domain record A '@' to '#{address}'")
|
24
25
|
domain = DropletKit::Domain.new(name: name, ip_address: address)
|
25
26
|
@client.domains.create(domain)
|
26
27
|
end
|
@@ -28,22 +29,30 @@ module Provisioning
|
|
28
29
|
restore_verbose
|
29
30
|
end
|
30
31
|
|
31
|
-
def
|
32
|
-
|
32
|
+
def create_record(domain, type:, name:, value:)
|
33
|
+
name = name.gsub("#{domain}.", "@").gsub(".@", "")
|
34
|
+
value = value.is_a?(Array) ? value : [value]
|
35
|
+
value_to_s = value.join(", ")
|
36
|
+
Console.info("Creating domain record #{type} '#{name}' to '#{value_to_s}'")
|
33
37
|
|
34
38
|
disable_verbose
|
35
39
|
return if @opts[:mock]
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
|
41
|
+
records = @client.domain_records.all(for_domain: domain)
|
42
|
+
value.each do |data|
|
43
|
+
data = data.gsub("#{domain}.", "@")
|
44
|
+
if records.any? { |r| r.name == name && r.data == data }
|
45
|
+
Console.warning("Record already exists, skipping")
|
46
|
+
else
|
47
|
+
record = DropletKit::DomainRecord.new(type: type, name: name, data: data)
|
48
|
+
@client.domain_records.create(record, for_domain: domain)
|
49
|
+
end
|
41
50
|
end
|
42
51
|
ensure
|
43
52
|
restore_verbose
|
44
53
|
end
|
45
54
|
|
46
|
-
def
|
55
|
+
def get_name_servers(domain)
|
47
56
|
disable_verbose
|
48
57
|
return %w[ns1.example.net ns2.example.net] if @opts[:mock]
|
49
58
|
@client.domain_records.all(for_domain: domain).
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "provisioning"
|
2
|
+
|
3
|
+
module Provisioning
|
4
|
+
module Platform
|
5
|
+
class Base
|
6
|
+
include Provisioning::Core
|
7
|
+
|
8
|
+
def initialize(config, opts, env)
|
9
|
+
@opts = opts
|
10
|
+
@config = config
|
11
|
+
@servers = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup(address:, user: "root")
|
15
|
+
@servers << [address, user]
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_app(config)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def ssh_exec(ssh, cmd, user: "root")
|
26
|
+
cmd = "sudo bash -c '#{cmd}'" if user != "root"
|
27
|
+
ssh.exec!(cmd)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -4,58 +4,60 @@ require "provisioning"
|
|
4
4
|
|
5
5
|
module Provisioning
|
6
6
|
module Platform
|
7
|
-
class Dokku
|
8
|
-
def
|
9
|
-
@
|
10
|
-
|
11
|
-
@servers = []
|
12
|
-
end
|
13
|
-
|
14
|
-
def setup(address:, domain:)
|
15
|
-
@servers << address
|
16
|
-
version = @config["version"]
|
7
|
+
class Dokku < Base
|
8
|
+
def setup(address:, user: "root")
|
9
|
+
@servers << [address, user]
|
10
|
+
version = fetch("version", config: @config)
|
17
11
|
Console.info("Installing dokku #{version} on '#{address}'")
|
18
12
|
return if @opts[:mock]
|
19
|
-
Net::SSH.start(address,
|
20
|
-
if ssh
|
13
|
+
Net::SSH.start(address, user) do |ssh|
|
14
|
+
if ssh_exec(ssh, "which dokku", user: user).present?
|
21
15
|
Console.warning("Dokku already installed, skipping")
|
22
16
|
else
|
17
|
+
# TODO: configure hostname
|
18
|
+
domain = fetch("domain", config: @config)
|
23
19
|
[
|
24
20
|
"wget https://raw.githubusercontent.com/dokku/dokku/#{version}/bootstrap.sh",
|
25
21
|
"DOKKU_TAG=#{version} bash bootstrap.sh",
|
26
22
|
"service dokku-installer stop",
|
27
23
|
"systemctl disable dokku-installer",
|
28
24
|
"cat .ssh/authorized_keys | sshcommand acl-add dokku admin",
|
29
|
-
"echo
|
30
|
-
"echo
|
31
|
-
].each { |
|
25
|
+
"echo #{domain} > /home/dokku/VHOST",
|
26
|
+
"echo #{domain} > /home/dokku/HOSTNAME"
|
27
|
+
].each { |cmd| Console.debug(ssh_exec(ssh, cmd, user: user)) }
|
32
28
|
end
|
33
29
|
end
|
34
30
|
end
|
35
31
|
|
36
32
|
def create_app(config)
|
37
|
-
name =
|
38
|
-
@servers.each do |address|
|
33
|
+
name = fetch("name", config: config)
|
34
|
+
@servers.each do |address, user|
|
39
35
|
Console.info("Creating dokku app '#{name}' on '#{address}'")
|
40
|
-
|
41
|
-
Net::SSH.start(address,
|
42
|
-
existing_apps = ssh
|
36
|
+
next if @opts[:mock]
|
37
|
+
Net::SSH.start(address, user) do |ssh|
|
38
|
+
existing_apps = ssh_exec(ssh, "dokku apps", user: user).to_s.lines.map(&:chomp)
|
43
39
|
if existing_apps.include?(name)
|
44
40
|
Console.warning("App already exists, skipping")
|
45
41
|
else
|
46
|
-
Console.debug(ssh
|
42
|
+
Console.debug(ssh_exec(ssh, "dokku apps:create #{name}", user: user))
|
47
43
|
|
48
|
-
|
49
|
-
|
44
|
+
fetch("services", [], config: config).each do |service|
|
45
|
+
if @servers.count > 1
|
46
|
+
Console.warning("Service '#{service}' for dokku wont work in a cluster configuration")
|
47
|
+
end
|
48
|
+
# TODO: check if service exists
|
50
49
|
[
|
51
50
|
"dokku plugin:install https://github.com/dokku/dokku-#{service}.git #{service}",
|
52
51
|
"dokku #{service}:create #{name}-#{service}",
|
53
52
|
"dokku #{service}:link #{name}-#{service} #{name}"
|
54
|
-
].each { |
|
53
|
+
].each { |cmd| Console.debug(ssh_exec(ssh, cmd, user: user)) }
|
55
54
|
end
|
56
55
|
|
57
|
-
domains
|
58
|
-
|
56
|
+
fetch("domains", [], config: config).each do |domain|
|
57
|
+
# This will replace default subdomain 'foo.example.com' in /dokku/foo/VHOST
|
58
|
+
# because the app has not been deployed once yet.
|
59
|
+
Console.debug(ssh_exec(ssh, "dokku domains:add #{name} #{domain}", user: user))
|
60
|
+
end
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "net/ssh"
|
2
|
+
|
3
|
+
require "provisioning"
|
4
|
+
|
5
|
+
module Provisioning
|
6
|
+
module Platform
|
7
|
+
class Flynn < Base
|
8
|
+
def setup(address:, user: "root")
|
9
|
+
Console.info("Installing flynn on '#{address}'")
|
10
|
+
@servers << [address, user]
|
11
|
+
return if @opts[:mock]
|
12
|
+
Net::SSH.start(address, user) do |ssh|
|
13
|
+
if ssh_exec(ssh, "which flynn", user: user).present?
|
14
|
+
Console.warning("Flynn already installed, skipping")
|
15
|
+
else
|
16
|
+
# Install flynn
|
17
|
+
ssh_exec(ssh, "bash < <(curl -fsSL https://dl.flynn.io/install-flynn)", user: user)
|
18
|
+
|
19
|
+
# Init cluster
|
20
|
+
if @servers.count == 1
|
21
|
+
@token = ssh_exec(ssh, "flynn-host init --init-discovery", user: user)
|
22
|
+
else
|
23
|
+
ssh_exec(ssh, "flynn-host init --discovery #{@token}", user: user)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Start flynn
|
27
|
+
ssh_exec(ssh, "systemctl start flynn-host", user: user)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_app(config)
|
33
|
+
name = fetch("name", config: config)
|
34
|
+
(address, user) = @servers.first
|
35
|
+
|
36
|
+
Console.info("Creating flynn app '#{name}' on '#{address}'")
|
37
|
+
return if @opts[:mock]
|
38
|
+
Net::SSH.start(address, user) do |ssh|
|
39
|
+
out = ssh_exec(ssh, "flynn apps | awk '{ print $2 }'", user: user)
|
40
|
+
existing_apps = out.lines.map(&:chomp)
|
41
|
+
if existing_apps.include?(name)
|
42
|
+
Console.warning("App already exists, skipping")
|
43
|
+
else
|
44
|
+
cmds = []
|
45
|
+
|
46
|
+
# Bootstrap flynn
|
47
|
+
platform_domain = fetch("domain", config: @config)
|
48
|
+
bootstrap = "CLUSTER_DOMAIN=#{platform_domain} flynn-host bootstrap"
|
49
|
+
bootstrap += " --min-hosts 3 --discovery #{@token}" if @token && @servers.count >= 3
|
50
|
+
cmds << bootstrap
|
51
|
+
|
52
|
+
# Configure flynn command
|
53
|
+
out = ssh_exec(ssh, "flynn-host cli-add-command", user: user)
|
54
|
+
cmds << out.lines.map(&:chomp).find do |line|
|
55
|
+
line.start_with?("flynn cluster add")
|
56
|
+
end
|
57
|
+
|
58
|
+
cmds << "flynn create #{name}"
|
59
|
+
|
60
|
+
fetch("services", [], config: config).each do |service|
|
61
|
+
cmds << "flynn -a #{name} resource add #{service}"
|
62
|
+
end
|
63
|
+
|
64
|
+
fetch("domains", [], config: config).each do |domain|
|
65
|
+
cmds << "flynn -a #{name} route add http #{domain}"
|
66
|
+
end
|
67
|
+
|
68
|
+
cmds.each { |cmd| Console.debug(ssh_exec(ssh, cmd, user: user)) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/provisioning/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: provisioning
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.alpha.
|
4
|
+
version: 0.0.1.alpha.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vincent Ollivier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: droplet_kit
|
@@ -150,6 +150,26 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 2.1.0
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rspec
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '3.6'
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: 3.6.0
|
163
|
+
type: :development
|
164
|
+
prerelease: false
|
165
|
+
version_requirements: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - "~>"
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '3.6'
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: 3.6.0
|
153
173
|
description: Open PaaS provisioning on cloud providers from JSON manifest file
|
154
174
|
email: v@vinc.cc
|
155
175
|
executables:
|
@@ -164,10 +184,16 @@ files:
|
|
164
184
|
- lib/provisioning.rb
|
165
185
|
- lib/provisioning/cli.rb
|
166
186
|
- lib/provisioning/compute/aws.rb
|
187
|
+
- lib/provisioning/compute/base.rb
|
167
188
|
- lib/provisioning/compute/digitalocean.rb
|
168
189
|
- lib/provisioning/console.rb
|
190
|
+
- lib/provisioning/core.rb
|
191
|
+
- lib/provisioning/dns/aws.rb
|
192
|
+
- lib/provisioning/dns/base.rb
|
169
193
|
- lib/provisioning/dns/digitalocean.rb
|
194
|
+
- lib/provisioning/platform/base.rb
|
170
195
|
- lib/provisioning/platform/dokku.rb
|
196
|
+
- lib/provisioning/platform/flynn.rb
|
171
197
|
- lib/provisioning/public_key.rb
|
172
198
|
- lib/provisioning/version.rb
|
173
199
|
homepage: https://github.com/vinc/provisioning.rb
|
@@ -190,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
216
|
version: 1.3.1
|
191
217
|
requirements: []
|
192
218
|
rubyforge_project:
|
193
|
-
rubygems_version: 2.6.
|
219
|
+
rubygems_version: 2.6.11
|
194
220
|
signing_key:
|
195
221
|
specification_version: 4
|
196
222
|
summary: Open PaaS provisioning
|