Compare commits

...

2 commits

Author SHA1 Message Date
Gabriel Simmer 4883c89b36
Use nix to build docker image
Some checks failed
Build Docker Image / arm-docker-build (push) Failing after 4s
2023-07-16 19:24:57 +01:00
Gabriel Simmer 3aea275d8f
Split common functions into lib.rs 2023-07-16 19:20:39 +01:00
6 changed files with 93 additions and 87 deletions

View file

@ -12,9 +12,9 @@ jobs:
uses: https://github.com/RouxAntoine/checkout@v3.5.4 uses: https://github.com/RouxAntoine/checkout@v3.5.4
with: with:
ref: trunk ref: trunk
- name: Build and Push ARM Image - name: Build Image
uses: https://github.com/docker/build-push-action@v4.1.1 run: nix build .#docker
with: - name: Load and tag image
push: true run: |
tags: icr.gmem.ca/dref:arm docker tag $(docker load < result) icr.gmem.ca/dref:arm
docker push icr.gmem.ca/dref:arm

4
Cargo.lock generated
View file

@ -170,8 +170,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]] [[package]]
name = "docker-rs-dashboard" name = "dref"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"axum", "axum",
"axum-extra", "axum-extra",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "docker-rs-dashboard" name = "dref"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,5 +1,5 @@
{ {
description = "Build a cargo project without extra checks"; description = "Docker registry frontend & basic library";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
@ -22,7 +22,7 @@
}; };
craneLib = crane.lib.${system}; craneLib = crane.lib.${system};
my-crate = craneLib.buildPackage { dref = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.; src = craneLib.cleanCargoSource ./.;
buildInputs = [ buildInputs = [
@ -38,7 +38,7 @@
dockerImage = pkgs.dockerTools.buildImage { dockerImage = pkgs.dockerTools.buildImage {
name = "dref"; name = "dref";
config = { config = {
Cmd = [ "${my-crate}/bin/docker-rs-dashboard" ]; Cmd = [ "${dref}/bin/dref" ];
ExposedPorts = { ExposedPorts = {
"3000/tcp" = {}; "3000/tcp" = {};
}; };
@ -47,15 +47,15 @@
in in
{ {
checks = { checks = {
inherit my-crate; inherit dref;
}; };
apps.default = flake-utils.lib.mkApp { apps.default = flake-utils.lib.mkApp {
drv = my-crate; drv = dref;
}; };
packages = rec { packages = rec {
bin = my-crate; bin = dref;
docker = dockerImage; docker = dockerImage;
default = pkgs.symlinkJoin { default = pkgs.symlinkJoin {
name = "dref-${bin.version}"; name = "dref-${bin.version}";

58
src/lib.rs Normal file
View file

@ -0,0 +1,58 @@
use std::collections::HashMap;
use std::{boxed, error};
use serde::{Serialize, Deserialize};
#[derive(Clone)]
pub struct Registry {
pub url: String,
}
#[derive(Debug)]
pub struct Image {
pub name: String,
}
#[derive(Debug)]
pub struct RichTag {
pub name: String,
architectures: Vec<String>
}
#[derive(Deserialize)]
struct Tags {
name: String,
tags: Vec<String>
}
impl Registry {
pub async fn images(&self) -> Result<Vec<Image>, boxed::Box<dyn error::Error>> {
let resq_url = format!("{}/v2/_catalog", self.url);
let resp = reqwest::get(resq_url)
.await?
.json::<HashMap<String, Vec<String>>>()
.await?;
let i = resp.get("repositories").unwrap().into_iter().map(|i| async {
Image{
name: i.to_string(),
}
});
let images: Vec<Image> = futures::future::join_all(i).await;
Ok(images)
}
pub async fn image_tags(&self, image: String) -> Result<Vec<RichTag>, boxed::Box<dyn error::Error>> {
let resq_url = format!("{}/v2/{}/tags/list", self.url, image);
let resp: Tags = reqwest::get(resq_url)
.await?
.json::<Tags>()
.await?;
let i = resp.tags.into_iter().map(|i| async move {
RichTag{
name: i.to_string(),
architectures: Vec::new(),
}
});
let tags: Vec<RichTag> = futures::future::join_all(i).await;
Ok(tags)
}
}

View file

@ -2,33 +2,24 @@ mod css;
use std::{boxed, error, env, collections::{HashMap, self}}; use std::{boxed, error, env, collections::{HashMap, self}};
use maud::{html, Markup, DOCTYPE}; use maud::{html, Markup, DOCTYPE};
use axum::{Router, routing::get, response::IntoResponse}; use axum::{Router, routing::get, response::IntoResponse, extract::State};
use futures::{stream::StreamExt, future::join_all}; use dref;
use serde::{Serialize, Deserialize};
// To be expanded upon. #[derive(Clone)]
#[derive(Debug)] struct AppState {
struct Image { registry: dref::Registry
name: String,
tags: Vec<RichTag>,
}
#[derive(Debug)]
struct RichTag {
name: String,
architectures: Vec<String>
}
#[derive(Deserialize)]
struct Tags {
name: String,
tags: Vec<String>
} }
#[tokio::main(flavor = "multi_thread", worker_threads = 10)] #[tokio::main(flavor = "multi_thread", worker_threads = 10)]
async fn main() { async fn main() {
let registry = dref::Registry{
url: env::var("DREF_REGISTRY").unwrap(),
};
let state = AppState {registry};
// build our application with a single route // build our application with a single route
let app = Router::new() let app = Router::new()
.route("/", get(root)).route("/styles.css", get(css)); .route("/", get(root)).route("/styles.css", get(css))
.with_state(state);
// run it with hyper on localhost:3000 // run it with hyper on localhost:3000
println!("Running webserver on port :3000"); println!("Running webserver on port :3000");
@ -48,7 +39,7 @@ async fn css() -> impl IntoResponse {
fn header(page_title: &str) -> Markup { fn header(page_title: &str) -> Markup {
html! { html! {
(DOCTYPE) (DOCTYPE)
meta charset="utf-8"; meta charset="utf-8";
title { (page_title) } title { (page_title) }
link rel="stylesheet" href="/styles.css"; link rel="stylesheet" href="/styles.css";
} }
@ -61,29 +52,23 @@ fn footer() -> Markup {
} }
} }
async fn root() -> Markup { async fn root(State(state): State<AppState>) -> Markup {
let c = get_images().await.unwrap_or_else(|e| panic!("{}", e)); let c = state.registry.images().await.unwrap();
html! { html! {
(header("/DREF")) (header("/DREF"))
body { body {
main { main {
h1 { "/DREF" } h1 { "/DREF" }
hr; hr;
@for image in &c { @for image in c {
div { div {
details { details {
summary { (image.name) } summary { (image.name) }
ul { ul {
@for tag in &image.tags { @for tag in state.registry.image_tags(image.name).await.unwrap() {
li { (tag.name) li { (tag.name) }
// div {
// @for arch in &tag.architectures {
// span { (arch) }
// }
// }
}
} }
} }
} }
} }
} }
@ -92,40 +77,3 @@ async fn root() -> Markup {
(footer()) (footer())
} }
} }
async fn get_images() -> Result<Vec<Image>, boxed::Box<dyn error::Error>> {
let host = env::var("DREF_REGISTRY").unwrap();
let resp = reqwest::get(host + "/v2/_catalog")
.await?
.json::<HashMap<String, Vec<String>>>()
.await?;
let i = resp.get("repositories").unwrap().into_iter().map(|i| async {
let tags = get_tags(i.to_string()).await;
Image{
name: i.to_string(),
tags: tags.unwrap(),
}
});
let images: Vec<Image> = futures::future::join_all(i).await;
Ok(images)
}
async fn get_tags(image: String) -> Result<Vec<RichTag>, boxed::Box<dyn error::Error>> {
let host = env::var("DREF_REGISTRY").unwrap();
let resp: Tags = reqwest::get(host + "/v2/" + &image + "/tags/list")
.await?
.json::<Tags>()
.await?;
let i = resp.tags.into_iter().map(|i| async move {
RichTag{
name: i.to_string(),
architectures: Vec::new(),
}
});
let tags: Vec<RichTag> = futures::future::join_all(i).await;
Ok(tags)
}
async fn get_platforms(image: &String, tag: String) -> Result<Vec<String>, boxed::Box<dyn error::Error>> {
todo!()
}