derelict_m 0.6.2a

Sign up to get free protection for your applications and to get access to all the features.
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