Skip to content

Instantly share code, notes, and snippets.

@BransonGitomeh
Last active February 22, 2020 09:52
Show Gist options
  • Save BransonGitomeh/c5a2bc45da92a9def6633c5a6a40e89d to your computer and use it in GitHub Desktop.
Save BransonGitomeh/c5a2bc45da92a9def6633c5a6a40e89d to your computer and use it in GitHub Desktop.
CLIENT=respite
ENV=production

What is this

CLI tool i built to merge kubernetes config files into a top level manifest

Usage

$ tool ./glob/path/to/.yaml
Base path is ./
glob to match is ./**/*.yaml
Manifest writing complete

checkout final output named manifest.yaml

Variables

Create a .env on the base level of the repo. Consume the env variables using {{path}}.

[package]
name = "hello-world"
version = "0.1.0"
[dependencies]
glob = "0.3.0"
clap = "2.26.0"
dotenv = "0.10.0"
handlebars = "3.0.1"
serde_json = "1.0.48"
use std::env;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
extern crate glob;
use self::glob::glob;
use std::path::Path;
extern crate clap;
use clap::{App, Arg};
extern crate dotenv;
use dotenv::dotenv;
use std::collections::HashMap;
extern crate handlebars;
#[macro_use]
extern crate serde_json;
use handlebars::Handlebars;
fn string_to_static_str(s: &String) -> &'static str {
Box::leak(s.to_owned().into_boxed_str())
}
fn main() -> std::io::Result<()> {
dotenv().ok();
let env: Vec<(String, String)> = dotenv::vars().collect();
let reg = Handlebars::new();
// init key-value store
let mut store = HashMap::new();
// flatten the structure to make it accessible for handlebars
for c in &env {
// do something with `c`
let (key, value) = c;
// assign to key-value store
store.insert(key.to_string(), value.to_string());
}
let _is_in_production = env::var("PRODUCTION").is_ok();
// get the directory from the args
let matches = App::new("Rget")
.version("0.1.0")
.author("BransonGitomeh <[email protected]>")
.about("Combine kube files and env variables to create recreatable clusters")
.arg(
Arg::with_name("PATH")
.required(true)
.takes_value(true)
.index(1)
.help("Path to work with..."),
)
.arg(
Arg::with_name("FILE")
.required(true)
.takes_value(true)
.index(2)
.help("Final manifest to write to..."),
)
.get_matches();
let path = matches.value_of("PATH").unwrap();
let manifest_path = matches.value_of("FILE").unwrap();
println!("Base path is {}", path);
println!("Manifest path is {}", manifest_path);
// remove the existing manifest
let f = fs::remove_file(Path::new(manifest_path));
let _f = match f {
Ok(_file) => {
println!("Removed existing manifest {}", manifest_path)
},
Err(error) => {
let error = format!(
"No {manifest_path} found to delete, creating new one: {error}",
manifest_path = &manifest_path,
error = &error
);
println!("{}", error)
}
};
let mut manifest = String::new();
fn process_file(path: &Path) -> String {
let mut contents = String::new();
let mut file = File::open(&path).unwrap();
file.read_to_string(&mut contents).unwrap();
contents
}
let divider = "\n---\n";
let base_path = format!("{path}**/*.yaml", path = &path);
println!("Glob to match is {}", base_path);
// read all files in path
for entry in glob(&base_path).expect("Failed to read glob pattern") {
match entry {
Ok(path) => {
manifest.push_str(divider);
manifest.push_str(string_to_static_str(&process_file(&path)));
}
Err(e) => println!("{:?}", e),
}
}
println!("Manifest writing complete");
let mut file = File::create("./manifest.yaml").unwrap();
let processed_manifest = reg
.render_template(string_to_static_str(&manifest), &json!(store))
.unwrap();
file.write_all(processed_manifest.as_bytes())?;
Ok(())
}
#[test]
fn should_fail() {
unimplemented!();
}
---
apiVersion: v1
kind: Service
metadata:
name: my-service-production-respite-1
spec:
selector:
app: my-service-production-respite-1
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: my-service-production-respite-1
spec:
selector:
app: my-service-production-respite-1
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: my-service-production-respite-1
spec:
selector:
app: my-service-production-respite-1
ports:
- protocol: TCP
port: 80
targetPort: 9376
apiVersion: v1
kind: Service
metadata:
name: my-service-{{ENV}}-{{CLIENT}}-2
spec:
selector:
app: my-service-{{ENV}}-{{CLIENT}}-2
ports:
- protocol: TCP
port: 80
targetPort: 9376
apiVersion: v1
kind: Service
metadata:
name: my-service-{{ENV}}-{{CLIENT}}-1
spec:
selector:
app: my-service-{{ENV}}-{{CLIENT}}-1
ports:
- protocol: TCP
port: 80
targetPort: 9376
apiVersion: v1
kind: Service
metadata:
name: my-service-{{ENV}}-{{CLIENT}}-3
spec:
selector:
app: my-service-{{ENV}}-{{CLIENT}}-3
ports:
- protocol: TCP
port: 80
targetPort: 9376
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment