1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#
# Copyright:: Copyright (c) 2018, Noah Kantrowitz
# 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.
#
require "chef/knife"
require "chef/workstation_config_loader"
class Chef
class Knife
class ConfigListProfiles < Knife
banner "knife config list-profiles"
option :ignore_knife_rb,
short: "-i",
long: "--ignore-knife-rb",
description: "Ignore the current knife.rb configuration.",
default: false
def run
credentials_data = self.class.config_loader.parse_credentials_file
if credentials_data.nil? || credentials_data.empty?
# Should this just show the ambient knife.rb config as "default" instead?
ui.fatal("No profiles found, #{self.class.config_loader.credentials_file_path} does not exist or is empty")
exit 1
end
current_profile = self.class.config_loader.credentials_profile(config[:profile])
profiles = credentials_data.keys.map do |profile|
if config[:ignore_knife_rb]
# Don't do any fancy loading nonsense, just the raw data.
profile_data = credentials_data[profile]
{
profile: profile,
active: profile == current_profile,
client_name: profile_data["client_name"] || profile_data["node_name"],
client_key: profile_data["client_key"],
server_url: profile_data["chef_server_url"],
}
else
# Fancy loading nonsense so we get what the actual config would be.
# Note that this modifies the global config, after this, all bets are
# off as to whats in the config.
Chef::Config.reset
wcl = Chef::WorkstationConfigLoader.new(nil, Chef::Log, profile: profile)
wcl.load
{
profile: profile,
active: profile == current_profile,
client_name: Chef::Config[:node_name],
client_key: Chef::Config[:client_key],
server_url: Chef::Config[:chef_server_url],
}
end
end
# Try to reset the config.
unless config[:ignore_knife_rb]
Chef::Config.reset
Chef::WorkstationConfigLoader.new(config[:config_file], Chef::Log, profile: config[:profile]).load
apply_computed_config
end
if ui.interchange?
# Machine-readable output.
ui.output(profiles)
else
# Table output.
ui.output(render_table(profiles))
end
end
private
def render_table(profiles, padding: 2)
# Replace the home dir in the client key path with ~.
profiles.each do |profile|
profile[:client_key] = profile[:client_key].to_s.gsub(/^#{Regexp.escape(Dir.home)}/, "~") if profile[:client_key]
end
# Render the data to a 2D array that will be used for the table.
table_data = [["", "Profile", "Client", "Key", "Server"]] + profiles.map do |profile|
[profile[:active] ? "*" : ""] + profile.values_at(:profile, :client_name, :client_key, :server_url).map(&:to_s)
end
# Compute column widths.
column_widths = Array.new(table_data.first.length) do |i|
table_data.map { |row| row[i].length + padding }.max
end
# Special case, the first col gets no padding (because indicator) and last
# get no padding because last.
column_widths[0] -= padding
column_widths[-1] -= padding
# Build the format string for each row.
format_string = column_widths.map { |w| "%-#{w}.#{w}s" }.join("")
format_string << "\n"
# Print the header row and a separator.
table = ui.color(format_string % table_data.first, :green)
table << "-" * column_widths.sum
table << "\n"
# Print the rest of the table.
table_data.drop(1).each do |row|
table << format_string % row
end
# Trim the last newline because ui.output adds one.
table.chomp!
end
end
end
end
|