Skip to content

Commit

Permalink
individual refresh option working, but causing duplicateson webui
Browse files Browse the repository at this point in the history
  • Loading branch information
slackspace-io committed Mar 23, 2024
1 parent 0d8d064 commit a8a70d2
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 13 deletions.
68 changes: 68 additions & 0 deletions backend/src/kubernetes/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,74 @@ impl Client {
}
}

pub async fn find_specific_workload(
request_name: String,
request_namespace: String,
) -> Result<Workload, kube::Error> {
//get all pods
let client = Client::new().await?;
let pods = client.list_pods().await?;
//count number of pods
for p in pods {
if let Some(namespace) = p.namespace() {
if request_namespace != namespace {
log::info!("Skipping pod in different namespace");
continue;
}
if let Some(annotations) = p.metadata.annotations {
if let Some(enable) = annotations.get("slackwatch.enable") {
//Found a pod with slackwatch.enable annotation
if enable == "true" {
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) {
if request_name != name {
log::info!("Skipping pod with different name");
continue;
}
if let Some(image) = container.image {
let parts = image.split(":").collect::<Vec<&str>>();
let current_version = parts.get(1).unwrap_or(&"latest");
let workload = Workload {
exclude_pattern: exclude_pattern.clone(),
git_ops_repo: git_ops_repo.clone(),
include_pattern: include_pattern.clone(),
update_available: "NotAvailable"
.parse()
.unwrap_or(UpdateStatus::NotAvailable),
image: image.clone(),
name: name.clone(),
namespace: namespace.clone(),
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(),
};
if workload.name == name && workload.namespace == namespace
{
return Ok(workload);
}
}
}
}
}
}
}
}
}
}
Err(kube::Error::Api(kube::error::ErrorResponse {
code: 404,
message: "Workload not found".to_string(),
reason: "Not Found".to_string(),
status: "Failure".to_string(),
}))
}

pub async fn find_enabled_workloads() -> Result<Vec<Workload>, kube::Error> {
//get all pods
let client = Client::new().await?;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mod web;
async fn main() {
println!("Hello, world!");
//Logging and env variables
env::set_var("RUST_LOG", "debug");
env::set_var("RUST_LOG", "info");
env_logger::init();
dotenv::dotenv().ok();
// Load Configurations
Expand Down
27 changes: 26 additions & 1 deletion backend/src/services/workloads.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
use crate::database;
use crate::database::client::get_latest_scan_id;
use crate::kubernetes::client::find_enabled_workloads;
use crate::kubernetes::client::{find_enabled_workloads, find_specific_workload};
use crate::models::models::{UpdateStatus, Workload};
use crate::notifications::ntfy::send_notification;
use crate::repocheck::repocheck::get_tags_for_image;
use regex::Regex;
use semver::Version;

pub async fn update_single_workload(current_workload: Workload) -> Result<(), String> {
let workload = find_specific_workload(
current_workload.name.clone(),
current_workload.namespace.clone(),
)
.await
.map_err(|e| e.to_string())?;
log::info!("Found workload: {:?}", workload);
let latest_tag = find_latest_tag_for_image(&workload)
.await
.map_err(|e| e.to_string())?;
let workload = parse_tags(&workload).await.map_err(|e| e.to_string())?;
if workload.update_available.to_string() == "Available" {
send_notification(&workload)
.await
.unwrap_or_else(|e| log::error!("Error sending notification: {}", e));
}
let scan_id = get_latest_scan_id().unwrap_or(0) + 1;
std::thread::spawn(move || database::client::insert_workload(&workload, scan_id))
.join()
.map_err(|_| "Thread error".to_string())?
.expect("TODO: panic message");
Ok(())
}

pub async fn fetch_and_update_all_watched() -> Result<(), String> {
let workloads = find_enabled_workloads().await.map_err(|e| e.to_string())?;
log::info!("Found {} workloads", workloads.len());
Expand Down
46 changes: 46 additions & 0 deletions backend/src/web/exweb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,50 @@ async fn refresh_workloads() -> impl Responder {
})
}

#[get("/api/update/workload")]
async fn update_single(req: HttpRequest) -> impl Responder {
log::info!("Updating single workload");
let query_params =
web::Query::<HashMap<String, String>>::from_query(req.query_string()).unwrap();
//create workload struct
let name = query_params.get("name").unwrap();
let namespace = query_params.get("namespace").unwrap();
let image = query_params.get("image").unwrap();
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()),
include_pattern: None,
update_available: models::UpdateStatus::Available,
image: image.clone(),
last_scanned: "2021-08-01".to_string(),
namespace: namespace.clone(),
current_version: current_version.clone(),
latest_version: latest_version.clone(),
};

log::info!("name = {}, namespace = {}, image = {}, current_version = {}, latest_version = {}, git_ops_repo = {}", name, namespace, image, current_version, latest_version, git_ops_repo);
log::info!("Workload: {:?}", workload);
//update single workload
tokio::spawn(async move {
// This is the background task.
// Since we're not waiting on it, we won't hold up the HTTP response.
match services::workloads::update_single_workload(workload).await {
Ok(_) => println!("Workload updated successfully."),
Err(e) => eprintln!("Failed to update workload: {}", e),
}
});
HttpResponse::Ok().json(ApiResponse {
status: "success".to_string(),
message: "Workload refreshed".to_string(),
})
}

#[get("/api/workloads/{name}/{namespace}")]
async fn fetch_workload(path: web::Path<(String, String)>) -> impl Responder {
let (name, namespace) = path.into_inner();
Expand All @@ -62,6 +106,7 @@ async fn fetch_workload(path: web::Path<(String, String)>) -> impl Responder {

#[get("api/workloads/update")]
async fn update_workload(req: HttpRequest) -> impl Responder {
log::info!("Updating single workload");
let query_params =
web::Query::<HashMap<String, String>>::from_query(req.query_string()).unwrap();
//create workload struct
Expand Down Expand Up @@ -101,6 +146,7 @@ pub async fn site() -> std::io::Result<()> {
.service(fetch_all_workloads)
.service(fetch_workload)
.service(refresh_workloads)
.service(update_single)
.service(update_workload)
})
.bind(("0.0.0.0", 8080))?
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import RefreshButton from "@/components/RefreshButton";
import React from "react";


interface CombinedData {
export interface CombinedData {
name: string,
exclude_pattern: string,
git_ops_repo: string,
Expand Down Expand Up @@ -46,6 +46,7 @@ async function refreshData() {
return res.json();
}


export default async function Page() {
let data = await getData();

Expand All @@ -55,10 +56,6 @@ export default async function Page() {
return (
<main className="p-4">
<div className="flex justify-center items-end">
<form action={refreshData} >
<RefreshButton/>

</form>
</div>
{data.map((update, index) => (
<UpdateCard key={index} update={update}/>
Expand Down
50 changes: 44 additions & 6 deletions frontend/src/components/RefreshButton.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,50 @@
"use client";
import { CombinedData } from '@/types/workloads';
import {unstable_noStore as noStore} from "next/dist/server/web/spec-extension/unstable-no-store";

async function refreshSingleData(update: CombinedData) {
'use server';
noStore();
console.log('refreshSingleData');
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
if (!baseUrl) {
console.warn('NEXT_PUBLIC_API_BASE_URL is not defined. Skipping fetch.');
return [];
}
const params = new URLSearchParams({
name: update.name,
exclude_pattern: update.exclude_pattern,
git_ops_repo: update.git_ops_repo,
include_pattern: update.include_pattern,
update_available: update.update_available,
image: update.image,
last_scanned: update.last_scanned,
git_directory: update.git_directory,
namespace: update.namespace,
current_version: update.current_version,
latest_version: update.latest_version,
});
console.log(params.toString());
const res = await fetch(`${baseUrl}/api/update/workload?${params}`);
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.json();

const RefreshButton = () => {
}

const RefreshButton = ({update}: {update: CombinedData}) => {
const handleRefresh = async () => {
'use server';
console.log('Refresh clicked');
console.log(update);
await refreshSingleData(update);
}
return (
<button onClick={() => {console.log('Refresh clicked')}}><b>Refresh</b></button>
<div>
<form action={handleRefresh}>
<button>Refresh</button>
</form>
</div>
);
}



export default RefreshButton;
2 changes: 2 additions & 0 deletions frontend/src/components/UpdateCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ const UpdateCard: React.FC<UpdateCardProps> = ({ update }) => (
</form>
)}
</div>
<RefreshButton update={update}/>

<div className="mt-4">
{update.current_version && <p>Current Tag: <span className="font-semibold">{update.current_version}</span></p>}
{update.latest_version && <p className="text-green-500">New Tag: <span className="font-semibold">{update.latest_version}</span></p>}
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/types/workloads.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface CombinedData {
name: string,
exclude_pattern: string,
git_ops_repo: string,
include_pattern: string,
update_available: string,
image: string,
last_scanned: string,
git_directory: string,
namespace: string,
current_version: string,
latest_version: string,
}

0 comments on commit a8a70d2

Please sign in to comment.