vagrant-simple_cloud 0.0.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.
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