lib/ridley/resources/node_resource.rb in ridley-0.10.2 vs lib/ridley/resources/node_resource.rb in ridley-0.11.0.rc1

- old
+ new

@@ -1,348 +1,172 @@ module Ridley # @author Jamie Winsor <reset@riotgames.com> class NodeResource < Ridley::Resource - class << self - # @overload bootstrap(client, nodes, options = {}) - # @param [Ridley::Client] client - # @param [Array<String>, String] nodes - # @param [Hash] ssh - # * :user (String) a shell user that will login to each node and perform the bootstrap command on (required) - # * :password (String) the password for the shell user that will perform the bootstrap - # * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password - # * :timeout (Float) [5.0] timeout value for SSH bootstrap - # @option options [Hash] :winrm - # * :user (String) a user that will login to each node and perform the bootstrap command on (required) - # * :password (String) the password for the user that will perform the bootstrap - # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985) - # @option options [String] :validator_client - # @option options [String] :validator_path - # filepath to the validator used to bootstrap the node (required) - # @option options [String] :bootstrap_proxy - # URL to a proxy server to bootstrap through (default: nil) - # @option options [String] :encrypted_data_bag_secret_path - # filepath on your host machine to your organizations encrypted data bag secret (default: nil) - # @option options [Hash] :hints - # a hash of Ohai hints to place on the bootstrapped node (default: Hash.new) - # @option options [Hash] :attributes - # a hash of attributes to use in the first Chef run (default: Hash.new) - # @option options [Array] :run_list - # an initial run list to bootstrap with (default: Array.new) - # @option options [String] :chef_version - # version of Chef to install on the node (default: nil) - # @option options [String] :environment - # environment to join the node to (default: '_default') - # @option options [Boolean] :sudo - # bootstrap with sudo (default: true) - # @option options [String] :template - # bootstrap template to use (default: omnibus) - # - # @return [SSH::ResponseSet] - def bootstrap(client, *args) - options = args.extract_options! - - default_options = { - server_url: client.server_url, - validator_path: client.validator_path, - validator_client: client.validator_client, - encrypted_data_bag_secret_path: client.encrypted_data_bag_secret_path, - ssh: client.ssh, - winrm: client.winrm, - chef_version: client.chef_version - } - - options = default_options.merge(options) - Bootstrapper.new(args, options).run - end - - # Executes a Chef run using the best worker available for the given - # host. - # - # @param [Ridley::Client] client - # @param [String] host - # - # @return [HostConnector::Response] - def chef_run(client, host) - worker = configured_worker_for(client, host) - worker.chef_client - ensure - worker.terminate if worker && worker.alive? - end - - # Puts a secret on the host using the best worker available for - # the given host. - # - # @param [Ridley::Client] client - # @param [String] host - # @param [String] encrypted_data_bag_secret_path - # - # @return [HostConnector::Response] - def put_secret(client, host, encrypted_data_bag_secret_path) - worker = configured_worker_for(client, host) - worker.put_secret(encrypted_data_bag_secret_path) - ensure - worker.terminate if worker && worker.alive? - end - - # Executes an arbitrary ruby script using the best worker available - # for the given host. - # - # @param [Ridley::Client] client - # @param [String] host - # @param [Array<String>] command_lines - # - # @return [HostConnector::Response] - def ruby_script(client, host, command_lines) - worker = configured_worker_for(client, host) - worker.ruby_script(command_lines) - ensure - worker.terminate if worker && worker.alive? - end - - # Executes the given command on a node using the best worker - # available for the given host. - # - # @param [Ridley::Client] client - # @param [String] host - # @param [String] command - # - # @return [Array<Symbol, HostConnector::Response>] - def execute_command(client, host, command) - worker = configured_worker_for(client, host) - worker.run(command) - ensure - worker.terminate if worker && worker.alive? - end - - # Merges the given data with the the data of the target node on the remote - # - # @param [Ridley::Client] client - # @param [Ridley::NodeResource, String] target - # node or identifier of the node to merge - # - # @option options [Array] :run_list - # run list items to merge - # @option options [Hash] :attributes - # attributes of normal precedence to merge - # - # @raise [Errors::HTTPNotFound] - # if the target node is not found - # - # @return [Ridley::NodeResource] - def merge_data(client, target, options = {}) - find!(client, target).merge_data(options) - end - - private - # @param [Ridley::Client] client - # @param [String] host - # - # @return [SSH::Worker, WinRM::Worker] - def configured_worker_for(client, host) - connector_options = Hash.new - connector_options[:ssh] = client.ssh - connector_options[:winrm] = client.winrm - - HostConnector.best_connector_for(host, connector_options) do |host_connector| - host_connector::Worker.new(host, connector_options) - end - end - end - include Ridley::Logging - set_chef_id "name" - set_chef_type "node" - set_chef_json_class "Chef::Node" set_resource_path "nodes" + represented_by Ridley::NodeObject - attribute :name, - required: true + attr_reader :server_url + attr_reader :validator_path + attr_reader :validator_client + attr_reader :encrypted_data_bag_secret + attr_reader :ssh + attr_reader :winrm + attr_reader :chef_version - attribute :chef_environment, - default: "_default" - - attribute :automatic, - default: Hashie::Mash.new - - attribute :normal, - default: Hashie::Mash.new - - attribute :default, - default: Hashie::Mash.new - - attribute :override, - default: Hashie::Mash.new - - attribute :run_list, - default: Array.new - - alias_method :normal_attributes, :normal - alias_method :automatic_attributes, :automatic - alias_method :default_attributes, :default - alias_method :override_attributes, :override - - alias_method :normal_attributes=, :normal= - alias_method :automatic_attributes=, :automatic= - alias_method :default_attributes=, :default= - alias_method :override_attributes=, :override= - - # Set a node level normal attribute given the dotted path representation of the Chef - # attribute and value. + # @param [Celluloid::Registry] connection_registry # - # @note It is not possible to set any other attribute level on a node and have it persist after - # a Chef Run. This is because all other attribute levels are truncated at the start of a Chef Run. - # - # @example setting and saving a node level normal attribute - # - # obj = node.find("jwinsor-1") - # obj.set_chef_attribute("my_app.billing.enabled", false) - # obj.save - # - # @param [String] key - # @param [Object] value - # - # @return [Hashie::Mash] - def set_chef_attribute(key, value) - attr_hash = Hashie::Mash.from_dotted_path(key, value) - self.normal = self.normal.deep_merge(attr_hash) + # @option options [String] :server_url + # URL to the Chef API + # @option options [Hash] ssh + # * :user (String) a shell user that will login to each node and perform the bootstrap command on + # * :password (String) the password for the shell user that will perform the bootstrap + # * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password + # * :timeout (Float) [5.0] timeout value for SSH bootstrap + # @option options [Hash] :winrm + # * :user (String) a user that will login to each node and perform the bootstrap command on + # * :password (String) the password for the user that will perform the bootstrap + # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on + # @option options [String] :validator_client + # @option options [String] :validator_path + # filepath to the validator used to bootstrap the node + # @option options [String] :encrypted_data_bag_secret + # your organizations encrypted data bag secret + # @option options [String] :chef_version + # version of Chef to install on the node (default: nil) + def initialize(connection_registry, options = {}) + super(connection_registry) + @server_url = options[:server_url] + @validator_path = options[:validator_path] + @validator_client = options[:validator_client] + @encrypted_data_bag_secret = options[:encrypted_data_bag_secret] + @ssh = options[:ssh] + @winrm = options[:winrm] + @chef_version = options[:chef_version] end - # Returns the public hostname of the instantiated node. This hostname should be used for - # public communications to the node. + # @overload bootstrap(nodes, options = {}) + # @param [Array<String>, String] nodes # - # @example - # node.public_hostname => "reset.riotgames.com" + # @option options [Hash] ssh + # * :user (String) a shell user that will login to each node and perform the bootstrap command on (required) + # * :password (String) the password for the shell user that will perform the bootstrap + # * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password + # * :timeout (Float) [5.0] timeout value for SSH bootstrap + # @option options [Hash] :winrm + # * :user (String) a user that will login to each node and perform the bootstrap command on (required) + # * :password (String) the password for the user that will perform the bootstrap + # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985) + # @option options [String] :validator_client + # @option options [String] :validator_path + # filepath to the validator used to bootstrap the node (required) + # @option options [String] :bootstrap_proxy + # URL to a proxy server to bootstrap through (default: nil) + # @option options [String] :encrypted_data_bag_secret_path + # filepath on your host machine to your organizations encrypted data bag secret (default: nil) + # @option options [Hash] :hints + # a hash of Ohai hints to place on the bootstrapped node (default: Hash.new) + # @option options [Hash] :attributes + # a hash of attributes to use in the first Chef run (default: Hash.new) + # @option options [Array] :run_list + # an initial run list to bootstrap with (default: Array.new) + # @option options [String] :chef_version + # version of Chef to install on the node (default: nil) + # @option options [String] :environment + # environment to join the node to (default: '_default') + # @option options [Boolean] :sudo + # bootstrap with sudo (default: true) + # @option options [String] :template + # bootstrap template to use (default: omnibus) # - # @return [String] - def public_hostname - self.cloud? ? self.automatic[:cloud][:public_hostname] : self.automatic[:fqdn] - end + # @return [SSH::ResponseSet] + def bootstrap(*args) + args = args.dup + opts = args.extract_options! - # Returns the public IPv4 address of the instantiated node. This ip address should be - # used for public communications to the node. - # - # @example - # node.public_ipv4 => "10.33.33.1" - # - # @return [String] - def public_ipv4 - self.cloud? ? self.automatic[:cloud][:public_ipv4] : self.automatic[:ipaddress] - end - alias_method :public_ipaddress, :public_ipv4 + options = opts.reverse_merge( + server_url: server_url, + validator_path: validator_path, + validator_client: validator_client, + encrypted_data_bag_secret: encrypted_data_bag_secret, + ssh: ssh, + winrm: winrm, + chef_version: chef_version + ) - # Returns the cloud provider of the instantiated node. If the node is not identified as - # a cloud node, then nil is returned. - # - # @example - # node_1.cloud_provider => "eucalyptus" - # node_2.cloud_provider => "ec2" - # node_3.cloud_provider => "rackspace" - # node_4.cloud_provider => nil - # - # @return [nil, String] - def cloud_provider - self.cloud? ? self.automatic[:cloud][:provider] : nil + Bootstrapper.new(args, options).run end - # Returns true if the node is identified as a cloud node. + # Executes a Chef run using the best worker available for the given + # host. # - # @return [Boolean] - def cloud? - self.automatic.has_key?(:cloud) - end - - # Returns true if the node is identified as a cloud node using the eucalyptus provider. + # @param [String] host # - # @return [Boolean] - def eucalyptus? - self.cloud_provider == "eucalyptus" + # @return [HostConnector::Response] + def chef_run(host) + worker = HostConnector.new(host, ssh: ssh, winrm: winrm) + worker.chef_client + ensure + worker.terminate if worker && worker.alive? end - # Returns true if the node is identified as a cloud node using the ec2 provider. + # Puts a secret on the host using the best worker available for + # the given host. # - # @return [Boolean] - def ec2? - self.cloud_provider == "ec2" - end - - # Returns true if the node is identified as a cloud node using the rackspace provider. + # @param [String] host # - # @return [Boolean] - def rackspace? - self.cloud_provider == "rackspace" + # @return [HostConnector::Response] + def put_secret(host) + worker = HostConnector.new(host, ssh: ssh, winrm: winrm) + worker.put_secret(encrypted_data_bag_secret) + ensure + worker.terminate if worker && worker.alive? end - # Run Chef-Client on the instantiated node. + # Executes an arbitrary ruby script using the best worker available + # for the given host. # - # @param [Hash] options - # a hash of options to pass to the best {Ridley::HostConnector} + # @param [String] host + # @param [Array<String>] command_lines # # @return [HostConnector::Response] - def chef_client(options = {}) - connector_options = Hash.new - connector_options[:ssh] = client.ssh - connector_options[:winrm] = client.winrm - connector_options.merge(options) - - log.debug "Running Chef Client on: #{self.public_hostname}" - - HostConnector.best_connector_for(self.public_hostname, connector_options) do |host_connector| - host_connector.start(self, connector_options) do |connector| - _, response = connector.chef_client - response - end - end + def ruby_script(host, command_lines) + worker = HostConnector.new(host, ssh: ssh, winrm: winrm) + worker.ruby_script(command_lines) + ensure + worker.terminate if worker && worker.alive? end - # Put the client's encrypted data bag secret onto the instantiated node. If no - # encrypted data bag key path is set on the resource's client then nil will be - # returned + # Executes the given command on a node using the best worker + # available for the given host. # - # @param [Hash] options - # a hash of options to pass to the best {Ridley::HostConnector} + # @param [String] host + # @param [String] command # - # @return [HostConnector::Response, nil] - def put_secret(options = {}) - if client.encrypted_data_bag_secret_path.nil? || - !File.exists?(client.encrypted_data_bag_secret_path) - - return nil - end - - connector_options = Hash.new - connector_options[:ssh] = client.ssh - connector_options[:winrm] = client.winrm - - log.debug "Writing Encrypted Data Bag Secret to: #{self.public_hostname}" - - HostConnector.best_connector_for(self.public_hostname, connector_options) do |host_connector| - host_connector.start(self, connector_options) do |connector| - _, response = connector.put_secret(client.encrypted_data_bag_secret_path) - response - end - end + # @return [Array<Symbol, HostConnector::Response>] + def execute_command(host, command) + worker = HostConnector.new(host, ssh: ssh, winrm: winrm) + worker.run(command) + ensure + worker.terminate if worker && worker.alive? end - # Merges the instaniated nodes data with the given data and updates - # the remote with the merged results + # Merges the given data with the the data of the target node on the remote # + # @param [Ridley::NodeResource, String] target + # node or identifier of the node to merge + # # @option options [Array] :run_list # run list items to merge # @option options [Hash] :attributes # attributes of normal precedence to merge # + # @raise [Errors::HTTPNotFound] + # if the target node is not found + # # @return [Ridley::NodeResource] - def merge_data(options = {}) - unless options[:run_list].nil? - self.run_list = (self.run_list + Array(options[:run_list])).uniq - end - - unless options[:attributes].nil? - self.normal = self.normal.deep_merge(options[:attributes]) - end - - self.update - self + def merge_data(target, options = {}) + find(target).merge_data(options) end end end