ridley 0.3.2 → 0.4.0

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.
@@ -1,7 +1,7 @@
1
1
  module Ridley
2
- # @api private
3
2
  # @author Jamie Winsor <jamie@vialstudios.com>
4
- class DBIContext
3
+ # @api private
4
+ class DBIChainLink
5
5
  attr_reader :data_bag
6
6
  attr_reader :connection
7
7
 
@@ -44,8 +44,8 @@ module Ridley
44
44
  # @return [Ridley::DataBag]
45
45
  def find!(connection, object)
46
46
  chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
47
- name, uri = connection.get("#{self.resource_path}/#{chef_id}").body.first
48
- new(connection, name: name)
47
+ connection.get("#{self.resource_path}/#{chef_id}")
48
+ new(connection, name: chef_id)
49
49
  end
50
50
  end
51
51
 
@@ -56,7 +56,7 @@ module Ridley
56
56
  validates_presence_of :name
57
57
 
58
58
  def item
59
- @dbi_context ||= DBIContext.new(self, connection)
59
+ @dbi_link ||= DBIChainLink.new(self, connection)
60
60
  end
61
61
  end
62
62
 
@@ -64,12 +64,12 @@ module Ridley
64
64
  # Coerces instance functions into class functions on Ridley::DataBag. This coercion
65
65
  # sends an instance of the including class along to the class function.
66
66
  #
67
- # @see Ridley::Context
67
+ # @see Ridley::ChainLink
68
68
  #
69
- # @return [Ridley::Context]
69
+ # @return [Ridley::ChainLink]
70
70
  # a context object to delegate instance functions to class functions on Ridley::DataBag
71
71
  def data_bag
72
- Context.new(Ridley::DataBag, self)
72
+ ChainLink.new(self, Ridley::DataBag)
73
73
  end
74
74
  end
75
75
  end
@@ -84,12 +84,12 @@ module Ridley
84
84
  # Coerces instance functions into class functions on Ridley::Environment. This coercion
85
85
  # sends an instance of the including class along to the class function.
86
86
  #
87
- # @see Ridley::Context
87
+ # @see Ridley::ChainLink
88
88
  #
89
- # @return [Ridley::Context]
89
+ # @return [Ridley::ChainLink]
90
90
  # a context object to delegate instance functions to class functions on Ridley::Environment
91
91
  def environment
92
- Context.new(Ridley::Environment, self)
92
+ ChainLink.new(self, Ridley::Environment)
93
93
  end
94
94
  end
95
95
  end
@@ -1,6 +1,51 @@
1
1
  module Ridley
2
2
  # @author Jamie Winsor <jamie@vialstudios.com>
3
3
  class Node
4
+ class << self
5
+ # @overload bootstrap(connection, nodes, options = {})
6
+ # @param [Ridley::Connection] connection
7
+ # @param [Array<String>, String] nodes
8
+ # @option options [String] :ssh_user
9
+ # @option options [String] :ssh_password
10
+ # @option options [Array<String>, String] :ssh_keys
11
+ # @option options [Float] :ssh_timeout
12
+ # timeout value for SSH bootstrap (default: 1.5)
13
+ # @option options [String] :validator_client
14
+ # @option options [String] :validator_path
15
+ # filepath to the validator used to bootstrap the node (required)
16
+ # @option options [String] :bootstrap_proxy
17
+ # URL to a proxy server to bootstrap through (default: nil)
18
+ # @option options [String] :encrypted_data_bag_secret_path
19
+ # filepath on your host machine to your organizations encrypted data bag secret (default: nil)
20
+ # @option options [Hash] :hints
21
+ # a hash of Ohai hints to place on the bootstrapped node (default: Hash.new)
22
+ # @option options [Hash] :attributes
23
+ # a hash of attributes to use in the first Chef run (default: Hash.new)
24
+ # @option options [Array] :run_list
25
+ # an initial run list to bootstrap with (default: Array.new)
26
+ # @option options [String] :chef_version
27
+ # version of Chef to install on the node (default: {Ridley::CHEF_VERSION})
28
+ # @option options [String] :environment
29
+ # environment to join the node to (default: '_default')
30
+ # @option options [Boolean] :sudo
31
+ # bootstrap with sudo (default: true)
32
+ # @option options [String] :template
33
+ # bootstrap template to use (default: omnibus)
34
+ def bootstrap(connection, *args)
35
+ options = args.last.is_a?(Hash) ? args.pop : Hash.new
36
+
37
+ options[:server_url] ||= connection.server_url
38
+ options[:ssh_user] ||= connection.ssh[:user]
39
+ options[:ssh_password] ||= connection.ssh[:password]
40
+ options[:ssh_timeout] ||= connection.ssh[:timeout]
41
+ options[:validator_path] ||= connection.validator_path
42
+ options[:validator_client] ||= connection.validator_client
43
+ options[:encrypted_data_bag_secret_path] ||= connection.encrypted_data_bag_secret_path
44
+
45
+ Bootstrapper.new(args, options).run
46
+ end
47
+ end
48
+
4
49
  include Ridley::Resource
5
50
 
6
51
  set_chef_id "name"
@@ -132,18 +177,27 @@ module Ridley
132
177
  def rackspace?
133
178
  self.cloud_provider == "rackspace"
134
179
  end
180
+
181
+ # Run Chef-Client on the instantiated node
182
+ #
183
+ # @return [SSH::Response]
184
+ def chef_client
185
+ Ridley::SSH.start(self, connection.ssh) do |ssh|
186
+ ssh.run("sudo chef-client").first
187
+ end
188
+ end
135
189
  end
136
190
 
137
191
  module DSL
138
192
  # Coerces instance functions into class functions on Ridley::Node. This coercion
139
193
  # sends an instance of the including class along to the class function.
140
194
  #
141
- # @see Ridley::Context
195
+ # @see Ridley::ChainLink
142
196
  #
143
- # @return [Ridley::Context]
197
+ # @return [Ridley::ChainLink]
144
198
  # a context object to delegate instance functions to class functions on Ridley::Node
145
199
  def node
146
- Context.new(Ridley::Node, self)
200
+ ChainLink.new(self, Ridley::Node)
147
201
  end
148
202
  end
149
203
  end
@@ -68,12 +68,12 @@ module Ridley
68
68
  # Coerces instance functions into class functions on Ridley::Role. This coercion
69
69
  # sends an instance of the including class along to the class function.
70
70
  #
71
- # @see Ridley::Context
71
+ # @see Ridley::ChainLink
72
72
  #
73
- # @return [Ridley::Context]
73
+ # @return [Ridley::ChainLink]
74
74
  # a context object to delegate instance functions to class functions on Ridley::Role
75
75
  def role
76
- Context.new(Ridley::Role, self)
76
+ ChainLink.new(self, Ridley::Role)
77
77
  end
78
78
  end
79
79
  end
@@ -111,12 +111,12 @@ module Ridley
111
111
  # Coerces instance functions into class functions on Ridley::Sandbox. This coercion
112
112
  # sends an instance of the including class along to the class function.
113
113
  #
114
- # @see Ridley::Context
114
+ # @see Ridley::ChainLink
115
115
  #
116
- # @return [Ridley::Context]
116
+ # @return [Ridley::ChainLink]
117
117
  # a context object to delegate instance functions to class functions on Ridley::Sandbox
118
118
  def sandbox
119
- Context.new(Ridley::Sandbox, self)
119
+ ChainLink.new(self, Ridley::Sandbox)
120
120
  end
121
121
  end
122
122
  end
@@ -0,0 +1,13 @@
1
+ module Ridley
2
+ class SSH
3
+ # @author Jamie Winsor <jamie@vialstudios.com>
4
+ class Response < Struct.new(:stdout, :stderr, :exit_code, :exit_signal)
5
+ # Return true if the response was not successful
6
+ #
7
+ # @return [Boolean]
8
+ def error?
9
+ self.exit_code != 0
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ module Ridley
2
+ class SSH
3
+ # @author Jamie Winsor <jamie@vialstudios.com>
4
+ class ResponseSet
5
+ # @return [Array<SSH::Response>]
6
+ attr_reader :oks
7
+ # @return [Array<SSH::Response>]
8
+ attr_reader :errors
9
+
10
+ def initialize
11
+ @oks = Array.new
12
+ @errors = Array.new
13
+ end
14
+
15
+ # Add an "OK" response to the ResponseSet
16
+ #
17
+ # @param [SSH::Response] response
18
+ def add_ok(response)
19
+ self.oks << response
20
+ end
21
+
22
+ # Add an "Error" response to the ResponseSet
23
+ #
24
+ # @param [SSH::Response] response
25
+ def add_error(response)
26
+ self.errors << response
27
+ end
28
+
29
+ # Return true if the response set contains any errors
30
+ #
31
+ # @return [Boolean]
32
+ def has_errors?
33
+ self.errors.any?
34
+ end
35
+
36
+ # Return one of the responses
37
+ #
38
+ # @return [SSH::Response]
39
+ def first
40
+ (self.oks + self.errors).first
41
+ end
42
+
43
+ # Returns how many responses are in the set
44
+ #
45
+ # @return [Integer]
46
+ def length
47
+ self.oks.length + self.errors.length
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,71 @@
1
+ module Ridley
2
+ class SSH
3
+ # @author Jamie Winsor <jamie@vialstudios.com>
4
+ # @api private
5
+ class Worker
6
+ include Celluloid
7
+ include Celluloid::Logger
8
+
9
+ # @param [Hash] options
10
+ def initialize(options = {})
11
+ @options = options
12
+ @user = options.fetch(:user)
13
+ end
14
+
15
+ # @param [String] host
16
+ # @param [String] command
17
+ #
18
+ # @return [Array]
19
+ def run(host, command)
20
+ response = Response.new("", "")
21
+ debug "Running SSH command: '#{command}' on: '#{host}' as: '#{user}'"
22
+
23
+ Net::SSH.start(host, user, options) do |ssh|
24
+ ssh.open_channel do |channel|
25
+ channel.exec(command) do |ch, success|
26
+ unless success
27
+ raise "FAILURE: could not execute command"
28
+ end
29
+
30
+ channel.on_data do |ch, data|
31
+ response.stdout += data
32
+ end
33
+
34
+ channel.on_extended_data do |ch, type, data|
35
+ response.stderr += data
36
+ end
37
+
38
+ channel.on_request("exit-status") do |ch, data|
39
+ response.exit_code = data.read_long
40
+ end
41
+
42
+ channel.on_request("exit-signal") do |ch, data|
43
+ response.exit_signal = data.read_string
44
+ end
45
+ end
46
+ end
47
+
48
+ ssh.loop
49
+ end
50
+
51
+ case response.exit_code
52
+ when 0
53
+ debug "Successfully ran SSH command: '#{command}' on: '#{host}' as: '#{user}' and it succeeded"
54
+ [ :ok, response ]
55
+ else
56
+ debug "Successfully ran SSH command: '#{command}' on: '#{host}' as: '#{user}' but it failed"
57
+ [ :error, response ]
58
+ end
59
+ rescue => e
60
+ debug "Failed to run SSH command: '#{command}' on: '#{host}' as: '#{user}'"
61
+ [ :error, e.message ]
62
+ end
63
+
64
+ private
65
+
66
+ attr_reader :runner
67
+ attr_reader :user
68
+ attr_reader :options
69
+ end
70
+ end
71
+ end
data/lib/ridley/ssh.rb ADDED
@@ -0,0 +1,74 @@
1
+ require 'net/ssh'
2
+
3
+ module Ridley
4
+ # @author Jamie Winsor <jamie@vialstudios.com>
5
+ class SSH
6
+ autoload :Response, 'ridley/ssh/response'
7
+ autoload :ResponseSet, 'ridley/ssh/response_set'
8
+ autoload :Worker, 'ridley/ssh/worker'
9
+
10
+ class << self
11
+ # @param [Ridley::Node, Array<Ridley::Node>] nodes
12
+ # @param [Hash] options
13
+ def start(nodes, options = {}, &block)
14
+ runner = new(nodes, options)
15
+ result = yield runner
16
+ runner.terminate
17
+
18
+ result
19
+ end
20
+ end
21
+
22
+ include Celluloid
23
+ include Celluloid::Logger
24
+
25
+ attr_reader :nodes
26
+ attr_reader :options
27
+
28
+ # @param [Ridley::Node, Array<Ridley::Node>] nodes
29
+ # @param [Hash] options
30
+ # @see Net::SSH
31
+ def initialize(nodes, options = {})
32
+ @nodes = nodes
33
+ @options = options
34
+
35
+ self.options[:timeout] ||= 1.5
36
+ end
37
+
38
+ # @return [Array<SSH::Worker>]
39
+ def workers
40
+ @workers ||= Array(nodes).collect do |node|
41
+ Worker.new_link(current_actor, node.public_hostname, options)
42
+ end
43
+ end
44
+
45
+ # @param [String] command
46
+ #
47
+ # @return [Array]
48
+ def run(command)
49
+ workers.collect { |worker| worker.async.run(command) }
50
+
51
+ ResponseSet.new.tap do |responses|
52
+ until responses.length == workers.length
53
+ receive { |msg|
54
+ status, response = msg
55
+
56
+ case status
57
+ when :ok
58
+ responses.add_ok(response)
59
+ when :error
60
+ responses.add_error(response)
61
+ else
62
+ error "SSH Failure: #{command}. terminating..."
63
+ terminate
64
+ end
65
+ }
66
+ end
67
+ end
68
+ end
69
+
70
+ def finalize
71
+ workers.collect(&:terminate)
72
+ end
73
+ end
74
+ end
@@ -1,3 +1,3 @@
1
1
  module Ridley
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/ridley.rb CHANGED
@@ -1,37 +1,48 @@
1
+ require 'chozo'
2
+ require 'active_support/core_ext'
3
+ require 'celluloid'
1
4
  require 'faraday'
2
5
  require 'addressable/uri'
3
- require 'yajl'
4
6
  require 'multi_json'
5
7
  require 'active_model'
6
8
  require 'active_support/inflector'
7
- require 'active_support/core_ext'
8
9
  require 'forwardable'
9
- require 'set'
10
10
  require 'thread'
11
- require 'chozo/core_ext'
11
+
12
+ if jruby?
13
+ require 'json/pure'
14
+ else
15
+ require 'json/ext'
16
+ end
12
17
 
13
18
  require 'ridley/version'
14
19
  require 'ridley/errors'
15
20
 
21
+ JSON.create_id = nil
22
+
16
23
  # @author Jamie Winsor <jamie@vialstudios.com>
17
24
  module Ridley
18
- CHEF_VERSION = '10.14.4'.freeze
25
+ CHEF_VERSION = '10.16.2'.freeze
19
26
 
20
- autoload :Log, 'ridley/log'
27
+ autoload :Bootstrapper, 'ridley/bootstrapper'
28
+ autoload :Client, 'ridley/resources/client'
21
29
  autoload :Connection, 'ridley/connection'
30
+ autoload :ChainLink, 'ridley/chain_link'
31
+ autoload :Cookbook, 'ridley/resources/cookbook'
32
+ autoload :DataBag, 'ridley/resources/data_bag'
33
+ autoload :DataBagItem, 'ridley/resources/data_bag_item'
22
34
  autoload :DSL, 'ridley/dsl'
23
- autoload :Context, 'ridley/context'
24
- autoload :Resource, 'ridley/resource'
25
35
  autoload :Environment, 'ridley/resources/environment'
26
- autoload :Role, 'ridley/resources/role'
27
- autoload :Client, 'ridley/resources/client'
36
+ autoload :Logging, 'ridley/logging'
28
37
  autoload :Node, 'ridley/resources/node'
29
- autoload :DataBag, 'ridley/resources/data_bag'
30
- autoload :DataBagItem, 'ridley/resources/data_bag_item'
31
- autoload :Cookbook, 'ridley/resources/cookbook'
38
+ autoload :Resource, 'ridley/resource'
39
+ autoload :Role, 'ridley/resources/role'
32
40
  autoload :Search, 'ridley/resources/search'
41
+ autoload :SSH, 'ridley/ssh'
33
42
 
34
43
  class << self
44
+ attr_accessor :logger
45
+
35
46
  def connection(*args)
36
47
  Connection.new(*args)
37
48
  end
@@ -40,12 +51,26 @@ module Ridley
40
51
  Connection.sync(*args, &block)
41
52
  end
42
53
 
43
- # @return [Ridley::Log]
44
- def log
45
- Ridley::Log
54
+ # @return [Logger]
55
+ def logger
56
+ Ridley::Logging.logger
57
+ end
58
+ alias_method :log, :logger
59
+
60
+ # @param [Logger, nil] obj
61
+ #
62
+ # @return [Logger]
63
+ def set_logger(obj)
64
+ Ridley::Logging.set_logger(obj)
65
+ end
66
+
67
+ # @return [Pathname]
68
+ def root
69
+ @root ||= Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
46
70
  end
47
- alias_method :logger, :log
48
71
  end
49
72
  end
50
73
 
74
+ Celluloid.logger = Ridley.logger
75
+
51
76
  require 'ridley/middleware'
data/ridley.gemspec CHANGED
@@ -16,27 +16,16 @@ Gem::Specification.new do |s|
16
16
  s.version = Ridley::VERSION
17
17
  s.required_ruby_version = ">= 1.9.1"
18
18
 
19
- s.add_runtime_dependency 'chozo', '>= 0.0.3'
20
- s.add_runtime_dependency 'yajl-ruby'
19
+ s.add_runtime_dependency 'json', '>= 1.5.0'
20
+ s.add_runtime_dependency 'multi_json', '>= 1.0.4'
21
+ s.add_runtime_dependency 'chozo', '>= 0.2.2'
21
22
  s.add_runtime_dependency 'mixlib-log'
22
23
  s.add_runtime_dependency 'mixlib-authentication'
23
24
  s.add_runtime_dependency 'addressable'
24
25
  s.add_runtime_dependency 'faraday'
25
- s.add_runtime_dependency 'multi_json', '>= 1.0.4'
26
26
  s.add_runtime_dependency 'activemodel', '>= 3.2.0'
27
27
  s.add_runtime_dependency 'activesupport', '>= 3.2.0'
28
-
29
- s.add_development_dependency 'rake'
30
- s.add_development_dependency 'rspec'
31
- s.add_development_dependency 'fuubar'
32
- s.add_development_dependency 'spork'
33
- s.add_development_dependency 'yard'
34
- s.add_development_dependency 'guard'
35
- s.add_development_dependency 'guard-rspec'
36
- s.add_development_dependency 'guard-spork'
37
- s.add_development_dependency 'guard-yard'
38
- s.add_development_dependency 'coolline'
39
- s.add_development_dependency 'redcarpet'
40
- s.add_development_dependency 'json_spec'
41
- s.add_development_dependency 'webmock'
28
+ s.add_runtime_dependency 'celluloid'
29
+ s.add_runtime_dependency 'net-ssh'
30
+ s.add_runtime_dependency 'erubis'
42
31
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Bootstrapping a node", type: "acceptance" do
4
+ let(:server_url) { "https://api.opscode.com" }
5
+ let(:client_name) { "reset" }
6
+ let(:client_key) { "/Users/reset/.chef/reset.pem" }
7
+ let(:organization) { "vialstudios" }
8
+
9
+ let(:connection) do
10
+ Ridley.connection(
11
+ server_url: server_url,
12
+ client_name: client_name,
13
+ client_key: client_key,
14
+ organization: organization,
15
+ validator_client: "vialstudios-validator",
16
+ validator_path: "/Users/reset/.chef/vialstudios-validator.pem",
17
+ ssh: {
18
+ user: "vagrant",
19
+ password: "vagrant"
20
+ }
21
+ )
22
+ end
23
+
24
+ it "returns an array of response objects" do
25
+ pending
26
+
27
+ connection.node.bootstrap("33.33.33.10").should_not have_errors
28
+ end
29
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  require 'rubygems'
2
2
  require 'bundler'
3
- require 'spork'
3
+ require 'chozo'
4
4
 
5
- Spork.prefork do
5
+ def setup_rspec
6
6
  require 'rspec'
7
7
  require 'json_spec'
8
8
  require 'webmock/rspec'
@@ -17,9 +17,25 @@ Spork.prefork do
17
17
  config.treat_symbols_as_metadata_keys_with_true_values = true
18
18
  config.filter_run focus: true
19
19
  config.run_all_when_everything_filtered = true
20
+
21
+ config.before(:all) do
22
+ Ridley.logger = nil
23
+ Celluloid.logger = nil
24
+ end
20
25
  end
21
26
  end
22
27
 
23
- Spork.each_run do
28
+ if mri? && ENV['CI'] != 'true'
29
+ require 'spork'
30
+
31
+ Spork.prefork do
32
+ setup_rspec
33
+ end
34
+
35
+ Spork.each_run do
36
+ require 'ridley'
37
+ end
38
+ else
24
39
  require 'ridley'
40
+ setup_rspec
25
41
  end