vagrant-actionio 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.env-example +4 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +7 -0
- data/LICENSE +10 -0
- data/README.md +110 -0
- data/Rakefile +21 -0
- data/a.rb +1 -0
- data/actionio-dummy.box +0 -0
- data/data/cacert.pem +3895 -0
- data/example_box/README.md +13 -0
- data/example_box/metadata.json +3 -0
- data/lib/vagrant-actionio.rb +18 -0
- data/lib/vagrant-actionio/action.rb +152 -0
- data/lib/vagrant-actionio/action/connect_actionio.rb +31 -0
- data/lib/vagrant-actionio/action/is_created.rb +18 -0
- data/lib/vagrant-actionio/action/is_running.rb +18 -0
- data/lib/vagrant-actionio/action/is_stopped.rb +18 -0
- data/lib/vagrant-actionio/action/is_terminated.rb +18 -0
- data/lib/vagrant-actionio/action/message_already_created.rb +16 -0
- data/lib/vagrant-actionio/action/message_already_terminated.rb +16 -0
- data/lib/vagrant-actionio/action/message_cannot_terminate.rb +16 -0
- data/lib/vagrant-actionio/action/message_not_created.rb +16 -0
- data/lib/vagrant-actionio/action/message_not_running.rb +16 -0
- data/lib/vagrant-actionio/action/message_provisioning_not_yet_supported.rb +16 -0
- data/lib/vagrant-actionio/action/read_ssh_info.rb +53 -0
- data/lib/vagrant-actionio/action/read_state.rb +36 -0
- data/lib/vagrant-actionio/action/remove_machine_id.rb +19 -0
- data/lib/vagrant-actionio/action/run_instance.rb +94 -0
- data/lib/vagrant-actionio/action/start_instance.rb +56 -0
- data/lib/vagrant-actionio/action/stop_instance.rb +56 -0
- data/lib/vagrant-actionio/action/sync_folders.rb +57 -0
- data/lib/vagrant-actionio/action/terminate_instance.rb +57 -0
- data/lib/vagrant-actionio/action/timed_provision.rb +21 -0
- data/lib/vagrant-actionio/action/warn_networks.rb +19 -0
- data/lib/vagrant-actionio/config.rb +75 -0
- data/lib/vagrant-actionio/connection.rb +70 -0
- data/lib/vagrant-actionio/errors.rb +35 -0
- data/lib/vagrant-actionio/plugin.rb +73 -0
- data/lib/vagrant-actionio/provider.rb +50 -0
- data/lib/vagrant-actionio/util/env.rb +12 -0
- data/lib/vagrant-actionio/util/timer.rb +17 -0
- data/lib/vagrant-actionio/version.rb +5 -0
- data/locales/en.yml +121 -0
- data/spec/vagrant-actionio/action/read_ssh_info_spec.rb +58 -0
- data/spec/vagrant-actionio/action/read_state_spec.rb +46 -0
- data/spec/vagrant-actionio/action/run_instance_spec.rb +24 -0
- data/spec/vagrant-actionio/action/start_instance_spec.rb +21 -0
- data/spec/vagrant-actionio/action/stop_instance_spec.rb +21 -0
- data/spec/vagrant-actionio/action/terminate_instance_spec.rb +21 -0
- data/spec/vagrant-actionio/config_spec.rb +47 -0
- data/spec/vagrant-actionio/connection_spec.rb +105 -0
- data/vagrant-actionio.gemspec +59 -0
- metadata +2882 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'vagrant-actionio/util/env'
|
2
|
+
require 'vagrant-actionio/errors'
|
3
|
+
require 'vagrant-actionio/version'
|
4
|
+
require 'oauth2'
|
5
|
+
|
6
|
+
ENV['SSL_CERT_FILE'] = File.join(File.expand_path('../../../', __FILE__), '/data/cacert.pem')
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module ActionIO
|
10
|
+
class Connection
|
11
|
+
OAUTH_CLIENT_ID = Util::Env.read_with_default('VAGRANT_ACTIONIO_OAUTH_ID', '414b8765a91db88e3d9d2a391eb19117a652b23ec7c1222fd61ddb59d0298072')
|
12
|
+
OAUTH_CLIENT_SECRET = Util::Env.read_with_default('VAGRANT_ACTIONIO_OAUTH_SECRET', 'd64a2c76e05126366d9627f165978a2a810c6b853c853df805e22f7390e59950')
|
13
|
+
VERIFY_SSL = ENV['VAGRANT_ACTIONIO_VERIFY_SSL'] != 'false'
|
14
|
+
HOST = Util::Env.read_with_default('VAGRANT_ACTIONIO_HOST', 'https://www.action.io')
|
15
|
+
API_PATH_PREFIX = '/api/v0'
|
16
|
+
USER_AGENT = "Vagrant-ActionIO/#{VERSION} (Vagrant #{Vagrant::VERSION}; #{RUBY_DESCRIPTION})"
|
17
|
+
|
18
|
+
attr_accessor :client, :token
|
19
|
+
|
20
|
+
def initialize(access_token_string)
|
21
|
+
options = { site: HOST }
|
22
|
+
options[:ssl] = { verify_mode: OpenSSL::SSL::VERIFY_NONE } if !VERIFY_SSL
|
23
|
+
@client = OAuth2::Client.new(OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, options)
|
24
|
+
@token = OAuth2::AccessToken.new(client, access_token_string)
|
25
|
+
end
|
26
|
+
|
27
|
+
def request(verb, path, options={})
|
28
|
+
options = { parse: :json }.merge options
|
29
|
+
options[:headers] ||= {}
|
30
|
+
options[:headers]['User-Agent'] = USER_AGENT
|
31
|
+
|
32
|
+
@token.request verb.to_sym, "#{API_PATH_PREFIX}#{path}", options
|
33
|
+
end
|
34
|
+
|
35
|
+
def verify_access_token
|
36
|
+
response = request(:get, '/scopes')
|
37
|
+
rescue => e
|
38
|
+
if e.response && e.response.status == 401
|
39
|
+
raise Errors::APIError, 'invalid_access_token'
|
40
|
+
else
|
41
|
+
raise e
|
42
|
+
end
|
43
|
+
else
|
44
|
+
if response.status == 200
|
45
|
+
json = JSON.parse(response.body)
|
46
|
+
if json.kind_of?(Hash) && json['scopes'].split.include?('boxes')
|
47
|
+
return
|
48
|
+
else
|
49
|
+
raise Errors::APIError, 'invalid_access_token_scope'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def fetch_box_state(box_id)
|
55
|
+
begin
|
56
|
+
response = request(:get, "/boxes/#{box_id}")
|
57
|
+
rescue => e
|
58
|
+
if e.respond_to?(:response) && e.response.status == 404
|
59
|
+
# The box can't be found
|
60
|
+
return nil
|
61
|
+
else
|
62
|
+
raise e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
response.parsed['box']['state'].to_sym
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module ActionIO
|
5
|
+
module Errors
|
6
|
+
class VagrantActionIOError < Vagrant::Errors::VagrantError
|
7
|
+
error_namespace('vagrant_actionio.errors')
|
8
|
+
end
|
9
|
+
|
10
|
+
class APIError < VagrantActionIOError
|
11
|
+
error_key(:api_error)
|
12
|
+
end
|
13
|
+
|
14
|
+
class BoxNotYetStartedError < VagrantActionIOError
|
15
|
+
error_key(:box_not_yet_started_error)
|
16
|
+
end
|
17
|
+
|
18
|
+
class BoxNotYetStoppedError < VagrantActionIOError
|
19
|
+
error_key(:box_not_yet_stopped_error)
|
20
|
+
end
|
21
|
+
|
22
|
+
class BoxNotYetTerminatedError < VagrantActionIOError
|
23
|
+
error_key(:box_not_yet_terminated_error)
|
24
|
+
end
|
25
|
+
|
26
|
+
class RsyncError < VagrantActionIOError
|
27
|
+
error_key(:rsync_error)
|
28
|
+
end
|
29
|
+
|
30
|
+
class TimeoutError < VagrantActionIOError
|
31
|
+
error_key(:timeout_error)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
begin
|
2
|
+
require 'vagrant'
|
3
|
+
rescue LoadError
|
4
|
+
raise 'The Vagrant Action.IO plugin must be run within Vagrant.'
|
5
|
+
end
|
6
|
+
|
7
|
+
# This is a sanity check to make sure no one is attempting to install
|
8
|
+
# this into an early Vagrant version.
|
9
|
+
if Vagrant::VERSION < '1.1.0'
|
10
|
+
raise 'The Vagrant Action.IO plugin is only compatible with Vagrant 1.1+'
|
11
|
+
end
|
12
|
+
|
13
|
+
module VagrantPlugins
|
14
|
+
module ActionIO
|
15
|
+
class Plugin < Vagrant.plugin('2')
|
16
|
+
name 'Action.IO'
|
17
|
+
description <<-DESC
|
18
|
+
This plugin installs a provider that allows Vagrant to manage
|
19
|
+
boxes in Action.IO.
|
20
|
+
DESC
|
21
|
+
|
22
|
+
config(:actionio, :provider) do
|
23
|
+
require_relative 'config'
|
24
|
+
Config
|
25
|
+
end
|
26
|
+
|
27
|
+
provider(:actionio) do
|
28
|
+
# Setup logging and i18n
|
29
|
+
setup_logging
|
30
|
+
setup_i18n
|
31
|
+
|
32
|
+
# Return the provider
|
33
|
+
require_relative 'provider'
|
34
|
+
Provider
|
35
|
+
end
|
36
|
+
|
37
|
+
# This initializes the internationalization strings.
|
38
|
+
def self.setup_i18n
|
39
|
+
I18n.load_path << File.expand_path('locales/en.yml', ActionIO.source_root)
|
40
|
+
I18n.reload!
|
41
|
+
end
|
42
|
+
|
43
|
+
# This sets up our log level to be whatever VAGRANT_LOG is.
|
44
|
+
def self.setup_logging
|
45
|
+
require 'log4r'
|
46
|
+
|
47
|
+
level = nil
|
48
|
+
begin
|
49
|
+
level = Log4r.const_get(ENV['VAGRANT_LOG'].upcase)
|
50
|
+
rescue NameError
|
51
|
+
# This means that the logging constant wasn't found,
|
52
|
+
# which is fine. We just keep `level` as `nil`. But
|
53
|
+
# we tell the user.
|
54
|
+
level = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Some constants, such as "true" resolve to booleans, so the
|
58
|
+
# above error checking doesn't catch it. This will check to make
|
59
|
+
# sure that the log level is an integer, as Log4r requires.
|
60
|
+
level = nil if !level.is_a?(Integer)
|
61
|
+
|
62
|
+
# Set the logging level on all "vagrant" namespaced
|
63
|
+
# logs as long as we have a valid level.
|
64
|
+
if level
|
65
|
+
logger = Log4r::Logger.new('vagrant_actionio')
|
66
|
+
logger.outputters = Log4r::Outputter.stderr
|
67
|
+
logger.level = level
|
68
|
+
logger = nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
require 'vagrant'
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module ActionIO
|
6
|
+
class Provider < Vagrant.plugin('2', :provider)
|
7
|
+
def initialize(machine)
|
8
|
+
@machine = machine
|
9
|
+
end
|
10
|
+
|
11
|
+
def action(name)
|
12
|
+
# Attempt to get the action method from the Action class if it
|
13
|
+
# exists, otherwise return nil to show that we don't support the
|
14
|
+
# given action.
|
15
|
+
action_method = "action_#{name}"
|
16
|
+
return Action.send(action_method) if Action.respond_to?(action_method)
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def ssh_info
|
21
|
+
# Run a custom action called 'read_ssh_info' which does what it
|
22
|
+
# says and puts the resulting SSH info into the `:machine_ssh_info`
|
23
|
+
# key in the environment.
|
24
|
+
env = @machine.action('read_ssh_info')
|
25
|
+
env[:machine_ssh_info]
|
26
|
+
end
|
27
|
+
|
28
|
+
def state
|
29
|
+
# Run a custom action we define called 'read_state' which does
|
30
|
+
# what it says. It puts the state in the `:machine_state_id`
|
31
|
+
# key in the environment.
|
32
|
+
env = @machine.action('read_state')
|
33
|
+
|
34
|
+
state_id = env[:machine_state_id]
|
35
|
+
|
36
|
+
# Get the short and long description
|
37
|
+
short = I18n.t("vagrant_actionio.states.short_#{state_id}")
|
38
|
+
long = I18n.t("vagrant_actionio.states.long_#{state_id}")
|
39
|
+
|
40
|
+
# Return the MachineState object
|
41
|
+
Vagrant::MachineState.new(state_id, short, long)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
id = @machine.id.nil? ? 'new' : @machine.id
|
46
|
+
"Action.IO (#{id})"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module ActionIO
|
3
|
+
module Util
|
4
|
+
class Timer
|
5
|
+
# A basic utility method that times the execution of the given
|
6
|
+
# block and returns it.
|
7
|
+
def self.time
|
8
|
+
start_time = Time.now.to_f
|
9
|
+
yield
|
10
|
+
end_time = Time.now.to_f
|
11
|
+
|
12
|
+
end_time - start_time
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/locales/en.yml
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
en:
|
2
|
+
vagrant_actionio:
|
3
|
+
already_created: |-
|
4
|
+
The box already exists on Action.IO.
|
5
|
+
already_terminated: |-
|
6
|
+
The box is already terminated on Action.IO.
|
7
|
+
cannot_terminate: |-
|
8
|
+
The box needs to be in either "running" or "stopped" state to be terminatable.
|
9
|
+
launching_box: |-
|
10
|
+
Launching a box on Action.IO with the following settings...
|
11
|
+
not_created: |-
|
12
|
+
Your box on Action.IO is not created. Please create your box by running `vagrant up` first.
|
13
|
+
not_running: |-
|
14
|
+
Your box on Action.IO is not running. Please start your box by running `vagrant up` first.
|
15
|
+
provisioning_not_yet_supported: |-
|
16
|
+
Vagrant provisioning is coming soon, but not yet supported. Please check https://github.com/action-io/vagrant-actionio for updates.
|
17
|
+
ready: |-
|
18
|
+
Your box on Action.IO is running and ready for use!
|
19
|
+
rsync_folder: |-
|
20
|
+
Rsyncing folder: %{hostpath} => %{guestpath}
|
21
|
+
starting_box: |-
|
22
|
+
Starting the box on Action.IO...
|
23
|
+
stopped: |-
|
24
|
+
Your box on Action.IO is stopped.
|
25
|
+
stopping_box: |-
|
26
|
+
Stopping the box on Action.IO...
|
27
|
+
terminated: |-
|
28
|
+
Your box on Action.IO is terminated.
|
29
|
+
terminating_box: |-
|
30
|
+
Terminating the box on Action.IO...
|
31
|
+
warn_networks: |-
|
32
|
+
Warning! The Action.IO provider doesn't support any of the Vagrant
|
33
|
+
high-level network configurations (`config.vm.network`). They
|
34
|
+
will be silently ignored.
|
35
|
+
|
36
|
+
config:
|
37
|
+
access_token_required: |-
|
38
|
+
An access token must be specified via "access_token"
|
39
|
+
|
40
|
+
private_key_missing: |-
|
41
|
+
The specified SSH private key could not be found
|
42
|
+
|
43
|
+
region_required: |-
|
44
|
+
A region must be specified via "region"
|
45
|
+
|
46
|
+
ssh_private_key_path_required: |-
|
47
|
+
A path to an ssh private key must be specified via "ssh_private_key_path"
|
48
|
+
|
49
|
+
errors:
|
50
|
+
api_error: |-
|
51
|
+
There was an error talking to Action.IO. The error message is shown below:
|
52
|
+
|
53
|
+
%{message}
|
54
|
+
|
55
|
+
box_not_yet_started_error: |-
|
56
|
+
Box is not yet started.
|
57
|
+
|
58
|
+
box_not_yet_stopped_error: |-
|
59
|
+
Box is not yet stopped.
|
60
|
+
|
61
|
+
box_not_yet_terminated_error: |-
|
62
|
+
Box is not yet terminated.
|
63
|
+
|
64
|
+
invalid_access_token: |-
|
65
|
+
Access token is invalid.
|
66
|
+
|
67
|
+
invalid_access_token_scope: |-
|
68
|
+
Access token does not have "boxes" scope.
|
69
|
+
|
70
|
+
rsync_error: |-
|
71
|
+
There was an error when attemping to rsync a shared folder.
|
72
|
+
Please inspect the error message below for more info.
|
73
|
+
|
74
|
+
Host path: %{hostpath}
|
75
|
+
Guest path: %{guestpath}
|
76
|
+
Error: %{stderr}
|
77
|
+
|
78
|
+
timeout_error: |-
|
79
|
+
Box creation timed out.
|
80
|
+
|
81
|
+
states:
|
82
|
+
short_not_created: |-
|
83
|
+
not created
|
84
|
+
long_not_created: |-
|
85
|
+
The Action.IO box is not created. Run `vagrant up` to create it.
|
86
|
+
|
87
|
+
short_provisioning: |-
|
88
|
+
provisioning
|
89
|
+
long_provisioning: |-
|
90
|
+
The Action.IO box is being created.
|
91
|
+
|
92
|
+
short_running: |-
|
93
|
+
running
|
94
|
+
long_running: |-
|
95
|
+
The Action.IO box is running. To stop this machine, you can run
|
96
|
+
`vagrant halt`. To destroy the machine, you can run `vagrant destroy`.
|
97
|
+
|
98
|
+
short_starting: |-
|
99
|
+
starting
|
100
|
+
long_starting: |-
|
101
|
+
The Action.IO box is starting.
|
102
|
+
|
103
|
+
short_stopped: |-
|
104
|
+
stopped
|
105
|
+
long_stopped: |-
|
106
|
+
The Action.IO box is stopped. Run `vagrant up` to start it.
|
107
|
+
|
108
|
+
short_stopping: |-
|
109
|
+
stopping
|
110
|
+
long_stopping: |-
|
111
|
+
The Action.IO box is stopping.
|
112
|
+
|
113
|
+
short_terminated: |-
|
114
|
+
terminated
|
115
|
+
long_terminated: |-
|
116
|
+
The Action.IO box is terminated.
|
117
|
+
|
118
|
+
short_terminating: |-
|
119
|
+
terminating
|
120
|
+
long_terminating: |-
|
121
|
+
The Action.IO box is terminating.
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'vagrant-actionio/action/read_ssh_info'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe VagrantPlugins::ActionIO::Action::ReadSSHInfo do
|
5
|
+
let(:app) { double 'app', call: nil }
|
6
|
+
let(:instance) { VagrantPlugins::ActionIO::Action::ReadSSHInfo.new(app, env) }
|
7
|
+
|
8
|
+
context 'when machine id is nil' do
|
9
|
+
let(:machine) { double 'machine', id: nil }
|
10
|
+
let(:env) { { actionio: nil, machine: machine } }
|
11
|
+
|
12
|
+
it 'returns nil' do
|
13
|
+
instance.call(env)
|
14
|
+
expect(env[:machine_ssh_info]).to be_nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when machine id is not nil' do
|
19
|
+
let(:machine) { OpenStruct.new(id: 123) }
|
20
|
+
let!(:actionio) { double 'connection', request: nil }
|
21
|
+
let(:env) { { actionio: actionio, machine: machine } }
|
22
|
+
|
23
|
+
context 'when request is successful' do
|
24
|
+
before do
|
25
|
+
json = '{"box":{"id":123,"host":"foo-box-123.usw1.actionbox.io","port":12345}}'
|
26
|
+
response = double 'response', status: 200, body: json, parsed: JSON.parse(json)
|
27
|
+
actionio.should_receive(:request).with(:get, '/boxes/123').and_return response
|
28
|
+
machine.stub_chain(:provider_config, :ssh_private_key_path).and_return '/home/action/.ssh/id_rsa'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'makes a get request to /boxes/:id and returns a hash containing the ssh info' do
|
32
|
+
instance.call(env)
|
33
|
+
expect(env[:machine_ssh_info]).to eq({
|
34
|
+
host: 'foo-box-123.usw1.actionbox.io',
|
35
|
+
port: '12345',
|
36
|
+
private_key_path: '/home/action/.ssh/id_rsa',
|
37
|
+
username: 'action'
|
38
|
+
})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when request returns 404' do
|
43
|
+
before do
|
44
|
+
response = double 'response', status: 404
|
45
|
+
error = StandardError.new('request error')
|
46
|
+
error.stub(:response) { response }
|
47
|
+
actionio.should_receive(:request).with(:get, '/boxes/123').and_raise error
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'sets machine id to be nil and returns nil' do
|
51
|
+
instance.call(env)
|
52
|
+
expect(env[:machine].id).to be_nil
|
53
|
+
expect(env[:machine_ssh_info]).to be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|