ridley 0.3.2 → 0.4.0

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