sunzi-vps 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 22de99d93f1dcc0b6189b27a8f76827c611f85b142f75ac8220725a909f1746b
4
+ data.tar.gz: 4d10928a3be79b0c81e103eff8b3fcd2dc9c88eaec980584d275c91c9d341539
5
+ SHA512:
6
+ metadata.gz: 655ef8b565505126a3a20438c1f79699d84f50aba835680cf3ea9ca9565935d27a6456260e5ef14c788ec2eee4fac34f8e2fcf5c8864ef449c5bae4cbe1b4a2d
7
+ data.tar.gz: 236e67762ce6e2aa1dbf82d39f795b5f8d612d35d19b3dd14c0d6b4c43e4fbe05bd25c0d691046cf22620bbdc8d6eaf0b1c6e426be6eca76f806835d99121c82
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /.bundle/
2
+ /pkg/
3
+ /tmp/
4
+ /Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - ruby-head
7
+ before_install: gem install bundler -v 1.16.0
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) {|repo_name| 'https://github.com/#{repo_name}' }
4
+
5
+ # Specify your gem's dependencies in sunzi-vps.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # Sunzi VPS
2
+
3
+ sunzi-vps is a simple command line utility to create and/or destroy VPS instances interactively.
4
+
5
+ It works as a plugin for [sunzi](https://github.com/kenn/sunzi), but can be used as a standalone tool.
6
+
7
+ ![Sunzi for Linode](http://farm8.staticflickr.com/7210/6783789868_ab89010d5c.jpg)
8
+
9
+ ## Quick start
10
+
11
+ Install:
12
+
13
+ ```bash
14
+ $ gem install sunzi-vps
15
+ ```
16
+
17
+ ## Commands
18
+
19
+ ```bash
20
+ $ sunzi vps # Show command help
21
+ $ sunzi vps init # Generate VPS config file
22
+ $ sunzi vps up # Set up a new instance
23
+ $ sunzi vps down # Tear down an existing instance
24
+ ```
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
11
+
12
+ namespace :vcr do
13
+ task :refresh do
14
+ FileUtils.rm_f('test/vcr/vps_up.yml')
15
+ Rake::Task['test'].invoke
16
+ end
17
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'sunzi/vps'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,51 @@
1
+ require 'sunzi/vps/mapping'
2
+
3
+ require 'sunzi/vps/compute/base'
4
+ require 'sunzi/vps/compute/linode'
5
+ require 'sunzi/vps/compute/digital_ocean'
6
+
7
+ require 'sunzi/vps/dns/base'
8
+ require 'sunzi/vps/dns/linode'
9
+ require 'sunzi/vps/dns/digital_ocean'
10
+
11
+ module Sunzi
12
+ module Vps
13
+ class Api
14
+ attr_reader :provider
15
+
16
+ def initialize(provider)
17
+ @provider = provider
18
+ Sunzi::Dependency.load(mapping[:gem])
19
+ end
20
+
21
+ def client
22
+ @client ||= begin
23
+ case provider
24
+ when 'linode'
25
+ ::Linode.new(api_key: config.api_key)
26
+ when 'digital_ocean'
27
+ DropletKit::Client.new(access_token: config.api_key)
28
+ end
29
+ end
30
+ end
31
+
32
+ def config
33
+ @config ||= YAML.load(File.read("#{provider}/#{provider}.yml")).to_hashugar
34
+ end
35
+
36
+ def compute
37
+ @compute ||= Object.const_get("Sunzi::Vps::Compute::#{mapping[:klass]}").new(self)
38
+ end
39
+
40
+ def dns
41
+ @dns ||= Object.const_get("Sunzi::Vps::DNS::#{mapping[:klass]}").new(self)
42
+ end
43
+
44
+ private
45
+
46
+ def mapping
47
+ @mapping ||= Mapping.fetch(provider.to_sym)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,27 @@
1
+ require 'sunzi/vps/init'
2
+ require 'sunzi/vps/api'
3
+
4
+ module Sunzi
5
+ module Vps
6
+ class Cli < Thor
7
+
8
+ desc 'init [provider]', 'Generate VPS config file (provider: linode, digital_ocean)'
9
+ def init(provider)
10
+ Sunzi::Vps::Init.new.run(provider)
11
+ end
12
+
13
+ desc 'up [provider]', 'Set up a new instance (provider: linode, digital_ocean)'
14
+ def up(provider)
15
+ api = Sunzi::Vps::Api.new(provider)
16
+ api.compute.up
17
+ end
18
+
19
+ desc 'down [provider]', 'Tear down an existing instance (provider: linode, digital_ocean)'
20
+ def down(provider)
21
+ api = Sunzi::Vps::Api.new(provider)
22
+ api.compute.down
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,93 @@
1
+ module Sunzi
2
+ module Vps
3
+ class Compute
4
+ class Base
5
+ include Sunzi::Actions::Delegate
6
+
7
+ delegate_to_thor :empty_directory, :template, :create_file, :remove_file, :say
8
+
9
+ attr_reader :api
10
+
11
+ def initialize(api)
12
+ @api = api
13
+ api.dns.verify # fail early if domain is not registered
14
+ end
15
+
16
+ def up
17
+ if config.api_key == 'your_api_key'
18
+ abort_with "You must have your own settings in #{api.provider}.yml"
19
+ end
20
+
21
+ # Ask environment and hostname
22
+ @env = ask("environment?", limited_to: config.environments, default: 'production')
23
+ @host = ask('hostname? (only the first part of subdomain): ')
24
+
25
+ abort_with '"label" field in linode.yml is no longer supported. rename it to "name".' if config.label
26
+ @fqdn = config.fqdn.send(@env).gsub(/%{host}/, @host)
27
+ @name = config.name.send(@env).gsub(/%{host}/, @host)
28
+ abort_with "#{@name} already exists!" if instance_config_path.exist?
29
+
30
+ @attributes = {}
31
+
32
+ # Run Linode / DigitalOcean specific tasks
33
+ do_up
34
+
35
+ # Save instance info
36
+ create_file instance_config_path, YAML.dump(@instance)
37
+
38
+ # Register IP to DNS
39
+ api.dns.add(@fqdn, @public_ip)
40
+ end
41
+
42
+ def down
43
+ names = Dir.glob("#{api.provider}/instances/*.yml").map{|i| i.split('/').last.sub('.yml','') }
44
+ abort_with "No match found with #{api.provider}/instances/*.yml" if names.empty?
45
+
46
+ names.each{|i| say i }
47
+ @name = ask('which instance?', limited_to: names)
48
+
49
+ @instance = YAML.load(instance_config_path.read).to_hashugar
50
+
51
+ # Are you sure?
52
+ moveon = ask("Are you sure about deleting #{@instance.fqdn} permanently? (y/n)", limited_to: ['y','n'])
53
+ exit unless moveon == 'y'
54
+
55
+ # Run Linode / DigitalOcean specific tasks
56
+ do_down
57
+
58
+ # Delete DNS record
59
+ api.dns.delete @instance.send(ip_key)
60
+
61
+ # Remove the instance config file
62
+ remove_file instance_config_path
63
+
64
+ say 'Done.'
65
+ end
66
+
67
+ def ask(statement, *args)
68
+ Sunzi.thor.ask statement.color(:green).bright, *args
69
+ end
70
+
71
+ def proceed?
72
+ moveon = ask("Are you ready to go ahead and create #{@fqdn}? (y/n)", limited_to: ['y','n'])
73
+ exit unless moveon == 'y'
74
+ end
75
+
76
+ private
77
+
78
+ def instance_config_path
79
+ Pathname.new "#{api.provider}/instances/#{@name}.yml"
80
+ end
81
+
82
+ def config
83
+ api.config
84
+ end
85
+
86
+ def client
87
+ @api.client
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,90 @@
1
+ module Sunzi
2
+ module Vps
3
+ class Compute
4
+ class DigitalOcean < Base
5
+
6
+ def do_up
7
+ choose(:size, client.sizes.all.to_a)
8
+ choose(:region, client.regions.all.to_a)
9
+
10
+ # Choose an image
11
+ result = client.images.all
12
+ if config.distributions_filter
13
+ result = result.select{|i| i.distribution.match Regexp.new(config.distributions_filter, Regexp::IGNORECASE) }
14
+ end
15
+ choose(:image, result)
16
+
17
+ # Go ahead?
18
+ proceed?
19
+
20
+ ssh_keys = client.ssh_keys.all.map(&:fingerprint)
21
+
22
+ # Create
23
+ say "creating a new droplets..."
24
+ droplet = client.droplets.create(
25
+ DropletKit::Droplet.new(
26
+ name: @name,
27
+ size: @attributes[:size],
28
+ image: @attributes[:image],
29
+ region: @attributes[:region],
30
+ ssh_keys: ssh_keys
31
+ )
32
+ )
33
+
34
+ @droplet_id = droplet.id
35
+ say "Created a new droplet (id: #{@droplet_id}). Booting..."
36
+
37
+ # Boot - we need this before getting public IP
38
+ while droplet.status.downcase != 'active'
39
+ sleep 3
40
+ droplet = client.droplets.find(id: @droplet_id)
41
+ end
42
+
43
+ @public_ip = droplet.networks.v4.first.ip_address
44
+ say "Done. ip address = #{@public_ip}"
45
+
46
+ @instance = {
47
+ droplet_id: @droplet_id,
48
+ env: @env,
49
+ host: @host,
50
+ fqdn: @fqdn,
51
+ name: @name,
52
+ ip_address: @public_ip,
53
+ size: @attributes[:size],
54
+ region: @attributes[:region],
55
+ image: @attributes[:image],
56
+ }
57
+ end
58
+
59
+ def choose(key, result)
60
+ abort "no #{key} found!" if result.first.nil?
61
+
62
+ rows = result.map(&:attributes).map do |h|
63
+ h.values.map do |v|
64
+ next v unless v.is_a?(Array)
65
+ if v.size > 5
66
+ v.first(5).join(', ') + ', ...'
67
+ else
68
+ v.join(', ')
69
+ end
70
+ end
71
+ end
72
+
73
+ table = Terminal::Table.new headings: result.first.attributes.keys, rows: rows
74
+ say table
75
+
76
+ @attributes[key] = ask("which #{key}?: ", default: result.first.slug, limited_to: result.map(&:slug))
77
+ end
78
+
79
+ def do_down
80
+ say 'deleting droplet...'
81
+ client.droplets.delete(id: @instance[:droplet_id])
82
+ end
83
+
84
+ def ip_key
85
+ :ip_address
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,151 @@
1
+ module Sunzi
2
+ module Vps
3
+ class Compute
4
+ class Linode < Base
5
+
6
+ def do_up
7
+ @sshkey = File.read(File.expand_path(config.root_sshkey_path)).chomp
8
+ if @sshkey.match(/\n/)
9
+ abort_with "RootSSHKey #{@sshkey.inspect} must not be multi-line! Check inside \"#{config.root_sshkey_path}\""
10
+ end
11
+
12
+ choose(:plan, client.avail.linodeplans)
13
+ choose(:datacenter, client.avail.datacenters, :label_method => :location)
14
+ choose(:distribution, client.avail.distributions, :filter => 'distributions_filter')
15
+ choose(:kernel, client.avail.kernels, :filter => 'kernels_filter')
16
+
17
+ # Choose swap size
18
+ @swap_size = ask('swap size in MB?', default: 256).to_i
19
+
20
+ # Go ahead?
21
+ proceed?
22
+
23
+ # Create
24
+ say "creating a new linode..."
25
+ result = client.linode.create(
26
+ :DatacenterID => @attributes[:datacenterid],
27
+ :PlanID => @attributes[:planid],
28
+ :PaymentTerm => config.payment_term)
29
+ @linodeid = result.linodeid
30
+ say "created a new instance: linodeid = #{@linodeid}"
31
+
32
+ result = client.linode.list.select{|i| i.linodeid == @linodeid }.first
33
+ @totalhd = result.totalhd
34
+
35
+ # Update settings
36
+ say "Updating settings..."
37
+ @group = config.group[@env]
38
+ settings = { :LinodeID => @linodeid, :Label => @name, :lpm_displayGroup => @group }
39
+ settings.update(config.settings) if config.settings
40
+ client.linode.update(settings)
41
+
42
+ # Create a root disk
43
+ say "Creating a root disk..."
44
+ result = client.linode.disk.createfromdistribution(
45
+ :LinodeID => @linodeid,
46
+ :DistributionID => @attributes[:distributionid],
47
+ :Label => "#{@attributes[:distribution_label]} Image",
48
+ :Size => @totalhd - @swap_size,
49
+ :rootPass => config.root_pass,
50
+ :rootSSHKey => @sshkey
51
+ )
52
+ @root_diskid = result.diskid
53
+
54
+ # Create a swap disk
55
+ say "Creating a swap disk..."
56
+ result = client.linode.disk.create(
57
+ :LinodeID => @linodeid,
58
+ :Label => "#{@swap_size}MB Swap Image",
59
+ :Type => 'swap',
60
+ :Size => @swap_size
61
+ )
62
+ @swap_diskid = result.diskid
63
+
64
+ # Create a config profiile
65
+ say "Creating a config profile..."
66
+ result = client.linode.config.create(
67
+ :LinodeID => @linodeid,
68
+ :KernelID => @attributes[:kernelid],
69
+ :Label => "#{@attributes[:distribution_label]} Profile",
70
+ :DiskList => [ @root_diskid, @swap_diskid ].join(',')
71
+ )
72
+ @config_id = result.configid
73
+
74
+ # Add a private IP
75
+ say "Adding a private IP..."
76
+ result = client.linode.ip.list(:LinodeID => @linodeid)
77
+ @public_ip = result.first.ipaddress
78
+ result = client.linode.ip.addprivate(:LinodeID => @linodeid)
79
+ result = client.linode.ip.list(:LinodeID => @linodeid).find{|i| i.ispublic == 0 }
80
+ @private_ip = result.ipaddress
81
+
82
+ @instance = {
83
+ :linode_id => @linodeid,
84
+ :env => @env,
85
+ :host => @host,
86
+ :fqdn => @fqdn,
87
+ :label => @name,
88
+ :group => @group,
89
+ :plan_id => @attributes[:planid],
90
+ :datacenter_id => @attributes[:datacenterid],
91
+ :datacenter_location => @attributes[:datacenter_location],
92
+ :distribution_id => @attributes[:distributionid],
93
+ :distribution_label => @attributes[:distribution_label],
94
+ :kernel_id => @attributes[:kernelid],
95
+ :kernel_label => @attributes[:kernel_label],
96
+ :swap_size => @swap_size,
97
+ :totalhd => @totalhd,
98
+ :root_diskid => @root_diskid,
99
+ :swap_diskid => @swap_diskid,
100
+ :config_id => @config_id,
101
+ :public_ip => @public_ip,
102
+ :private_ip => @private_ip,
103
+ }
104
+
105
+ # Boot
106
+ say 'Done. Booting...'
107
+ client.linode.boot(:LinodeID => @linodeid)
108
+ end
109
+
110
+ def choose(key, result, options = {})
111
+ label_method = options[:label_method] || :label
112
+ id = :"#{key}id"
113
+ label = :"#{key}_#{label_method}"
114
+
115
+ # Filters
116
+ if options[:filter] and config[options[:filter]]
117
+ result = result.select{|i| i.label.match Regexp.new(config[options[:filter]], Regexp::IGNORECASE) }
118
+ end
119
+
120
+ result.each{|i| say "#{i.send(id)}: #{i.send(label_method)}" }
121
+ @attributes[id] = ask("which #{key}?", limited_to: result.map(&id).map(&:to_s), default: result.first.send(id).to_s).to_i
122
+ @attributes[label] = result.find{|i| i.send(id) == @attributes[id] }.send(label_method)
123
+ end
124
+
125
+ def do_down
126
+ @linode_id_hash = { :LinodeID => @instance[:linode_id] }
127
+
128
+ # Shutdown first or disk deletion will fail
129
+ say 'shutting down...'
130
+ client.linode.shutdown(@linode_id_hash)
131
+ # Wait until linode.shutdown has completed
132
+ wait_for('linode.shutdown')
133
+
134
+ # Delete the instance
135
+ say 'deleting linode...'
136
+ client.linode.delete(@linode_id_hash.merge(:skipChecks => 1))
137
+ end
138
+
139
+ def ip_key
140
+ :public_ip
141
+ end
142
+
143
+ def wait_for(action)
144
+ begin
145
+ sleep 3
146
+ end until client.linode.job.list(@linode_id_hash).find{|i| i.action == action }.host_success == 1
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,12 @@
1
+ module Sunzi
2
+ module Vps
3
+ class Dependency
4
+ [
5
+ ['linode', '~> 0.7'],
6
+ ['droplet_kit', '~> 2.2'],
7
+ ].each do |name, version|
8
+ Sunzi::Dependency.new(name, version)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module Sunzi
2
+ module Vps
3
+ class DNS
4
+ class Base
5
+ include Sunzi::Actions::Delegate
6
+
7
+ delegate_to_thor :say
8
+
9
+ attr_reader :api
10
+
11
+ def initialize(api)
12
+ @api = api
13
+ @zone = api.config.fqdn.zone
14
+ end
15
+
16
+ def client
17
+ api.client
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ module Sunzi
2
+ module Vps
3
+ class DNS
4
+ class DigitalOcean < Base
5
+
6
+ def verify
7
+ @domain = client.domains.find(name: @zone)
8
+ rescue DropletKit::Error => e
9
+ abort_with "zone for #{@zone} was not found on DigitalOcean DNS!" if e.message =~ /not_found/
10
+ raise
11
+ end
12
+
13
+ def add(fqdn, ip)
14
+ say 'adding the public IP to DigitalOcean DNS...'
15
+ client.domain_records.create(
16
+ DropletKit::DomainRecord.new(
17
+ type: 'A',
18
+ name: fqdn.sub('.' + @domain.name, ''),
19
+ data: ip,
20
+ ),
21
+ for_domain: @domain.name
22
+ )
23
+ end
24
+
25
+ def delete(ip)
26
+ say 'deleting the public IP from DigitalOcean DNS...'
27
+ domain_record = client.domain_records.all(for_domain: @domain.name).find{|i| i.type == 'A' && i.data == ip }
28
+ abort_with "ip address #{ip} was not found on DigitalOcean DNS!" unless domain_record
29
+ client.domain_records.delete(id: domain_record.id, for_domain: @domain.name)
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ module Sunzi
2
+ module Vps
3
+ class DNS
4
+ class Linode < Base
5
+
6
+ def verify
7
+ @domain = client.domain.list.find{|i| i.domain == @zone }
8
+ abort_with "zone for #{@zone} was not found on Linode DNS!" unless @domain
9
+ end
10
+
11
+ def add(fqdn, ip)
12
+ say 'adding the public IP to Linode DNS Manager...'
13
+ client.domain.resource.create(:DomainID => @domain.domainid, :Type => 'A', :Name => fqdn, :Target => ip)
14
+ end
15
+
16
+ def delete(ip)
17
+ say 'deleting the public IP from Linode DNS Manager...'
18
+ resource = client.domain.resource.list(:DomainID => @domain.domainid).find{|i| i.target == ip }
19
+ abort_with "ip address #{ip} was not found on Linode DNS!" unless resource
20
+ client.domain.resource.delete(:DomainID => @domain.domainid, :ResourceID => resource.resourceid)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ module Sunzi
2
+ module Vps
3
+ class Init
4
+ include Sunzi::Actions::Delegate
5
+
6
+ delegate_to_thor :empty_directory, :template
7
+
8
+ def run(provider)
9
+ config_path = "#{provider}/#{provider}.yml"
10
+ return if File.exist? config_path
11
+
12
+ empty_directory "#{provider}/instances"
13
+ template "templates/#{provider}.yml", config_path, context: binding
14
+ exit_with "Now go ahead and edit #{provider}.yml"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ module Sunzi
2
+ module Vps
3
+ Mapping = {
4
+ linode: {
5
+ gem: 'linode',
6
+ klass: 'Linode',
7
+ },
8
+ digital_ocean: {
9
+ gem: 'droplet_kit',
10
+ klass: 'DigitalOcean',
11
+ },
12
+ }.freeze
13
+ end
14
+ end
data/lib/sunzi/vps.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'terminal-table'
2
+
3
+ require 'sunzi'
4
+ require 'sunzi/vps/dependency'
5
+ require 'sunzi/vps/cli'
6
+
7
+ module Sunzi
8
+ class Cli
9
+ desc 'vps [...]', 'VPS setup/teardown commands'
10
+ subcommand 'vps', Sunzi::Vps::Cli
11
+ end
12
+ end
data/sunzi-vps.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'sunzi-vps'
5
+ spec.version = '0.1.0' # retrieve this value by: Gem.loaded_specs['sunzi'].version.to_s
6
+ spec.authors = ['Kenn Ejima']
7
+ spec.email = ['kenn.ejima@gmail.com']
8
+ spec.summary = %q{Sunzi VPS}
9
+ spec.description = %q{Simple CLI to create and/or destroy VPS instances}
10
+ spec.homepage = 'https://github.com/kenn/sunzi-vps'
11
+ spec.license = 'MIT'
12
+ spec.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test|spec|features)/}) }
13
+ spec.bindir = 'exe'
14
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
15
+ spec.require_paths = ['lib']
16
+
17
+ spec.add_dependency 'sunzi', '~> 2.0'
18
+ spec.add_dependency 'hashugar', '~> 1.0'
19
+ spec.add_dependency 'terminal-table', '~> 1.8'
20
+ spec.add_development_dependency 'bundler', '~> 1.16'
21
+ spec.add_development_dependency 'rake', '~> 10.0'
22
+ spec.add_development_dependency 'minitest', '~> 5.0'
23
+ spec.add_development_dependency 'vcr', '~> 4.0'
24
+ spec.add_development_dependency 'webmock', '~> 3.2'
25
+ spec.add_development_dependency 'linode', '~> 0.7'
26
+ spec.add_development_dependency 'droplet_kit', '~> 2.2'
27
+ end
@@ -0,0 +1,18 @@
1
+ ---
2
+ api_key: your_api_key
3
+ client_id: your_client_id
4
+
5
+ # add / remove environments
6
+ environments:
7
+ - production
8
+ - staging
9
+ fqdn:
10
+ zone: example.com
11
+ production: '%{host}.example.com'
12
+ staging: '%{host}.staging.example.com'
13
+ name:
14
+ production: 'example-%{host}'
15
+ staging: 'example-staging-%{host}'
16
+
17
+ # filter out large lists by keyword
18
+ distributions_filter: debian
@@ -0,0 +1,34 @@
1
+ ---
2
+ api_key: your_api_key
3
+ root_pass: your_root_password
4
+ root_sshkey_path: ~/.ssh/id_rsa.pub
5
+
6
+ # payment_term must be 1, 12 or 24
7
+ payment_term: 1
8
+
9
+ # add / remove environments
10
+ environments:
11
+ - production
12
+ - staging
13
+ fqdn:
14
+ zone: example.com
15
+ production: '%{host}.example.com'
16
+ staging: '%{host}.staging.example.com'
17
+ name:
18
+ production: 'example-%{host}'
19
+ staging: 'example-staging-%{host}'
20
+ group:
21
+ production: example
22
+ staging: example-staging
23
+
24
+ # filter out large lists by keyword
25
+ distributions_filter: debian
26
+ kernels_filter: latest
27
+
28
+ # other parameters for settings.
29
+ # settings:
30
+ # alert_cpu_threshold: 90
31
+ # alert_diskio_threshold: 1000
32
+ # alert_bwin_threshold: 5
33
+ # alert_bwout_threshold: 5
34
+ # alert_bwquota_threshold: 80
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sunzi-vps
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kenn Ejima
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sunzi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashugar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: terminal-table
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.16'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.16'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: vcr
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.2'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.2'
125
+ - !ruby/object:Gem::Dependency
126
+ name: linode
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.7'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.7'
139
+ - !ruby/object:Gem::Dependency
140
+ name: droplet_kit
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '2.2'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.2'
153
+ description: Simple CLI to create and/or destroy VPS instances
154
+ email:
155
+ - kenn.ejima@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - ".travis.yml"
162
+ - Gemfile
163
+ - README.md
164
+ - Rakefile
165
+ - bin/console
166
+ - bin/setup
167
+ - lib/sunzi/vps.rb
168
+ - lib/sunzi/vps/api.rb
169
+ - lib/sunzi/vps/cli.rb
170
+ - lib/sunzi/vps/compute/base.rb
171
+ - lib/sunzi/vps/compute/digital_ocean.rb
172
+ - lib/sunzi/vps/compute/linode.rb
173
+ - lib/sunzi/vps/dependency.rb
174
+ - lib/sunzi/vps/dns/base.rb
175
+ - lib/sunzi/vps/dns/digital_ocean.rb
176
+ - lib/sunzi/vps/dns/linode.rb
177
+ - lib/sunzi/vps/init.rb
178
+ - lib/sunzi/vps/mapping.rb
179
+ - sunzi-vps.gemspec
180
+ - templates/digital_ocean.yml
181
+ - templates/linode.yml
182
+ homepage: https://github.com/kenn/sunzi-vps
183
+ licenses:
184
+ - MIT
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.7.3
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: Sunzi VPS
206
+ test_files: []