vagrant-tiktalik 0.0.1

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.
@@ -0,0 +1,36 @@
1
+ require "log4r"
2
+ require "time"
3
+
4
+ require "tiktalik"
5
+
6
+ module VagrantPlugins
7
+ module TiktalikVagrant
8
+ module Action
9
+ # This terminates the running instance.
10
+ class TerminateInstance
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_tiktalik::action::terminate_instance")
14
+ @config = env[:machine].provider_config
15
+ end
16
+
17
+ def call(env)
18
+ # Destroy the server and remove the tracking ID
19
+ env[:ui].info(I18n.t("vagrant_tiktalik.terminating"))
20
+
21
+ t = Tiktalik
22
+ t.api_key = @config.api_key
23
+ t.api_secret_key = @config.api_secret
24
+
25
+ i = Tiktalik::Computing::Instance
26
+ instance = i.find env[:machine].id
27
+ instance.destroy
28
+
29
+ env[:machine].id = nil
30
+
31
+ @app.call(env)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ require "vagrant-tiktalik/util/timer"
2
+
3
+ module VagrantPlugins
4
+ module TiktalikVagrant
5
+ module Action
6
+ # This is the same as the builtin provision except it times the
7
+ # provisioner runs.
8
+ class TimedProvision < Vagrant::Action::Builtin::Provision
9
+ def run_provisioner(env, name, p)
10
+ timer = Util::Timer.time do
11
+ super
12
+ end
13
+
14
+ env[:metrics] ||= {}
15
+ env[:metrics]["provisioner_times"] ||= []
16
+ env[:metrics]["provisioner_times"] << [name, timer]
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module VagrantPlugins
2
+ module TiktalikVagrant
3
+ module Action
4
+ class WarnNetworks
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ if env[:machine].config.vm.networks.length > 0
11
+ env[:ui].warn(I18n.t("vagrant_tiktalik.warn_networks"))
12
+ end
13
+
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module TiktalikVagrant
5
+ class Config < Vagrant.plugin("2", :config)
6
+ attr_accessor :api_key
7
+ attr_accessor :image
8
+ attr_accessor :size
9
+ attr_accessor :ssh_key
10
+ attr_accessor :api_secret
11
+ #attr_accessor :network
12
+
13
+ def initialize
14
+ @api_key = UNSET_VALUE
15
+ @image = UNSET_VALUE
16
+ @size = UNSET_VALUE
17
+ @ssh_key = UNSET_VALUE
18
+ @api_secret = UNSET_VALUE
19
+ #@network = UNSET_VALUE
20
+ end
21
+
22
+ def finalize!
23
+ @api_key = ENV['TIKTALIK_API_KEY'] if @api_key == UNSET_VALUE
24
+ @image = nil if @image == UNSET_VALUE
25
+ @size = 0.5 if @size == UNSET_VALUE
26
+ @ssh_key = nil if @ssh_key == UNSET_VALUE
27
+ @api_secret = ENV['TIKTALIK_API_SECRET'] if @api_secret == UNSET_VALUE
28
+ #@network = nil if @network == UNSET_VALUE
29
+ end
30
+
31
+ def validate(machine)
32
+ errors = []
33
+
34
+ errors << I18n.t("vagrant_tiktalik.config.api_key_required") if config.api_key.nil?
35
+ errors << I18n.t("vagrant_tiktalik.config.api_secret_required") if config.api_secret.nil?
36
+ errors << I18n.t("vagrant_tiktalik.config.image_required") if config.image.nil?
37
+ errors << I18n.t("vagrant_tiktalik.config.ssh_key_required") if !@ssh_key
38
+
39
+ # TODO validate network
40
+
41
+ { "Tiktalik Provider" => errors }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module TiktalikVagrant
5
+ module Errors
6
+ class VagrantTiktalikError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_tiktalik.errors")
8
+ end
9
+
10
+ class FogError < VagrantTiktalikError
11
+ error_key(:fog_error)
12
+ end
13
+
14
+ class InstanceReadyTimeout < VagrantTiktalikError
15
+ error_key(:instance_ready_timeout)
16
+ end
17
+
18
+ class RsyncError < VagrantTiktalikError
19
+ error_key(:rsync_error)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,73 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The Vagrant Tiktalik.com 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.2.0"
10
+ raise "The Vagrant Tiktalik.com plugin is only compatible with Vagrant 1.2+"
11
+ end
12
+
13
+ module VagrantPlugins
14
+ module TiktalikVagrant
15
+ class Plugin < Vagrant.plugin("2")
16
+ name "Tiktalik.com"
17
+ description <<-DESC
18
+ This plugin installs a provider that allows Vagrant to manage
19
+ machines in Tiktalik.com.
20
+ DESC
21
+
22
+ config(:"tiktalik.com", :provider) do
23
+ require_relative "config"
24
+ Config
25
+ end
26
+
27
+ provider(:"tiktalik.com", parallel: true) 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", TiktalikVagrant.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_tiktalik")
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 TiktalikVagrant
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_tiktalik.states.short_#{state_id}")
38
+ long = I18n.t("vagrant_tiktalik.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
+ "Tiktalik.com (#{id})"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,17 @@
1
+ module VagrantPlugins
2
+ module TiktalikVagrant
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
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module TiktalikVagrant
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,94 @@
1
+ en:
2
+ vagrant_tiktalik:
3
+ already_created: |-
4
+ The machine is already created.
5
+ launching_instance: |-
6
+ Launching an instance with the following settings...
7
+ launch_no_keypair: |-
8
+ Warning! You didn't specify a keypair to launch your instance with.
9
+ This can sometimes result in not being able to access your instance.
10
+ launch_vpc_warning: |-
11
+ Warning! You're launching this instance into a VPC without an
12
+ elastic IP. Please verify you're properly connected to a VPN so
13
+ you can access this machine, otherwise Vagrant will not be able
14
+ to SSH into it.
15
+ not_created: |-
16
+ Instance is not created. Please run `vagrant up` first.
17
+ ready: |-
18
+ Machine is booted and ready for use!
19
+ rsync_folder: |-
20
+ Rsyncing folder: %{hostpath} => %{guestpath}
21
+ terminating: |-
22
+ Terminating the instance...
23
+ powering_off: |-
24
+ Stopping the instance...
25
+ powering_on: |-
26
+ Starting the instance...
27
+ waiting_for_ready: |-
28
+ Waiting for instance to become "ready"...
29
+ waiting_for_ssh: |-
30
+ Waiting for SSH to become available...
31
+ active_instance: |-
32
+ Instance is already up and running
33
+ unknown_state: |-
34
+ Instance is in unknown state
35
+ warn_networks: |-
36
+ Warning! The Tiktalik.com provider doesn't support any of the Vagrant
37
+ high-level network configurations (`config.vm.network`). They
38
+ will be silently ignored.
39
+ will_not_destroy: |-
40
+ The instance '%{name}' will not be destroyed, since the confirmation
41
+ was declined.
42
+
43
+ config:
44
+ api_key_required: |-
45
+ An access key ID must be specified via "api_key"
46
+ image_required: |-
47
+ An instance image UUID must be configured via "image"
48
+ api_secret_required: |-
49
+ A secret access key is required via "api_secret"
50
+ ssh_key_required: |-
51
+ A SSH key is required via "ssh_key"
52
+
53
+ errors:
54
+ fog_error: |-
55
+ There was an error talking to Tiktalik.com API. The error message is shown
56
+ below:
57
+
58
+ %{message}
59
+ instance_ready_timeout: |-
60
+ The instance never became "ready" in Tiktalik.com. The timeout currently
61
+ set waiting for the instance to become ready is %{timeout} seconds.
62
+ Please verify that the machine properly boots. If you need more time
63
+ set the `instance_ready_timeout` configuration on the Tiktalik.com provider.
64
+ rsync_error: |-
65
+ There was an error when attemping to rsync a share folder.
66
+ Please inspect the error message below for more info.
67
+
68
+ Host path: %{hostpath}
69
+ Guest path: %{guestpath}
70
+ Error: %{stderr}
71
+
72
+ states:
73
+ short_not_created: |-
74
+ not created
75
+ long_not_created: |-
76
+ The Tiktalik.com instance is not created. Run `vagrant up` to create it.
77
+
78
+ short_running: |-
79
+ running
80
+ long_running: |-
81
+ The Tiktalik.com instance is running. To stop this machine, you can run
82
+ `vagrant halt`. To destroy the machine, you can run `vagrant destroy`.
83
+
84
+ short_pending: |-
85
+ pending
86
+ long_pending: |-
87
+ The Tiktalik.com instance is still being initialized. To destroy this machine,
88
+ you can run `vagrant destroy`.
89
+
90
+ short_stopped: |-
91
+ stopped
92
+ long_stopped: |-
93
+ The Tiktalik.com instance is stopped. To destroy this machine,
94
+ you can run `vagrant destroy`.
@@ -0,0 +1,200 @@
1
+ require "vagrant-tiktalik/config"
2
+
3
+ describe VagrantPlugins::Tiktalik::Config do
4
+ let(:instance) { described_class.new }
5
+
6
+ # Ensure tests are not affected by Tiktalik credential environment variables
7
+ before :each do
8
+ ENV.stub(:[] => nil)
9
+ end
10
+
11
+ puts "config_spec"
12
+
13
+ describe "defaults" do
14
+ subject do
15
+ instance.tap do |o|
16
+ o.finalize!
17
+ end
18
+ end
19
+
20
+ its("api_key") { should be_nil }
21
+ its("image") { should be_nil }
22
+ #its("availability_zone") { should be_nil }
23
+ #its("instance_ready_timeout") { should == 120 }
24
+ #its("instance_type") { should == "m1.small" }
25
+ #its("keypair_name") { should be_nil }
26
+ #its("private_ip_address") { should be_nil }
27
+ #its("region") { should == "us-east-1" }
28
+ its("api_secret") { should be_nil }
29
+ #its("security_groups") { should == [] }
30
+ #its("subnet_id") { should be_nil }
31
+ #its("tags") { should == {} }
32
+ #its("user_data") { should be_nil }
33
+ #its("use_iam_profile") { should be_false }
34
+ #its("block_device_mapping") {should == {} }
35
+ end
36
+
37
+ describe "overriding defaults" do
38
+ # I typically don't meta-program in tests, but this is a very
39
+ # simple boilerplate test, so I cut corners here. It just sets
40
+ # each of these attributes to "foo" in isolation, and reads the value
41
+ # and asserts the proper result comes back out.
42
+ #[:api_key, :ami, :availability_zone, :instance_ready_timeout,
43
+ # :instance_type, :keypair_name,
44
+ # :region, :api_secret, :security_groups,
45
+ # :subnet_id, :tags,
46
+ # :use_iam_profile, :user_data, :block_device_mapping].each do |attribute|
47
+ [:api_key, :api_secret, :image].each do |attribute|
48
+ it "should not default #{attribute} if overridden" do
49
+ instance.send("#{attribute}=".to_sym, "foo")
50
+ instance.finalize!
51
+ instance.send(attribute).should == "foo"
52
+ end
53
+ end
54
+ end
55
+
56
+ #describe "getting credentials from environment" do
57
+ # context "without EC2 credential environment variables" do
58
+ # subject do
59
+ # instance.tap do |o|
60
+ # o.finalize!
61
+ # end
62
+ # end
63
+ #
64
+ # its("api_key") { should be_nil }
65
+ # its("api_secret") { should be_nil }
66
+ # end
67
+ #
68
+ # context "with EC2 credential environment variables" do
69
+ # before :each do
70
+ # ENV.stub(:[]).with("AWS_ACCESS_KEY").and_return("access_key")
71
+ # ENV.stub(:[]).with("AWS_SECRET_KEY").and_return("secret_key")
72
+ # end
73
+ #
74
+ # subject do
75
+ # instance.tap do |o|
76
+ # o.finalize!
77
+ # end
78
+ # end
79
+ #
80
+ # its("api_key") { should == "access_key" }
81
+ # its("api_secret") { should == "secret_key" }
82
+ # end
83
+ #end
84
+
85
+ describe "region config" do
86
+ let(:config_api_key) { "foo" }
87
+ let(:config_ami) { "foo" }
88
+ let(:config_instance_type) { "foo" }
89
+ let(:config_keypair_name) { "foo" }
90
+ let(:config_region) { "foo" }
91
+ let(:config_api_secret) { "foo" }
92
+
93
+ def set_test_values(instance)
94
+ instance.api_key = config_api_key
95
+ instance.ami = config_ami
96
+ instance.instance_type = config_instance_type
97
+ instance.keypair_name = config_keypair_name
98
+ instance.region = config_region
99
+ instance.api_secret = config_api_secret
100
+ end
101
+
102
+ it "should raise an exception if not finalized" do
103
+ expect { instance.get_region_config("us-east-1") }.
104
+ to raise_error
105
+ end
106
+
107
+ context "with no specific config set" do
108
+ subject do
109
+ # Set the values on the top-level object
110
+ set_test_values(instance)
111
+
112
+ # Finalize so we can get the region config
113
+ instance.finalize!
114
+
115
+ # Get a lower level region
116
+ instance.get_region_config("us-east-1")
117
+ end
118
+
119
+ its("api_key") { should == config_api_key }
120
+ its("ami") { should == config_ami }
121
+ its("instance_type") { should == config_instance_type }
122
+ its("keypair_name") { should == config_keypair_name }
123
+ its("region") { should == config_region }
124
+ its("api_secret") { should == config_api_secret }
125
+ end
126
+
127
+ context "with a specific config set" do
128
+ let(:region_name) { "hashi-region" }
129
+
130
+ subject do
131
+ # Set the values on a specific region
132
+ instance.region_config region_name do |config|
133
+ set_test_values(config)
134
+ end
135
+
136
+ # Finalize so we can get the region config
137
+ instance.finalize!
138
+
139
+ # Get the region
140
+ instance.get_region_config(region_name)
141
+ end
142
+
143
+ its("api_key") { should == config_api_key }
144
+ its("ami") { should == config_ami }
145
+ its("instance_type") { should == config_instance_type }
146
+ its("keypair_name") { should == config_keypair_name }
147
+ its("region") { should == region_name }
148
+ its("api_secret") { should == config_api_secret }
149
+ end
150
+
151
+ describe "inheritance of parent config" do
152
+ let(:region_name) { "hashi-region" }
153
+
154
+ subject do
155
+ # Set the values on a specific region
156
+ instance.region_config region_name do |config|
157
+ config.ami = "child"
158
+ end
159
+
160
+ # Set some top-level values
161
+ instance.api_key = "parent"
162
+ instance.ami = "parent"
163
+
164
+ # Finalize and get the region
165
+ instance.finalize!
166
+ instance.get_region_config(region_name)
167
+ end
168
+
169
+ its("api_key") { should == "parent" }
170
+ its("ami") { should == "child" }
171
+ end
172
+
173
+ describe "shortcut configuration" do
174
+ subject do
175
+ # Use the shortcut configuration to set some values
176
+ instance.region_config "us-east-1", :ami => "child"
177
+ instance.finalize!
178
+ instance.get_region_config("us-east-1")
179
+ end
180
+
181
+ its("ami") { should == "child" }
182
+ end
183
+
184
+ describe "merging" do
185
+ let(:first) { described_class.new }
186
+ let(:second) { described_class.new }
187
+
188
+ it "should merge the tags" do
189
+ first.tags["one"] = "one"
190
+ second.tags["two"] = "two"
191
+
192
+ third = first.merge(second)
193
+ third.tags.should == {
194
+ "one" => "one",
195
+ "two" => "two"
196
+ }
197
+ end
198
+ end
199
+ end
200
+ end