New control scripts (#108)

* add app and deployment overrides info scripts

* overrides/keys scripts

* add new scripts to the chart config

* archive using scaling

* archive check script + README

* fix timings values + up contolscripts version

* Get rid of kubectl calls

* remove tags

* remove status mode

* rename tag_check to config_check

* remove default values logic, optional name, additional checks at helm init

* bump chart and app version

* add keys sorting

* ingress.host to ingress.hostname hardcode

* add default override for wordpress

* parametrize ingress host key

* add gzip static + etag

* config_check: logs to stderr + proper user notifications

* fix bug with empty value key not being shown

* rename app-env-override and deployment-override cli options

* rename env configuration for octopod
This commit is contained in:
Alex-Sizov 2021-09-29 17:49:36 +03:00 committed by GitHub
parent a2c8ab0915
commit c8ac911f6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 715 additions and 243 deletions

View File

@ -2,8 +2,8 @@ apiVersion: v2
name: octopod
description: An opensource self-hosted solution for managing multiple deployments in a Kubernetes cluster.
type: application
version: 0.5.1
appVersion: 1.3.1
version: 0.6.0
appVersion: 1.4
keywords:
- kubernetes
- octopod

View File

@ -1,4 +1,5 @@
{{- $octopodAppAuthPassword := include "octopodUiAuthSecret" . -}}
{{- $etag := sha256sum (now | date "Mon Jan 2 15:04:05 MSK 2021") -}}
apiVersion: v1
kind: ConfigMap
metadata:
@ -14,11 +15,10 @@ data:
index index.html;
error_page 404 =200 /index.html;
charset utf-8;
gzip on;
gzip_disable msie6;
gzip_buffers 32 4k;
gzip_comp_level 3;
gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/rss+xml text/javascript image/svg+xml application/vnd.ms-f ontobject application/x-font-ttf font/opentype;
location ~* \.(html|css|js|xml)$ {
gzip_static on;
add_header 'ETag' '{{ $etag }}';
}
}
config.json: |
{

View File

@ -16,8 +16,13 @@ data:
CHECKING_COMMAND: {{ printf "%s/check" (include "controlScriptsPath" .) | quote }}
CLEANUP_COMMAND: {{ printf "%s/cleanup" (include "controlScriptsPath" .) | quote }}
ARCHIVE_CHECKING_COMMAND: {{ printf "%s/archive_check" (include "controlScriptsPath" .) | quote }}
CONFIG_CHECKING_COMMAND: {{ printf "%s/tag_check" (include "controlScriptsPath" .) | quote }}
CONFIG_CHECKING_COMMAND: {{ printf "%s/config_check" (include "controlScriptsPath" .) | quote }}
INFO_COMMAND: {{ printf "%s/info" (include "controlScriptsPath" .) | quote }}
DEPLOYMENT_CONFIG_COMMAND: {{ printf "%s/deployment_overrides" (include "controlScriptsPath" .) | quote }}
DEPLOYMENT_KEYS_COMMAND: {{ printf "%s/deployment_keys" (include "controlScriptsPath" .) | quote }}
APPLICATION_CONFIG_COMMAND: {{ printf "%s/app_overrides" (include "controlScriptsPath" .) | quote }}
APPLICATION_KEYS_COMMAND: {{ printf "%s/app_keys" (include "controlScriptsPath" .) | quote }}
UNARCHIVE_COMMAND: {{ printf "%s/unarchive" (include "controlScriptsPath" .) | quote }}
{{- range $name, $value := .Values.octopod.env }}
{{ $name }}: {{ $value | quote }}
{{- end }}

View File

@ -79,27 +79,28 @@ octopod:
projectName: Octopod
deploymentNamespace: octopod-deployment
baseDomain: ""
statusUpdateTimeout: 600
archiveRetention: 1209600
statusUpdateTimeout: "600"
archiveRetention: "1209600"
migrations:
enabled: true
env:
HELM_BIN: "/utils/helm"
KUBECTL_BIN: "/utils/kubectl"
DEFAULTS: |
{
"chart_name": "wordpress",
"chart_repo_name": "bitnami",
"chart_repo_url": "https://charts.bitnami.com/bitnami",
"chart_version": "12.0.0",
"default_overrides": []
"default_overrides": [
"ingress.enabled=true"
]
}
vaultEnv: {}
controlScripts:
image:
repository: typeable/octopod-helm-control-scripts
pullPolicy: IfNotPresent
tag: 0.1.0
tag: 0.2.0
sqitch:
image:
repository: typeable/sqitch

View File

@ -465,7 +465,7 @@ dependencies = [
[[package]]
name = "helm-control-scripts"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"dkregistry",
"env_logger",

View File

@ -1,6 +1,6 @@
[package]
name = "helm-control-scripts"
version = "0.1.0"
version = "0.2.0"
authors = ["Aleksei Sizov <a.sizov@typeable.io>"]
edition = "2018"
@ -54,5 +54,25 @@ name = "init"
path = "src/init.rs"
[[bin]]
name = "tag_check"
path = "src/tag_check.rs"
name = "config_check"
path = "src/config_check.rs"
[[bin]]
name = "app_overrides"
path = "src/app_overrides.rs"
[[bin]]
name = "deployment_overrides"
path = "src/deployment_overrides.rs"
[[bin]]
name = "deployment_keys"
path = "src/deployment_keys.rs"
[[bin]]
name = "app_keys"
path = "src/app_keys.rs"
[[bin]]
name = "unarchive"
path = "src/unarchive.rs"

View File

@ -1,17 +1,13 @@
FROM alpine:3
ARG HELM_BIN=/utils/helm
ARG KUBECTL_BIN=/utils/kubectl
ENV HELM_BIN=$HELM_BIN
ENV KUBECTL_BIN=$KUBECTL_BIN
ADD https://get.helm.sh/helm-v3.6.0-linux-amd64.tar.gz /tmp/helm.tar.gz
ADD https://dl.k8s.io/release/v1.21.0/bin/linux/amd64/kubectl $KUBECTL_BIN
RUN tar -xf /tmp/helm.tar.gz -C /tmp &&\
mkdir -p /utils &&\
cp /tmp/linux-amd64/helm $HELM_BIN &&\
chmod +x $HELM_BIN &&\
chmod +x $KUBECTL_BIN &&\
rm -r /tmp/*
ADD target/x86_64-unknown-linux-musl/release/archive /utils/
@ -22,4 +18,9 @@ ADD target/x86_64-unknown-linux-musl/release/create /utils/
ADD target/x86_64-unknown-linux-musl/release/info /utils/
ADD target/x86_64-unknown-linux-musl/release/update /utils/
ADD target/x86_64-unknown-linux-musl/release/init /utils/
ADD target/x86_64-unknown-linux-musl/release/tag_check /utils/
ADD target/x86_64-unknown-linux-musl/release/config_check /utils/
ADD target/x86_64-unknown-linux-musl/release/app_overrides /utils/
ADD target/x86_64-unknown-linux-musl/release/app_keys /utils/
ADD target/x86_64-unknown-linux-musl/release/deployment_overrides /utils/
ADD target/x86_64-unknown-linux-musl/release/deployment_keys /utils/
ADD target/x86_64-unknown-linux-musl/release/unarchive /utils/

View File

@ -7,13 +7,18 @@ You can get pre-built docker images from [hub.docker.com/r/typeable/octopod-gene
- create a script to install a helm release
- update a script to upgrade a helm release
- archive a script to uninstall a helm release
- archive a script to scale to zero all deployments and statefulsets
- archive_check a script to check if all resources scaled to zero correctly
- unarchive - scale all deployments and statefulsets back (right now it will only scale to 1 and not to previous value)
- info a script to print comma-separated pairs which will be placed as links in the Octopod Web UI
- cleanup a script to delete dangling PVCs and letsencrypt certs left after `helm uninstall` command
- check a script to check if resources created by helm are healthy. Right now only deployments and statefuls sets are checked
- archive_check a script to check if `helm uninstall` really deleted the release
- init a script to initialize something. This is the only script not run by Octopod, but as an init container.
- tag_check a script to check that images in helm release are present in registries before invoking create script
- config_check a script to check that configuration (such as image tag name) passed to Octopod is correct. This script is invoked before deployment creation.
- app_overrides - a scirpt which returns a list of default app overrides wichh were passed in default_overrides parameter described below.
- deployment_overrides - a scirpt which returns a list of default deployment overrides from default paramaters option.
- deployment_keys - a scripts which returns a list of the possible deployment overrides keys user can use.
- app_keys - a scripts which returns a list of the possible app overrides keys. These are parsed from an output of the `helm show values` command.
### Parameters
@ -22,10 +27,11 @@ All scipts accept common parametes which are passed by Octopod when it invokes t
Also, several environment variables are used to parametrize the default behavior:
- HELM_BIN the path to the `helm` executable
- KUBECTL_BIN the path to the `kubectl` executable
- HELM_USER the (optional) user for a private helm registry
- HELM_PASS the (optional) password for a private helm registry
- DEFAUTLS the json with the default parameters (described below)
- HELM_ON_INIT_ONLY - run helm add and update repository as a part of init script execution only. Otherwise `helm repo add` and `helm repo update` will be executed every time before any other helm command.
- INGRESS_HOST_KEY - key name which will be populated with domain name generated for the Octopod deployment. Defaults to `ingress.hostname`.
#### Default parameters
- default_overrides an array with key-value pairs which will be passed as a `--set` flags for helm for each deployment
@ -34,4 +40,4 @@ Also, several environment variables are used to parametrize the default behavior
- chart_version the version of a chart you want to install
- chart_name the name of a chart you want to install
These parameters, if set up in the `DEFAULTS` variable, will be passed to every deployment unless overridden in the "deployment overrides" section of an Octopod deployment configuration.
These parameters, if set up in the `DEFAULTS` variable, will be passed to every deployment unless overridden in the "deployment overrides" section of an Octopod deployment configuration.

View File

@ -0,0 +1,31 @@
use helm_control_scripts::lib::*;
fn main() {
let mut log_builder = Builder::from_default_env();
log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init();
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
helm_init(&envs, &deployment_parameters);
let helm_values = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::ShowValues,
release_name: String::from(""),
namespace: String::from(""),
deployment_parameters: deployment_parameters,
overrides: vec![],
};
info!("Generated Helm args: {:?}", &helm_values.args());
match helm_values.run_stdout() {
Ok(status) => {
print!("{}", print_keys(helm_values_as_keys(status)));
}
Err(status) => {
error!("Error during helm execution");
panic!("{:?}", status);
}
}
}

View File

@ -0,0 +1,11 @@
use helm_control_scripts::lib::*;
fn main() {
let mut log_builder = Builder::from_default_env();
log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init();
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
print!("{}", print_kv(default_values.app_overrides()));
}

View File

@ -6,24 +6,45 @@ fn main() {
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &default_values, &envs);
let helm_cmd = HelmCmd {
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
let namespace = String::from(&cli_opts.namespace);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory name argument was not provided");
panic!();
}
};
let helm_template = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::Uninstall,
release_name: cli_opts.name,
release_domain: String::from(""),
mode: HelmMode::Template,
release_name: release_name,
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: vec![],
default_values: vec![],
image_tag: String::from("")
overrides: overrides,
};
info!("Generated Helm args: {:?}", &helm_cmd.args());
match helm_cmd.run() {
Ok(_status) => info!("Success!"),
info!("Generated Helm args: {:?}", &helm_template.args());
match helm_template.run_stdout() {
Ok(status) => {
let (deployments, statefulsets, _ingresses, _old_ingresses) = match parse_to_k8s(status) {
Ok((deployments, statefulsets, ingresses, old_ingresses)) => (deployments, statefulsets, ingresses, old_ingresses),
Err(err) => panic!("{}", err)
};
match scale(deployments, statefulsets, namespace, 0) {
Ok(_status) => info!("Success!"),
Err(status) => {
error!("Error checking statuses");
panic!("{}", status);
}
}
}
Err(status) => {
error!("Error during helm execution");
panic!("{:?}", status);

View File

@ -4,28 +4,50 @@ fn main() {
let mut log_builder = Builder::from_default_env();
log_builder.target(Target::Stdout).filter(None, LevelFilter::Info).init();
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let cli_opts = CliOpts::from_args();
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &default_values, &envs);
let helm_cmd = HelmCmd {
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
let namespace = String::from(&cli_opts.namespace);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory name argument was not provided");
panic!();
}
};
let helm_template = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::Status,
release_name: cli_opts.name,
release_domain: String::from(""),
mode: HelmMode::Template,
release_name: release_name,
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: vec![],
default_values: vec![],
image_tag: String::from("")
overrides: overrides,
};
info!("Generated Helm args: {:?}", &helm_cmd.args());
match helm_cmd.run() {
Ok(_status) => {
error!("Release is still present in cluster");
panic!();
},
Err(_status) => info!("Success!"),
info!("Generated Helm args: {:?}", &helm_template.args());
match helm_template.run_stdout() {
Ok(status) => {
let (deployments, statefulsets, _ingresses, _old_ingresses) = match parse_to_k8s(status) {
Ok((deployments, statefulsets, ingresses, old_ingresses)) => (deployments, statefulsets, ingresses, old_ingresses),
Err(err) => panic!("{}", err)
};
match check_all(deployments, statefulsets, namespace) {
Ok(status) => {
error!("Deployment hasn't scaled down correctly");
panic!("{:?}", status);
},
Err(_status) => info!("Success!")
}
}
Err(status) => {
error!("Error during helm execution");
panic!("{:?}", status);
}
}
}

View File

@ -6,35 +6,29 @@ fn main() {
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let overrides = match overrides(&cli_opts) {
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let domain_name = domain_name(&cli_opts);
info!("Domain generated for deployment: {}", &domain_name);
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &default_values, &envs);
let image_tag = match cli_opts.tag {
Some(tag) => tag,
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
let namespace = String::from(&cli_opts.namespace);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory tag argument was not provided");
error!("mandatory name argument was not provided");
panic!();
}
};
let namespace = String::from(&cli_opts.namespace);
let helm_template = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::Template,
release_name: cli_opts.name,
release_domain: domain_name,
release_name: release_name,
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: overrides,
default_values: default_values.default_overrides,
image_tag: image_tag
};
info!("Generated Helm args: {:?}", &helm_template.args());
match helm_template.run_stdout() {
@ -47,7 +41,7 @@ fn main() {
Ok(_status) => info!("Success!"),
Err(status) => {
error!("Error checking statuses");
panic!("{}", status);
panic!("{:?}", status);
}
}
}

View File

@ -6,16 +6,77 @@ fn main() {
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &default_values, &envs);
let kubectl_cmd = KubectlCmd {
name: envs.kubectl_bin,
release_name: cli_opts.name,
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
let namespace = String::from(&cli_opts.namespace);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory name argument was not provided");
panic!();
}
};
helm_init(&envs, &deployment_parameters);
let helm_cmd = HelmCmd {
name: envs.helm_bin.clone(),
mode: HelmMode::Uninstall,
release_name: release_name.clone(),
namespace: cli_opts.namespace.clone(),
deployment_parameters: deployment_parameters.clone(),
overrides: vec![],
};
let helm_template = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::Template,
release_name: release_name.clone(),
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: overrides,
};
kubectl_cmd.run();
info!("Success!");
info!("Generated Helm args: {:?}", &helm_cmd.args());
match helm_template.run_stdout() {
Ok(status) => {
let (_deployments, _statefulsets, ingresses, old_ingresses) = match parse_to_k8s(status) {
Ok((deployments, statefulsets, ingresses, old_ingresses)) => (deployments, statefulsets, ingresses, old_ingresses),
Err(err) => panic!("{}", err)
};
match helm_cmd.run() {
Ok(_status) => {
match delete_pvcs(&namespace, &format!("app.kubernetes.io/instance={}", release_name)) {
Ok(_status) => info!("All pvcs were deleted successfully"),
Err(err) => {
error!("Error deleting pvcs");
panic!("{:?}", err);
}
}
match ingresses_to_secrets(ingresses, old_ingresses) {
Some(secrets) => {
for secret in secrets {
match delete_secret(&namespace, &secret) {
Ok(_status) => info!("Successfully deleted secret {}", &secret),
Err(error) => error!("Can't delete secret {}\n {}", &secret, error)
}
}
},
None => info!("No secrets to delete")
}
info!("Success!");
},
Err(status) => {
error!("Error during helm uninstall");
panic!("{:?}", status);
}
}
}
Err(status) => {
error!("Error during helm templating");
panic!("{:?}", status);
}
}
}

View File

@ -2,39 +2,33 @@ use helm_control_scripts::lib::*;
fn main() {
let mut log_builder = Builder::from_default_env();
log_builder.target(Target::Stdout).filter(None, LevelFilter::Info).init();
log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init();
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let overrides = match overrides(&cli_opts) {
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let domain_name = domain_name(&cli_opts);
info!("Domain generated for deployment: {}", &domain_name);
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &default_values, &envs);
let image_tag = match cli_opts.tag {
Some(tag) => tag,
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
helm_init(&envs, &deployment_parameters);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory tag argument was not provided");
error!("mandatory name argument was not provided");
panic!();
}
};
helm_init(&envs, &deployment_parameters);
let helm_template = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::Template,
release_name: cli_opts.name,
release_domain: domain_name,
release_name: release_name,
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: overrides,
default_values: default_values.default_overrides,
image_tag: image_tag
};
match helm_template.run_stdout() {
Ok(status) => {
@ -46,10 +40,16 @@ fn main() {
Some(images) => {
match check_images(images) {
Ok(_) => info!("Success!"),
Err(err) => panic!("{}", err),
Err(err) => {
println!("Error checking images. Are they present in the registry?");
panic!("{}", err);
}
}
},
None => panic!("No images found!"),
None => {
println!("No images found in pods' declarations");
panic!("No images found!");
}
}
}
Err(status) => {

View File

@ -6,34 +6,29 @@ fn main() {
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let overrides = match overrides(&cli_opts) {
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let domain_name = domain_name(&cli_opts);
info!("Domain generated for deployment: {}", &domain_name);
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &default_values, &envs);
let image_tag = match cli_opts.tag {
Some(tag) => tag,
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
helm_init(&envs, &deployment_parameters);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory tag argument was not provided");
error!("mandatory name argument was not provided");
panic!();
}
};
helm_init(&envs, &deployment_parameters);
let helm_cmd = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::UpgradeInstall,
release_name: cli_opts.name,
release_domain: domain_name,
release_name: release_name,
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: overrides,
default_values: default_values.default_overrides,
image_tag: image_tag,
};
info!("Generated Helm args: {:?}", &helm_cmd.args());
match helm_cmd.run() {

View File

@ -0,0 +1,11 @@
use helm_control_scripts::lib::*;
fn main() {
let mut log_builder = Builder::from_default_env();
log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init();
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
print!("{}", print_keys(Some(default_values.deployment_keys())));
}

View File

@ -0,0 +1,11 @@
use helm_control_scripts::lib::*;
fn main() {
let mut log_builder = Builder::from_default_env();
log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init();
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
print!("{}", print_kv(Some(default_values.deployment_overrides())));
}

View File

@ -6,36 +6,30 @@ fn main() {
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap();
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let overrides = match overrides(&cli_opts) {
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let domain_name = domain_name(&cli_opts);
info!("Domain generated for deployment: {}", &domain_name);
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &default_values, &envs);
let image_tag = match cli_opts.tag {
Some(tag) => tag,
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
helm_init(&envs, &deployment_parameters);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory tag argument was not provided");
error!("mandatory name argument was not provided");
panic!();
}
};
let default_name = String::from(&cli_opts.name);
helm_init(&envs, &deployment_parameters);
let default_name = String::from(&release_name);
let helm_template = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::Template,
release_name: cli_opts.name,
release_domain: domain_name,
release_name: release_name,
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: overrides,
default_values: default_values.default_overrides,
image_tag: image_tag
};
match helm_template.run_stdout() {
Ok(status) => {

View File

@ -1,24 +1,28 @@
pub mod lib {
pub use std::io::prelude::*;
pub use std::process::{Command, ExitStatus, Stdio};
pub use std::error::Error;
pub use std::fmt;
pub use structopt::StructOpt;
pub use env_logger::{Builder, Target};
pub use log::{LevelFilter, info, warn, error};
pub use serde::{Serialize, Deserialize};
pub use serde_json::json;
pub use std::error;
pub use yaml_rust::{YamlLoader, YamlEmitter};
pub use yaml_rust::{YamlLoader, YamlEmitter, Yaml};
pub use k8s_openapi::api::{
apps::v1::{Deployment, StatefulSet},
networking::v1::Ingress,
networking::v1beta1::Ingress as OldIngress
networking::v1beta1::Ingress as OldIngress,
core::v1::{PersistentVolumeClaim, Secret}
};
pub use kube::{
api::{Api, ListParams, ResourceExt},
api::{Api, ListParams, ResourceExt, Patch, PatchParams, DeleteParams},
Client,
};
pub use dkregistry::{
v2::Client as RegClient,
errors::Error
errors::Error as RegError,
};
pub use regex::Regex;
@ -32,25 +36,25 @@ pub mod lib {
#[structopt(long)]
pub namespace: String,
#[structopt(long)]
pub name: String,
pub name: Option<String>,
#[structopt(long)]
pub tag: Option<String>,
#[structopt(long)]
pub app_env_override: Vec<String>,
pub application_config: Vec<String>,
#[structopt(long)]
pub deployment_override: Vec<String>,
pub deployment_config: Vec<String>,
}
#[derive(Deserialize, Debug)]
pub struct EnvVars {
pub helm_bin: String,
pub kubectl_bin: String,
pub defaults: String, //json of DefaultValues
#[serde(default)]
pub helm_user: String,
#[serde(default)]
pub helm_pass: String,
pub helm_on_init_only: Option<bool>,
pub ingress_host_key: Option<String>,
}
impl EnvVars {
@ -66,6 +70,12 @@ pub mod lib {
}
}
#[derive(Debug, Serialize, Deserialize)]
struct HelmRepo {
name: String,
url: String
}
#[derive(Deserialize, Debug)]
pub struct DefaultValues {
pub default_overrides: Vec<String>,
@ -75,7 +85,42 @@ pub mod lib {
pub chart_name: String,
}
#[derive(Debug, Clone)]
impl DefaultValues {
pub fn app_overrides(&self) -> Option<Vec<(String,String)>> {
if self.default_overrides.is_empty() {
None
} else {
let mut overrides: Vec<(String,String)> = Vec::new();
for app_override in &self.default_overrides {
let split_override = app_override.split('=').collect::<Vec<_>>();
overrides.push((split_override.first().unwrap().to_string(), split_override.last().unwrap().to_string()));
}
Some(overrides)
}
}
pub fn deployment_overrides(&self) -> Vec<(String,String)> {
vec![
(String::from("chart_repo_url"), self.chart_repo_url.clone()),
(String::from("chart_repo_name"), self.chart_repo_name.clone()),
(String::from("chart_version"), self.chart_version.clone()),
(String::from("chart_name"), self.chart_name.clone()),
]
}
pub fn deployment_keys(&self) -> Vec<String> {
let mut keys: Vec<String> = vec![
String::from("chart_repo_url"),
String::from("chart_repo_name"),
String::from("chart_version"),
String::from("chart_name"),
String::from("chart_repo_user"),
String::from("chart_repo_pass"),
];
keys.sort();
keys
}
}
#[derive(Debug, Clone, Default)]
pub struct HelmDeploymentParameters {
pub chart_repo_url: String,
pub chart_repo_name: String,
@ -86,30 +131,23 @@ pub mod lib {
}
impl HelmDeploymentParameters {
pub fn new(cli_opts: &CliOpts, default_values: &DefaultValues, envs: &EnvVars) -> Self {
let mut default_parameters = Self {
chart_repo_url: default_values.chart_repo_url.clone(),
chart_repo_name: default_values.chart_repo_name.clone(),
chart_repo_user: envs.helm_user.clone(),
chart_repo_pass: envs.helm_pass.clone(),
chart_version: default_values.chart_version.clone(),
chart_name: default_values.chart_name.clone(),
};
if cli_opts.deployment_override.is_empty() {
return default_parameters;
} else {
for deployment_override in cli_opts.deployment_override.clone().into_iter() {
let split_override = deployment_override.split('=').collect::<Vec<_>>();
match split_override.first().unwrap().as_ref() {
"chart_repo_url" => default_parameters.chart_repo_url = split_override.last().unwrap().to_string(),
"chart_repo_name" => default_parameters.chart_repo_name = split_override.last().unwrap().to_string(),
"chart_version" => default_parameters.chart_version = split_override.last().unwrap().to_string(),
"chart_name" => default_parameters.chart_name = split_override.last().unwrap().to_string(),
_ => continue,
}
pub fn new(cli_opts: &CliOpts, envs: &EnvVars) -> Self {
let mut deployment_parameters = Self::default();
deployment_parameters.chart_repo_user = envs.helm_user.clone();
deployment_parameters.chart_repo_pass = envs.helm_pass.clone();
for deployment_override in cli_opts.deployment_config.clone().into_iter() {
let split_override = deployment_override.split('=').collect::<Vec<_>>();
match split_override.first().unwrap().as_ref() {
"chart_repo_url" => deployment_parameters.chart_repo_url = split_override.last().unwrap().to_string(),
"chart_repo_name" => deployment_parameters.chart_repo_name = split_override.last().unwrap().to_string(),
"chart_version" => deployment_parameters.chart_version = split_override.last().unwrap().to_string(),
"chart_name" => deployment_parameters.chart_name = split_override.last().unwrap().to_string(),
"chart_repo_user" => deployment_parameters.chart_repo_user = split_override.last().unwrap().to_string(),
"chart_repo_pass" => deployment_parameters.chart_repo_pass = split_override.last().unwrap().to_string(),
_ => continue,
}
return default_parameters;
}
return deployment_parameters;
}
pub fn new_env_only(default_values: &DefaultValues, envs: &EnvVars) -> Self {
Self {
@ -126,12 +164,9 @@ pub mod lib {
pub name: String,
pub mode: HelmMode,
pub release_name: String,
pub release_domain: String,
pub namespace: String,
pub deployment_parameters: HelmDeploymentParameters,
pub overrides: Vec<String>,
pub default_values: Vec<String>,
pub image_tag: String,
}
impl HelmCmd {
@ -167,6 +202,13 @@ pub mod lib {
args.extend(chart_version);
args.extend(self.set_flag_values());
},
HelmMode::ShowValues => {
args.extend(namespace);
args.push(String::from("show"));
args.push(String::from("values"));
args.push(chart_location);
args.extend(chart_version);
},
HelmMode::RepoAdd => {
args.push(String::from("repo"));
args.push(String::from("add"));
@ -180,12 +222,12 @@ pub mod lib {
args.push(String::from("repo"));
args.push(String::from("update"));
},
HelmMode::Status => {
args.extend(namespace);
args.push(String::from("status"));
args.push(String::from(&self.release_name));
HelmMode::RepoList => {
args.push(String::from("repo"));
args.push(String::from("list"));
args.push(String::from("-o"));
args.push(String::from("json"));
},
_ => panic!("This helm mode is not expected"),
}
return args;
@ -193,9 +235,6 @@ pub mod lib {
fn set_flag_values(&self) -> Vec<String> {
let mut values = Vec::new();
let mut set_values = Vec::new();
values.push(format!("image.tag={}", &self.image_tag));
values.push(format!("ingress.host={}", &self.release_domain));
values.extend(self.default_values.clone());
values.extend(self.overrides.clone());
for value in values.into_iter() {
set_values.push(String::from("--set"));
@ -236,58 +275,8 @@ pub mod lib {
Template,
RepoAdd,
RepoUpdate,
Status,
}
pub struct KubectlCmd {
pub name: String,
pub release_name: String,
pub namespace: String,
pub deployment_parameters: HelmDeploymentParameters,
}
impl KubectlCmd {
pub fn args(&self, cmd_type: &str) -> Vec<String> {
let mut args: Vec<String> = Vec::new();
let namespace = vec![String::from("-n"), String::from(&self.namespace),];
let cert_secret_name = format!("{}-{}-tls", &self.release_name, &self.deployment_parameters.chart_name);
let label = vec![String::from("-l"), format!("app.kubernetes.io/instance={}", &self.release_name)];
args.extend(namespace);
args.push(String::from("delete"));
match cmd_type {
"pvc" => {
args.push(String::from("pvc"));
args.extend(label);
return args;
},
"cert" => {
args.push(String::from("secret"));
args.push(cert_secret_name);
return args;
},
_ => unreachable!(),
}
}
pub fn run(&self) {
info!("Starting pvc cleanup");
info!("kubectl args: {:?}", &self.args("pvc"));
let kubectl_pvc = Command::new(&self.name)
.args(&self.args("pvc"))
.output()
.expect("Failed to run");
info!("kubectl stdout:\n {}", String::from_utf8(kubectl_pvc.stdout).unwrap());
info!("kubectl stderr:\n {}", String::from_utf8(kubectl_pvc.stderr).unwrap());
info!("Starting certificates cleanup");
info!("kubectl args: {:?}", &self.args("pvc"));
let kubectl_cert = Command::new(&self.name)
.args(&self.args("cert"))
.output()
.expect("Failed to run");
info!("kubectl stdout:\n {}", String::from_utf8(kubectl_cert.stdout).unwrap());
info!("kubectl stderr:\n {}", String::from_utf8(kubectl_cert.stderr).unwrap());
assert!(kubectl_pvc.status.success());
assert!(kubectl_cert.status.success());
}
RepoList,
ShowValues,
}
fn check_value(value: String) -> Result<String, String> {
@ -298,9 +287,14 @@ pub mod lib {
Err(format!("Override value {} is malformed", value))
}
}
pub fn overrides(cli_opts: &CliOpts) -> Option<Vec<String>> {
pub fn overrides(cli_opts: &CliOpts, envs: &EnvVars) -> Option<Vec<String>> {
let mut overrides_opts = Vec::new();
overrides_opts.extend(&cli_opts.app_env_override);
overrides_opts.extend(&cli_opts.application_config);
let ingress_override = match &envs.ingress_host_key {
Some(key) => format!("{}={}", &key, domain_name(&cli_opts)),
None => format!("ingress.hostname={}", domain_name(&cli_opts)),
};
overrides_opts.push(&ingress_override);
if overrides_opts.is_empty() {
None
} else {
@ -311,8 +305,13 @@ pub mod lib {
)
}
}
pub fn domain_name(cli_opts: &CliOpts) -> String {
format!("{}.{}", &cli_opts.name, &cli_opts.base_domain)
fn domain_name(cli_opts: &CliOpts) -> String {
match &cli_opts.name {
Some(name) => {
return format!("{}.{}", name, &cli_opts.base_domain);
},
None => panic!("No name argument provided")
}
}
pub fn parse_to_k8s(yaml: String) -> Result<(Vec<Deployment>, Vec<StatefulSet>, Vec<Ingress>, Vec<OldIngress>), serde_yaml::Error> {
let docs = YamlLoader::load_from_str(&yaml).unwrap();
@ -356,8 +355,37 @@ pub mod lib {
}
return Ok((deployments, statefulsets, ingresses, old_ingresses));
}
#[derive(Debug)]
pub struct NoReplicasError(String);
impl fmt::Display for NoReplicasError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Deployment {} doesn't have any replicas", self.0)
}
}
impl Error for NoReplicasError {}
#[derive(Debug)]
pub enum KubeError {
NoReplicasError(NoReplicasError),
KubeApiError(kube::Error),
}
impl From<NoReplicasError> for KubeError {
fn from(error: NoReplicasError) -> Self {
KubeError::NoReplicasError(error)
}
}
impl From<kube::Error> for KubeError {
fn from(error: kube::Error) -> Self {
KubeError::KubeApiError(error)
}
}
#[tokio::main]
async fn check_deployment(namespace: &str, name: &str) -> Result<(), kube::Error> {
async fn check_deployment(namespace: &str, name: &str) -> Result<(), KubeError> {
let client = Client::try_default().await?;
let api: Api<Deployment> = Api::namespaced(client, &namespace);
let deployment = api.get(&name).await?;
@ -366,22 +394,22 @@ pub mod lib {
match status.available_replicas {
Some(replicas) => {
if !replicas >= 1 {
panic!("Deployment {} doesn't have any replicas", &name);
return Err(KubeError::NoReplicasError(NoReplicasError(name.to_string())));
}
},
None => {
panic!("Deployment {} doesn't have any replicas", &name);
return Err(KubeError::NoReplicasError(NoReplicasError(name.to_string())));
}
}
},
None => {
panic!("Unable to get status for deployment {}", &name);
return Err(KubeError::NoReplicasError(NoReplicasError(name.to_string())));
}
}
Ok(())
}
#[tokio::main]
async fn check_statefulset(namespace: &str, name: &str) -> Result<(), kube::Error> {
async fn check_statefulset(namespace: &str, name: &str) -> Result<(), KubeError> {
let client = Client::try_default().await?;
let api: Api<StatefulSet> = Api::namespaced(client, &namespace);
let statefulset = api.get(&name).await?;
@ -390,21 +418,21 @@ pub mod lib {
match status.current_replicas {
Some(replicas) => {
if !replicas >= 1 {
panic!("StatefulSet {} doesn't have any replicas", &name);
return Err(KubeError::NoReplicasError(NoReplicasError(name.to_string())));
}
},
None => {
panic!("StatefulSet {} doesn't have any replicas", &name);
return Err(KubeError::NoReplicasError(NoReplicasError(name.to_string())));
}
}
},
None => {
panic!("Unable to get status for StatefulSet {}", &name);
return Err(KubeError::NoReplicasError(NoReplicasError(name.to_string())));
}
}
Ok(())
}
pub fn check_all(deployments: Vec<Deployment>, statefulsets: Vec<StatefulSet>, namespace: String) -> Result<(), Box<dyn error::Error>> {
pub fn check_all(deployments: Vec<Deployment>, statefulsets: Vec<StatefulSet>, namespace: String) -> Result<(), KubeError> {
if deployments.is_empty() {
info!("No deployments to check");
}else{
@ -412,7 +440,7 @@ pub mod lib {
match check_deployment(&namespace, &deploy.name()) {
Ok(status) => status,
Err(err) => {
return Err(Box::new(err));
return Err(err);
}
}
}
@ -425,7 +453,7 @@ pub mod lib {
match check_statefulset(&namespace, &statefulset.name()) {
Ok(status) => status,
Err(err) => {
return Err(Box::new(err));
return Err(err);
}
}
}
@ -506,6 +534,18 @@ pub mod lib {
}
return out_string;
}
pub fn print_keys(vals: Option<Vec<String>>) -> String {
let mut out_string = String::from("");
match vals {
Some(values) => {
for value in values {
out_string.push_str(&format!("{}\n", value));
}
},
None => return out_string
}
return out_string;
}
//TODO: Generics!!!
pub fn deployments_statefulsets_to_images(deployments: Vec<Deployment>, statefulsets: Vec<StatefulSet>) -> Option<Vec<String>> {
let mut images: Vec<String> = Vec::new();
@ -564,7 +604,7 @@ pub mod lib {
}
}
#[tokio::main]
async fn check_image(registry: &str, repository: &str, tag: &str) -> Result<(), dkregistry::errors::Error> {
async fn check_image(registry: &str, repository: &str, tag: &str) -> Result<(), RegError> {
let client = RegClient::configure()
.insecure_registry(false)
.registry(registry)
@ -598,7 +638,7 @@ pub mod lib {
info!("Checking image: {} {} {}", registry, repository, tag);
(registry, repository, tag)
}
pub fn check_images(images: Vec<String>) -> Result<(), dkregistry::errors::Error> {
pub fn check_images(images: Vec<String>) -> Result<(), RegError> {
for image in images {
let (registry, repository, tag) = parse_image_name(&image);
match check_image(&registry, &repository, &tag) {
@ -613,23 +653,17 @@ pub mod lib {
name: String::from(&envs.helm_bin),
mode: HelmMode::RepoAdd,
release_name: String::from(""),
release_domain: String::from(""),
namespace: String::from(""),
deployment_parameters: deployment_parameters.clone(),
overrides: vec![],
default_values: vec![],
image_tag: String::from(""),
};
let helm_repo_update = HelmCmd {
name: String::from(&envs.helm_bin),
mode: HelmMode::RepoUpdate,
release_name: String::from(""),
release_domain: String::from(""),
namespace: String::from(""),
deployment_parameters: deployment_parameters.clone(),
overrides: vec![],
default_values: vec![],
image_tag: String::from(""),
};
match helm_repo_add.run() {
Ok(_status) => info!("Repo add success!"),
@ -652,14 +686,215 @@ pub mod lib {
if *enabled {
info!("Skipping helm initialization, it must be initialied on init");
} else {
info!("Starting helm initialization");
helm_repo_add_update(&envs, &deployment_parameters);
if helm_repo_exists(&envs, &deployment_parameters) {
info!("Skipping helm initialization, because requested repo was already added");
} else {
info!("Starting helm initialization");
helm_repo_add_update(&envs, &deployment_parameters);
}
}
},
None => {
info!("Starting helm initialization");
helm_repo_add_update(&envs, &deployment_parameters);
if helm_repo_exists(&envs, &deployment_parameters) {
info!("Skipping helm initialization, because requested repo was already added");
} else {
info!("Starting helm initialization");
helm_repo_add_update(&envs, &deployment_parameters);
}
}
}
}
fn helm_repo_exists(envs: &EnvVars, deployment_parameters: &HelmDeploymentParameters) -> bool {
let chart_repo_url = String::from(&deployment_parameters.chart_repo_url);
let chart_repo_name = String::from(&deployment_parameters.chart_repo_name);
let helm_repo_list = HelmCmd {
name: String::from(&envs.helm_bin),
mode: HelmMode::RepoList,
release_name: String::from(""),
namespace: String::from(""),
deployment_parameters: deployment_parameters.clone(),
overrides: vec![],
};
match helm_repo_list.run_stdout() {
Ok(list) => {
let helm_repos: Vec<HelmRepo> = serde_json::from_str(&list).unwrap();
for repo in helm_repos {
if repo.name == chart_repo_name && repo.url == chart_repo_url {
return true;
} else {
continue
}
}
},
Err(_err) => {
return false;
}
}
false
}
pub fn helm_values_as_keys(yaml: String) -> Option<Vec<String>> {
let mut keys: Vec<String> = Vec::new();
let docs = YamlLoader::load_from_str(&yaml).unwrap();
let docs_hash = &docs[0].clone().into_hash().unwrap();
for doc in docs_hash {
match doc.1.as_hash() {
Some(hash) => {
if hash.is_empty() {
keys.push(format!("{}", doc.0.as_str().unwrap()));
}else{
for doc_2 in hash {
match doc_2.1.as_hash() {
Some(hash_2) => {
if hash_2.is_empty() {
keys.push(format!("{}.{}", doc.0.as_str().unwrap(), doc_2.0.as_str().unwrap()));
}else{
for key in hash_2.keys() {
keys.push(format!("{}.{}.{}", doc.0.as_str().unwrap(), doc_2.0.as_str().unwrap(), key.as_str().unwrap()));
}
}
},
None => keys.push(format!("{}.{}", doc.0.as_str().unwrap(), doc_2.0.as_str().unwrap()))
}
}
}
},
None => keys.push(format!("{}", doc.0.as_str().unwrap())),
}
}
if keys.is_empty() {
None
}else{
keys.sort();
Some(keys)
}
}
#[tokio::main]
async fn scale_deployment(namespace: &str, name: &str, replicas: i32) -> Result<(), kube::Error> {
let client = Client::try_default().await?;
let api: Api<Deployment> = Api::namespaced(client, &namespace);
let deployment = api.get(&name).await?;
let mut spec = match deployment.spec {
Some(spec) => spec,
None => panic!("Deployment without any spec!")
};
spec.replicas = Some(replicas);
let patched_deployment = Deployment {
metadata: deployment.metadata,
spec: Some(spec),
status: None
};
let patch = Patch::Merge(patched_deployment);
let patch_params = PatchParams::apply("octopod");
api.patch(&name, &patch_params, &patch).await?;
Ok(())
}
#[tokio::main]
async fn scale_statefulset(namespace: &str, name: &str, replicas: i32) -> Result<(), kube::Error> {
let client = Client::try_default().await?;
let api: Api<StatefulSet> = Api::namespaced(client, &namespace);
let statefulset = api.get(&name).await?;
let mut spec = match statefulset.spec {
Some(spec) => spec,
None => panic!("StatefulSet without any spec!")
};
spec.replicas = Some(replicas);
let patched_statefulset = StatefulSet {
metadata: statefulset.metadata,
spec: Some(spec),
status: None
};
let patch = Patch::Merge(patched_statefulset);
let patch_params = PatchParams::apply("octopod");
api.patch(&name, &patch_params, &patch).await?;
Ok(())
}
pub fn scale(deployments: Vec<Deployment>, statefulsets: Vec<StatefulSet>, namespace: String, replicas: i32) -> Result<(), Box<dyn error::Error>> {
if deployments.is_empty() {
info!("No deployments to check");
}else{
for deploy in deployments {
match scale_deployment(&namespace, &deploy.name(), replicas) {
Ok(status) => status,
Err(err) => {
return Err(Box::new(err));
}
}
}
}
if statefulsets.is_empty() {
info!("No statefulsets to check");
}else{
for statefulset in statefulsets {
match scale_statefulset(&namespace, &statefulset.name(), replicas) {
Ok(status) => status,
Err(err) => {
return Err(Box::new(err));
}
}
}
}
Ok(())
}
pub fn ingresses_to_secrets(new_ingresses: Vec<Ingress>, old_ingresses: Vec<OldIngress>) -> Option<Vec<String>> {
let mut secrets: Vec<String> = Vec::new();
if new_ingresses.is_empty() {
for ingress in old_ingresses {
match &ingress.spec {
Some(spec) => {
if spec.tls.is_empty() {
continue
}else{
for tls in &spec.tls {
match &tls.secret_name {
Some(secret) => secrets.push(String::from(secret)),
None => continue
}
}
}
},
None => continue
}
}
}else{
for ingress in new_ingresses {
match &ingress.spec {
Some(spec) => {
if spec.tls.is_empty() {
continue
}else{
for tls in &spec.tls {
match &tls.secret_name {
Some(secret) => secrets.push(String::from(secret)),
None => continue
}
}
}
},
None => continue
}
}
}
if secrets.is_empty() {
None
}else{
Some(secrets)
}
}
#[tokio::main]
pub async fn delete_pvcs(namespace: &str, selector: &str) -> Result<(), kube::Error> {
let client = Client::try_default().await?;
let api: Api<PersistentVolumeClaim> = Api::namespaced(client, &namespace);
let pvc_list = ListParams::default().labels(selector);
api.delete_collection(&DeleteParams::default(), &pvc_list).await?;
Ok(())
}
#[tokio::main]
pub async fn delete_secret(namespace: &str, secret: &str) -> Result<(), kube::Error> {
let client = Client::try_default().await?;
let api: Api<Secret> = Api::namespaced(client, &namespace);
api.delete(&secret, &DeleteParams::default()).await?;
Ok(())
}
}

View File

@ -0,0 +1,53 @@
use helm_control_scripts::lib::*;
fn main() {
let mut log_builder = Builder::from_default_env();
log_builder.target(Target::Stdout).filter(None, LevelFilter::Info).init();
info!("Utils version {}", env!("CARGO_PKG_VERSION"));
let envs = EnvVars::parse();
info!("Env variables received {:?}", &envs);
let cli_opts = CliOpts::from_args();
info!("Cli options received {:?}", &cli_opts);
let overrides = match overrides(&cli_opts, &envs) {
Some(inner) => inner,
None => vec![],
};
let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs);
let namespace = String::from(&cli_opts.namespace);
let release_name = match cli_opts.name {
Some(name) => name,
None => {
error!("mandatory name argument was not provided");
panic!();
}
};
let helm_template = HelmCmd {
name: envs.helm_bin,
mode: HelmMode::Template,
release_name: release_name,
namespace: cli_opts.namespace,
deployment_parameters: deployment_parameters,
overrides: overrides,
};
info!("Generated Helm args: {:?}", &helm_template.args());
match helm_template.run_stdout() {
Ok(status) => {
let (deployments, statefulsets, _ingresses, _old_ingresses) = match parse_to_k8s(status) {
Ok((deployments, statefulsets, ingresses, old_ingresses)) => (deployments, statefulsets, ingresses, old_ingresses),
Err(err) => panic!("{}", err)
};
match scale(deployments, statefulsets, namespace, 1) {
Ok(_status) => info!("Success!"),
Err(status) => {
error!("Error checking statuses");
panic!("{}", status);
}
}
}
Err(status) => {
error!("Error during helm execution");
panic!("{:?}", status);
}
}
}