From 358d94588e21180d4006d70203541981f8f1c2f8 Mon Sep 17 00:00:00 2001 From: Michail Resvanis Date: Fri, 5 Jan 2024 18:05:04 +0100 Subject: [PATCH 01/11] Add hostname argument to config Signed-off-by: Michail Resvanis --- run_seed.sh | 2 ++ src/config.rs | 8 ++++++++ src/config/cli.rs | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/run_seed.sh b/run_seed.sh index e6abce6e..556a678d 100755 --- a/run_seed.sh +++ b/run_seed.sh @@ -93,6 +93,7 @@ use_cert_rules: BgIejfD1dYW2Fp02z5sF6Pw6vhobpfDYgsTAKNonh5P6NxMiD14eQxYrNJ6DAF0= -----END CERTIFICATE----- cluster_rename: new-name:foo.com:some-random-infra-id +hostname: test.hostname summary_file: summary.yaml summary_file_clean: summary_redacted.yaml extend_expiration: true @@ -111,6 +112,7 @@ else --cn-san-replace *.apps.seed.redhat.com:*.apps.new-name.foo.com \ --cn-san-replace 192.168.126.10:192.168.127.11 \ --cluster-rename new-name:foo.com:some-random-infra-id \ + --hostname test.hostname \ --summary-file summary.yaml \ --summary-file-clean summary_redacted.yaml \ --extend-expiration diff --git a/src/config.rs b/src/config.rs index 57e1e1b8..6e9bc6c8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -65,6 +65,7 @@ pub(crate) struct RecertConfig { pub(crate) static_files: Vec, pub(crate) customizations: Customizations, pub(crate) cluster_rename: Option, + pub(crate) hostname: Option, pub(crate) threads: Option, pub(crate) regenerate_server_ssh_keys: Option, pub(crate) summary_file: Option, @@ -231,6 +232,11 @@ impl RecertConfig { None => None, }; + let hostname = match value.get("hostname") { + Some(value) => Some(value.as_str().context("hostname must be a string")?.to_string()), + None => None, + }; + let threads = match value.get("threads") { Some(value) => Some( value @@ -285,6 +291,7 @@ impl RecertConfig { force_expire, }, cluster_rename, + hostname, threads, regenerate_server_ssh_keys, summary_file, @@ -326,6 +333,7 @@ impl RecertConfig { force_expire: cli.force_expire, }, cluster_rename: cli.cluster_rename, + hostname: cli.hostname, threads: cli.threads, regenerate_server_ssh_keys: cli.regenerate_server_ssh_keys.map(ConfigPath::from), summary_file: cli.summary_file.map(ConfigPath::from), diff --git a/src/config/cli.rs b/src/config/cli.rs index 4903062a..292f176e 100644 --- a/src/config/cli.rs +++ b/src/config/cli.rs @@ -35,6 +35,11 @@ pub(crate) struct Cli { #[clap(long, value_parser = ClusterRenameParameters::cli_parse)] pub(crate) cluster_rename: Option, + /// If given, the cluster resources that include the hostname will be modified to use this one + /// instead. + #[clap(long)] + pub(crate) hostname: Option, + /// A list of CNs and the private keys to use for their certs. By default, new keys will be /// generated for all regenerated certificates, this option allows you to use existing keys /// instead. Must come in pairs of CN and private key file path, separated by a space. For From 0a0dd4c9d63b9bf2d0e123fd172e5fe02276961d Mon Sep 17 00:00:00 2001 From: Michail Resvanis Date: Fri, 5 Jan 2024 18:07:48 +0100 Subject: [PATCH 02/11] Add hostname rename to finalize Signed-off-by: Michail Resvanis --- src/ocp_postprocess.rs | 23 +++++++++++++++++++++++ src/recert.rs | 4 +++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/ocp_postprocess.rs b/src/ocp_postprocess.rs index 69d1096b..5373b2c5 100644 --- a/src/ocp_postprocess.rs +++ b/src/ocp_postprocess.rs @@ -16,12 +16,14 @@ use sha2::Digest; use std::{collections::HashSet, sync::Arc}; pub(crate) mod cluster_domain_rename; +pub(crate) mod hostname_rename; mod fnv; /// Perform some OCP-related post-processing to make some OCP operators happy pub(crate) async fn ocp_postprocess( in_memory_etcd_client: &Arc, cluster_rename_params: &Option, + hostname: &Option, static_dirs: &Vec, static_files: &Vec, ) -> Result<()> { @@ -47,6 +49,12 @@ pub(crate) async fn ocp_postprocess( .context("renaming cluster")?; } + if let Some(hostname) = hostname { + hostname_rename(in_memory_etcd_client, hostname, static_dirs, static_files) + .await + .context("renaming hostname")?; + } + fix_deployment_dep_annotations( in_memory_etcd_client, K8sResourceLocation::new(Some("openshift-apiserver"), "Deployment", "apiserver", "v1"), @@ -357,3 +365,18 @@ pub(crate) async fn cluster_rename( Ok(()) } + +pub(crate) async fn hostname_rename( + in_memory_etcd_client: &Arc, + hostname: &str, + static_dirs: &Vec, + static_files: &Vec, +) -> Result<()> { + let etcd_client = in_memory_etcd_client; + + hostname_rename::rename_all(etcd_client, hostname, static_dirs, static_files) + .await + .context("renaming all")?; + + Ok(()) +} diff --git a/src/recert.rs b/src/recert.rs index 6cc0f69a..e5b71c21 100644 --- a/src/recert.rs +++ b/src/recert.rs @@ -61,6 +61,7 @@ pub(crate) async fn run( Arc::clone(&in_memory_etcd_client), cluster_crypto, &parsed_cli.cluster_rename, + &parsed_cli.hostname, &parsed_cli.static_dirs, &parsed_cli.static_files, parsed_cli.regenerate_server_ssh_keys.as_deref(), @@ -121,6 +122,7 @@ async fn finalize( in_memory_etcd_client: Arc, cluster_crypto: &mut ClusterCryptoObjects, cluster_rename: &Option, + hostname: &Option, static_dirs: &Vec, static_files: &Vec, regenerate_server_ssh_keys: Option<&Path>, @@ -136,7 +138,7 @@ async fn finalize( let start = std::time::Instant::now(); if in_memory_etcd_client.etcd_client.is_some() { - ocp_postprocess(&in_memory_etcd_client, cluster_rename, static_dirs, static_files) + ocp_postprocess(&in_memory_etcd_client, cluster_rename, hostname, static_dirs, static_files) .await .context("performing ocp specific post-processing")?; } From c5eb3422bedee182a1362d7bd2493eec762f28b9 Mon Sep 17 00:00:00 2001 From: Michail Resvanis Date: Tue, 9 Jan 2024 15:51:04 +0100 Subject: [PATCH 03/11] Add hostname_rename etcd changes Signed-off-by: Michail Resvanis --- src/ocp_postprocess/hostname_rename.rs | 54 ++++ .../hostname_rename/etcd_rename.rs | 267 ++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 src/ocp_postprocess/hostname_rename.rs create mode 100644 src/ocp_postprocess/hostname_rename/etcd_rename.rs diff --git a/src/ocp_postprocess/hostname_rename.rs b/src/ocp_postprocess/hostname_rename.rs new file mode 100644 index 00000000..6d61d8a5 --- /dev/null +++ b/src/ocp_postprocess/hostname_rename.rs @@ -0,0 +1,54 @@ +use crate::{ + config::ConfigPath, + k8s_etcd::{InMemoryK8sEtcd}, +}; +use anyhow::{Context, Result}; +use std::{sync::Arc}; + +mod etcd_rename; + +pub(crate) async fn rename_all( + etcd_client: &Arc, + hostname: &str, + _static_dirs: &Vec, + _static_files: &Vec, +) -> Result<(), anyhow::Error> { + + fix_etcd_resources(etcd_client, hostname) + .await + .context("renaming etcd resources")?; + + Ok(()) +} + +async fn fix_etcd_resources( + etcd_client: &Arc, + hostname: &str, +) -> Result<(), anyhow::Error> { + etcd_rename::fix_etcd_all_certs(etcd_client, hostname) + .await + .context("fixing etcd-all-certs")?; + etcd_rename::fix_etcd_secrets(etcd_client, hostname) + .await + .context("fixing etcd secrets")?; + etcd_rename::fix_etcd_pod(etcd_client, hostname) + .await + .context("fixing etcd-pod")?; + etcd_rename::fix_etcd_scripts(etcd_client, hostname) + .await + .context("fixing etcd-scripts")?; + etcd_rename::fix_restore_etcd_pod(etcd_client, hostname) + .await + .context("fixing restore-etcd-pod")?; + etcd_rename::fix_kubeapiservers_cluster(etcd_client, hostname) + .await + .context("fixing kubeapiservers/cluster")?; + etcd_rename::fix_kubeschedulers_cluster(etcd_client, hostname) + .await + .context("fixing kubeschedulers/cluster")?; + etcd_rename::fix_kubecontrollermanagers_cluster(etcd_client, hostname) + .await + .context("fixing kubecontrollermanagers/cluster")?; + + Ok(()) +} diff --git a/src/ocp_postprocess/hostname_rename/etcd_rename.rs b/src/ocp_postprocess/hostname_rename/etcd_rename.rs new file mode 100644 index 00000000..4c6c235a --- /dev/null +++ b/src/ocp_postprocess/hostname_rename/etcd_rename.rs @@ -0,0 +1,267 @@ +use crate::{ + cluster_crypto::locations::K8sResourceLocation, + k8s_etcd::{get_etcd_json, put_etcd_yaml, InMemoryK8sEtcd}, +}; +use anyhow::{Context, Result}; +use futures_util::future::join_all; +use serde_json::{Map, Value}; +use std::{sync::Arc}; + +pub(crate) async fn fix_etcd_all_certs(etcd_client: &Arc, hostname: &str) -> Result<()> { + join_all( + etcd_client + .list_keys("secrets/openshift-etcd/etcd-all-certs") + .await? + .into_iter() + .map(|key| async move { + let etcd_result = etcd_client + .get(key.clone()) + .await + .with_context(|| format!("getting key {:?}", key))? + .context("key disappeared")?; + let value: Value = serde_yaml::from_slice(etcd_result.value.as_slice()) + .with_context(|| format!("deserializing value of key {:?}", key,))?; + let k8s_resource_location = K8sResourceLocation::try_from(&value)?; + + let mut secret = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting secret")?; + + let data = &mut secret + .pointer_mut("/data") + .context("no /data")? + .as_object_mut() + .context("data not an object")?; + + let keys = vec![ + ("etcd-peer-", ".crt"), + ("etcd-peer-", ".key"), + ("etcd-serving-", ".crt"), + ("etcd-serving-", ".key"), + ("etcd-serving-metrics-", ".crt"), + ("etcd-serving-metrics-", ".key"), + ]; + // TODO: we ignore errors, but maybe we shouldn't? + let _ = keys + .into_iter() + .filter_map(|(prefix, suffix)| replace_data_field(data, prefix, suffix, hostname, key.to_owned()).ok()) + .collect::>(); + + put_etcd_yaml(etcd_client, &k8s_resource_location, secret) + .await + .context(format!("could not put etcd key: {}", key))?; + + Ok(()) + }), + ) + .await + .into_iter() + .collect::>>()?; + + Ok(()) +} + +fn replace_data_field(data: &mut Map, prefix: &str, suffix: &str, hostname: &str, key: String) -> Result<()> { + let (key, value) = data + .into_iter() + .filter(|(k, _v)| k.starts_with(prefix) && k.ends_with(suffix)) + .map(|(k, v)| (k.clone(), v.clone())) + .next() + .context(format!("no {}*{} key in {}", prefix, suffix, key))? + .clone(); + data.insert(format!("{}{}{}", prefix, hostname, suffix), value); + data.remove(&key); + + Ok(()) +} + +pub(crate) async fn fix_etcd_secrets(etcd_client: &Arc, hostname: &str) -> Result<()> { + join_all( + etcd_client + .list_keys("secrets/openshift-etcd/") + .await? + .into_iter() + .filter(|key| { + vec![ + "etcd-peer-", + "etcd-serving-", + "etcd-serving-metrics-", + ] + .into_iter() + .any(|prefix| key.contains(prefix)) + }) + .map(|key| async move { + let etcd_result = etcd_client + .get(key.clone()) + .await + .with_context(|| format!("getting key {:?}", key))? + .context("key disappeared")?; + let value: Value = serde_yaml::from_slice(etcd_result.value.as_slice()) + .with_context(|| format!("deserializing value of key {:?}", key,))?; + let mut k8s_resource_location = K8sResourceLocation::try_from(&value)?; + + let mut secret = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting secret")?; + let metadata = &mut secret + .pointer_mut("/metadata") + .context("no /metadata")? + .as_object_mut() + .context("data not an object")?; + let (_, previous_name) = key.rsplit_once('/').unwrap(); + // TODO: what if the hostname contains a `-` + let mut parts = previous_name.split('-').collect::>(); + if let Some(last) = parts.last_mut() { + *last = hostname; + } + let name: String = parts.join("-"); + metadata.insert("name".to_string(), serde_json::Value::String(name.clone())); + k8s_resource_location.name = name.to_string(); + put_etcd_yaml(etcd_client, &k8s_resource_location, secret).await?; + + etcd_client.delete(&key).await.context(format!("deleting {}", key))?; + + Ok(()) + }), + ) + .await + .into_iter() + .collect::>>()?; + + Ok(()) +} + +// TODO: needs change in the data.install-config[metadata.name] +// TODO: although this is probably the cluster name and not the hostname? +// pub(crate) async fn fix_cluster_config_v1(etcd_client: &Arc, hostname: &str) -> Result<()> { +// let k8s_resource_location = K8sResourceLocation::new(Some("openshift-etcd"), "ConfigMap", "cluster-config-v1", "v1"); +// let mut configmap = get_etcd_json(etcd_client, &k8s_resource_location) +// .await? +// .context("getting configmap")?; +// Ok(()) +// } + +// /kubernetes.io/configmaps/openshift-etcd/etcd-pod +// /kubernetes.io/configmaps/openshift-etcd/etcd-pod-2 +// TODO: data["pod.yaml"] - env.[NODE_seed_ETCD_NAME,NODE_seed_ETCD_URL_HOST,NODE_seed_IP] +// TODO: we need to account for unsupported chars in the hostname being an env var name +// e.g. "another-hostname" -> "another_hostname" when used in an env var name +pub(crate) async fn fix_etcd_pod(etcd_client: &Arc, _hostname: &str) -> Result<()> { + join_all( + etcd_client + .list_keys("configmaps/openshift-etcd/etcd-pod") + .await? + .into_iter() + .map(|key| async move { + let etcd_result = etcd_client + .get(key.clone()) + .await + .with_context(|| format!("getting key {:?}", key))? + .context("key disappeared")?; + let value: Value = serde_yaml::from_slice(etcd_result.value.as_slice()) + .with_context(|| format!("deserializing value of key {:?}", key,))?; + let k8s_resource_location = K8sResourceLocation::try_from(&value)?; + + let mut configmap = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting secret")?; + + let _data = &mut configmap + .pointer_mut("/data") + .context("no /data")? + .as_object_mut() + .context("data not an object")?; + + // TODO: implement - replace the hostname + + put_etcd_yaml(etcd_client, &k8s_resource_location, configmap).await?; + + Ok(()) + }), + ) + .await + .into_iter() + .collect::>>()?; + + Ok(()) +} + +pub(crate) async fn fix_etcd_scripts(etcd_client: &Arc, _hostname: &str) -> Result<()> { + let k8s_resource_location = K8sResourceLocation::new(Some("openshift-etcd"), "ConfigMap", "etcd-scripts", "v1"); + let mut _configmap = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting configmap")?; + // TODO: implement + Ok(()) +} + +pub(crate) async fn fix_restore_etcd_pod(etcd_client: &Arc, _hostname: &str) -> Result<()> { + let k8s_resource_location = K8sResourceLocation::new(Some("openshift-etcd"), "ConfigMap", "etcd-scripts", "v1"); + let mut _configmap = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting configmap")?; + // TODO: implement + Ok(()) +} + +pub(crate) async fn fix_kubeapiservers_cluster(etcd_client: &Arc, hostname: &str) -> Result<()> { + let k8s_resource_location = K8sResourceLocation::new(None, "KubeAPIServer", "cluster", "operator.openshift.io/v1"); + let mut cluster = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting kubeapiservers/cluster")?; + + replace_node_status_name(&mut cluster, hostname) + .context("could not replace nodeName for kubeapiservers/cluster")?; + + put_etcd_yaml(etcd_client, &k8s_resource_location, cluster).await?; + + Ok(()) +} + +pub(crate) async fn fix_kubeschedulers_cluster(etcd_client: &Arc, hostname: &str) -> Result<()> { + let k8s_resource_location = K8sResourceLocation::new(None, "KubeScheduler", "cluster", "operator.openshift.io/v1"); + let mut cluster = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting kubeschedulers/cluster")?; + + replace_node_status_name(&mut cluster, hostname) + .context("could not replace nodeName for kubeschedulers/cluster")?; + + put_etcd_yaml(etcd_client, &k8s_resource_location, cluster).await?; + + Ok(()) +} + +pub(crate) async fn fix_kubecontrollermanagers_cluster(etcd_client: &Arc, hostname: &str) -> Result<()> { + let k8s_resource_location = K8sResourceLocation::new(None, "KubeControllerManager", "cluster", "operator.openshift.io/v1"); + let mut cluster = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting kubecontrollermanagers/cluster")?; + + replace_node_status_name(&mut cluster, hostname) + .context("could not replace nodeName for kubecontrollermanagers/cluster")?; + + put_etcd_yaml(etcd_client, &k8s_resource_location, cluster).await?; + + Ok(()) +} + +fn replace_node_status_name(cluster: &mut Value, hostname: &str) -> Result<()> { + let node_statuses = &mut cluster + .pointer_mut("/status/nodeStatuses") + .context("no /status/nodeStatuses")? + .as_array_mut() + .context("/status/nodeStatuses not an array")?; + + node_statuses + .iter_mut() + .for_each(|status: &mut Value| { + status + .as_object_mut() + .unwrap() + .insert("nodeName".to_string(), Value::String(hostname.to_string())); + }); + + Ok(()) +} + From 69ecb093aea6a7a43edda68c3defe912f9fe69db Mon Sep 17 00:00:00 2001 From: Omer Tuchfeld Date: Sat, 20 Jan 2024 09:58:21 +0100 Subject: [PATCH 04/11] "WIP" --- run_seed.sh | 2 +- src/file_utils.rs | 8 + src/ocp_postprocess.rs | 6 +- src/ocp_postprocess/cluster_domain_rename.rs | 2 +- .../filesystem_rename.rs | 10 +- .../cluster_domain_rename/rename_utils.rs | 252 ++++++++++ src/ocp_postprocess/hostname_rename.rs | 64 ++- .../hostname_rename/etcd_rename.rs | 431 +++++++++++------- .../hostname_rename/filesystem_rename.rs | 75 +++ src/recert.rs | 1 + 10 files changed, 667 insertions(+), 184 deletions(-) create mode 100644 src/ocp_postprocess/hostname_rename/filesystem_rename.rs diff --git a/run_seed.sh b/run_seed.sh index 556a678d..a279e372 100755 --- a/run_seed.sh +++ b/run_seed.sh @@ -3,7 +3,7 @@ set -ex RELEASE_IMAGE=quay.io/openshift-release-dev/ocp-release:4.13.0-x86_64 -BACKUP_IMAGE=${1:-quay.io/otuchfel/ostbackup:seed} +BACKUP_IMAGE=${1:-quay.io/otuchfel/ostbackup:infoseed} AUTH_FILE=${AUTH_FILE:-~/omer-ps} SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" diff --git a/src/file_utils.rs b/src/file_utils.rs index 69d88943..f440a7e8 100644 --- a/src/file_utils.rs +++ b/src/file_utils.rs @@ -6,6 +6,7 @@ use anyhow::{bail, Context, Result}; use base64::{engine::general_purpose::STANDARD as base64_standard, Engine as _}; use serde_json::Value; use std::{ + io::Read, path::{Path, PathBuf}, sync::atomic::{AtomicBool, Ordering::Relaxed}, }; @@ -46,6 +47,13 @@ pub(crate) async fn read_file_to_string(file_path: &Path) -> Result { Ok(contents) } +pub(crate) fn read_file_to_string_sync(file_path: &Path) -> Result { + let mut file = std::fs::File::open(file_path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents).context("failed to read file")?; + Ok(contents) +} + pub(crate) async fn get_filesystem_yaml(file_location: &FileLocation) -> Result { serde_yaml::from_str(read_file_to_string(&PathBuf::from(&file_location.path)).await?.as_str()).context("failed to parse yaml") } diff --git a/src/ocp_postprocess.rs b/src/ocp_postprocess.rs index 5373b2c5..34e7fbde 100644 --- a/src/ocp_postprocess.rs +++ b/src/ocp_postprocess.rs @@ -16,8 +16,8 @@ use sha2::Digest; use std::{collections::HashSet, sync::Arc}; pub(crate) mod cluster_domain_rename; -pub(crate) mod hostname_rename; mod fnv; +pub(crate) mod hostname_rename; /// Perform some OCP-related post-processing to make some OCP operators happy pub(crate) async fn ocp_postprocess( @@ -369,8 +369,8 @@ pub(crate) async fn cluster_rename( pub(crate) async fn hostname_rename( in_memory_etcd_client: &Arc, hostname: &str, - static_dirs: &Vec, - static_files: &Vec, + static_dirs: &[ConfigPath], + static_files: &[ConfigPath], ) -> Result<()> { let etcd_client = in_memory_etcd_client; diff --git a/src/ocp_postprocess/cluster_domain_rename.rs b/src/ocp_postprocess/cluster_domain_rename.rs index 67a65e32..6d58814e 100644 --- a/src/ocp_postprocess/cluster_domain_rename.rs +++ b/src/ocp_postprocess/cluster_domain_rename.rs @@ -6,7 +6,7 @@ use std::{path::Path, sync::Arc}; mod etcd_rename; mod filesystem_rename; pub(crate) mod params; -mod rename_utils; +pub(crate) mod rename_utils; pub(crate) async fn rename_all( etcd_client: &Arc, diff --git a/src/ocp_postprocess/cluster_domain_rename/filesystem_rename.rs b/src/ocp_postprocess/cluster_domain_rename/filesystem_rename.rs index 547997d8..7a3dca46 100644 --- a/src/ocp_postprocess/cluster_domain_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/cluster_domain_rename/filesystem_rename.rs @@ -1,10 +1,6 @@ -use super::{ - rename_utils::fix_api_server_arguments, - rename_utils::fix_apiserver_url_file, - rename_utils::fix_kcm_extended_args, - rename_utils::fix_kubeconfig, - rename_utils::fix_oauth_metadata, - rename_utils::{fix_kcm_pod, fix_machineconfig}, +use super::rename_utils::{ + fix_api_server_arguments, fix_apiserver_url_file, fix_kcm_extended_args, fix_kcm_pod, fix_kubeconfig, fix_machineconfig, + fix_oauth_metadata, }; use crate::file_utils::{self, commit_file, read_file_to_string}; use anyhow::{self, Context, Result}; diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index 1fe8fc99..506d74e6 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -279,6 +279,258 @@ pub(crate) fn fix_kcm_pod(pod: &mut Value, generated_infra_id: &str) -> Result<( Ok(()) } +#[cfg(test)] +mod test_fix_etcd_static_pod { + use crate::{file_utils::read_file_to_string_sync, ocp_postprocess::cluster_domain_rename::rename_utils::fix_etcd_static_pod}; + use anyhow::Result; + use serde_json::Value; + + #[test] + fn test_fix_etcd_static_pod6() -> Result<()> { + let path_str = "backup/etc_orig/kubernetes/static-pod-resources/etcd-pod-6/etcd-pod.yaml"; + let path = std::path::Path::new(path_str); + let pod = read_file_to_string_sync(path)?; + let mut pod: Value = serde_json::from_str(&pod)?; + fix_etcd_static_pod(&mut pod, "seed", "test-hostname")?; + let pod = serde_json::to_string(&pod)?; + assert!(!pod.contains("seed"), "seed still in pod: {}", pod); + + Ok(()) + } + + #[test] + fn test_fix_etcd_static_pod() -> Result<()> { + let path_str = "backup/etc_orig/kubernetes/manifests/etcd-pod.yaml"; + let path = std::path::Path::new(path_str); + let pod = read_file_to_string_sync(path)?; + let mut pod: Value = serde_json::from_str(&pod)?; + fix_etcd_static_pod(&mut pod, "seed", "test-hostname")?; + let pod = serde_json::to_string(&pod)?; + assert!(!pod.contains("seed"), "seed still in pod: {}", pod); + + Ok(()) + } +} + +// Mimics https://github.com/openshift/cluster-etcd-operator/blob/5973046e2d216b290740cf64a071a272bbf83aea/pkg/etcdenvvar/etcd_env.go#L244-L246 +pub(crate) fn env_var_safe(node_name: &str) -> String { + node_name.replace(['-', '.'], "_") +} + +pub(crate) fn fix_etcd_pod_yaml(pod_yaml: &str, original_hostname: &str, hostname: &str) -> Result { + let mut pod_yaml = pod_yaml.to_string(); + + // TODO: The "value:" replacement below is risky - if the hostname is "existing", + // or "REVISION", or "true" this will wreak havoc because these appear in the + // pod.yaml as values. Unlikely but crash if we see these values for now. + ensure!( + ["existing", "REVISION", "true"] + .iter() + .all(|invalid_hostname| invalid_hostname != &original_hostname), + "{} hostname is unsupported at the moment, please use a different seed hostname", + original_hostname + ); + + let patterns = [ + ( + format!(r#"- name: "NODE_{original_hostname}_ETCD_NAME"#), + r#"- name: "NODE_{}_ETCD_NAME"#, + ), + (format!(r#"value: "{original_hostname}""#), r#"value: "{}""#), + ( + format!(r#"- name: "NODE_{original_hostname}_ETCD_URL_HOST"#), + r#"- name: "NODE_{}_ETCD_URL_HOST"#, + ), + (format!(r#"- name: "NODE_{original_hostname}_IP"#), r#"- name: "NODE_{}_IP"#), + ]; + + for (pattern, replacement) in patterns { + let re = regex::Regex::new(&pattern).context("compiling regex")?; + pod_yaml = re + .replace_all(&pod_yaml, replacement.replace("{}", &env_var_safe(hostname)).as_str()) + .to_string(); + } + + Ok(pod_yaml) +} + +pub(crate) fn fix_etcd_static_pod(pod: &mut Value, original_hostname: &str, hostname: &str) -> Result<()> { + { + let init_containers = &mut pod + .pointer_mut("/spec/initContainers") + .context("initContainers not found")? + .as_array_mut() + .context("initContainers not an object")?; + + ensure!(!init_containers.is_empty(), "expected at least one init container in pod.yaml"); + + init_containers + .iter_mut() + .try_for_each(|container| fix_etcd_static_pod_container(container, original_hostname, hostname))?; + } + + { + let containers = &mut pod + .pointer_mut("/spec/containers") + .context("containers not found")? + .as_array_mut() + .context("containers not an object")?; + + ensure!(!containers.is_empty(), "expected at least one container in pod.yaml"); + + containers + .iter_mut() + .try_for_each(|container| { + fix_etcd_static_pod_container(container, original_hostname, hostname) + .context(format!("fixing container {}", container.get("name").unwrap_or(&Value::Null))) + }) + .context("fixing etcd static pod container")?; + } + + Ok(()) +} + +fn fix_etcd_static_pod_container(container: &mut Value, original_hostname: &str, hostname: &str) -> Result<()> { + 'hostname_args_replace: { + let args = container + .pointer_mut("/command") + .context("command not found")? + .as_array_mut() + .context("command not an array")?; + + ensure!(!args.is_empty(), "expected at least one arg in etcd static pod container"); + + let shell_arg = args + .iter_mut() + .find_map(|arg| arg.as_str()?.starts_with("#!/bin/sh\n").then_some(arg)); + + let shell_arg = match shell_arg { + None => break 'hostname_args_replace, + Some(shell_arg) => shell_arg, + }; + + for (original, new) in [ + ("NODE_{original_hostname}_ETCD_URL_HOST", "NODE_{hostname}_ETCD_URL_HOST"), + ("NODE_{original_hostname}_ETCD_NAME", "NODE_{hostname}_ETCD_NAME"), + ("NODE_{original_hostname}_IP", "NODE_{hostname}_ETCD_LISTEN_CLIENT_URLS"), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.crt", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.key", + ), + ("--target-name={original_hostname}", "--target-name={hostname}"), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{hostname}.crt", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{hostname}.key", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{hostname}.crt", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{hostname}.key", + ), + ] { + *shell_arg = serde_json::Value::String( + regex::Regex::new(original.replace("{original_hostname}", original_hostname).as_str()) + .unwrap() + .replace_all( + shell_arg.as_str().context("arg not string")?, + new.replace("{hostname}", hostname).as_str(), + ) + .to_string(), + ); + } + } + + 'hostname_env_replace: { + let maybe_env = container.pointer_mut("/env"); + + let envs = match maybe_env { + Some(env) => env.as_array_mut().context("env not an array")?, + None => break 'hostname_env_replace, + }; + + ensure!(!envs.is_empty(), "expected at least one env in etcd static pod container"); + + for (key, new_name, new_value) in [ + ( + "ETCDCTL_CERT", + None, + Some(format!("/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.crt").as_str()), + ), + ( + "ETCDCTL_KEY", + None, + Some(format!("/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.key").as_str()), + ), + ( + "ETCDCTL_KEY_FILE", + None, + Some(format!("/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.key").as_str()), + ), + ( + format!("NODE_{original_hostname}_ETCD_NAME").as_str(), + Some(format!("NODE_{hostname}_ETCD_NAME").as_str()), + Some(format!("{hostname}").as_str()), + ), + ( + format!("NODE_{original_hostname}_ETCD_URL_HOST").as_str(), + Some(format!("NODE_{hostname}_ETCD_URL_HOST").as_str()), + None, + ), + ( + format!("NODE_{original_hostname}_IP").as_str(), + Some(format!("NODE_{hostname}_IP").as_str()), + None, + ), + ] { + adjust_env(envs, key, new_name, new_value).context(format!("adjusting env var {}", key))?; + } + } + + Ok(()) +} + +fn adjust_env(envs: &mut Vec, env_name: &str, new_name: Option<&str>, new_value: Option<&str>) -> Result<()> { + let found_env = envs + .iter_mut() + .find_map(|env| (env.as_object()?.get("name") == Some(&Value::String(env_name.to_string()))).then_some(env)); + + match found_env { + None => Ok(()), + Some(env) => { + match new_name { + None => {} + Some(new_name) => { + env.as_object_mut() + .context("env var not an object")? + .insert("name".to_string(), serde_json::Value::String(new_name.to_string())); + } + }; + + match new_value { + None => {} + Some(new_value) => { + env.as_object_mut() + .context("env var not an object")? + .insert("value".to_string(), serde_json::Value::String(new_value.to_string())); + } + }; + + Ok(()) + } + } +} + pub(crate) fn fix_pod_container_env(pod: &mut Value, domain: &str, container_name: &str, env_name: &str, init: bool) -> Result<()> { let containers = &mut pod .pointer_mut(&format!("/spec/{}", if init { "initContainers" } else { "containers" })) diff --git a/src/ocp_postprocess/hostname_rename.rs b/src/ocp_postprocess/hostname_rename.rs index 6d61d8a5..4c1624b8 100644 --- a/src/ocp_postprocess/hostname_rename.rs +++ b/src/ocp_postprocess/hostname_rename.rs @@ -1,45 +1,71 @@ -use crate::{ - config::ConfigPath, - k8s_etcd::{InMemoryK8sEtcd}, -}; +use crate::{config::ConfigPath, k8s_etcd::InMemoryK8sEtcd}; use anyhow::{Context, Result}; -use std::{sync::Arc}; +use std::{path::Path, sync::Arc}; mod etcd_rename; +mod filesystem_rename; pub(crate) async fn rename_all( etcd_client: &Arc, hostname: &str, - _static_dirs: &Vec, - _static_files: &Vec, + static_dirs: &[ConfigPath], + static_files: &[ConfigPath], ) -> Result<(), anyhow::Error> { + let original_hostname = fix_etcd_resources(etcd_client, hostname).await.context("renaming etcd resources")?; - fix_etcd_resources(etcd_client, hostname) + fix_filesystem_resources(&original_hostname, &hostname, static_dirs, static_files) .await - .context("renaming etcd resources")?; + .context("renaming filesystem resources")?; Ok(()) } -async fn fix_etcd_resources( - etcd_client: &Arc, +async fn fix_filesystem_resources( + original_hostname: &str, hostname: &str, + static_dirs: &[ConfigPath], + static_files: &[ConfigPath], ) -> Result<(), anyhow::Error> { - etcd_rename::fix_etcd_all_certs(etcd_client, hostname) + for dir in static_dirs { + fix_dir_resources(original_hostname, hostname, dir).await?; + } + + for file in static_files { + fix_file_resources(original_hostname, hostname, file).await?; + } + + Ok(()) +} + +async fn fix_dir_resources(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { + filesystem_rename::fix_filesystem_etcd_static_pods(original_hostname, hostname, dir) + .await + .context("fixing etcd static pods")?; + + filesystem_rename::fix_filesystem_etcd_configmap_pod_yaml(original_hostname, hostname, dir) + .await + .context("fixing etcd static pod configmap pod yaml")?; + + Ok(()) +} + +async fn fix_file_resources(_original_hostname: &str, _hostname: &str, _file: &Path) -> Result<()> { + Ok(()) +} + +async fn fix_etcd_resources(etcd_client: &Arc, hostname: &str) -> Result { + let original_hostname = etcd_rename::fix_etcd_all_certs(etcd_client, hostname) .await .context("fixing etcd-all-certs")?; - etcd_rename::fix_etcd_secrets(etcd_client, hostname) + etcd_rename::fix_etcd_secrets(etcd_client, &original_hostname, hostname) .await .context("fixing etcd secrets")?; - etcd_rename::fix_etcd_pod(etcd_client, hostname) + etcd_rename::fix_etcd_pod(etcd_client, &original_hostname, hostname) .await .context("fixing etcd-pod")?; - etcd_rename::fix_etcd_scripts(etcd_client, hostname) + etcd_rename::fix_etcd_scripts(etcd_client, &original_hostname, hostname) .await .context("fixing etcd-scripts")?; - etcd_rename::fix_restore_etcd_pod(etcd_client, hostname) - .await - .context("fixing restore-etcd-pod")?; etcd_rename::fix_kubeapiservers_cluster(etcd_client, hostname) .await .context("fixing kubeapiservers/cluster")?; @@ -50,5 +76,5 @@ async fn fix_etcd_resources( .await .context("fixing kubecontrollermanagers/cluster")?; - Ok(()) + Ok(original_hostname) } diff --git a/src/ocp_postprocess/hostname_rename/etcd_rename.rs b/src/ocp_postprocess/hostname_rename/etcd_rename.rs index 4c6c235a..2d9bafb3 100644 --- a/src/ocp_postprocess/hostname_rename/etcd_rename.rs +++ b/src/ocp_postprocess/hostname_rename/etcd_rename.rs @@ -1,157 +1,246 @@ use crate::{ cluster_crypto::locations::K8sResourceLocation, k8s_etcd::{get_etcd_json, put_etcd_yaml, InMemoryK8sEtcd}, + ocp_postprocess::cluster_domain_rename::rename_utils::{env_var_safe, fix_etcd_pod_yaml}, }; -use anyhow::{Context, Result}; +use anyhow::{ensure, Context, Result}; use futures_util::future::join_all; use serde_json::{Map, Value}; -use std::{sync::Arc}; +use std::{collections::HashSet, fmt::Display, sync::Arc}; + +async fn fix_etcd_all_certs_secret(etcd_client: &Arc, key: &str, hostname: &str) -> Result> { + let etcd_result = etcd_client + .get(key.to_string()) + .await + .with_context(|| format!("getting key {:?}", key))? + .context("key disappeared")?; + let value: Value = + serde_yaml::from_slice(etcd_result.value.as_slice()).with_context(|| format!("deserializing value of key {:?}", key,))?; + let k8s_resource_location = K8sResourceLocation::try_from(&value)?; + + let mut secret = get_etcd_json(etcd_client, &k8s_resource_location) + .await? + .context("getting secret")?; + + let original_hostname = { + let data = &mut secret + .pointer_mut("/data") + .context("no /data")? + .as_object_mut() + .context("data not an object")?; + + match data + .iter() + // etcd-peer is the only key that we can use to unambiguously identify the original hostname + .find(|(k, _v)| k.starts_with("etcd-peer-") && k.ends_with(".crt")) + .map(|(k, _v)| k.clone()) + { + Some(data_key) => data_key.trim_start_matches("etcd-peer-").trim_end_matches(".crt").to_string(), + None => { + // This is OK, some of the secrets don't have data keys + return anyhow::Ok(None); + } + } + }; + + let data_prefixes = ["etcd-peer-", "etcd-serving-", "etcd-serving-metrics-"]; + let managed_fields_data_prefixes = data_prefixes.iter().map(|prefix| format!("f:{}", prefix)).collect::>(); + + let suffixes = [".crt", ".key"]; + + fn replace_keys( + original_hostname: &str, + new_hostname: &str, + data_prefixes: &[impl Display], + suffixes: &[impl Display], + data: &mut &mut Map, + ) -> Result<()> { + let old_keys = data_prefixes.iter().flat_map(|prefix| { + suffixes + .iter() + .map(move |suffix| format!("{}{}{}", prefix, original_hostname, suffix)) + }); -pub(crate) async fn fix_etcd_all_certs(etcd_client: &Arc, hostname: &str) -> Result<()> { - join_all( - etcd_client - .list_keys("secrets/openshift-etcd/etcd-all-certs") - .await? - .into_iter() - .map(|key| async move { - let etcd_result = etcd_client - .get(key.clone()) - .await - .with_context(|| format!("getting key {:?}", key))? - .context("key disappeared")?; - let value: Value = serde_yaml::from_slice(etcd_result.value.as_slice()) - .with_context(|| format!("deserializing value of key {:?}", key,))?; - let k8s_resource_location = K8sResourceLocation::try_from(&value)?; + let new_keys = data_prefixes + .iter() + .flat_map(|prefix| suffixes.iter().map(move |suffix| format!("{}{}{}", prefix, new_hostname, suffix))); - let mut secret = get_etcd_json(etcd_client, &k8s_resource_location) - .await? - .context("getting secret")?; + old_keys.zip(new_keys).for_each(|(old_key, new_key)| { + let value = data.remove(&old_key).context(format!("could not remove key: {}", old_key)).unwrap(); + data.insert(new_key, value); + }); - let data = &mut secret - .pointer_mut("/data") - .context("no /data")? - .as_object_mut() - .context("data not an object")?; + Ok(()) + } + + // Adjust .data + { + let data = &mut secret + .pointer_mut("/data") + .context("no /data")? + .as_object_mut() + .context("data not an object")?; + + replace_keys(&original_hostname, hostname, &data_prefixes, &suffixes, data).context("could not replace keys")?; + } + + // Adjust .metadata.managedFields.fieldsV1.data + { + let metadata = &mut secret + .pointer_mut("/metadata") + .context("no /metadata")? + .as_object_mut() + .context("data not an object")?; + + let managed_fields = metadata + .get_mut("managedFields") + .context("no managedFields")? + .as_array_mut() + .context("managedFields not an array")?; + + managed_fields.iter_mut().try_for_each(|managed_field| { + let fields_v1_raw_byte_array = managed_field + .pointer("/fieldsV1/raw") + .context("no /fieldsV1/raw")? + .as_array() + .context("/fieldsV1/raw not an array")?; + + let mut fields_v1_raw_parsed: Value = serde_json::from_str( + &String::from_utf8( + fields_v1_raw_byte_array + .iter() + .map(|v| v.as_u64().context("fieldsV1 not a number")) + .collect::>>() + .context("parsing byte array")? + .into_iter() + .map(|v| v as u8) + .collect::>(), + ) + .context("fieldsV1 not valid utf8")?, + ) + .context("deserializing fieldsV1")?; + + let mut data = (match fields_v1_raw_parsed.pointer_mut("/f:data") { + Some(data) => data, + None => return anyhow::Ok(()), + }) + .as_object_mut() + .context("f:data not an object")?; - let keys = vec![ - ("etcd-peer-", ".crt"), - ("etcd-peer-", ".key"), - ("etcd-serving-", ".crt"), - ("etcd-serving-", ".key"), - ("etcd-serving-metrics-", ".crt"), - ("etcd-serving-metrics-", ".key"), - ]; - // TODO: we ignore errors, but maybe we shouldn't? - let _ = keys - .into_iter() - .filter_map(|(prefix, suffix)| replace_data_field(data, prefix, suffix, hostname, key.to_owned()).ok()) - .collect::>(); - - put_etcd_yaml(etcd_client, &k8s_resource_location, secret) - .await - .context(format!("could not put etcd key: {}", key))?; + replace_keys(&original_hostname, hostname, &managed_fields_data_prefixes, &suffixes, &mut data) + .context("could not replace managed fields keys")?; - Ok(()) - }), + managed_field + .pointer_mut("/fieldsV1") + .context("no /fieldsV1")? + .as_object_mut() + .context("/fieldsV1 not an object")? + .insert( + "raw".to_string(), + serde_json::Value::Array( + serde_json::Value::String(serde_json::to_string(&fields_v1_raw_parsed).context("serializing fieldsV1")?) + .as_str() + .context("serialized not a string")? + .as_bytes() + .iter() + .map(|b| serde_json::Value::Number(serde_json::Number::from(*b))) + .collect(), + ), + ); + + anyhow::Ok(()) + })?; + } + + put_etcd_yaml(etcd_client, &k8s_resource_location, secret) + .await + .context(format!("could not put etcd key: {}", key))?; + + Ok(Some(original_hostname)) +} + +pub(crate) async fn fix_etcd_all_certs(etcd_client: &Arc, hostname: &str) -> Result { + let hostnames = join_all( + etcd_client + .list_keys("secrets/openshift-etcd/etcd-all-certs") + .await? + .into_iter() + .map(|key| async move { anyhow::Ok(fix_etcd_all_certs_secret(etcd_client, &key, hostname).await?) }), ) .await .into_iter() - .collect::>>()?; + .collect::>>>()?; - Ok(()) -} + let hostnames = hostnames.into_iter().flatten().collect::>(); -fn replace_data_field(data: &mut Map, prefix: &str, suffix: &str, hostname: &str, key: String) -> Result<()> { - let (key, value) = data - .into_iter() - .filter(|(k, _v)| k.starts_with(prefix) && k.ends_with(suffix)) - .map(|(k, v)| (k.clone(), v.clone())) - .next() - .context(format!("no {}*{} key in {}", prefix, suffix, key))? - .clone(); - data.insert(format!("{}{}{}", prefix, hostname, suffix), value); - data.remove(&key); + ensure!( + hostnames.len() == 1, + "no hostnames or multiple hostnames found in etcd-all-certs secrets: {:?}", + hostnames + ); - Ok(()) + let original_hostname = hostnames.into_iter().next().unwrap(); + + Ok(original_hostname) } -pub(crate) async fn fix_etcd_secrets(etcd_client: &Arc, hostname: &str) -> Result<()> { - join_all( - etcd_client - .list_keys("secrets/openshift-etcd/") - .await? - .into_iter() - .filter(|key| { - vec![ - "etcd-peer-", - "etcd-serving-", - "etcd-serving-metrics-", - ] +pub(crate) async fn fix_etcd_secrets(etcd_client: &Arc, original_hostname: &str, hostname: &str) -> Result<()> { + for key_prefix in ["etcd-peer", "etcd-serving", "etcd-serving-metrics"] { + join_all( + etcd_client + .list_keys(format!("secrets/openshift-etcd/{key_prefix}-{original_hostname}").as_str()) + .await? .into_iter() - .any(|prefix| key.contains(prefix)) - }) - .map(|key| async move { - let etcd_result = etcd_client - .get(key.clone()) - .await - .with_context(|| format!("getting key {:?}", key))? - .context("key disappeared")?; - let value: Value = serde_yaml::from_slice(etcd_result.value.as_slice()) - .with_context(|| format!("deserializing value of key {:?}", key,))?; - let mut k8s_resource_location = K8sResourceLocation::try_from(&value)?; - - let mut secret = get_etcd_json(etcd_client, &k8s_resource_location) - .await? - .context("getting secret")?; - let metadata = &mut secret - .pointer_mut("/metadata") - .context("no /metadata")? - .as_object_mut() - .context("data not an object")?; - let (_, previous_name) = key.rsplit_once('/').unwrap(); - // TODO: what if the hostname contains a `-` - let mut parts = previous_name.split('-').collect::>(); - if let Some(last) = parts.last_mut() { - *last = hostname; - } - let name: String = parts.join("-"); - metadata.insert("name".to_string(), serde_json::Value::String(name.clone())); - k8s_resource_location.name = name.to_string(); - put_etcd_yaml(etcd_client, &k8s_resource_location, secret).await?; - - etcd_client.delete(&key).await.context(format!("deleting {}", key))?; - - Ok(()) - }), - ) - .await - .into_iter() - .collect::>>()?; + .map(|key| async move { + let etcd_result = etcd_client + .get(key.clone()) + .await + .with_context(|| format!("getting key {key:?}"))? + .context("key disappeared")?; + + let mut etcd_value: Value = serde_yaml::from_slice(etcd_result.value.as_slice()).context("deserializing value")?; + + let new_secret_name = format!("{key_prefix}-{hostname}"); + + etcd_value + .pointer_mut("/metadata") + .context("no /metadata")? + .as_object_mut() + .context("/metadata not an object")? + .insert("name".to_string(), serde_json::Value::String(new_secret_name.clone())); + + etcd_client + .put( + &(format!("/kubernetes.io/secrets/openshift-etcd/{new_secret_name}")), + serde_json::to_string(&etcd_value).context("serializing value")?.as_bytes().to_vec(), + ) + .await; + + etcd_client.delete(&key).await.context(format!("deleting {}", key))?; + + Ok(()) + }), + ) + .await + .into_iter() + .collect::>>()?; + } Ok(()) } -// TODO: needs change in the data.install-config[metadata.name] -// TODO: although this is probably the cluster name and not the hostname? -// pub(crate) async fn fix_cluster_config_v1(etcd_client: &Arc, hostname: &str) -> Result<()> { -// let k8s_resource_location = K8sResourceLocation::new(Some("openshift-etcd"), "ConfigMap", "cluster-config-v1", "v1"); -// let mut configmap = get_etcd_json(etcd_client, &k8s_resource_location) -// .await? -// .context("getting configmap")?; -// Ok(()) -// } - -// /kubernetes.io/configmaps/openshift-etcd/etcd-pod -// /kubernetes.io/configmaps/openshift-etcd/etcd-pod-2 -// TODO: data["pod.yaml"] - env.[NODE_seed_ETCD_NAME,NODE_seed_ETCD_URL_HOST,NODE_seed_IP] -// TODO: we need to account for unsupported chars in the hostname being an env var name -// e.g. "another-hostname" -> "another_hostname" when used in an env var name -pub(crate) async fn fix_etcd_pod(etcd_client: &Arc, _hostname: &str) -> Result<()> { +pub(crate) async fn fix_etcd_pod(etcd_client: &Arc, original_hostname: &str, hostname: &str) -> Result<()> { join_all( etcd_client .list_keys("configmaps/openshift-etcd/etcd-pod") .await? .into_iter() + .chain( + etcd_client + .list_keys("configmaps/openshift-etcd/restore-etcd-pod") + .await? + .into_iter(), + ) .map(|key| async move { let etcd_result = etcd_client .get(key.clone()) @@ -166,13 +255,25 @@ pub(crate) async fn fix_etcd_pod(etcd_client: &Arc, _hostname: .await? .context("getting secret")?; - let _data = &mut configmap + let data = &mut configmap .pointer_mut("/data") .context("no /data")? .as_object_mut() .context("data not an object")?; - // TODO: implement - replace the hostname + // TODO: We can't roundtrip arbitrary YAML, ask etcd folks to stop using YAML + // That's why we have to do primitive string manipulation here instead of proper + // parsing + let mut pod_yaml = data + .get_mut("pod.yaml") + .context("no pod.yaml")? + .as_str() + .context("pod.yaml not a string")? + .to_string(); + + let pod_yaml = fix_etcd_pod_yaml(&mut pod_yaml, original_hostname, hostname).context("could not fix pod yaml")?; + + data.insert("pod.yaml".to_string(), serde_json::Value::String(pod_yaml)); put_etcd_yaml(etcd_client, &k8s_resource_location, configmap).await?; @@ -186,21 +287,51 @@ pub(crate) async fn fix_etcd_pod(etcd_client: &Arc, _hostname: Ok(()) } -pub(crate) async fn fix_etcd_scripts(etcd_client: &Arc, _hostname: &str) -> Result<()> { +pub(crate) async fn fix_etcd_scripts(etcd_client: &Arc, original_hostname: &str, hostname: &str) -> Result<()> { let k8s_resource_location = K8sResourceLocation::new(Some("openshift-etcd"), "ConfigMap", "etcd-scripts", "v1"); - let mut _configmap = get_etcd_json(etcd_client, &k8s_resource_location) + let mut configmap = get_etcd_json(etcd_client, &k8s_resource_location) .await? .context("getting configmap")?; - // TODO: implement - Ok(()) -} -pub(crate) async fn fix_restore_etcd_pod(etcd_client: &Arc, _hostname: &str) -> Result<()> { - let k8s_resource_location = K8sResourceLocation::new(Some("openshift-etcd"), "ConfigMap", "etcd-scripts", "v1"); - let mut _configmap = get_etcd_json(etcd_client, &k8s_resource_location) - .await? - .context("getting configmap")?; - // TODO: implement + let data = &mut configmap + .pointer_mut("/data") + .context("no /data")? + .as_object_mut() + .context("data not an object")?; + + // TODO: We can't roundtrip arbitrary YAML, ask etcd folks to stop using YAML + // That's why we have to do primitive string manipulation here instead of proper + // parsing + let mut pod_yaml = data + .get_mut("etcd.env") + .context("no etcd.env")? + .as_str() + .context("etcd.env not a string")? + .to_string(); + + let patterns = [ + ( + format!(r#"export NODE_{original_hostname}_ETCD_NAME="{original_hostname}""#), + r#"export NODE_{}_="{}""#, + ), + ( + format!(r#"export NODE_({original_hostname})_ETCD_URL_HOST="#), + r#"export NODE_{}_ETCD_URL_HOST="#, + ), + (format!(r#"export NODE_{original_hostname}_IP="#), r#"export NODE_{}_IP="#), + ]; + + for (pattern, replacement) in patterns { + let re = regex::Regex::new(&pattern).context("compiling regex")?; + pod_yaml = re + .replace_all(&pod_yaml, replacement.replace("{}", &env_var_safe(hostname)).as_str()) + .to_string(); + } + + data.insert("etcd.env".to_string(), serde_json::Value::String(pod_yaml)); + + put_etcd_yaml(etcd_client, &k8s_resource_location, configmap).await?; + Ok(()) } @@ -210,8 +341,7 @@ pub(crate) async fn fix_kubeapiservers_cluster(etcd_client: &Arc Result<()> { .as_array_mut() .context("/status/nodeStatuses not an array")?; - node_statuses - .iter_mut() - .for_each(|status: &mut Value| { - status - .as_object_mut() - .unwrap() - .insert("nodeName".to_string(), Value::String(hostname.to_string())); - }); + node_statuses.iter_mut().for_each(|status: &mut Value| { + status + .as_object_mut() + .unwrap() + .insert("nodeName".to_string(), Value::String(hostname.to_string())); + }); Ok(()) } - diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs new file mode 100644 index 00000000..d6337abb --- /dev/null +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -0,0 +1,75 @@ +use crate::{ + file_utils::{self, commit_file, read_file_to_string}, + ocp_postprocess::cluster_domain_rename::rename_utils, +}; +use anyhow::{self, Context, Result}; +use futures_util::future::join_all; +use serde_json::Value; +use std::path::Path; + +pub(crate) async fn fix_filesystem_etcd_static_pods(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { + join_all(file_utils::globvec(dir, "**/etcd-pod.yaml")?.into_iter().map(|file_path| { + let etcd_pod_path = file_path.clone(); + let original_hostname = original_hostname.to_string(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path) + .await + .context("reading kube-controller-manager-pod.yaml")?; + + let mut pod: Value = serde_json::from_str(&contents).context("parsing etcd.yaml")?; + + rename_utils::fix_etcd_static_pod(&mut pod, &original_hostname, &hostname).context("fixing etcd-pod.yaml")?; + + commit_file(file_path, serde_json::to_string(&pod).context("serializing etcd-pod.yaml")?) + .await + .context("writing etcd-pod.yaml to disk")?; + + anyhow::Ok(()) + } + .await + .context(format!("fixing etcd-pod.yaml {:?}", etcd_pod_path)) + }) + })) + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .collect::>>()?; + + Ok(()) +} + +pub(crate) async fn fix_filesystem_etcd_configmap_pod_yaml(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { + join_all(file_utils::globvec(dir, "**/etcd-pod/pod.yaml")?.into_iter().map(|file_path| { + let etcd_pod_path = file_path.clone(); + let original_hostname = original_hostname.to_string(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path) + .await + .context("reading kube-controller-manager-pod.yaml")?; + + commit_file( + file_path, + rename_utils::fix_etcd_pod_yaml(&contents, &original_hostname, &hostname).context("fixing etcd-pod.yaml")?, + ) + .await + .context("writing etcd-pod.yaml to disk")?; + + anyhow::Ok(()) + } + .await + .context(format!("fixing etcd-pod.yaml {:?}", etcd_pod_path)) + }) + })) + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .collect::>>()?; + + Ok(()) +} diff --git a/src/recert.rs b/src/recert.rs index e5b71c21..b6d31ca5 100644 --- a/src/recert.rs +++ b/src/recert.rs @@ -118,6 +118,7 @@ async fn recertify( Ok((rsa_key_pool_and_scanning_run_time, processing_run_time)) } +#[allow(clippy::too_many_arguments)] async fn finalize( in_memory_etcd_client: Arc, cluster_crypto: &mut ClusterCryptoObjects, From a8f8cd0335b350d240a07e839b91abf4f50dcb59 Mon Sep 17 00:00:00 2001 From: Eran Cohen Date: Thu, 25 Jan 2024 15:37:44 +0200 Subject: [PATCH 05/11] Add hostname rename of cluster-backup.sh Signed-off-by: Eran Cohen --- .../cluster_domain_rename/rename_utils.rs | 22 +++++++++++-- src/ocp_postprocess/hostname_rename.rs | 4 +++ .../hostname_rename/filesystem_rename.rs | 33 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index 506d74e6..34e92864 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -122,6 +122,13 @@ pub(crate) fn fix_kcm_extended_args(config: &mut Value, generated_infra_id: &str Ok(()) } +pub(crate) fn fix_cluster_backup_sh(cluster_backup_sh: &str, original_hostname: &str, hostname: &str) -> Result { + let cluster_backup = cluster_backup_sh.to_string(); + let pattern = format!(r"NODE_{original_hostname}_IP"); + let replacement = format!(r"NODE_{}_IP", env_var_safe(hostname)); + Ok(cluster_backup.replace(dbg!(&pattern), &replacement)) +} + pub(crate) async fn fix_kubeconfig(cluster_name: &str, cluster_domain: &str, kubeconfig: &mut Value) -> Result<()> { let is_kubelet_kubeconfig = kubeconfig .pointer_mut("/contexts") @@ -212,7 +219,7 @@ fn fix_kubeconfig_server(cluster: &mut serde_json::Map, cluster_d // Could be something like `https://localhost:6443`, ignore return Ok(()); } - .context("no previous value")?; + .context("no previous value")?; Ok(()) } @@ -281,10 +288,21 @@ pub(crate) fn fix_kcm_pod(pod: &mut Value, generated_infra_id: &str) -> Result<( #[cfg(test)] mod test_fix_etcd_static_pod { - use crate::{file_utils::read_file_to_string_sync, ocp_postprocess::cluster_domain_rename::rename_utils::fix_etcd_static_pod}; + use crate::{file_utils::read_file_to_string_sync, ocp_postprocess::cluster_domain_rename::rename_utils::{fix_etcd_static_pod,fix_cluster_backup_sh}}; use anyhow::Result; use serde_json::Value; + #[test] + fn test_fix_etcd_scripts_cluster_backup() -> Result<()> { + let path_str = "backup/etc_orig/kubernetes/static-pod-resources/etcd-certs/configmaps/etcd-scripts/cluster-backup.sh"; + let path = std::path::Path::new(path_str); + let script = read_file_to_string_sync(path)?; + let script = fix_cluster_backup_sh(&script, "seed", "test-hostname")?; + assert!(!script.contains("seed"), "seed still in pod: {}", script); + + Ok(()) + } + #[test] fn test_fix_etcd_static_pod6() -> Result<()> { let path_str = "backup/etc_orig/kubernetes/static-pod-resources/etcd-pod-6/etcd-pod.yaml"; diff --git a/src/ocp_postprocess/hostname_rename.rs b/src/ocp_postprocess/hostname_rename.rs index 4c1624b8..b7e4b2ff 100644 --- a/src/ocp_postprocess/hostname_rename.rs +++ b/src/ocp_postprocess/hostname_rename.rs @@ -46,6 +46,10 @@ async fn fix_dir_resources(original_hostname: &str, hostname: &str, dir: &Path) .await .context("fixing etcd static pod configmap pod yaml")?; + filesystem_rename::fix_filesystem_etcd_scripts_cluster_backup_sh(&original_hostname, hostname, dir) + .await + .context("fixing etcd certs clsuter-backup.sh")?; + Ok(()) } diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs index d6337abb..c038a90a 100644 --- a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -73,3 +73,36 @@ pub(crate) async fn fix_filesystem_etcd_configmap_pod_yaml(original_hostname: &s Ok(()) } + +pub(crate) async fn fix_filesystem_etcd_scripts_cluster_backup_sh(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { + join_all(file_utils::globvec(dir, "**/etcd-scripts/cluster-backup.sh")?.into_iter().map(|file_path| { + let cluster_backup_path = file_path.clone(); + let original_hostname = original_hostname.to_string(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path) + .await + .context("reading cluster-backup.sh")?; + + commit_file( + file_path, + dbg!(rename_utils::fix_cluster_backup_sh(&contents, &original_hostname, &hostname).context("fixing cluster-backup.sh")?), + ) + .await + .context("writing cluster-backup.sh to disk")?; + + anyhow::Ok(()) + } + .await + .context(format!("fixing cluster-backup.sh {:?}", cluster_backup_path)) + }) + })) + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .collect::>>()?; + + Ok(()) +} From 61909e4d0a57cfaa5cd7a83425cd9d62e2316be7 Mon Sep 17 00:00:00 2001 From: Omer Tuchfeld Date: Thu, 25 Jan 2024 14:42:16 +0100 Subject: [PATCH 06/11] work --- .../cluster_domain_rename/rename_utils.rs | 73 ++++++++++++++++--- .../hostname_rename/filesystem_rename.rs | 4 +- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index 34e92864..5aeb80b3 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -288,7 +288,7 @@ pub(crate) fn fix_kcm_pod(pod: &mut Value, generated_infra_id: &str) -> Result<( #[cfg(test)] mod test_fix_etcd_static_pod { - use crate::{file_utils::read_file_to_string_sync, ocp_postprocess::cluster_domain_rename::rename_utils::{fix_etcd_static_pod,fix_cluster_backup_sh}}; + use crate::{file_utils::read_file_to_string_sync, ocp_postprocess::cluster_domain_rename::rename_utils::{fix_etcd_pod_yaml, fix_etcd_static_pod,fix_cluster_backup_sh}}; use anyhow::Result; use serde_json::Value; @@ -328,6 +328,17 @@ mod test_fix_etcd_static_pod { Ok(()) } + + #[test] + fn test_fix_etcd_static_pod_configmap() -> Result<()> { + let path_str = "backup/etc_orig/kubernetes/static-pod-resources/etcd-pod-6/configmaps/etcd-pod/pod.yaml"; + let path = std::path::Path::new(path_str); + let pod = read_file_to_string_sync(path)?; + let pod = fix_etcd_pod_yaml(&pod, "seed", "test-hostname")?; + assert!(!pod.contains("seed"), "seed still in pod: {}", pod); + + Ok(()) + } } // Mimics https://github.com/openshift/cluster-etcd-operator/blob/5973046e2d216b290740cf64a071a272bbf83aea/pkg/etcdenvvar/etcd_env.go#L244-L246 @@ -351,22 +362,62 @@ pub(crate) fn fix_etcd_pod_yaml(pod_yaml: &str, original_hostname: &str, hostnam let patterns = [ ( - format!(r#"- name: "NODE_{original_hostname}_ETCD_NAME"#), - r#"- name: "NODE_{}_ETCD_NAME"#, + r#"- name: "NODE_{original_hostname_safe}_ETCD_NAME"#, + r#"- name: "NODE_{hostname_safe}_ETCD_NAME"#, + ), + (r#"value: "{original_hostname}""#, r#"value: "{hostname}""#), + ( + r#"- name: "NODE_{original_hostname_safe}_ETCD_URL_HOST"#, + r#"- name: "NODE_{hostname_safe}_ETCD_URL_HOST"#, + ), + ( + r#"- name: "NODE_{original_hostname_safe}_IP"#, + r#"- name: "NODE_{hostname_safe}_IP"#, + ), + ( + r#"${NODE_{original_hostname_safe}_ETCD_URL_HOST"#, + r#"${NODE_{hostname_safe}_ETCD_URL_HOST"#, ), - (format!(r#"value: "{original_hostname}""#), r#"value: "{}""#), ( - format!(r#"- name: "NODE_{original_hostname}_ETCD_URL_HOST"#), - r#"- name: "NODE_{}_ETCD_URL_HOST"#, + r#"${NODE_{original_hostname_safe}_ETCD_NAME"#, + r#"${NODE_{hostname_safe}_ETCD_NAME""#, ), - (format!(r#"- name: "NODE_{original_hostname}_IP"#), r#"- name: "NODE_{}_IP"#), + ("${NODE_{original_hostname_safe}_IP", "${NODE_{hostname_safe}_IP"), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.crt", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.key", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{hostname}.crt", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{hostname}.key", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{hostname}.key", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{hostname}.crt", + ), + ("--target-name={original_hostname}", "--target-name={hostname}"), ]; for (pattern, replacement) in patterns { - let re = regex::Regex::new(&pattern).context("compiling regex")?; - pod_yaml = re - .replace_all(&pod_yaml, replacement.replace("{}", &env_var_safe(hostname)).as_str()) - .to_string(); + let pattern = pattern.replace("{original_hostname}", original_hostname); + let replacement = replacement.replace("{hostname}", &hostname); + + let pattern = pattern.replace("{original_hostname_safe}", &env_var_safe(original_hostname)); + let replacement = replacement.replace("{hostname_safe}", &env_var_safe(hostname)); + + pod_yaml = pod_yaml.replace(dbg!(&pattern), dbg!(&replacement)).to_string(); } Ok(pod_yaml) diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs index c038a90a..ff66dcdb 100644 --- a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -34,7 +34,7 @@ pub(crate) async fn fix_filesystem_etcd_static_pods(original_hostname: &str, hos })) .await .into_iter() - .collect::, _>>()? + .collect::, _>>()? .into_iter() .collect::>>()?; @@ -67,7 +67,7 @@ pub(crate) async fn fix_filesystem_etcd_configmap_pod_yaml(original_hostname: &s })) .await .into_iter() - .collect::, _>>()? + .collect::, _>>()? .into_iter() .collect::>>()?; From 8cbaf0de71beacb138e82f0304d23961b58c6852 Mon Sep 17 00:00:00 2001 From: Omer Tuchfeld Date: Thu, 25 Jan 2024 18:27:08 +0100 Subject: [PATCH 07/11] work --- .../cluster_domain_rename/rename_utils.rs | 25 +++++---- .../hostname_rename/filesystem_rename.rs | 55 ++++++++++--------- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index 5aeb80b3..b8040570 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -126,7 +126,7 @@ pub(crate) fn fix_cluster_backup_sh(cluster_backup_sh: &str, original_hostname: let cluster_backup = cluster_backup_sh.to_string(); let pattern = format!(r"NODE_{original_hostname}_IP"); let replacement = format!(r"NODE_{}_IP", env_var_safe(hostname)); - Ok(cluster_backup.replace(dbg!(&pattern), &replacement)) + Ok(cluster_backup.replace(&pattern, &replacement) } pub(crate) async fn fix_kubeconfig(cluster_name: &str, cluster_domain: &str, kubeconfig: &mut Value) -> Result<()> { @@ -219,7 +219,7 @@ fn fix_kubeconfig_server(cluster: &mut serde_json::Map, cluster_d // Could be something like `https://localhost:6443`, ignore return Ok(()); } - .context("no previous value")?; + .context("no previous value")?; Ok(()) } @@ -288,7 +288,10 @@ pub(crate) fn fix_kcm_pod(pod: &mut Value, generated_infra_id: &str) -> Result<( #[cfg(test)] mod test_fix_etcd_static_pod { - use crate::{file_utils::read_file_to_string_sync, ocp_postprocess::cluster_domain_rename::rename_utils::{fix_etcd_pod_yaml, fix_etcd_static_pod,fix_cluster_backup_sh}}; + use crate::{ + file_utils::read_file_to_string_sync, + ocp_postprocess::cluster_domain_rename::rename_utils::{fix_cluster_backup_sh, fix_etcd_pod_yaml, fix_etcd_static_pod}, + }; use anyhow::Result; use serde_json::Value; @@ -396,7 +399,7 @@ pub(crate) fn fix_etcd_pod_yaml(pod_yaml: &str, original_hostname: &str, hostnam "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{hostname}.crt", ), ( - "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{original_hostname}.key", "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-metrics-{hostname}.key", ), ( @@ -404,20 +407,22 @@ pub(crate) fn fix_etcd_pod_yaml(pod_yaml: &str, original_hostname: &str, hostnam "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{hostname}.key", ), ( - "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{original_hostname}.crt", "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-serving-{hostname}.crt", ), ("--target-name={original_hostname}", "--target-name={hostname}"), ]; for (pattern, replacement) in patterns { - let pattern = pattern.replace("{original_hostname}", original_hostname); - let replacement = replacement.replace("{hostname}", &hostname); + let pattern = pattern + .replace("{original_hostname}", original_hostname) + .replace("{original_hostname_safe}", &env_var_safe(original_hostname)); - let pattern = pattern.replace("{original_hostname_safe}", &env_var_safe(original_hostname)); - let replacement = replacement.replace("{hostname_safe}", &env_var_safe(hostname)); + let replacement = replacement + .replace("{hostname}", &hostname) + .replace("{hostname_safe}", &env_var_safe(hostname)); - pod_yaml = pod_yaml.replace(dbg!(&pattern), dbg!(&replacement)).to_string(); + pod_yaml = pod_yaml.replace(&pattern, &replacement).to_string(); } Ok(pod_yaml) diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs index ff66dcdb..36460662 100644 --- a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -75,34 +75,37 @@ pub(crate) async fn fix_filesystem_etcd_configmap_pod_yaml(original_hostname: &s } pub(crate) async fn fix_filesystem_etcd_scripts_cluster_backup_sh(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { - join_all(file_utils::globvec(dir, "**/etcd-scripts/cluster-backup.sh")?.into_iter().map(|file_path| { - let cluster_backup_path = file_path.clone(); - let original_hostname = original_hostname.to_string(); - let hostname = hostname.to_string(); - tokio::spawn(async move { - async move { - let contents = read_file_to_string(&file_path) - .await - .context("reading cluster-backup.sh")?; + join_all( + file_utils::globvec(dir, "**/etcd-scripts/cluster-backup.sh")? + .into_iter() + .map(|file_path| { + let cluster_backup_path = file_path.clone(); + let original_hostname = original_hostname.to_string(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path).await.context("reading cluster-backup.sh")?; - commit_file( - file_path, - dbg!(rename_utils::fix_cluster_backup_sh(&contents, &original_hostname, &hostname).context("fixing cluster-backup.sh")?), - ) - .await - .context("writing cluster-backup.sh to disk")?; + commit_file( + file_path, + rename_utils::fix_cluster_backup_sh(&contents, &original_hostname, &hostname) + .context("fixing cluster-backup.sh")?, + ) + .await + .context("writing cluster-backup.sh to disk")?; - anyhow::Ok(()) - } - .await - .context(format!("fixing cluster-backup.sh {:?}", cluster_backup_path)) - }) - })) - .await - .into_iter() - .collect::, _>>()? - .into_iter() - .collect::>>()?; + anyhow::Ok(()) + } + .await + .context(format!("fixing cluster-backup.sh {:?}", cluster_backup_path)) + }) + }), + ) + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .collect::>>()?; Ok(()) } From 3a091d726ee71ea3e0ad293fac77f762b53d0bf8 Mon Sep 17 00:00:00 2001 From: Omer Tuchfeld Date: Thu, 25 Jan 2024 18:43:06 +0100 Subject: [PATCH 08/11] work --- src/ocp_postprocess/cluster_domain_rename/rename_utils.rs | 2 +- src/ocp_postprocess/hostname_rename/filesystem_rename.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index b8040570..310bb72e 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -126,7 +126,7 @@ pub(crate) fn fix_cluster_backup_sh(cluster_backup_sh: &str, original_hostname: let cluster_backup = cluster_backup_sh.to_string(); let pattern = format!(r"NODE_{original_hostname}_IP"); let replacement = format!(r"NODE_{}_IP", env_var_safe(hostname)); - Ok(cluster_backup.replace(&pattern, &replacement) + Ok(cluster_backup.replace(&pattern, &replacement)) } pub(crate) async fn fix_kubeconfig(cluster_name: &str, cluster_domain: &str, kubeconfig: &mut Value) -> Result<()> { diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs index 36460662..60bc0df8 100644 --- a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -42,7 +42,7 @@ pub(crate) async fn fix_filesystem_etcd_static_pods(original_hostname: &str, hos } pub(crate) async fn fix_filesystem_etcd_configmap_pod_yaml(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { - join_all(file_utils::globvec(dir, "**/etcd-pod/pod.yaml")?.into_iter().map(|file_path| { + join_all(file_utils::globvec(dir, "**/*etcd-pod/pod.yaml")?.into_iter().map(|file_path| { let etcd_pod_path = file_path.clone(); let original_hostname = original_hostname.to_string(); let hostname = hostname.to_string(); From b412053bb799a119599e28871ba23c2f6543a927 Mon Sep 17 00:00:00 2001 From: Michail Resvanis Date: Fri, 26 Jan 2024 11:09:24 +0100 Subject: [PATCH 09/11] Replace hostname in kube-apiserver-startup-monitor-pod Signed-off-by: Michail Resvanis --- .../cluster_domain_rename/rename_utils.rs | 44 +++++++++++++ src/ocp_postprocess/hostname_rename.rs | 8 +++ .../hostname_rename/filesystem_rename.rs | 66 +++++++++++++++++++ 3 files changed, 118 insertions(+) diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index 310bb72e..629e5850 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -730,3 +730,47 @@ pub(crate) fn fix_machineconfig(machineconfig: &mut Value, cluster_domain: &str) Ok(()) } + +pub(crate) fn fix_kapi_startup_monitor_pod_container_args(pod: &mut Value, hostname: &str) -> Result<()> { + let containers = &mut pod + .pointer_mut("/spec/containers") + .context("containers not found")? + .as_array_mut() + .context("containers not an object")?; + + if containers.is_empty() { + bail!("expected at least one container in pod.yaml"); + } + + containers + .iter_mut() + .filter(|container| container["name"] == "startup-monitor") + .try_for_each(|container| { + let args = container + .pointer_mut("/args") + .context("args not found")? + .as_array_mut() + .context("args not an array")?; + + ensure!(!args.is_empty(), "expected at least one arg in container"); + + let arg_idx = args + .iter_mut() + .enumerate() + .find_map(|(i, arg)| arg.as_str()?.starts_with("--node-name=").then_some(i)) + .context("--node-name not found")?; + + args[arg_idx] = serde_json::Value::String(format!("--node-name={}", hostname)); + + Ok(()) + })?; + + Ok(()) +} + +pub(crate) fn fix_kapi_startup_monitor_pod_yaml(pod_yaml: &str, original_hostname: &str, hostname: &str) -> Result { + let pod_yaml = pod_yaml.to_string(); + let pattern = format!(r"--node-name={}", original_hostname); + let replacement = format!(r"--node-name={}", hostname); + Ok(pod_yaml.replace(&pattern, &replacement)) +} diff --git a/src/ocp_postprocess/hostname_rename.rs b/src/ocp_postprocess/hostname_rename.rs index b7e4b2ff..6af94701 100644 --- a/src/ocp_postprocess/hostname_rename.rs +++ b/src/ocp_postprocess/hostname_rename.rs @@ -50,6 +50,14 @@ async fn fix_dir_resources(original_hostname: &str, hostname: &str, dir: &Path) .await .context("fixing etcd certs clsuter-backup.sh")?; + filesystem_rename::fix_filesystem_kapi_startup_monitor_pod(hostname, dir) + .await + .context("fixing kube-apiserver-startup-monitor-pod")?; + + filesystem_rename::fix_filesystem_kapi_startup_monitor_configmap_pod_yaml(original_hostname, hostname, dir) + .await + .context("fixing kube-apiserver-startup-monitor-pod configmap pod yaml")?; + Ok(()) } diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs index 60bc0df8..40326d9b 100644 --- a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -109,3 +109,69 @@ pub(crate) async fn fix_filesystem_etcd_scripts_cluster_backup_sh(original_hostn Ok(()) } + +pub(crate) async fn fix_filesystem_kapi_startup_monitor_pod(hostname: &str, dir: &Path) -> Result<()> { + join_all(file_utils::globvec(dir, "**/kube-apiserver-startup-monitor-pod.yaml")?.into_iter().map(|file_path| { + let kapi_startup_monitor_pod_path = file_path.clone(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path) + .await + .context("reading kube-apiserver-startup-monitor-pod.yaml")?; + + let mut pod: Value = serde_json::from_str(&contents).context("parsing kube-apiserver-startup-monitor-pod.yaml")?; + + rename_utils::fix_kapi_startup_monitor_pod_container_args(&mut pod, &hostname).context("fixing kube-apiserver-startup-monitor-pod.yaml")?; + + commit_file(file_path, serde_json::to_string(&pod).context("serializing kube-apiserver-startup-monitor-pod.yaml")?) + .await + .context("writing kube-apiserver-startup-monitor-pod.yaml to disk")?; + + anyhow::Ok(()) + } + .await + .context(format!("fixing kube-apiserver-startup-monitor-pod.yaml {:?}", kapi_startup_monitor_pod_path)) + }) + })) + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .collect::>>()?; + + Ok(()) +} + +pub(crate) async fn fix_filesystem_kapi_startup_monitor_configmap_pod_yaml(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { + join_all(file_utils::globvec(dir, "**/kube-apiserver-pod*/configmaps/kube-apiserver-pod/kube-apiserver-startup-monitor-pod.yaml")?.into_iter().map(|file_path| { + let kapi_startup_monitor_pod_path = file_path.clone(); + let original_hostname = original_hostname.to_string(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path) + .await + .context("reading kube-apiserver-startup-monitor-pod.yaml")?; + + commit_file( + file_path, + rename_utils::fix_kapi_startup_monitor_pod_yaml(&contents, &original_hostname, &hostname).context("fixing kube-apiserver-startup-monitor-pod.yaml")?, + ) + .await + .context("writing kube-apiserver-startup-monitor-pod.yaml to disk")?; + + anyhow::Ok(()) + } + .await + .context(format!("fixing kube-apiserver-startup-monitor-pod.yaml {:?}", kapi_startup_monitor_pod_path)) + }) + })) + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .collect::>>()?; + + Ok(()) +} From 72dfdcf231a56d1f677a3773d844f667e0fe1c51 Mon Sep 17 00:00:00 2001 From: Omer Tuchfeld Date: Fri, 26 Jan 2024 14:07:09 +0100 Subject: [PATCH 10/11] work --- run_seed.sh | 2 +- src/file_utils.rs | 8 -- .../cluster_domain_rename/rename_utils.rs | 64 +---------- src/ocp_postprocess/hostname_rename.rs | 4 +- .../hostname_rename/etcd_rename.rs | 4 +- .../hostname_rename/filesystem_rename.rs | 106 +++++++++++------- 6 files changed, 74 insertions(+), 114 deletions(-) diff --git a/run_seed.sh b/run_seed.sh index a279e372..556a678d 100755 --- a/run_seed.sh +++ b/run_seed.sh @@ -3,7 +3,7 @@ set -ex RELEASE_IMAGE=quay.io/openshift-release-dev/ocp-release:4.13.0-x86_64 -BACKUP_IMAGE=${1:-quay.io/otuchfel/ostbackup:infoseed} +BACKUP_IMAGE=${1:-quay.io/otuchfel/ostbackup:seed} AUTH_FILE=${AUTH_FILE:-~/omer-ps} SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" diff --git a/src/file_utils.rs b/src/file_utils.rs index f440a7e8..69d88943 100644 --- a/src/file_utils.rs +++ b/src/file_utils.rs @@ -6,7 +6,6 @@ use anyhow::{bail, Context, Result}; use base64::{engine::general_purpose::STANDARD as base64_standard, Engine as _}; use serde_json::Value; use std::{ - io::Read, path::{Path, PathBuf}, sync::atomic::{AtomicBool, Ordering::Relaxed}, }; @@ -47,13 +46,6 @@ pub(crate) async fn read_file_to_string(file_path: &Path) -> Result { Ok(contents) } -pub(crate) fn read_file_to_string_sync(file_path: &Path) -> Result { - let mut file = std::fs::File::open(file_path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents).context("failed to read file")?; - Ok(contents) -} - pub(crate) async fn get_filesystem_yaml(file_location: &FileLocation) -> Result { serde_yaml::from_str(read_file_to_string(&PathBuf::from(&file_location.path)).await?.as_str()).context("failed to parse yaml") } diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index 629e5850..61167f75 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -286,64 +286,6 @@ pub(crate) fn fix_kcm_pod(pod: &mut Value, generated_infra_id: &str) -> Result<( Ok(()) } -#[cfg(test)] -mod test_fix_etcd_static_pod { - use crate::{ - file_utils::read_file_to_string_sync, - ocp_postprocess::cluster_domain_rename::rename_utils::{fix_cluster_backup_sh, fix_etcd_pod_yaml, fix_etcd_static_pod}, - }; - use anyhow::Result; - use serde_json::Value; - - #[test] - fn test_fix_etcd_scripts_cluster_backup() -> Result<()> { - let path_str = "backup/etc_orig/kubernetes/static-pod-resources/etcd-certs/configmaps/etcd-scripts/cluster-backup.sh"; - let path = std::path::Path::new(path_str); - let script = read_file_to_string_sync(path)?; - let script = fix_cluster_backup_sh(&script, "seed", "test-hostname")?; - assert!(!script.contains("seed"), "seed still in pod: {}", script); - - Ok(()) - } - - #[test] - fn test_fix_etcd_static_pod6() -> Result<()> { - let path_str = "backup/etc_orig/kubernetes/static-pod-resources/etcd-pod-6/etcd-pod.yaml"; - let path = std::path::Path::new(path_str); - let pod = read_file_to_string_sync(path)?; - let mut pod: Value = serde_json::from_str(&pod)?; - fix_etcd_static_pod(&mut pod, "seed", "test-hostname")?; - let pod = serde_json::to_string(&pod)?; - assert!(!pod.contains("seed"), "seed still in pod: {}", pod); - - Ok(()) - } - - #[test] - fn test_fix_etcd_static_pod() -> Result<()> { - let path_str = "backup/etc_orig/kubernetes/manifests/etcd-pod.yaml"; - let path = std::path::Path::new(path_str); - let pod = read_file_to_string_sync(path)?; - let mut pod: Value = serde_json::from_str(&pod)?; - fix_etcd_static_pod(&mut pod, "seed", "test-hostname")?; - let pod = serde_json::to_string(&pod)?; - assert!(!pod.contains("seed"), "seed still in pod: {}", pod); - - Ok(()) - } - - #[test] - fn test_fix_etcd_static_pod_configmap() -> Result<()> { - let path_str = "backup/etc_orig/kubernetes/static-pod-resources/etcd-pod-6/configmaps/etcd-pod/pod.yaml"; - let path = std::path::Path::new(path_str); - let pod = read_file_to_string_sync(path)?; - let pod = fix_etcd_pod_yaml(&pod, "seed", "test-hostname")?; - assert!(!pod.contains("seed"), "seed still in pod: {}", pod); - - Ok(()) - } -} - // Mimics https://github.com/openshift/cluster-etcd-operator/blob/5973046e2d216b290740cf64a071a272bbf83aea/pkg/etcdenvvar/etcd_env.go#L244-L246 pub(crate) fn env_var_safe(node_name: &str) -> String { node_name.replace(['-', '.'], "_") @@ -419,7 +361,7 @@ pub(crate) fn fix_etcd_pod_yaml(pod_yaml: &str, original_hostname: &str, hostnam .replace("{original_hostname_safe}", &env_var_safe(original_hostname)); let replacement = replacement - .replace("{hostname}", &hostname) + .replace("{hostname}", hostname) .replace("{hostname_safe}", &env_var_safe(hostname)); pod_yaml = pod_yaml.replace(&pattern, &replacement).to_string(); @@ -554,7 +496,7 @@ fn fix_etcd_static_pod_container(container: &mut Value, original_hostname: &str, ( format!("NODE_{original_hostname}_ETCD_NAME").as_str(), Some(format!("NODE_{hostname}_ETCD_NAME").as_str()), - Some(format!("{hostname}").as_str()), + Some(hostname), ), ( format!("NODE_{original_hostname}_ETCD_URL_HOST").as_str(), @@ -574,7 +516,7 @@ fn fix_etcd_static_pod_container(container: &mut Value, original_hostname: &str, Ok(()) } -fn adjust_env(envs: &mut Vec, env_name: &str, new_name: Option<&str>, new_value: Option<&str>) -> Result<()> { +fn adjust_env(envs: &mut [Value], env_name: &str, new_name: Option<&str>, new_value: Option<&str>) -> Result<()> { let found_env = envs .iter_mut() .find_map(|env| (env.as_object()?.get("name") == Some(&Value::String(env_name.to_string()))).then_some(env)); diff --git a/src/ocp_postprocess/hostname_rename.rs b/src/ocp_postprocess/hostname_rename.rs index 6af94701..8e4ecf7a 100644 --- a/src/ocp_postprocess/hostname_rename.rs +++ b/src/ocp_postprocess/hostname_rename.rs @@ -13,7 +13,7 @@ pub(crate) async fn rename_all( ) -> Result<(), anyhow::Error> { let original_hostname = fix_etcd_resources(etcd_client, hostname).await.context("renaming etcd resources")?; - fix_filesystem_resources(&original_hostname, &hostname, static_dirs, static_files) + fix_filesystem_resources(&original_hostname, hostname, static_dirs, static_files) .await .context("renaming filesystem resources")?; @@ -46,7 +46,7 @@ async fn fix_dir_resources(original_hostname: &str, hostname: &str, dir: &Path) .await .context("fixing etcd static pod configmap pod yaml")?; - filesystem_rename::fix_filesystem_etcd_scripts_cluster_backup_sh(&original_hostname, hostname, dir) + filesystem_rename::fix_filesystem_etcd_scripts_cluster_backup_sh(original_hostname, hostname, dir) .await .context("fixing etcd certs clsuter-backup.sh")?; diff --git a/src/ocp_postprocess/hostname_rename/etcd_rename.rs b/src/ocp_postprocess/hostname_rename/etcd_rename.rs index 2d9bafb3..4f6604d2 100644 --- a/src/ocp_postprocess/hostname_rename/etcd_rename.rs +++ b/src/ocp_postprocess/hostname_rename/etcd_rename.rs @@ -264,14 +264,14 @@ pub(crate) async fn fix_etcd_pod(etcd_client: &Arc, original_ho // TODO: We can't roundtrip arbitrary YAML, ask etcd folks to stop using YAML // That's why we have to do primitive string manipulation here instead of proper // parsing - let mut pod_yaml = data + let pod_yaml = data .get_mut("pod.yaml") .context("no pod.yaml")? .as_str() .context("pod.yaml not a string")? .to_string(); - let pod_yaml = fix_etcd_pod_yaml(&mut pod_yaml, original_hostname, hostname).context("could not fix pod yaml")?; + let pod_yaml = fix_etcd_pod_yaml(&pod_yaml, original_hostname, hostname).context("could not fix pod yaml")?; data.insert("pod.yaml".to_string(), serde_json::Value::String(pod_yaml)); diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs index 40326d9b..176a7a28 100644 --- a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -111,29 +111,40 @@ pub(crate) async fn fix_filesystem_etcd_scripts_cluster_backup_sh(original_hostn } pub(crate) async fn fix_filesystem_kapi_startup_monitor_pod(hostname: &str, dir: &Path) -> Result<()> { - join_all(file_utils::globvec(dir, "**/kube-apiserver-startup-monitor-pod.yaml")?.into_iter().map(|file_path| { - let kapi_startup_monitor_pod_path = file_path.clone(); - let hostname = hostname.to_string(); - tokio::spawn(async move { - async move { - let contents = read_file_to_string(&file_path) - .await - .context("reading kube-apiserver-startup-monitor-pod.yaml")?; + join_all( + file_utils::globvec(dir, "**/kube-apiserver-startup-monitor-pod.yaml")? + .into_iter() + .map(|file_path| { + let kapi_startup_monitor_pod_path = file_path.clone(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path) + .await + .context("reading kube-apiserver-startup-monitor-pod.yaml")?; - let mut pod: Value = serde_json::from_str(&contents).context("parsing kube-apiserver-startup-monitor-pod.yaml")?; + let mut pod: Value = serde_json::from_str(&contents).context("parsing kube-apiserver-startup-monitor-pod.yaml")?; - rename_utils::fix_kapi_startup_monitor_pod_container_args(&mut pod, &hostname).context("fixing kube-apiserver-startup-monitor-pod.yaml")?; + rename_utils::fix_kapi_startup_monitor_pod_container_args(&mut pod, &hostname) + .context("fixing kube-apiserver-startup-monitor-pod.yaml")?; - commit_file(file_path, serde_json::to_string(&pod).context("serializing kube-apiserver-startup-monitor-pod.yaml")?) - .await - .context("writing kube-apiserver-startup-monitor-pod.yaml to disk")?; + commit_file( + file_path, + serde_json::to_string(&pod).context("serializing kube-apiserver-startup-monitor-pod.yaml")?, + ) + .await + .context("writing kube-apiserver-startup-monitor-pod.yaml to disk")?; - anyhow::Ok(()) - } - .await - .context(format!("fixing kube-apiserver-startup-monitor-pod.yaml {:?}", kapi_startup_monitor_pod_path)) - }) - })) + anyhow::Ok(()) + } + .await + .context(format!( + "fixing kube-apiserver-startup-monitor-pod.yaml {:?}", + kapi_startup_monitor_pod_path + )) + }) + }), + ) .await .into_iter() .collect::, _>>()? @@ -143,30 +154,45 @@ pub(crate) async fn fix_filesystem_kapi_startup_monitor_pod(hostname: &str, dir: Ok(()) } -pub(crate) async fn fix_filesystem_kapi_startup_monitor_configmap_pod_yaml(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { - join_all(file_utils::globvec(dir, "**/kube-apiserver-pod*/configmaps/kube-apiserver-pod/kube-apiserver-startup-monitor-pod.yaml")?.into_iter().map(|file_path| { - let kapi_startup_monitor_pod_path = file_path.clone(); - let original_hostname = original_hostname.to_string(); - let hostname = hostname.to_string(); - tokio::spawn(async move { - async move { - let contents = read_file_to_string(&file_path) +pub(crate) async fn fix_filesystem_kapi_startup_monitor_configmap_pod_yaml( + original_hostname: &str, + hostname: &str, + dir: &Path, +) -> Result<()> { + join_all( + file_utils::globvec( + dir, + "**/kube-apiserver-pod*/configmaps/kube-apiserver-pod/kube-apiserver-startup-monitor-pod.yaml", + )? + .into_iter() + .map(|file_path| { + let kapi_startup_monitor_pod_path = file_path.clone(); + let original_hostname = original_hostname.to_string(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path) + .await + .context("reading kube-apiserver-startup-monitor-pod.yaml")?; + + commit_file( + file_path, + rename_utils::fix_kapi_startup_monitor_pod_yaml(&contents, &original_hostname, &hostname) + .context("fixing kube-apiserver-startup-monitor-pod.yaml")?, + ) .await - .context("reading kube-apiserver-startup-monitor-pod.yaml")?; + .context("writing kube-apiserver-startup-monitor-pod.yaml to disk")?; - commit_file( - file_path, - rename_utils::fix_kapi_startup_monitor_pod_yaml(&contents, &original_hostname, &hostname).context("fixing kube-apiserver-startup-monitor-pod.yaml")?, - ) + anyhow::Ok(()) + } .await - .context("writing kube-apiserver-startup-monitor-pod.yaml to disk")?; - - anyhow::Ok(()) - } - .await - .context(format!("fixing kube-apiserver-startup-monitor-pod.yaml {:?}", kapi_startup_monitor_pod_path)) - }) - })) + .context(format!( + "fixing kube-apiserver-startup-monitor-pod.yaml {:?}", + kapi_startup_monitor_pod_path + )) + }) + }), + ) .await .into_iter() .collect::, _>>()? From d82f5296199c7a4f16e38ba287ebe86848c58e37 Mon Sep 17 00:00:00 2001 From: Michail Resvanis Date: Fri, 26 Jan 2024 15:00:25 +0100 Subject: [PATCH 11/11] Replace hostname in etcd-scripts etcd.env Signed-off-by: Michail Resvanis --- .../cluster_domain_rename/rename_utils.rs | 36 +++++++++++++++++++ src/ocp_postprocess/hostname_rename.rs | 6 +++- .../hostname_rename/filesystem_rename.rs | 31 ++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs index 61167f75..2318058e 100644 --- a/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs +++ b/src/ocp_postprocess/cluster_domain_rename/rename_utils.rs @@ -129,6 +129,42 @@ pub(crate) fn fix_cluster_backup_sh(cluster_backup_sh: &str, original_hostname: Ok(cluster_backup.replace(&pattern, &replacement)) } +pub(crate) fn fix_etcd_env(etcd_env: &str, original_hostname: &str, hostname: &str) -> Result { + let mut etcd_env = etcd_env.to_string(); + let patterns = [ + (r#"NODE_{original_hostname_safe}_IP"#, r#"NODE_{hostname_safe}_IP"#), + ( + r#"NODE_{original_hostname_safe}_ETCD_NAME="{original_hostname}""#, + r#"NODE_{hostname_safe}_ETCD_NAME="{hostname}""#, + ), + ( + r#"NODE_{original_hostname_safe}_ETCD_URL_HOST"#, + r#"NODE_{hostname_safe}_ETCD_URL_HOST"#, + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{original_hostname}.crt", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.crt", + ), + ( + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{original_hostname}.key", + "/etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-{hostname}.key", + ), + ]; + for (pattern, replacement) in patterns { + let pattern = pattern + .replace("{original_hostname}", original_hostname) + .replace("{original_hostname_safe}", &env_var_safe(original_hostname)); + + let replacement = replacement + .replace("{hostname}", hostname) + .replace("{hostname_safe}", &env_var_safe(hostname)); + + etcd_env = etcd_env.replace(&pattern, &replacement).to_string(); + } + + Ok(etcd_env) +} + pub(crate) async fn fix_kubeconfig(cluster_name: &str, cluster_domain: &str, kubeconfig: &mut Value) -> Result<()> { let is_kubelet_kubeconfig = kubeconfig .pointer_mut("/contexts") diff --git a/src/ocp_postprocess/hostname_rename.rs b/src/ocp_postprocess/hostname_rename.rs index 8e4ecf7a..ad53c410 100644 --- a/src/ocp_postprocess/hostname_rename.rs +++ b/src/ocp_postprocess/hostname_rename.rs @@ -48,7 +48,11 @@ async fn fix_dir_resources(original_hostname: &str, hostname: &str, dir: &Path) filesystem_rename::fix_filesystem_etcd_scripts_cluster_backup_sh(original_hostname, hostname, dir) .await - .context("fixing etcd certs clsuter-backup.sh")?; + .context("fixing etcd scripts cluster-backup.sh")?; + + filesystem_rename::fix_filesystem_etcd_scripts_etcd_env(original_hostname, hostname, dir) + .await + .context("fixing etcd scripts etcd.env")?; filesystem_rename::fix_filesystem_kapi_startup_monitor_pod(hostname, dir) .await diff --git a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs index 176a7a28..0e12512b 100644 --- a/src/ocp_postprocess/hostname_rename/filesystem_rename.rs +++ b/src/ocp_postprocess/hostname_rename/filesystem_rename.rs @@ -110,6 +110,37 @@ pub(crate) async fn fix_filesystem_etcd_scripts_cluster_backup_sh(original_hostn Ok(()) } +pub(crate) async fn fix_filesystem_etcd_scripts_etcd_env(original_hostname: &str, hostname: &str, dir: &Path) -> Result<()> { + join_all(file_utils::globvec(dir, "**/etcd-scripts/etcd.env")?.into_iter().map(|file_path| { + let etcd_env_path = file_path.clone(); + let original_hostname = original_hostname.to_string(); + let hostname = hostname.to_string(); + tokio::spawn(async move { + async move { + let contents = read_file_to_string(&file_path).await.context("reading etcd.env")?; + + commit_file( + file_path, + rename_utils::fix_etcd_env(&contents, &original_hostname, &hostname).context("fixing etcd.env")?, + ) + .await + .context("writing etcd.env to disk")?; + + anyhow::Ok(()) + } + .await + .context(format!("fixing etcd.env {:?}", etcd_env_path)) + }) + })) + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .collect::>>()?; + + Ok(()) +} + pub(crate) async fn fix_filesystem_kapi_startup_monitor_pod(hostname: &str, dir: &Path) -> Result<()> { join_all( file_utils::globvec(dir, "**/kube-apiserver-startup-monitor-pod.yaml")?