Skip to content

Commit

Permalink
Support for secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
byrnedo committed Aug 2, 2024
1 parent eca4894 commit 547b936
Show file tree
Hide file tree
Showing 16 changed files with 433 additions and 152 deletions.
15 changes: 15 additions & 0 deletions hack/test-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,19 @@ spec:
containers:
- name: nginx1
image: nginx:1.14.2
env:
- name: TEST_SECRET
valueFrom:
secretKeyRef:
name: test
key: password
volumeMounts:
- name: test-mount
mountPath: /etc/foo
readOnly: true
volumes:
- name: test-mount
secret:
secretName: test
optional: false
---
9 changes: 9 additions & 0 deletions hack/test-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: test
namespace: foo
type: Opaque
data:
username: dXNlcg==
password: NTRmNDFkMTJlOGZh
7 changes: 5 additions & 2 deletions src/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub enum DeleteCommands {
Node(DeleteResourceArgs),
Ingress(DeleteResourceArgs),
Cronjob(DeleteResourceArgs),
Secret(DeleteResourceArgs),
}

#[derive(Debug, Args)]
Expand All @@ -39,6 +40,8 @@ pub async fn delete(args: DeleteArgs) -> Result<(), Box<dyn Error>> {
DeleteCommands::Node(args) => delete_node(args).await?,
DeleteCommands::Ingress(args) => delete_resource(ResourceType::Ingress, args).await?,
DeleteCommands::Cronjob(args) => delete_resource(ResourceType::CronJob, args).await?,
DeleteCommands::Secret(args) => delete_resource(ResourceType::Secret, args).await?,
_ => todo!()
}
Ok(())
}
Expand All @@ -65,13 +68,13 @@ async fn delete_resource(r_type: ResourceType, args: DeleteResourceArgs) -> Resu
match conn.remove_resource(r_type.clone(), &args.name, &args.namespace).await {
Ok(result) => {
results.push(result)
},
}
Err(e) => errors.push(e.to_string())
}
}

match errors.is_empty() {
false => Err(anyhow!(errors.join("\n")).into()),
false => Err(anyhow!("\n{}", errors.join("\n")).into()),
true => Ok(())
}
}
Expand Down
39 changes: 31 additions & 8 deletions src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use anyhow::anyhow;
use handlebars::Handlebars;

use k8s_openapi::api::batch::v1::CronJob;
use k8s_openapi::api::core::v1::Pod;
use k8s_openapi::api::core::v1::{Pod, Secret};
use k8s_openapi::api::networking::v1::Ingress;
use serde_json::{json, Value};

Expand Down Expand Up @@ -248,8 +248,6 @@ impl DefaultExecutor {

fn apply_play(&self, object: SupportedResources) -> Result<(), Box<dyn Error>> {

// check if object's hostNetwork: true then don't use network=podman

let file_path = DefaultExecutor::write_manifest_to_file(&serde_yaml::to_string(&object)?)?;

let mut args = vec!["play", "kube", &file_path, "--start"];
Expand All @@ -271,7 +269,29 @@ impl DefaultExecutor {

Ok(())
}
fn remove_pod(&self, id: &str, _namespace: &str, grace_period: Option<usize>) -> Result<(), Box<dyn Error>> {

fn remove_secret(&self, secret: Secret) -> Result<(), Box<dyn Error>> {
let fqn = format!("{}.{}", secret.metadata.name.unwrap(), secret.metadata.namespace.unwrap());

let output = process::Command::new("podman")
.args(["secret", "rm", &fqn])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.output()
.expect("failed to remove secret");

if !output.status.success() {
return Err(anyhow!("`podman secret rm {}` exited with code {}, stderr: {}", fqn, output.status.code().unwrap(), String::from_utf8_lossy(&output.stderr).trim().to_string()).into());
}

if !output.stdout.is_empty() {
println!("{}", String::from_utf8_lossy(&output.stdout).trim());
}

Ok(())
}

fn remove_pod(&self, id: &str, grace_period: Option<usize>) -> Result<(), Box<dyn Error>> {
if id.is_empty() {
return Err(anyhow!("no metadata.name found").into());
}
Expand Down Expand Up @@ -317,7 +337,7 @@ impl Executor for DefaultExecutor {
// just to check
let object: SupportedResources = serde_yaml::from_str(manifest).expect("failed to deserialize manifest");
match object {
SupportedResources::Pod(_) | SupportedResources::Deployment(_) | SupportedResources::DaemonSet(_) => {
SupportedResources::Pod(_) | SupportedResources::Deployment(_) | SupportedResources::DaemonSet(_) | SupportedResources::Secret(_) => {
self.apply_play(object)
}
SupportedResources::Ingress(ingress) => {
Expand All @@ -331,10 +351,10 @@ impl Executor for DefaultExecutor {


fn manifest_delete(&self, object: SupportedResources, grace_period: Option<usize>) -> Result<(), Box<dyn Error>> {
let namespaced_name = object.name();
match object {
SupportedResources::Pod(_p) => {
self.remove_pod(&namespaced_name.name, &namespaced_name.namespace, grace_period)
SupportedResources::Pod(p) => {
let name = p.metadata.name.unwrap();
self.remove_pod(&name, grace_period)
}
SupportedResources::Deployment(_d) => {
Err(anyhow!("removing a deployment is not supported, instead supply it's individual pods").into())
Expand All @@ -348,6 +368,9 @@ impl Executor for DefaultExecutor {
SupportedResources::CronJob(cron) => {
self.remove_cron(cron)
}
SupportedResources::Secret(secret) => {
self.remove_secret(secret)
}
}
}
}
13 changes: 13 additions & 0 deletions src/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod deployment;
mod cronjob;
mod pod;
mod lister;
mod daemonset;


use std::error::Error;
Expand All @@ -21,6 +22,7 @@ use crate::skate::{ConfigFileArgs};
use crate::{ssh};

use crate::get::cronjob::CronjobsLister;
use crate::get::daemonset::DaemonsetLister;
use crate::get::deployment::DeploymentLister;
use crate::get::ingress::IngresssLister;
use crate::get::lister::Lister;
Expand Down Expand Up @@ -57,22 +59,28 @@ pub enum GetCommands {
Pod(GetObjectArgs),
#[command(alias("deployments"))]
Deployment(GetObjectArgs),
#[command(alias("daemonsets"))]
Daemonset(GetObjectArgs),
#[command(alias("nodes"))]
Node(GetObjectArgs),
#[command()]
Ingress(GetObjectArgs),
#[command(alias("cronjobs"))]
Cronjob(GetObjectArgs),
#[command(alias("secrets"))]
Secret(GetObjectArgs),
}

pub async fn get(args: GetArgs) -> Result<(), Box<dyn Error>> {
let global_args = args.clone();
match args.commands {
GetCommands::Pod(args) => get_pod(global_args, args).await,
GetCommands::Deployment(args) => get_deployment(global_args, args).await,
GetCommands::Daemonset(args) => todo!(),
GetCommands::Node(args) => get_nodes(global_args, args).await,
GetCommands::Ingress(args) => get_ingress(global_args, args).await,
GetCommands::Cronjob(args) => get_cronjobs(global_args, args).await,
GetCommands::Secret(args) => todo!(),
}
}

Expand Down Expand Up @@ -112,6 +120,11 @@ async fn get_deployment(global_args: GetArgs, args: GetObjectArgs) -> Result<(),
get_objects(global_args, args, &lister).await
}

async fn get_daemonsets(global_args: GetArgs, args: GetObjectArgs) -> Result<(), Box<dyn Error>> {
let lister = DaemonsetLister {};
get_objects(global_args, args, &lister).await
}

async fn get_pod(global_args: GetArgs, args: GetObjectArgs) -> Result<(), Box<dyn Error>> {
let lister = PodLister {};
get_objects(global_args, args, &lister).await
Expand Down
78 changes: 78 additions & 0 deletions src/get/daemonset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::collections::HashMap;
use chrono::{Local, SecondsFormat};
use itertools::Itertools;
use crate::get::{GetObjectArgs, IdCommand, Lister};
use crate::skatelet::{PodmanPodInfo, PodmanPodStatus, SystemInfo};
use crate::state::state::ClusterState;

pub(crate) struct DaemonsetLister {}

impl Lister<(String, PodmanPodInfo)> for DaemonsetLister {
fn selector(&self, _si: &SystemInfo, _ns: &str, _id: &str) -> Option<Vec<(String, PodmanPodInfo)>> {
todo!()
}
fn list(&self, args: &GetObjectArgs, state: &ClusterState) -> Vec<(String, PodmanPodInfo)> {
let pods: Vec<_> = state.nodes.iter().filter_map(|n| {
let items: Vec<_> = n.host_info.clone()?.system_info?.pods.unwrap_or_default().into_iter().filter_map(|p| {
let ns = args.namespace.clone().unwrap_or("default".to_string());
let id = match args.id.clone() {
Some(cmd) => match cmd {
IdCommand::Id(ids) => Some(ids.into_iter().next().unwrap_or("".to_string()))
}
None => None
};
let daemonset = p.labels.get("skate.io/daemonset").and_then(|n| Some(n.clone())).unwrap_or_default();
if daemonset == "" {
return None;
}
let daemonset_ns = p.labels.get("skate.io/namespace").unwrap_or(&"".to_string()).clone();


let match_ns = ns == daemonset_ns;

let match_id = match id.clone() {
Some(id) => {
id == daemonset
}
None => false
};
if match_ns || match_id || (id.is_none() && ns == "" && daemonset_ns != "skate" ) {
return Some((daemonset, p));
}
None
}).collect();
match items.len() {
0 => None,
_ => Some(items)
}
}).flatten().collect();
pods
}

fn print(&self, items: Vec<(String, PodmanPodInfo)>) {
println!(
"{0: <30} {1: <10} {2: <10} {3: <10} {4: <30}",
"NAME", "READY", "STATUS", "RESTARTS", "CREATED"
);
let pods = items.into_iter().fold(HashMap::<String, Vec<PodmanPodInfo>>::new(), |mut acc, (depl, pod)| {
acc.entry(depl).or_insert(vec![]).push(pod);
acc
});

for (deployment, pods) in pods {
let health_pods = pods.iter().filter(|p| PodmanPodStatus::Running == p.status).collect_vec().len();
let all_pods = pods.len();
let created = pods.iter().fold(Local::now(), |acc, item| {
if item.created < acc {
return item.created;
}
return acc;
});

println!(
"{0: <30} {1: <10} {2: <10} {3: <10} {4: <30}",
deployment, format!("{}/{}", health_pods, all_pods), "", "", created.to_rfc3339_opts(SecondsFormat::Secs, true)
)
}
}
}
7 changes: 4 additions & 3 deletions src/get/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::get::{GetObjectArgs, IdCommand, Lister};
use crate::skatelet::{PodmanPodInfo, PodmanPodStatus, SystemInfo};
use crate::state::state::ClusterState;

pub (crate) struct DeploymentLister {}
pub(crate) struct DeploymentLister {}

impl Lister<(String, PodmanPodInfo)> for DeploymentLister {
fn selector(&self, _si: &SystemInfo, _ns: &str, _id: &str) -> Option<Vec<(String, PodmanPodInfo)>> {
Expand All @@ -22,11 +22,12 @@ impl Lister<(String, PodmanPodInfo)> for DeploymentLister {
None => None
};
let deployment = p.labels.get("skate.io/deployment");
let pod_ns = p.labels.get("skate.io/namespace").unwrap_or(&"default".to_string()).clone();
match deployment {
Some(deployment) => {
let match_ns = match ns.clone() {
Some(ns) => {
ns == p.labels.get("skate.io/namespace").unwrap_or(&"".to_string()).clone()
ns == pod_ns
}
None => false
};
Expand All @@ -36,7 +37,7 @@ impl Lister<(String, PodmanPodInfo)> for DeploymentLister {
}
None => false
};
if match_ns || match_id || (id.is_none() && ns.is_none()) {
if match_ns || match_id || (id.is_none() && ns.is_none() && pod_ns != "skate") {
return Some((deployment.clone(), p));
}
None
Expand Down
2 changes: 1 addition & 1 deletion src/get/ingress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub(crate) struct IngresssLister {}
impl Lister<ObjectListItem> for IngresssLister {
fn selector(&self, si: &SystemInfo, ns: &str, id: &str) -> Option<Vec<ObjectListItem>> {
si.ingresses.as_ref().and_then(|jobs| Some(jobs.iter().filter(|j| {
let filterable: Box<dyn NameFilters> = Box::new(j.clone());
let filterable: Box<dyn NameFilters> = Box::new(*j);
return filterable.filter_names(id, ns);
}).map(|p| p.clone()).collect()))
}
Expand Down
Loading

0 comments on commit 547b936

Please sign in to comment.