Make CDH great (...not again) #3
61
.github/workflows/build-version.yaml
vendored
Normal file
61
.github/workflows/build-version.yaml
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
name: "Version build"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --release --all-features --target=${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Archive build artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: build-${{matrix.target}}
|
||||||
|
path: ${{ github.workspace }}/target/${{ matrix.target }}/release/cdh
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Download artifact
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
|
||||||
|
- name: Set version variable
|
||||||
|
run: echo "CDH_VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Rename release to avoid name conflict
|
||||||
|
run: ./scripts/rename_releases.sh
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: release/*
|
53
.github/workflows/container-stable.yaml
vendored
Normal file
53
.github/workflows/container-stable.yaml
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
name: "Stable container"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "src/**"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
containerization:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set action link variable
|
||||||
|
run: echo "LINK=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@master
|
||||||
|
with:
|
||||||
|
platforms: all
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@master
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.CR_PAT }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
ghcr.io/allanger/${{ env.GITHUB_REPOSITORY }}:stable
|
||||||
|
ghcr.io/allanger/${{ env.GITHUB_REPOSITORY }}:latest
|
||||||
|
labels: |
|
||||||
|
action_id=${{ github.action }}
|
||||||
|
action_link=${{ env.LINK }}
|
||||||
|
actor=${{ github.actor }}
|
||||||
|
sha=${{ github.sha }}
|
||||||
|
ref=${{ github.ref }}
|
53
.github/workflows/container-version.yaml
vendored
Normal file
53
.github/workflows/container-version.yaml
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
name: "Version container"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
containerization:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set version variable
|
||||||
|
run: echo "TAG=${GITHUB_REF##*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set action link variable
|
||||||
|
run: echo "LINK=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@master
|
||||||
|
with:
|
||||||
|
platforms: all
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@master
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.CR_PAT }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
ghcr.io/allanger/${{ env.GITHUB_REPOSITORY }}:${{ env.TAG }}
|
||||||
|
labels: |
|
||||||
|
action_id=${{ github.action }}
|
||||||
|
action_link=${{ env.LINK }}
|
||||||
|
actor=${{ github.actor }}
|
||||||
|
sha=${{ github.sha }}
|
||||||
|
ref=${{ github.ref }}
|
57
.github/workflows/tests.yaml
vendored
Normal file
57
.github/workflows/tests.yaml
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
name: "Tests"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "src/**"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cargo_udeps:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
|
||||||
|
- name: Install cargo-udeps
|
||||||
|
run: cargo install cargo-udeps --locked
|
||||||
|
|
||||||
|
- name: Check dependencies
|
||||||
|
run: cargo +nightly udeps
|
||||||
|
|
||||||
|
cargo_test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --release --all-features
|
||||||
|
- run: cargo test
|
||||||
|
cargo_clippy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --release --all-features
|
||||||
|
- run: cargo clippy
|
17
Cargo.lock
generated
17
Cargo.lock
generated
@ -32,12 +32,6 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "build_html"
|
|
||||||
version = "2.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa8ffb62af7b0911893e2d6126891b2a018e387078fa5d855cb42d0d90d88075"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecount"
|
name = "bytecount"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
@ -54,8 +48,8 @@ checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
|||||||
name = "cdh"
|
name = "cdh"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"build_html",
|
|
||||||
"clap",
|
"clap",
|
||||||
|
"clap_complete",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"handlebars",
|
"handlebars",
|
||||||
"log",
|
"log",
|
||||||
@ -87,6 +81,15 @@ dependencies = [
|
|||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_complete"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce8955d4e8cd4f28f9a01c93a050194c4d131e73ca02f6636bcddbed867014d7"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
|
@ -14,5 +14,6 @@ version-compare = "0.1.0"
|
|||||||
clap = { version = "4.1.1", features = ["derive", "env"] }
|
clap = { version = "4.1.1", features = ["derive", "env"] }
|
||||||
serde_yaml = "0.9.16"
|
serde_yaml = "0.9.16"
|
||||||
tabled = "0.10.0"
|
tabled = "0.10.0"
|
||||||
build_html = "2.1.0"
|
|
||||||
handlebars = "4.3.1"
|
handlebars = "4.3.1"
|
||||||
|
clap_complete = "4.0.6"
|
||||||
|
|
||||||
|
31
README.md
31
README.md
@ -1,2 +1,33 @@
|
|||||||
# Check Da Helm
|
# Check Da Helm
|
||||||
> Your helm releases are outdated, aren't they? Now you can check!
|
> Your helm releases are outdated, aren't they? Now you can check!
|
||||||
|
|
||||||
|
[![Version build](https://github.com/allanger/check-da-helm/actions/workflows/build-version.yaml/badge.svg)](https://github.com/allanger/check-da-helm/actions/workflows/build-version.yaml)
|
||||||
|
[![Version container](https://github.com/allanger/check-da-helm/actions/workflows/container-version.yaml/badge.svg)](https://github.com/allanger/check-da-helm/actions/workflows/container-version.yaml)
|
||||||
|
[![Stable container](https://github.com/allanger/check-da-helm/actions/workflows/container-stable.yaml/badge.svg)](https://github.com/allanger/check-da-helm/actions/workflows/container-stable.yaml)
|
||||||
|
|
||||||
|
## What's this?
|
||||||
|
It's a simple command line tool that lets you check whether your helm releases (currently installed by helmfile or argo) are outdated or not. Why it's created? But the main reason why it's created, is a necessity to check if helm releases that you have installed in your cluster still exist in repos. Once `Bitnami` removed old charts from their main repo and, I believe, everybody needed then some time to understand what happened. So I decided to write this tool. I was checking helmfiles and testing if chart were still in repos. And in case something is broken, I would be notified in the morning. Of course, broken helm charts are something you'll eventually know about, but it just feels better to know about them with this simple cli.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
### Dependencies
|
||||||
|
Depending on the tool you want to use `cdm` with, you must have either `helmfile` or `argocd` installed. And in any case you need to have `helm`
|
||||||
|
|
||||||
|
### Download
|
||||||
|
|
||||||
|
Get executable from github releases
|
||||||
|
|
||||||
|
Prebuilt binaries exist for **Linux x86_64** and **MacOS arm64** and **x86_64**
|
||||||
|
|
||||||
|
Don't forget to add the binary to $PATH
|
||||||
|
```BASH
|
||||||
|
$ curl https://raw.githubusercontent.com/allanger/check-da-helm/main/scripts/download_cdm.sh | bash
|
||||||
|
$ cdm -h
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build from source
|
||||||
|
1. Build binary
|
||||||
|
```BASH
|
||||||
|
$ cargo build --release
|
||||||
|
```
|
||||||
|
2. Run `gum help`
|
||||||
|
|
||||||
|
50
scripts/download_cdh.sh
Executable file
50
scripts/download_cdh.sh
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
case "$(uname)" in
|
||||||
|
|
||||||
|
"Darwin")
|
||||||
|
SYSTEM="apple-darwin"
|
||||||
|
case $(uname -m) in
|
||||||
|
"arm64")
|
||||||
|
TARGET="aarch64-$SYSTEM"
|
||||||
|
;;
|
||||||
|
"x86_64")
|
||||||
|
TARGET="x86_64-$SYSTEM"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsuported target"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"Linux")
|
||||||
|
SYSTEM="unknown-linux-gnu"
|
||||||
|
case $(uname -m) in
|
||||||
|
"x86_64")
|
||||||
|
TARGET="x86_64-$SYSTEM"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsuported target"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Signal number $1 is not processed"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
LATEST_VERSION="v$(curl -s https://raw.githubusercontent.com/allanger/check-da-helm/main/Cargo.toml | awk -F ' = ' '$1 ~ /version/ { gsub(/[\"]/, "", $2); printf("%s",$2) }')"
|
||||||
|
echo "Downloading $LATEST_VERSION"
|
||||||
|
|
||||||
|
RELEASE_NAME=cdh-$LATEST_VERSION-$TARGET
|
||||||
|
RELEASE_URL="https://github.com/allanger/check-da-helm/releases/download/$LATEST_VERSION/$RELEASE_NAME"
|
||||||
|
echo "Link for downloading: $RELEASE_URL"
|
||||||
|
curl -LJO $RELEASE_URL
|
||||||
|
|
||||||
|
mv $RELEASE_NAME cdh
|
||||||
|
chmod +x cdh
|
||||||
|
|
||||||
|
echo 'Make sure that cdh is in your $PATH'
|
||||||
|
echo 'Try: '
|
||||||
|
echo ' $ export PATH=$PATH:$PWD'
|
||||||
|
echo ' $ cdh -h'
|
10
scripts/rename_releases.sh
Executable file
10
scripts/rename_releases.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo 'renaming cdh to cdh-$VERSION-$SYSTEM format'
|
||||||
|
mkdir -p release
|
||||||
|
echo "version - $CDH_VERSION"
|
||||||
|
for BUILD in build*; do
|
||||||
|
SYSTEM=$(echo $BUILD | sed -e 's/build-//g')
|
||||||
|
echo "system - $SYSTEM"
|
||||||
|
cp $BUILD/cdh release/cdh-$CDH_VERSION-$SYSTEM
|
||||||
|
done
|
||||||
|
ls release
|
@ -1,10 +1,13 @@
|
|||||||
use clap::Arg;
|
|
||||||
use log::{debug, info, error};
|
|
||||||
use serde_json::from_str;
|
|
||||||
use crate::types::{self, HelmRepo};
|
use crate::types::{self, HelmRepo};
|
||||||
|
use log::{debug, error, info};
|
||||||
|
use serde_json::from_str;
|
||||||
|
|
||||||
use super::Connector;
|
use super::Connector;
|
||||||
use std::{borrow::Borrow, io::{Result, Error, ErrorKind}, process::Command};
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
io::{Error, ErrorKind, Result},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct Argo;
|
pub(crate) struct Argo;
|
||||||
|
|
||||||
@ -104,9 +107,7 @@ impl Connector for Argo {
|
|||||||
String::from_utf8_lossy(&output.stderr),
|
String::from_utf8_lossy(&output.stderr),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
impl Argo {
|
impl Argo {
|
||||||
pub(crate) fn init() -> Argo {
|
pub(crate) fn init() -> Argo {
|
||||||
|
@ -4,7 +4,7 @@ use serde_json::from_str;
|
|||||||
use crate::types;
|
use crate::types;
|
||||||
|
|
||||||
use super::Connector;
|
use super::Connector;
|
||||||
use std::{borrow::Borrow, fmt::format, io::Result, process::Command};
|
use std::{borrow::Borrow, io::Result, process::Command};
|
||||||
|
|
||||||
pub(crate) struct Helmfile {
|
pub(crate) struct Helmfile {
|
||||||
path: String,
|
path: String,
|
||||||
|
207
src/main.rs
207
src/main.rs
@ -1,22 +1,21 @@
|
|||||||
mod connectors;
|
mod connectors;
|
||||||
|
mod output;
|
||||||
mod types;
|
mod types;
|
||||||
|
use clap::{arg, command, Parser, Subcommand, ValueEnum};
|
||||||
use clap::{Parser, ValueEnum};
|
|
||||||
use connectors::{Argo, Connector, Helm, Helmfile};
|
use connectors::{Argo, Connector, Helm, Helmfile};
|
||||||
use handlebars::Handlebars;
|
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
|
use output::Output;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
fmt::{self, format},
|
io::Result,
|
||||||
io::{Error, ErrorKind, Result},
|
|
||||||
process::{exit, Command},
|
process::{exit, Command},
|
||||||
};
|
};
|
||||||
use tabled::Tabled;
|
use types::ExecResult;
|
||||||
use version_compare::{Cmp, Version};
|
use version_compare::{Cmp, Version};
|
||||||
|
|
||||||
use crate::types::HelmChart;
|
use crate::types::{HelmChart, Status};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||||
enum Kinds {
|
enum Kinds {
|
||||||
@ -25,13 +24,22 @@ enum Kinds {
|
|||||||
Helmfile,
|
Helmfile,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||||
|
enum Outputs {
|
||||||
|
Yaml,
|
||||||
|
HTML,
|
||||||
|
}
|
||||||
|
|
||||||
/// Check you helm releaseas managed by Argo
|
/// Check you helm releaseas managed by Argo
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Type of the
|
/// How do you install your helm charts
|
||||||
#[clap(long, value_enum)]
|
#[clap(long, value_enum)]
|
||||||
kind: Kinds,
|
kind: Kinds,
|
||||||
|
/// What kind of output would you like to receive?
|
||||||
|
#[clap(long, value_enum, default_value = "yaml")]
|
||||||
|
output: Outputs,
|
||||||
/// Path to the helmfile
|
/// Path to the helmfile
|
||||||
#[clap(short, long, value_parser, default_value = "./")]
|
#[clap(short, long, value_parser, default_value = "./")]
|
||||||
path: String,
|
path: String,
|
||||||
@ -43,6 +51,14 @@ struct Args {
|
|||||||
no_sync: bool,
|
no_sync: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
#[command(arg_required_else_help = true)]
|
||||||
|
Generate {
|
||||||
|
#[arg(value_name = "SHELL", default_missing_value = "zsh")]
|
||||||
|
shell: clap_complete::shells::Shell,
|
||||||
|
},
|
||||||
|
}
|
||||||
/// A struct to write helm repo description to
|
/// A struct to write helm repo description to
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
struct Repo {
|
struct Repo {
|
||||||
@ -58,42 +74,7 @@ struct LocalCharts {
|
|||||||
version: Option<String>,
|
version: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Three possible statuses of versions comparison
|
|
||||||
#[derive(Clone, Serialize)]
|
|
||||||
enum Status {
|
|
||||||
Uptodate,
|
|
||||||
Outdated,
|
|
||||||
Missing,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Status {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Status::Uptodate => write!(f, "Up-to-date"),
|
|
||||||
Status::Outdated => write!(f, "Outdated"),
|
|
||||||
Status::Missing => write!(f, "Missing"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Tabled, Serialize)]
|
|
||||||
struct ExecResult {
|
|
||||||
name: String,
|
|
||||||
latest_version: String,
|
|
||||||
current_version: String,
|
|
||||||
status: Status,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for the ExecResult struct
|
// Implementation for the ExecResult struct
|
||||||
impl ExecResult {
|
|
||||||
fn new(name: String, latest_version: String, current_version: String, status: Status) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
latest_version,
|
|
||||||
current_version,
|
|
||||||
status,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Preparations step
|
// Preparations step
|
||||||
@ -122,12 +103,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
charts.iter().for_each(|a| {
|
charts.iter().for_each(|a| {
|
||||||
let err = check_chart(&mut result, a);
|
check_chart(&mut result, a).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Parse the helmfile
|
// Parse the helmfile
|
||||||
// Handling the result
|
// Handling the result
|
||||||
match handle_result(&result, args.outdated_fail) {
|
match handle_result(&result, args.outdated_fail, args.output) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
if result {
|
if result {
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -141,7 +122,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_chart(result: &mut Vec<ExecResult>, local_chart: &types::HelmChart) -> Result<()> {
|
fn check_chart(result: &mut Vec<ExecResult>, local_chart: &types::HelmChart) -> Result<()> {
|
||||||
if local_chart.clone().name.is_some() {
|
if local_chart.name.is_some() {
|
||||||
let version = local_chart.version.clone().unwrap();
|
let version = local_chart.version.clone().unwrap();
|
||||||
let chart = local_chart.name.clone().unwrap();
|
let chart = local_chart.name.clone().unwrap();
|
||||||
return match version.is_empty() {
|
return match version.is_empty() {
|
||||||
@ -215,7 +196,11 @@ fn check_chart(result: &mut Vec<ExecResult>, local_chart: &types::HelmChart) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Handle result
|
/// Handle result
|
||||||
fn handle_result(result: &Vec<ExecResult>, outdated_fail: bool) -> Result<bool> {
|
fn handle_result(
|
||||||
|
result: &Vec<ExecResult>,
|
||||||
|
outdated_fail: bool,
|
||||||
|
output_kind: Outputs,
|
||||||
|
) -> Result<bool> {
|
||||||
let mut failed = false;
|
let mut failed = false;
|
||||||
for r in result.clone() {
|
for r in result.clone() {
|
||||||
match r.status {
|
match r.status {
|
||||||
@ -238,135 +223,15 @@ fn handle_result(result: &Vec<ExecResult>, outdated_fail: bool) -> Result<bool>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let template = r#"
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Chart Name</th>
|
|
||||||
<th>Current Version</th>
|
|
||||||
<th>Latest Version</th>
|
|
||||||
<th>Status</th>
|
|
||||||
</tr>
|
|
||||||
{{#each this as |tr|}}
|
|
||||||
<tr>
|
|
||||||
<th>{{tr.name}}</th>
|
|
||||||
<th>{{tr.current_version}}</th>
|
|
||||||
<th>{{tr.latest_version}}</th>
|
|
||||||
<th>{{tr.status}}</th>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</table>
|
|
||||||
"#;
|
|
||||||
let mut reg = Handlebars::new();
|
|
||||||
|
|
||||||
// TODO: Handle this error
|
match output_kind {
|
||||||
reg.register_template_string("html_table", template)
|
Outputs::Yaml => print!("{}", output::YAML::print(result)?),
|
||||||
.unwrap();
|
Outputs::HTML => print!("{}", output::HTML::print(result)?),
|
||||||
|
|
||||||
match reg.render("html_table", &result) {
|
|
||||||
Ok(res) => println!("{}", res),
|
|
||||||
Err(err) => error!("{}", err),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(failed)
|
Ok(failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Downloading repos from repositories
|
|
||||||
fn repo_sync() -> Result<()> {
|
|
||||||
info!("syncing helm repos");
|
|
||||||
let cmd: String = "argocd app list -o json | jq '[ .[] | {name: .spec.source.chart, url: .spec.source.repoURL} ]'".to_string();
|
|
||||||
let output = Command::new("bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(cmd)
|
|
||||||
.output()
|
|
||||||
.expect("helmfile is failed");
|
|
||||||
info!("{:?}", output.clone());
|
|
||||||
if output.status.success() {
|
|
||||||
let repos: Vec<Repo> = serde_json::from_slice(&output.stdout).unwrap();
|
|
||||||
info!("adding repositories");
|
|
||||||
for repo in repos.iter() {
|
|
||||||
let name = repo.name.clone();
|
|
||||||
if name.is_some() {
|
|
||||||
info!(
|
|
||||||
"syncing {} with the origin {}",
|
|
||||||
name.clone().unwrap(),
|
|
||||||
repo.url
|
|
||||||
);
|
|
||||||
let cmd = format!(
|
|
||||||
"helm repo add {} {}",
|
|
||||||
name.clone().unwrap(),
|
|
||||||
repo.url.clone()
|
|
||||||
);
|
|
||||||
debug!("running {}", cmd);
|
|
||||||
let output = Command::new("bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(cmd)
|
|
||||||
.output()
|
|
||||||
.expect("helm repo sync is failed");
|
|
||||||
match output.status.success() {
|
|
||||||
true => {
|
|
||||||
info!(
|
|
||||||
"{} with the origin {} is synced successfully",
|
|
||||||
name.unwrap(),
|
|
||||||
repo.url
|
|
||||||
);
|
|
||||||
}
|
|
||||||
false => {
|
|
||||||
error!(
|
|
||||||
"{} with the origin {} can't be synced",
|
|
||||||
name.unwrap(),
|
|
||||||
repo.url
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let cmd = "helm repo update";
|
|
||||||
let output = Command::new("bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(cmd)
|
|
||||||
.output()
|
|
||||||
.expect("helm repo sync is failed");
|
|
||||||
match output.status.success() {
|
|
||||||
true => {
|
|
||||||
info!("repositories are updated successfully");
|
|
||||||
}
|
|
||||||
false => {
|
|
||||||
error!(
|
|
||||||
"repositories can't be updated, {}",
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::new(
|
|
||||||
ErrorKind::Other,
|
|
||||||
String::from_utf8_lossy(&output.stderr),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run helmfile list and write the result into struct
|
|
||||||
fn parse_argo_apps() -> Result<Vec<LocalCharts>> {
|
|
||||||
let cmd: String = "argocd app list -o json | jq '[.[] | {chart: .spec.source.chart, version: .spec.source.targetRevision}]'".to_string();
|
|
||||||
|
|
||||||
debug!("executing '${}'", cmd);
|
|
||||||
let output = Command::new("bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(cmd)
|
|
||||||
.output()
|
|
||||||
.expect("helmfile is failed");
|
|
||||||
let helm_stdout = String::from_utf8_lossy(&output.stdout);
|
|
||||||
|
|
||||||
match from_str::<Vec<LocalCharts>>(Borrow::borrow(&helm_stdout)) {
|
|
||||||
Ok(mut charts) => {
|
|
||||||
charts.dedup();
|
|
||||||
Ok(charts)
|
|
||||||
}
|
|
||||||
Err(err) => Err(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes two version and returns the newer one.
|
/// Takes two version and returns the newer one.
|
||||||
fn get_newer_version(v1: String, v2: String) -> String {
|
fn get_newer_version(v1: String, v2: String) -> String {
|
||||||
match Version::from(&v1.replace('v', ""))
|
match Version::from(&v1.replace('v', ""))
|
||||||
|
61
src/output/mod.rs
Normal file
61
src/output/mod.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use std::io::{Result, Error, ErrorKind};
|
||||||
|
|
||||||
|
use handlebars::Handlebars;
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use crate::types::ExecResult;
|
||||||
|
|
||||||
|
pub(crate) trait Output {
|
||||||
|
fn print(data: &Vec<ExecResult>) -> Result<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct HTML;
|
||||||
|
|
||||||
|
impl Output for HTML {
|
||||||
|
fn print(data: &Vec<ExecResult>) -> Result<String> {
|
||||||
|
// To generate htlm output, I have to use templates because I haven't found any other good
|
||||||
|
// solution
|
||||||
|
let template = r#"
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Chart Name</th>
|
||||||
|
<th>Current Version</th>
|
||||||
|
<th>Latest Version</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
{{#each this as |tr|}}
|
||||||
|
<tr>
|
||||||
|
<th>{{tr.name}}</th>
|
||||||
|
<th>{{tr.current_version}}</th>
|
||||||
|
<th>{{tr.latest_version}}</th>
|
||||||
|
<th>{{tr.status}}</th>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</table>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut reg = Handlebars::new();
|
||||||
|
// TODO: Handle this error
|
||||||
|
reg.register_template_string("html_table", template)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match reg.render("html_table", &data) {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(err) => {
|
||||||
|
error!("{}", err);
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, err.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct YAML;
|
||||||
|
|
||||||
|
impl Output for YAML {
|
||||||
|
fn print(data: &Vec<ExecResult>) -> Result<String> {
|
||||||
|
match serde_yaml::to_string(&data) {
|
||||||
|
Ok(res) => return Ok(res),
|
||||||
|
Err(err) => return Err(Error::new(ErrorKind::InvalidData, err.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use tabled::Tabled;
|
||||||
|
|
||||||
/// Struct for parsing charts info from helmfile
|
/// Struct for parsing charts info from helmfile
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
@ -15,8 +16,8 @@ pub(crate) struct HelmRepo {
|
|||||||
pub(crate) url: String,
|
pub(crate) url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
enum Status {
|
pub(crate) enum Status {
|
||||||
Uptodate,
|
Uptodate,
|
||||||
Outdated,
|
Outdated,
|
||||||
Missing,
|
Missing,
|
||||||
@ -31,3 +32,22 @@ impl fmt::Display for Status {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Tabled, Serialize, Deserialize)]
|
||||||
|
pub(crate) struct ExecResult {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) latest_version: String,
|
||||||
|
pub(crate) current_version: String,
|
||||||
|
pub(crate) status: Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecResult {
|
||||||
|
pub(crate) fn new(name: String, latest_version: String, current_version: String, status: Status) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
latest_version,
|
||||||
|
current_version,
|
||||||
|
status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user