staticpages-plugin-digitalocean 0.3.5

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.
@@ -0,0 +1,30 @@
1
+ module Kontena
2
+ module Machine
3
+ module DigitalOcean
4
+ class MasterDestroyer
5
+ include Kontena::Cli::ShellSpinner
6
+
7
+ attr_reader :client, :api_client
8
+
9
+ # @param [String] token Digital Ocean token
10
+ def initialize(token)
11
+ @client = DropletKit::Client.new(access_token: token)
12
+ end
13
+
14
+ def run!(name)
15
+ droplet = client.droplets.all.find{|d| d.name == name}
16
+ if droplet
17
+ spinner "Terminating DigitalOcean droplet #{name.colorize(:cyan)} " do
18
+ result = client.droplets.delete(id: droplet.id)
19
+ if result.is_a?(String)
20
+ abort "Cannot delete droplet #{name.colorize(:cyan)} in DigitalOcean"
21
+ end
22
+ end
23
+ else
24
+ abort "Cannot find droplet #{name.colorize(:cyan)} in DigitalOcean"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,99 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+ require 'open3'
4
+
5
+ module Kontena
6
+ module Machine
7
+ module DigitalOcean
8
+ class MasterProvisioner
9
+ include RandomName
10
+ include Machine::CertHelper
11
+ include Kontena::Cli::ShellSpinner
12
+
13
+ attr_reader :client, :http_client
14
+
15
+ # @param [String] token Digital Ocean token
16
+ def initialize(token)
17
+ @client = DropletKit::Client.new(access_token: token)
18
+ end
19
+
20
+ def run!(opts)
21
+ if opts[:ssl_cert]
22
+ abort('Invalid ssl cert') unless File.exists?(File.expand_path(opts[:ssl_cert]))
23
+ ssl_cert = File.read(File.expand_path(opts[:ssl_cert]))
24
+ else
25
+ spinner "Generating self-signed SSL certificate" do
26
+ ssl_cert = generate_self_signed_cert
27
+ end
28
+ end
29
+
30
+ name = opts[:name]
31
+ userdata_vars = opts.merge(
32
+ ssl_cert: ssl_cert,
33
+ server_name: name
34
+ )
35
+
36
+ droplet = DropletKit::Droplet.new(
37
+ name: name,
38
+ region: opts[:region],
39
+ image: 'coreos-stable',
40
+ size: opts[:size],
41
+ private_networking: true,
42
+ user_data: user_data(userdata_vars),
43
+ ssh_keys: [opts[:ssh_key_id]],
44
+ tags: ['master']
45
+ )
46
+
47
+ spinner "Creating DigitalOcean droplet #{droplet.name.colorize(:cyan)} " do
48
+ droplet = client.droplets.create(droplet)
49
+ until droplet.status == 'active'
50
+ droplet = client.droplets.find(id: droplet.id)
51
+ sleep 1
52
+ end
53
+ end
54
+
55
+ master_url = "https://#{droplet.public_ip}"
56
+ Excon.defaults[:ssl_verify_peer] = false
57
+ @http_client = Excon.new("#{master_url}", {
58
+ :connect_timeout => 10,
59
+ :ssl_verify_peer => false
60
+ })
61
+
62
+ spinner "Waiting for #{droplet.name.colorize(:cyan)} to start" do
63
+ sleep 0.5 until master_running?
64
+ end
65
+
66
+ puts
67
+ puts "Kontena Master is now running at #{master_url}".colorize(:green)
68
+ puts
69
+
70
+ data = {
71
+ name: name.sub('kontena-master-', ''),
72
+ public_ip: droplet.public_ip,
73
+ code: opts[:initial_admin_code]
74
+ }
75
+ if respond_to?(:certificate_public_key) && !opts[:ssl_cert]
76
+ data[:ssl_certificate] = certificate_public_key(ssl_cert)
77
+ end
78
+
79
+ data
80
+ end
81
+
82
+ def user_data(vars)
83
+ cloudinit_template = File.join(__dir__ , '/cloudinit_master.yml')
84
+ erb(File.read(cloudinit_template), vars)
85
+ end
86
+
87
+ def master_running?
88
+ http_client.get(path: '/').status == 200
89
+ rescue
90
+ false
91
+ end
92
+
93
+ def erb(template, vars)
94
+ ERB.new(template, nil, '%<>-').result(OpenStruct.new(vars).instance_eval { binding })
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,39 @@
1
+ module Kontena
2
+ module Machine
3
+ module DigitalOcean
4
+ class NodeDestroyer
5
+ include RandomName
6
+ include Kontena::Cli::ShellSpinner
7
+
8
+ attr_reader :client, :api_client
9
+
10
+ # @param [Kontena::Client] api_client Kontena api client
11
+ # @param [String] token Digital Ocean token
12
+ def initialize(api_client, token)
13
+ @api_client = api_client
14
+ @client = DropletKit::Client.new(access_token: token)
15
+ end
16
+
17
+ def run!(grid, name)
18
+ droplet = client.droplets.all.find{|d| d.name == name}
19
+ if droplet
20
+ spinner "Terminating DigitalOcean droplet #{name.colorize(:cyan)} " do
21
+ result = client.droplets.delete(id: droplet.id)
22
+ if result.is_a?(String)
23
+ abort "Cannot delete droplet #{name.colorize(:cyan)} in DigitalOcean"
24
+ end
25
+ end
26
+ else
27
+ abort "Cannot find droplet #{name.colorize(:cyan)} in DigitalOcean"
28
+ end
29
+ node = api_client.get("grids/#{grid['id']}/nodes")['nodes'].find{|n| n['name'] == name}
30
+ if node
31
+ spinner "Removing node #{name.colorize(:cyan)} from grid #{grid['name'].colorize(:cyan)} " do
32
+ api_client.delete("nodes/#{grid['id']}/#{name}")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,90 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+ require 'open3'
4
+
5
+ module Kontena
6
+ module Machine
7
+ module DigitalOcean
8
+ class NodeProvisioner
9
+ include RandomName
10
+ include Kontena::Cli::ShellSpinner
11
+
12
+ attr_reader :client, :api_client
13
+
14
+ # @param [Kontena::Client] api_client Kontena api client
15
+ # @param [String] token Digital Ocean token
16
+ def initialize(api_client, token)
17
+ @api_client = api_client
18
+ @client = DropletKit::Client.new(access_token: token)
19
+ end
20
+
21
+ def run!(opts)
22
+ userdata_vars = {
23
+ version: opts[:version],
24
+ master_uri: opts[:master_uri],
25
+ grid_token: opts[:grid_token],
26
+ }
27
+ image = "coreos-#{opts[:channel]}"
28
+ droplets = []
29
+ opts[:count].to_i.times do
30
+ droplet = DropletKit::Droplet.new(
31
+ name: opts[:name] || generate_name,
32
+ region: opts[:region],
33
+ image: image,
34
+ size: opts[:size],
35
+ private_networking: true,
36
+ user_data: user_data(userdata_vars),
37
+ ssh_keys: [opts[:ssh_key_id]],
38
+ tags: [opts[:grid]]
39
+ )
40
+ created = client.droplets.create(droplet)
41
+ spinner "Creating DigitalOcean droplet #{droplet.name.colorize(:cyan)} " do
42
+ sleep 1 until client.droplets.find(id: created.id).status == 'active'
43
+ end
44
+ droplets << droplet
45
+ end
46
+ droplets.each do |droplet|
47
+ node = nil
48
+ spinner "Waiting for node #{droplet.name.colorize(:cyan)} join to grid #{opts[:grid].colorize(:cyan)} " do
49
+ sleep 1 until node = droplet_exists_in_grid?(opts[:grid], droplet)
50
+ end
51
+ set_labels(
52
+ node,
53
+ [
54
+ "region=#{opts[:region]}",
55
+ "az=#{opts[:region]}",
56
+ "provider=digitalocean"
57
+ ]
58
+ )
59
+ end
60
+ end
61
+
62
+ def user_data(vars)
63
+ cloudinit_template = File.join(__dir__ , '/cloudinit.yml')
64
+ erb(File.read(cloudinit_template), vars)
65
+ end
66
+
67
+ def generate_name
68
+ "#{super}-#{rand(1..99)}"
69
+ end
70
+
71
+ def ssh_key(public_key)
72
+ ssh_key = client.ssh_keys.all.find{|key| key.public_key == public_key}
73
+ end
74
+
75
+ def droplet_exists_in_grid?(grid, droplet)
76
+ api_client.get("grids/#{grid}/nodes")['nodes'].find{|n| n['name'] == droplet.name}
77
+ end
78
+
79
+ def erb(template, vars)
80
+ ERB.new(template).result(OpenStruct.new(vars).instance_eval { binding })
81
+ end
82
+
83
+ def set_labels(node, labels)
84
+ data = {labels: labels}
85
+ api_client.put("nodes/#{node['id']}", data, {}, {'Kontena-Grid-Token' => node['grid']['token']})
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,31 @@
1
+ module Kontena
2
+ module Machine
3
+ module DigitalOcean
4
+ class SshKeyManager
5
+
6
+ attr_reader :client
7
+
8
+ # @param [String] token Digital Ocean API token
9
+ def initialize(token)
10
+ @client = DropletKit::Client.new(access_token: token)
11
+ end
12
+
13
+ def find_by_public_key(public_key)
14
+ list.find { |key| key.public_key == public_key }
15
+ end
16
+
17
+ def list
18
+ client.ssh_keys.all.to_a
19
+ end
20
+
21
+ def create(public_key)
22
+ client.ssh_keys.create(DropletKit::SSHKey.new(public_key: public_key, name: public_key.split(/\s+/).last))
23
+ end
24
+
25
+ def find_or_create_by_public_key(public_key)
26
+ find_by_public_key(public_key) || create(public_key)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ require 'droplet_kit'
2
+ require 'kontena/machine/random_name'
3
+ require 'kontena/machine/cert_helper'
4
+ require_relative 'digital_ocean/node_provisioner'
5
+ require_relative 'digital_ocean/node_destroyer'
6
+ require_relative 'digital_ocean/master_provisioner'
7
+ require_relative 'digital_ocean/master_destroyer'
8
+ require_relative 'digital_ocean/ssh_key_manager'
@@ -0,0 +1,53 @@
1
+ require 'kontena/plugin/digital_ocean/prompts'
2
+
3
+ module Kontena::Plugin::DigitalOcean::Master
4
+ class CreateCommand < Kontena::Command
5
+ include Kontena::Cli::Common
6
+ include Kontena::Plugin::DigitalOcean::Prompts
7
+
8
+ option "--name", "[NAME]", "Set master name"
9
+ option "--token", "TOKEN", "DigitalOcean API token", environment_variable: "DO_TOKEN"
10
+ option "--region", "REGION", "Region"
11
+ option "--size", "SIZE", "Droplet size"
12
+ option "--ssh-key", "SSH_KEY", "Path to ssh public key"
13
+ option "--ssl-cert", "SSL CERT", "SSL certificate file"
14
+ option "--vault-secret", "VAULT_SECRET", "Secret key for Vault (optional)"
15
+ option "--vault-iv", "VAULT_IV", "Initialization vector for Vault (optional)"
16
+ option "--mongodb-uri", "URI", "External MongoDB uri (optional)"
17
+ option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
18
+
19
+ def execute
20
+ suppress_warnings # until DO merges resource_kit pr #32
21
+ do_token = ask_do_token
22
+
23
+ require 'securerandom'
24
+ require 'kontena/machine/digital_ocean'
25
+
26
+ do_token = ask_do_token
27
+ do_region = ask_droplet_region(do_token)
28
+ do_size = ask_droplet_size(do_token, do_region)
29
+ do_ssh_key_id = ask_ssh_key(do_token)
30
+
31
+ provisioner = provisioner(do_token)
32
+ provisioner.run!(
33
+ name: name,
34
+ ssh_key_id: do_ssh_key_id,
35
+ ssl_cert: ssl_cert,
36
+ size: do_size,
37
+ region: do_region,
38
+ version: version,
39
+ vault_secret: vault_secret || SecureRandom.hex(24),
40
+ vault_iv: vault_iv || SecureRandom.hex(24),
41
+ initial_admin_code: SecureRandom.hex(16),
42
+ mongodb_uri: mongodb_uri
43
+ )
44
+ ensure
45
+ resume_warnings
46
+ end
47
+
48
+ # @param [String] token
49
+ def provisioner(token)
50
+ Kontena::Machine::DigitalOcean::MasterProvisioner.new(token)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ require 'kontena/plugin/digital_ocean/prompts'
2
+
3
+ module Kontena::Plugin::DigitalOcean::Master
4
+ class TerminateCommand < Kontena::Command
5
+ include Kontena::Cli::Common
6
+ include Kontena::Cli::GridOptions
7
+ include Kontena::Plugin::DigitalOcean::Prompts
8
+
9
+ parameter "NAME", "Master name"
10
+ option "--token", "TOKEN", "DigitalOcean API token", environment_variable: "DO_TOKEN"
11
+ option "--force", :flag, "Force remove", default: false, attribute_name: :forced
12
+
13
+ def execute
14
+ suppress_warnings # until DO merges resource_kit pr #32
15
+ require 'kontena/machine/digital_ocean'
16
+ do_token = ask_do_token
17
+ confirm_command(name) unless forced?
18
+ destroyer = destroyer(do_token)
19
+ destroyer.run!(name)
20
+ ensure
21
+ resume_warnings
22
+ end
23
+
24
+ # @param [String] token
25
+ def destroyer(token)
26
+ Kontena::Machine::DigitalOcean::MasterDestroyer.new(token)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ class Kontena::Plugin::DigitalOcean::MasterCommand < Kontena::Command
2
+ subcommand "create", "Create a new master to DigitalOcean", load_subcommand('kontena/plugin/digital_ocean/master/create_command')
3
+ subcommand "terminate", "Terminate DigitalOcean master", load_subcommand('kontena/plugin/digital_ocean/master/terminate_command')
4
+ end
@@ -0,0 +1,5 @@
1
+ class Kontena::Plugin::DigitalOcean::NodeCommand < Kontena::Command
2
+ subcommand "create", "Create a new node to DigitalOcean", load_subcommand('kontena/plugin/digital_ocean/nodes/create_command')
3
+ subcommand "restart", "Restart DigitalOcean node", load_subcommand('kontena/plugin/digital_ocean/nodes/restart_command')
4
+ subcommand "terminate", "Terminate DigitalOcean node", load_subcommand('kontena/plugin/digital_ocean/nodes/terminate_command')
5
+ end
@@ -0,0 +1,68 @@
1
+ require 'kontena/plugin/digital_ocean/prompts'
2
+
3
+ module Kontena::Plugin::DigitalOcean::Nodes
4
+ class CreateCommand < Kontena::Command
5
+ include Kontena::Cli::Common
6
+ include Kontena::Cli::GridOptions
7
+ include Kontena::Plugin::DigitalOcean::Prompts
8
+
9
+ parameter "[NAME]", "Node name"
10
+ option "--token", "TOKEN", "DigitalOcean API token", environment_variable: 'DO_TOKEN'
11
+ option "--region", "REGION", "Region"
12
+ option "--ssh-key", "SSH_KEY", "Path to ssh public key"
13
+ option "--size", "SIZE", "Droplet size"
14
+ option "--count", "COUNT", "How many droplets to create"
15
+ option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
16
+ option "--channel", "CHANNEL", "Define CoreOS image channel"
17
+
18
+ def execute
19
+ suppress_warnings # until DO merges resource_kit pr #32
20
+ require 'kontena/machine/digital_ocean'
21
+ require_api_url
22
+ require_current_grid
23
+
24
+ do_token = ask_do_token
25
+ do_region = ask_droplet_region(do_token)
26
+ coreos_channel = self.channel || ask_channel
27
+ do_size = ask_droplet_size(do_token, do_region)
28
+ do_count = ask_droplet_count
29
+ do_ssh_key_id = ask_ssh_key(do_token)
30
+
31
+ grid = fetch_grid
32
+ provisioner = provisioner(client(require_token), do_token)
33
+ provisioner.run!(
34
+ master_uri: api_url,
35
+ grid_token: grid['token'],
36
+ grid: current_grid,
37
+ ssh_key_id: do_ssh_key_id,
38
+ name: name,
39
+ size: do_size,
40
+ count: do_count,
41
+ region: do_region,
42
+ version: version,
43
+ channel: coreos_channel
44
+ )
45
+ ensure
46
+ resume_warnings
47
+ end
48
+
49
+ def ask_droplet_count
50
+ if self.count.nil?
51
+ prompt.ask('How many droplets?:', default: 1)
52
+ else
53
+ self.count
54
+ end
55
+ end
56
+
57
+ # @param [Kontena::Client] client
58
+ # @param [String] token
59
+ def provisioner(client, token)
60
+ Kontena::Machine::DigitalOcean::NodeProvisioner.new(client, token)
61
+ end
62
+
63
+ # @return [Hash]
64
+ def fetch_grid
65
+ client(require_token).get("grids/#{current_grid}")
66
+ end
67
+ end
68
+ end