Skip to content

Commit

Permalink
[suiop][image] add list and get current status endpoint (#18373)
Browse files Browse the repository at this point in the history
## Description 

Adding two endpoints for improving image building process visibility:
1. list: show all the images in the AR
2. status: check whether the image is built and stored in AR, if not in
AR, show real-time status of the building process

## Test plan 
list
<img width="469" alt="image"
src="https://github.com/MystenLabs/sui/assets/147538877/651a1e1c-b4fa-4b99-b343-8a9d1e1e2652">

status
```
suiop ci image status --repo-name infra-metadata-service --image-name go-executor
Requested status for repo: infra-metadata-service, image: go-executor, ref: branch:main
Image Status: Found
Image SHA: 1d01c74ca5cfb3c7103
```

```
suiop ci image status --repo-name infra-metadata-service --image-name go-executor --ref-type=commit --ref-val=123
Requested status for repo: infra-metadata-service, image: go-executor, ref: commit:123
Image Status: Not built and image not found
Image SHA: 123
```
How did you test the new or updated feature?

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
  • Loading branch information
pei-mysten authored Jun 24, 2024
1 parent f862b10 commit 77b242f
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/suiop-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
license = "Apache-2.0"
name = "suiop-cli"
publish = false
version = "0.2.3"
version = "0.2.4"

[lib]
name = "suioplib"
Expand Down
202 changes: 199 additions & 3 deletions crates/suiop-cli/src/cli/ci/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use anyhow::Result;
use chrono::{DateTime, Local, Utc};
use clap::{Parser, ValueEnum};
use colored::Colorize;
use serde::{self, Serialize};
use serde::{self, Deserialize, Serialize};
use std::{fmt::Display, str::FromStr};
use tabled::{settings::Style, Table, Tabled};
use tracing::debug;
Expand Down Expand Up @@ -89,6 +89,26 @@ pub enum ImageAction {
#[arg(short, long)]
limit: Option<u32>,
},
#[command(name = "status")]
Status {
#[arg(short = 'r', long)]
repo_name: String,
#[arg(short = 'i', long)]
image_name: String,
#[arg(short = 't', long)]
ref_type: Option<RefType>,
#[arg(short = 'v', long)]
ref_val: Option<String>,
},
#[command(name = "list")]
List {
#[arg(short, long)]
repo_name: String,
#[arg(short, long)]
image_name: Option<String>,
#[arg(short, long)]
limit: Option<i32>,
},
}

#[derive(serde::Serialize, Debug)]
Expand All @@ -107,7 +127,17 @@ struct QueryBuildsRequest {
limit: u32,
}

#[derive(serde::Serialize)]
struct ImageStatusRequest {
repo_name: String,
image_name: String,
repo_ref_type: RefType,
repo_ref: String,
}

const ENDPOINT: &str = "/automation/image-build";
const STATUS_ENDPOINT: &str = "/automation/image-status";
const LIST_ENDPOINT: &str = "/automation/images";

pub async fn image_cmd(args: &ImageArgs) -> Result<()> {
let token = get_oauth_token().await?;
Expand All @@ -130,6 +160,85 @@ struct QueryBuildResponse {
pods: Vec<JobStatus>,
}

#[derive(ValueEnum, Clone, Debug)]
// #[clap(rename_all = "snake_case")]
enum ImageStatus {
Found,
Pending,
Building,
BuiltNotFound,
Failed,
Unknown,
NotBuiltNotFound,
}

impl<'a> Deserialize<'a> for ImageStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
let s = i32::deserialize(deserializer)?;
match s {
0 => Ok(ImageStatus::Found),
1 => Ok(ImageStatus::Pending),
2 => Ok(ImageStatus::Building),
3 => Ok(ImageStatus::BuiltNotFound),
4 => Ok(ImageStatus::Failed),
5 => Ok(ImageStatus::Unknown),
6 => Ok(ImageStatus::NotBuiltNotFound),
_ => Ok(ImageStatus::Unknown),
}
}
}

impl Display for ImageStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImageStatus::Found => write!(f, "Found"),
ImageStatus::Pending => write!(f, "Pending"),
ImageStatus::Building => write!(f, "Building"),
ImageStatus::BuiltNotFound => {
write!(f, "Build succeed but image not found")
}
ImageStatus::Failed => write!(f, "Failed"),
ImageStatus::Unknown => write!(f, "Unknown"),
ImageStatus::NotBuiltNotFound => {
write!(f, "Not built and image not found")
}
}
}
}

#[derive(serde::Deserialize)]
struct ImageStatusResponse {
pub status: ImageStatus,
pub image_sha: String,
}

#[derive(serde::Serialize)]
struct ImageListRequest {
repo_name: String,
image_name: Option<String>,
limit: Option<i32>,
}

#[derive(serde::Deserialize)]
struct ImageDetails {
pub name: String,
pub tags: Vec<String>,
}

#[derive(Tabled)]
struct ImageRow {
name: String,
tags: String,
}

#[derive(serde::Deserialize)]
struct ImageListResponse {
pub images: Vec<ImageDetails>,
}

async fn send_image_request(token: &str, action: &ImageAction) -> Result<()> {
let req = generate_image_request(token, action);

Expand Down Expand Up @@ -193,6 +302,57 @@ async fn send_image_request(token: &str, action: &ImageAction) -> Result<()> {
let mut tabled = Table::new(job_statuses);
tabled.with(Style::rounded());

let tabled_str = tabled.to_string();
println!("{}", tabled_str);
}
ImageAction::Status {
repo_name,
image_name,
ref_type,
ref_val,
} => {
let mut ref_name = "".to_string();
if let Some(ref_type) = ref_type {
ref_name.push_str(&ref_type.to_string())
} else {
ref_name.push_str("branch");
}
if let Some(ref_val) = ref_val {
ref_name.push_str(&format!(":{}", ref_val))
} else {
ref_name.push_str(":main")
}
println!(
"Requested status for repo: {}, image: {}, ref: {}",
repo_name.green(),
image_name.green(),
ref_name.green()
);
// println!("resp: {:?}", resp.text().await?);

let json_resp = resp.json::<ImageStatusResponse>().await?;
println!("Image Status: {}", json_resp.status.to_string().green());
println!("Image SHA: {}", json_resp.image_sha.green());
}
ImageAction::List {
repo_name,
image_name: _,
limit: _,
} => {
println!("Requested list for repo: {}", repo_name.green());
let json_resp = resp.json::<ImageListResponse>().await?;
let details = json_resp.images.into_iter().map(|image| {
let image_name = image.name;
let image_tags = image.tags;
ImageRow {
name: image_name,
// convert images tags vec to multiple strings
tags: image_tags.join(" | "),
}
});
let mut tabled = Table::new(details);
tabled.with(Style::rounded());

let tabled_str = tabled.to_string();
println!("{}", tabled_str);
}
Expand Down Expand Up @@ -233,8 +393,6 @@ fn generate_headers_with_auth(token: &str) -> reqwest::header::HeaderMap {
fn generate_image_request(token: &str, action: &ImageAction) -> reqwest::RequestBuilder {
let client = reqwest::Client::new();
let api_server = get_api_server();
let full_url = format!("{}{}", api_server, ENDPOINT);
debug!("full_url: {}", full_url);
let req = match action {
ImageAction::Build {
repo_name,
Expand All @@ -244,6 +402,8 @@ fn generate_image_request(token: &str, action: &ImageAction) -> reqwest::Request
ref_type,
ref_val,
} => {
let full_url = format!("{}{}", api_server, ENDPOINT);
debug!("full_url: {}", full_url);
let req = client.post(full_url);
let body = RequestBuildRequest {
repo_name: repo_name.clone(),
Expand All @@ -257,6 +417,8 @@ fn generate_image_request(token: &str, action: &ImageAction) -> reqwest::Request
req.json(&body).headers(generate_headers_with_auth(token))
}
ImageAction::Query { repo_name, limit } => {
let full_url = format!("{}{}", api_server, ENDPOINT);
debug!("full_url: {}", full_url);
let req = client.get(full_url);
let limit = (*limit).unwrap_or(10);
let query = QueryBuildsRequest {
Expand All @@ -265,6 +427,40 @@ fn generate_image_request(token: &str, action: &ImageAction) -> reqwest::Request
};
req.query(&query).headers(generate_headers_with_auth(token))
}
ImageAction::Status {
repo_name,
image_name,
ref_type,
ref_val,
} => {
let full_url = format!("{}{}", api_server, STATUS_ENDPOINT);
debug!("full_url: {}", full_url);
let req = client.get(full_url);
let ref_type = ref_type.clone().unwrap_or(RefType::Branch);
let ref_val = ref_val.clone().unwrap_or("main".to_string());
let query = ImageStatusRequest {
repo_name: repo_name.clone(),
image_name: image_name.clone(),
repo_ref_type: ref_type,
repo_ref: ref_val,
};
req.query(&query).headers(generate_headers_with_auth(token))
}
ImageAction::List {
repo_name,
image_name,
limit,
} => {
let full_url = format!("{}{}", api_server, LIST_ENDPOINT);
debug!("full_url: {}", full_url);
let req = client.get(full_url);
let query = ImageListRequest {
repo_name: repo_name.clone(),
image_name: image_name.clone(),
limit: *limit,
};
req.query(&query).headers(generate_headers_with_auth(token))
}
};
debug!("req: {:?}", req);

Expand Down

0 comments on commit 77b242f

Please sign in to comment.