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,30 @@
1
+ module Derelict
2
+ class Instance
3
+ # Represents an invalid instance, which can't be used with Derelict
4
+ class CommandFailed < Derelict::Exception
5
+ # Initializes a new instance of this exception, for a command
6
+ #
7
+ # * command: The name of the command which failed (optional,
8
+ # provides extra detail in the message)
9
+ # * result: The result (Derelict::Executer) for the command
10
+ # which failed (optional, provides extra detail in
11
+ # the message)
12
+ def initialize(command = nil, result = nil)
13
+ super [default_message, describe(command, result)].join
14
+ end
15
+
16
+ private
17
+ # Retrieves the default error message
18
+ def default_message
19
+ "Error executing Vagrant command"
20
+ end
21
+
22
+ def describe(command = nil, result = nil)
23
+ [
24
+ command.nil? ? "" : " '#{command}'",
25
+ result.nil? ? "" : ", STDERR output:\n#{result.stderr}",
26
+ ].join
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ module Derelict
2
+ class Instance
3
+ # Represents an invalid instance, which can't be used with Derelict
4
+ class Invalid < Derelict::Exception
5
+ include Derelict::Exception::OptionalReason
6
+
7
+ private
8
+ # Retrieves the default error message
9
+ def default_message
10
+ "Invalid Derelict instance"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Derelict
2
+ class Instance
3
+ # The "vagrant" binary was missing from the instance
4
+ class MissingBinary < Invalid
5
+ # Initializes a new instance of this exception for a given file
6
+ #
7
+ # * file: The expected location of the binary
8
+ def initialize(file)
9
+ super "'vagrant' binary not found at #{file}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Derelict
2
+ class Instance
3
+ # The path used for the instance was a file, not a directory
4
+ class NonDirectory < Invalid
5
+ # Initializes a new instance of this exception for a given path
6
+ #
7
+ # * path: The requested path of the instance
8
+ def initialize(path)
9
+ super "expected directory, found file: #{path}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Derelict
2
+ class Instance
3
+ # The path used for the instance was not found
4
+ class NotFound < Invalid
5
+ # Initializes a new instance of this exception for a given path
6
+ #
7
+ # * path: The requested path of the instance
8
+ def initialize(path)
9
+ super "directory doesn't exist: #{path}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ module Derelict
2
+ # Base class for parsers, which extract data from command output
3
+ class Parser
4
+ autoload :BoxList, "derelict/parser/box_list"
5
+ autoload :PluginList, "derelict/parser/plugin_list"
6
+ autoload :Status, "derelict/parser/status"
7
+ autoload :Version, "derelict/parser/version"
8
+
9
+ # Include "logger" method to get a logger for this class
10
+ include Utils::Logger
11
+
12
+ attr_reader :output
13
+
14
+ # Initializes the parser with the output it will be parsing
15
+ def initialize(output)
16
+ @output = output
17
+ logger.debug "Successfully initialized #{description}"
18
+ end
19
+
20
+ # Provides a description of this Parser
21
+ #
22
+ # Mainly used for log messages.
23
+ def description
24
+ "Derelict::Parser (unknown type)"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ module Derelict
2
+ # Parses the output of "vagrant box list"
3
+ class Parser::BoxList < Parser
4
+ autoload :InvalidFormat, "derelict/parser/box_list/invalid_format"
5
+
6
+ # Include "memoize" class method to memoize methods
7
+ extend Memoist
8
+
9
+ # Output from "vagrant box list" if there are no boxes installed
10
+ NO_BOXES = <<-END.gsub(/^ {6}/, '')
11
+ There are no installed boxes! Use `vagrant box add` to add some.
12
+ END
13
+
14
+ # Regexp to parse a box line into a box name and provider
15
+ #
16
+ # Capture groups:
17
+ #
18
+ # 1. Box name, as listed in the output
19
+ # 2. Name of the provider for that box
20
+ PARSE_BOX = %r[
21
+ ^(.*?) # Box name starts at the start of the line
22
+ \ +\( # Provider is separated by spaces and open-parenthesis
23
+ (\w+) # Provider name
24
+ (:?,.+)? # Version, ignored
25
+ \)$ # Ends with close-parenthesis and end-of-line
26
+ ]x # Ignore whitespace to allow these comments
27
+
28
+ # Retrieves a Set containing all the boxes from the output
29
+ def boxes
30
+ box_lines.map {|l| parse_line l.match(PARSE_BOX) }.to_set
31
+ end
32
+
33
+ # Provides a description of this Parser
34
+ #
35
+ # Mainly used for log messages.
36
+ def description
37
+ "Derelict::Parser::BoxList instance"
38
+ end
39
+
40
+ private
41
+ # Retrieves an array of the box lines in the output
42
+ def box_lines
43
+ return [] if output.match NO_BOXES
44
+ output.lines
45
+ end
46
+
47
+ # Parses a single line of the output into a Box object
48
+ def parse_line(match)
49
+ raise InvalidFormat.new "Couldn't parse box list" if match.nil?
50
+ Derelict::Box.new *match.captures[0..1]
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,16 @@
1
+ module Derelict
2
+ class Parser
3
+ class BoxList
4
+ # The box list wasn't in the expected format, can't be parsed
5
+ class InvalidFormat < Derelict::Exception
6
+ include Derelict::Exception::OptionalReason
7
+
8
+ private
9
+ # Retrieves the default error message
10
+ def default_message
11
+ "Output from 'vagrant box list' was in an unexpected format"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,63 @@
1
+ module Derelict
2
+ # Parses the output of "vagrant plugin list"
3
+ class Parser::PluginList < Parser
4
+ autoload :InvalidFormat, "derelict/parser/plugin_list/invalid_format"
5
+ autoload :NeedsReinstall, "derelict/parser/plugin_list/needs_reinstall"
6
+
7
+ # Include "memoize" class method to memoize methods
8
+ extend Memoist
9
+
10
+ # Regexp to parse a plugin line into a plugin name and version
11
+ #
12
+ # Capture groups:
13
+ #
14
+ # 1. Plugin name, as listed in the output
15
+ # 2. Currently installed version (without surrounding brackets)
16
+ PARSE_PLUGIN = %r[
17
+ ^([A-Za-z0-9\-_.]+) # Plugin name starts at the start of the line.
18
+ \ # Version is separated by a space character,
19
+ \( # surrounded by brackets,
20
+ ([0-9\-_.]+) # consists of numbers, hyphens, underscore, dot,
21
+ (, .*)? # optionally followed by a comma space and word
22
+ \)
23
+ $ # at the end of the line.
24
+ ]x # Ignore whitespace to allow these comments.
25
+
26
+ # Regexp to determine whether plugins need to be reinstalled
27
+ NEEDS_REINSTALL = %r[
28
+ ^The\splugins\sbelow\swill\snot\sbe\sloaded\suntil\sthey're\s
29
+ uninstalled\sand\sreinstalled:$
30
+ ]x
31
+
32
+ # Retrieves a Set containing all the plugins from the output
33
+ def plugins
34
+ raise NeedsReinstall, output if needs_reinstall?
35
+ plugin_lines.map {|l| parse_line l.match(PARSE_PLUGIN) }.to_set
36
+ end
37
+
38
+ # Determines if old plugins need to be reinstalled
39
+ def needs_reinstall?
40
+ output =~ NEEDS_REINSTALL
41
+ end
42
+
43
+ # Provides a description of this Parser
44
+ #
45
+ # Mainly used for log messages.
46
+ def description
47
+ "Derelict::Parser::PluginList instance"
48
+ end
49
+
50
+ private
51
+ # Retrieves an array of the plugin lines in the output
52
+ def plugin_lines
53
+ return [] if output.match /no plugins installed/i
54
+ output.lines.grep(/^\w/)
55
+ end
56
+
57
+ # Parses a single line of the output into a Plugin object
58
+ def parse_line(match)
59
+ raise InvalidFormat.new "Couldn't parse plugin" if match.nil?
60
+ Derelict::Plugin.new *match.captures[0..1]
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,16 @@
1
+ module Derelict
2
+ class Parser
3
+ class PluginList
4
+ # The plugin list wasn't in the expected format, can't be parsed
5
+ class InvalidFormat < Derelict::Exception
6
+ include Derelict::Exception::OptionalReason
7
+
8
+ private
9
+ # Retrieves the default error message
10
+ def default_message
11
+ "Output from 'vagrant plugin list' was in an unexpected format"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module Derelict
2
+ class Parser
3
+ class PluginList
4
+ # Vagrant plugins need to be uninstalled and re-installed
5
+ class NeedsReinstall < Derelict::Exception
6
+ # Retrieves the output from Vagrant
7
+ attr_reader :output
8
+
9
+ # Initializes a new instance, for a particular box name/provider
10
+ #
11
+ # * output: The output from Vagrant
12
+ def initialize(output)
13
+ @output = output
14
+ super <<-END.gsub(/\s+/, ' ').strip
15
+ Vagrant plugins installed before upgrading to version 1.4.x
16
+ need to be uninstalled and re-installed.
17
+ END
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,90 @@
1
+ module Derelict
2
+ # Parses the output of "vagrant status"
3
+ class Parser::Status < Parser
4
+ autoload :InvalidFormat, "derelict/parser/status/invalid_format"
5
+
6
+ # Include "memoize" class method to memoize methods
7
+ extend Memoist
8
+
9
+ # Regexp to extract the VM list from the "vagrant status" output
10
+ PARSE_LIST_FROM_OUTPUT = /Current machine states:\n\n((?:.*\n)+)\n/i
11
+
12
+ # Regexp to extract the state from a line in the VM list
13
+ PARSE_STATE_FROM_LIST_ITEM = %r[
14
+ ^(.*?) # VM name starts at the start of the line,
15
+ \s{2,} # to the first instance of 2 or more spaces.
16
+ (.*?) # VM state starts after the whitespace,
17
+ \s+\( # continuing until whitespace and open bracket.
18
+ (.*) # The provider name starts after the bracket,
19
+ \)$ # and ends at a closing bracket at line end.
20
+ ]x # Ignore whitespace to allow these comments
21
+
22
+ # Retrieves the names of all virtual machines in the output
23
+ #
24
+ # The names are returned as a Set of symbols.
25
+ def vm_names
26
+ Set[*states.keys]
27
+ end
28
+
29
+ # Determines if a particular virtual machine exists in the output
30
+ #
31
+ # * vm_name: The name of the virtual machine to look for
32
+ def exists?(vm_name = nil)
33
+ return (vm_names.count > 0) if vm_name.nil?
34
+ vm_names.include? vm_name.to_sym
35
+ end
36
+
37
+ # Determines the state of a particular virtual machine
38
+ #
39
+ # The state is returned as a symbol, e.g. :running.
40
+ #
41
+ # * vm_name: The name of the virtual machine to retrieve state
42
+ def state(vm_name)
43
+ unless states.include? vm_name.to_sym
44
+ raise Derelict::VirtualMachine::NotFound.new vm_name
45
+ end
46
+
47
+ states[vm_name.to_sym]
48
+ end
49
+
50
+ # Provides a description of this Parser
51
+ #
52
+ # Mainly used for log messages.
53
+ def description
54
+ "Derelict::Parser::Status instance"
55
+ end
56
+
57
+ private
58
+ # Retrieves the virtual machine list section of the output
59
+ def vm_lines
60
+ output.match(PARSE_LIST_FROM_OUTPUT).tap {|list|
61
+ logger.debug "Parsing VM list from output using #{description}"
62
+ raise InvalidFormat.new "Couldn't find VM list" if list.nil?
63
+ }.captures[0].lines
64
+ end
65
+ memoize :vm_lines
66
+
67
+ # Retrieves the state data for all virtual machines in the output
68
+ #
69
+ # The state is returned as a Hash, mapping virtual machine names
70
+ # (as symbols) to their state (also as a symbol). Both of these
71
+ # symbols have spaces converted to underscores (for convenience
72
+ # when writing literals in other code).
73
+ def states
74
+ logger.debug "Parsing states from VM list using #{description}"
75
+ vm_lines.inject Hash.new do |hash, line|
76
+ hash.merge! parse_line(line.match PARSE_STATE_FROM_LIST_ITEM)
77
+ end
78
+ end
79
+ memoize :states
80
+
81
+ def parse_line(match)
82
+ raise InvalidFormat.new "Couldn't parse VM list" if match.nil?
83
+ Hash[*match.captures[0..1].map {|value| sanitize value }]
84
+ end
85
+
86
+ def sanitize(value)
87
+ value.to_s.gsub(/\s+/, "_").downcase.to_sym
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,16 @@
1
+ module Derelict
2
+ class Parser
3
+ class Status
4
+ # The status wasn't in the expected format and couldn't be parsed
5
+ class InvalidFormat < Derelict::Exception
6
+ include Derelict::Exception::OptionalReason
7
+
8
+ private
9
+ # Retrieves the default error message
10
+ def default_message
11
+ "Output from 'vagrant status' was in an unexpected format"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ module Derelict
2
+ # Parses the output of "vagrant --version"
3
+ class Parser::Version < Parser
4
+ autoload :InvalidFormat, "derelict/parser/version/invalid_format"
5
+
6
+ # Include "memoize" class method to memoize methods
7
+ extend Memoist
8
+
9
+ # Regexp to extract the version from the "vagrant --version" output
10
+ PARSE_VERSION_FROM_OUTPUT = /^Vagrant (?:v(?:ersion )?)?(.*)?$/
11
+
12
+ # Determines the version of Vagrant based on the output
13
+ def version
14
+ logger.debug "Parsing version from output using #{description}"
15
+ matches = output.match PARSE_VERSION_FROM_OUTPUT
16
+ raise InvalidFormat.new output if matches.nil?
17
+ matches.captures[0]
18
+ end
19
+ memoize :version
20
+
21
+ # Provides a description of this Parser
22
+ #
23
+ # Mainly used for log messages.
24
+ def description
25
+ "Derelict::Parser::Version instance"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ module Derelict
2
+ class Parser
3
+ class Version
4
+ # The version wasn't in the expected format and couldn't be parsed
5
+ class InvalidFormat < Derelict::Exception
6
+ include Derelict::Exception::OptionalReason
7
+
8
+ private
9
+ # Retrieves the default error message
10
+ def default_message
11
+ "Output from 'vagrant --version' was in an unexpected format"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end