cyclid-digitalocean-plugin 0.1.0
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/lib/cyclid/plugins/builder/digitalocean.rb +204 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0eb1eb8572ab14e2bc8d035c4cae3c277ce812e0
|
4
|
+
data.tar.gz: c08f1446391ee32afdc64804e4dbf781da35bf26
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fb1d59fe2f3b574721f95945d15517ad2cabf8e50977ed5b07901d2e4fe96016ac2b4e24cd49e65f398a87c992aec1c9c1fdbe5bbf32a3a91b880d8510a87279
|
7
|
+
data.tar.gz: b7a079b37b81daa21147860e2fc1cb5f7758494a6f3eff6613edc067bfd5a9111c810cda2134471edf669e3785b1c8d873d975b7f842894cc2f560f7972e6198
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Copyright 2016 Liqwyd Ltd.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'droplet_kit'
|
17
|
+
|
18
|
+
# Top level module for the core Cyclid code.
|
19
|
+
module Cyclid
|
20
|
+
# Module for the Cyclid API
|
21
|
+
module API
|
22
|
+
# Module for Cyclid Plugins
|
23
|
+
module Plugins
|
24
|
+
# Digitalocean build host
|
25
|
+
class DigitaloceanHost < BuildHost
|
26
|
+
# SSH is the only acceptable Transport
|
27
|
+
def transports
|
28
|
+
['ssh']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Digitalocean builder. Uses the Digitalocean API to obtain a build host instance.
|
33
|
+
class Digitalocean < Builder
|
34
|
+
def initialize
|
35
|
+
@config = load_digitalocean_config(Cyclid.config.plugins)
|
36
|
+
@client = DropletKit::Client.new(access_token: @config[:access_token])
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create & return a build host
|
40
|
+
def get(args = {})
|
41
|
+
args.symbolize_keys!
|
42
|
+
|
43
|
+
Cyclid.logger.debug "digitalocean: args=#{args}"
|
44
|
+
|
45
|
+
# If there is one, split the 'os' into a 'distro' and 'release'
|
46
|
+
if args.key? :os
|
47
|
+
match = args[:os].match(/\A(\w*)_(.*)\Z/)
|
48
|
+
distro = match[1] if match
|
49
|
+
release = match[2] if match
|
50
|
+
else
|
51
|
+
# No OS was specified; use the default
|
52
|
+
# XXX Defaults should be configurable
|
53
|
+
distro = 'ubuntu'
|
54
|
+
release = 'trusty'
|
55
|
+
end
|
56
|
+
|
57
|
+
# Convert Debian & Ubuntu release codenames
|
58
|
+
release_version = if distro == 'ubuntu' or distro == 'debian'
|
59
|
+
codename_to_version(release)
|
60
|
+
else
|
61
|
+
release
|
62
|
+
end
|
63
|
+
|
64
|
+
begin
|
65
|
+
# Find, or create, the SSH key Cyclid can use
|
66
|
+
build_key = build_ssh_key
|
67
|
+
|
68
|
+
# Create the Droplet
|
69
|
+
droplet = DropletKit::Droplet.new(name: create_name,
|
70
|
+
region: @config[:region],
|
71
|
+
image: "#{distro}-#{release_version}-x64",
|
72
|
+
size: @config[:size],
|
73
|
+
ssh_keys: [build_key.id])
|
74
|
+
created = @client.droplets.create droplet
|
75
|
+
|
76
|
+
# Wait for the droplet to become 'active'
|
77
|
+
created = wait_for_droplet(created)
|
78
|
+
|
79
|
+
# Create a buildhost from the active Droplet details
|
80
|
+
buildhost = DigitaloceanHost.new(name: created.name,
|
81
|
+
host: created.networks.v4.first.ip_address,
|
82
|
+
id: created.id,
|
83
|
+
username: 'root',
|
84
|
+
workspace: '/root',
|
85
|
+
key: @config[:ssh_private_key],
|
86
|
+
distro: distro,
|
87
|
+
release: release)
|
88
|
+
rescue StandardError => ex
|
89
|
+
Cyclid.logger.error "couldn't get a build host from Digitalocean: #{ex}"
|
90
|
+
raise "digitalocean failed: #{ex}"
|
91
|
+
end
|
92
|
+
|
93
|
+
Cyclid.logger.debug "digitalocean buildhost=#{buildhost.inspect}"
|
94
|
+
return buildhost
|
95
|
+
end
|
96
|
+
|
97
|
+
# Destroy the build host
|
98
|
+
def release(_transport, buildhost)
|
99
|
+
@client.droplets.delete(id: buildhost[:id])
|
100
|
+
rescue StandardError => ex
|
101
|
+
Cyclid.logger.error "Digitalcoean destroy timed out: #{ex}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Register this plugin
|
105
|
+
register_plugin 'digitalocean'
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Load the config for the Digitalocean plugin and set defaults if they're not
|
110
|
+
# in the config
|
111
|
+
def load_digitalocean_config(config)
|
112
|
+
config.symbolize_keys!
|
113
|
+
|
114
|
+
do_config = config[:digitalocean] || {}
|
115
|
+
do_config.symbolize_keys!
|
116
|
+
Cyclid.logger.debug "config=#{do_config}"
|
117
|
+
|
118
|
+
raise 'the Digitalocean API access token must be provided' \
|
119
|
+
unless do_config.key? :access_token
|
120
|
+
|
121
|
+
# Set defaults
|
122
|
+
do_config[:region] = 'nyc1' unless do_config.key? :region
|
123
|
+
do_config[:size] = '512mb' unless do_config.key? :size
|
124
|
+
do_config[:ssh_private_key] = File.join(%w(/ etc cyclid id_rsa_build)) \
|
125
|
+
unless do_config.key? :ssh_private_key
|
126
|
+
do_config[:ssh_public_key] = File.join(%w(/ etc cyclid id_rsa_build.pub)) \
|
127
|
+
unless do_config.key? :ssh_public_key
|
128
|
+
do_config[:ssh_key_name] = 'cyclid-build' \
|
129
|
+
unless do_config.key? :ssh_key_name
|
130
|
+
do_config[:instance_name] = 'cyclid-build' \
|
131
|
+
unless do_config.key? :instance_name
|
132
|
+
|
133
|
+
return do_config
|
134
|
+
end
|
135
|
+
|
136
|
+
# Convert Ubuntu & Debian release codenames to Digitialocean approved release versions
|
137
|
+
def codename_to_version(release)
|
138
|
+
versions = { 'precise' => '12-04',
|
139
|
+
'trusty' => '14-04',
|
140
|
+
'xenial' => '16-04',
|
141
|
+
'yakkety' => '16-10',
|
142
|
+
'wheezy' => '7',
|
143
|
+
'jessie' => '8' }
|
144
|
+
|
145
|
+
# No need to convert if it isn't a codename
|
146
|
+
return release if release =~ /\A\d*[-\d*]/
|
147
|
+
|
148
|
+
raise "don't know what version Ubuntu/Debian #{release} is" \
|
149
|
+
unless versions.key? release
|
150
|
+
|
151
|
+
return versions[release]
|
152
|
+
end
|
153
|
+
|
154
|
+
def build_ssh_key
|
155
|
+
# Find the build key, if it exists
|
156
|
+
build_key = nil
|
157
|
+
all_keys = @client.ssh_keys.all
|
158
|
+
all_keys.each do |key|
|
159
|
+
build_key = key if key.name == @config[:ssh_key_name]
|
160
|
+
end
|
161
|
+
Cyclid.logger.debug "build_key=#{build_key.inspect}"
|
162
|
+
|
163
|
+
# If the key doesn't exist, create it
|
164
|
+
if build_key.nil?
|
165
|
+
pubkey = File.read(@config[:ssh_public_key])
|
166
|
+
key = DropletKit::SSHKey.new(name: @config[:ssh_key_name],
|
167
|
+
public_key: pubkey)
|
168
|
+
build_key = @client.ssh_keys.create(key)
|
169
|
+
Cyclid.logger.debug "build_key=#{build_key.inspect}"
|
170
|
+
end
|
171
|
+
|
172
|
+
return build_key
|
173
|
+
end
|
174
|
+
|
175
|
+
def wait_for_droplet(created)
|
176
|
+
# Wait for the droplet to become active; wait a maximum of 1 minute,
|
177
|
+
# polling every 2 seconds.
|
178
|
+
Cyclid.logger.debug "Waiting for instance #{created.id} to become 'active'..."
|
179
|
+
29.times do
|
180
|
+
created = @client.droplets.find(id: created.id.to_s)
|
181
|
+
break if created.status == 'active'
|
182
|
+
|
183
|
+
sleep 2
|
184
|
+
end
|
185
|
+
Cyclid.logger.debug "created=#{created.inspect}"
|
186
|
+
|
187
|
+
unless created.status == 'active'
|
188
|
+
@client.droplets.delete(id: created.id)
|
189
|
+
|
190
|
+
raise 'failed to create build host: did not become active within 1 minute. ' \
|
191
|
+
"Status is #{created.status}" \
|
192
|
+
end
|
193
|
+
|
194
|
+
return created
|
195
|
+
end
|
196
|
+
|
197
|
+
def create_name
|
198
|
+
base = @config[:instance_name]
|
199
|
+
"#{base}-#{SecureRandom.hex(16)}"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cyclid-digitalocean-plugin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kristian Van Der Vliet
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: droplet_kit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.4'
|
27
|
+
description: Creates Digitalocean Droplet build hosts
|
28
|
+
email: contact@cyclid.io
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/cyclid/plugins/builder/digitalocean.rb
|
34
|
+
homepage: https://cyclid.io
|
35
|
+
licenses:
|
36
|
+
- Apache-2.0
|
37
|
+
metadata: {}
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 2.5.1
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: Cyclid Digitalocean plugin
|
58
|
+
test_files: []
|