sunzi-vps 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []