From patchwork Mon Feb 24 19:12:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuck Lever X-Patchwork-Id: 13988834 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5F591EDA26 for ; Mon, 24 Feb 2025 19:12:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740424341; cv=none; b=mTmJqYrVK/UKp2Cl69WDHLx2F75M6IoXElqgoAEOZIegkthLFwWxBEUcQk7oup3iEF4Dy5H6Sn4JxuS9AawyuxDheXXXZAkOywA5M6sJVF+VEV2/zTwmiYFK8lHrWWkpIqKDBM2pN8+qTdjpPe7yWqeAW6d0ypkqMQ8ENdS6hik= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740424341; c=relaxed/simple; bh=BPDKvH08vET7G8QQbDgwfMnnhqdtg1iClgZdkK4h0xI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=srSfLOrDES9jZWQ1RUZXcoJ2wnNArRiZc7qv5RJaSynrTIfaKYkzn8ggS2Z7aBPbDney5PgPJqkbePRCkn7uCh0/DN00wjUZPBb4j3DBlRCzPTh2Ql+y1H9NTVgPbJNCbsF66lDGURkMfwfjg8R+8UAt1cR2oJLjWL3fWuzyQYw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EsweWgsF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EsweWgsF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6437FC4CEDD; Mon, 24 Feb 2025 19:12:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740424340; bh=BPDKvH08vET7G8QQbDgwfMnnhqdtg1iClgZdkK4h0xI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EsweWgsFfFJ28p/XE6aBuO9oPZhbXDV80R2EGrSvcZCSH4dBtHGXCMUuGvQja1T6Y CyIaXJ180oExO9Tf/4nwA3jwn8Y9aTsTXTMdCn/sCS85UWpFEs3A7PujMS8REC7kXY S6l/f/hNICfeAy/fnD2sf1SepMYsnXmUJpgdUtal+etxS2eLf2WAIUj7xuRKqssRfb hwWYUhtGJLS63Db7q0FjYGwy+16B0sxN2Gl6Hfppdqn5V0pELMPRWpDQtVVw/1eDfr VqDIZTzDOlt0CRlejhnNYEo9F9rtrg6IzFTtD2U6uLUB9jR8BWuJeGqMSLJozPeCvg W65f5IJn25jcA== From: cel@kernel.org To: Cc: Chuck Lever , Luis Chamberlain Subject: [PATCH v3 4/6] terraform: Add ssh hosts to ~/.ssh/config_kdevops_{{ sha1sum }} Date: Mon, 24 Feb 2025 14:12:13 -0500 Message-ID: <20250224191215.637818-5-cel@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250224191215.637818-1-cel@kernel.org> References: <20250224191215.637818-1-cel@kernel.org> Precedence: bulk X-Mailing-List: kdevops@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chuck Lever The fixed update_ssh_config module is still not removing ssh Host configuration information with "make destroy". Also, we want to have more control over how the control host's ssh config is managed. Updating a separately maintained terraform module is getting awkward. Replace the independent terraform module that handles ssh configuration with tasks in a playbook that operate the same as guestfs: the host config is stuffed into a common file under ~/.ssh that is included in ~/.ssh/config, and then is easily located and deleted by "make destroy". Suggested-by: Luis Chamberlain Signed-off-by: Chuck Lever --- playbooks/roles/terraform/defaults/main.yml | 3 + playbooks/roles/terraform/tasks/main.yml | 59 +++++++++++++++++++ .../roles/terraform/templates/ssh_config.j2 | 15 +++++ scripts/terraform.Makefile | 21 +++++-- terraform/aws/output.tf | 7 +++ terraform/azure/output.tf | 8 +++ terraform/gce/output.tf | 8 +++ terraform/oci/output.tf | 9 +++ terraform/openstack/output.tf | 7 +++ 9 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 playbooks/roles/terraform/defaults/main.yml create mode 100644 playbooks/roles/terraform/templates/ssh_config.j2 create mode 100644 terraform/oci/output.tf diff --git a/playbooks/roles/terraform/defaults/main.yml b/playbooks/roles/terraform/defaults/main.yml new file mode 100644 index 000000000000..f3ef9b18c6cd --- /dev/null +++ b/playbooks/roles/terraform/defaults/main.yml @@ -0,0 +1,3 @@ +--- +ssh_config_kexalgorithms: "" +kdevops_terraform_ssh_config_genkey_overwrite: false diff --git a/playbooks/roles/terraform/tasks/main.yml b/playbooks/roles/terraform/tasks/main.yml index 31c97fdb78b1..272ebf93a355 100644 --- a/playbooks/roles/terraform/tasks/main.yml +++ b/playbooks/roles/terraform/tasks/main.yml @@ -7,6 +7,65 @@ tags: - bringup +- name: Retrieve the public_ip_map from terraform + delegate_to: localhost + run_once: true + ansible.builtin.command: + chdir: "{{ topdir_path }}/terraform/{{ kdevops_terraform_provider }}" + cmd: "terraform output -json public_ip_map" + register: terraform_output + changed_when: false + when: + - kdevops_terraform_ssh_config_genkey_overwrite|bool + tags: + - ssh + +- name: Convert the retrieved public_ip_map into a dictionary + delegate_to: localhost + run_once: true + ansible.builtin.set_fact: + public_ip_map: "{{ terraform_output.stdout | from_json }}" + when: + - kdevops_terraform_ssh_config_genkey_overwrite|bool + tags: + - ssh + +- name: Add each target node's ssh Host entry on the control host + delegate_to: localhost + throttle: 1 + ansible.builtin.blockinfile: + block: "{{ lookup('template', 'ssh_config.j2') }}" + create: true + dest: "{{ kdevops_ssh_config }}" + insertafter: "EOF" + marker: "# {mark} host configuration for {{ inventory_hostname }}" + mode: "u=rw,g=r,o=r" + when: + - kdevops_terraform_ssh_config_genkey_overwrite|bool + tags: + - ssh + +- name: Ensure the Include directive is present on the controller + delegate_to: localhost + run_once: true + ansible.builtin.blockinfile: + path: "{{ sshconfig }}" + insertbefore: BOF + append_newline: true + marker: "# {mark} Managed by kdevops" + mode: "u=rw,g=r,o=r" + block: "Include {{ kdevops_ssh_config_prefix }}*" + when: + - kdevops_terraform_ssh_config_genkey_overwrite|bool + tags: + - ssh + +- name: Wait for target nodes to become reachable + ansible.builtin.wait_for_connection: + timeout: 60 + tags: + - ssh + - name: Destroy terraform resources delegate_to: localhost run_once: true diff --git a/playbooks/roles/terraform/templates/ssh_config.j2 b/playbooks/roles/terraform/templates/ssh_config.j2 new file mode 100644 index 000000000000..d9b057b13da8 --- /dev/null +++ b/playbooks/roles/terraform/templates/ssh_config.j2 @@ -0,0 +1,15 @@ +Host {{ inventory_hostname }} {{ public_ip_map[inventory_hostname] }} + HostName {{ public_ip_map[inventory_hostname] }} + User {{ kdevops_terraform_ssh_config_user }} + Port 22 + IdentityFile {{ kdevops_terraform_ssh_config_privkey_file }} +{% if ssh_config_kexalgorithms %} + KexAlgorithms {{ ssh_config_kexalgorithms }} +{% endif %} +{% if kdevops_terraform_ssh_config_update_strict %} + UserKnownHostsFile /dev/null + StrictHostKeyChecking no + PasswordAuthentication no + IdentitiesOnly yes + LogLevel FATAL +{% endif %} diff --git a/scripts/terraform.Makefile b/scripts/terraform.Makefile index f22538721a55..6543da89a17f 100644 --- a/scripts/terraform.Makefile +++ b/scripts/terraform.Makefile @@ -25,9 +25,10 @@ KDEVOPS_NODES_TEMPLATE := $(KDEVOPS_NODES_ROLE_TEMPLATE_DIR)/terraform_nodes.tf. KDEVOPS_NODES := terraform/$(KDEVOPS_CLOUD_PROVIDER)/nodes.tf TERRAFORM_EXTRA_VARS += kdevops_enable_terraform='True' - TERRAFORM_EXTRA_VARS += kdevops_terraform_provider='$(KDEVOPS_CLOUD_PROVIDER)' +export KDEVOPS_PROVISIONED_SSH := $(KDEVOPS_PROVISIONED_SSH_DEFAULT_GUARD) + TFVARS_TEMPLATE_DIR=playbooks/roles/gen_tfvars/templates TFVARS_FILE_NAME=terraform.tfvars TFVARS_FILE_POSTFIX=$(TFVARS_FILE_NAME).j2 @@ -145,17 +146,17 @@ endif endif # CONFIG_KDEVOPS_SSH_CONFIG_UPDATE -TERRAFORM_EXTRA_VARS += kdevops_terraform_ssh_config_pubkey_file='$(subst ",,$(CONFIG_TERRAFORM_SSH_CONFIG_PUBKEY_FILE))' -TERRAFORM_EXTRA_VARS += kdevops_terraform_ssh_config_user='$(subst ",,$(CONFIG_TERRAFORM_SSH_CONFIG_USER))' +export KDEVOPS_SSH_PUBKEY:=$(shell realpath $(subst ",,$(CONFIG_TERRAFORM_SSH_CONFIG_PUBKEY_FILE))) +TERRAFORM_EXTRA_VARS += kdevops_terraform_ssh_config_pubkey_file='$(KDEVOPS_SSH_PUBKEY)' +TERRAFORM_EXTRA_VARS += kdevops_terraform_ssh_config_user='$(SSH_CONFIG_USER)' ifeq (y,$(CONFIG_TERRAFORM_SSH_CONFIG_GENKEY)) -export KDEVOPS_SSH_PUBKEY:=$(subst ",,$(CONFIG_TERRAFORM_SSH_CONFIG_PUBKEY_FILE)) -# We have to do shell expansion. Oh, life is so hard. -export KDEVOPS_SSH_PUBKEY:=$(subst ~,$(HOME),$(KDEVOPS_SSH_PUBKEY)) export KDEVOPS_SSH_PRIVKEY:=$(basename $(KDEVOPS_SSH_PUBKEY)) +TERRAFORM_EXTRA_VARS += kdevops_terraform_ssh_config_privkey_file='$(KDEVOPS_SSH_PRIVKEY)' ifeq (y,$(CONFIG_TERRAFORM_SSH_CONFIG_GENKEY_OVERWRITE)) DEFAULT_DEPS += remove-ssh-key +TERRAFORM_EXTRA_VARS += kdevops_terraform_ssh_config_genkey_overwrite='True' endif DEFAULT_DEPS += $(KDEVOPS_SSH_PRIVKEY) @@ -170,6 +171,14 @@ bringup_terraform: --extra-vars=@./extra_vars.yaml \ -e 'ansible_python_interpreter=/usr/bin/python3' +$(KDEVOPS_PROVISIONED_SSH): + $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + -i $(KDEVOPS_HOSTFILE) \ + playbooks/terraform.yml --tags ssh \ + --extra-vars=@./extra_vars.yaml \ + -e 'ansible_python_interpreter=/usr/bin/python3' + $(Q)touch $(KDEVOPS_PROVISIONED_SSH) + destroy_terraform: $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ --connection=local -i $(KDEVOPS_HOSTFILE) \ diff --git a/terraform/aws/output.tf b/terraform/aws/output.tf index 6ff195be2515..cb8cab4afcdd 100644 --- a/terraform/aws/output.tf +++ b/terraform/aws/output.tf @@ -25,3 +25,10 @@ output "login_using" { value = data.null_data_source.group_hostnames_and_ips.*.outputs } +# Each provider's output.tf needs to define a public_ip_map. This +# map is used to build the Ansible controller's ssh configuration. +# Each map entry contains the node's hostname and public IP address. +output "public_ip_map" { + description = "The public IP addresses assigned to each instance" + value = "${zipmap(var.kdevops_nodes[*], aws_eip.kdevops_eip[*].public_ip)}" +} diff --git a/terraform/azure/output.tf b/terraform/azure/output.tf index 5a2654970011..22dfa2f0736d 100644 --- a/terraform/azure/output.tf +++ b/terraform/azure/output.tf @@ -37,3 +37,11 @@ data "null_data_source" "group_hostnames_and_ips" { output "login_using" { value = data.null_data_source.group_hostnames_and_ips.*.outputs } + +# Each provider's output.tf needs to define a public_ip_map. This +# map is used to build the Ansible controller's ssh configuration. +# Each map entry contains the node's hostname and public IP address. +output "public_ip_map" { + description = "The public IP addresses assigned to each instance" + value = "${zipmap(var.kdevops_nodes[*], azurerm_public_ip.kdevops_publicip[*].ip_address)}" +} diff --git a/terraform/gce/output.tf b/terraform/gce/output.tf index 7b96c829173b..b95667cc7efd 100644 --- a/terraform/gce/output.tf +++ b/terraform/gce/output.tf @@ -24,3 +24,11 @@ data "null_data_source" "group_hostnames_and_ips" { output "login_using" { value = data.null_data_source.group_hostnames_and_ips.*.outputs } + +# Each provider's output.tf needs to define a public_ip_map. This +# map is used to build the Ansible controller's ssh configuration. +# Each map entry contains the node's hostname and public IP address. +output "public_ip_map" { + description = "The public IP addresses assigned to each instance" + value = "${zipmap(var.kdevops_nodes[*], local.ipv4s[*])}" +} diff --git a/terraform/oci/output.tf b/terraform/oci/output.tf new file mode 100644 index 000000000000..91ef37101c6b --- /dev/null +++ b/terraform/oci/output.tf @@ -0,0 +1,9 @@ +# All generic output goes here + +# Each provider's output.tf needs to define a public_ip_map. This +# map is used to build the Ansible controller's ssh configuration. +# Each map entry contains the node's hostname and public IP address. +output "public_ip_map" { + description = "The public IP addresses assigned to each instance" + value = "${zipmap(var.kdevops_nodes[*], oci_core_instance.kdevops_instance.*.public_ip)}" +} diff --git a/terraform/openstack/output.tf b/terraform/openstack/output.tf index 148343561ae5..aff44d1b45f9 100644 --- a/terraform/openstack/output.tf +++ b/terraform/openstack/output.tf @@ -16,3 +16,10 @@ output "kdevops_hosts_and_ipv4" { value = data.null_data_source.group_hostnames_and_ips.*.outputs } +# Each provider's output.tf needs to define a public_ip_map. This +# map is used to build the Ansible controller's ssh configuration. +# Each map entry contains the node's hostname and public IP address. +output "public_ip_map" { + description = "The public IP addresses assigned to each instance" + value = "${zipmap(var.kdevops_nodes[*], openstack_compute_instance_v2.kdevops_instances[*].access_ip_v4)}" +}