Skip to content

Commit

Permalink
Merge pull request #33 from opscode/sid-oc-9533-chef-data
Browse files Browse the repository at this point in the history
OC-9533 knife CLOUD server list needs to expose Chef data (node names, attributes)
  • Loading branch information
Matt Ray committed Aug 30, 2013
2 parents 3c211f1 + 0caa92e commit fe75abd
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 10 deletions.
26 changes: 16 additions & 10 deletions lib/chef/knife/cloud/list_resource_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,30 @@ def is_resource_filtered?(attribute, value)
end
false
end

# Derived class can override this to add more functionality.
def get_resource_col_val(resource)
resource_filtered = false
list = []
@columns_with_info.each do |col_info|
value = (col_info[:value_callback].nil? ? resource.send(col_info[:key]).to_s : col_info[:value_callback].call(resource.send(col_info[:key])))
if !config[:disable_filter]
resource_filtered = true if is_resource_filtered?(col_info[:key], value)
end
list << value
end
return list unless resource_filtered
end

# When @columns_with_info is nil display all
def list(resources)
# display column wise only if @columns_with_info is specified, else as a json for readable display.
begin
resource_list = @columns_with_info.map { |col_info| ui.color(col_info[:label], :bold) } if @columns_with_info.length > 0
resources.sort_by(&@sort_by_field.to_sym).each do |resource|
resource_filtered = false
if @columns_with_info.length > 0
list = []
@columns_with_info.each do |col_info|
value = (col_info[:value_callback].nil? ? resource.send(col_info[:key]).to_s : col_info[:value_callback].call(resource.send(col_info[:key])))
if !config[:disable_filter]
resource_filtered = true if is_resource_filtered?(col_info[:key], value)
end
list << value
end
resource_list.concat(list) unless resource_filtered
list = get_resource_col_val(resource)
resource_list.concat(list) unless list.nil?
else
puts resource.to_json
puts "\n"
Expand Down
53 changes: 53 additions & 0 deletions lib/chef/knife/cloud/server/list_command.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,63 @@

require 'chef/knife/cloud/list_resource_command'
require 'chef/knife/cloud/exceptions'

class Chef
class Knife
class Cloud
class ServerListCommand < ResourceListCommand

def before_exec_command
if config[:chef_data]
begin
# Chef::Node.list(inflate = true) to use Solr search.
@node_list = Chef::Node.list(true)
rescue Errno::ECONNREFUSED => e
raise e
end

@chef_data_col_info = [
{:label => 'Chef Node Name', :key => 'name'},
{:label => 'Environment', :key => 'chef_environment'},
{:label => 'FQDN', :key => 'fqdn'},
{:label => 'Runlist', :key => 'run_list'},
{:label => 'Tags', :key => 'tags'},
{:label => 'Platform', :key => 'platform'},
]

if config[:chef_node_attribute]
@chef_data_col_info << {:label => "#{config[:chef_node_attribute]}", :key => "#{config[:chef_node_attribute]}"}
end
@columns_with_info.concat(@chef_data_col_info)
end
end

# Override from base to display chef node data along with server list display.
def get_resource_col_val(server)
list = []
@columns_with_info.each do |col_info|
if config[:chef_data] && @chef_data_col_info.include?(col_info)
if @node_list.include?(server.name)
node = @node_list[server.name]
# Raise serverlisting error on invalid chef_node_attribute.
if col_info[:key] == config[:chef_node_attribute] && ! node.attribute?(col_info[:key])
error_message = "The Node does not have a #{col_info[:key]} attribute."
ui.error(error_message)
raise CloudExceptions::ServerListingError, error_message
else
value = (col_info[:value_callback].nil? ? node.send(col_info[:key]).to_s : col_info[:value_callback].call(node.send(col_info[:key])))
end
else
# Set chef data value for those server which is not part chef server.
value = ""
end
else
value = (col_info[:value_callback].nil? ? server.send(col_info[:key]).to_s : col_info[:value_callback].call(server.send(col_info[:key])))
end
list << value
end
list
end

def query_resource
@service.list_servers
Expand Down
43 changes: 43 additions & 0 deletions lib/chef/knife/cloud/server/list_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#
# Author:: Siddheshwar More (<siddheshwar.more@clogeny.com>)
# Copyright:: Copyright (c) 2013 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

class Chef
class Knife
class Cloud
module ServerListOptions
def self.included(includer)
includer.class_eval do

option :chef_data,
:long => "--chef-data",
:boolean => true,
:default => false,
:description => "Display chef node data which include chef node name, environment name, fqdn, platform, runlist and tags."

option :chef_node_attribute,
:long => "--chef-node-attribute CHEF_NODE_ATTRIBUTE_NAME",
:description => "Used with --chef-data option. It display node attributes details by adding new column in server list display.",
:proc => Proc.new { |i| Chef::Config[:knife][:chef_node_attribute] = i }

end
end
end
end
end
end

75 changes: 75 additions & 0 deletions spec/unit/server_list_command_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Author:: Siddheshwar More (<siddheshwar.more@clogeny.com>)
# Copyright:: Copyright (c) 2013 Opscode, Inc.

require 'spec_helper'
require 'support/shared_examples_for_command'
require 'chef/knife/cloud/server/list_command'
require 'chef/node'

describe Chef::Knife::Cloud::ServerListCommand do
it_behaves_like Chef::Knife::Cloud::Command, Chef::Knife::Cloud::ServerListCommand.new

describe "#before_exec_command" do
it "set chef data columns info on chef data options" do
instance = Chef::Knife::Cloud::ServerListCommand.new
instance.config[:chef_data] = true
Chef::Node.should_receive(:list).with(true)
instance.before_exec_command.should include({:label => "Chef Node Name", :key => "name"})
end

it "set chef data columns info on chef-data and chef-node-attribute options" do
chef_node_attribute = "platform_family"
instance = Chef::Knife::Cloud::ServerListCommand.new
instance.config[:chef_data] = true
instance.config[:chef_node_attribute] = chef_node_attribute
Chef::Node.should_receive(:list).with(true)
instance.before_exec_command.should include({:label => chef_node_attribute, :key => chef_node_attribute})
end

it "not set chef data columns info if chef-data option is not set" do
instance = Chef::Knife::Cloud::ServerListCommand.new
Chef::Node.should_not_receive(:list).with(true)
instance.before_exec_command.should be(nil)
end

it "not set chef data columns info on chef-node-attribute option set but chef-data option is not set" do
instance = Chef::Knife::Cloud::ServerListCommand.new
instance.config[:chef_node_attribute] = "platform_family"
Chef::Node.should_not_receive(:list).with(true)
instance.before_exec_command.should be(nil)
end
end

describe "#get_resource_col_val" do
let (:resources) {[ TestResource.new({:id => "server-1", :name => "server-1", :os => "ubuntu"})]}
before do
class DerivedServerList < Chef::Knife::Cloud::ServerListCommand
attr_accessor :node
def before_exec_command
@columns_with_info = [ { :key => 'id', :label => 'Instance ID' }, {:label => 'Environment', :key => 'chef_environment'}, {:label => 'platform_family', :key => 'platform_family'} ]
@chef_data_col_info = [ {:label => 'Environment', :key => 'chef_environment'}, {:label => 'platform_family', :key => 'platform_family'} ]
@node = TestResource.new({:id => "server-1", :name => "server-1",
:chef_environment => "_default", :platform_family => "debian"})
@node.define_singleton_method(:attribute?) do |attribute|
end
@node_list = {"server-1" => @node}
end
end
@derived_instance = DerivedServerList.new
@derived_instance.config[:chef_data] = true
@derived_instance.config[:chef_node_attribute] = "platform_family"
@derived_instance.before_exec_command
end

it "return columns_with_info values" do
@derived_instance.node.should_receive(:attribute?).with("platform_family").and_return(true)
@derived_instance.get_resource_col_val(resources.first).should eq(["server-1", "_default", "debian"])
end

it "raise error on invalide chef_node_attribute" do
@derived_instance.ui.stub(:error)
@derived_instance.node.should_receive(:attribute?).with("platform_family").and_return(false)
expect { @derived_instance.get_resource_col_val(resources.first) }.to raise_error(Chef::Knife::Cloud::CloudExceptions::ServerListingError, "The Node does not have a platform_family attribute.")
end
end
end

0 comments on commit fe75abd

Please sign in to comment.