provisioning 0.0.1.alpha.2 → 0.0.1.alpha.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/LICENSE +21 -0
- data/README.md +86 -0
- data/bin/provision +1 -1
- data/lib/object.rb +13 -0
- data/lib/provisioning.rb +7 -2
- data/lib/provisioning/cli.rb +127 -77
- data/lib/provisioning/compute/aws.rb +50 -0
- data/lib/provisioning/compute/digitalocean.rb +52 -0
- data/lib/provisioning/console.rb +18 -4
- data/lib/provisioning/dns/digitalocean.rb +68 -0
- data/lib/provisioning/platform/dokku.rb +65 -0
- data/lib/provisioning/public_key.rb +21 -0
- data/lib/provisioning/version.rb +3 -0
- metadata +88 -21
- data/lib/provisioning/digitalocean.rb +0 -100
- data/lib/provisioning/dokku.rb +0 -58
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4c3ddbf8d9e399ad23cec152889080da86482c6
|
4
|
+
data.tar.gz: 586672476340031fa884f5e9fad0abc008811b79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdc9b54888a68f7e09483aa574eeaa9a8365676cc833ec39ae027cc0d0fcbce477150840d2f4891a3238e2b77c7dcffcf5ac9962a0e012d5e9c840cb02721668
|
7
|
+
data.tar.gz: 51b45ae173f24de850b15564a3f6ff2319d907acc7384ecc863ff6259c919cafcee4c1aad8b6197be8b1476b5c57886024abe1d2090793460044e42486393221
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Vincent Ollivier
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
Provisioning.rb
|
2
|
+
===============
|
3
|
+
|
4
|
+
Open PaaS provisioning.
|
5
|
+
|
6
|
+
This gem will install a `provision` command for provisioning an open
|
7
|
+
platform-as-a-service (PaaS) from a JSON manifest file.
|
8
|
+
|
9
|
+
Currently limited to dokku apps on AWS or DigitalOcean servers.
|
10
|
+
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
|
15
|
+
$ gem install provisioning
|
16
|
+
|
17
|
+
Alternatively you can build the gem from its repository:
|
18
|
+
|
19
|
+
$ git clone git://github.com/vinc/provisioning.git
|
20
|
+
$ cd provisioning
|
21
|
+
$ gem build provisioning.gemspec
|
22
|
+
$ gem install provisioning-0.0.1.gem
|
23
|
+
|
24
|
+
|
25
|
+
Usage
|
26
|
+
-----
|
27
|
+
|
28
|
+
Run the provisioning script:
|
29
|
+
|
30
|
+
$ provision manifest.json
|
31
|
+
Uploading SSH key to DigitalOcean
|
32
|
+
|
33
|
+
Creating server 'dokku.server.net'
|
34
|
+
|
35
|
+
Creating domain 'server.net'
|
36
|
+
Configue 'server.net' with the following DNS servers:
|
37
|
+
- ns1.digitalocean.com
|
38
|
+
- ns2.digitalocean.com
|
39
|
+
- ns3.digitalocean.com
|
40
|
+
|
41
|
+
Creating domain record A 'dokku.server.net' to '192.168.13.37'
|
42
|
+
Creating domain record CNAME 'example.server.net' to 'dokku.server.net.'
|
43
|
+
Configue 'example.com' to point to 'dokku.server.net'
|
44
|
+
Configue 'www.example.com' to point to 'dokku.server.net'
|
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
|
+
|
51
|
+
Adding dokku to git remotes
|
52
|
+
Run `git push dokku master` to deploy your code
|
53
|
+
|
54
|
+
Provisioning manifest json file:
|
55
|
+
|
56
|
+
```json
|
57
|
+
{
|
58
|
+
"manifest": {
|
59
|
+
"app": {
|
60
|
+
"name": "example",
|
61
|
+
"domains": ["example.com", "www.example.com"],
|
62
|
+
"services": ["postgres", "redis"]
|
63
|
+
},
|
64
|
+
"platform": {
|
65
|
+
"provider": "dokku",
|
66
|
+
"domain": "sfo1.example.net",
|
67
|
+
"version": "v0.10.4"
|
68
|
+
},
|
69
|
+
"compute": {
|
70
|
+
"provider": "digitalocean",
|
71
|
+
"region": "sfo1",
|
72
|
+
"image": "ubuntu-16-04-x64",
|
73
|
+
"size": "1gb"
|
74
|
+
},
|
75
|
+
"dns": {
|
76
|
+
"provider": "digitalocean"
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
```
|
81
|
+
|
82
|
+
|
83
|
+
License
|
84
|
+
-------
|
85
|
+
|
86
|
+
Copyright (C) 2017 Vincent Ollivier. Released under MIT.
|
data/bin/provision
CHANGED
data/lib/object.rb
ADDED
data/lib/provisioning.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
require "object"
|
1
2
|
require "provisioning/cli"
|
3
|
+
require "provisioning/compute/aws"
|
4
|
+
require "provisioning/compute/digitalocean"
|
2
5
|
require "provisioning/console"
|
3
|
-
require "provisioning/digitalocean"
|
4
|
-
require "provisioning/dokku"
|
6
|
+
require "provisioning/dns/digitalocean"
|
7
|
+
require "provisioning/platform/dokku"
|
8
|
+
require "provisioning/public_key"
|
9
|
+
require "provisioning/version"
|
5
10
|
|
6
11
|
module Provisioning
|
7
12
|
end
|
data/lib/provisioning/cli.rb
CHANGED
@@ -2,106 +2,156 @@ require "git"
|
|
2
2
|
require "json"
|
3
3
|
require "net/ssh"
|
4
4
|
require "rainbow"
|
5
|
+
require "trollop"
|
5
6
|
|
6
7
|
require "provisioning"
|
7
8
|
|
8
9
|
module Provisioning
|
9
|
-
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
class CLI
|
11
|
+
def initialize(args, env)
|
12
|
+
@opts = parse_opts(args)
|
13
|
+
@env = env
|
14
|
+
|
15
|
+
if @opts[:silent]
|
16
|
+
Console.silent_mode!
|
17
|
+
elsif @opts[:verbose]
|
18
|
+
Console.debug_mode!
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
json_file = args.shift || "manifest.json"
|
21
|
+
@manifest = self.class.read_manifest_file(args.shift || "manifest.json")
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
set_instance_variable_from_manifest(%w[app name])
|
24
|
+
set_instance_variable_from_manifest(%w[platform domain])
|
25
|
+
set_instance_variable_from_manifest(%w[platform provider])
|
26
|
+
set_instance_variable_from_manifest(%w[compute provider])
|
27
|
+
set_instance_variable_from_manifest(%w[dns provider])
|
28
|
+
|
29
|
+
@server_address = nil
|
30
|
+
@server_hostname = [@platform_provider, @platform_domain].join(".")
|
31
|
+
|
32
|
+
@ssh_key = PublicKey.new(@env["SSH_PUBLIC_KEY"])
|
33
|
+
end
|
26
34
|
|
27
|
-
|
35
|
+
def parse_opts(args)
|
36
|
+
Trollop::options(args) do
|
37
|
+
version "Provisioning v#{Provisioning::VERSION}"
|
38
|
+
opt :silent, "Use silent mode"
|
39
|
+
opt :verbose, "Use verbose mode"
|
40
|
+
opt :mock, "Use mock mode"
|
41
|
+
opt :help, "Show this message"
|
42
|
+
opt :version, "Print version and exit", short: "V"
|
43
|
+
end
|
44
|
+
end
|
28
45
|
|
29
|
-
|
46
|
+
def run
|
47
|
+
provision_compute
|
48
|
+
provision_dns
|
49
|
+
provision_platform
|
50
|
+
add_git_remote
|
51
|
+
end
|
30
52
|
|
31
|
-
|
32
|
-
|
53
|
+
def provision_compute
|
54
|
+
klass = Compute.const_get(@compute_provider.capitalize)
|
55
|
+
compute = klass.new(@manifest["compute"], @opts, @env)
|
33
56
|
|
34
|
-
|
35
|
-
domain = manifest["domain"]
|
36
|
-
platform = manifest["providers"]["platform"]
|
37
|
-
server_hostname = [platform, domain].join(".")
|
38
|
-
server_address = nil
|
57
|
+
compute.upload_ssh_key(@ssh_key)
|
39
58
|
|
40
|
-
|
41
|
-
|
59
|
+
server = compute.find_or_create_server(
|
60
|
+
name: @server_hostname,
|
61
|
+
ssh_key: @ssh_key
|
62
|
+
)
|
63
|
+
server.wait_for { ready? }
|
64
|
+
@server_address = server.public_ip_address
|
65
|
+
end
|
42
66
|
|
43
|
-
|
44
|
-
|
67
|
+
def provision_dns
|
68
|
+
klass = DNS.const_get(@dns_provider.capitalize)
|
69
|
+
dns = klass.new(@manifest["dns"], @opts, @env)
|
45
70
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
)
|
50
|
-
server_address = droplet.networks.v4.first.ip_address
|
51
|
-
puts
|
71
|
+
dns.create_domain(@platform_domain, @server_address)
|
72
|
+
Console.success("Configue '#{@platform_domain}' with the following DNS servers:")
|
73
|
+
dns.get_domain_name_servers(@platform_domain).each do |hostname|
|
74
|
+
Console.success(" - #{hostname}")
|
52
75
|
end
|
53
76
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
)
|
70
|
-
digitalocean.create_domain_record(
|
71
|
-
domain: domain,
|
72
|
-
type: "CNAME",
|
73
|
-
name: app_name,
|
74
|
-
data: "#{server_hostname}."
|
75
|
-
)
|
76
|
-
manifest["app"]["domains"].each do |app_domain|
|
77
|
-
Console.success("Configue '#{app_domain}' to point to '#{server_hostname}'")
|
78
|
-
end
|
79
|
-
puts
|
77
|
+
dns.create_domain_record(
|
78
|
+
domain: @platform_domain,
|
79
|
+
type: "A",
|
80
|
+
name: @platform_provider,
|
81
|
+
data: @server_address
|
82
|
+
)
|
83
|
+
|
84
|
+
dns.create_domain_record(
|
85
|
+
domain: @platform_domain,
|
86
|
+
type: "CNAME",
|
87
|
+
name: @app_name,
|
88
|
+
data: @server_hostname + "."
|
89
|
+
)
|
90
|
+
|
91
|
+
@manifest["app"]["domains"].each do |app_domain|
|
92
|
+
Console.success("Configue '#{app_domain}' to point to '#{@server_hostname}'")
|
80
93
|
end
|
94
|
+
end
|
81
95
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
Console.success("Run `gem install dokku-cli` to get dokku client on your machine")
|
86
|
-
puts
|
96
|
+
def provision_platform
|
97
|
+
klass = Platform.const_get(@platform_provider.capitalize)
|
98
|
+
platform = klass.new(@manifest["platform"], @opts, @env)
|
87
99
|
|
88
|
-
|
89
|
-
|
100
|
+
platform.setup(address: @server_address, domain: @platform_domain)
|
101
|
+
platform.create_app(@manifest["app"])
|
90
102
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
103
|
+
case @platform_provider
|
104
|
+
when "dokku"
|
105
|
+
Console.success("Run `gem install dokku-cli` to get dokku client on your computer")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_git_remote
|
110
|
+
Console.info("Adding #{@platform_provider} to git remotes")
|
111
|
+
return if @opts[:mock]
|
112
|
+
begin
|
113
|
+
git = Git.open(".")
|
114
|
+
rescue ArgumentError
|
115
|
+
Console.warning("Not a git repository, skipping")
|
116
|
+
else
|
117
|
+
if git.remotes.map(&:name).include?(@platform_provider)
|
118
|
+
Console.warning("Remote already exists, skipping")
|
96
119
|
else
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
git.add_remote(
|
120
|
+
case @platform_provider
|
121
|
+
when "dokku"
|
122
|
+
url = "#{@platform_provider}@#{@server_hostname}:#{@app_name}"
|
123
|
+
git.add_remote(@platform_provider, url)
|
101
124
|
end
|
102
|
-
Console.success("Run `git push dokku master` to deploy your code")
|
103
125
|
end
|
126
|
+
Console.success("Run `git push dokku master` to deploy your code")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.read_manifest_file(filename)
|
131
|
+
Console.info("Reading provisioning manifest file '#{filename}'")
|
132
|
+
begin
|
133
|
+
json = JSON.parse(File.open(filename).read)
|
134
|
+
rescue Errno::ENOENT, JSON::ParserError
|
135
|
+
Console.error("Could not read provisioning manifest file '#{filename}'")
|
136
|
+
end
|
137
|
+
json["manifest"]
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def set_instance_variable_from_manifest(keys)
|
143
|
+
name = "@" + keys.join("_")
|
144
|
+
instance_variable_set(name, dig_manifest(keys))
|
145
|
+
end
|
146
|
+
|
147
|
+
def dig_manifest(keys)
|
148
|
+
path = []
|
149
|
+
hash = @manifest
|
150
|
+
keys.each do |key|
|
151
|
+
path << key
|
152
|
+
hash = hash[key] || Console.error("Could not find #{path.join(".")} in manifest")
|
104
153
|
end
|
154
|
+
hash
|
105
155
|
end
|
106
156
|
end
|
107
157
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "fog/aws"
|
2
|
+
|
3
|
+
require "provisioning"
|
4
|
+
|
5
|
+
module Provisioning
|
6
|
+
module Compute
|
7
|
+
class Aws
|
8
|
+
KEY_NAME = "provisioning key".freeze
|
9
|
+
|
10
|
+
def initialize(config, opts, env)
|
11
|
+
@config = config
|
12
|
+
@opts = opts
|
13
|
+
Fog.mock! if @opts[:mock]
|
14
|
+
@client = Fog::Compute.new(
|
15
|
+
provider: "aws",
|
16
|
+
region: @config["region"],
|
17
|
+
aws_access_key_id: env["AWS_ACCESS_KEY_ID"],
|
18
|
+
aws_secret_access_key: env["AWS_SECRET_ACCESS_KEY"]
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def upload_ssh_key(ssh_key)
|
23
|
+
Console.info("Uploading SSH key to Amazon Web Services")
|
24
|
+
if @client.key_pairs.get(KEY_NAME).nil?
|
25
|
+
@client.import_key_pair(KEY_NAME, ssh_key.to_s)
|
26
|
+
else
|
27
|
+
Console.warning("SSH key already uploaded, skipping")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_or_create_server(name:, ssh_key:)
|
32
|
+
Console.info("Creating server '#{name}'")
|
33
|
+
|
34
|
+
@client.servers.all.each do |server|
|
35
|
+
if server.ready? && server.tags["name"] == name
|
36
|
+
Console.warning("Server already exists, skipping")
|
37
|
+
return server
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@client.servers.create(
|
42
|
+
image_id: @config["image_id"],
|
43
|
+
flavor_id: @config["flavor_id"],
|
44
|
+
tags: { name: name },
|
45
|
+
key_name: KEY_NAME
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "fog/digitalocean"
|
2
|
+
|
3
|
+
require "provisioning"
|
4
|
+
|
5
|
+
module Provisioning
|
6
|
+
module Compute
|
7
|
+
class Digitalocean
|
8
|
+
KEY_NAME = "provisioning key".freeze
|
9
|
+
|
10
|
+
def initialize(config, opts, env)
|
11
|
+
@config = config
|
12
|
+
@opts = opts
|
13
|
+
Fog.mock! if @opts[:mock]
|
14
|
+
@client = Fog::Compute.new(
|
15
|
+
provider: :digitalocean,
|
16
|
+
digitalocean_token: env["DIGITALOCEAN_TOKEN"]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def upload_ssh_key(ssh_key)
|
21
|
+
Console.info("Uploading SSH key to DigitalOcean")
|
22
|
+
if @client.ssh_keys.all.map(&:fingerprint).include?(ssh_key.fingerprint)
|
23
|
+
Console.warning("SSH key already uploaded, skipping")
|
24
|
+
else
|
25
|
+
@client.ssh_keys.create(
|
26
|
+
name: KEY_NAME,
|
27
|
+
public_key: ssh_key.to_s
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_or_create_server(name:, ssh_key:)
|
33
|
+
Console.info("Creating server '#{name}'")
|
34
|
+
|
35
|
+
@client.servers.all.each do |server|
|
36
|
+
if server.name == name
|
37
|
+
Console.warning("Server already exists, skipping")
|
38
|
+
return server
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@client.servers.create(
|
43
|
+
name: name,
|
44
|
+
region: @config["region"],
|
45
|
+
image: @config["image"],
|
46
|
+
size: @config["size"],
|
47
|
+
ssh_keys: [ssh_key.fingerprint]
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/provisioning/console.rb
CHANGED
@@ -2,20 +2,34 @@ require "rainbow"
|
|
2
2
|
|
3
3
|
module Provisioning
|
4
4
|
module Console
|
5
|
+
@verbosity = 3
|
6
|
+
|
7
|
+
def self.debug_mode!
|
8
|
+
@verbosity = 4
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.silent_mode!
|
12
|
+
@verbosity = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.debug(text)
|
16
|
+
puts text if @verbosity > 3
|
17
|
+
end
|
18
|
+
|
5
19
|
def self.info(text)
|
6
|
-
puts text
|
20
|
+
puts text if @verbosity > 2
|
7
21
|
end
|
8
22
|
|
9
23
|
def self.success(text)
|
10
|
-
puts Rainbow("#{text}").green
|
24
|
+
puts Rainbow("#{text}").green if @verbosity > 1
|
11
25
|
end
|
12
26
|
|
13
27
|
def self.warning(text)
|
14
|
-
puts Rainbow("Warning: #{text}").yellow
|
28
|
+
puts Rainbow("Warning: #{text}").yellow if @verbosity > 1
|
15
29
|
end
|
16
30
|
|
17
31
|
def self.error(text)
|
18
|
-
puts Rainbow("Error: #{text}").red
|
32
|
+
puts Rainbow("Error: #{text}").red if @verbosity > 0
|
19
33
|
exit 1
|
20
34
|
end
|
21
35
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "droplet_kit"
|
2
|
+
|
3
|
+
require "provisioning"
|
4
|
+
|
5
|
+
module Provisioning
|
6
|
+
module DNS
|
7
|
+
class Digitalocean
|
8
|
+
def initialize(config, opts, env)
|
9
|
+
@config = config
|
10
|
+
@opts = opts
|
11
|
+
@client = DropletKit::Client.new(
|
12
|
+
access_token: env["DIGITALOCEAN_TOKEN"]
|
13
|
+
) unless @opts[:mock]
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_domain(name, address)
|
17
|
+
Console.info("Creating domain '#{name}'")
|
18
|
+
|
19
|
+
disable_verbose
|
20
|
+
return if @opts[:mock]
|
21
|
+
if @client.domains.all.map(&:name).include?(name)
|
22
|
+
Console.warning("Domain already exists, skipping")
|
23
|
+
else
|
24
|
+
domain = DropletKit::Domain.new(name: name, ip_address: address)
|
25
|
+
@client.domains.create(domain)
|
26
|
+
end
|
27
|
+
ensure
|
28
|
+
restore_verbose
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_domain_record(domain:, type:, name:, data:)
|
32
|
+
Console.info("Creating domain record #{type} '#{name}.#{domain}' to '#{data}'")
|
33
|
+
|
34
|
+
disable_verbose
|
35
|
+
return if @opts[:mock]
|
36
|
+
if @client.domain_records.all(for_domain: domain).map(&:name).include?(name)
|
37
|
+
Console.warning("Record already exists, skipping")
|
38
|
+
else
|
39
|
+
record = DropletKit::DomainRecord.new(type: type, name: name, data: data)
|
40
|
+
@client.domain_records.create(record, for_domain: domain)
|
41
|
+
end
|
42
|
+
ensure
|
43
|
+
restore_verbose
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_domain_name_servers(domain)
|
47
|
+
disable_verbose
|
48
|
+
return %w[ns1.example.net ns2.example.net] if @opts[:mock]
|
49
|
+
@client.domain_records.all(for_domain: domain).
|
50
|
+
select { |r| r.type == "NS" }.
|
51
|
+
map(&:data)
|
52
|
+
ensure
|
53
|
+
restore_verbose
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def disable_verbose
|
59
|
+
$OLD_VERBOSE = $VERBOSE
|
60
|
+
$VERBOSE = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def restore_verbose
|
64
|
+
$VERBOSE = $OLD_VERBOSE
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "net/ssh"
|
2
|
+
|
3
|
+
require "provisioning"
|
4
|
+
|
5
|
+
module Provisioning
|
6
|
+
module Platform
|
7
|
+
class Dokku
|
8
|
+
def initialize(config, opts, env)
|
9
|
+
@opts = opts
|
10
|
+
@config = config
|
11
|
+
@servers = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup(address:, domain:)
|
15
|
+
@servers << address
|
16
|
+
version = @config["version"]
|
17
|
+
Console.info("Installing dokku #{version} on '#{address}'")
|
18
|
+
return if @opts[:mock]
|
19
|
+
Net::SSH.start(address, "root") do |ssh|
|
20
|
+
if ssh.exec!("which dokku").present?
|
21
|
+
Console.warning("Dokku already installed, skipping")
|
22
|
+
else
|
23
|
+
[
|
24
|
+
"wget https://raw.githubusercontent.com/dokku/dokku/#{version}/bootstrap.sh",
|
25
|
+
"DOKKU_TAG=#{version} bash bootstrap.sh",
|
26
|
+
"service dokku-installer stop",
|
27
|
+
"systemctl disable dokku-installer",
|
28
|
+
"cat .ssh/authorized_keys | sshcommand acl-add dokku admin",
|
29
|
+
"echo -n #{domain} > /home/dokku/VHOST",
|
30
|
+
"echo -n #{domain} > /home/dokku/HOSTNAME"
|
31
|
+
].each { |command| Console.debug(ssh.exec!(command)) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_app(config)
|
37
|
+
name = config["name"]
|
38
|
+
@servers.each do |address|
|
39
|
+
Console.info("Creating dokku app '#{name}' on '#{address}'")
|
40
|
+
return if @opts[:mock]
|
41
|
+
Net::SSH.start(address, "root") do |ssh|
|
42
|
+
existing_apps = ssh.exec!("dokku apps").to_s.lines.map(&:chomp)
|
43
|
+
if existing_apps.include?(name)
|
44
|
+
Console.warning("App already exists, skipping")
|
45
|
+
else
|
46
|
+
Console.debug(ssh.exec!("dokku apps:create #{name}"))
|
47
|
+
|
48
|
+
config["services"].each do |service|
|
49
|
+
#TODO check if service exists
|
50
|
+
[
|
51
|
+
"dokku plugin:install https://github.com/dokku/dokku-#{service}.git #{service}",
|
52
|
+
"dokku #{service}:create #{name}-#{service}",
|
53
|
+
"dokku #{service}:link #{name}-#{service} #{name}"
|
54
|
+
].each { |command| Console.debug(ssh.exec!(command)) }
|
55
|
+
end
|
56
|
+
|
57
|
+
domains = config["domains"].join(" ")
|
58
|
+
Console.debug(ssh.exec!("dokku domains:add #{name} #{domains}"))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Provisioning
|
2
|
+
class PublicKey
|
3
|
+
def initialize(key)
|
4
|
+
unless key.start_with?("ssh-rsa ")
|
5
|
+
raise ArgumentError.new("Wrong SSH RSA public key format")
|
6
|
+
end
|
7
|
+
@key = key
|
8
|
+
end
|
9
|
+
|
10
|
+
def fingerprint
|
11
|
+
bin = Base64.decode64(@key.split[1])
|
12
|
+
md5 = OpenSSL::Digest::MD5.new(bin)
|
13
|
+
md5.to_s.scan(/../).join(":")
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
@key
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
metadata
CHANGED
@@ -1,75 +1,95 @@
|
|
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.3
|
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-09-
|
11
|
+
date: 2017-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: droplet_kit
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1
|
19
|
+
version: '2.1'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.
|
22
|
+
version: 2.1.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '1
|
29
|
+
version: '2.1'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 1.
|
32
|
+
version: 2.1.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: fog-aws
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '1.4'
|
40
40
|
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
42
|
+
version: 1.4.0
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '
|
49
|
+
version: '1.4'
|
50
50
|
- - ">="
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
52
|
+
version: 1.4.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
54
|
+
name: fog-digitalocean
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
59
|
+
version: '0.3'
|
60
60
|
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
62
|
+
version: 0.3.0
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
69
|
+
version: '0.3'
|
70
70
|
- - ">="
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
72
|
+
version: 0.3.0
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: git
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '1.3'
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.3.0
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.3'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.3.0
|
73
93
|
- !ruby/object:Gem::Dependency
|
74
94
|
name: net-ssh
|
75
95
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,19 +110,66 @@ dependencies:
|
|
90
110
|
- - ">="
|
91
111
|
- !ruby/object:Gem::Version
|
92
112
|
version: 4.1.0
|
93
|
-
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: rainbow
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '2.2'
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 2.2.0
|
123
|
+
type: :runtime
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '2.2'
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 2.2.0
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: trollop
|
135
|
+
requirement: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - "~>"
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '2.1'
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: 2.1.0
|
143
|
+
type: :runtime
|
144
|
+
prerelease: false
|
145
|
+
version_requirements: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - "~>"
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '2.1'
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 2.1.0
|
153
|
+
description: Open PaaS provisioning on cloud providers from JSON manifest file
|
94
154
|
email: v@vinc.cc
|
95
155
|
executables:
|
96
156
|
- provision
|
97
157
|
extensions: []
|
98
158
|
extra_rdoc_files: []
|
99
159
|
files:
|
160
|
+
- LICENSE
|
161
|
+
- README.md
|
100
162
|
- bin/provision
|
163
|
+
- lib/object.rb
|
101
164
|
- lib/provisioning.rb
|
102
165
|
- lib/provisioning/cli.rb
|
166
|
+
- lib/provisioning/compute/aws.rb
|
167
|
+
- lib/provisioning/compute/digitalocean.rb
|
103
168
|
- lib/provisioning/console.rb
|
104
|
-
- lib/provisioning/digitalocean.rb
|
105
|
-
- lib/provisioning/dokku.rb
|
169
|
+
- lib/provisioning/dns/digitalocean.rb
|
170
|
+
- lib/provisioning/platform/dokku.rb
|
171
|
+
- lib/provisioning/public_key.rb
|
172
|
+
- lib/provisioning/version.rb
|
106
173
|
homepage: https://github.com/vinc/provisioning.rb
|
107
174
|
licenses:
|
108
175
|
- MIT
|
@@ -126,5 +193,5 @@ rubyforge_project:
|
|
126
193
|
rubygems_version: 2.6.8
|
127
194
|
signing_key:
|
128
195
|
specification_version: 4
|
129
|
-
summary: PaaS
|
196
|
+
summary: Open PaaS provisioning
|
130
197
|
test_files: []
|
@@ -1,100 +0,0 @@
|
|
1
|
-
require "droplet_kit"
|
2
|
-
|
3
|
-
require "provisioning"
|
4
|
-
|
5
|
-
module Provisioning
|
6
|
-
class DigitalOcean
|
7
|
-
def initialize(config)
|
8
|
-
@config = config
|
9
|
-
@client = DropletKit::Client.new(access_token: @config["token"])
|
10
|
-
end
|
11
|
-
|
12
|
-
def upload_ssh_key(ssh_key)
|
13
|
-
Console.info("Uploading SSH key to DigitalOcean")
|
14
|
-
|
15
|
-
disable_verbose
|
16
|
-
if @client.ssh_keys.all.map(&:fingerprint).include?(ssh_key.fingerprint)
|
17
|
-
Console.warning("SSH key already uploaded to DigitalOcean, skipping")
|
18
|
-
else
|
19
|
-
key = DropletKit::SSHKey.new(
|
20
|
-
name: "provisioning key",
|
21
|
-
public_key: ssh_key.to_s
|
22
|
-
)
|
23
|
-
@client.ssh_keys.create(key)
|
24
|
-
end
|
25
|
-
restore_verbose
|
26
|
-
end
|
27
|
-
|
28
|
-
def create_droplet(name:, ssh_key_fingerprint:)
|
29
|
-
Console.info("Creating droplet '#{name}'")
|
30
|
-
|
31
|
-
disable_verbose
|
32
|
-
@client.droplets.all.each do |droplet|
|
33
|
-
if droplet.name == name
|
34
|
-
Console.warning("droplet already exists, skipping")
|
35
|
-
return droplet
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
droplet = DropletKit::Droplet.new(
|
40
|
-
name: name,
|
41
|
-
region: @config["droplet"]["region"],
|
42
|
-
image: @config["droplet"]["image"],
|
43
|
-
size: @config["droplet"]["size"],
|
44
|
-
ssh_keys: [ssh_key_fingerprint]
|
45
|
-
)
|
46
|
-
@client.droplets.create(droplet)
|
47
|
-
droplet
|
48
|
-
ensure
|
49
|
-
restore_verbose
|
50
|
-
end
|
51
|
-
|
52
|
-
def create_domain(name, address)
|
53
|
-
Console.info("Creating domain '#{name}'")
|
54
|
-
|
55
|
-
disable_verbose
|
56
|
-
if @client.domains.all.map(&:name).include?(name)
|
57
|
-
Console.warning("domain already exists, skipping")
|
58
|
-
else
|
59
|
-
domain = DropletKit::Domain.new(name: name, ip_address: address)
|
60
|
-
@client.domains.create(domain)
|
61
|
-
end
|
62
|
-
ensure
|
63
|
-
restore_verbose
|
64
|
-
end
|
65
|
-
|
66
|
-
def create_domain_record(domain:, type:, name:, data:)
|
67
|
-
Console.info("Creating domain record #{type} '#{name}.#{domain}' to '#{data}'")
|
68
|
-
|
69
|
-
disable_verbose
|
70
|
-
if @client.domain_records.all(for_domain: domain).map(&:name).include?(name)
|
71
|
-
Console.warning("record already exists, skipping")
|
72
|
-
else
|
73
|
-
record = DropletKit::DomainRecord.new(type: type, name: name, data: data)
|
74
|
-
@client.domain_records.create(record, for_domain: domain)
|
75
|
-
end
|
76
|
-
ensure
|
77
|
-
restore_verbose
|
78
|
-
end
|
79
|
-
|
80
|
-
def get_domain_name_servers(domain)
|
81
|
-
disable_verbose
|
82
|
-
@client.domain_records.all(for_domain: domain).
|
83
|
-
select { |r| r.type == "NS" }.
|
84
|
-
map(&:data)
|
85
|
-
ensure
|
86
|
-
restore_verbose
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def disable_verbose
|
92
|
-
$OLD_VERBOSE = $VERBOSE
|
93
|
-
$VERBOSE = nil
|
94
|
-
end
|
95
|
-
|
96
|
-
def restore_verbose
|
97
|
-
$VERBOSE = $OLD_VERBOSE
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
data/lib/provisioning/dokku.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
require "net/ssh"
|
2
|
-
|
3
|
-
require "provisioning"
|
4
|
-
|
5
|
-
module Provisioning
|
6
|
-
class Dokku
|
7
|
-
def initialize(config)
|
8
|
-
@config = config
|
9
|
-
@servers = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def setup(address:, domain:)
|
13
|
-
@servers << address
|
14
|
-
version = @config["version"]
|
15
|
-
Console.info("Installing dokku #{version} on '#{address}'")
|
16
|
-
Net::SSH.start(address, "root") do |ssh|
|
17
|
-
if ssh.exec!("which dokku").present?
|
18
|
-
Console.warning("dokku already installed, skipping")
|
19
|
-
else
|
20
|
-
puts ssh.exec!("wget https://raw.githubusercontent.com/dokku/dokku/#{version}/bootstrap.sh")
|
21
|
-
puts ssh.exec!("DOKKU_TAG=#{version} bash bootstrap.sh")
|
22
|
-
puts ssh.exec!("service dokku-installer stop")
|
23
|
-
puts ssh.exec!("systemctl disable dokku-installer")
|
24
|
-
puts ssh.exec!("cat .ssh/authorized_keys | sshcommand acl-add dokku admin")
|
25
|
-
puts ssh.exec!("echo -n #{domain} > /home/dokku/VHOST")
|
26
|
-
puts ssh.exec!("echo -n #{domain} > /home/dokku/HOSTNAME")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def create_app(config)
|
32
|
-
name = config["name"]
|
33
|
-
@servers.each do |address|
|
34
|
-
Console.info("Creating dokku app '#{name}' on '#{address}'")
|
35
|
-
Net::SSH.start(address, "root") do |ssh|
|
36
|
-
existing_apps = ssh.exec!("dokku apps").to_s.lines.map(&:chomp)
|
37
|
-
if existing_apps.include?(name)
|
38
|
-
Console.warning("app already exists, skipping")
|
39
|
-
else
|
40
|
-
puts ssh.exec!("dokku apps:create #{name}")
|
41
|
-
|
42
|
-
config["services"].each do |service|
|
43
|
-
case service
|
44
|
-
when "mongo", "postgres", "redis"
|
45
|
-
puts ssh.exec!("dokku plugin:install https://github.com/dokku/dokku-#{service}.git #{service}")
|
46
|
-
puts ssh.exec!("dokku #{service}:create #{name}-#{service}")
|
47
|
-
puts ssh.exec!("dokku #{service}:link #{name}-#{service} #{name}")
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
domains = config["domains"].join(" ")
|
52
|
-
puts ssh.exec!("dokku domains:add #{name} #{domains}")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|