Skip to content

Commit

Permalink
functioning deployment, and stateful, with optional annotation for di…
Browse files Browse the repository at this point in the history
…rectory. Only cloning, editing, no commit or push yet
  • Loading branch information
slackspace-io committed Mar 23, 2024
1 parent 8dcb3c1 commit d6bc7e8
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 32 deletions.
13 changes: 8 additions & 5 deletions backend/src/database/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ pub fn create_table_if_not_exist() -> Result<()> {
latest_version TEXT NOT NULL,
last_scanned TEXT NOT NULL,
scan_id INTEGER,
scan_type TEXT
scan_type TEXT,
git_directory TEXT
)",
[],
)?;
Expand All @@ -41,6 +42,7 @@ pub fn return_workload(name: String, namespace: String) -> Result<Workload> {
current_version: row.get(8)?,
latest_version: row.get(9)?,
last_scanned: row.get(10)?,
git_directory: row.get(12)?,
})
})?;
if let Some(workload) = workload.next() {
Expand All @@ -66,6 +68,7 @@ pub fn return_all_workloads() -> Result<Vec<Workload>> {
current_version: row.get(8)?,
latest_version: row.get(9)?,
last_scanned: row.get(10)?,
git_directory: row.get(12)?,
})
})?;
let mut result = Vec::new();
Expand Down Expand Up @@ -93,9 +96,9 @@ impl FromSql for UpdateStatus {
pub fn get_latest_scan_id() -> std::result::Result<i32, Error> {
let conn = Connection::open("data.db")?;
let mut stmt = conn.prepare("SELECT MAX(scan_id) FROM workloads")?;
let scan_id_iter = stmt.query_map([], |row| row.get(0))?;
let mut scan_id_iter = stmt.query_map([], |row| row.get(0))?;

for scan_id_result in scan_id_iter {
if let Some(scan_id_result) = scan_id_iter.next() {
return scan_id_result.map(|id: Option<i32>| id.unwrap_or(0)); // Handle potential NULL
}
Ok(0)
Expand All @@ -107,7 +110,7 @@ pub fn insert_workload(workload: &Workload, scan_id: i32) -> Result<()> {
match conn.execute(
"INSERT INTO workloads (name, image, namespace, git_ops_repo, include_pattern, exclude_pattern, update_available, current_version, latest_version, last_scanned, scan_id, scan_type)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)",
&[
[
&workload.name,
&workload.image,
&workload.namespace,
Expand All @@ -119,7 +122,7 @@ pub fn insert_workload(workload: &Workload, scan_id: i32) -> Result<()> {
&workload.latest_version,
&workload.last_scanned,
&scan_id.to_string(),
"placeholder",
workload.git_directory.as_ref().map(String::as_str).unwrap_or_default(),
],
) {
Ok(_) => Ok(()),
Expand Down
55 changes: 44 additions & 11 deletions backend/src/gitops/gitops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use walkdir::WalkDir;

use crate::web::exweb::update_workload;
use k8s_openapi::api::apps::v1::{Deployment, StatefulSet};
use log::info;
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
Expand Down Expand Up @@ -51,7 +52,18 @@ fn clone_or_open_repo(

fn edit_files(local_path: &Path, workload: &Workload) {
let name = &workload.name;
let search_path = local_path.join(name);
let search_path = if let Some(git_directory) = &workload.git_directory {
if git_directory.is_empty() {
log::info!("No git directory specified for workload: {}", name);
local_path.join(name)
} else {
info!("git directory: {:?}", git_directory);
local_path.join(git_directory)
}
} else {
log::info!("No git directory specified for workload: {}", name);
local_path.join(name)
};
let image = Some(workload.image.clone());
let current_version = Some(workload.current_version.clone());
let latest_version = Some(workload.latest_version.clone());
Expand All @@ -71,14 +83,7 @@ fn edit_files(local_path: &Path, workload: &Workload) {
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
let mut image_updated = false; // Flag to track if the image was updated

let deployment: Result<Deployment, _> = serde_yaml::from_str(&contents);
if let Ok(deployment) = deployment {
log::info!("File: {:?}", entry.path());
log::info!("Deployment: {:?}", &deployment);
} else {
log::info!("Not a deployment {:?}", entry.path());
}
// Check if the file is a statefulset
let statefulset_result: Result<StatefulSet, _> = serde_yaml::from_str(&contents);
if let Ok(mut statefulset) = statefulset_result {
if let Some(spec) = statefulset.spec.as_mut() {
Expand Down Expand Up @@ -107,9 +112,37 @@ fn edit_files(local_path: &Path, workload: &Workload) {
file.write_all(serde_yaml::to_string(&statefulset).unwrap().as_bytes())
.unwrap();
}
}
//deployment
log::info!("Deployment checking");
let deployment_result: Result<Deployment, _> = serde_yaml::from_str(&contents);
if let Ok(mut deployment) = deployment_result {
log::info!("Deployment: {:?}", &deployment);
if let Some(spec) = deployment.spec.as_mut() {
if let Some(template_spec) = spec.template.spec.as_mut() {
for container in &mut template_spec.containers {
// Replace image in Deployment
if container.image.as_ref().unwrap().contains(&base_image) {
log::info!("Found target image in file: {:?}", entry.path());
container.image = Some(new_image.clone());
image_updated = true; // Image has been updated
}
}
}
}
if image_updated {
log::info!("Updating image in file: {:?}", entry.path());
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.open(entry.path())
.unwrap();
file.write_all(serde_yaml::to_string(&deployment).unwrap().as_bytes())
.unwrap();
}
} else {
log::info!("Not a statefulset {:?}", entry.path());
// Handle non-statefulset scenario
log::info!("Not a deployment {:?}", entry.path());
// Handle non-deployment scenario
}
}
}
Expand Down
32 changes: 17 additions & 15 deletions backend/src/kubernetes/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@ impl Client {
Ok(Client { kube_client })
}

// pub async fn fetch_slackwatch_enabled_containers(&self) -> Result<Vec<Pod>, kube::Error> {
// let pods: Api<Pod> = Api::all(self.kube_client.clone());
// let lp = ListParams::default().labels("slackwatch.enable=true"); // Adjust based on actual use case
// let pod_list = pods.list(&lp).await?;
// Ok(pod_list.items)
// }
// pub async fn fetch_containers_with_annotation(
// &self,
// annotation_key: &str,
// ) -> Result<Vec<Pod>, kube::Error> {
// let pods: Api<Pod> = Api::all(self.kube_client.clone());
// let lp = ListParams::default().labels(format!("{}=*", annotation_key).as_str()); // Adjust based on actual use case
// let pod_list = pods.list(&lp).await?;
// Ok(pod_list.items)
// }
// pub async fn fetch_slackwatch_enabled_containers(&self) -> Result<Vec<Pod>, kube::Error> {
// let pods: Api<Pod> = Api::all(self.kube_client.clone());
// let lp = ListParams::default().labels("slackwatch.enable=true"); // Adjust based on actual use case
// let pod_list = pods.list(&lp).await?;
// Ok(pod_list.items)
// }
// pub async fn fetch_containers_with_annotation(
// &self,
// annotation_key: &str,
// ) -> Result<Vec<Pod>, kube::Error> {
// let pods: Api<Pod> = Api::all(self.kube_client.clone());
// let lp = ListParams::default().labels(format!("{}=*", annotation_key).as_str()); // Adjust based on actual use case
// let pod_list = pods.list(&lp).await?;
// Ok(pod_list.items)
// }

pub async fn list_pods(&self) -> Result<Vec<Pod>, kube::Error> {
let pods: Api<Pod> = Api::all(self.kube_client.clone());
Expand All @@ -56,6 +56,7 @@ pub async fn find_enabled_workloads() -> Result<Vec<Workload>, kube::Error> {
let exclude_pattern = annotations.get("slackwatch.exclude").cloned();
let include_pattern = annotations.get("slackwatch.include").cloned();
let git_ops_repo = annotations.get("slackwatch.repo").cloned();
let git_directory = annotations.get("slackwatch.directory").cloned();
for spec in p.spec {
for container in spec.containers.clone() {
if let Some(name) = Some(container.name) {
Expand All @@ -75,6 +76,7 @@ pub async fn find_enabled_workloads() -> Result<Vec<Workload>, kube::Error> {
current_version: current_version.to_string(),
last_scanned: chrono::Utc::now().to_rfc3339(),
latest_version: "1.0.0".to_string(),
git_directory: git_directory.clone(),
});
}
}
Expand Down
1 change: 1 addition & 0 deletions backend/src/models/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct Workload {
pub git_ops_repo: Option<String>,
pub include_pattern: Option<String>,
pub update_available: UpdateStatus,
pub git_directory: Option<String>,
pub image: String,
pub last_scanned: String,
pub namespace: String,
Expand Down
1 change: 1 addition & 0 deletions backend/src/services/workloads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,6 @@ pub async fn parse_tags(workload: &Workload) -> Result<Workload, Box<dyn std::er
update_available,
last_scanned: workload.last_scanned.clone(),
latest_version: latest_version.clone(),
git_directory: workload.git_directory.clone(),
})
}
2 changes: 2 additions & 0 deletions backend/src/web/exweb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ async fn update_workload(req: HttpRequest) -> impl Responder {
let current_version = query_params.get("current_version").unwrap();
let latest_version = query_params.get("latest_version").unwrap();
let git_ops_repo = query_params.get("git_ops_repo").unwrap();
let git_directory = query_params.get("git_directory").unwrap();
let workload = Workload {
git_directory: Some(git_directory.clone()),
name: name.clone(),
exclude_pattern: None,
git_ops_repo: Some(git_ops_repo.clone()),
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface CombinedData {
update_available: string,
image: string,
last_scanned: string,
git_directory: string,
namespace: string,
current_version: string,
latest_version: string,
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/UpdateCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface UpdateCardProps {
namespace: string,
current_version: string,
latest_version: string,
git_directory: string,
};
}

Expand All @@ -35,6 +36,7 @@ async function handleUpdate(data: FormData) {
const namespace = data.get('namespace');
const git_ops_repo = data.get('gitopsRepo');
const update_available = data.get('availableUpdate');
const git_directory = data.get('gitDirectory');
//create url
//fetch url
const params = new URLSearchParams({
Expand All @@ -44,7 +46,8 @@ async function handleUpdate(data: FormData) {
current_version: current_version as string,
namespace: namespace as string,
git_ops_repo: git_ops_repo as string,
update_available: update_available as string
update_available: update_available as string,
git_directory: git_directory as string
});
const response = await fetch(`${baseUrl}/api/workloads/update?${params}`);
if (!response.ok) {
Expand Down Expand Up @@ -88,6 +91,7 @@ const UpdateCard: React.FC<UpdateCardProps> = ({ update }) => (
<input name="currentTag" type="hidden" value={update.current_version} />
<input name="namespace" type="hidden" value={update.namespace} />
<input name="gitopsRepo" type="hidden" value={update.git_ops_repo} />
<input name="gitDirectory" type="hidden" value={update.git_directory} />
<input name="availableUpdate" type="hidden" value={update.update_available} />
<button className="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition duration-150 ease-in-out">
Update
Expand Down

0 comments on commit d6bc7e8

Please sign in to comment.