loom-core 0.0.1

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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +99 -0
  6. data/Guardfile +54 -0
  7. data/Rakefile +6 -0
  8. data/bin/loom +185 -0
  9. data/lib/env/development.rb +1 -0
  10. data/lib/loom.rb +44 -0
  11. data/lib/loom/all.rb +20 -0
  12. data/lib/loom/config.rb +106 -0
  13. data/lib/loom/core_ext.rb +37 -0
  14. data/lib/loom/dsl.rb +60 -0
  15. data/lib/loom/facts.rb +13 -0
  16. data/lib/loom/facts/all.rb +2 -0
  17. data/lib/loom/facts/fact_file_provider.rb +86 -0
  18. data/lib/loom/facts/fact_set.rb +138 -0
  19. data/lib/loom/host_spec.rb +32 -0
  20. data/lib/loom/inventory.rb +124 -0
  21. data/lib/loom/logger.rb +141 -0
  22. data/lib/loom/method_signature.rb +174 -0
  23. data/lib/loom/mods.rb +4 -0
  24. data/lib/loom/mods/action_proxy.rb +105 -0
  25. data/lib/loom/mods/all.rb +3 -0
  26. data/lib/loom/mods/mod_loader.rb +80 -0
  27. data/lib/loom/mods/module.rb +113 -0
  28. data/lib/loom/pattern.rb +15 -0
  29. data/lib/loom/pattern/all.rb +7 -0
  30. data/lib/loom/pattern/definition_context.rb +74 -0
  31. data/lib/loom/pattern/dsl.rb +176 -0
  32. data/lib/loom/pattern/hook.rb +28 -0
  33. data/lib/loom/pattern/loader.rb +48 -0
  34. data/lib/loom/pattern/reference.rb +71 -0
  35. data/lib/loom/pattern/reference_set.rb +169 -0
  36. data/lib/loom/pattern/result_reporter.rb +77 -0
  37. data/lib/loom/runner.rb +209 -0
  38. data/lib/loom/shell.rb +12 -0
  39. data/lib/loom/shell/all.rb +10 -0
  40. data/lib/loom/shell/api.rb +48 -0
  41. data/lib/loom/shell/cmd_result.rb +33 -0
  42. data/lib/loom/shell/cmd_wrapper.rb +164 -0
  43. data/lib/loom/shell/core.rb +226 -0
  44. data/lib/loom/shell/harness_blob.rb +26 -0
  45. data/lib/loom/shell/harness_command_builder.rb +50 -0
  46. data/lib/loom/shell/session.rb +25 -0
  47. data/lib/loom/trap.rb +44 -0
  48. data/lib/loom/version.rb +3 -0
  49. data/lib/loomext/all.rb +4 -0
  50. data/lib/loomext/corefacts.rb +6 -0
  51. data/lib/loomext/corefacts/all.rb +8 -0
  52. data/lib/loomext/corefacts/facter_provider.rb +24 -0
  53. data/lib/loomext/coremods.rb +5 -0
  54. data/lib/loomext/coremods/all.rb +13 -0
  55. data/lib/loomext/coremods/exec.rb +50 -0
  56. data/lib/loomext/coremods/files.rb +104 -0
  57. data/lib/loomext/coremods/net.rb +33 -0
  58. data/lib/loomext/coremods/package/adapter.rb +100 -0
  59. data/lib/loomext/coremods/package/package.rb +62 -0
  60. data/lib/loomext/coremods/user.rb +82 -0
  61. data/lib/loomext/coremods/vm.rb +0 -0
  62. data/lib/loomext/coremods/vm/all.rb +6 -0
  63. data/lib/loomext/coremods/vm/vbox.rb +84 -0
  64. data/loom.gemspec +39 -0
  65. data/loom/inventory.yml +13 -0
  66. data/scripts/harness.sh +242 -0
  67. data/spec/loom/host_spec_spec.rb +101 -0
  68. data/spec/loom/inventory_spec.rb +154 -0
  69. data/spec/loom/method_signature_spec.rb +275 -0
  70. data/spec/loom/pattern/dsl_spec.rb +207 -0
  71. data/spec/loom/shell/cmd_wrapper_spec.rb +239 -0
  72. data/spec/loom/shell/harness_blob_spec.rb +42 -0
  73. data/spec/loom/shell/harness_command_builder_spec.rb +36 -0
  74. data/spec/runloom.sh +35 -0
  75. data/spec/scripts/harness_spec.rb +385 -0
  76. data/spec/spec_helper.rb +94 -0
  77. data/spec/test.loom +370 -0
  78. data/spec/test_loom_spec.rb +57 -0
  79. metadata +287 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '00595d8a3c27f3c3a2567849e6d188c68a3c5914646238f0009767bff5769687'
4
+ data.tar.gz: 1988e1d88027c87309183db5355a2167f46ef2c48efcde7d5d143daf04011ea3
5
+ SHA512:
6
+ metadata.gz: a48f5d6d82ec87da2c8e0dca0107602a434f8e4d8f42e1eb5d4d5638106aea1733836745d91db52f497b0eb4d2b9812909a42e9aef10a6408a26b23cb227758c
7
+ data.tar.gz: dc23a63efd9405c7834911e66a709f944793e8b9bca62858e0a86864f999d0d8e942a21f9bda9893bd2672291806205d0b7e752cb9dbe70e0b2854630434bb97
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,99 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ loom-core (0.0.1)
5
+ bcrypt_pbkdf (= 1.0.0.alpha1)
6
+ commander (~> 4.4)
7
+ net-ssh (>= 3)
8
+ rbnacl-libsodium (= 1.0.10)
9
+ sshkit (~> 1.11)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ bcrypt_pbkdf (1.0.0.alpha1)
15
+ byebug (9.0.6)
16
+ coderay (1.1.1)
17
+ commander (4.4.0)
18
+ highline (~> 1.7.2)
19
+ diff-lcs (1.2.5)
20
+ ffi (1.9.14)
21
+ formatador (0.2.5)
22
+ guard (2.14.0)
23
+ formatador (>= 0.2.4)
24
+ listen (>= 2.7, < 4.0)
25
+ lumberjack (~> 1.0)
26
+ nenv (~> 0.1)
27
+ notiffany (~> 0.0)
28
+ pry (>= 0.9.12)
29
+ shellany (~> 0.0)
30
+ thor (>= 0.18.1)
31
+ guard-compat (1.2.1)
32
+ guard-rspec (4.7.3)
33
+ guard (~> 2.1)
34
+ guard-compat (~> 1.1)
35
+ rspec (>= 2.99.0, < 4.0)
36
+ highline (1.7.8)
37
+ listen (3.1.5)
38
+ rb-fsevent (~> 0.9, >= 0.9.4)
39
+ rb-inotify (~> 0.9, >= 0.9.7)
40
+ ruby_dep (~> 1.2)
41
+ lumberjack (1.0.10)
42
+ method_source (0.8.2)
43
+ nenv (0.3.0)
44
+ net-scp (1.2.1)
45
+ net-ssh (>= 2.6.5)
46
+ net-ssh (3.2.0)
47
+ notiffany (0.1.1)
48
+ nenv (~> 0.1)
49
+ shellany (~> 0.0)
50
+ pry (0.10.4)
51
+ coderay (~> 1.1.0)
52
+ method_source (~> 0.8.1)
53
+ slop (~> 3.4)
54
+ pry-byebug (3.4.0)
55
+ byebug (~> 9.0)
56
+ pry (~> 0.10)
57
+ rake (11.3.0)
58
+ rb-fsevent (0.9.8)
59
+ rb-inotify (0.9.7)
60
+ ffi (>= 0.5.0)
61
+ rbnacl (3.4.0)
62
+ ffi
63
+ rbnacl-libsodium (1.0.10)
64
+ rbnacl (>= 3.0.1)
65
+ rspec (3.5.0)
66
+ rspec-core (~> 3.5.0)
67
+ rspec-expectations (~> 3.5.0)
68
+ rspec-mocks (~> 3.5.0)
69
+ rspec-core (3.5.4)
70
+ rspec-support (~> 3.5.0)
71
+ rspec-expectations (3.5.0)
72
+ diff-lcs (>= 1.2.0, < 2.0)
73
+ rspec-support (~> 3.5.0)
74
+ rspec-mocks (3.5.0)
75
+ diff-lcs (>= 1.2.0, < 2.0)
76
+ rspec-support (~> 3.5.0)
77
+ rspec-support (3.5.0)
78
+ ruby_dep (1.5.0)
79
+ shellany (0.0.1)
80
+ slop (3.6.0)
81
+ sshkit (1.11.4)
82
+ net-scp (>= 1.1.2)
83
+ net-ssh (>= 2.8.0)
84
+ thor (0.19.1)
85
+
86
+ PLATFORMS
87
+ ruby
88
+
89
+ DEPENDENCIES
90
+ bundler (~> 1.13)
91
+ guard-rspec (~> 4.7)
92
+ loom-core!
93
+ pry (~> 0.10)
94
+ pry-byebug
95
+ rake (~> 11.3)
96
+ rspec (~> 3.5)
97
+
98
+ BUNDLED WITH
99
+ 1.13.6
data/Guardfile ADDED
@@ -0,0 +1,54 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: "bundle exec rspec" do
28
+ require "guard/rspec/dsl"
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Explicitly speficy emacs in order to disable notify-send notifications
32
+ notification :emacs
33
+
34
+ # Feel free to open issues for suggestions and improvements
35
+
36
+ # RSpec files
37
+ rspec = dsl.rspec
38
+ watch(rspec.spec_helper) { rspec.spec_dir }
39
+ watch(rspec.spec_support) { rspec.spec_dir }
40
+ watch(rspec.spec_files)
41
+
42
+ # Ruby files
43
+ ruby = dsl.ruby
44
+ dsl.watch_spec_files_for(ruby.lib_files)
45
+
46
+ # Loom shell scripts
47
+ watch(%r{^scripts/(.+)\.sh$}) { |m| "spec/scripts/#{m[1]}_spec.rb" }
48
+
49
+ # Turnip features and steps
50
+ watch(%r{^spec/acceptance/(.+)\.feature$})
51
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
52
+ Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
53
+ end
54
+ end
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/loom ADDED
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env ruby
2
+ if __FILE__ == $0
3
+ # for local development, when running bin/loom directly
4
+ $: << 'lib'
5
+ end
6
+
7
+ require 'commander'
8
+ require 'loom'
9
+
10
+ Loom.configure do |c|
11
+ c.log_level = :info
12
+ end
13
+
14
+ module Loom
15
+ class Cli
16
+ include Commander::Methods
17
+
18
+ def run
19
+ program :name, "Loom - Weaving through infrastructure"
20
+ program :version , Loom::VERSION
21
+ program :description, <<EOS
22
+ A lightweight infrastructure managment tool designed to manage hosts
23
+ through SSH, loosely inspired by Python Fabric - http://www.fabfile.org/.
24
+
25
+ Try `loom weave uptime -H localhost` to see an example.
26
+ EOS
27
+
28
+ global_option "-V", "--verbose", "Report verbose results" do |v|
29
+ Loom.configure do |c|
30
+ c.run_verbose = v
31
+ end
32
+ end
33
+
34
+ global_option "-d", "Enable loom debug logging, implies --verbose" do |d|
35
+ Loom.configure do |c|
36
+ c.log_level = :debug
37
+ c.run_verbose = true
38
+ end
39
+ end
40
+
41
+ global_option "--dbg [N]", Integer,
42
+ "Enable deep debug logging, where N is 0-6, implies --verbose" do |n|
43
+ raise "N must be greater than 0" if n < 0
44
+ Loom.configure do |c|
45
+ c.log_level = n * -1
46
+ c.run_verbose = true
47
+ end
48
+ end
49
+
50
+ global_option "-l", "--loom-files file1,f2,f3", Array,
51
+ "Load loom files from FILES instead of from the search path." do |files|
52
+ Loom.configure { |c| c.loom_files = files }
53
+ end
54
+
55
+ global_option "-a", "--all-hosts",
56
+ "Adds all known hostnames to the active inventory, " +
57
+ "can be combined with -H for additional hosts" do |flag|
58
+ Loom.configure { |c| c.inventory_all_hosts = flag }
59
+ end
60
+
61
+ global_option "-H", "--hosts host1,h2,h3", Array,
62
+ "Adds HOSTS to the active inventory" do |hosts|
63
+ Loom.configure { |c| c.inventory_hosts = hosts }
64
+ end
65
+
66
+ global_option "-G", "--groups group1,g2,g3", Array,
67
+ "Adds hostnames in GROUPS to the active inventory" do |groups|
68
+ Loom.configure { |c| c.inventory_groups = groups }
69
+ end
70
+
71
+ global_option "-X", "--custom-config KEY=VAL", String do |config_value|
72
+ key, val, *_rest = config_value.split "="
73
+ Loom.configure do |c|
74
+ c[key.to_sym] = val
75
+ end
76
+ end
77
+
78
+ default_command :weave
79
+
80
+ command :"weave" do |c|
81
+ c.syntax = "loom weave [pattern...] [options]"
82
+ c.description = "Applies the patterns to the active host inventory. " +
83
+ "This is the default."
84
+
85
+ c.option "-A", "--all",
86
+ "Run all loaded the patterns"
87
+ c.option "-n", "--dry-run",
88
+ "Don't run the patterns, only connect and log the expected patterns"
89
+ c.option "-F", "--fact F1=V1[,F2=V2,F3=V3]]]", "add custom fact"
90
+
91
+ c.action do |patterns, options|
92
+ if options.all
93
+ patterns = Loom::Pattern::Loader.load(Loom.config).slugs
94
+ end
95
+
96
+ other_facts = {}
97
+ if options.fact
98
+ fact_pairs = options.fact.split ","
99
+ fact_pairs.each do |pair|
100
+ key, val = pair.split "="
101
+ other_facts[key.to_sym] = val
102
+ end
103
+ end
104
+
105
+ runner = Loom::Runner.new Loom.config, patterns, other_facts
106
+ runner.run options.dry_run
107
+ end
108
+ end
109
+ alias_command :"w", :"weave"
110
+
111
+ command :"mods" do |c|
112
+ c.syntax = "loom mods [mod]"
113
+ c.description = "Prints the list of registered mods."
114
+
115
+ c.action do |mods, options|
116
+ puts "Loom mods are:"
117
+ puts ""
118
+
119
+ Loom::Mods::ModLoader.registered_mods.each do |name, aliases|
120
+ puts aliases.join(", ")
121
+ puts "\t#{name}"
122
+ end
123
+ end
124
+ end
125
+
126
+ command :"patterns" do |c|
127
+ c.syntax = "loom patterns [pattern]"
128
+ c.description = "Prints the list of known patterns."
129
+
130
+ c.option "--print", "Only print the space separted pattern names."
131
+ c.action do |patterns, options|
132
+ pattern_loader = Loom::Pattern::Loader.load Loom.config
133
+ pattern_slugs = pattern_loader.slugs
134
+ if options.print
135
+ puts pattern_slugs.join " "
136
+ return
137
+ end
138
+
139
+ puts "Loom patterns are:"
140
+ puts ""
141
+
142
+ max_slug_len = pattern_slugs.map(&:size).reduce(0) { |*args| args.max }
143
+ pattern_loader.patterns.each do |ref|
144
+ puts "\t#{ref.slug.ljust(max_slug_len)}\t#{ref.desc}"
145
+ end
146
+ end
147
+ end
148
+ alias_command :"p", :"patterns"
149
+
150
+ command :"config" do |c|
151
+ c.syntax = "loom config [options]"
152
+ c.description = "Print the config."
153
+ c.action do
154
+ puts Loom.config.to_yaml
155
+ end
156
+ end
157
+ alias_command :"c", :"config"
158
+
159
+ command :"inventory" do |c|
160
+ c.syntax = "loom inventory [options]"
161
+ c.description = "List all hosts in the inventory."
162
+
163
+ c.option "--active", <<EOS
164
+ Restricts to the hosts in the active inventory, useful to check which
165
+ hosts will be targeted with the config. Using with the -a flag is
166
+ equivalent to omitting --active.
167
+ EOS
168
+
169
+ c.action do |arg, options|
170
+ inventory = if options.active
171
+ Loom::Inventory::InventoryList.active_inventory Loom.config
172
+ else
173
+ Loom::Inventory::InventoryList.total_inventory Loom.config
174
+ end
175
+ puts inventory.hostnames.sort.to_yaml
176
+ end
177
+ end
178
+ alias_command :"i", :"inventory"
179
+
180
+ run!
181
+ end
182
+ end
183
+ end
184
+
185
+ Loom::Cli.new.run
@@ -0,0 +1 @@
1
+ require 'pry'
data/lib/loom.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'env/development'
2
+
3
+ module Loom
4
+
5
+ LoomError = Class.new ::StandardError
6
+ ExecutionError = Class.new LoomError
7
+
8
+ class << self
9
+ def configure(&block)
10
+ @config = Loom::Config.configure @config, &block
11
+ config_changed
12
+ end
13
+
14
+ def config
15
+ unless @config
16
+ @config = Loom::Config.configure
17
+ config_changed
18
+ end
19
+ @config
20
+ end
21
+
22
+ def reset_config
23
+ @config = nil
24
+ end
25
+
26
+ def log
27
+ @logger ||= config_logger
28
+ end
29
+
30
+ private
31
+ def config_changed
32
+ SSHKit.config.output_verbosity = config.sshkit_log_level
33
+ SSHKit.config.default_runner = config.sshkit_execution_strategy
34
+ @logger = nil
35
+ end
36
+
37
+ def config_logger
38
+ @logger = Loom::Logger.configure config
39
+ end
40
+
41
+ end
42
+ end
43
+
44
+ require 'loom/all'
data/lib/loom/all.rb ADDED
@@ -0,0 +1,20 @@
1
+ require "sshkit"
2
+
3
+ require_relative "core_ext"
4
+ require_relative "method_signature"
5
+ require_relative "trap"
6
+
7
+ require_relative "logger"
8
+ require_relative "config"
9
+
10
+ require_relative "shell"
11
+ require_relative "host_spec"
12
+ require_relative "dsl"
13
+
14
+ require_relative "inventory"
15
+ require_relative "facts"
16
+ require_relative "pattern"
17
+ require_relative "mods"
18
+ require_relative "runner"
19
+
20
+ require_relative "version"
@@ -0,0 +1,106 @@
1
+ require 'ostruct'
2
+ require 'yaml'
3
+
4
+ module Loom
5
+
6
+ ConfigError = Class.new Loom::LoomError
7
+
8
+ class Config
9
+
10
+ CONFIG_VARS = {
11
+ :loom_search_paths => ['/etc/loom', File.join(ENV['HOME'], '.loom'), './.loom'],
12
+ :loom_files => ['site.loom'],
13
+
14
+ :inventory_all_hosts => false,
15
+ :inventory_hosts => [],
16
+ :inventory_groups => [],
17
+
18
+ :log_level => :warn, # [debug, info, warn, error, fatal, or Integer]
19
+ :log_device => :stderr, # [stderr, stdout, file descriptor, or file name]
20
+ :log_colorize => true,
21
+
22
+ :run_failure_strategy => :exclude_host, # [exclude_host, fail_fast, cowboy]
23
+ :run_verbose => false,
24
+
25
+ :sshkit_execution_strategy => :sequence, # [sequence, parallel, groups]
26
+ :sshkit_log_level => :warn,
27
+ }.freeze
28
+
29
+ attr_reader *CONFIG_VARS.keys, :config_map
30
+
31
+ def initialize(**config_map)
32
+ config_map.each do |k,v|
33
+ # allows attr_reader methods from CONFIG_VAR to work
34
+ instance_variable_set :"@#{k}", v
35
+ end
36
+
37
+ @config_map = config_map
38
+ @file_manager = FileManager.new self
39
+ end
40
+
41
+ def [](key)
42
+ @config_map[key]
43
+ end
44
+
45
+ def to_yaml
46
+ @config_map.to_yaml
47
+ end
48
+ alias_method :dump, :to_yaml # aliased to dump for debugging purposes
49
+
50
+ def files
51
+ @file_manager
52
+ end
53
+
54
+ class << self
55
+ def configure(config=nil, &block)
56
+ # do NOT call Loom.log inside this block, the logger may not be
57
+ # configured, triggering an infinite recursion
58
+
59
+ map = config ? config.config_map : CONFIG_VARS.dup
60
+ config_struct = OpenStruct.new **map
61
+ yield config_struct if block_given?
62
+ Config.new config_struct.to_h
63
+ end
64
+ end
65
+
66
+ private
67
+ class FileManager
68
+
69
+ LOOM_FILE_PATTERNS = ["*.loom"]
70
+
71
+ def initialize(config)
72
+ @loom_search_paths = config.loom_search_paths
73
+ @loom_files = config.loom_files
74
+ end
75
+
76
+ def find(glob_patterns)
77
+ search_loom_paths(glob_patterns)
78
+ end
79
+
80
+ def loom_files
81
+ [@loom_files + search_loom_paths(LOOM_FILE_PATTERNS)].flatten.uniq
82
+ end
83
+
84
+ private
85
+ def search_loom_paths(file_patterns)
86
+ # Maps glob patterns into real file paths, selecting only
87
+ # readable files, and logs the result.
88
+ file_patterns.map do |file_pattern|
89
+ @loom_search_paths.map do |path|
90
+ Dir.glob File.join(path, "**", file_pattern)
91
+ end
92
+ end.flatten.uniq.select do |path|
93
+ should_select = File.file?(path) && File.readable?(path)
94
+ unless should_select
95
+ Loom.log.debug1(self) { "skipping config path => #{path}" }
96
+ end
97
+ should_select
98
+ end.tap do |config_files|
99
+ unless config_files.empty?
100
+ Loom.log.debug1(self) { "found config files => #{config_files}" }
101
+ end
102
+ end.uniq
103
+ end
104
+ end
105
+ end
106
+ end