diff --git a/src/config.rs b/src/config.rs index e91ce56..06bedab 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,12 +14,14 @@ //! Handle reading .kube/config files +use certs::{get_cert, get_cert_from_pem, get_key_from_str, get_private_key}; use chrono::{DateTime, Local, TimeZone}; use chrono::offset::Utc; use duct::cmd; +use error::{KubeErrNo, KubeError}; +use kube::{Kluster, KlusterAuth}; use serde_json::{self, Value}; use serde_yaml; - use std::cell::RefCell; use std::collections::HashMap; use std::convert::From; @@ -27,10 +29,7 @@ use std::env; use std::error::Error; use std::fs::File; use std::io::{self, BufReader, Read}; - -use error::{KubeErrNo, KubeError}; -use kube::{Kluster, KlusterAuth}; -use certs::{get_cert, get_cert_from_pem, get_key_from_str, get_private_key}; +use std::process::Command; /// Kubernetes cluster config #[derive(Debug, Deserialize)] @@ -108,6 +107,56 @@ pub struct AuthProvider { pub config: AuthProviderConfig, } +#[derive(Debug, Deserialize, Clone)] +pub struct Exec { + apiVersion: String, + pub args: Option>, + pub command: Option, + pub env: Option>, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct Env { + name: String, + value: String, +} + +#[derive(Serialize, Deserialize)] +struct Spec {} + +#[derive(Serialize, Deserialize)] +struct Status { + token: String, +} + +#[derive(Serialize, Deserialize)] +struct AWSCredential { + kind: String, + apiVersion: String, + spec: Spec, + status: Status, +} + +impl Exec { + pub fn generate_token(&self) -> String { + let mut filtered_env : HashMap = HashMap::new(); + for e in self.env.clone().unwrap(){ + filtered_env.insert(e.name, e.value); + } + + let output = Command::new(self.command.clone().unwrap()) + .args(self.args.clone().unwrap()) + .envs(filtered_env) + .output() + .expect("failed to execute process"); + + let out: String = String::from_utf8_lossy(&output.stdout).to_string(); + let v: AWSCredential = serde_json::from_str(&out).unwrap(); + let token = v.status.token; + return token; + } +} + impl AuthProvider { // Copy the token and expiry out of the config into the refcells fn copy_up(&self) { @@ -254,6 +303,7 @@ pub struct IUserConf { pub username: Option, pub password: Option, + pub exec: Option, #[serde(rename = "auth-provider")] pub auth_provider: Option, } @@ -268,6 +318,7 @@ pub enum UserConf { KeyCertPath(String, String), KeyCertData(String, String), UserPass(String, String), + Exec(Exec), AuthProvider(AuthProvider), Unsupported, } @@ -278,15 +329,17 @@ impl From for UserConf { UserConf::Token(token) } else if let (Some(username), Some(password)) = (iconf.username, iconf.password) { UserConf::UserPass(username, password) + } else if let Some(exec) = iconf.exec { + UserConf::Exec(exec) } else if let (Some(client_cert_path), Some(key_path)) = - (iconf.client_cert, iconf.client_key) - { - UserConf::KeyCertPath(client_cert_path, key_path) - } else if let (Some(client_cert_data), Some(key_data)) = - (iconf.client_cert_data, iconf.client_key_data) - { - UserConf::KeyCertData(client_cert_data, key_data) - } else if let Some(auth_provider) = iconf.auth_provider { + (iconf.client_cert, iconf.client_key) + { + UserConf::KeyCertPath(client_cert_path, key_path) + } else if let (Some(client_cert_data), Some(key_data)) = + (iconf.client_cert_data, iconf.client_key_data) + { + UserConf::KeyCertData(client_cert_data, key_data) + } else if let Some(auth_provider) = iconf.auth_provider { UserConf::AuthProvider(auth_provider) } else { UserConf::Unsupported @@ -523,6 +576,9 @@ impl Config { &UserConf::UserPass(ref username, ref password) => { Ok(KlusterAuth::with_userpass(username, password)) } + &UserConf::Exec(ref exec) => { + Ok(KlusterAuth::with_exec(exec.clone())) + } &UserConf::KeyCertPath(ref cert_path, ref key_path) => { auth_from_paths( cert_path.clone(), diff --git a/src/kube.rs b/src/kube.rs index 1af609e..b7aad14 100644 --- a/src/kube.rs +++ b/src/kube.rs @@ -17,6 +17,10 @@ use ansi_term::Colour::{Green, Red, Yellow}; use chrono::DateTime; use chrono::offset::Utc; +use config::AuthProvider; +use config::Exec; +use connector::ClickSslConnector; +use error::{KubeErrNo, KubeError}; use hyper::{Client, Url}; use hyper::client::{Body, RequestBuilder}; use hyper::client::request::Request; @@ -25,11 +29,10 @@ use hyper::header::{Authorization, Basic, Bearer}; use hyper::method::Method; use hyper::status::StatusCode; use hyper_rustls::TlsClient; +use rustls::{self, Certificate, PrivateKey}; use serde::Deserialize; use serde_json; use serde_json::{Map, Value}; -use rustls::{self, Certificate, PrivateKey}; - use std::fmt; use std::io::BufReader; use std::net::IpAddr; @@ -37,10 +40,6 @@ use std::str::FromStr; use std::sync::Arc; use std::time::Duration; -use config::AuthProvider; -use connector::ClickSslConnector; -use error::{KubeErrNo, KubeError}; - // Various things we can return from the kubernetes api // objects @@ -339,6 +338,7 @@ pub struct JobList { pub enum KlusterAuth { Token(String), UserPass(String, String), + Exec(Exec), CertKey(Vec, PrivateKey), AuthProvider(AuthProvider), } @@ -352,6 +352,10 @@ impl KlusterAuth { KlusterAuth::UserPass(user.to_owned(), pass.to_owned()) } + pub fn with_exec(exec: Exec) -> KlusterAuth { + KlusterAuth::Exec(exec.to_owned()) + } + pub fn with_cert_and_key(cert: Certificate, private_key: PrivateKey) -> KlusterAuth { KlusterAuth::CertKey(vec![cert], private_key) } @@ -371,6 +375,7 @@ pub struct Kluster { // NoCertificateVerification struct/impl taken from the rustls example code struct NoCertificateVerification {} + impl rustls::ServerCertVerifier for NoCertificateVerification { fn verify_server_cert( &self, @@ -466,6 +471,10 @@ impl Kluster { username: user.clone(), password: Some(pass.clone()), })), + KlusterAuth::Exec(ref exec) => req.header(Authorization(Bearer { + //TODO + token: exec.generate_token(), + })), KlusterAuth::CertKey(..) => req, } } @@ -521,8 +530,8 @@ impl Kluster { /// Get a resource and deserialize it as a T pub fn get(&self, path: &str) -> Result - where - for<'de> T: Deserialize<'de>, + where + for<'de> T: Deserialize<'de>, { let resp = try!(self.send_req(path)); let resp = try!(self.check_resp(resp)); @@ -534,7 +543,7 @@ impl Kluster { pub fn get_read(&self, path: &str, timeout: Option) -> Result { if timeout.is_some() { let url = try!(self.endpoint.join(path)); - let mut req = try!(Request::with_connector(Method::Get, url, &self.connector,)); + let mut req = try!(Request::with_connector(Method::Get, url, &self.connector)); { // scope for mutable borrow of req let headers = req.headers_mut(); @@ -557,6 +566,12 @@ impl Kluster { password: Some(pass.clone()), })); } + KlusterAuth::Exec(ref exec) => { + headers.set(Authorization(Bearer { + //TODO + token: exec.generate_token(), + })); + } KlusterAuth::CertKey(..) => {} } }