vagrant-dustcloud 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 (33) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/Gemfile +9 -0
  4. data/README.md +56 -0
  5. data/Rakefile +14 -0
  6. data/dummy.box +0 -0
  7. data/example_box/README.md +0 -0
  8. data/example_box/metadata.json +3 -0
  9. data/lib/vagrant-dustcloud.rb +18 -0
  10. data/lib/vagrant-dustcloud/action.rb +189 -0
  11. data/lib/vagrant-dustcloud/action/connect_dustcloud.rb +65 -0
  12. data/lib/vagrant-dustcloud/action/is_created.rb +18 -0
  13. data/lib/vagrant-dustcloud/action/is_stopped.rb +18 -0
  14. data/lib/vagrant-dustcloud/action/message_already_created.rb +16 -0
  15. data/lib/vagrant-dustcloud/action/message_not_created.rb +16 -0
  16. data/lib/vagrant-dustcloud/action/message_will_not_destroy.rb +16 -0
  17. data/lib/vagrant-dustcloud/action/read_ssh_info.rb +38 -0
  18. data/lib/vagrant-dustcloud/action/read_state.rb +33 -0
  19. data/lib/vagrant-dustcloud/action/run_instance.rb +110 -0
  20. data/lib/vagrant-dustcloud/action/start_instance.rb +82 -0
  21. data/lib/vagrant-dustcloud/action/stop_instance.rb +26 -0
  22. data/lib/vagrant-dustcloud/action/terminate_instance.rb +32 -0
  23. data/lib/vagrant-dustcloud/action/timed_provision.rb +21 -0
  24. data/lib/vagrant-dustcloud/action/wait_for_state.rb +41 -0
  25. data/lib/vagrant-dustcloud/config.rb +52 -0
  26. data/lib/vagrant-dustcloud/errors.rb +23 -0
  27. data/lib/vagrant-dustcloud/plugin.rb +38 -0
  28. data/lib/vagrant-dustcloud/provider.rb +49 -0
  29. data/lib/vagrant-dustcloud/util/timer.rb +17 -0
  30. data/lib/vagrant-dustcloud/version.rb +5 -0
  31. data/locales/en.yaml +71 -0
  32. data/vagrant-dustcloud.gemspec +50 -0
  33. metadata +115 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 272a9f6a7493a0e03ad17e99ae2f3645a2cf379f462e648c744d2029c8d96f90
4
+ data.tar.gz: cba88ed71023e27524ea5847cc5ad7f5e52004daf9daa0aac483b14a20069dd3
5
+ SHA512:
6
+ metadata.gz: 3a6acacca644325a3f45971a39079821c1c6925a92b04917fae2f550e7c3838a1c3cc2dc4d321cfe7bf292138a0eaeac418d5a949f5f5c5f7ac142f3d2368741
7
+ data.tar.gz: be4d1851abbfd48b58da6ecf45c34e132b30d1a27520370c37e63ba9507e6da665b705eac271da2e2b9bead989c9332420e8fa20c08e5497f2e67c5ff243de1c
@@ -0,0 +1,21 @@
1
+ # OS-specific
2
+ .DS_Store
3
+
4
+ # editors
5
+ *.swp
6
+
7
+ # Bundler/Rubygems
8
+ *.gem
9
+ .bundle
10
+ pkg/*
11
+ tags
12
+ Gemfile.lock
13
+
14
+ # Vagrant
15
+ .vagrant
16
+ Vagrantfile
17
+ !example_box/Vagrantfile
18
+
19
+ # RVM files for gemset/ruby setting
20
+ .ruby-*
21
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ group :development do
4
+ gem "vagrant", git: "https://github.com/hashicorp/vagrant.git"
5
+ end
6
+
7
+ group :plugins do
8
+ gem "vagrant-dustcloud", path: "."
9
+ end
@@ -0,0 +1,56 @@
1
+ # Vagrant LUDD DUSTcloud Provider
2
+
3
+ ## Features
4
+ - Boot instances in LUDD DUSTcloud.
5
+ - SSH into the instances.
6
+ - Provision the instances with any built-in Vagrant provisioner.
7
+ - Minimal synced folder support via `rsync`.
8
+
9
+ ## Usage
10
+ Install using the standard Vagrant 1.1+ plugin installation methods. After installing, `vagrant up` and specify the `dustcloud` provider. An example is shown below.
11
+ ```
12
+ $ vagrant plugin install vagrant-dustcloud
13
+ ...
14
+ $ vagrant up --provider=dustcloud
15
+ ...
16
+ ```
17
+
18
+ ## Quick Start
19
+ After installing the plugin (instructions above), the quickest way to get started is to actually use a dummy Dustcloud box and specify all the details manually within a config.vm.provider block. So first, add the dummy box using any name you want:
20
+ ```
21
+ $ vagrant box add dummy https://git.ludd.ltu.se/ludd/vagrant-dustcloud/raw/master/dummy.box
22
+ ...
23
+ ```
24
+ And then make a Vagrantfile that looks like the following.
25
+ ```ruby
26
+ Vagrant.configure("2") do |config|
27
+ config.vm.box = "dummy"
28
+
29
+ config.vm.provider :dustcloud do |dustcloud|
30
+ dustcloud.token_file = "~/.dustcloud"
31
+ dustcloud.image = "focal"
32
+ dustcloud.flavor = "small"
33
+ end
34
+ end
35
+ ```
36
+ And then run `vagrant up --provider=dustcloud`.
37
+
38
+ ## Development
39
+ To work on the `vagrant-dustcloud` plugin, clone this repository out, and use
40
+ [Bundler](http://gembundler.com) to get the dependencies:
41
+
42
+ ```
43
+ $ bundle
44
+ ```
45
+
46
+ If those pass, you're ready to start developing the plugin. You can test
47
+ the plugin without installing it into your Vagrant environment by just
48
+ creating a `Vagrantfile` in the top level of this directory (it is gitignored)
49
+ and add the following line to your `Vagrantfile`
50
+ ```ruby
51
+ Vagrant.require_plugin "vagrant-dustcloud"
52
+ ```
53
+ Use bundler to execute Vagrant:
54
+ ```
55
+ $ bundle exec vagrant up --provider=dustcloud
56
+ ```
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ # Immediately sync all stdout so that tools like buildbot can
5
+ # immediately load in the output.
6
+ $stdout.sync = true
7
+ $stderr.sync = true
8
+
9
+ # Change to the directory of this file.
10
+ Dir.chdir(File.expand_path("../", __FILE__))
11
+
12
+ # This installs the tasks that help with gem creation and
13
+ # publishing.
14
+ Bundler::GemHelper.install_tasks
Binary file
File without changes
@@ -0,0 +1,3 @@
1
+ {
2
+ "provider": "dustcloud"
3
+ }
@@ -0,0 +1,18 @@
1
+ require "pathname"
2
+
3
+ require "vagrant-dustcloud/plugin"
4
+
5
+ module VagrantPlugins
6
+ module Dustcloud
7
+ lib_path = Pathname.new(File.expand_path("../vagrant-dustcloud", __FILE__))
8
+ autoload :Action, lib_path.join("action")
9
+ autoload :Errors, lib_path.join("errors")
10
+
11
+ # This returns the path to the source of this plugin.
12
+ #
13
+ # @return [Pathname]
14
+ def self.source_root
15
+ @source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,189 @@
1
+ require "pathname"
2
+
3
+ require "vagrant/action/builder"
4
+
5
+ module VagrantPlugins
6
+ module Dustcloud
7
+ module Action
8
+ # Include the built-in modules so we can use them as top-level things.
9
+ include Vagrant::Action::Builtin
10
+
11
+ # This action is called to halt the remote machine.
12
+ def self.action_halt
13
+ Vagrant::Action::Builder.new.tap do |b|
14
+ b.use ConfigValidate
15
+ b.use Call, IsCreated do |env, b2|
16
+ if !env[:result]
17
+ b2.use MessageNotCreated
18
+ next
19
+ end
20
+
21
+ b2.use ConnectDustcloud
22
+ b2.use StopInstance
23
+ end
24
+ end
25
+ end
26
+
27
+ # This action is called to terminate the remote machine.
28
+ def self.action_destroy
29
+ Vagrant::Action::Builder.new.tap do |b|
30
+ b.use Call, DestroyConfirm do |env, b2|
31
+ if env[:result]
32
+ b2.use ConfigValidate
33
+ b2.use Call, IsCreated do |env2, b3|
34
+ if !env2[:result]
35
+ b3.use MessageNotCreated
36
+ next
37
+ end
38
+
39
+ b3.use ConnectDustcloud
40
+ b3.use ProvisionerCleanup, :before if defined?(ProvisionerCleanup)
41
+ b3.use TerminateInstance
42
+ end
43
+ else
44
+ b2.use MessageWillNotDestroy
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # This action is called when `vagrant provision` is called.
51
+ def self.action_provision
52
+ Vagrant::Action::Builder.new.tap do |b|
53
+ b.use ConfigValidate
54
+ b.use Call, IsCreated do |env, b2|
55
+ if !env[:result]
56
+ b2.use MessageNotCreated
57
+ next
58
+ end
59
+
60
+ b2.use Provision
61
+ end
62
+ end
63
+ end
64
+
65
+ # This action is called to read the SSH info of the machine. The
66
+ # resulting state is expected to be put into the `:machine_ssh_info`
67
+ # key.
68
+ def self.action_read_ssh_info
69
+ Vagrant::Action::Builder.new.tap do |b|
70
+ b.use ConfigValidate
71
+ b.use ConnectDustcloud
72
+ b.use ReadSSHInfo
73
+ end
74
+ end
75
+
76
+ # This action is called to read the state of the machine. The
77
+ # resulting state is expected to be put into the `:machine_state_id`
78
+ # key.
79
+ def self.action_read_state
80
+ Vagrant::Action::Builder.new.tap do |b|
81
+ b.use ConfigValidate
82
+ b.use ConnectDustcloud
83
+ b.use ReadState
84
+ end
85
+ end
86
+
87
+ # This action is called to SSH into the machine.
88
+ def self.action_ssh
89
+ Vagrant::Action::Builder.new.tap do |b|
90
+ b.use ConfigValidate
91
+ b.use Call, IsCreated do |env, b2|
92
+ if !env[:result]
93
+ b2.use MessageNotCreated
94
+ next
95
+ end
96
+
97
+ b2.use SSHExec
98
+ end
99
+ end
100
+ end
101
+
102
+ def self.action_ssh_run
103
+ Vagrant::Action::Builder.new.tap do |b|
104
+ b.use ConfigValidate
105
+ b.use Call, IsCreated do |env, b2|
106
+ if !env[:result]
107
+ b2.use MessageNotCreated
108
+ next
109
+ end
110
+
111
+ b2.use SSHRun
112
+ end
113
+ end
114
+ end
115
+
116
+ def self.action_prepare_boot
117
+ Vagrant::Action::Builder.new.tap do |b|
118
+ b.use Provision
119
+ b.use SyncedFolders
120
+ end
121
+ end
122
+
123
+ # This action is called to bring the box up from nothing.
124
+ def self.action_up
125
+ Vagrant::Action::Builder.new.tap do |b|
126
+ b.use HandleBox
127
+ b.use ConfigValidate
128
+ b.use BoxCheckOutdated
129
+ b.use ConnectDustcloud
130
+ b.use Call, IsCreated do |env1, b1|
131
+ if env1[:result]
132
+ b1.use Call, IsStopped do |env2, b2|
133
+ if env2[:result]
134
+ b2.use action_prepare_boot
135
+ b2.use StartInstance # restart this instance
136
+ else
137
+ b2.use MessageAlreadyCreated # TODO write a better message
138
+ end
139
+ end
140
+ else
141
+ b1.use action_prepare_boot
142
+ b1.use RunInstance # launch a new instance
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ def self.action_reload
149
+ Vagrant::Action::Builder.new.tap do |b|
150
+ b.use ConfigValidate
151
+ b.use ConnectDustcloud
152
+ b.use Call, IsCreated do |env, b2|
153
+ if !env[:result]
154
+ b2.use MessageNotCreated
155
+ next
156
+ end
157
+
158
+ b2.use action_halt
159
+ b2.use Call, WaitForState, :shutoff, 120 do |env2, b3|
160
+ if env2[:result]
161
+ b3.use action_up
162
+ else
163
+ # TODO we couldn't reach :stopped, what now?
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ # The autoload farm
171
+ action_root = Pathname.new(File.expand_path("../action", __FILE__))
172
+ autoload :ConnectDustcloud, action_root.join("connect_dustcloud")
173
+ autoload :IsCreated, action_root.join("is_created")
174
+ autoload :IsStopped, action_root.join("is_stopped")
175
+ autoload :MessageAlreadyCreated, action_root.join("message_already_created")
176
+ autoload :MessageNotCreated, action_root.join("message_not_created")
177
+ autoload :MessageWillNotDestroy, action_root.join("message_will_not_destroy")
178
+ autoload :PackageInstance, action_root.join("package_instance")
179
+ autoload :ReadSSHInfo, action_root.join("read_ssh_info")
180
+ autoload :ReadState, action_root.join("read_state")
181
+ autoload :RunInstance, action_root.join("run_instance")
182
+ autoload :StartInstance, action_root.join("start_instance")
183
+ autoload :StopInstance, action_root.join("stop_instance")
184
+ autoload :TerminateInstance, action_root.join("terminate_instance")
185
+ autoload :TimedProvision, action_root.join("timed_provision") # some plugins now expect this action to exist
186
+ autoload :WaitForState, action_root.join("wait_for_state")
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,65 @@
1
+ require "log4r"
2
+ require "json"
3
+
4
+ require 'net/http'
5
+
6
+ module VagrantPlugins
7
+ module Dustcloud
8
+ module Action
9
+ class ConnectDustcloud
10
+ def initialize(app, env)
11
+ @app = app
12
+ @logger = Log4r::Logger.new("vagrant_dustcloud::action::connect_dustcloud")
13
+ end
14
+
15
+ def call(env)
16
+
17
+ api_token = env[:machine].provider_config.token.to_s
18
+ env[:dustcloud] = DustcloudAPI.new(api_token)
19
+
20
+ pw_file = env[:machine].data_dir.join('ssh_password')
21
+ if pw_file.file?
22
+ env[:machine_ssh_password] = pw_file.read
23
+ end
24
+
25
+ @app.call(env)
26
+ end
27
+ end
28
+
29
+ class DustcloudAPI
30
+ def initialize(api_token)
31
+ net = Net::HTTP.new("dust.ludd.ltu.se", 443)
32
+ net.use_ssl = true
33
+ @api_token = api_token
34
+ @net = net
35
+ end
36
+
37
+ def get_vm(uuid)
38
+ res = @net.get("/cloud/vm/#{uuid}", "X-Dustcloud-Token" => @api_token )
39
+ JSON.parse(res.body)
40
+ end
41
+
42
+ def create_vm(image, flavor)
43
+ res = @net.post("/cloud/new", "image=#{image}&flavor=#{flavor}", "x-dustcloud-token" => @api_token )
44
+ JSON.parse(res.body)
45
+ end
46
+
47
+
48
+ def start_vm(uuid)
49
+ res = @net.post("/cloud/vm/#{uuid}/start", "", "x-dustcloud-token" => @api_token)
50
+ JSON.parse(res.body)
51
+ end
52
+
53
+ def stop_vm(uuid)
54
+ res = @net.post("/cloud/vm/#{uuid}/stop", "", "x-dustcloud-token" => @api_token)
55
+ JSON.parse(res.body)
56
+ end
57
+
58
+ def destroy_vm(uuid)
59
+ res = @net.post("/cloud/vm/#{uuid}/delete", "", "x-dustcloud-token" => @api_token)
60
+ JSON.parse(res.body)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ module VagrantPlugins
2
+ module Dustcloud
3
+ module Action
4
+ # This can be used with "Call" built-in to check if the machine
5
+ # is created and branch in the middleware.
6
+ class IsCreated
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ env[:result] = env[:machine].state.id != :not_created
13
+ @app.call(env)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module VagrantPlugins
2
+ module Dustcloud
3
+ module Action
4
+ # This can be used with "Call" built-in to check if the machine
5
+ # is stopped and branch in the middleware.
6
+ class IsStopped
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ env[:result] = env[:machine].state.id == :shutoff
13
+ @app.call(env)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Dustcloud
3
+ module Action
4
+ class MessageAlreadyCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_dustcloud.already_status", :status => "created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Dustcloud
3
+ module Action
4
+ class MessageNotCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_dustcloud.not_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Dustcloud
3
+ module Action
4
+ class MessageWillNotDestroy
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_dustcloud.will_not_destroy", name: env[:machine].name))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Dustcloud
5
+ module Action
6
+ # This action reads the SSH info for the machine and puts it into the
7
+ # `:machine_ssh_info` key in the environment.
8
+ class ReadSSHInfo
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_dustcloud::action::read_ssh_info")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_ssh_info] = read_ssh_info(env[:dustcloud], env)
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_ssh_info(dustcloud, env)
21
+ machine = env[:machine]
22
+ return nil if machine.id.nil?
23
+
24
+ # Find the machine
25
+ server = dustcloud.get_vm(machine.id)
26
+ if server.nil?
27
+ # The machine can't be found
28
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
29
+ machine.id = nil
30
+ return nil
31
+ end
32
+
33
+ return { :host => server["ssh_host"], :port => server["ssh_port"], :username => server["ssh_username"], :password => env[:machine_ssh_password] }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Dustcloud
5
+ module Action
6
+ # This action reads the state of the machine and puts it in the
7
+ # `:machine_state_id` key in the environment.
8
+ class ReadState
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_dustcloud::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:dustcloud], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(dustcloud, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = dustcloud.get_vm(machine.id)
25
+ if server.nil?
26
+ return :not_created
27
+ end
28
+ return server["status"].downcase.to_sym
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,110 @@
1
+ require "log4r"
2
+ require 'json'
3
+
4
+ require 'vagrant/util/retryable'
5
+ require 'vagrant-dustcloud/util/timer'
6
+
7
+ module VagrantPlugins
8
+ module Dustcloud
9
+ module Action
10
+ # This runs the configured instance.
11
+ class RunInstance
12
+ include Vagrant::Util::Retryable
13
+
14
+ def initialize(app, env)
15
+ @app = app
16
+ @logger = Log4r::Logger.new("vagrant_dustcloud::action::run_instance")
17
+ end
18
+
19
+ def call(env)
20
+ # Initialize metrics if they haven't been
21
+ env[:metrics] ||= {}
22
+
23
+ flavor = env[:machine].provider_config.flavor.to_s
24
+ image = env[:machine].provider_config.image.to_s
25
+
26
+ env[:ui].info(I18n.t("vagrant_dustcloud.launching_instance"))
27
+ env[:ui].info(" -- Image: #{image}")
28
+ env[:ui].info(" -- Flavor: #{flavor}")
29
+
30
+ begin
31
+ server = env[:dustcloud].create_vm(image, flavor)
32
+ end
33
+
34
+ env[:machine].id = server["uuid"]
35
+ env[:machine_ssh_username] = server["username"]
36
+ env[:machine_ssh_password] = server["password"]
37
+ pw_file = env[:machine].data_dir.join('ssh_password')
38
+ pw_file.open('w+') do |f|
39
+ f.write(server["password"])
40
+ end
41
+
42
+
43
+
44
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
45
+ env[:ui].info(I18n.t("vagrant_dustcloud.waiting_for_ready"))
46
+ total_waited = 0
47
+ while true
48
+ # If we're interrupted don't worry about waiting
49
+ break if env[:interrupted]
50
+
51
+ # Wait for the server to be ready
52
+ vm = env[:dustcloud].get_vm(server["uuid"])
53
+ if vm["status"] == "ACTIVE"
54
+ break
55
+ end
56
+ sleep(2)
57
+ total_waited += 2
58
+ if total_waited > 180
59
+ raise Errors::InstanceReadyTimeout, timeout: 180
60
+ end
61
+ end
62
+ end
63
+
64
+ env[:ui].info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
65
+ if !env[:interrupted]
66
+ env[:metrics]["instance_ssh_time"] = Util::Timer.time do
67
+ # Wait for SSH to be ready.
68
+ env[:ui].info(I18n.t("vagrant_dustcloud.waiting_for_ssh"))
69
+ network_ready_retries = 0
70
+ network_ready_retries_max = 10
71
+ while true
72
+ # If we're interrupted then just back out
73
+ break if env[:interrupted]
74
+ # When an ec2 instance comes up, it's networking may not be ready
75
+ # by the time we connect.
76
+ begin
77
+ break if env[:machine].communicate.ready?
78
+ rescue Exception => e
79
+ if network_ready_retries < network_ready_retries_max then
80
+ network_ready_retries += 1
81
+ @logger.warn("SSH not yet up, retrying...")
82
+ else
83
+ raise e
84
+ end
85
+ end
86
+ sleep 2
87
+ end
88
+ end
89
+ env[:ui].info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
90
+
91
+ # Ready and booted!
92
+ env[:ui].info(I18n.t("vagrant_dustcloud.ready"))
93
+ end
94
+
95
+ terminate(env) if env[:interrupted]
96
+
97
+ @app.call(env)
98
+ end
99
+
100
+ def terminate(env)
101
+ destroy_env = env.dup
102
+ destroy_env.delete(:interrupted)
103
+ destroy_env[:config_validate] = false
104
+ destroy_env[:force_confirm_destroy] = true
105
+ env[:action_runner].run(Action.action_destroy, destroy_env)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,82 @@
1
+ require "log4r"
2
+
3
+ require 'vagrant/util/retryable'
4
+ require 'vagrant-dustcloud/util/timer'
5
+
6
+ module VagrantPlugins
7
+ module Dustcloud
8
+ module Action
9
+ # This starts a stopped instance.
10
+ class StartInstance
11
+
12
+ def initialize(app, env)
13
+ @app = app
14
+ @logger = Log4r::Logger.new("vagrant_dustcloud::action::start_instance")
15
+ end
16
+
17
+ def call(env)
18
+ # Initialize metrics if they haven't been
19
+ env[:metrics] ||= {}
20
+ env[:ui].info(I18n.t("vagrant_dustcloud.starting"))
21
+
22
+ begin
23
+ env[:dustcloud].start_vm(env[:machine].id)
24
+
25
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
26
+ env[:ui].info(I18n.t("vagrant_dustcloud.waiting_for_ready"))
27
+ total_waited = 0
28
+ while true
29
+ # If we're interrupted don't worry about waiting
30
+ break if env[:interrupted]
31
+
32
+ # Wait for the server to be ready
33
+ vm = env[:dustcloud].get_vm(env[:machine].id)
34
+ if vm["status"] == "ACTIVE"
35
+ break
36
+ end
37
+ sleep(2)
38
+ total_waited += 2
39
+ if total_waited > 180
40
+ raise Errors::InstanceReadyTimeout, timeout: 180
41
+ end
42
+ end
43
+ end
44
+
45
+ env[:ui].info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
46
+ if !env[:interrupted]
47
+ env[:metrics]["instance_ssh_time"] = Util::Timer.time do
48
+ # Wait for SSH to be ready.
49
+ env[:ui].info(I18n.t("vagrant_dustcloud.waiting_for_ssh"))
50
+ network_ready_retries = 0
51
+ network_ready_retries_max = 10
52
+ while true
53
+ # If we're interrupted then just back out
54
+ break if env[:interrupted]
55
+ # When an ec2 instance comes up, it's networking may not be ready
56
+ # by the time we connect.
57
+ begin
58
+ break if env[:machine].communicate.ready?
59
+ rescue Exception => e
60
+ if network_ready_retries < network_ready_retries_max then
61
+ network_ready_retries += 1
62
+ @logger.warn("SSH not yet up, retrying...")
63
+ else
64
+ raise e
65
+ end
66
+ end
67
+ sleep 2
68
+ end
69
+ end
70
+ env[:ui].info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
71
+
72
+ # Ready and booted!
73
+ env[:ui].info(I18n.t("vagrant_dustcloud.ready"))
74
+ end
75
+ end
76
+
77
+ @app.call(env)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,26 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Dustcloud
5
+ module Action
6
+ # This stops the running instance.
7
+ class StopInstance
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant_dustcloud::action::stop_instance")
11
+ end
12
+
13
+ def call(env)
14
+ if env[:machine].state.id == :shutoff
15
+ env[:ui].info(I18n.t("vagrant_dustcloud.already_status", :status => env[:machine].state.id))
16
+ else
17
+ env[:ui].info(I18n.t("vagrant_dustcloud.stopping"))
18
+ env[:dustcloud].stop_vm(env[:machine].id)
19
+ end
20
+
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ require "log4r"
2
+ require "json"
3
+
4
+ module VagrantPlugins
5
+ module Dustcloud
6
+ module Action
7
+ # This terminates the running instance.
8
+ class TerminateInstance
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_aws::action::terminate_instance")
12
+ end
13
+
14
+ def call(env)
15
+
16
+ env[:ui].info(I18n.t("vagrant_dustcloud.terminating"))
17
+ result = env[:dustcloud].destroy_vm(env[:machine].id)
18
+ if result["errors"].length > 0
19
+ env[:ui].info(I18n.t("vagrant_dustcloud.terminating_failed"))
20
+ result["errors"].each do |item|
21
+ env[:ui].info(" -- #{item}")
22
+ end
23
+ env[:ui].info(I18n.t("vagrant_dustcloud.terminating_help"))
24
+ end
25
+ env[:machine].id = nil
26
+
27
+ @app.call(env)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ require "vagrant-aws/util/timer"
2
+
3
+ module VagrantPlugins
4
+ module AWS
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,41 @@
1
+ require "log4r"
2
+ require "timeout"
3
+
4
+ module VagrantPlugins
5
+ module Dustcloud
6
+ module Action
7
+ # This action will wait for a machine to reach a specific state or quit by timeout
8
+ class WaitForState
9
+ # env[:result] will be false in case of timeout.
10
+ # @param [Symbol] state Target machine state.
11
+ # @param [Number] timeout Timeout in seconds.
12
+ def initialize(app, env, state, timeout)
13
+ @app = app
14
+ @logger = Log4r::Logger.new("vagrant_dustcloud::action::wait_for_state")
15
+ @state = state
16
+ @timeout = timeout
17
+ end
18
+
19
+ def call(env)
20
+ env[:result] = true
21
+ if env[:machine].state.id == @state
22
+ @logger.info("Status of machine is already #{@state}")
23
+ else
24
+ @logger.info("Waiting for machine to reach state #{@state}")
25
+ begin
26
+ Timeout.timeout(@timeout) do
27
+ until env[:machine].state.id == @state
28
+ sleep 2
29
+ end
30
+ end
31
+ rescue Timeout::Error
32
+ env[:result] = false # couldn't reach state in time
33
+ end
34
+ end
35
+
36
+ @app.call(env)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module Dustcloud
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # @return [String]
7
+ attr_accessor :token_file
8
+
9
+ # @return [String]
10
+ attr_accessor :token
11
+
12
+ # @return [String]
13
+ attr_accessor :image
14
+
15
+ # @return [String]
16
+ attr_accessor :flavor
17
+
18
+ def initialize()
19
+ @token_file = UNSET_VALUE
20
+ @token = UNSET_VALUE
21
+ @image = UNSET_VALUE
22
+ @flavor = UNSET_VALUE
23
+ end
24
+
25
+ def finalize!
26
+ @token_file = ENV['HOME'].to_s + '/.dustcloud' if @token_file == UNSET_VALUE
27
+ @token = Credentials.new.get_token(@token_file) if @token == UNSET_VALUE
28
+ @image = nil if @image == UNSET_VALUE
29
+ @flavor = "tiny" if @flavor == UNSET_VALUE
30
+ end
31
+
32
+ def validate(machine)
33
+ errors = _detected_errors
34
+
35
+ errors << "No token specified" if @token.nil? || @token == ""
36
+ errors << "No image specified" if @image.nil? || @image == ""
37
+
38
+ { "LUDD DUSTCloud Provider" => errors }
39
+ end
40
+ end
41
+
42
+ class Credentials < Vagrant.plugin("2", :config)
43
+ def get_token(token_file)
44
+ if !token_file.nil? && File.exists?(token_file)
45
+ File.read(token_file)
46
+ else
47
+ ENV["DUSTCLOUD_TOKEN"].to_s
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module Dustcloud
5
+ module Errors
6
+ class VagrantDustcloudError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_dustcloud.errors")
8
+ end
9
+
10
+ class APIError < VagrantDustcloudError
11
+ error_key(:api_error)
12
+ end
13
+
14
+ class InternalAPIError < VagrantDustcloudError
15
+ error_key(:internal_api_error)
16
+ end
17
+
18
+ class InstanceReadyTimeout < VagrantDustcloudError
19
+ error_key(:instance_ready_timeout)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The Vagrant Dustcloud plugin must be run within Vagrant."
5
+ end
6
+ # This is a sanity check to make sure no one is attempting to install
7
+ # this into an early Vagrant version.
8
+ if Vagrant::VERSION < "1.2.0"
9
+ raise "The Vagrant Dustcloud plugin is only compatible with Vagrant 1.2+"
10
+ end
11
+ module VagrantPlugins
12
+ module Dustcloud
13
+ class Plugin < Vagrant.plugin("2")
14
+ name "LUDD DUSTcloud Provisioner"
15
+ description <<-DESC
16
+ This plugin installs a provider that allows Vagrant to manage
17
+ machines in LUDD DUSTcloud.
18
+ DESC
19
+
20
+ config(:dustcloud, :provider) do
21
+ require_relative "config"
22
+ Config
23
+ end
24
+
25
+ provider(:dustcloud) do
26
+ setup_i18n
27
+
28
+ require_relative "provider"
29
+ Provider
30
+ end
31
+
32
+ def self.setup_i18n
33
+ I18n.load_path << File.expand_path("locales/en.yaml", Dustcloud.source_root)
34
+ I18n.reload!
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module Dustcloud
5
+ class Provider < Vagrant.plugin("2", :provider)
6
+ def initialize(machine)
7
+ @machine = machine
8
+ end
9
+
10
+ def action(name)
11
+ # Attempt to get the action method from the Action class if it
12
+ # exists, otherwise return nil to show that we don't support the
13
+ # given action.
14
+ action_method = "action_#{name}"
15
+ return Action.send(action_method) if Action.respond_to?(action_method)
16
+ nil
17
+ end
18
+
19
+ def ssh_info
20
+ # Run a custom action called "read_ssh_info" which does what it
21
+ # says and puts the resulting SSH info into the `:machine_ssh_info`
22
+ # key in the environment.
23
+ env = @machine.action("read_ssh_info", lock: false)
24
+ env[:machine_ssh_info]
25
+ end
26
+
27
+ def state
28
+ # Run a custom action we define called "read_state" which does
29
+ # what it says. It puts the state in the `:machine_state_id`
30
+ # key in the environment.
31
+ env = @machine.action("read_state", lock: false)
32
+
33
+ state_id = env[:machine_state_id]
34
+
35
+ # Get the short and long description
36
+ short = I18n.t("vagrant_dustcloud.states.short_#{state_id}")
37
+ long = I18n.t("vagrant_dustcloud.states.long_#{state_id}")
38
+
39
+ # Return the MachineState object
40
+ Vagrant::MachineState.new(state_id, short, long)
41
+ end
42
+
43
+ def to_s
44
+ id = @machine.id.nil? ? "new" : @machine.id
45
+ "AWS (#{id})"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,17 @@
1
+ module VagrantPlugins
2
+ module Dustcloud
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 Dustcloud
3
+ VERSION = '0.0.5'
4
+ end
5
+ end
@@ -0,0 +1,71 @@
1
+ en:
2
+ vagrant_dustcloud:
3
+ already_status: |-
4
+ The machine is already %{status}.
5
+ launching_instance: |-
6
+ Launching an instance with the following settings...
7
+ not_created: |-
8
+ Instance is not created. Please run `vagrant up` first.
9
+ ready: |-
10
+ Machine is booted and ready for use!
11
+ rsync_not_found_warning: |-
12
+ Warning! Folder sync disabled because the rsync binary is missing in the %{side}.
13
+ Make sure rsync is installed and the binary can be found in the PATH.
14
+ rsync_folder: |-
15
+ Rsyncing folder: %{hostpath} => %{guestpath}
16
+ starting: |-
17
+ Starting the instance...
18
+ stopping: |-
19
+ Stopping the instance...
20
+ terminating: |-
21
+ Terminating the instance...
22
+ terminating_failed: |-
23
+ Failed to terminate the instance
24
+ terminating_help: |-
25
+ You may need to use the Cloud Dashboard (https://dust.ludd.ltu.se) or contact the administrators.
26
+ waiting_for_ready: |-
27
+ Waiting for instance to become "ready"...
28
+ waiting_for_ssh: |-
29
+ Waiting for SSH to become available...
30
+ will_not_destroy: |-
31
+ The instance '%{name}' will not be destroyed, since the confirmation
32
+ was declined.
33
+ config:
34
+ token_required: |-
35
+ An access key ID must be specified via "access_key_id"
36
+ errors:
37
+ api_error: |-
38
+ There was an error talking to Dustcloud. The error message is shown
39
+ below:
40
+ %{message}
41
+ internal_api_error: |-
42
+ There was an error talking to Dustcloud. The error message is shown
43
+ below:
44
+ Error: %{error}
45
+ Response: %{response}
46
+ instance_ready_timeout: |-
47
+ The instance never became "ready" in Dustcloud. The timeout currently
48
+ set waiting for the instance to become ready is %{timeout} seconds.
49
+ Please verify that the machine properly boots.
50
+ states:
51
+ short_not_created: |-
52
+ not created
53
+ long_not_created: |-
54
+ The Dustcloud instance is not created. Run `vagrant up` to create it.
55
+ short_shutoff: |-
56
+ shut off
57
+ long_shutoff: |-
58
+ The Dustcloud instance is stopped. Run `vagrant up` to start it.
59
+ short_build: |-
60
+ building
61
+ long_build: |-
62
+ The Dustcloud instance is building/planning. It will soon start installation.
63
+ short_installing: |-
64
+ installing
65
+ long_installing: |-
66
+ The Dustcloud instance is currently setting up.
67
+ short_active: |-
68
+ running
69
+ long_active: |-
70
+ The Dustcloud instance is running. To stop this machine, you can run
71
+ `vagrant halt`. To destroy the machine, you can run `vagrant destroy`.
@@ -0,0 +1,50 @@
1
+ $:.unshift File.expand_path("../lib", __FILE__)
2
+ require "vagrant-dustcloud/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "vagrant-dustcloud"
6
+ s.version = VagrantPlugins::Dustcloud::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.license = "MIT"
9
+ s.authors = "Johan Jatko"
10
+ s.email = "armedguy@ludd.ltu.se"
11
+ s.homepage = "https://www.ludd.ltu.se"
12
+ s.summary = "Enables Vagrant to manage LUDD Dustcloud machines."
13
+ s.description = "Enables Vagrant to manage LUDD Dustcloud machines."
14
+
15
+
16
+ s.add_development_dependency "rake"
17
+ # rspec 3.4 to mock File
18
+ s.add_development_dependency "rspec", "~> 3.4"
19
+ s.add_development_dependency "rspec-its"
20
+
21
+ root_path = File.dirname(__FILE__)
22
+ all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") }
23
+ all_files.reject! { |file| [".", ".."].include?(File.basename(file)) }
24
+ gitignore_path = File.join(root_path, ".gitignore")
25
+ gitignore = File.readlines(gitignore_path)
26
+ gitignore.map! { |line| line.chomp.strip }
27
+ gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ }
28
+
29
+ unignored_files = all_files.reject do |file|
30
+ # Ignore any directories, the gemspec only cares about files
31
+ next true if File.directory?(file)
32
+
33
+ # Ignore any paths that match anything in the gitignore. We do
34
+ # two tests here:
35
+ #
36
+ # - First, test to see if the entire path matches the gitignore.
37
+ # - Second, match if the basename does, this makes it so that things
38
+ # like '.DS_Store' will match sub-directories too (same behavior
39
+ # as git).
40
+ #
41
+ gitignore.any? do |ignore|
42
+ File.fnmatch(ignore, file, File::FNM_PATHNAME) ||
43
+ File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME)
44
+ end
45
+ end
46
+
47
+ s.files = unignored_files
48
+ s.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact
49
+ s.require_path = 'lib'
50
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vagrant-dustcloud
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Johan Jatko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-its
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Enables Vagrant to manage LUDD Dustcloud machines.
56
+ email: armedguy@ludd.ltu.se
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".gitignore"
62
+ - Gemfile
63
+ - README.md
64
+ - Rakefile
65
+ - dummy.box
66
+ - example_box/README.md
67
+ - example_box/metadata.json
68
+ - lib/vagrant-dustcloud.rb
69
+ - lib/vagrant-dustcloud/action.rb
70
+ - lib/vagrant-dustcloud/action/connect_dustcloud.rb
71
+ - lib/vagrant-dustcloud/action/is_created.rb
72
+ - lib/vagrant-dustcloud/action/is_stopped.rb
73
+ - lib/vagrant-dustcloud/action/message_already_created.rb
74
+ - lib/vagrant-dustcloud/action/message_not_created.rb
75
+ - lib/vagrant-dustcloud/action/message_will_not_destroy.rb
76
+ - lib/vagrant-dustcloud/action/read_ssh_info.rb
77
+ - lib/vagrant-dustcloud/action/read_state.rb
78
+ - lib/vagrant-dustcloud/action/run_instance.rb
79
+ - lib/vagrant-dustcloud/action/start_instance.rb
80
+ - lib/vagrant-dustcloud/action/stop_instance.rb
81
+ - lib/vagrant-dustcloud/action/terminate_instance.rb
82
+ - lib/vagrant-dustcloud/action/timed_provision.rb
83
+ - lib/vagrant-dustcloud/action/wait_for_state.rb
84
+ - lib/vagrant-dustcloud/config.rb
85
+ - lib/vagrant-dustcloud/errors.rb
86
+ - lib/vagrant-dustcloud/plugin.rb
87
+ - lib/vagrant-dustcloud/provider.rb
88
+ - lib/vagrant-dustcloud/util/timer.rb
89
+ - lib/vagrant-dustcloud/version.rb
90
+ - locales/en.yaml
91
+ - vagrant-dustcloud.gemspec
92
+ homepage: https://www.ludd.ltu.se
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.1.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Enables Vagrant to manage LUDD Dustcloud machines.
115
+ test_files: []