provisioning 0.0.1.alpha.3 → 0.0.1.alpha.4
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 +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
|