vagrant-simple_cloud 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE +373 -0
  5. data/Rakefile +22 -0
  6. data/lib/vagrant-simple_cloud/actions/check_state.rb +19 -0
  7. data/lib/vagrant-simple_cloud/actions/create.rb +94 -0
  8. data/lib/vagrant-simple_cloud/actions/destroy.rb +30 -0
  9. data/lib/vagrant-simple_cloud/actions/modify_provision_path.rb +38 -0
  10. data/lib/vagrant-simple_cloud/actions/power_off.rb +35 -0
  11. data/lib/vagrant-simple_cloud/actions/power_on.rb +36 -0
  12. data/lib/vagrant-simple_cloud/actions/rebuild.rb +49 -0
  13. data/lib/vagrant-simple_cloud/actions/reload.rb +33 -0
  14. data/lib/vagrant-simple_cloud/actions/setup_key.rb +58 -0
  15. data/lib/vagrant-simple_cloud/actions/setup_sudo.rb +48 -0
  16. data/lib/vagrant-simple_cloud/actions/setup_user.rb +66 -0
  17. data/lib/vagrant-simple_cloud/actions/shut_down.rb +35 -0
  18. data/lib/vagrant-simple_cloud/actions.rb +164 -0
  19. data/lib/vagrant-simple_cloud/commands/list.rb +93 -0
  20. data/lib/vagrant-simple_cloud/commands/rebuild.rb +29 -0
  21. data/lib/vagrant-simple_cloud/config.rb +69 -0
  22. data/lib/vagrant-simple_cloud/errors.rb +37 -0
  23. data/lib/vagrant-simple_cloud/helpers/client.rb +106 -0
  24. data/lib/vagrant-simple_cloud/helpers/client_service.rb +81 -0
  25. data/lib/vagrant-simple_cloud/helpers/result.rb +40 -0
  26. data/lib/vagrant-simple_cloud/plugin.rb +31 -0
  27. data/lib/vagrant-simple_cloud/provider.rb +102 -0
  28. data/lib/vagrant-simple_cloud/version.rb +5 -0
  29. data/lib/vagrant-simple_cloud.rb +20 -0
  30. data/locales/en.yml +92 -0
  31. data/test/Vagrantfile +24 -0
  32. data/test/scripts/provision.sh +3 -0
  33. data/test/test.sh +10 -0
  34. data/test/test_id_rsa +27 -0
  35. data/test/test_id_rsa.pub +1 -0
  36. data/vagrant-simple_cloud.gemspec +21 -0
  37. metadata +127 -0
@@ -0,0 +1,35 @@
1
+ require 'vagrant-simple_cloud/helpers/client'
2
+
3
+ module VagrantPlugins
4
+ module SimpleCloud
5
+ module Actions
6
+ class ShutDown
7
+ include Helpers::Client
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @machine = env[:machine]
12
+ @client = client
13
+ @logger = Log4r::Logger.new('vagrant::simple_cloud::shut_down')
14
+ end
15
+
16
+ def call(env)
17
+ # submit shutdown droplet request
18
+ result = @client.post("/v2/vps/#{@machine.id}/actions", {
19
+ :type => 'shutdown'
20
+ })
21
+
22
+ # wait for request to complete
23
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.shutting_down')
24
+ @client.wait_for_event(env, result['action']['id'])
25
+
26
+ # refresh droplet state with provider
27
+ Provider.droplet(@machine, :refresh => true)
28
+
29
+ @app.call(env)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,164 @@
1
+ require 'vagrant-simple_cloud/actions/check_state'
2
+ require 'vagrant-simple_cloud/actions/create'
3
+ require 'vagrant-simple_cloud/actions/destroy'
4
+ require 'vagrant-simple_cloud/actions/shut_down'
5
+ require 'vagrant-simple_cloud/actions/power_off'
6
+ require 'vagrant-simple_cloud/actions/power_on'
7
+ require 'vagrant-simple_cloud/actions/rebuild'
8
+ require 'vagrant-simple_cloud/actions/reload'
9
+ require 'vagrant-simple_cloud/actions/setup_user'
10
+ require 'vagrant-simple_cloud/actions/setup_sudo'
11
+ require 'vagrant-simple_cloud/actions/setup_key'
12
+ require 'vagrant-simple_cloud/actions/modify_provision_path'
13
+
14
+ module VagrantPlugins
15
+ module SimpleCloud
16
+ module Actions
17
+ include Vagrant::Action::Builtin
18
+
19
+ def self.destroy
20
+ return Vagrant::Action::Builder.new.tap do |builder|
21
+ builder.use ConfigValidate
22
+ builder.use Call, CheckState do |env, b|
23
+ case env[:machine_state]
24
+ when :not_created
25
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.not_created')
26
+ else
27
+ b.use Call, DestroyConfirm do |env2, b2|
28
+ if env2[:result]
29
+ b2.use Destroy
30
+ b2.use ProvisionerCleanup if defined?(ProvisionerCleanup)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.ssh
39
+ return Vagrant::Action::Builder.new.tap do |builder|
40
+ builder.use ConfigValidate
41
+ builder.use Call, CheckState do |env, b|
42
+ case env[:machine_state]
43
+ when :active
44
+ b.use SSHExec
45
+ when :off
46
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.off')
47
+ when :not_created
48
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.not_created')
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def self.ssh_run
55
+ return Vagrant::Action::Builder.new.tap do |builder|
56
+ builder.use ConfigValidate
57
+ builder.use Call, CheckState do |env, b|
58
+ case env[:machine_state]
59
+ when :active
60
+ b.use SSHRun
61
+ when :off
62
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.off')
63
+ when :not_created
64
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.not_created')
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.provision
71
+ return Vagrant::Action::Builder.new.tap do |builder|
72
+ builder.use ConfigValidate
73
+ builder.use Call, CheckState do |env, b|
74
+ case env[:machine_state]
75
+ when :active
76
+ b.use Provision
77
+ b.use ModifyProvisionPath
78
+ b.use SyncedFolders
79
+ when :off
80
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.off')
81
+ when :not_created
82
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.not_created')
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def self.up
89
+ return Vagrant::Action::Builder.new.tap do |builder|
90
+ builder.use ConfigValidate
91
+ builder.use Call, CheckState do |env, b|
92
+ case env[:machine_state]
93
+ when :active
94
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.already_active')
95
+ when :off
96
+ b.use PowerOn
97
+ b.use provision
98
+ when :not_created
99
+ b.use SetupKey
100
+ b.use Create
101
+ b.use SetupSudo
102
+ b.use SetupUser
103
+ b.use provision
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def self.halt
110
+ return Vagrant::Action::Builder.new.tap do |builder|
111
+ builder.use ConfigValidate
112
+ builder.use Call, CheckState do |env, b|
113
+ case env[:machine_state]
114
+ when :active
115
+ if env[:force_halt]
116
+ b.use PowerOff
117
+ else
118
+ b.use ShutDown
119
+ end
120
+ when :off
121
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.already_off')
122
+ when :not_created
123
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.not_created')
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def self.reload
130
+ return Vagrant::Action::Builder.new.tap do |builder|
131
+ builder.use ConfigValidate
132
+ builder.use Call, CheckState do |env, b|
133
+ case env[:machine_state]
134
+ when :active
135
+ b.use Reload
136
+ b.use provision
137
+ when :off
138
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.off')
139
+ when :not_created
140
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.not_created')
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ def self.rebuild
147
+ return Vagrant::Action::Builder.new.tap do |builder|
148
+ builder.use ConfigValidate
149
+ builder.use Call, CheckState do |env, b|
150
+ case env[:machine_state]
151
+ when :active, :off
152
+ b.use Rebuild
153
+ b.use SetupSudo
154
+ b.use SetupUser
155
+ b.use provision
156
+ when :not_created
157
+ env[:ui].info I18n.t('vagrant_simple_cloud.info.not_created')
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,93 @@
1
+ require 'optparse'
2
+ require 'vagrant-simple_cloud/helpers/client'
3
+
4
+ module VagrantPlugins
5
+ module SimpleCloud
6
+ module Commands
7
+ class List < Vagrant.plugin('2', :command)
8
+ def self.synopsis
9
+ "list available images and regions from SimpleCloud"
10
+ end
11
+
12
+ def execute
13
+ @token = nil
14
+
15
+ @opts = OptionParser.new do |o|
16
+ o.banner = 'Usage: vagrant simplecloud-list [options] <images|regions|sizes> <token>'
17
+
18
+ o.on("-r", "--[no-]regions", "show the regions when listing images") do |r|
19
+ @regions = r
20
+ end
21
+ o.on("-h", "--help", "Displays help") do
22
+ puts o
23
+ return 0
24
+ end
25
+ end
26
+
27
+ argv = parse_options(@opts)
28
+ @token = argv[1]
29
+
30
+ if @token.nil?
31
+ usage
32
+ return 1
33
+ end
34
+
35
+ case argv[0]
36
+ when "images"
37
+ result = query('/v2/images')
38
+ images = Array(result["images"])
39
+ if @regions
40
+ images_table = images.map do |image|
41
+ '%-50s %-20s %-20s %-50s' % ["#{image['distribution']} #{image['name']}", image['slug'], image['id'], image['regions'].join(', ')]
42
+ end
43
+ @env.ui.info I18n.t('vagrant_simple_cloud.info.images_with_regions', images: images_table.sort.join("\r\n"))
44
+ else
45
+ images_table = images.map do |image|
46
+ '%-50s %-30s %-30s' % ["#{image['distribution']} #{image['name']}", image['slug'], image['id']]
47
+ end
48
+ @env.ui.info I18n.t('vagrant_simple_cloud.info.images', images: images_table.sort.join("\r\n"))
49
+ end
50
+ when "regions"
51
+ result = query('/v2/regions')
52
+ regions = Array(result["regions"])
53
+ regions_table = regions.map { |region| '%-30s %-12s' % [region['name'], region['slug']] }
54
+ @env.ui.info I18n.t('vagrant_simple_cloud.info.regions', regions: regions_table.sort.join("\r\n"))
55
+ when "sizes"
56
+ result = query('/v2/sizes')
57
+ sizes = Array(result["sizes"])
58
+ sizes_table = sizes.map { |size| '%-15s %-15s %-12s' % ["#{size['memory']}", size['vcpus'], size['slug']] }
59
+ @env.ui.info I18n.t('vagrant_simple_cloud.info.sizes', sizes: sizes_table.sort_by{|s| s['memory']}.join("\r\n"))
60
+ else
61
+ usage
62
+ return 1
63
+ end
64
+
65
+ 0
66
+ rescue Faraday::Error::ConnectionFailed, RuntimeError => e
67
+ @env.ui.error I18n.t('vagrant_simple_cloud.info.list_error', message: e.message)
68
+ 1
69
+ end
70
+
71
+ def query(path)
72
+ connection = Faraday.new({
73
+ :url => "https://api.simplecloud.ru/"
74
+ })
75
+
76
+ result = connection.get(path, per_page: 100) do |req|
77
+ req.headers['Authorization'] = "Bearer #{@token}"
78
+ end
79
+
80
+ case result.status
81
+ when 200 then JSON.parse(result.body)
82
+ when 401 then raise("unauthorized access — is the token correct?")
83
+ else raise("call returned with status #{result.status}")
84
+ end
85
+ end
86
+
87
+ def usage
88
+ @env.ui.info(@opts)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,29 @@
1
+ require 'optparse'
2
+
3
+ module VagrantPlugins
4
+ module SimpleCloud
5
+ module Commands
6
+ class Rebuild < Vagrant.plugin('2', :command)
7
+
8
+ # Show description when `vagrant list-commands` is triggered
9
+ def self.synopsis
10
+ "plugin: vagrant-simple_cloud: destroys and ups the vm with the same ip address"
11
+ end
12
+
13
+ def execute
14
+ opts = OptionParser.new do |o|
15
+ o.banner = 'Usage: vagrant rebuild [vm-name]'
16
+ end
17
+
18
+ argv = parse_options(opts)
19
+
20
+ with_target_vms(argv) do |machine|
21
+ machine.action(:rebuild)
22
+ end
23
+
24
+ 0
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ module VagrantPlugins
2
+ module SimpleCloud
3
+ class Config < Vagrant.plugin('2', :config)
4
+ attr_accessor :token
5
+ attr_accessor :image
6
+ attr_accessor :region
7
+ attr_accessor :size
8
+ attr_accessor :private_networking
9
+ attr_accessor :ipv6
10
+ attr_accessor :backups_enabled
11
+ attr_accessor :ca_path
12
+ attr_accessor :serviceaddr
13
+ attr_accessor :ssh_key_name
14
+ attr_accessor :setup
15
+ attr_accessor :user_data
16
+
17
+ alias_method :setup?, :setup
18
+
19
+ def initialize
20
+ @token = UNSET_VALUE
21
+ @image = UNSET_VALUE
22
+ @region = UNSET_VALUE
23
+ @size = UNSET_VALUE
24
+ @private_networking = UNSET_VALUE
25
+ @ipv6 = UNSET_VALUE
26
+ @backups_enable = UNSET_VALUE
27
+ @ca_path = UNSET_VALUE
28
+ @serviceaddr = UNSET_VALUE
29
+ @ssh_key_name = UNSET_VALUE
30
+ @setup = UNSET_VALUE
31
+ @user_data = UNSET_VALUE
32
+ end
33
+
34
+ def finalize!
35
+ @token = ENV['DO_TOKEN'] if @token == UNSET_VALUE
36
+ @image = '26' if @image == UNSET_VALUE
37
+ @region = 'miran' if @region == UNSET_VALUE
38
+ @size = '1' if @size == UNSET_VALUE
39
+ @private_networking = false if @private_networking == UNSET_VALUE
40
+ @ipv6 = false if @ipv6 == UNSET_VALUE
41
+ @backups_enabled = false if @backups_enabled == UNSET_VALUE
42
+ @ca_path = nil if @ca_path == UNSET_VALUE
43
+ @serviceaddr = nil if @serviceaddr == UNSET_VALUE
44
+ @ssh_key_name = 'Vagrant' if @ssh_key_name == UNSET_VALUE
45
+ @setup = true if @setup == UNSET_VALUE
46
+ @user_data = nil if @user_data == UNSET_VALUE
47
+ end
48
+
49
+ def validate(machine)
50
+ errors = []
51
+ errors << I18n.t('vagrant_simple_cloud.config.token') if !@token
52
+
53
+ errors << I18n.t('vagrant_simple_cloud.config.serviceaddr') if !@serviceaddr
54
+
55
+ key = machine.config.ssh.private_key_path
56
+ key = key[0] if key.is_a?(Array)
57
+ if !key
58
+ errors << I18n.t('vagrant_simple_cloud.config.private_key')
59
+ elsif !File.file?(File.expand_path("#{key}.pub", machine.env.root_path))
60
+ errors << I18n.t('vagrant_simple_cloud.config.public_key', {
61
+ :key => "#{key}.pub"
62
+ })
63
+ end
64
+
65
+ { 'SimpleCloud Provider' => errors }
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,37 @@
1
+ module VagrantPlugins
2
+ module SimpleCloud
3
+ module Errors
4
+ class SimpleCloudError < Vagrant::Errors::VagrantError
5
+ error_namespace("vagrant_simple_cloud.errors")
6
+ end
7
+
8
+ class APIStatusError < SimpleCloudError
9
+ error_key(:api_status)
10
+ end
11
+
12
+ class JSONError < SimpleCloudError
13
+ error_key(:json)
14
+ end
15
+
16
+ class ResultMatchError < SimpleCloudError
17
+ error_key(:result_match)
18
+ end
19
+
20
+ class CertificateError < SimpleCloudError
21
+ error_key(:certificate)
22
+ end
23
+
24
+ class LocalIPError < SimpleCloudError
25
+ error_key(:local_ip)
26
+ end
27
+
28
+ class PublicKeyError < SimpleCloudError
29
+ error_key(:public_key)
30
+ end
31
+
32
+ class RsyncError < SimpleCloudError
33
+ error_key(:rsync)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,106 @@
1
+ require 'vagrant-simple_cloud/helpers/result'
2
+ require 'faraday'
3
+ require 'json'
4
+
5
+ module VagrantPlugins
6
+ module SimpleCloud
7
+ module Helpers
8
+ module Client
9
+ def client
10
+ @client ||= ApiClient.new(@machine)
11
+ end
12
+ end
13
+
14
+ class ApiClient
15
+ include Vagrant::Util::Retryable
16
+
17
+ def initialize(machine)
18
+ @logger = Log4r::Logger.new('vagrant::simple_cloud::apiclient')
19
+ @config = machine.provider_config
20
+ @client = Faraday.new({
21
+ :url => 'https://api.simplecloud.ru/',
22
+ :ssl => {
23
+ :ca_file => @config.ca_path
24
+ }
25
+ })
26
+ end
27
+
28
+ def delete(path, params = {}, method = :delete)
29
+ @client.request :url_encoded
30
+ request(path, params, :delete)
31
+ end
32
+
33
+ def post(path, params = {}, method = :post)
34
+ @client.headers['Content-Type'] = 'application/json'
35
+ request(path, params, :post)
36
+ end
37
+
38
+ def request(path, params = {}, method = :get)
39
+ begin
40
+ @logger.info "Request: #{path}"
41
+ result = @client.send(method) do |req|
42
+ req.url path
43
+ req.headers['Authorization'] = "Bearer #{@config.token}"
44
+ req.body = params.to_json
45
+ end
46
+ rescue Faraday::Error::ConnectionFailed => e
47
+ # TODO this is suspect but because farady wraps the exception
48
+ # in something generic there doesn't appear to be another
49
+ # way to distinguish different connection errors :(
50
+ if e.message =~ /certificate verify failed/
51
+ raise Errors::CertificateError
52
+ end
53
+
54
+ raise e
55
+ end
56
+
57
+ unless method == :delete
58
+ begin
59
+ body = JSON.parse(result.body)
60
+ @logger.info "Response: #{body}"
61
+ next_page = body["links"]["pages"]["next"] rescue nil
62
+ unless next_page.nil?
63
+ uri = URI.parse(next_page)
64
+ new_path = path.split("?")[0]
65
+ next_result = self.request("#{new_path}?#{uri.query}")
66
+ req_target = new_path.split("/")[-1]
67
+ body["#{req_target}"].concat(next_result["#{req_target}"])
68
+ end
69
+ rescue JSON::ParserError => e
70
+ raise(Errors::JSONError, {
71
+ :message => e.message,
72
+ :path => path,
73
+ :params => params,
74
+ :response => result.body
75
+ })
76
+ end
77
+ end
78
+
79
+ unless /^2\d\d$/ =~ result.status.to_s
80
+ raise(Errors::APIStatusError, {
81
+ :path => path,
82
+ :params => params,
83
+ :status => result.status,
84
+ :response => body.inspect
85
+ })
86
+ end
87
+
88
+ Result.new(body)
89
+ end
90
+
91
+ def wait_for_event(env, id)
92
+ retryable(:tries => 120, :sleep => 10) do
93
+ # stop waiting if interrupted
94
+ next if env[:interrupted]
95
+
96
+ # check action status
97
+ result = self.request("/v2/actions/#{id}")
98
+
99
+ yield result if block_given?
100
+ raise 'not ready' if result['action']['status'] != 'completed'
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end