├── .gitignore ├── lib ├── knife-lastrun │ └── version.rb ├── lastrun_update.rb └── chef │ └── knife │ └── lastrun.rb ├── knife-lastrun.gemspec ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | -------------------------------------------------------------------------------- /lib/knife-lastrun/version.rb: -------------------------------------------------------------------------------- 1 | module Knife 2 | module NodeLastrun 3 | VERSION = '0.0.8' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /knife-lastrun.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path('../lib', __FILE__) 3 | require 'knife-lastrun/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'knife-lastrun' 7 | s.version = Knife::NodeLastrun::VERSION 8 | s.date = '2017-07-06' 9 | s.summary = 'A plugin for Chef::Knife which displays node metadata about the last chef run.' 10 | s.description = s.summary 11 | s.authors = ['John Goulah'] 12 | s.email = ['jgoulah@gmail.com'] 13 | s.homepage = 'https://github.com/jgoulah/knife-lastrun' 14 | s.files = `git ls-files`.split("\n") 15 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 16 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 17 | s.require_paths = ['lib'] 18 | end 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 John Goulah 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # knife-lastrun 2 | 3 | A plugin for Chef::Knife which displays node metadata about the last chef run. 4 | 5 | ## Usage 6 | 7 | Supply a node name to get metrics from the last chef run 8 | 9 | ``` 10 | % knife node lastrun jgoulah.vm.mydomain.com 11 | Status success 12 | Elapsed Time 74.198614 13 | Start Time 2012-02-22 00:39:04 +0000 14 | End Time 2012-02-22 00:40:18 +0000 15 | 16 | Recipe Action Resource Type Resource 17 | bigdata::default run bash check for bashrc 18 | 19 | Backtrace none 20 | Exception none 21 | Formatted Exception none 22 | ``` 23 | 24 | ## Installation 25 | 26 | #### Gem install 27 | 28 | knife-lastrun is available on rubygems.org - if you have that source in your gemrc, you can simply use: 29 | 30 | gem install knife-lastrun 31 | 32 | #### Configure the Handler 33 | 34 | in /etc/chef/client.rb 35 | 36 | ``` 37 | require "lastrun_update" 38 | handler = LastRunUpdateHandler.new 39 | report_handlers << handler 40 | exception_handlers << handler 41 | ``` 42 | -------------------------------------------------------------------------------- /lib/lastrun_update.rb: -------------------------------------------------------------------------------- 1 | require 'chef/log' 2 | 3 | class LastRunUpdateHandler < Chef::Handler 4 | 5 | def report 6 | _node = Chef::Node.load(node.name) 7 | _node.normal[:lastrun] = {} 8 | 9 | _node.normal[:lastrun][:status] = run_status.success? ? 'success' : 'failed' 10 | 11 | _node.normal[:lastrun][:runtimes] = {} 12 | _node.normal[:lastrun][:runtimes][:elapsed] = run_status.elapsed_time 13 | _node.normal[:lastrun][:runtimes][:start] = run_status.start_time 14 | _node.normal[:lastrun][:runtimes][:end] = run_status.end_time 15 | 16 | _node.normal[:lastrun][:debug] = {} 17 | _node.normal[:lastrun][:debug][:backtrace] = run_status.backtrace 18 | _node.normal[:lastrun][:debug][:exception] = run_status.exception 19 | _node.normal[:lastrun][:debug][:formatted_exception] = run_status.formatted_exception 20 | 21 | _node.normal[:lastrun][:updated_resources] = [] 22 | Array(run_status.updated_resources).each do |resource| 23 | m = "recipe[#{resource.cookbook_name}::#{resource.recipe_name}] ran '#{resource.action}' on #{resource.resource_name} '#{resource.name}'" 24 | Chef::Log.debug(m) 25 | 26 | _node.normal[:lastrun][:updated_resources].insert(0, { 27 | :cookbook_name => resource.cookbook_name, 28 | :recipe_name => resource.recipe_name, 29 | :action => resource.action, 30 | :resource => resource.name, 31 | :resource_type => resource.resource_name 32 | }) 33 | 34 | end 35 | 36 | # Save attributes to node unless overridden runlist has been supplied 37 | if Chef::Config.override_runlist 38 | Chef::Log.warn('Skipping final node save because override_runlist was given') 39 | else 40 | _node.save 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/chef/knife/lastrun.rb: -------------------------------------------------------------------------------- 1 | module GoulahPlugins 2 | class NodeLastrun < Chef::Knife 3 | 4 | banner 'knife node lastrun NODE' 5 | 6 | deps do 7 | require 'highline' 8 | end 9 | 10 | def h 11 | @highline ||= HighLine.new 12 | end 13 | 14 | def run 15 | unless @node_name = name_args[0] 16 | ui.error "You need to specify a node" 17 | exit 1 18 | end 19 | 20 | node = Chef::Node.load(@node_name) 21 | if node.nil? 22 | ui.msg "Could not find a node named #{@node_name}" 23 | exit 1 24 | end 25 | 26 | unless node[:lastrun] 27 | ui.msg "no information found for last run on #{@node_name}" 28 | exit 29 | end 30 | 31 | # time 32 | time_entries = header('Status', 'Elapsed Time', 'Start Time', 'End Time'); 33 | 34 | time_entries << node[:lastrun][:status] 35 | [:elapsed, :start, :end].each do |time| 36 | time_entries << node[:lastrun][:runtimes][time].to_s 37 | end 38 | ui.msg h.list(time_entries, :columns_down, 2) 39 | ui.msg "\n" 40 | 41 | # updated resources 42 | log_entries = header('Recipe', 'Action', 'Resource Type', 'Resource'); 43 | 44 | node[:lastrun][:updated_resources].each do |log_entry| 45 | log_entries << "#{log_entry[:cookbook_name]}::#{log_entry[:recipe_name]}" 46 | [:action, :resource_type, :resource].each do |entry| 47 | log_entries << log_entry[entry].to_s 48 | end 49 | end 50 | 51 | ui.msg h.list(log_entries, :uneven_columns_across, 4) 52 | ui.msg "\n" 53 | 54 | # debug stuff 55 | debug_entries = [] 56 | debug_entries << h.color('Backtrace', :bold) 57 | debug_entries << (node[:lastrun][:debug][:backtrace] ? node[:lastrun][:debug][:backtrace].join("\n") : "none") 58 | debug_entries << "" 59 | 60 | debug_entries << h.color('Exception', :bold) 61 | debug_entries << (node[:lastrun][:debug][:formatted_exception] ? node[:lastrun][:debug][:formatted_exception].strip : "none") 62 | ui.msg h.list(debug_entries, :rows) 63 | ui.msg "\n" 64 | 65 | end 66 | 67 | def header(*args) 68 | entry = [] 69 | args.each do |arg| 70 | entry << h.color(arg, :bold) 71 | end 72 | entry 73 | end 74 | end 75 | end 76 | --------------------------------------------------------------------------------