vagrant-niftycloud 0.1.0.dev

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/.gitignore +18 -0
  2. data/CHANGELOG.md +1 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE +8 -0
  5. data/README.md +256 -0
  6. data/Rakefile +21 -0
  7. data/dummy.box +0 -0
  8. data/example_box/README.md +13 -0
  9. data/example_box/metadata.json +3 -0
  10. data/lib/vagrant-niftycloud.rb +18 -0
  11. data/lib/vagrant-niftycloud/action.rb +178 -0
  12. data/lib/vagrant-niftycloud/action/connect_niftycloud.rb +56 -0
  13. data/lib/vagrant-niftycloud/action/is_created.rb +18 -0
  14. data/lib/vagrant-niftycloud/action/message_already_created.rb +16 -0
  15. data/lib/vagrant-niftycloud/action/message_not_created.rb +16 -0
  16. data/lib/vagrant-niftycloud/action/message_will_not_destroy.rb +16 -0
  17. data/lib/vagrant-niftycloud/action/read_ssh_info.rb +52 -0
  18. data/lib/vagrant-niftycloud/action/read_state.rb +56 -0
  19. data/lib/vagrant-niftycloud/action/resume_instance.rb +56 -0
  20. data/lib/vagrant-niftycloud/action/run_instance.rb +131 -0
  21. data/lib/vagrant-niftycloud/action/suspend_instance.rb +56 -0
  22. data/lib/vagrant-niftycloud/action/sync_folders.rb +67 -0
  23. data/lib/vagrant-niftycloud/action/terminate_instance.rb +61 -0
  24. data/lib/vagrant-niftycloud/action/timed_provision.rb +21 -0
  25. data/lib/vagrant-niftycloud/config.rb +210 -0
  26. data/lib/vagrant-niftycloud/errors.rb +35 -0
  27. data/lib/vagrant-niftycloud/plugin.rb +73 -0
  28. data/lib/vagrant-niftycloud/provider.rb +50 -0
  29. data/lib/vagrant-niftycloud/util/timer.rb +17 -0
  30. data/lib/vagrant-niftycloud/version.rb +5 -0
  31. data/locales/en.yml +91 -0
  32. data/spec/vagrant-niftycloud/config_spec.rb +154 -0
  33. data/vagrant-niftycloud.gemspec +57 -0
  34. metadata +157 -0
@@ -0,0 +1,67 @@
1
+ require "log4r"
2
+
3
+ require "vagrant/util/subprocess"
4
+
5
+ module VagrantPlugins
6
+ module NiftyCloud
7
+ module Action
8
+ # This middleware uses `rsync` to sync the folders over to the
9
+ # NiftyCloud instance.
10
+ class SyncFolders
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_niftycloud::action::sync_folders")
14
+ end
15
+
16
+ def call(env)
17
+ @app.call(env)
18
+
19
+ ssh_info = env[:machine].ssh_info
20
+ env[:machine].config.vm.synced_folders.each do |id, data|
21
+ begin
22
+ # Ignore disabled shared folders
23
+ next if data[:disabled]
24
+
25
+ hostpath = File.expand_path(data[:hostpath], env[:root_path])
26
+ guestpath = data[:guestpath]
27
+
28
+ # Make sure there is a trailing slash on the host path to
29
+ # avoid creating an additional directory with rsync
30
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
31
+
32
+ env[:ui].info(I18n.t("vagrant_niftycloud.rsync_folder",
33
+ :hostpath => hostpath,
34
+ :guestpath => guestpath))
35
+
36
+ # Create the guest path
37
+ env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
38
+ env[:machine].communicate.sudo(
39
+ "chown #{ssh_info[:username]} '#{guestpath}'")
40
+
41
+ # Rsync over to the guest path using the SSH info
42
+ command = [
43
+ "rsync", "--verbose", "--archive", "-z",
44
+ "--exclude", ".vagrant/",
45
+ "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no -i '#{ssh_info[:private_key_path]}'",
46
+ hostpath,
47
+ "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
48
+
49
+ r = Vagrant::Util::Subprocess.execute(*command)
50
+ if r.exit_code != 0
51
+ raise Errors::RsyncError,
52
+ :guestpath => guestpath,
53
+ :hostpath => hostpath,
54
+ :stderr => r.stderr
55
+ end
56
+ rescue => e
57
+ raise Errors::RsyncError,
58
+ :guestpath => guestpath,
59
+ :hostpath => hostpath,
60
+ :stderr => e.message
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "log4r"
3
+
4
+ module VagrantPlugins
5
+ module NiftyCloud
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_niftycloud::action::terminate_instance")
12
+ end
13
+
14
+ def call(env)
15
+
16
+ # 例外の定義は以下参照
17
+ # http://cloud.nifty.com/api/sdk/rdoc/
18
+ begin
19
+ env[:ui].info(I18n.t("vagrant_niftycloud.terminating"))
20
+
21
+ # 起動直後等、terminate処理できないステータスの場合一旦待つ
22
+ server = env[:niftycloud_compute].describe_instances(:instance_id => env[:machine].id).reservationSet.item.first.instancesSet.item.first
23
+ while server.instanceState.name == 'pending'
24
+ sleep 5
25
+ server = env[:niftycloud_compute].describe_instances(:instance_id => env[:machine].id).reservationSet.item.first.instancesSet.item.first
26
+ end
27
+
28
+ attribute = env[:niftycloud_compute].describe_instance_attribute(:instance_id => env[:machine].id, :attribute => 'disableApiTermination')
29
+ if attribute.disableApiTermination.value == 'false'
30
+ # AWSのように即terminateができないため念のため一旦stopする
31
+ # TODO API経由でのterminate不可の場合を考慮する必要があるか
32
+ server = env[:niftycloud_compute].describe_instances(:instance_id => env[:machine].id).reservationSet.item.first.instancesSet.item.first
33
+ if server.instanceState.name != 'stopped'
34
+ env[:niftycloud_compute].stop_instances(:instance_id => env[:machine].id, :force => true)
35
+ while server.instanceState.name != 'stopped'
36
+ sleep 5
37
+ server = env[:niftycloud_compute].describe_instances(:instance_id => env[:machine].id).reservationSet.item.first.instancesSet.item.first
38
+ end
39
+ end
40
+ end
41
+
42
+ # terminate処理
43
+ server = env[:niftycloud_compute].describe_instances(:instance_id => env[:machine].id).reservationSet.item.first.instancesSet.item.first
44
+ if server.instanceState.name == 'stopped'
45
+ response = env[:niftycloud_compute].terminate_instances(:instance_id => env[:machine].id)
46
+ env[:machine].id = nil
47
+
48
+ @app.call(env)
49
+ end
50
+ rescue NIFTY::ConfigurationError => e
51
+ raise VagrantPlugins::NiftyCloud::Errors::NiftyCloudConfigurationError,
52
+ :message => e.message
53
+ rescue NIFTY::ArgumentError => e
54
+ raise VagrantPlugins::NiftyCloud::Errors::NiftyCloudArgumentError,
55
+ :message => e.message
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ require "vagrant-niftycloud/util/timer"
2
+
3
+ module VagrantPlugins
4
+ module NiftyCloud
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,210 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module NiftyCloud
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # The access key ID for accessing NiftyCloud.
7
+ #
8
+ # @return [String]
9
+ attr_accessor :access_key_id
10
+
11
+ # The ID of the AMI to use.
12
+ #
13
+ # @return [String]
14
+ attr_accessor :image_id
15
+
16
+ # The zone to launch the instance into. If nil, it will
17
+ # use the default for your account.
18
+ #
19
+ # @return [String]
20
+ attr_accessor :zone
21
+
22
+ # The timeout to wait for an instance to become ready.
23
+ #
24
+ # @return [Fixnum]
25
+ attr_accessor :instance_ready_timeout
26
+
27
+ # The type of instance to launch, such as "m1.small"
28
+ #
29
+ # @return [String]
30
+ attr_accessor :instance_type
31
+
32
+ # The name of the keypair to use.
33
+ #
34
+ # @return [String]
35
+ attr_accessor :key_name
36
+
37
+ # The secret access key for accessing NiftyCloud.
38
+ #
39
+ # @return [String]
40
+ attr_accessor :secret_access_key
41
+
42
+ # The firewall to set on the instance.
43
+ # be a list of IDs. For NiftyCloud, it can be either.
44
+ #
45
+ # @return [Array<String>]
46
+ attr_accessor :firewall
47
+
48
+ # The user data string
49
+ #
50
+ # @return [String]
51
+ attr_accessor :user_data
52
+
53
+ def initialize(zone_specific=false)
54
+ @access_key_id = UNSET_VALUE
55
+ @image_id = UNSET_VALUE
56
+ @zone = UNSET_VALUE
57
+ @instance_ready_timeout = UNSET_VALUE
58
+ @instance_type = UNSET_VALUE
59
+ @key_name = UNSET_VALUE
60
+ @secret_access_key = UNSET_VALUE
61
+ @firewall = UNSET_VALUE
62
+ @user_data = UNSET_VALUE
63
+
64
+ # Internal state (prefix with __ so they aren't automatically
65
+ # merged)
66
+ @__compiled_zone_configs = {}
67
+ @__finalized = false
68
+ @__zone_config = {}
69
+ @__zone_specific = zone_specific
70
+ end
71
+
72
+ # Allows zone-specific overrides of any of the settings on this
73
+ # configuration object. This allows the user to override things like
74
+ # image_id and key name for zones. Example:
75
+ #
76
+ # niftycloud.zone_config "east-12" do |zone|
77
+ # zone.image_id = 21
78
+ # zone.key_name = "company-east12"
79
+ # end
80
+ #
81
+ # @param [String] zone The zone name to configure.
82
+ # @param [Hash] attributes Direct attributes to set on the configuration
83
+ # as a shortcut instead of specifying a full block.
84
+ # @yield [config] Yields a new Nifty Cloud configuration.
85
+ def zone_config(zone, attributes=nil, &block)
86
+ # Append the block to the list of zone configs for that zone.
87
+ # We'll evaluate these upon finalization.
88
+ @__zone_config[zone] ||= []
89
+
90
+ # Append a block that sets attributes if we got one
91
+ if attributes
92
+ attr_block = lambda do |config|
93
+ config.set_options(attributes)
94
+ end
95
+
96
+ @__zone_config[zone] << attr_block
97
+ end
98
+
99
+ # Append a block if we got one
100
+ @__zone_config[zone] << block if block_given?
101
+ end
102
+
103
+ #-------------------------------------------------------------------
104
+ # Internal methods.
105
+ #-------------------------------------------------------------------
106
+
107
+ def merge(other)
108
+ super.tap do |result|
109
+ # Copy over the zone specific flag. "True" is retained if either
110
+ # has it.
111
+ new_zone_specific = other.instance_variable_get(:@__zone_specific)
112
+ result.instance_variable_set(
113
+ :@__zone_specific, new_zone_specific || @__zone_specific)
114
+
115
+ # Go through all the zone configs and prepend ours onto
116
+ # theirs.
117
+ new_zone_config = other.instance_variable_get(:@__zone_config)
118
+ @__zone_config.each do |key, value|
119
+ new_zone_config[key] ||= []
120
+ new_zone_config[key] = value + new_zone_config[key]
121
+ end
122
+
123
+ # Set it
124
+ result.instance_variable_set(:@__zone_config, new_zone_config)
125
+ end
126
+ end
127
+
128
+ def finalize!
129
+ # Try to get access keys from standard NiftyCloud environment variables; they
130
+ # will default to nil if the environment variables are not present.
131
+ @access_key_id = ENV['NIFTY_ACCESS_KEY'] if @access_key_id == UNSET_VALUE
132
+ @secret_access_key = ENV['NIFTY_SECRET_KEY'] if @secret_access_key == UNSET_VALUE
133
+
134
+ # AMI must be nil, since we can't default that
135
+ @image_id = nil if @image_id == UNSET_VALUE
136
+
137
+ # Set the default timeout for waiting for an instance to be ready
138
+ @instance_ready_timeout = 300 if @instance_ready_timeout == UNSET_VALUE
139
+
140
+ # Default instance type is an mini
141
+ @instance_type = "mini" if @instance_type == UNSET_VALUE
142
+
143
+ # Keypair defaults to nil
144
+ @key_name = nil if @key_name == UNSET_VALUE
145
+
146
+ @zone = nil if @zone == UNSET_VALUE
147
+
148
+ # The firewall are empty by default.
149
+ @firewall = [] if @firewall == UNSET_VALUE
150
+
151
+ # User Data is nil by default
152
+ @user_data = nil if @user_data == UNSET_VALUE
153
+
154
+ if !@__zone_specific
155
+ @__zone_config.each do |zone, blocks|
156
+ config = self.class.new(true).merge(self)
157
+
158
+ # Execute the configuration for each block
159
+ blocks.each { |b| b.call(config) }
160
+
161
+ # The zone name of the configuration always equals the
162
+ # zone config name:
163
+ config.zone = zone
164
+
165
+ # Finalize the configuration
166
+ config.finalize!
167
+
168
+ # Store it for retrieval
169
+ @__compiled_zone_configs[zone] = config
170
+ end
171
+ end
172
+
173
+ # Mark that we finalized
174
+ @__finalized = true
175
+ end
176
+
177
+ def validate(machine)
178
+ errors = _detected_errors
179
+
180
+ errors << I18n.t("vagrant_niftycloud.config.zone_required") if @zone.nil?
181
+
182
+ if @zone
183
+ # Get the configuration for the zone we're using and validate only
184
+ # that zone.
185
+ config = get_zone_config(@zone)
186
+
187
+ errors << I18n.t("vagrant_niftycloud.config.access_key_id_required") if \
188
+ config.access_key_id.nil?
189
+ errors << I18n.t("vagrant_niftycloud.config.secret_access_key_required") if \
190
+ config.secret_access_key.nil?
191
+
192
+ errors << I18n.t("vagrant_niftycloud.config.image_id_required") if config.image_id.nil?
193
+ end
194
+
195
+ { "NiftyCloud Provider" => errors }
196
+ end
197
+
198
+ # This gets the configuration for a specific zone. It shouldn't
199
+ # be called by the general public and is only used internally.
200
+ def get_zone_config(name)
201
+ if !@__finalized
202
+ raise "Configuration must be finalized before calling this method."
203
+ end
204
+
205
+ # Return the compiled zone config
206
+ @__compiled_zone_configs[name] || self
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,35 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module NiftyCloud
5
+ module Errors
6
+ class VagrantNiftyCloudError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_niftycloud.errors")
8
+ end
9
+
10
+ class NiftyCloudConfigurationError < VagrantNiftyCloudError
11
+ error_key(:niftycloud_configuration_error)
12
+ end
13
+
14
+ class NiftyCloudArgumentError < VagrantNiftyCloudError
15
+ error_key(:niftycloud_argument_error)
16
+ end
17
+
18
+ class NiftyCloudResponseError < VagrantNiftyCloudError
19
+ error_key(:niftycloud_response_error)
20
+ end
21
+
22
+ class NiftyCloudResponseFormatError < VagrantNiftyCloudError
23
+ error_key(:niftycloud_response_format_error)
24
+ end
25
+
26
+ class InstanceReadyTimeout < VagrantNiftyCloudError
27
+ error_key(:instance_ready_timeout)
28
+ end
29
+
30
+ class RsyncError < VagrantNiftyCloudError
31
+ error_key(:rsync_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 NiftyCloud 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 NiftyCloud plugin is only compatible with Vagrant 1.2+"
11
+ end
12
+
13
+ module VagrantPlugins
14
+ module NiftyCloud
15
+ class Plugin < Vagrant.plugin("2")
16
+ name "NiftyCloud"
17
+ description <<-DESC
18
+ This plugin installs a provider that allows Vagrant to manage
19
+ machines in NiftyCloud.
20
+ DESC
21
+
22
+ config(:niftycloud, :provider) do
23
+ require_relative "config"
24
+ Config
25
+ end
26
+
27
+ provider(:niftycloud, 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", NiftyCloud.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_niftycloud")
66
+ logger.outputters = Log4r::Outputter.stderr
67
+ logger.level = level
68
+ logger = nil
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end