vagrant-skytap 0.1.1a
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.hgignore +22 -0
- data/.project +11 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +85 -0
- data/Gemfile +29 -0
- data/LICENSE +8 -0
- data/README.md +292 -0
- data/Rakefile +31 -0
- data/Vagrantfile.orig +34 -0
- data/bar/checksums.yaml +7 -0
- data/bar/data.tar +0 -0
- data/bar/metadata +242 -0
- data/dummy.box +0 -0
- data/example_box/README.md +13 -0
- data/example_box/metadata.json +3 -0
- data/lib/vagrant-skytap/action/add_vm_to_environment.rb +35 -0
- data/lib/vagrant-skytap/action/create_environment.rb +44 -0
- data/lib/vagrant-skytap/action/delete_environment.rb +26 -0
- data/lib/vagrant-skytap/action/delete_vm.rb +27 -0
- data/lib/vagrant-skytap/action/existence_check.rb +35 -0
- data/lib/vagrant-skytap/action/fetch_environment.rb +32 -0
- data/lib/vagrant-skytap/action/initialize_api_client.rb +28 -0
- data/lib/vagrant-skytap/action/is_running.rb +19 -0
- data/lib/vagrant-skytap/action/is_stopped.rb +19 -0
- data/lib/vagrant-skytap/action/is_suspended.rb +19 -0
- data/lib/vagrant-skytap/action/message_already_created.rb +16 -0
- data/lib/vagrant-skytap/action/message_already_running.rb +16 -0
- data/lib/vagrant-skytap/action/message_environment_url.rb +16 -0
- data/lib/vagrant-skytap/action/message_not_created.rb +16 -0
- data/lib/vagrant-skytap/action/message_will_not_destroy.rb +16 -0
- data/lib/vagrant-skytap/action/mixin_machine_index.rb +22 -0
- data/lib/vagrant-skytap/action/prepare_nfs_settings.rb +46 -0
- data/lib/vagrant-skytap/action/prepare_nfs_valid_ids.rb +28 -0
- data/lib/vagrant-skytap/action/read_ssh_info.rb +23 -0
- data/lib/vagrant-skytap/action/read_state.rb +42 -0
- data/lib/vagrant-skytap/action/run_environment.rb +53 -0
- data/lib/vagrant-skytap/action/run_vm.rb +51 -0
- data/lib/vagrant-skytap/action/set_hostname.rb +31 -0
- data/lib/vagrant-skytap/action/set_up_vm.rb +21 -0
- data/lib/vagrant-skytap/action/stop_environment.rb +43 -0
- data/lib/vagrant-skytap/action/stop_vm.rb +43 -0
- data/lib/vagrant-skytap/action/store_extra_data.rb +35 -0
- data/lib/vagrant-skytap/action/suspend_environment.rb +32 -0
- data/lib/vagrant-skytap/action/suspend_vm.rb +32 -0
- data/lib/vagrant-skytap/action/timed_provision.rb +21 -0
- data/lib/vagrant-skytap/action/update_hardware.rb +37 -0
- data/lib/vagrant-skytap/action.rb +272 -0
- data/lib/vagrant-skytap/api/busyable.rb +37 -0
- data/lib/vagrant-skytap/api/client.rb +127 -0
- data/lib/vagrant-skytap/api/credentials.rb +41 -0
- data/lib/vagrant-skytap/api/environment.rb +99 -0
- data/lib/vagrant-skytap/api/interface.rb +123 -0
- data/lib/vagrant-skytap/api/network.rb +40 -0
- data/lib/vagrant-skytap/api/public_ip.rb +103 -0
- data/lib/vagrant-skytap/api/published_service.rb +90 -0
- data/lib/vagrant-skytap/api/resource.rb +44 -0
- data/lib/vagrant-skytap/api/runstate_operations.rb +63 -0
- data/lib/vagrant-skytap/api/specified_attributes.rb +27 -0
- data/lib/vagrant-skytap/api/vm.rb +88 -0
- data/lib/vagrant-skytap/api/vpn.rb +146 -0
- data/lib/vagrant-skytap/api/vpn_attachment.rb +57 -0
- data/lib/vagrant-skytap/config.rb +106 -0
- data/lib/vagrant-skytap/core_ext/object/blank.rb +82 -0
- data/lib/vagrant-skytap/core_ext/object/tap.rb +8 -0
- data/lib/vagrant-skytap/core_ext/try.rb +42 -0
- data/lib/vagrant-skytap/environment_properties.rb +11 -0
- data/lib/vagrant-skytap/errors.rb +59 -0
- data/lib/vagrant-skytap/plugin.rb +73 -0
- data/lib/vagrant-skytap/properties.rb +42 -0
- data/lib/vagrant-skytap/provider.rb +50 -0
- data/lib/vagrant-skytap/setup_helper.rb +193 -0
- data/lib/vagrant-skytap/util/ip_address.rb +69 -0
- data/lib/vagrant-skytap/util/subnet.rb +97 -0
- data/lib/vagrant-skytap/util/timer.rb +17 -0
- data/lib/vagrant-skytap/version.rb +5 -0
- data/lib/vagrant-skytap/version.rb.orig +5 -0
- data/lib/vagrant-skytap/vm_properties.rb +22 -0
- data/lib/vagrant-skytap.rb +23 -0
- data/locales/en.yml +127 -0
- data/skytap-dummy.box +0 -0
- data/spec/acceptance/base.rb +2 -0
- data/spec/acceptance/provider/halt_spec.rb +3 -0
- data/spec/acceptance/shared/context_skytap.rb +3 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/isolated_environment.rb +45 -0
- data/spec/unit/base.rb +57 -0
- data/spec/unit/config_spec.rb +73 -0
- data/spec/unit/environment_spec.rb +144 -0
- data/spec/unit/skeletons/empty_environment.json +19 -0
- data/spec/unit/skeletons/network1.json +36 -0
- data/spec/unit/skeletons/vm1.json +85 -0
- data/spec/unit/support/dummy_communicator.rb +83 -0
- data/spec/unit/support/dummy_provider.rb +41 -0
- data/spec/unit/support/isolated_environment.rb +217 -0
- data/spec/unit/support/shared/action_synced_folders_context.rb +15 -0
- data/spec/unit/support/shared/base_context.rb +116 -0
- data/spec/unit/support/shared/capability_helpers_context.rb +29 -0
- data/spec/unit/support/shared/plugin_command_context.rb +12 -0
- data/spec/unit/support/shared/skytap_context.rb +3 -0
- data/spec/unit/vm_spec.rb +118 -0
- data/tasks/acceptance.rake +22 -0
- data/tasks/bundler.rake +3 -0
- data/tasks/test.rake +14 -0
- data/vagrant-skytap.gemspec +62 -0
- data/vagrant-spec.config.rb +10 -0
- metadata +247 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
require "vagrant"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Skytap
|
5
|
+
class Config < Vagrant.plugin("2", :config)
|
6
|
+
# The user id for accessing Skytap.
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :username
|
10
|
+
|
11
|
+
# The secret API token for accessing Skytap.
|
12
|
+
#
|
13
|
+
# @return [String]
|
14
|
+
attr_accessor :api_token
|
15
|
+
|
16
|
+
# The base URL for Skytap API calls.
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
attr_accessor :base_url
|
20
|
+
|
21
|
+
# The url of the source VM to use.
|
22
|
+
#
|
23
|
+
# @return [String]
|
24
|
+
attr_accessor :vm_url
|
25
|
+
|
26
|
+
# The timeout to wait for a VM to become ready.
|
27
|
+
#
|
28
|
+
# @return [Fixnum]
|
29
|
+
attr_accessor :instance_ready_timeout
|
30
|
+
|
31
|
+
# The total number of virtual CPUs for this VM.
|
32
|
+
#
|
33
|
+
# @return [Integer]
|
34
|
+
attr_accessor :cpus
|
35
|
+
|
36
|
+
# The number of virtual CPUs per socket.
|
37
|
+
#
|
38
|
+
# @return [Integer]
|
39
|
+
attr_accessor :cpuspersocket
|
40
|
+
|
41
|
+
# The RAM to use for this machine (measured in MB).
|
42
|
+
#
|
43
|
+
# @return [Integer]
|
44
|
+
attr_accessor :ram
|
45
|
+
|
46
|
+
# The VMware guest OS setting for this machine.
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
attr_accessor :guestos
|
50
|
+
|
51
|
+
def initialize(region_specific=false)
|
52
|
+
@username = UNSET_VALUE
|
53
|
+
@api_token = UNSET_VALUE
|
54
|
+
@base_url = UNSET_VALUE
|
55
|
+
@vm_url = UNSET_VALUE
|
56
|
+
@instance_ready_timeout = UNSET_VALUE
|
57
|
+
@region = UNSET_VALUE
|
58
|
+
|
59
|
+
@cpus = UNSET_VALUE
|
60
|
+
@cpuspersocket = UNSET_VALUE
|
61
|
+
@ram = UNSET_VALUE
|
62
|
+
@guestos = UNSET_VALUE
|
63
|
+
end
|
64
|
+
|
65
|
+
#-------------------------------------------------------------------
|
66
|
+
# Internal methods.
|
67
|
+
#-------------------------------------------------------------------
|
68
|
+
|
69
|
+
def finalize!
|
70
|
+
# Try to get access keys from standard Skytap environment variables; they
|
71
|
+
# will default to nil if the environment variables are not present.
|
72
|
+
@username = ENV['VAGRANT_SKYTAP_USERNAME'] if @username == UNSET_VALUE
|
73
|
+
@api_token = ENV['VAGRANT_SKYTAP_API_TOKEN'] if @api_token == UNSET_VALUE
|
74
|
+
|
75
|
+
# Base URL for API calls.
|
76
|
+
@base_url = "https://cloud.skytap.com/" if @base_url == UNSET_VALUE
|
77
|
+
|
78
|
+
# Source VM url must be set.
|
79
|
+
@vm_url = nil if @vm_url == UNSET_VALUE
|
80
|
+
|
81
|
+
# Set the default timeout for waiting for an instance to be ready
|
82
|
+
@instance_ready_timeout = 120 if @instance_ready_timeout == UNSET_VALUE
|
83
|
+
|
84
|
+
# Hardware settings default to nil (will be obtained
|
85
|
+
# from the source VM)
|
86
|
+
@cpus = nil if @cpus == UNSET_VALUE
|
87
|
+
@cpuspersocket = nil if @cpuspersocket == UNSET_VALUE
|
88
|
+
@ram = nil if @ram == UNSET_VALUE
|
89
|
+
@guestos = nil if @guestos == UNSET_VALUE
|
90
|
+
|
91
|
+
# Mark that we finalized
|
92
|
+
@__finalized = true
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate(machine)
|
96
|
+
errors = _detected_errors
|
97
|
+
|
98
|
+
errors << I18n.t('vagrant_skytap.config.username_required') unless username
|
99
|
+
errors << I18n.t('vagrant_skytap.config.api_token_required') unless api_token
|
100
|
+
errors << I18n.t('vagrant_skytap.config.vm_url_required') unless vm_url
|
101
|
+
|
102
|
+
{ "Skytap Provider" => errors }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Copied from Rails ActiveSupport 2.3.18:
|
2
|
+
# activesupport-2.3.18/lib/active_support/core_ext/object/blank.rb
|
3
|
+
#
|
4
|
+
# == License
|
5
|
+
# Active Support is released under the MIT license.
|
6
|
+
|
7
|
+
class Object
|
8
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
9
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
10
|
+
#
|
11
|
+
# This simplifies:
|
12
|
+
#
|
13
|
+
# if !address.nil? && !address.empty?
|
14
|
+
#
|
15
|
+
# ...to:
|
16
|
+
#
|
17
|
+
# if !address.blank?
|
18
|
+
def blank?
|
19
|
+
respond_to?(:empty?) ? empty? : !self
|
20
|
+
end
|
21
|
+
|
22
|
+
# An object is present if it's not blank.
|
23
|
+
def present?
|
24
|
+
!blank?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns object if it's #present? otherwise returns nil.
|
28
|
+
# object.presence is equivalent to object.present? ? object : nil.
|
29
|
+
#
|
30
|
+
# This is handy for any representation of objects where blank is the same
|
31
|
+
# as not present at all. For example, this simplifies a common check for
|
32
|
+
# HTTP POST/query parameters:
|
33
|
+
#
|
34
|
+
# state = params[:state] if params[:state].present?
|
35
|
+
# country = params[:country] if params[:country].present?
|
36
|
+
# region = state || country || 'US'
|
37
|
+
#
|
38
|
+
# ...becomes:
|
39
|
+
#
|
40
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
41
|
+
def presence
|
42
|
+
self if present?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class NilClass #:nodoc:
|
47
|
+
def blank?
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class FalseClass #:nodoc:
|
53
|
+
def blank?
|
54
|
+
true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class TrueClass #:nodoc:
|
59
|
+
def blank?
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Array #:nodoc:
|
65
|
+
alias_method :blank?, :empty?
|
66
|
+
end
|
67
|
+
|
68
|
+
class Hash #:nodoc:
|
69
|
+
alias_method :blank?, :empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
class String #:nodoc:
|
73
|
+
def blank?
|
74
|
+
self !~ /\S/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Numeric #:nodoc:
|
79
|
+
def blank?
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copied from Rails ActiveSupport 2.3.18:
|
2
|
+
# activesupport-2.3.18/lib/active_support/core_ext/try.rb
|
3
|
+
#
|
4
|
+
# == License
|
5
|
+
# Active Support is released under the MIT license.
|
6
|
+
#
|
7
|
+
class Object
|
8
|
+
# Invokes the method identified by the symbol +method+, passing it any arguments
|
9
|
+
# and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
|
10
|
+
#
|
11
|
+
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
|
12
|
+
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
|
13
|
+
#
|
14
|
+
# ==== Examples
|
15
|
+
#
|
16
|
+
# Without try
|
17
|
+
# @person && @person.name
|
18
|
+
# or
|
19
|
+
# @person ? @person.name : nil
|
20
|
+
#
|
21
|
+
# With try
|
22
|
+
# @person.try(:name)
|
23
|
+
#
|
24
|
+
# +try+ also accepts arguments and/or a block, for the method it is trying
|
25
|
+
# Person.try(:find, 1)
|
26
|
+
# @people.try(:collect) {|p| p.name}
|
27
|
+
#--
|
28
|
+
# This method definition below is for rdoc purposes only. The alias_method call
|
29
|
+
# below overrides it as an optimization since +try+ behaves like +Object#send+,
|
30
|
+
# unless called on +NilClass+.
|
31
|
+
def try(method, *args, &block)
|
32
|
+
send(method, *args, &block)
|
33
|
+
end
|
34
|
+
remove_method :try
|
35
|
+
alias_method :try, :__send__
|
36
|
+
end
|
37
|
+
|
38
|
+
class NilClass
|
39
|
+
def try(*args)
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "vagrant"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Skytap
|
5
|
+
module Errors
|
6
|
+
class VagrantSkytapError < Vagrant::Errors::VagrantError
|
7
|
+
error_namespace("vagrant_skytap.errors")
|
8
|
+
end
|
9
|
+
|
10
|
+
class InstanceReadyTimeout < VagrantSkytapError
|
11
|
+
error_key(:instance_ready_timeout)
|
12
|
+
end
|
13
|
+
|
14
|
+
class RsyncError < VagrantSkytapError
|
15
|
+
error_key(:rsync_error)
|
16
|
+
end
|
17
|
+
|
18
|
+
class MkdirError < VagrantSkytapError
|
19
|
+
error_key(:mkdir_error)
|
20
|
+
end
|
21
|
+
|
22
|
+
class Unauthorized < VagrantSkytapError
|
23
|
+
error_key(:unauthorized)
|
24
|
+
end
|
25
|
+
|
26
|
+
class DoesNotExist < VagrantSkytapError
|
27
|
+
error_key(:does_not_exist)
|
28
|
+
end
|
29
|
+
|
30
|
+
class ResourceBusy < VagrantSkytapError
|
31
|
+
error_key(:resource_busy)
|
32
|
+
end
|
33
|
+
|
34
|
+
class RateLimited < VagrantSkytapError
|
35
|
+
error_key(:rate_limited)
|
36
|
+
end
|
37
|
+
|
38
|
+
class UnprocessableEntity < VagrantSkytapError
|
39
|
+
error_key(:unprocessable_entity)
|
40
|
+
end
|
41
|
+
|
42
|
+
class OperationFailed < VagrantSkytapError
|
43
|
+
error_key(:operation_failed)
|
44
|
+
end
|
45
|
+
|
46
|
+
class VpnConnectionFailed < VagrantSkytapError
|
47
|
+
error_key(:vpn_connection_failed)
|
48
|
+
end
|
49
|
+
|
50
|
+
class SourceVmNotStopped < VagrantSkytapError
|
51
|
+
error_key(:source_vm_not_stopped)
|
52
|
+
end
|
53
|
+
|
54
|
+
class NoConnectionOptions < VagrantSkytapError
|
55
|
+
error_key(:no_connection_options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
begin
|
2
|
+
require "vagrant"
|
3
|
+
rescue LoadError
|
4
|
+
raise "The Vagrant Skytap 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 Skytap plugin is only compatible with Vagrant 1.2+"
|
11
|
+
end
|
12
|
+
|
13
|
+
module VagrantPlugins
|
14
|
+
module Skytap
|
15
|
+
class Plugin < Vagrant.plugin("2")
|
16
|
+
name "Skytap"
|
17
|
+
description <<-DESC
|
18
|
+
This plugin installs a provider that allows Vagrant to manage
|
19
|
+
machines in Skytap (EC2/VPC).
|
20
|
+
DESC
|
21
|
+
|
22
|
+
config(:skytap, :provider) do
|
23
|
+
require_relative "config"
|
24
|
+
Config
|
25
|
+
end
|
26
|
+
|
27
|
+
provider(:skytap, parallel: false) 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", Skytap.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_skytap")
|
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,42 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Skytap
|
5
|
+
class Properties
|
6
|
+
attr_reader :properties
|
7
|
+
attr_reader :data_dir
|
8
|
+
|
9
|
+
def self.filename
|
10
|
+
raise NotImplementedError.new('Must override')
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.read(data_dir)
|
14
|
+
file = data_dir.join(filename)
|
15
|
+
if file.exist?
|
16
|
+
YAML.load_file(file)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.write(data_dir, properties={})
|
21
|
+
existing_props = read(data_dir) || {}
|
22
|
+
props = existing_props.merge(properties)
|
23
|
+
|
24
|
+
IO.write(data_dir.join(filename),
|
25
|
+
YAML.dump(props))
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(data_dir)
|
29
|
+
@properties = self.class.read(data_dir)
|
30
|
+
@data_dir = data_dir
|
31
|
+
end
|
32
|
+
|
33
|
+
def read
|
34
|
+
self.class.read(@data_dir)
|
35
|
+
end
|
36
|
+
|
37
|
+
def write(properties={})
|
38
|
+
self.class.write(@data_dir, properties)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require "vagrant"
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module Skytap
|
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_skytap.states.short_#{state_id}")
|
38
|
+
long = I18n.t("vagrant_skytap.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
|
+
"Skytap (#{id})"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
require 'yaml'
|
3
|
+
require 'vagrant-skytap/vm_properties'
|
4
|
+
require 'vagrant-skytap/api/vpn'
|
5
|
+
require 'net/ssh/transport/session'
|
6
|
+
|
7
|
+
module VagrantPlugins
|
8
|
+
module Skytap
|
9
|
+
class SetupHelper
|
10
|
+
attr_reader :env, :environment, :machine, :provider_config
|
11
|
+
attr_reader :username, :password, :host, :port
|
12
|
+
|
13
|
+
def self.run!(env, environment)
|
14
|
+
new(env, environment).run!
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(env, environment)
|
18
|
+
@env = env
|
19
|
+
@logger = Log4r::Logger.new("vagrant_skytap::setup_helper")
|
20
|
+
@environment = environment
|
21
|
+
@machine = env[:machine]
|
22
|
+
@provider_config = env[:machine].provider_config
|
23
|
+
@username = @machine.config.ssh.username
|
24
|
+
@password = @machine.config.ssh.password
|
25
|
+
@host = @machine.config.ssh.host
|
26
|
+
@port = @machine.config.ssh.port || Net::SSH::Transport::Session::DEFAULT_PORT
|
27
|
+
end
|
28
|
+
|
29
|
+
def current_vm
|
30
|
+
environment.current_vm
|
31
|
+
end
|
32
|
+
|
33
|
+
def run!
|
34
|
+
ask_routing
|
35
|
+
ask_credentials
|
36
|
+
write_properties
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
|
43
|
+
def ask_credentials
|
44
|
+
@logger.debug("ask_credentials")
|
45
|
+
return if username && password
|
46
|
+
|
47
|
+
env[:ui].info("Note that the machine password will be stored in " \
|
48
|
+
"cleartext on your local filesystem.")
|
49
|
+
|
50
|
+
creds = current_vm.credentials.select(&:recognized?)
|
51
|
+
|
52
|
+
if username
|
53
|
+
env[:ui].info("SSH username found in Vagrantfile: #{username}")
|
54
|
+
match = creds.detect{|c| c.username == username}
|
55
|
+
|
56
|
+
if match
|
57
|
+
@logger.info("Found username in Vagrantfile. Using matching password from credentials.")
|
58
|
+
env[:ui].info("Matched SSH password in Skytap VM credentials.")
|
59
|
+
@password = match.password
|
60
|
+
else
|
61
|
+
@logger.info("Found username in Vagrantfile. Will use manual password entry.")
|
62
|
+
end
|
63
|
+
elsif creds.present?
|
64
|
+
question = "How do you want to choose SSH credentials for machine '#{@machine.name}'?"
|
65
|
+
choices = creds.collect do |c|
|
66
|
+
"Use VM credentials stored in Skytap: #{c}"
|
67
|
+
end
|
68
|
+
choices << 'Type credentials manually'
|
69
|
+
|
70
|
+
ask_from_list(question, choices, 0) do |i|
|
71
|
+
if cred = creds[i]
|
72
|
+
@username = cred.username
|
73
|
+
@password = cred.password
|
74
|
+
end
|
75
|
+
end
|
76
|
+
else
|
77
|
+
@logger.info("No login credentials found for the VM. Prompting for manual username/password entry.")
|
78
|
+
end
|
79
|
+
|
80
|
+
@username ||= ask_username
|
81
|
+
@password ||= ask_password
|
82
|
+
end
|
83
|
+
|
84
|
+
def ask_username
|
85
|
+
@username = ask('Enter SSH username:').strip
|
86
|
+
end
|
87
|
+
|
88
|
+
def ask_password
|
89
|
+
@password = ask('Enter SSH password (no output will appear):', echo: false).strip
|
90
|
+
end
|
91
|
+
|
92
|
+
def ask_routing
|
93
|
+
@logger.debug("ask_routing")
|
94
|
+
return if host && port
|
95
|
+
|
96
|
+
iface = current_vm.interfaces.first
|
97
|
+
choices = connection_choices(iface).select(&:valid?)
|
98
|
+
raise Errors::NoConnectionOptions unless choices.present?
|
99
|
+
|
100
|
+
question = "How do you want to connect to machine '#{@machine.name}'?"
|
101
|
+
ask_from_list(question, choices, 0) do |i, choice|
|
102
|
+
@host, @port = choice.choose
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def connection_choices(iface)
|
107
|
+
vpn_choices(iface)
|
108
|
+
end
|
109
|
+
|
110
|
+
def vpn_choices(iface)
|
111
|
+
candidates = vpns.select do |vpn|
|
112
|
+
vpn.nat_enabled? || vpn.subsumes?(iface.network)
|
113
|
+
end
|
114
|
+
|
115
|
+
candidates.collect {|vpn| vpn.choice_for_setup(iface.vm) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def public_ip_choices(iface)
|
119
|
+
ips = iface.public_ips + iface.available_ips
|
120
|
+
ips.collect {|ip| ip.choice_for_setup(iface) }
|
121
|
+
end
|
122
|
+
|
123
|
+
def published_service_choices(iface)
|
124
|
+
iface.published_service_choices
|
125
|
+
end
|
126
|
+
|
127
|
+
def vpns
|
128
|
+
VagrantPlugins::Skytap::API::Vpn.all(env, query: {region: environment.region})
|
129
|
+
end
|
130
|
+
|
131
|
+
# Given a message and an array of choices, displays them to the user as a
|
132
|
+
# numbered list and asks the user to choose one. Returns the index that
|
133
|
+
# the user chose.
|
134
|
+
#
|
135
|
+
# The choices are presented to the user starting at 1, but the return
|
136
|
+
# value is a 0-based Ruby index. If specified, +default_index+ should be
|
137
|
+
# 0-based.
|
138
|
+
#
|
139
|
+
# If a block is given, yields chosen index.
|
140
|
+
def ask_from_list(message, choices, default_index=nil, &block)
|
141
|
+
if default_index && (default_index < 0 || default_index >= choices.size)
|
142
|
+
raise ArgumentError.new("Bad value for default #{default_index.inspect}")
|
143
|
+
end
|
144
|
+
|
145
|
+
numbered_choices = choices.each_with_index.collect do |choice, i|
|
146
|
+
"#{i+1}. #{choice}"
|
147
|
+
end.join("\n")
|
148
|
+
|
149
|
+
default_choice = " [#{default_index+1}]" if default_index
|
150
|
+
prompt = "Enter choice number:#{default_choice} "
|
151
|
+
|
152
|
+
index = nil
|
153
|
+
|
154
|
+
until index && index >= 0 && index < choices.length
|
155
|
+
input = env[:ui].ask([message, numbered_choices, prompt].join("\n\n"),
|
156
|
+
prefix: false)
|
157
|
+
begin
|
158
|
+
if default_index && input.blank?
|
159
|
+
index = default_index
|
160
|
+
else
|
161
|
+
index = Integer(input, 10) - 1
|
162
|
+
end
|
163
|
+
rescue ArgumentError
|
164
|
+
# No-op
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
yield index, choices[index] if block_given?
|
169
|
+
|
170
|
+
index
|
171
|
+
end
|
172
|
+
|
173
|
+
def ask(message, options={})
|
174
|
+
prompt = message
|
175
|
+
if default = options[:default]
|
176
|
+
prompt = "#{message} [#{default}] "
|
177
|
+
else
|
178
|
+
prompt = "#{message} "
|
179
|
+
end
|
180
|
+
|
181
|
+
env[:ui].ask(prompt, {prefix: false}.merge(options))
|
182
|
+
end
|
183
|
+
|
184
|
+
def write_properties
|
185
|
+
VmProperties.write(env[:machine].data_dir,
|
186
|
+
'username' => username,
|
187
|
+
'password' => password,
|
188
|
+
'host' => host,
|
189
|
+
'port' => port)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|