mirror of
https://github.com/typeable/octopod.git
synced 2024-08-17 02:30:35 +03:00
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:
parent
a2c8ab0915
commit
c8ac911f6b
@ -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
|
||||
|
@ -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: |
|
||||
{
|
||||
|
@ -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 }}
|
||||
|
@ -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
|
||||
|
2
helm-control-scripts/Cargo.lock
generated
2
helm-control-scripts/Cargo.lock
generated
@ -465,7 +465,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "helm-control-scripts"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"dkregistry",
|
||||
"env_logger",
|
||||
|
@ -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"
|
||||
|
@ -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/
|
||||
|
@ -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.
|
||||
|
31
helm-control-scripts/src/app_keys.rs
Normal file
31
helm-control-scripts/src/app_keys.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
11
helm-control-scripts/src/app_overrides.rs
Normal file
11
helm-control-scripts/src/app_overrides.rs
Normal 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()));
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) => {
|
@ -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() {
|
||||
|
11
helm-control-scripts/src/deployment_keys.rs
Normal file
11
helm-control-scripts/src/deployment_keys.rs
Normal 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())));
|
||||
}
|
11
helm-control-scripts/src/deployment_overrides.rs
Normal file
11
helm-control-scripts/src/deployment_overrides.rs
Normal 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())));
|
||||
}
|
@ -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) => {
|
||||
|
@ -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(®istry, &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(())
|
||||
}
|
||||
}
|
||||
|
53
helm-control-scripts/src/unarchive.rs
Normal file
53
helm-control-scripts/src/unarchive.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user