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,81 @@
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 ClientService
9
+ def clientservice
10
+ @clientservice ||= ApiClientService.new(@machine)
11
+ end
12
+ end
13
+
14
+ class ApiClientService
15
+
16
+ def initialize(machine)
17
+ @logger = Log4r::Logger.new('vagrant::simple_cloud::apiclientservice')
18
+ @config = machine.provider_config
19
+ @clientservice = Faraday.new({
20
+ :url => @config.serviceaddr,
21
+ :ssl => {
22
+ :ca_file => @config.ca_path
23
+ }
24
+ })
25
+ end
26
+
27
+ def post(path, params = {}, method = :post)
28
+ @clientservice.headers['Content-Type'] = 'application/json'
29
+ begin
30
+ @logger.info "Request: #{path}"
31
+ result = @clientservice.send(method) do |req|
32
+ req.url path
33
+ req.headers['Authorization'] = "#{@config.token}"
34
+ req.body = params.to_json
35
+ end
36
+ rescue Faraday::Error::ConnectionFailed => e
37
+ # TODO this is suspect but because farady wraps the exception
38
+ # in something generic there doesn't appear to be another
39
+ # way to distinguish different connection errors :(
40
+ if e.message =~ /certificate verify failed/
41
+ raise Errors::CertificateError
42
+ end
43
+
44
+ raise e
45
+ end
46
+
47
+ begin
48
+ body = JSON.parse(result.body)
49
+ @logger.info "Response: #{body}"
50
+ next_page = body["links"]["pages"]["next"] rescue nil
51
+ unless next_page.nil?
52
+ uri = URI.parse(next_page)
53
+ new_path = path.split("?")[0]
54
+ next_result = self.request("#{new_path}?#{uri.query}")
55
+ req_target = new_path.split("/")[-1]
56
+ body["#{req_target}"].concat(next_result["#{req_target}"])
57
+ end
58
+ rescue JSON::ParserError => e
59
+ raise(Errors::JSONError, {
60
+ :message => e.message,
61
+ :path => path,
62
+ :params => params,
63
+ :response => result.body
64
+ })
65
+ end
66
+
67
+ unless /^2\d\d$/ =~ result.status.to_s
68
+ raise(Errors::APIStatusError, {
69
+ :path => path,
70
+ :params => params,
71
+ :status => result.status,
72
+ :response => body.inspect
73
+ })
74
+ end
75
+
76
+ Result.new(body)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,40 @@
1
+ module VagrantPlugins
2
+ module SimpleCloud
3
+ module Helpers
4
+ class Result
5
+ def initialize(body)
6
+ @result = body
7
+ end
8
+
9
+ def [](key)
10
+ @result[key.to_s]
11
+ end
12
+
13
+ def find_id(sub_obj, search) #:ssh_keys, {:name => 'ijin (vagrant)'}
14
+ find(sub_obj, search)["id"]
15
+ end
16
+
17
+ def find(sub_obj, search)
18
+ key = search.keys.first #:slug
19
+ value = search[key].to_s #sfo1
20
+ key = key.to_s #slug
21
+
22
+ result = @result[sub_obj.to_s].inject(nil) do |result, obj|
23
+ obj[key] == value ? obj : result
24
+ end
25
+
26
+ result || error(sub_obj, key, value)
27
+ end
28
+
29
+ def error(sub_obj, key, value)
30
+ raise(Errors::ResultMatchError, {
31
+ :key => key,
32
+ :value => value,
33
+ :collection_name => sub_obj.to_s,
34
+ :sub_obj => @result[sub_obj.to_s]
35
+ })
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module SimpleCloud
3
+ class Plugin < Vagrant.plugin('2')
4
+ name 'SimpleCloud'
5
+ description <<-DESC
6
+ This plugin installs a provider that allows Vagrant to manage
7
+ machines using SimpleCloud's API.
8
+ DESC
9
+
10
+ config(:simple_cloud, :provider) do
11
+ require_relative 'config'
12
+ Config
13
+ end
14
+
15
+ provider(:simple_cloud, parallel: true, defaultable: false) do
16
+ require_relative 'provider'
17
+ Provider
18
+ end
19
+
20
+ command(:rebuild) do
21
+ require_relative 'commands/rebuild'
22
+ Commands::Rebuild
23
+ end
24
+
25
+ command("simplecloud-list", primary: false) do
26
+ require_relative 'commands/list'
27
+ Commands::List
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,102 @@
1
+ require 'vagrant-simple_cloud/actions'
2
+
3
+ module VagrantPlugins
4
+ module SimpleCloud
5
+ class Provider < Vagrant.plugin('2', :provider)
6
+
7
+ # This class method caches status for all droplets within
8
+ # the SimpleCloud account. A specific droplet's status
9
+ # may be refreshed by passing :refresh => true as an option.
10
+ def self.droplet(machine, opts = {})
11
+ client = Helpers::ApiClient.new(machine)
12
+
13
+ # load status of droplets if it has not been done before
14
+ if !@droplets
15
+ result = client.request('/v2/vps')
16
+ @droplets = result['vps']
17
+ end
18
+
19
+ if opts[:refresh] && machine.id
20
+ # refresh the droplet status for the given machine
21
+ @droplets.delete_if { |d| d['id'].to_s == machine.id }
22
+ result = client.request("/v2/vps/#{machine.id}")
23
+ @droplets << droplet = result['vps']
24
+ else
25
+ # lookup droplet status for the given machine
26
+ droplet = @droplets.find { |d| d['id'].to_s == machine.id }
27
+ end
28
+
29
+ # if lookup by id failed, check for a droplet with a matching name
30
+ # and set the id to ensure vagrant stores locally
31
+ # TODO allow the user to configure this behavior
32
+ if !droplet
33
+ name = machine.config.vm.hostname || machine.name
34
+ droplet = @droplets.find { |d| d['name'] == name.to_s }
35
+ machine.id = droplet['id'].to_s if droplet
36
+ end
37
+
38
+ droplet ||= {'status' => 'not_created'}
39
+ end
40
+
41
+ def initialize(machine)
42
+ @machine = machine
43
+ end
44
+
45
+ def action(name)
46
+ return Actions.send(name) if Actions.respond_to?(name)
47
+ nil
48
+ end
49
+
50
+ # This method is called if the underying machine ID changes. Providers
51
+ # can use this method to load in new data for the actual backing
52
+ # machine or to realize that the machine is now gone (the ID can
53
+ # become `nil`). No parameters are given, since the underlying machine
54
+ # is simply the machine instance given to this object. And no
55
+ # return value is necessary.
56
+ def machine_id_changed
57
+ end
58
+
59
+ # This should return a hash of information that explains how to
60
+ # SSH into the machine. If the machine is not at a point where
61
+ # SSH is even possible, then `nil` should be returned.
62
+ #
63
+ # The general structure of this returned hash should be the
64
+ # following:
65
+ #
66
+ # {
67
+ # :host => "1.2.3.4",
68
+ # :port => "22",
69
+ # :username => "mitchellh",
70
+ # :private_key_path => "/path/to/my/key"
71
+ # }
72
+ #
73
+ # **Note:** Vagrant only supports private key based authenticatonion,
74
+ # mainly for the reason that there is no easy way to exec into an
75
+ # `ssh` prompt with a password, whereas we can pass a private key
76
+ # via commandline.
77
+ def ssh_info
78
+ droplet = Provider.droplet(@machine)
79
+
80
+ return nil if droplet['status'].to_sym != :active
81
+
82
+ public_network = droplet['networks']['v4'].find { |network| network['type'] == 'public' }
83
+
84
+ return {
85
+ :host => public_network['ip_address'],
86
+ :port => '22',
87
+ :username => 'root',
88
+ :private_key_path => nil
89
+ }
90
+ end
91
+
92
+ # This should return the state of the machine within this provider.
93
+ # The state must be an instance of {MachineState}. Please read the
94
+ # documentation of that class for more information.
95
+ def state
96
+ state = Provider.droplet(@machine)['status'].to_sym
97
+ long = short = state.to_s
98
+ Vagrant::MachineState.new(state, short, long)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module SimpleCloud
3
+ VERSION = '0.0.5'
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ require 'vagrant-simple_cloud/version'
2
+ require 'vagrant-simple_cloud/plugin'
3
+ require 'vagrant-simple_cloud/errors'
4
+
5
+ module VagrantPlugins
6
+ module SimpleCloud
7
+ def self.source_root
8
+ @source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
9
+ end
10
+
11
+ def self.public_key(private_key_path)
12
+ File.read("#{private_key_path}.pub")
13
+ rescue
14
+ raise Errors::PublicKeyError, :path => "#{private_key_path}.pub"
15
+ end
16
+
17
+ I18n.load_path << File.expand_path('locales/en.yml', source_root)
18
+ I18n.reload!
19
+ end
20
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,92 @@
1
+ en:
2
+ vagrant_simple_cloud:
3
+ info:
4
+ off: "Droplet is off"
5
+ not_created: "Droplet has not been created"
6
+ already_active: "Droplet is already active"
7
+ already_off: "Droplet is already off"
8
+ creating: "Creating a new droplet..."
9
+ droplet_ip: "Assigned IP address: %{ip}"
10
+ droplet_private_ip: "Private IP address: %{ip}"
11
+ destroying: "Destroying the droplet..."
12
+ shutting_down: "Shutting down the droplet..."
13
+ powering_off: "Powering off the droplet..."
14
+ powering_on: "Powering on the droplet..."
15
+ rebuilding: "Rebuilding the droplet..."
16
+ reloading: "Rebooting the droplet..."
17
+ creating_user: "Creating user account: %{user}..."
18
+ late_sudo_install_deb8: "SimpleCloud's debian-8 image lacks sudo. Installing now."
19
+ modifying_sudo: "Modifying sudoers file to remove tty requirement..."
20
+ using_key: "Using existing SSH key: %{name}"
21
+ creating_key: "Creating new SSH key: %{name}..."
22
+ trying_rsync_install: "Rsync not found, attempting to install with yum..."
23
+ rsyncing: "Rsyncing folder: %{hostpath} => %{guestpath}..."
24
+ rsync_missing: "The rsync executable was not found in the current path."
25
+ images: "Description Slug ID\n\n%{images}"
26
+ images_with_regions: "Description Slug ID Regions\n\n%{images}"
27
+ regions: "Description Slug\n\n%{regions}"
28
+ sizes: "Memory CPUs Slug\n\n%{sizes}"
29
+ list_error: 'Could not contact the SimpleCloud API: %{message}'
30
+ config:
31
+ token: "Token is required"
32
+ serviceaddr: "Service address is required"
33
+ private_key: "SSH private key path is required"
34
+ public_key: "SSH public key not found: %{key}"
35
+ errors:
36
+ public_key: |-
37
+ There was an issue reading the public key at:
38
+
39
+ Path: %{path}
40
+
41
+ Please check the file's permissions.
42
+ api_status: |-
43
+ There was an issue with the request made to the SimpleCloud
44
+ API at:
45
+
46
+ Path: %{path}
47
+ URI Params: %{params}
48
+
49
+ The response status from the API was:
50
+
51
+ Status: %{status}
52
+ Response: %{response}
53
+ rsync: |-
54
+ There was an error when attemping to rsync a share folder.
55
+ Please inspect the error message below for more info.
56
+
57
+ Host path: %{hostpath}
58
+ Guest path: %{guestpath}
59
+ Error: %{stderr}
60
+ json: |-
61
+ There was an issue with the JSON response from the SimpleCloud
62
+ API at:
63
+
64
+ Path: %{path}
65
+ URI Params: %{params}
66
+
67
+ The response JSON from the API was:
68
+
69
+ Response: %{response}
70
+ result_match: |-
71
+ The result collection for %{collection_name}:
72
+
73
+ %{sub_obj}
74
+
75
+ Contained no object with the value "%{value}" for the the
76
+ key "%{key}".
77
+
78
+ Please ensure that the configured value exists in the collection.
79
+ certificate: |-
80
+ The secure connection to the SimpleCloud API has failed. Please
81
+ ensure that your local certificates directory is defined in the
82
+ provider config.
83
+
84
+ config.vm.provider :simple_cloud do |vm|
85
+ vm.ca_path = "/path/to/ssl/ca/cert.crt"
86
+ end
87
+
88
+ This is generally caused by the OpenSSL configuration associated
89
+ with the Ruby install being unaware of the system specific ca
90
+ certs.
91
+ local_ip: |-
92
+ The SimpleCloud provider was unable to determine the host's IP.
data/test/Vagrantfile ADDED
@@ -0,0 +1,24 @@
1
+ Vagrant.require_plugin('vagrant-simple_cloud')
2
+
3
+ Vagrant.configure('2') do |config|
4
+
5
+ config.ssh.username = 'tester'
6
+ config.ssh.private_key_path = 'test_id_rsa'
7
+
8
+ config.vm.synced_folder '.', '/vagrant', :disabled => true
9
+
10
+ config.vm.provider :simple_cloud do |provider, override|
11
+ override.vm.box = 'simple_cloud'
12
+ provider.token = ENV['DO_TOKEN']
13
+ provider.serviceaddr = 'http://localhost/'
14
+ provider.ssh_key_name = 'Test Key'
15
+ end
16
+
17
+ config.vm.provision :shell, :path => 'scripts/provision.sh'
18
+
19
+ config.vm.define :ubuntu do |ubuntu|
20
+ ubuntu.vm.provider :simple_cloud do |provider|
21
+ provider.image = '26'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ echo 'Testing 1 2 3!'
data/test/test.sh ADDED
@@ -0,0 +1,10 @@
1
+ cd test
2
+
3
+ bundle exec vagrant up --provider=simple_cloud
4
+ bundle exec vagrant up
5
+ bundle exec vagrant provision
6
+ bundle exec vagrant rebuild
7
+ bundle exec vagrant halt
8
+ bundle exec vagrant destroy
9
+
10
+ cd ..
data/test/test_id_rsa ADDED
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEowIBAAKCAQEAmxZRyfvgXFxlPW9ivoxffdK9erqpCp1oitghUtDxbzPSCbNw
3
+ qBoiJcnPVA/TuCxMnruUcNEXYgKfTL8lD3A1Hom8N1pTAhSed5m4qAGqTMubT15s
4
+ cSR+SnDdriShErB/9YSb9LVn1aR0MsFS3H/+x1j4w5d6hBas8BhDfuVd16shvoaA
5
+ OKy0ywy+NBuvGy/6Au3q3t7M9wdelODRnYLSWWqaLeYExRKxWWc7ape+oduQoe4r
6
+ BNVwGmIOjWOM9aFPEPVHdLGO+LQyPExdeuS0rW96a39U4p8GjGzsrkNcKzVOGjM3
7
+ pIsGs3qOi7RzJ3z48HiBj9NT8I2fFpGHERerbQIDAQABAoIBABXsIcObhyuHJAh7
8
+ JkopLZZro70lhZ+qgIyf4JYEUxyVBqu4YcRhbVJKJLSNSDBQksQdX+5SoCuKk1oV
9
+ 6vcztU6Lyb9JVVKF96CQajnVgm04msutXUbhEbkUG0Hyi5JIwM3D4QfGXNcmWAaU
10
+ rVHeBfXH7eI4F2l0ix2lUGUvpwRFRDq9HgpOjXzyc57B4jeF7na/UTnt+Uoi4hzZ
11
+ FjjQ7nSLqEJLXtQBqt4EnAZu6/9JlAApunyMOX2oTqRNn8XGmD0Rc+AouipHM+Mc
12
+ 9/fN9oqVxxXw2MdJA6S/sEFLEDrbifmyyHOereuZtOjdWLqsCdZwewYl8nuBnYEU
13
+ GjVzYgECgYEAx+efis7xma28HWqtW9GLvjBcFAD/f+MDDeqX9TKFwf+91tUq0QZi
14
+ SqXvmIvCnpsO8I70WEskT+pPwJWReAbZBrCbCVDbH34KEkAHywH9sK6chWnB8OpU
15
+ 0mp0gH89A4bq/tedKVHCQ2sAbKgbIc1zf3zpmMQiV+smMDQXU1fTg/kCgYEAxpst
16
+ BD2cYftFjxFZE1v8fx6t6oHtzYRtNNFTYzfxzzRBaTTRRzdhSfh0tLFueyg/fcKR
17
+ oCXUxbfCYRLk+zHP2p/AyyN9R5p2AMAc6lOZPpBj7u9kjjDVnk76DYnLDqP3Da2s
18
+ i7b0DNYxm2gt1VSZfOuJHv7z85SLcJQsg+3ymBUCgYBrOpFX0d3Cw3COjvRitiox
19
+ YJtjl411uf2fb2EHg4xAHcBlBn8rFDOROyUkPIOutBn1a5kh61yVCWiyMwiOy42K
20
+ ixz+iEKhx+f7FiGYAX9lUKRg4/PGGMxa+gN4EchWpf5TqLCCw3pi03is0BeNsDjt
21
+ /8EF0t9hLZ+UZ7zDVe79cQKBgGTPi5AlfeW2V96BHcfX31jfR8RLY1v4pj4zKrKo
22
+ SRO2IKW4a6pMkBOuC/9UORJGocPCKY0y5sfduMrxfk2LQUhl4sS6JPNdkhxbZ9IB
23
+ 0T2SqUc1OMN8QlJzIDYTBYFO9S56Q6U/nq2NY+zQesNYh/iCzj1viIDRm93vOJFX
24
+ DNbpAoGBALlQvzzMsT3/fPYn8moQiUCJ9XRZ4X2qwYy5Q8J8QvutI+j9o9+pJBhc
25
+ 3zSlB8HHa7asf27GUbYtv7oFDpqqcC6EFtvfp1OCiX/OjBIJA1YXTFG3YWC5ngC4
26
+ JPxyTn4MdoX0enm8PRDg7CSZwa4AK1MIYetbiuJgWJ2wKXDFxuGH
27
+ -----END RSA PRIVATE KEY-----