Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Handle relative ssh key paths in TF JSON #1398

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions e2e/digitalocean/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ resource "tls_private_key" "ssh_key" {

resource "local_file" "ssh_key" {
sensitive_content = "${tls_private_key.ssh_key.private_key_pem}"
filename = "ssh_key.pem"
filename = "${path.module}/ssh_key.pem"
provisioner "local-exec" {
command = "chmod 0600 ${local_file.ssh_key.filename}"
}
Expand Down Expand Up @@ -111,7 +111,7 @@ output "pharos_hosts" {
private_address = "${digitalocean_droplet.pharos_master.*.ipv4_address_private}"
role = "master"
user = "root"
ssh_key_path = "./ssh_key.pem"
ssh_key_path = "${local_file.ssh_key.filename}"

label = {
"beta.kubernetes.io/instance-type" = "${var.worker_size}"
Expand All @@ -133,10 +133,10 @@ output "pharos_hosts" {
private_address = "${digitalocean_droplet.pharos_worker.*.ipv4_address_private}"
role = "worker"
user = "root"
ssh_key_path = "./ssh_key.pem"
ssh_key_path = "${local_file.ssh_key.filename}"
bastion = {
address = "${digitalocean_droplet.pharos_master.*.ipv4_address[0]}"
ssh_key_path = "./ssh_key.pem"
ssh_key_path = "${local_file.ssh_key.filename}"
user = "root"
}

Expand Down
4 changes: 2 additions & 2 deletions e2e/digitalocean/terraform-0.12/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ resource "tls_private_key" "ssh_key" {

resource "local_file" "ssh_key" {
sensitive_content = tls_private_key.ssh_key.private_key_pem
filename = "ssh_key.pem"
filename = "${path.module}/ssh_key.pem"
provisioner "local-exec" {
command = "chmod 0600 ${local_file.ssh_key.filename}"
}
Expand Down Expand Up @@ -126,7 +126,7 @@ output "pharos_cluster" {
private_address = host.droplet.ipv4_address_private
role = host.role
user = "root"
ssh_key_path = "./ssh_key.pem"
ssh_key_path = "${local_file.ssh_key.filename}"

label = {
"beta.kubernetes.io/instance-type" = "${host.droplet.size}"
Expand Down
3 changes: 2 additions & 1 deletion lib/pharos/command_options/load_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Pharos
module CommandOptions
module LoadConfig
using Pharos::CoreExt::Colorize
using Pharos::CoreExt::DeepTransformKeys

def self.included(base)
base.prepend(InstanceMethods)
Expand Down Expand Up @@ -56,7 +57,7 @@ def load_config(master_only: false)

puts("==> Reading instructions ...".green) if $stdout.tty?

config_hash = config_yaml.load(ENV.to_h)
config_hash = config_yaml.load(ENV.to_h).deep_stringify_keys

load_external_config(config_hash)

Expand Down
57 changes: 35 additions & 22 deletions lib/pharos/command_options/tf_json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Pharos
module CommandOptions
module TfJson
using Pharos::CoreExt::Colorize
using Pharos::CoreExt::DeepTransformKeys
using K8s::Util::HashDeepMerge

def self.included(base)
Expand All @@ -12,7 +13,7 @@ def self.included(base)
base.option '--tf-json', 'PATH', 'path to terraform output json' do |config_path|
@config_options ||= []
@config_options.concat(['--tf-json', config_path])
File.realpath(config_path)
File.expand_path(config_path)
rescue Errno::ENOENT
signal_usage_error 'File does not exist: %<path>s' % { path: config_path }
end
Expand All @@ -32,30 +33,42 @@ def load_external_config(config_hash)
def load_terraform(file, config)
puts("==> Importing configuration from Terraform ...".green) if $stdout.tty?

json = File.read(file)
tf_parser = Pharos::Terraform::JsonParser.new(json)
if tf_parser.valid?
config.deep_merge!(
tf_parser.cluster,
overwrite_arrays: false,
union_arrays: true
)
else
tf_parser = Pharos::Terraform::LegacyJsonParser.new(json)
config['hosts'] ||= []
config['api'] ||= {}
config['addons'] ||= {}
config['hosts'].concat(tf_parser.hosts)
config['api'].merge!(tf_parser.api) if tf_parser.api
config['name'] ||= tf_parser.cluster_name if tf_parser.cluster_name
config['addons'].each do |name, conf|
if addon_config = tf_parser.addons[name]
conf.merge!(addon_config)
Dir.chdir(File.dirname(file)) do
json = File.read(file)
tf_parser = Pharos::Terraform::JsonParser.new(json)
if tf_parser.valid?
config.deep_merge!(
tf_parser.cluster,
overwrite_arrays: false,
union_arrays: true
)
else
tf_parser = Pharos::Terraform::LegacyJsonParser.new(json)
config['hosts'] ||= []
config['api'] ||= {}
config['addons'] ||= {}
config['hosts'].concat(tf_parser.hosts)
config['api'].merge!(tf_parser.api) if tf_parser.api
config['name'] ||= tf_parser.cluster_name if tf_parser.cluster_name
tf_parser.addons.each do |name, conf|
if config['addons'][name]
config['addons'][name].merge!(conf)
else
config['addons'][name] = conf
end
end
end
end

config
config.deep_stringify_keys!

config['hosts'].each do |host|
host['ssh_key_path'] = File.expand_path(host['ssh_key_path']) if host['ssh_key_path']

next unless host.dig('bastion', 'ssh_key_path')

host['bastion']['ssh_key_path'] = File.expand_path(host['bastion']['ssh_key_path'])
end
end
end
end
end
Expand Down
9 changes: 7 additions & 2 deletions lib/pharos/config_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ module HostPredicates
predicate(:hostname_or_ip?) do |value|
value.match?(/\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/) || value.match?(/\A[a-z0-9\-\.]+\z/)
end

predicate(:file_exist?) do |value|
File.exist?(File.expand_path(value))
end
end

# @return [Dry::Validation::Schema]
Expand All @@ -56,6 +60,7 @@ def self.messages
errors: {
network_dns_replicas: "network.dns_replicas cannot be larger than the number of hosts",
hostname_or_ip?: "is invalid",
file_exist?: "file %{value} does not exist",
unique_addresses?: "duplicate address:ssh_port"
}
}
Expand Down Expand Up @@ -84,7 +89,7 @@ def unique_addresses?(hosts)
end
end
optional(:user).filled
optional(:ssh_key_path).filled
optional(:ssh_key_path).filled(:str?, :file_exist?)
optional(:ssh_port).filled(:int?, gt?: 0, lt?: 65_536)
optional(:ssh_proxy_command).filled(:str?)
optional(:container_runtime).filled(included_in?: ['docker', 'custom_docker', 'cri-o'])
Expand All @@ -93,7 +98,7 @@ def unique_addresses?(hosts)
predicates(HostPredicates)
required(:address).filled(:str?, :hostname_or_ip?)
optional(:user).filled(:str?)
optional(:ssh_key_path).filled(:str?)
optional(:ssh_key_path).filled(:str?, :file_exist?)
optional(:ssh_port).filled(:int?, gt?: 0, lt?: 65_536)
optional(:ssh_proxy_command).filled(:str?)
end
Expand Down
12 changes: 6 additions & 6 deletions lib/pharos/exec_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ class ExecCommand < Pharos::Command
banner "Opens SSH sessions to hosts in the Kontena Pharos cluster."

def execute
if command_list.empty?
signal_usage_error 'interactive mode can not be used with a non-interactive terminal' unless $stdin.tty? && $stdout.tty?
run_interactive
exit 0
end

Dir.chdir(config_yaml.dirname) do
if command_list.empty?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config parsing in ssh/exec interactive mode was not done in config_yaml.dirname context.

signal_usage_error 'interactive mode can not be used with a non-interactive terminal' unless $stdin.tty? && $stdout.tty?
run_interactive
exit 0
end

filtered_hosts.each { |host| host.transport.connect }

exit run_single(filtered_hosts.first) if filtered_hosts.size == 1
Expand Down