derelict_m 0.6.2a

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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/.cane +2 -0
  3. data/.coveralls.yml +1 -0
  4. data/.gitignore +18 -0
  5. data/.travis.yml +16 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +99 -0
  9. data/Rakefile +22 -0
  10. data/derelict.gemspec +63 -0
  11. data/lib/derelict.rb +74 -0
  12. data/lib/derelict/box.rb +29 -0
  13. data/lib/derelict/box/manager.rb +111 -0
  14. data/lib/derelict/box/not_found.rb +16 -0
  15. data/lib/derelict/connection.rb +84 -0
  16. data/lib/derelict/connection/invalid.rb +14 -0
  17. data/lib/derelict/connection/not_found.rb +13 -0
  18. data/lib/derelict/exception.rb +6 -0
  19. data/lib/derelict/exception/optional_reason.rb +32 -0
  20. data/lib/derelict/executer.rb +237 -0
  21. data/lib/derelict/instance.rb +147 -0
  22. data/lib/derelict/instance/command_failed.rb +30 -0
  23. data/lib/derelict/instance/invalid.rb +14 -0
  24. data/lib/derelict/instance/missing_binary.rb +13 -0
  25. data/lib/derelict/instance/non_directory.rb +13 -0
  26. data/lib/derelict/instance/not_found.rb +13 -0
  27. data/lib/derelict/parser.rb +27 -0
  28. data/lib/derelict/parser/box_list.rb +53 -0
  29. data/lib/derelict/parser/box_list/invalid_format.rb +16 -0
  30. data/lib/derelict/parser/plugin_list.rb +63 -0
  31. data/lib/derelict/parser/plugin_list/invalid_format.rb +16 -0
  32. data/lib/derelict/parser/plugin_list/needs_reinstall.rb +22 -0
  33. data/lib/derelict/parser/status.rb +90 -0
  34. data/lib/derelict/parser/status/invalid_format.rb +16 -0
  35. data/lib/derelict/parser/version.rb +28 -0
  36. data/lib/derelict/parser/version/invalid_format.rb +16 -0
  37. data/lib/derelict/plugin.rb +29 -0
  38. data/lib/derelict/plugin/manager.rb +107 -0
  39. data/lib/derelict/plugin/not_found.rb +14 -0
  40. data/lib/derelict/utils.rb +11 -0
  41. data/lib/derelict/utils/logger.rb +59 -0
  42. data/lib/derelict/utils/logger/array_outputter.rb +43 -0
  43. data/lib/derelict/utils/logger/invalid_type.rb +15 -0
  44. data/lib/derelict/utils/logger/raw_formatter.rb +12 -0
  45. data/lib/derelict/version.rb +3 -0
  46. data/lib/derelict/virtual_machine.rb +190 -0
  47. data/lib/derelict/virtual_machine/invalid.rb +14 -0
  48. data/lib/derelict/virtual_machine/not_found.rb +18 -0
  49. data/spec/coverage_helper.rb +19 -0
  50. data/spec/derelict/box/manager_spec.rb +171 -0
  51. data/spec/derelict/box/not_found_spec.rb +13 -0
  52. data/spec/derelict/box_spec.rb +37 -0
  53. data/spec/derelict/connection/invalid_spec.rb +16 -0
  54. data/spec/derelict/connection/not_found_spec.rb +13 -0
  55. data/spec/derelict/connection_spec.rb +107 -0
  56. data/spec/derelict/exception/optional_reason_spec.rb +41 -0
  57. data/spec/derelict/exception_spec.rb +11 -0
  58. data/spec/derelict/executer_spec.rb +129 -0
  59. data/spec/derelict/instance/command_failed_spec.rb +40 -0
  60. data/spec/derelict/instance/invalid_spec.rb +16 -0
  61. data/spec/derelict/instance/missing_binary_spec.rb +13 -0
  62. data/spec/derelict/instance/non_directory_spec.rb +13 -0
  63. data/spec/derelict/instance/not_found_spec.rb +13 -0
  64. data/spec/derelict/instance_spec.rb +258 -0
  65. data/spec/derelict/parser/box_list/invalid_format_spec.rb +16 -0
  66. data/spec/derelict/parser/box_list_spec.rb +64 -0
  67. data/spec/derelict/parser/plugin_list/invalid_format_spec.rb +16 -0
  68. data/spec/derelict/parser/plugin_list/needs_reinstall_spec.rb +13 -0
  69. data/spec/derelict/parser/plugin_list_spec.rb +82 -0
  70. data/spec/derelict/parser/status/invalid_format_spec.rb +16 -0
  71. data/spec/derelict/parser/status_spec.rb +214 -0
  72. data/spec/derelict/parser/version/invalid_format_spec.rb +16 -0
  73. data/spec/derelict/parser/version_spec.rb +42 -0
  74. data/spec/derelict/parser_spec.rb +24 -0
  75. data/spec/derelict/plugin/manager_spec.rb +208 -0
  76. data/spec/derelict/plugin/not_found_spec.rb +13 -0
  77. data/spec/derelict/plugin_spec.rb +37 -0
  78. data/spec/derelict/utils/logger/array_outputter_spec.rb +40 -0
  79. data/spec/derelict/utils/logger/invalid_type_spec.rb +13 -0
  80. data/spec/derelict/utils/logger/raw_formatter_spec.rb +17 -0
  81. data/spec/derelict/utils/logger_spec.rb +35 -0
  82. data/spec/derelict/virtual_machine/invalid_spec.rb +16 -0
  83. data/spec/derelict/virtual_machine/not_found_spec.rb +34 -0
  84. data/spec/derelict/virtual_machine_spec.rb +356 -0
  85. data/spec/derelict_spec.rb +50 -0
  86. data/spec/spec_helper.rb +32 -0
  87. data/spec/support/log_context.rb +36 -0
  88. metadata +332 -0
@@ -0,0 +1,29 @@
1
+ module Derelict
2
+ # Represents an individual Vagrant plugin at a particular version
3
+ class Plugin
4
+ autoload :Manager, "derelict/plugin/manager"
5
+ autoload :NotFound, "derelict/plugin/not_found"
6
+
7
+ attr_reader :name, :version
8
+
9
+ # Initializes a plugin with a particular name and version
10
+ #
11
+ # * name: The name of the plugin represented by this object
12
+ # * version: The version of the plugin represented by this object
13
+ def initialize(name, version)
14
+ @name = name
15
+ @version = version
16
+ end
17
+
18
+ # Ensure equivalent Plugins are equal to this one
19
+ def ==(other)
20
+ other.name == name and other.version == version
21
+ end
22
+ alias_method :eql?, :==
23
+
24
+ # Make equivalent Plugins hash to the same value
25
+ def hash
26
+ name.hash ^ version.hash
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,107 @@
1
+ module Derelict
2
+ class Plugin
3
+ # A class that handles managing plugins for a Vagrant instance
4
+ class Manager
5
+ # Include "memoize" class method to memoize methods
6
+ extend Memoist
7
+
8
+ # Include "logger" method to get a logger for this class
9
+ include Utils::Logger
10
+
11
+ attr_reader :instance
12
+
13
+ # Initializes a Manager for use with a particular instance
14
+ #
15
+ # * instance: The Derelict::Instance which will have its
16
+ # plugins managed by this Manager
17
+ def initialize(instance)
18
+ @instance = instance
19
+ logger.debug "Successfully initialized #{description}"
20
+ end
21
+
22
+ # Retrieves the Set of currently installed plugins
23
+ def list
24
+ logger.info "Retrieving Vagrant plugin list for #{description}"
25
+ output = instance.execute!(:plugin, "list").stdout
26
+ Derelict::Parser::PluginList.new(output).plugins
27
+ end
28
+ memoize :list
29
+
30
+ # Determines whether a particular plugin is installed
31
+ #
32
+ # * plugin_name: Name of the plugin to look for (as a string)
33
+ def installed?(plugin_name, version = nil)
34
+ fetch(plugin_name).version == version or version.nil?
35
+ rescue Plugin::NotFound
36
+ false
37
+ end
38
+
39
+ # Installs a plugin (optionally a particular version)
40
+ #
41
+ # If no version is specified, the latest stable version is used
42
+ # by Vagrant.
43
+ #
44
+ # * plugin_name: Name of the plugin to install (as a string)
45
+ # * options: Hash of options, valid keys:
46
+ # * version: Particular version to install (optional,
47
+ # latest version will be installed if omitted)
48
+ # * log: Whether to log the output (optional, defaults
49
+ # to false)
50
+ def install(plugin_name, options = {})
51
+ options = {:log => false, :version => nil}.merge(options)
52
+ logger.info "Installing plugin '#{plugin_name}' using #{description}"
53
+
54
+ version = options[:version]
55
+ command = [:plugin, "install", plugin_name]
56
+ command.concat ["--plugin-version", version] unless version.nil?
57
+
58
+ log_block = options[:log] ? shell_log_block : nil
59
+ instance.execute!(*command, &log_block).tap do
60
+ flush_cache # flush memoized method return values
61
+ end
62
+ end
63
+
64
+ # Uninstalls a particular Vagrant plugin
65
+ #
66
+ # * plugin_name: Name of the plugin to uninstall (as a string)
67
+ def uninstall(plugin_name, options = {})
68
+ options = {:log => false}.merge(options)
69
+ logger.info "Uninstalling plugin '#{plugin_name}' using #{description}"
70
+
71
+ log_block = options[:log] ? shell_log_block : nil
72
+ instance.execute!(:plugin, "uninstall", plugin_name, &log_block).tap do
73
+ flush_cache # flush memoized method return values
74
+ end
75
+ end
76
+
77
+ # Updates a particular Vagrant plugin
78
+ #
79
+ # * plugin_name: Name of the plugin to update (as a string)
80
+ def update(plugin_name, options = {})
81
+ options = {:log => false}.merge(options)
82
+ logger.info "Updating plugin '#{plugin_name}' using #{description}"
83
+
84
+ log_block = options[:log] ? shell_log_block : nil
85
+ instance.execute!(:plugin, "update", plugin_name, &log_block).tap do
86
+ flush_cache # flush memoized method return values
87
+ end
88
+ end
89
+
90
+ # Retrieves a plugin with a particular name
91
+ #
92
+ # * plugin_name: Name of the plugin to look for (as a string)
93
+ def fetch(plugin_name)
94
+ list.find {|plugin| plugin.name == plugin_name}.tap do |plugin|
95
+ raise Plugin::NotFound.new plugin_name if plugin.nil?
96
+ end
97
+ end
98
+
99
+ # Provides a description of this Connection
100
+ #
101
+ # Mainly used for log messages.
102
+ def description
103
+ "Derelict::Plugin::Manager for #{instance.description}"
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,14 @@
1
+ module Derelict
2
+ class Plugin
3
+ # A plugin that isn't currently installed has been retrieved
4
+ class NotFound < Derelict::Exception
5
+ # Initializes a new instance of this exception, for a plugin name
6
+ #
7
+ # * plugin_name: The name of the plugin that this exception
8
+ # relates to
9
+ def initialize(plugin_name)
10
+ super "Plugin '#{plugin_name}' is not currently installed"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Derelict
2
+ # A namespaced collection of utilities for general purpose use
3
+ #
4
+ # Derelict::Utils contains all the individual sub-modules inside it.
5
+ module Utils
6
+ autoload :Logger, "derelict/utils/logger"
7
+
8
+ # Include sub-modules here
9
+ include Derelict::Utils::Logger
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module Derelict
2
+ module Utils
3
+ # Provides a method to retrieve a logger
4
+ module Logger
5
+ autoload :ArrayOutputter, "derelict/utils/logger/array_outputter"
6
+ autoload :InvalidType, "derelict/utils/logger/invalid_type"
7
+ autoload :RawFormatter, "derelict/utils/logger/raw_formatter"
8
+
9
+ # Retrieves the logger for this class
10
+ def logger(options = {})
11
+ options = {:type => :internal}.merge(options)
12
+
13
+ case options[:type].to_sym
14
+ when :external
15
+ external_logger
16
+ when :internal
17
+ find_or_create_logger(logger_name)
18
+ else raise InvalidType.new options[:type]
19
+ end
20
+ end
21
+
22
+ private
23
+ # A block that can be passed to #execute to log the output
24
+ def shell_log_block
25
+ Proc.new do |stdout, stderr|
26
+ # Only stdout or stderr is populated, the other will be nil
27
+ logger(:type => :external).info(stdout || stderr)
28
+ end
29
+ end
30
+
31
+ # Finds or creates a Logger with a particular fullname
32
+ def find_or_create_logger(fullname)
33
+ Log4r::Logger[fullname.to_s] || Log4r::Logger.new(fullname.to_s)
34
+ end
35
+
36
+ # Gets the "external" logger, used to print to stdout
37
+ def external_logger
38
+ @@external ||= find_or_create_logger("external").tap do |external|
39
+ logger.debug "Created external logger instance"
40
+ external.add(Log4r::Outputter.stdout.tap do |outputter|
41
+ outputter.formatter = RawFormatter.new
42
+ end)
43
+ end
44
+ end
45
+
46
+ # Retrieves the name of the logger for this class
47
+ #
48
+ # By default, the name of the logger is just the lowercase
49
+ # version of the class name.
50
+ def logger_name
51
+ if self.is_a? Module
52
+ self.name.downcase
53
+ else
54
+ self.class.name.downcase
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,43 @@
1
+ module Derelict
2
+ module Utils
3
+ module Logger
4
+ # A Log4r Outputter which stores all logs in an array
5
+ #
6
+ # Logs are stored in the internal array by #write. Logs can be
7
+ # cleared using #flush, which returns the flushed logs too.
8
+ class ArrayOutputter < Log4r::Outputter
9
+ # Include "memoize" class method to memoize methods
10
+ extend Memoist
11
+
12
+ # Force the outputter to receive and store all levels of messages
13
+ def level
14
+ Log4r::ALL
15
+ end
16
+
17
+ # The internal array of messages
18
+ def messages
19
+ []
20
+ end
21
+ memoize :messages
22
+
23
+ # Clear internal log messages array and return the erased data
24
+ def flush
25
+ messages.dup.tap { messages.clear }
26
+ end
27
+
28
+ private
29
+
30
+ # Write a message to the internal array
31
+ #
32
+ # This is an abstract method in the parent class, and handles
33
+ # persisting the log data. In this class, it saves the message
34
+ # into an internal array to be retrieved later.
35
+ #
36
+ # * message: The log message to be persisted
37
+ def write(message)
38
+ messages << message
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ module Derelict
2
+ module Utils
3
+ module Logger
4
+ # The "type" option when requesting the logger was invalid
5
+ class InvalidType < ::Derelict::Exception
6
+ # Initializes a new instance of this exception for a type
7
+ #
8
+ # * type: The (invalid) requested type
9
+ def initialize(type)
10
+ super "Invalid logger type '#{type}'"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module Derelict
2
+ module Utils
3
+ module Logger
4
+ # A Formatter that passes the log message through untouched
5
+ class RawFormatter < Log4r::Formatter
6
+ def format(event)
7
+ event.data
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module Derelict
2
+ VERSION = "0.6.2a"
3
+ end
@@ -0,0 +1,190 @@
1
+ module Derelict
2
+ # A Vagrant virtual machine in a particular Derelict connection
3
+ class VirtualMachine
4
+ autoload :Invalid, "derelict/virtual_machine/invalid"
5
+ autoload :NotFound, "derelict/virtual_machine/not_found"
6
+
7
+ # Include "memoize" class method to memoize methods
8
+ extend Memoist
9
+
10
+ # Include "logger" method to get a logger for this class
11
+ include Utils::Logger
12
+
13
+ COMMANDS = [
14
+ :up,
15
+ :halt,
16
+ :destroy,
17
+ :reload,
18
+ :suspend,
19
+ :resume,
20
+ ]
21
+
22
+ attr_reader :connection
23
+ attr_reader :name
24
+
25
+ # Initializes a new VirtualMachine for a connection and name
26
+ #
27
+ # * connection: The +Derelict::Connection+ to use to manipulate
28
+ # the VirtualMachine instance
29
+ # * name: The name of the virtual machine, used when
30
+ # communicating with the connection)
31
+ def initialize(connection, name)
32
+ @connection = connection
33
+ @name = name
34
+ logger.debug "Successfully initialized #{description}"
35
+ end
36
+
37
+ # Validates the data used for this connection
38
+ #
39
+ # Raises exceptions on failure:
40
+ #
41
+ # * +Derelict::VirtualMachine::NotFound+ if the connection
42
+ # doesn't know about a virtual machine with the requested
43
+ # name
44
+ def validate!
45
+ logger.debug "Starting validation for #{description}"
46
+ raise NotFound.new name, connection unless exists?
47
+ logger.info "Successfully validated #{description}"
48
+ self
49
+ end
50
+
51
+ # Determines whether this Vagrant virtual machine exists
52
+ #
53
+ # Returns +true+ if the connection reports a virtual machine with
54
+ # the requested name, otherwise returns +false+.
55
+ def exists?
56
+ status.exists? name
57
+ end
58
+ memoize :exists?
59
+
60
+ # Gets the current state of this Vagrant virtual machine
61
+ #
62
+ # The state is returned as a symbol, e.g. :running.
63
+ def state
64
+ status.state name
65
+ end
66
+ memoize :state
67
+
68
+ # Determines whether this virtual machine is currently running
69
+ def running?
70
+ (state == :running)
71
+ end
72
+ memoize :running?
73
+
74
+ # Add methods for each command
75
+ #
76
+ # A method is defined for each of the symbols in COMMANDS. The
77
+ # method name will be the symbol with an added bang (!). For
78
+ # example, #up!, #halt!, etc.
79
+ #
80
+ # Each method takes an options hash as an argument. For example:
81
+ #
82
+ # vm.up! :log => true
83
+ #
84
+ # This example will run the "up" command with logging enabled. The
85
+ # option keys can optionally include any of the following symbols:
86
+ #
87
+ # * log: Should the log output be printed? (defaults to false)
88
+ COMMANDS.each do |command|
89
+ define_method :"#{command}!" do |*opts|
90
+ # Ideally this block would have one argument with a default
91
+ # value of an empty Hash. Unfortunately, setting a default
92
+ # value for the arguments to a block is only supported in Ruby
93
+ # 1.9+. The splatted arguments thing is a way to allow zero or
94
+ # one argument, but it actually allows any number of arguments.
95
+ # So we need to emulate the error Ruby would throw if you give
96
+ # the wrong number of arguments.
97
+ if opts.length > 1
98
+ message = "wrong number of arguments (#{opts.length} for 0-1)"
99
+ raise ArgumentError.new message
100
+ end
101
+
102
+ # Set defaults for the opts hash
103
+ opts = {:color => false, :log => false}.merge(opts.first || {})
104
+
105
+ # Log message if there's one for this command
106
+ log_message_for(command).tap {|m| logger.info m unless m.nil? }
107
+
108
+ # Execute the command!
109
+ execute! command, opts
110
+ end
111
+ end
112
+
113
+ # Retrieves the (parsed) status from the connection
114
+ def status
115
+ logger.info "Retrieving Vagrant status for #{description}"
116
+ output = connection.execute!(:status).stdout
117
+ Derelict::Parser::Status.new(output)
118
+ end
119
+ memoize :status
120
+
121
+ # Provides a description of this Connection
122
+ #
123
+ # Mainly used for log messages.
124
+ def description
125
+ "Derelict::VirtualMachine '#{name}' from #{connection.description}"
126
+ end
127
+
128
+ private
129
+ # Executes a command on the connection for this VM
130
+ #
131
+ # * command: The command to execute (as a symbol)
132
+ # * options: A Hash of options, with the following optional keys:
133
+ # * log: Logs the output of the command if true
134
+ # (defaults to false)
135
+ # * log_mode: Controls how commands are logged (one of
136
+ # either :chars or :lines, defaults to :lines)
137
+ # * color: Uses color in the log output (defaults to
138
+ # false, only relevant if log is true)
139
+ # * provider: The Vagrant provider to use, one of
140
+ # "virtualbox" or "vmware_fusion" (defaults to
141
+ # "virtualbox")
142
+ def execute!(command, options)
143
+ # Build up the arguments to pass to connection.execute!
144
+ arguments = [command, name, *arguments_for(command)]
145
+ arguments << "--color" if options[:color]
146
+ if options[:provider]
147
+ arguments << "--provider"
148
+ arguments << options[:provider]
149
+ end
150
+
151
+ if options[:log_mode]
152
+ arguments << {:mode => options[:log_mode]}
153
+ end
154
+
155
+ # Set up the block to use when executing -- if logging is
156
+ # enabled, use a block that logs the output; otherwise no block.
157
+ block = options[:log] ? shell_log_block : nil
158
+
159
+ # Execute the command
160
+ connection.execute! *arguments, &block
161
+ end
162
+
163
+ # Retrieves the arguments for a particular action
164
+ #
165
+ # * action: The symbol representing the action (one of :up,
166
+ # :halt, :destroy, :reload, :suspend, :resume)
167
+ def arguments_for(action)
168
+ case action
169
+ when :destroy then ['--force']
170
+ else []
171
+ end
172
+ end
173
+
174
+ # Retrieves the correct log message for a particular action
175
+ #
176
+ # * action: The symbol representing the action (one of :up,
177
+ # :halt, :destroy, :reload, :suspend, :resume)
178
+ def log_message_for(action)
179
+ case action
180
+ when :up then "Bringing up #{description}"
181
+ when :halt then "Halting #{description}"
182
+ when :destroy then "Destroying #{description}"
183
+ when :reload then "Reloading #{description}"
184
+ when :suspend then "Suspending #{description}"
185
+ when :resume then "Resuming #{description}"
186
+ else nil
187
+ end
188
+ end
189
+ end
190
+ end