Skip to content

Commit

Permalink
Feat | Read apps from all namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
dag-andersen committed Dec 21, 2024
1 parent 74a6f35 commit f7e1bbe
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 41 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ output/
/apps_target_branch.yaml
/apps_base_branch.yaml
venv/
base-branch/
target-branch/
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Overrides done by argocd-diff-preview
# These will happen after the values.yaml file is loaded
# These will happen AFTER the values.yaml file is loaded
notifications:
enabled: false
dex:
Expand Down
9 changes: 9 additions & 0 deletions argocd-config/values-pre.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Overrides done by argocd-diff-preview
# These will happen BEFORE the values.yaml file is loaded
notifications:
enabled: false
dex:
enabled: false
configs:
params:
application.namespaces: "*"
11 changes: 6 additions & 5 deletions src/argo_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct ArgoResource {
pub yaml: serde_yaml::Value,
pub kind: ApplicationKind,
pub name: String,
pub namespace: String,
}

impl std::fmt::Display for ArgoResource {
Expand All @@ -33,11 +34,6 @@ impl PartialEq for ArgoResource {
}

impl ArgoResource {
pub fn set_namespace(mut self, namespace: &str) -> ArgoResource {
self.yaml["metadata"]["namespace"] = serde_yaml::Value::String(namespace.to_string());
self
}

pub fn set_project_to_default(mut self) -> Result<ArgoResource, Box<dyn Error>> {
let spec = match self.kind {
ApplicationKind::Application => self.yaml["spec"].as_mapping_mut(),
Expand Down Expand Up @@ -166,11 +162,16 @@ impl ArgoResource {
_ => None,
})?;

let namespace = k8s_resource.yaml["metadata"]["namespace"]
.as_str()
.unwrap_or("default");

match k8s_resource.yaml["metadata"]["name"].as_str() {
Some(name) => Some(ArgoResource {
kind,
file_name: k8s_resource.file_name,
name: name.to_string(),
namespace: namespace.to_string(),
yaml: k8s_resource.yaml,
}),
_ => None,
Expand Down
57 changes: 36 additions & 21 deletions src/argocd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,23 @@ pub struct ArgoCDOptions<'a> {

const CONFIG_PATH: &str = "argocd-config";

pub fn create_namespace() -> Result<(), Box<dyn Error>> {
run_command("kubectl create ns argocd").map_err(|e| {
error!("❌ Failed to create namespace argocd");
CommandError::new(e)
})?;
debug!("🦑 Namespace argocd created successfully");
Ok(())
pub fn create_namespace(s: &str) -> Result<(), Box<dyn Error>> {
let already_exist = run_command(&format!("kubectl get ns {}", s)).is_ok();

match already_exist {
true => {
debug!("🦑 Namespace {} already exists", s);
Ok(())
}
false => {
run_command(&format!("kubectl create ns {}", s)).map_err(|e| {
error!("❌ Failed to create namespace {}", s);
CommandError::new(e)
})?;
debug!("🦑 Namespace {} created successfully", s);
Ok(())
}
}
}

pub async fn install_argo_cd(options: ArgoCDOptions<'_>) -> Result<(), Box<dyn Error>> {
Expand All @@ -28,24 +38,26 @@ pub async fn install_argo_cd(options: ArgoCDOptions<'_>) -> Result<(), Box<dyn E
options.version.unwrap_or("latest")
);

let (values, values_override) = match std::fs::read_dir(CONFIG_PATH) {
let (values_pre, values, values_post) = match std::fs::read_dir(CONFIG_PATH) {
Ok(dir) => {
debug!("📂 Files in folder 'argocd-config':");
for file in dir {
debug!("- 📄 {:?}", file.unwrap().file_name());
}
let values_exist = std::fs::metadata(format!("{}/values.yaml", CONFIG_PATH))
let values_pre = std::fs::metadata(format!("{}/values-pre.yaml", CONFIG_PATH))
.is_ok()
.then_some(format!("-f {}/values-pre.yaml", CONFIG_PATH));
let values = std::fs::metadata(format!("{}/values.yaml", CONFIG_PATH))
.is_ok()
.then_some(format!("-f {}/values.yaml", CONFIG_PATH));
let values_override_exist =
std::fs::metadata(format!("{}/values-override.yaml", CONFIG_PATH))
.is_ok()
.then_some(format!("-f {}/values-override.yaml", CONFIG_PATH));
(values_exist, values_override_exist)
let values_post = std::fs::metadata(format!("{}/values-post.yaml", CONFIG_PATH))
.is_ok()
.then_some(format!("-f {}/values-post.yaml", CONFIG_PATH));
(values, values_pre, values_post)
}
Err(_e) => {
info!("📂 Folder '{}' doesn't exist. Installing Argo CD Helm Chart with default configuration", CONFIG_PATH);
(None, None)
(None, None, None)
}
};

Expand All @@ -56,15 +68,17 @@ pub async fn install_argo_cd(options: ArgoCDOptions<'_>) -> Result<(), Box<dyn E
})?;

let helm_install_command = format!(
"helm install argocd argo/argo-cd -n argocd {} {} {}",
"helm install argocd argo/argo-cd -n argocd {} {} {} {}",
values_pre.unwrap_or_default(),
values.unwrap_or_default(),
values_override.unwrap_or_default(),
values_post.unwrap_or_default(),
options
.version
.map(|a| format!("--version {}", a))
.unwrap_or_default(),
);

debug!("Installing Argo CD Helm Chart...");
run_command(&helm_install_command).map_err(|e| {
error!("❌ Failed to install Argo CD");
CommandError::new(e)
Expand Down Expand Up @@ -109,14 +123,12 @@ pub async fn install_argo_cd(options: ArgoCDOptions<'_>) -> Result<(), Box<dyn E
}
}
let password_encoded = password_encoded.unwrap().stdout;
let password_decoded = BASE64_STANDARD.decode(password_encoded).map_err(|e| {
let password_decoded = BASE64_STANDARD.decode(password_encoded).inspect_err(|e| {
error!("❌ Failed to decode password: {}", e);
e
})?;

String::from_utf8(password_decoded).map_err(|e| {
String::from_utf8(password_decoded).inspect_err(|_e| {
error!("❌ failed to convert password to string");
e
})?
};

Expand Down Expand Up @@ -159,6 +171,9 @@ pub async fn install_argo_cd(options: ArgoCDOptions<'_>) -> Result<(), Box<dyn E
}
}

// Add extra permissions to the default AppProject
let _ = run_command("argocd proj add-source-namespace default *", None);

info!("🦑 Argo CD installed successfully");
Ok(())
}
6 changes: 4 additions & 2 deletions src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ pub async fn delete_applications() -> Result<(), Box<dyn Error>> {
loop {
debug!("🗑 Deleting ApplicationSets");

match run_command("kubectl delete applicationsets.argoproj.io --all -n argocd") {
match run_command("kubectl delete applicationsets.argoproj.io --all --all-namespaces") {
Ok(_) => debug!("🗑 Deleted ApplicationSets"),
Err(e) => {
error!("❌ Failed to delete applicationsets: {}", &e.stderr)
Expand All @@ -230,9 +230,11 @@ pub async fn delete_applications() -> Result<(), Box<dyn Error>> {
debug!("🗑 Deleting Applications");

let mut child = spawn_command(
"kubectl delete applications.argoproj.io --all -n argocd",
"kubectl delete applications.argoproj.io --all --all-namespaces
",
None,
);

tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
if run_command("kubectl get applications -A --no-headers")
.map(|e| e.stdout.trim().is_empty())
Expand Down
27 changes: 17 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use argo_resource::ArgoResource;
use argocd::create_namespace;
use branch::{Branch, BranchType};
use error::CommandOutput;
use log::{debug, error, info};
Expand Down Expand Up @@ -124,13 +125,12 @@ impl FromStr for ClusterTool {

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
run().await.map_err(|e| {
run().await.inspect_err(|e| {
let opt = Opt::from_args();
error!("❌ {}", e);
if !opt.keep_cluster_alive {
cleanup_cluster(opt.local_cluster_tool, &opt.cluster_name);
}
e
})
}

Expand Down Expand Up @@ -299,6 +299,12 @@ async fn run() -> Result<(), Box<dyn Error>> {
return Ok(());
}

let unique_namespaces = base_apps
.iter()
.map(|a| a.namespace.clone())
.chain(target_apps.iter().map(|a| a.namespace.clone()))
.collect::<std::collections::HashSet<String>>();

fs::write(base_branch.app_file(), applications_to_string(base_apps))?;
fs::write(
target_branch.app_file(),
Expand All @@ -310,15 +316,17 @@ async fn run() -> Result<(), Box<dyn Error>> {
ClusterTool::Minikube => minikube::create_cluster()?,
}

argocd::create_namespace()?;
// Create a namespace for each application
for namespace in unique_namespaces {
create_namespace(&namespace)?;
}

create_folder_if_not_exists(secrets_folder)?;
match apply_folder(secrets_folder) {
Ok(count) if count > 0 => info!("🤫 Applied {} secrets", count),
Ok(_) => info!("🤷 No secrets found in {}", secrets_folder),
Err(e) => {
error!("❌ Failed to apply secrets");
return Err(e);
return Err(e.into());
}
}

Expand Down Expand Up @@ -401,15 +409,14 @@ fn cleanup_cluster(tool: ClusterTool, cluster_name: &str) {
}

fn apply_manifest(file_name: &str) -> Result<CommandOutput, CommandOutput> {
run_command(&format!("kubectl apply -f {}", file_name)).map_err(|e| {
run_command(&format!("kubectl apply -f {}", file_name)).inspect_err(|_e| {
error!("❌ Failed to apply manifest: {}", file_name);
e
})
}

fn apply_folder(folder_name: &str) -> Result<u64, Box<dyn Error>> {
fn apply_folder(folder_name: &str) -> Result<u64, String> {
if !PathBuf::from(folder_name).is_dir() {
return Err(format!("{} is not a directory", folder_name).into());
return Err(format!("{} is not a directory", folder_name));
}
let mut count = 0;
if let Ok(entries) = fs::read_dir(folder_name) {
Expand All @@ -419,7 +426,7 @@ fn apply_folder(folder_name: &str) -> Result<u64, Box<dyn Error>> {
if file_name.ends_with(".yaml") || file_name.ends_with(".yml") {
match apply_manifest(file_name) {
Ok(_) => count += 1,
Err(e) => return Err(e.stderr.into()),
Err(e) => return Err(e.stderr),
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl Clone for K8sResource {
}
}

pub fn get_applications_for_both_branches<'a>(
pub fn get_applications_for_both_branches(
base_branch: &Branch,
target_branch: &Branch,
regex: &Option<Regex>,
Expand Down Expand Up @@ -195,7 +195,6 @@ fn patch_applications(
.map(|a| {
let app_name = a.name.clone();
let app: Result<ArgoResource, Box<dyn Error>> = a
.set_namespace("argocd")
.remove_sync_policy()
.set_project_to_default()
.and_then(|a| a.point_destination_to_in_cluster())
Expand Down
3 changes: 3 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::path::PathBuf;
use std::process::{Child, Stdio};
use std::{fs, process::Command};

use log::debug;

use crate::error::CommandOutput;

pub fn create_folder_if_not_exists(folder_name: &str) -> Result<(), Box<dyn Error>> {
Expand All @@ -25,6 +27,7 @@ pub fn run_command_in_dir(
command: &str,
current_dir: &str,
) -> Result<CommandOutput, CommandOutput> {
debug!("Running command: {}", command);
let args = command.split_whitespace().collect::<Vec<&str>>();
run_command_from_list(args, Some(current_dir))
}
Expand Down

0 comments on commit f7e1bbe

Please sign in to comment.