Skip to content

Commit

Permalink
add: clean-harbor-images
Browse files Browse the repository at this point in the history
  • Loading branch information
k8scat committed Dec 21, 2021
1 parent 4d2c053 commit c4b6e49
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 134 deletions.
86 changes: 86 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Release

on:
push:
tags:
- "v*.*.*"

jobs:
release:
name: Create Release
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Get version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_version.outputs.VERSION }}
release_name: ${{ steps.get_version.outputs.VERSION }}
draft: false
prerelease: false

crate-publish:
name: Crate Publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Publish
run: cargo publish --all-features --token ${{ secrets.CRATE_REGISTRY_TOKEN }}

linux-release:
name: Build on Linux
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build
run: cargo build --release --all-features --bin clean-harbor-images
- name: Get version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Upload assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: target/release/clean-harbor-images
asset_name: clean-harbor-images-${{ steps.get_version.outputs.VERSION }}-linux-amd64
asset_content_type: application/octet-stream

macos-release:
name: Build on MacOS
needs: release
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build
run: cargo build --release --all-features --bin clean-harbor-images
- name: Get version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Upload assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: target/release/clean-harbor-images
asset_name: clean-harbor-images-${{ steps.get_version.outputs.VERSION }}-darwin-amd64
asset_content_type: application/octet-stream
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
Cargo.lock
.idea
/config.yml
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[package]
name = "harbor_rs"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
description = "Harbor SDK for Rust"
license = "MIT"
homepage = "https://github.com/k8scat/harbor_rs"
authors = ["k8scat <[email protected]>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -15,3 +17,9 @@ chrono = "0.4.19"
base64 = "0.13.0"
serde_json = "1.0.73"
serde = { version = "1.0.132", features = ["derive"] }
clap = "2.34.0"
serde_yaml = "0.8.23"

[[bin]]
name = "clean-harbor-images"
path = "src/bin/clean-harbor-images.rs"
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
version = 0.1.1
upgrade:
sed -i "" 's/^version = "[0-9]*.[0-9]*.[0-9]*"/version = "${version}"/' Cargo.toml
sed -i "" 's/harbor_rs = "[0-9]*.[0-9]*.[0-9]*"/harbor_rs = "${version}"/g' README.md
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
# harbor_rs

[Harbor](https://goharbor.io/) SDK for Rust

```toml
[dependencies]
harbor_rs = "0.1.0"
harbor_rs = "0.1.1"
```

## clean-harbor-images

根据时间间隔清理 [Harbor](https://goharbor.io/) 上的镜像 Tag

```yaml
harbor_base_api: "https://example.com/api"
harbor_username: "admin"
harbor_password": "admin"
clean_interval": 20 # 清理 20 天前构建的镜像
repos:
- "test/image1"
- "test/image2"
```
## LICENSE
[MIT](https://github.com/k8scat/harbor_rs/blob/main/LICENSE)
89 changes: 89 additions & 0 deletions src/bin/clean-harbor-images.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::io::Read;
use std::ops::Sub;
use chrono::Duration;
use chrono::prelude::*;
use anyhow::{anyhow, Result};
use clap::{App, Arg};
use harbor_rs::Client;
use serde::{Deserialize, Serialize};
use harbor_rs::harbor::common::parse_time;
use harbor_rs::harbor::gc::{Schedule, ScheduleType};

extern crate base64;

#[derive(Debug, Deserialize, Serialize)]
struct Config {
pub harbor_base_api: String,
pub harbor_username: String,
pub harbor_password: String,
pub clean_interval: u32,
pub repos: Vec<String>,
}

#[tokio::main]
async fn main() {
let matches = App::new("clean-harbor-images")
.version("1.0")
.author("K8sCat <[email protected]>")
.about("Clean harbor images")
.arg(Arg::with_name("config")
.short("c")
.long("config")
.value_name("FILE")
.default_value("config.yml")
.help("Set config file")
.takes_value(true))
.get_matches();
let config_file = matches.value_of("config").unwrap();
let config = load_config(config_file).unwrap();
let client = harbor_rs::Client::new(config.harbor_base_api, config.harbor_username, config.harbor_password).unwrap();
let clean_interval = Local::now().sub(Duration::days(config.clean_interval as i64));
for repo in config.repos {
clean(&client, repo.as_str(), clean_interval).await.unwrap();
}
}

fn load_config(path: &str) -> Result<Config> {
let mut f = std::fs::File::open(path)?;
let mut s = String::new();
f.read_to_string(&mut s)?;
let config: Config = serde_yaml::from_str(&s)?;
Ok(config)
}

async fn clean(client: &Client, repo: &str, interval: DateTime<Local>) -> Result<()> {
let tags = client.list_tags(repo, None, Some(true)).await?;
for tag in tags {
let push_time = parse_time(tag.push_time)?;
if push_time.le(&interval) {
client.delete_tag(repo, tag.name.as_str()).await?;
println!("deleted {} which pushed at {}", tag.name, push_time.format("%Y-%m-%d %H:%M:%S"));
}
}
Ok(manual_gc(client).await?)
}

async fn manual_gc(client: &Client) -> Result<()> {
let schedule = Schedule {
schedule_type: ScheduleType::Manual.to_string(),
cron: None,
};
client.create_schedule(&schedule).await?;
println!("manual gc schedule created");

for _ in 1..100 {
let gc_results = client.list_gc_results().await?;
if gc_results.len() == 0 {
return Err(anyhow!("gc results is empty"));
}
let gc_result = gc_results.get(0).unwrap();
if gc_result.job_status == "finished" {
println!("gc status: finished");
return Ok(());
}
println!("gc status: {}", gc_result.job_status);
std::thread::sleep(std::time::Duration::from_secs(1));
}
Err(anyhow!("gc is still not finished, please check manually"))
}

7 changes: 7 additions & 0 deletions src/bin/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
harbor_base_api: "https://example.com/api"
harbor_username: "admin"
harbor_password": "admin"
clean_interval": 20
repos:
- "test/image1"
- "test/image2"
4 changes: 0 additions & 4 deletions src/harbor/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use anyhow::Result;

pub struct Client {
base_url: String,
username: String,
password: String,
pub client: reqwest::Client,
}

Expand All @@ -14,8 +12,6 @@ impl Client {
let token = base64::encode(format!("{}:{}", username, password));
let client = Client {
base_url,
username,
password,
client: reqwest::Client::builder()
.timeout(core::time::Duration::from_secs(60))
.default_headers(
Expand Down
2 changes: 1 addition & 1 deletion src/harbor/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct Label {
pub name: String
}

pub fn parse_time(time: &str) -> Result<DateTime<Local>> {
pub fn parse_time(time: String) -> Result<DateTime<Local>> {
Ok(time.parse::<DateTime<Local>>()?)
}

63 changes: 47 additions & 16 deletions src/harbor/gc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
use std::fmt;
use super::client::Client;
use serde::{Deserialize, Serialize};
use anyhow::Result;
use anyhow::{anyhow, Result};

#[derive(Debug)]
pub enum ScheduleType {
Hourly,
Daily,
Weekly,
Custom,
Manual,
None
}

/// enum to String
///
/// ```rust
/// ScheduleType::Hourly.to_string() // "Hourly"
/// ```
impl fmt::Display for ScheduleType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Schedule {
Expand All @@ -10,28 +32,37 @@ pub struct Schedule {
pub cron: Option<String>
}

#[derive(Debug, Deserialize, Serialize)]
pub struct GCResult {
pub job_status: String,
pub update_time: String,
pub schedule: Schedule,
pub deleted: bool,
pub job_kind: String,
pub creation_time: String,
pub id: u32,
pub job_name: String
}

impl Client {
pub async fn create_schedule(&self) -> Result<()> {
pub async fn create_schedule(&self, schedule: &Schedule) -> Result<()> {
let url = self.build_api(String::from("system/gc/schedule"));
let resp = self.client.post(url)
.header("Content-Type", "application/json")
.body(r#"{"schedule": {"type": "Manual"}}"#)
.body(format!("{{\"schedule\": {}}}", serde_json::to_string(schedule)?))
.send()
.await?;
Ok(())
if resp.status().eq(&reqwest::StatusCode::CREATED) {
Ok(())
} else {
Err(anyhow!("{} {}", resp.status(), resp.text().await?))
}
}
}

#[cfg(test)]
mod tests {
use crate::harbor::gc::Schedule;

#[test]
fn unmarshal_schedule() {
let s = Schedule{
schedule_type: "Manual".to_string(),
cron: None
};
println!("{}", serde_json::to_string(&s).unwrap());
pub async fn list_gc_results(&self) -> Result<Vec<GCResult>> {
let url = self.build_api(String::from("system/gc"));
let resp = self.client.get(url).send().await?;
let results = resp.json::<Vec<GCResult>>().await?;
Ok(results)
}
}
Loading

0 comments on commit c4b6e49

Please sign in to comment.