summaryrefslogtreecommitdiff
path: root/lib/chef/knife
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2018-07-13 10:37:34 -0700
committerGitHub <noreply@github.com>2018-07-13 10:37:34 -0700
commitd99bc319e244e4d4fac57bc1853ea82f8b97c0e9 (patch)
tree77eac01f6baac6486308a30c8c3982f03309fb39 /lib/chef/knife
parent8baff8ae8f6dc0e88c1a467566b1ac595ceef267 (diff)
parent6fb425078b5986998c37450445f5b3ded61e02ac (diff)
downloadchef-d99bc319e244e4d4fac57bc1853ea82f8b97c0e9.tar.gz
Merge pull request #7455 from coderanger/config-commands
Add knife config get/use-profile commands
Diffstat (limited to 'lib/chef/knife')
-rw-r--r--lib/chef/knife/config_get_profile.rb37
-rw-r--r--lib/chef/knife/config_list_profiles.rb121
-rw-r--r--lib/chef/knife/config_use_profile.rb50
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb3
4 files changed, 210 insertions, 1 deletions
diff --git a/lib/chef/knife/config_get_profile.rb b/lib/chef/knife/config_get_profile.rb
new file mode 100644
index 0000000000..309b7f81e8
--- /dev/null
+++ b/lib/chef/knife/config_get_profile.rb
@@ -0,0 +1,37 @@
+#
+# 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"
+
+class Chef
+ class Knife
+ class ConfigGetProfile < Knife
+ banner "knife config get-profile"
+
+ # Disable normal config loading since this shouldn't fail if the profile
+ # doesn't exist of the config is otherwise corrupted.
+ def configure_chef
+ apply_computed_config
+ end
+
+ def run
+ ui.msg(self.class.config_loader.credentials_profile(config[:profile]))
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/knife/config_list_profiles.rb b/lib/chef/knife/config_list_profiles.rb
new file mode 100644
index 0000000000..16b0c5df27
--- /dev/null
+++ b/lib/chef/knife/config_list_profiles.rb
@@ -0,0 +1,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
diff --git a/lib/chef/knife/config_use_profile.rb b/lib/chef/knife/config_use_profile.rb
new file mode 100644
index 0000000000..515c4a5336
--- /dev/null
+++ b/lib/chef/knife/config_use_profile.rb
@@ -0,0 +1,50 @@
+#
+# 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 "fileutils"
+
+require "chef/knife"
+
+class Chef
+ class Knife
+ class ConfigUseProfile < Knife
+ banner "knife config use-profile PROFILE"
+
+ # Disable normal config loading since this shouldn't fail if the profile
+ # doesn't exist of the config is otherwise corrupted.
+ def configure_chef
+ apply_computed_config
+ end
+
+ def run
+ context_file = ChefConfig::PathHelper.home(".chef", "context").freeze
+ profile = @name_args[0]&.strip
+ if profile && !profile.empty?
+ # Ensure the .chef/ folder exists.
+ FileUtils.mkdir_p(File.dirname(context_file))
+ IO.write(context_file, "#{profile}\n")
+ ui.msg("Set default profile to #{profile}")
+ else
+ show_usage
+ ui.fatal("You must specify a profile")
+ exit 1
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb
index 026967d6ec..fb3723de50 100644
--- a/lib/chef/knife/core/subcommand_loader.rb
+++ b/lib/chef/knife/core/subcommand_loader.rb
@@ -139,9 +139,10 @@ class Chef
# hash composed of the given words joined by the separator.
#
def find_longest_key(hash, words, sep = "_")
+ words = words.dup
match = nil
until match || words.empty?
- candidate = words.join(sep)
+ candidate = words.join(sep).tr("-", "_")
if hash.key?(candidate)
match = candidate
else