From 9ca6de7ffe51057ff27cbfcce135917f15ea4a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Mon, 23 Nov 2020 15:40:07 +0100 Subject: [PATCH 01/22] Working version after move from external project. --- Cargo.lock | 5 - Cargo.toml | 19 ++ src/main.rs | 41 ++- src/provider/error.rs | 33 +++ src/provider/mod.rs | 147 +++++++++++ src/provider/repository/mod.rs | 46 ++++ src/provider/repository/package.rs | 43 +++ src/provider/repository/repository.rs | 27 ++ .../repository/stackablerepository.rs | 198 ++++++++++++++ src/provider/states.rs | 39 +++ src/provider/states/create_config.rs | 249 ++++++++++++++++++ src/provider/states/create_service.rs | 29 ++ src/provider/states/download_package.rs | 97 +++++++ .../states/download_package_backoff.rs | 33 +++ src/provider/states/failed.rs | 33 +++ src/provider/states/install_package.rs | 81 ++++++ src/provider/states/running.rs | 50 ++++ src/provider/states/setup_failed.rs | 31 +++ src/provider/states/starting.rs | 27 ++ src/provider/states/stopped.rs | 31 +++ src/provider/states/stopping.rs | 27 ++ src/provider/states/terminated.rs | 27 ++ src/provider/states/waiting_config.rs | 35 +++ 23 files changed, 1342 insertions(+), 6 deletions(-) delete mode 100644 Cargo.lock create mode 100644 src/provider/error.rs create mode 100644 src/provider/mod.rs create mode 100644 src/provider/repository/mod.rs create mode 100644 src/provider/repository/package.rs create mode 100644 src/provider/repository/repository.rs create mode 100644 src/provider/repository/stackablerepository.rs create mode 100644 src/provider/states.rs create mode 100644 src/provider/states/create_config.rs create mode 100644 src/provider/states/create_service.rs create mode 100644 src/provider/states/download_package.rs create mode 100644 src/provider/states/download_package_backoff.rs create mode 100644 src/provider/states/failed.rs create mode 100644 src/provider/states/install_package.rs create mode 100644 src/provider/states/running.rs create mode 100644 src/provider/states/setup_failed.rs create mode 100644 src/provider/states/starting.rs create mode 100644 src/provider/states/stopped.rs create mode 100644 src/provider/states/stopping.rs create mode 100644 src/provider/states/terminated.rs create mode 100644 src/provider/states/waiting_config.rs diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 0889798..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,5 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "stackable-agent" -version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 8f8da9c..85081a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,22 @@ authors = ["Sönke Liebau "] edition = "2018" [dependencies] +kubelet = { git="https://github.com/deislabs/krustlet.git", rev="ac218b38ba564de806568e49d9e38aaef9f41537", default-features = true, features= ["derive", "cli"] } +oci-distribution = { git="https://github.com/deislabs/krustlet.git", rev="ac218b38ba564de806568e49d9e38aaef9f41537"} +#kubelet = { version="0.5", default-features = true, features= ["derive", "cli"] } +k8s-openapi = { version = "0.9", default-features = false, features = ["v1_18"] } +kube = { version= "0.42", default-features = false, features = ["native-tls"] } +kube-derive = "0.43" +anyhow = "1.0" +env_logger = "0.7" +async-trait = "0.1" +tokio = { version = "0.2.22", features = ["fs", "stream", "macros", "io-util", "sync", "signal", "uds"] } +log = "0.4" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +reqwest = "0.10" +flate2 = "1.0" +tar = "0.4" +handlebars = "3.5" +thiserror = "1.0" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f328e4d..afa9a70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1 +1,40 @@ -fn main() {} +use kubelet::config::Config; +use kubelet::store::composite::ComposableStore; +use kubelet::store::oci::FileStore; +use kubelet::Kubelet; +use std::sync::Arc; +use crate::provider::StackableProvider; +use kube::config::KubeConfigOptions; +use kube::config::Config as KubeConfig; +use std::path::PathBuf; + +mod provider; + +#[tokio::main(threaded_scheduler)] +async fn main() -> anyhow::Result<()> { + // The provider is responsible for all the "back end" logic. If you are creating + // a new Kubelet, all you need to implement is a provider. + let config = Config::new_from_file_and_flags(env!("CARGO_PKG_VERSION"), None); + + // Initialize the logger + env_logger::init(); + + //let kubeconfig = kubelet::bootstrap(&config, &config.bootstrap_file, notify_bootstrap).await?; + let kubeconfig = KubeConfig::from_kubeconfig(&KubeConfigOptions::default()) + .await + .expect("Failed to create Kubernetes Client!"); + + let parcel_directory = PathBuf::from("/home/sliebau/IdeaProjects/krustlet/work/parcels"); + let config_directory = PathBuf::from("/home/sliebau/IdeaProjects/krustlet/work/config"); + let provider = StackableProvider::new(kube::Client::new(kubeconfig.clone()), parcel_directory, config_directory) + .await + .expect("Error initializing provider."); + + let kubelet = Kubelet::new(provider, kubeconfig, config).await?; + kubelet.start().await +} + + +fn notify_bootstrap(message: String) { + println!("BOOTSTRAP: {}", message); +} diff --git a/src/provider/error.rs b/src/provider/error.rs new file mode 100644 index 0000000..9698a7d --- /dev/null +++ b/src/provider/error.rs @@ -0,0 +1,33 @@ +use thiserror::Error; +use k8s_openapi::url; +use crate::provider::repository::package::Package; +use handlebars::{RenderError, TemplateError}; + + +#[derive(Error, Debug)] +pub enum StackableError { + #[error(transparent)] + Parse(#[from] url::ParseError), + #[error(transparent)] + Reqwest(#[from] reqwest::Error), + #[error(transparent)] + IO(#[from] std::io::Error), + #[error("unable to create repository from received repo object")] + RepositoryConversionError, + #[error("error parsing package from containerimage string, has to be in the form of: \"repositoryname/package:version\"")] + PackageParseError, + #[error("Invalid content in pod object: {msg}")] + PodValidationError{msg: String}, + #[error(transparent)] + Kube(#[from] kube::Error), + #[error(transparent)] + TemplateRenderError(#[from] RenderError), + #[error(transparent)] + TemplateError(#[from] TemplateError), + #[error("A required CRD has not been registered: {missing_crds:?}")] + CrdMissing{missing_crds: Vec}, + #[error("Package {package} not found in repository")] + PackageNotFound{package: Package}, + #[error("{msg}")] + RuntimeError{msg: String} +} \ No newline at end of file diff --git a/src/provider/mod.rs b/src/provider/mod.rs new file mode 100644 index 0000000..1dc727b --- /dev/null +++ b/src/provider/mod.rs @@ -0,0 +1,147 @@ +use kubelet::provider::Provider; +use kubelet::log::Sender; +use kubelet::pod::Pod; + +use crate::provider::states::failed::Failed; +use kubelet::backoff::ExponentialBackoffStrategy; +use kubelet::node::Builder; +use crate::provider::states::terminated::Terminated; +use crate::provider::states::download_package::Downloading; +use kube::{Client, Api}; +use crate::provider::error::StackableError; +use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition; +use crate::provider::error::StackableError::{CrdMissing, PodValidationError}; +use log::{debug, info, error}; +use std::path::PathBuf; +use std::fs; +use crate::provider::repository::package::Package; +use std::convert::TryFrom; +use std::sync::Arc; +use tokio::sync::Notify; +use oci_distribution::Reference; + +pub struct StackableProvider { + client: Client, + parcel_directory: PathBuf, + config_directory: PathBuf, +} + +pub const CRDS: &'static [&'static str] = &["repositories.stable.stackable.de"]; + + +mod states; +mod repository; +mod error; + +pub struct PodState { + client: Client, + parcel_directory: PathBuf, + download_directory: PathBuf, + config_directory: PathBuf, + package_download_backoff_strategy: ExponentialBackoffStrategy, + package: Package, +} + +impl StackableProvider { + pub async fn new(client: Client, parcel_directory: PathBuf, config_directory: PathBuf) -> Result { + let provider = StackableProvider { + client, + parcel_directory, + config_directory, + }; + let missing_crds = provider.check_crds().await; + if missing_crds.is_empty() { + debug!("All required CRDS present!"); + return Ok(provider); + } else { + debug!("Missing required CDRS"); + return Err(CrdMissing { missing_crds }); + } + } + + fn get_package(&self, pod: &Pod) -> Result { + let containers = pod.containers(); + if (containers.len().ne(&1)) { + let e = PodValidationError { msg: String::from("Size of containers list in PodSpec has to be exactly 1") }; + return Err(e); + } else { + // List has exactly one value, try to parse this + if let Ok(Some(reference)) = containers[0].image() { + return Package::try_from(reference); + } else { + let e = PodValidationError { msg: String::from("Unable to get package reference from pod") }; + return Err(e); + } + } + } + + async fn check_crds(&self) -> Vec { + let mut missing_crds = vec![]; + let crds: Api = Api::all(self.client.clone()); + + // Check all CRDS + for crd in CRDS.into_iter() { + debug!("Checking if CRD \"{}\" is registered", crd); + match crds.get(crd).await { + Err(e) => { + error!("Missing required CRD: \"{}\"", crd); + missing_crds.push(String::from(*crd)) + } + _ => { + debug!("Found registered crd: {}", crd) + } + } + } + missing_crds + } +} + +// No cleanup state needed, we clean up when dropping PodState. +#[async_trait::async_trait] +impl kubelet::state::AsyncDrop for PodState { + async fn async_drop(self) {} +} + +#[async_trait::async_trait] +impl Provider for StackableProvider { + type PodState = PodState; + type InitialState = Downloading; + type TerminatedState = Terminated; + + + const ARCH: &'static str = "stackable-linux"; + + async fn node(&self, builder: &mut Builder) -> anyhow::Result<()> { + builder.set_architecture(Self::ARCH); + builder.add_taint("NoSchedule", "kubernetes.io/arch", Self::ARCH); + builder.add_taint("NoExecute", "kubernetes.io/arch", Self::ARCH); + Ok(()) + } + + async fn initialize_pod_state(&self, pod: &Pod) -> anyhow::Result { + let parcel_directory = self.parcel_directory.clone(); + let download_directory = parcel_directory.join("_download"); + let config_directory = self.config_directory.clone(); + + let package = self.get_package(pod)?; + if !(&download_directory.is_dir()) { + fs::create_dir_all(&download_directory)?; + } + if !(&config_directory.is_dir()) { + fs::create_dir_all(&config_directory)?; + } + + Ok(PodState { + client: self.client.clone(), + parcel_directory, + download_directory, + config_directory: self.config_directory.clone(), + package_download_backoff_strategy: ExponentialBackoffStrategy::default(), + package, + }) + } + + async fn logs(&self, namespace: String, pod: String, container: String, sender: Sender) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/src/provider/repository/mod.rs b/src/provider/repository/mod.rs new file mode 100644 index 0000000..7bbc101 --- /dev/null +++ b/src/provider/repository/mod.rs @@ -0,0 +1,46 @@ +use crate::provider::repository::package::Package; +use crate::provider::repository::stackablerepository::StackableRepoProvider; +use kube::{Client, Api}; +use crate::provider::error::StackableError; +use kube::api::ListParams; +use std::convert::TryFrom; +use log::{trace, debug, info, error}; +use crate::provider::repository::repository::Repository; + +pub mod package; +pub mod repository; +pub mod stackablerepository; + +pub async fn find_repository(client: Client, package: &Package, repository_reference: Option) -> Result, StackableError> { + let repositories: Api = Api::namespaced(client.clone(), "default"); + if let Some(repository_name) = repository_reference { + // A repository name was provided, just check that exact repository for the package + let repo = repositories.get(&repository_name).await?; + let mut repo = StackableRepoProvider::try_from(&repo)?; + if repo.provides_package(package.clone()).await? { + return Ok(Some(repo)); + } else { + return Ok(None); + } + } else { + // No repository name was provided, retrieve all repositories from the orchestrator/apiserver + // and check which one provides the package + let list_params = ListParams::default(); + let repos = repositories.list(&list_params).await?; + for repository in repos.iter() { + let repo: &Repository = repository; + debug!("got repo definition: {:?}", repository); + // Convert repository to object implementing our trait + // TODO: add generic implementation here to support different types of repository + let mut repo = StackableRepoProvider::try_from(repository)?; + trace!("converted to stackable repo: {:?}", repository); + if repo.provides_package(package.clone()).await? { + debug!("Found package {} in repository {}", &package, repo); + return Ok(Some(repo)); + } else { + debug!("Package {} not provided by repository {}", &package, repo); + } + } + } + Ok(None) +} \ No newline at end of file diff --git a/src/provider/repository/package.rs b/src/provider/repository/package.rs new file mode 100644 index 0000000..4e33703 --- /dev/null +++ b/src/provider/repository/package.rs @@ -0,0 +1,43 @@ +use std::collections::HashMap; +use std::convert::TryFrom; +use k8s_openapi::serde_value::DeserializerError; +use serde::{Deserialize, Serialize}; +use crate::provider::error::StackableError::PackageParseError; +use oci_distribution::Reference; +use crate::provider::error::StackableError; +use std::fmt; + + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Package { + pub product: String, + pub version: String, +} + +impl Package { + pub fn get_file_name(&self) -> String { + format!("{}.tar.gz", self.get_directory_name()) + } + + pub fn get_directory_name(&self) -> String { + format!("{}-{}", self.product, self.version) + } +} + +impl TryFrom for Package { + type Error = StackableError; + + fn try_from(value: Reference) -> Result { + Ok(Package { + product: String::from(value.repository()), + version: String::from(value.tag().unwrap()), + }) + } +} + +impl fmt::Display for Package { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.product, self.version) + } +} + diff --git a/src/provider/repository/repository.rs b/src/provider/repository/repository.rs new file mode 100644 index 0000000..1db32f9 --- /dev/null +++ b/src/provider/repository/repository.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use crate::provider::repository::package::Package; +use kube_derive::CustomResource; + +#[derive(CustomResource, Serialize, Deserialize, Default, Clone, Debug)] +#[kube( +kind = "Repository", +group = "stable.stackable.de", +version = "v1", +namespaced +)] +pub struct RepositorySpec { + pub repo_type: RepoType, + pub properties: HashMap, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum RepoType { + StackableRepo, +} + +impl Default for RepoType { + fn default() -> Self { + RepoType::StackableRepo + } +} diff --git a/src/provider/repository/stackablerepository.rs b/src/provider/repository/stackablerepository.rs new file mode 100644 index 0000000..1520b60 --- /dev/null +++ b/src/provider/repository/stackablerepository.rs @@ -0,0 +1,198 @@ +use std::collections::HashMap; +use std::convert::{TryFrom, TryInto}; +use std::hash::{Hash, Hasher}; + +use kube::api::Meta; +use serde::{Deserialize, Serialize}; +use k8s_openapi::url::{ParseError, Url}; + +use std::path::PathBuf; +use std::fs::File; +use std::io::{Cursor, copy}; +use crate::provider::repository::package::Package; +use crate::provider::repository::repository::Repository; +use crate::provider::error::StackableError; +use log::{trace, debug, info, error}; +use std::fmt; +use crate::provider::error::StackableError::PackageNotFound; + + +#[derive(Debug, Clone)] +pub struct StackableRepoProvider { + base_url: Url, + pub name: String, + content: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +struct RepoData { + version: String, + parcels: HashMap>, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Product { + version: String, + path: String, + hashes: HashMap, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct RepositoryContent { + pub version: String, + pub parcels: HashMap>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct StackablePackage { + pub product: String, + pub version: String, + pub link: String, + pub hashes: HashMap, +} + +impl StackableRepoProvider { + pub fn new(name: String, base_url: String) -> Result { + let base_url = Url::parse(&base_url)?; + + Ok(StackableRepoProvider { base_url, name, content: None }) + } + + pub async fn provides_package>(&mut self, package: T) -> Result { + debug!("Starting metadata refresh for repository of type {} at location {}", "StackableRepo", self.base_url); + let package = package.into(); + let metadata = self.get_repo_metadata().await?; + debug!("Repository provides the following products: {:?}", metadata); + if let Some(product) = metadata.parcels.get(&package.product) { + return Ok(product.contains_key(&package.version)); + } + Ok(false) + } + + async fn get_package(&mut self, package: Package) -> Result { + if self.content.is_none() { + self.get_repo_metadata().await?; + } + if let Some(content) = &self.content { + let parcels = &content.parcels; + if let Some(product) = parcels.get(&package.product) { + // product exists in repo + if let Some(version) = product.get(&package.version) { + // found our package + return Ok(version.clone()); + } + }; + } + Err(PackageNotFound {package}) + } + + pub async fn download_package(&mut self, package: &Package, target_path: PathBuf) -> Result<(), StackableError> { + if self.content.is_none() { + let _content = self.get_repo_metadata(); + } + + let stackable_package = self.get_package(package.clone()).await?; + let download_link = Url::parse(&stackable_package.link)?; + let mut response = reqwest::get(download_link).await?; + + let mut content = Cursor::new(response.bytes().await?); + + let mut out = File::create(target_path.join(package.get_file_name()))?; + copy(&mut content, &mut out)?; + Ok(()) + } + + // TODO: implement caching based on version of metadata + async fn get_repo_metadata(&mut self) -> Result { + trace!("entering get_repo_metadata"); + let mut metadata_url = self.base_url.clone(); + + // TODO: add error propagation + // path_segments_mut returns () in an error case, not sure how to handle this + metadata_url + .path_segments_mut() + .expect("") + .push("metadata.json"); + + debug!("Retrieving repository metadata from {}", metadata_url); + + let repo_data = reqwest::get(metadata_url).await?.json::().await?; + + debug!("Got repository metadata: {:?}", repo_data); + + let mut parcels: HashMap> = HashMap::new(); + for (product, versions) in repo_data.parcels { + let mut versionlist = HashMap::new(); + for version in versions { + versionlist.insert( + version.version.clone(), + StackablePackage { + product: product.clone(), + version: version.version, + link: self.resolve_url(version.path.clone())?, + hashes: version.hashes.clone() + }, + ); + } + parcels.insert(product, versionlist); + } + let repo_content: RepositoryContent = RepositoryContent { + version: repo_data.version, + parcels, + }; + self.content = Some(repo_content.clone()); + Ok(repo_content) + } + + fn resolve_url(&self, path: String) -> Result { + if let Result::Ok(absolute_link) = Url::parse(&path) { + return Ok(path); + } + let resolved_path = self.base_url.join(&path)?; + Ok(resolved_path.as_str().to_string()) + } +} + +impl fmt::Display for StackableRepoProvider { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name) + } +} + +impl TryFrom<&Repository> for StackableRepoProvider { + type Error = StackableError; + + fn try_from(value: &Repository) -> Result { + let properties: HashMap = value.clone().spec.properties; + let path = properties.get("url"); + match path { + Some(gna) => return Ok(StackableRepoProvider { name: Meta::name(value), base_url: Url::parse(gna)?, content: None }), + None => return Err(StackableError::RepositoryConversionError) + } + } +} + +impl Eq for StackableRepoProvider {} + +impl PartialEq for StackableRepoProvider { + fn eq(&self, other: &Self) -> bool { + self.name.eq(&other.name) + } +} + +impl Hash for StackableRepoProvider { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +#[cfg(test)] +mod tests { + use url::Url; + + #[test] + fn test_url_functions() { + assert!(true); + } +} + diff --git a/src/provider/states.rs b/src/provider/states.rs new file mode 100644 index 0000000..2eefe12 --- /dev/null +++ b/src/provider/states.rs @@ -0,0 +1,39 @@ +pub(crate) mod download_package; +pub(crate) mod install_package; +pub(crate) mod create_config; +pub(crate) mod waiting_config; +pub(crate) mod create_service; +pub(crate) mod download_package_backoff; +pub(crate) mod setup_failed; +pub(crate) mod starting; +pub(crate) mod running; +pub(crate) mod stopping; +pub(crate) mod stopped; +pub(crate) mod failed; +pub(crate) mod terminated; + + +/// When called in a state's `next` function, exits the current state +/// and transitions to the Error state. +#[macro_export] +macro_rules! transition_to_error { + ($slf:ident, $err:ident) => {{ + let aerr = anyhow::Error::from($err); + log::error!("{:?}", aerr); + let error_state = super::error::Error { + message: aerr.to_string(), + }; + return Transition::next($slf, error_state); + }}; +} + +/// When called in a state's `next` function, exits the state machine +/// returns a fatal error to the kubelet. +#[macro_export] +macro_rules! fail_fatal { + ($err:ident) => {{ + let aerr = anyhow::Error::from($err); + log::error!("{:?}", aerr); + return Transition::Complete(Err(aerr)); + }}; +} diff --git a/src/provider/states/create_config.rs b/src/provider/states/create_config.rs new file mode 100644 index 0000000..5155571 --- /dev/null +++ b/src/provider/states/create_config.rs @@ -0,0 +1,249 @@ +use crate::provider::error::StackableError; +use crate::provider::error::StackableError::{PodValidationError, RuntimeError}; +use crate::fail_fatal; +use crate::provider::states::create_service::CreatingService; +use crate::provider::states::failed::Failed; +use crate::provider::states::running::Running; +use crate::provider::states::setup_failed::SetupFailed; +use crate::provider::PodState; +use handlebars::{Handlebars, RenderError}; +use k8s_openapi::api::core::v1::{ConfigMap, Volume, VolumeMount}; +use kube::api::ListParams; +use kube::{Api, Client}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; +use log::{trace, debug, error, info, warn}; +use std::collections::{BTreeMap, HashMap}; +use std::fs; +use std::path::{PathBuf, Path}; +use std::fs::read_to_string; +use crate::provider::states::waiting_config::WaitingConfigMap; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(CreatingService, SetupFailed, WaitingConfigMap)] +pub struct CreatingConfig { + pub target_directory: Option, +} + +impl CreatingConfig { + fn render_config_template( + data: BTreeMap, + template: String, + ) -> Result { + let mut handlebars = Handlebars::new(); + debug!("Rendering template with context: {:?}", data); + + // register the template. The template string will be verified and compiled. + handlebars.register_template_string("t1", template)?; + + // Set strict mode, so that we fail with an error if any non-existent fields are accessed + handlebars.set_strict_mode(true); + + // Render the template with the provided data and return the resulting String + Ok(handlebars.render("t1", &data)?) + } + + fn create_render_data(pod_state: &PodState) -> BTreeMap { + let mut render_data = BTreeMap::new(); + let directory_name = pod_state.package.get_directory_name(); + + if let Ok(package_dir) = &pod_state.parcel_directory.join(&directory_name).into_os_string().into_string() { + render_data.insert( + String::from("packageroot"), + String::from(package_dir), + ); + } else { + warn!("Unable to parse value for package directory as UTF8") + } + + if let Ok(conf_dir) = &pod_state.config_directory.join(&directory_name).into_os_string().into_string() { + render_data.insert( + String::from("configroot"), + String::from(conf_dir), + ); + } else { + warn!("Unable to parse value for config directory as UTF8"); + } + render_data + } + + async fn missing_config_maps(&self, client: Client, configmaps: Vec) -> Vec { + // TODO: distinguish between an actually missing configmap and an error when talking to + // the apiserver + let configmaps_api: Api = Api::namespaced(client.clone(), "default"); + let mut missing_configmaps = vec![]; + for map in configmaps { + let result = configmaps_api.get(&map).await; + match result { + Ok(configmap) => {} + Err(e) => { + debug!("ConfigMap {} not found", &map); + missing_configmaps.push(String::from(map)); + } + } + } + missing_configmaps + } + + async fn get_config_maps(&self, pod: &Pod) -> Vec { + let mut get_config_maps = vec![]; + + if let Some(volumes) = pod.volumes() { + for volume in volumes { + if let Some(config_map) = &volume.config_map { + // config map was present, check if a name was set + // not sure when it would not be set, but it is a valid possibility, so we need + // to handle it - if no name is present, we'll just ignore this map, not sure + // how to retrieve it otherwise + if let Some(config_map_name) = &config_map.name { + debug!("Found reference to config map {}", &config_map_name); + get_config_maps.push(String::from(config_map_name)); + } + } + } + } + get_config_maps + } + + async fn retrieve_config_map( + &self, + client: Client, + name: String, + ) -> Result { + let config_maps: Api = Api::namespaced(client.clone(), "default"); + + Ok(config_maps.get(&name).await?) + } + + fn apply_config_map( + &self, + map: ConfigMap, + target_directory: PathBuf, + template_data: &BTreeMap, + ) -> Result<(), StackableError> + { + debug!( + "applying configmap {} to directory {:?}", + map.metadata.name.unwrap_or(String::from("undefined")), + target_directory + ); + if !(&target_directory.is_dir()) { + info!("creating config directory {:?}", target_directory); + fs::create_dir_all(&target_directory)?; + } + if let Some(data) = map.data { + for key in data.keys() { + debug!("found key: {} in configmap", key); + if let Some(content) = data.get(key) { + trace!("content of key: {}", &content); + let rendered_content = CreatingConfig::render_config_template( + template_data.clone(), + content.clone(), + )?; + let target_file = target_directory.join(&key); + + // TODO: compare existing file with intended state + if CreatingConfig::needs_update(&target_file, &rendered_content)? { + debug!( + "writing content of map entry {} to file {:?}", + key, target_file + ); + let write_result = fs::write(target_directory.join(&key), rendered_content); + match write_result { + Ok(()) => debug!("write of file {:?} successful!", target_file), + Err(e) => error!("write of file {:?} failed: {}", target_file, e), + } + } else { + debug!("No update needed for {:?}", target_file); + } + } else { + info!("No content found for key {}", key); + } + } + } + Ok(()) + } + + fn needs_update(target_file: &PathBuf, content: &str) -> Result { + let current_content = read_to_string(target_file)?; + Ok(current_content.ne(content)) + } +} + +#[async_trait::async_trait] +impl State for CreatingConfig { + async fn next( + mut self: Box, + pod_state: &mut PodState, + _pod: &Pod, + ) -> Transition { + let name = _pod.name(); + let client = pod_state.client.clone(); + let package = pod_state.package.clone(); + let config_directory = pod_state.config_directory.clone(); + self.target_directory = Some(config_directory.join(package.get_directory_name())); + let target_directory = self.target_directory.clone().unwrap(); + + // Check if all required config maps have been created in the api-server + let referenced_config_maps = self.get_config_maps(_pod).await; + let missing_config_maps = self.missing_config_maps(client.clone(), referenced_config_maps).await; + if !missing_config_maps.is_empty() { + // not all configmaps are present + info!("Missing config maps, waiting.."); + return Transition::next(self, WaitingConfigMap { missing_config_maps }) + + } + + + debug!("Entering state \"creating config\" for service {}", name); + let containers = _pod.containers(); + if containers.len().ne(&1) { + let e = PodValidationError { + msg: "Only pods containing exactly one container element are supported!" + .to_string(), + }; + fail_fatal!(e); + } + let container = containers[0].clone(); + + if let Some(volumes) = _pod.volumes() { + debug!("Found {} volumes in pod {}", volumes.len(), _pod.name()); + if let Some(mounts) = container.volume_mounts() { + debug!("Found {} mounts in pod {}", mounts.len(), _pod.name()); + // Got mounts and volumes, we can now decide which ones we need to act upon + for mount in mounts { + for volume in volumes { + if mount.name.eq(&volume.name) { + let target_dir = + target_directory.join(&mount.mount_path.trim_start_matches('/')); + if let Some(config_map) = &volume.config_map { + if let Some(map_name) = &config_map.name { + if let Ok(map) = self + .retrieve_config_map(client.clone(), map_name.to_string()) + .await + { + debug!("found config map: {:?}", config_map); + self.apply_config_map(map, target_dir, &CreatingConfig::create_render_data(pod_state)); + } + } + } else { + warn!("Skipping volume {} - it is not a config map", volume.name); + } + } + } + } + }; + } + + Transition::next(self, CreatingService) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:initializing") + } +} diff --git a/src/provider/states/create_service.rs b/src/provider/states/create_service.rs new file mode 100644 index 0000000..1ecd5c4 --- /dev/null +++ b/src/provider/states/create_service.rs @@ -0,0 +1,29 @@ +use kubelet::state::{State, Transition}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use crate::provider::PodState; +use crate::provider::states::running::Running; +use crate::provider::states::failed::Failed; +use crate::provider::states::starting::Starting; +use crate::provider::states::setup_failed::SetupFailed; + + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Starting, SetupFailed)] +pub struct CreatingService; + +#[async_trait::async_trait] +impl State for CreatingService { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + println!("creating service"); + Transition::next(self, Starting) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:initializing") + } +} \ No newline at end of file diff --git a/src/provider/states/download_package.rs b/src/provider/states/download_package.rs new file mode 100644 index 0000000..849761b --- /dev/null +++ b/src/provider/states/download_package.rs @@ -0,0 +1,97 @@ +use kubelet::state::{State, Transition}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use crate::provider::PodState; +use crate::provider::states::running::Running; +use crate::provider::states::failed::Failed; +use crate::provider::states::install_package::Installing; +use crate::provider::states::setup_failed::SetupFailed; +use crate::provider::error::StackableError::PodValidationError; +use crate::fail_fatal; +use kube::api::Meta; +use k8s_openapi::api::core::v1::PodSpec; +use crate::provider::repository::package::Package; +use crate::provider::error::StackableError; +use kubelet::container::Container; +use std::convert::TryFrom; +use log::{debug, info, warn, error}; +use crate::provider::repository::find_repository; +use crate::provider::states::download_package_backoff::DownloadingBackoff; +use std::path::{Path, PathBuf}; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Installing, DownloadingBackoff)] +pub struct Downloading; + +impl Downloading { + fn package_downloaded>(&self, package: T, download_directory: PathBuf) -> bool { + let package = package.into(); + let package_file_name = download_directory.join(package.get_file_name()); + debug!("Checking if package {} has already been downloaded to {:?}", package, package_file_name); + Path::new(&package_file_name).exists() + } +} + +#[async_trait::async_trait] +impl State for Downloading { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + let package = pod_state.package.clone(); + + info!("Looking for package: {} in known repositories", &package); + debug!("Checking if package {} has already been downloaded.", package); + if self.package_downloaded(package.clone(), pod_state.download_directory.clone()) { + info!("Package {} has already been downloaded to {:?}, continuing with installation", package, pod_state.download_directory); + return Transition::next(self, Installing { + download_directory: pod_state.download_directory.clone(), + parcel_directory: pod_state.parcel_directory.clone(), + package: package.clone(), + }); + } + let repo = find_repository(pod_state.client.clone(), &package, None).await; + match repo { + Ok(Some(mut repo)) => { + // We found a repository providing the package, proceed with download + // The repository has already downloaded its metadata it this time, as that + // was used to check whether it provides the package + info!("Starting download of package {} from repository {}", &package, &repo); + let download_directory = pod_state.download_directory.clone(); + let download_result = repo.download_package(&package, download_directory.clone()).await; + match download_result { + Ok(()) => { + info!("Successfully downloaded package {} to {:?}", package, download_directory.clone()); + return Transition::next(self, Installing { + download_directory: pod_state.download_directory.clone(), + parcel_directory: pod_state.parcel_directory.clone(), + package: package.clone(), + }); + } + Err(e) => { + warn!("Download of package {} failed: {}", package, e); + return Transition::next(self, DownloadingBackoff { package: package.clone() }); + } + } + } + Ok(None) => { + // No repository was found that provides this package + let message = format!("Cannot find package {} in any repository, aborting ..", &package); + error!("{}", &message); + return Transition::next(self, DownloadingBackoff { package: package.clone() }); + } + Err(e) => { + // An error occurred when looking for a repository providing this package + let message = format!("Error occurred trying to find package {}: {:?}", &package, e); + error!("{}", &message); + return Transition::next(self, DownloadingBackoff { package: package.clone() }); + } + } + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:initializing") + } +} + diff --git a/src/provider/states/download_package_backoff.rs b/src/provider/states/download_package_backoff.rs new file mode 100644 index 0000000..531946c --- /dev/null +++ b/src/provider/states/download_package_backoff.rs @@ -0,0 +1,33 @@ +use kubelet::state::prelude::*; + +use crate::provider::PodState; +use crate::provider::states::install_package::Installing; +use crate::provider::states::download_package::Downloading; +use kubelet::backoff::BackoffStrategy; +use crate::provider::repository::package::Package; +use log::{debug, info, error}; + +#[derive(Debug, TransitionTo)] +#[transition_to(Downloading)] +/// The Pod failed to run. +// If we manually implement, we can allow for arguments. +pub struct DownloadingBackoff { + pub package: Package, +} + +#[async_trait::async_trait] +impl State for DownloadingBackoff { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + info!("Backing of before retrying download of package {}", self.package); + pod_state.package_download_backoff_strategy.wait().await; + Transition::next(self, Downloading) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:running") + } +} diff --git a/src/provider/states/failed.rs b/src/provider/states/failed.rs new file mode 100644 index 0000000..b130c8f --- /dev/null +++ b/src/provider/states/failed.rs @@ -0,0 +1,33 @@ +use kubelet::state::prelude::*; + + +use crate::provider::PodState; +use crate::provider::states::install_package::Installing; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Installing)] +/// The Pod failed to run. +// If we manually implement, we can allow for arguments. +pub struct Failed { + pub message: String, +} + +#[async_trait::async_trait] +impl State for Failed { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + println!("failed"); + Transition::next(self, Installing{ + download_directory: pod_state.download_directory.clone(), + parcel_directory: pod_state.parcel_directory.clone(), + package: pod_state.package.clone() + }) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Failed, &self.message) + } +} diff --git a/src/provider/states/install_package.rs b/src/provider/states/install_package.rs new file mode 100644 index 0000000..4acc2eb --- /dev/null +++ b/src/provider/states/install_package.rs @@ -0,0 +1,81 @@ +use kubelet::state::{State, Transition}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use crate::provider::PodState; +use crate::provider::states::running::Running; +use crate::provider::states::failed::Failed; +use crate::provider::states::create_config::CreatingConfig; +use crate::provider::states::setup_failed::SetupFailed; +use log::{debug, info}; +use kube::api::Meta; +use k8s_openapi::api::core::v1::PodSpec; +use crate::provider::repository::package::Package; +use std::path::{Path, PathBuf}; +use crate::provider::error::StackableError; +use std::fs::File; +use flate2::read::GzDecoder; +use tar::Archive; + +#[derive(Debug, TransitionTo)] +#[transition_to(CreatingConfig, SetupFailed)] +pub struct Installing { + pub download_directory: PathBuf, + pub parcel_directory: PathBuf, + pub package: Package, +} + +impl Installing { + fn package_installed>(&self, package: T) -> bool { + let package = package.into(); + + let package_file_name = self.parcel_directory.join(package.get_directory_name()); + debug!("Checking if package {:?} has already been installed to {:?}", package, package_file_name); + Path::new(&package_file_name).exists() + } + + fn get_target_directory(&self, package: Package) -> PathBuf { + self.parcel_directory.join(package.get_directory_name()) + } + + fn install_package>(&self, package: T) -> Result<(), StackableError> { + let package: Package = package.into(); + // To be on the safe side, check if the package is actually there + + let archive_path = self.download_directory.join(package.get_file_name()); + let tar_gz = File::open(&archive_path)?; + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + + let target_directory = self.get_target_directory(package.clone()); + + println!("Installing package: {:?} from {:?} into {:?}", package, archive_path, target_directory); + archive.unpack(self.parcel_directory.join(package.get_directory_name()))?; + Ok(()) + } +} + +#[async_trait::async_trait] +impl State for Installing { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + let package = self.package.clone(); + if self.package_installed(package.clone()) { + info!("Package {} has already been installed", package); + return Transition::next(self, CreatingConfig{ target_directory: None }); + } else { + info!("Installing package {}", package); + self.install_package(package.clone()); + } + + + debug!("installing package"); + Transition::next(self, CreatingConfig{ target_directory: None }) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:initializing") + } +} \ No newline at end of file diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs new file mode 100644 index 0000000..1151b6d --- /dev/null +++ b/src/provider/states/running.rs @@ -0,0 +1,50 @@ +use kubelet::state::{State, Transition}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use crate::provider::PodState; +use crate::provider::states::failed::Failed; +use crate::provider::states::stopping::Stopping; +use crate::provider::states::install_package::Installing; +use kubelet::container::ContainerKey; +use log::{debug, info, warn, error}; +use std::sync::Arc; +use std::time::Duration; +use tokio::time::timeout; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Stopping, Failed, Running, Installing)] +pub struct Running; + + +#[async_trait::async_trait] +impl State for Running { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + /*while let Ok(_) = timeout(Duration::from_millis(100), changed.notified()).await { + debug!("drained a waiting notification"); + } + debug!("done draining"); + */ + loop { + println!("running"); + tokio::select! { + _ = tokio::time::delay_for(std::time::Duration::from_secs(10)) => { + debug!("timer expired"); + continue; + } + } + } + Transition::next(self, Installing{ + download_directory: pod_state.download_directory.clone(), + parcel_directory: pod_state.parcel_directory.clone(), + package: pod_state.package.clone() + }) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Running, &"status:running") + } +} \ No newline at end of file diff --git a/src/provider/states/setup_failed.rs b/src/provider/states/setup_failed.rs new file mode 100644 index 0000000..4f607b8 --- /dev/null +++ b/src/provider/states/setup_failed.rs @@ -0,0 +1,31 @@ +use kubelet::state::prelude::*; + +use crate::provider::PodState; +use crate::provider::states::download_package::Downloading; +use log::{debug, info, error}; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Downloading)] +/// The Pod failed to run. +// If we manually implement, we can allow for arguments. +pub struct SetupFailed { + pub message: String, +} + +#[async_trait::async_trait] +impl State for SetupFailed { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + error!("setup failed for pod {} due to: {}", _pod.name(), self.message); + info!("Waiting for {} seconds before retrying..", 10); + tokio::time::delay_for(std::time::Duration::from_secs(10)).await; + Transition::next(self, Downloading) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &self.message) + } +} diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs new file mode 100644 index 0000000..7618b0e --- /dev/null +++ b/src/provider/states/starting.rs @@ -0,0 +1,27 @@ +use kubelet::state::{State, Transition}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use crate::provider::PodState; +use crate::provider::states::failed::Failed; +use crate::provider::states::running::Running; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Running, Failed)] +pub struct Starting; + + +#[async_trait::async_trait] +impl State for Starting { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + println!("starting"); + Transition::next(self, Running) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:running") + } +} \ No newline at end of file diff --git a/src/provider/states/stopped.rs b/src/provider/states/stopped.rs new file mode 100644 index 0000000..14357ca --- /dev/null +++ b/src/provider/states/stopped.rs @@ -0,0 +1,31 @@ +use kubelet::state::{State, Transition}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use crate::provider::PodState; +use crate::provider::states::failed::Failed; +use crate::provider::states::stopping::Stopping; +use crate::provider::states::starting::Starting; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Starting)] +pub struct Stopped; + + +#[async_trait::async_trait] +impl State for Stopped { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + for i in 1..8 { + tokio::time::delay_for(std::time::Duration::from_secs(2)).await; + println!("stopped"); + } + Transition::next(self, Starting) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:running") + } +} \ No newline at end of file diff --git a/src/provider/states/stopping.rs b/src/provider/states/stopping.rs new file mode 100644 index 0000000..458c38d --- /dev/null +++ b/src/provider/states/stopping.rs @@ -0,0 +1,27 @@ +use kubelet::state::{State, Transition}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use crate::provider::PodState; +use crate::provider::states::failed::Failed; +use crate::provider::states::stopped::Stopped; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(Stopped, Failed)] +pub struct Stopping; + + +#[async_trait::async_trait] +impl State for Stopping { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + println!("stopping"); + Transition::next(self, Stopped) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:running") + } +} \ No newline at end of file diff --git a/src/provider/states/terminated.rs b/src/provider/states/terminated.rs new file mode 100644 index 0000000..5b2ce69 --- /dev/null +++ b/src/provider/states/terminated.rs @@ -0,0 +1,27 @@ +use kubelet::state::prelude::*; + +use crate::provider::PodState; +use crate::provider::states::install_package::Installing; + +#[derive(Default, Debug)] +/// The Pod failed to run. +// If we manually implement, we can allow for arguments. +pub struct Terminated { + pub message: String, +} + +#[async_trait::async_trait] +impl State for Terminated { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + println!("terminated"); + Transition::Complete(Ok(())) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Succeeded, &self.message) + } +} diff --git a/src/provider/states/waiting_config.rs b/src/provider/states/waiting_config.rs new file mode 100644 index 0000000..f72af78 --- /dev/null +++ b/src/provider/states/waiting_config.rs @@ -0,0 +1,35 @@ +use kubelet::state::prelude::*; + +use crate::provider::PodState; +use crate::provider::states::install_package::Installing; +use crate::provider::states::download_package::Downloading; +use kubelet::backoff::BackoffStrategy; +use crate::provider::repository::package::Package; +use log::{debug, info, error}; +use crate::provider::states::create_config::CreatingConfig; + +#[derive(Debug, TransitionTo)] +#[transition_to(CreatingConfig)] +/// The Pod failed to run. +// If we manually implement, we can allow for arguments. +pub struct WaitingConfigMap { + pub missing_config_maps: Vec , +} + +#[async_trait::async_trait] +impl State for WaitingConfigMap { + async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + info!("Delaying execution due to missing configmaps: {:?}", &self.missing_config_maps); + pod_state.package_download_backoff_strategy.wait().await; + + Transition::next(self, CreatingConfig { target_directory: None }) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &"status:running") + } +} From 5ec1579760f48777441909211b2a9d9686397055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Mon, 23 Nov 2020 15:41:31 +0100 Subject: [PATCH 02/22] Added cargo.lock to repo --- Cargo.lock | 3496 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3496 insertions(+) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1c50488 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3496 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +dependencies = [ + "const-random", +] + +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "anyhow" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" + +[[package]] +name = "anymap" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-compression" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1ff21a63d3262af46b9f33a826a8d134e2d0d9b2179c86034948b732ea8b2a" +dependencies = [ + "bytes 0.5.6", + "flate2", + "futures-core", + "memchr", + "pin-project-lite 0.1.11", +] + +[[package]] +name = "async-stream" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22068c0c19514942eefcfd4daf8976ef1aad84e61539f95cd200c35202f80af5" +dependencies = [ + "async-stream-impl 0.2.1", + "futures-core", +] + +[[package]] +name = "async-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c" +dependencies = [ + "async-stream-impl 0.3.0", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.3", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "cc" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time 0.1.44", + "winapi 0.3.9", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "term_size", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +dependencies = [ + "cfg-if 0.1.10", + "wasm-bindgen", +] + +[[package]] +name = "const-random" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486d435a7351580347279f374cb8a3c16937485441db80181357b7c4d70f17ed" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a84d8ff70e3ec52311109b019c27672b4c1929e4cf7c18bcf0cd9fb5e230be" +dependencies = [ + "getrandom 0.2.0", + "lazy_static", + "proc-macro-hack", + "tiny-keccak", +] + +[[package]] +name = "const_fn" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg 1.0.1", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "dashmap" +version = "3.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f260e2fc850179ef410018660006951c1b55b79e8087e87111a2c388994b9b5" +dependencies = [ + "ahash", + "cfg-if 0.1.10", + "num_cpus", +] + +[[package]] +name = "derivative" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "dirs" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "dtoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "filetime" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "flate2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding 2.1.0", +] + +[[package]] +name = "fsevent" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97f347202c95c98805c216f9e1df210e8ebaec9fdb2365700a43c10797a35e63" +dependencies = [ + "bitflags", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a29c77f1ca394c3e73a9a5d24cfcabb734682d9634fc398f2204a63c994120" +dependencies = [ + "libc", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" + +[[package]] +name = "futures-executor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" + +[[package]] +name = "futures-macro" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" + +[[package]] +name = "futures-task" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project 1.0.2", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check 0.9.2", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "h2" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.1", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", + "tracing-futures", +] + +[[package]] +name = "handlebars" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2764f9796c0ddca4b82c07f25dd2cb3db30b9a8f47940e78e1c883d9e95c3db9" +dependencies = [ + "log", + "pest", + "pest_derive", + "quick-error 2.0.0", + "serde", + "serde_json", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "headers" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f" +dependencies = [ + "base64 0.12.3", + "bitflags", + "bytes 0.5.6", + "headers-core", + "http 0.2.1", + "mime", + "sha-1 0.8.2", + "time 0.1.44", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http 0.2.1", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.9", +] + +[[package]] +name = "http" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +dependencies = [ + "bytes 0.4.12", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" +dependencies = [ + "bytes 0.5.6", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes 0.5.6", + "http 0.2.1", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error 1.2.3", +] + +[[package]] +name = "hyper" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" +dependencies = [ + "bytes 0.5.6", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.1", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.2", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" +dependencies = [ + "bytes 0.5.6", + "hyper", + "native-tls", + "tokio", + "tokio-tls", +] + +[[package]] +name = "hyperx" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a94cbc2c6f63028e5736ca4e811ae36d3990059c384cbe68298c66728a9776" +dependencies = [ + "base64 0.10.1", + "bytes 0.4.12", + "http 0.1.21", + "httparse", + "language-tags", + "log", + "mime", + "percent-encoding 1.0.1", + "time 0.1.44", + "unicase 2.6.0", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +dependencies = [ + "autocfg 1.0.1", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46dd0a94b393c730779ccfd2a872b67b1eb67be3fc33082e733bdb38b5fde4d4" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55" +dependencies = [ + "libc", +] + +[[package]] +name = "input_buffer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" +dependencies = [ + "bytes 0.5.6", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "js-sys" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f995a3c8f2bc3dd52a18a583e90f9ec109c047fa1603a853e46bcda14d2e279d" +dependencies = [ + "serde", + "serde_json", + "treediff", +] + +[[package]] +name = "k8s-openapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f95fd36c08ce592e67400a0f1a66f432196997d5a7e9a97e8743c33d8a9312" +dependencies = [ + "base64 0.12.3", + "bytes 0.5.6", + "chrono", + "http 0.2.1", + "percent-encoding 2.1.0", + "serde", + "serde-value", + "serde_json", + "url 2.2.0", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kube" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f52dbe2c0e7ca54e43f1bc7b77b916750e63d7694e5a18c09b11307acc6b9d" +dependencies = [ + "Inflector", + "base64 0.12.3", + "bytes 0.5.6", + "chrono", + "dirs", + "either", + "futures", + "futures-util", + "http 0.2.1", + "k8s-openapi", + "log", + "openssl", + "pem", + "reqwest", + "serde", + "serde_json", + "serde_yaml", + "static_assertions", + "thiserror", + "time 0.2.23", + "tokio", + "url 2.2.0", +] + +[[package]] +name = "kube-derive" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd71bf282e5551ac0852afcf25352b7fb8dd9a66eed7b6e66a6ebbf6b5b2f475" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "kube-runtime" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78d0d3673ec4eff2c5276bd10db8e596106ce9dee8e035e01650a8c84454ac78" +dependencies = [ + "dashmap", + "derivative", + "futures", + "k8s-openapi", + "kube", + "pin-project 0.4.27", + "serde", + "smallvec", + "snafu", + "tokio", +] + +[[package]] +name = "kubelet" +version = "0.5.0" +source = "git+https://github.com/deislabs/krustlet.git?rev=ac218b38ba564de806568e49d9e38aaef9f41537#ac218b38ba564de806568e49d9e38aaef9f41537" +dependencies = [ + "anyhow", + "async-stream 0.3.0", + "async-trait", + "base64 0.12.3", + "chrono", + "dirs", + "futures", + "hostname", + "http 0.2.1", + "hyper", + "iovec", + "json-patch", + "k8s-openapi", + "kernel32-sys", + "kube", + "kube-runtime", + "kubelet-derive", + "lazy_static", + "lazycell", + "log", + "mio", + "miow", + "notify", + "oci-distribution", + "prost", + "prost-types", + "rcgen", + "reqwest", + "serde", + "serde_json", + "serde_yaml", + "structopt", + "thiserror", + "tokio", + "tonic", + "tonic-build", + "tower", + "url 2.2.0", + "uuid", + "warp", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "kubelet-derive" +version = "0.1.0" +source = "git+https://github.com/deislabs/krustlet.git?rev=ac218b38ba564de806568e49d9e38aaef9f41537#ac218b38ba564de806568e49d9e38aaef9f41537" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" + +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase 2.6.0", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +dependencies = [ + "adler", + "autocfg 1.0.1", +] + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio", + "slab", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "multimap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" + +[[package]] +name = "multipart" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8209c33c951f07387a8497841122fc6f712165e3f9bda3e6be4645b58188f676" +dependencies = [ + "buf_redux", + "httparse", + "log", + "mime", + "mime_guess", + "quick-error 1.2.3", + "rand 0.6.5", + "safemem", + "tempfile", + "twoway", +] + +[[package]] +name = "native-tls" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "notify" +version = "5.0.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b946889dfdad884379cd56367d93b6d0ce8889cc027d26a69a3a31c0a03bb5" +dependencies = [ + "anymap", + "bitflags", + "crossbeam-channel", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio", + "mio-extras", + "walkdir", + "winapi 0.3.9", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg 1.0.1", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oci-distribution" +version = "0.4.0" +source = "git+https://github.com/deislabs/krustlet.git?rev=ac218b38ba564de806568e49d9e38aaef9f41537#ac218b38ba564de806568e49d9e38aaef9f41537" +dependencies = [ + "anyhow", + "futures-util", + "hyperx", + "lazy_static", + "log", + "regex", + "reqwest", + "serde", + "serde_json", + "tokio", + "www-authenticate", +] + +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" +dependencies = [ + "bitflags", + "cfg-if 0.1.10", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +dependencies = [ + "autocfg 1.0.1", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe9037165d7023b1228bc4ae9a2fa1a2b0095eca6c2998c624723dfd01314a5" +dependencies = [ + "num-traits", +] + +[[package]] +name = "pem" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59698ea79df9bf77104aefd39cc3ec990cb9693fb59c3b0a70ddf2646fdffb4b" +dependencies = [ + "base64 0.12.3", + "once_cell", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1 0.8.2", +] + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +dependencies = [ + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" +dependencies = [ + "pin-project-internal 1.0.2", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + +[[package]] +name = "pin-project-lite" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check 0.9.2", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check 0.9.2", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prost" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212" +dependencies = [ + "bytes 0.5.6", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26" +dependencies = [ + "bytes 0.5.6", + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa" +dependencies = [ + "bytes 0.5.6", + "prost", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda" + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg 0.1.2", + "rand_xorshift", + "winapi 0.3.9", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.15", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", + "rand_pcg 0.2.1", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.15", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi 0.3.9", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rcgen" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4974f7e96ee51fa3c90c3022e02c3a7117e71cb2a84518a55e44360135200c25" +dependencies = [ + "chrono", + "pem", + "ring", + "yasna", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom 0.1.15", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb15d6255c792356a0f578d8a645c677904dc02e862bebe2ecc18e0c01b9a0ce" +dependencies = [ + "async-compression", + "base64 0.13.0", + "bytes 0.5.6", + "encoding_rs", + "futures-core", + "futures-util", + "http 0.2.1", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding 2.1.0", + "pin-project-lite 0.2.0", + "serde", + "serde_json", + "serde_urlencoded 0.7.0", + "tokio", + "tokio-tls", + "url 2.2.0", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72b84d47e8ec5a4f2872e8262b8f8256c5be1c938a7d6d3a867a3ba8f722f74" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "rust-argon2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" +dependencies = [ + "base64 0.12.3", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" +dependencies = [ + "base64 0.12.3", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi 0.3.9", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +dependencies = [ + "dtoa", + "itoa", + "serde", + "url 2.2.0", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha-1" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpuid-bool", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "signal-hook-registry" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" + +[[package]] +name = "snafu" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c4e6046e4691afe918fd1b603fd6e515bcda5388a1092a9edbada307d159f09" +dependencies = [ + "doc-comment", + "futures-core", + "pin-project 0.4.27", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7073448732a89f2f3e6581989106067f403d378faeafb4a50812eb814170d3e5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "socket2" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c29947abdee2a218277abeca306f25789c938e500ea5a9d4b12a5a504466902" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stackable-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "env_logger", + "flate2", + "handlebars", + "k8s-openapi", + "kube", + "kube-derive", + "kubelet", + "log", + "oci-distribution", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "tar", + "thiserror", + "tokio", +] + +[[package]] +name = "standback" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" +dependencies = [ + "version_check 0.9.2", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126d630294ec449fae0b16f964e35bf3c74f940da9dca17ee9b905f7b3112eb8" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e51c492f9e23a220534971ff5afc14037289de430e3c83f9daf6a1b6ae91e8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443b4178719c5a851e1bde36ce12da21d74a0e60b4d982ec3385a933c812f0f6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tar" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" +dependencies = [ + "filetime", + "libc", + "redox_syscall", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "rand 0.7.3", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "term_size", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "time" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check 0.9.2", + "winapi 0.3.9", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6d7ad61edd59bfcc7e80dababf0f4aed2e6d5e0ba1659356ae889752dfc12ff" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-uds", + "pin-project-lite 0.1.11", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" +dependencies = [ + "futures-core", + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c" +dependencies = [ + "futures-util", + "log", + "pin-project 0.4.27", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.1.11", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a5d6e7439ecf910463667080de772a9c7ddf26bc9fb4f3252ac3862e43337d" +dependencies = [ + "async-stream 0.2.1", + "async-trait", + "base64 0.12.3", + "bytes 0.5.6", + "futures-core", + "futures-util", + "http 0.2.1", + "http-body", + "hyper", + "percent-encoding 2.1.0", + "pin-project 0.4.27", + "prost", + "prost-derive", + "tokio", + "tokio-util", + "tower", + "tower-balance", + "tower-load", + "tower-make", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19970cf58f3acc820962be74c4021b8bbc8e8a1c4e3a02095d0aa60cde5f3633" +dependencies = [ + "proc-macro2", + "prost-build", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3169017c090b7a28fce80abaad0ab4f5566423677c9331bb320af7e49cfe62" +dependencies = [ + "futures-core", + "tower-buffer", + "tower-discover", + "tower-layer", + "tower-limit", + "tower-load-shed", + "tower-retry", + "tower-service", + "tower-timeout", + "tower-util", +] + +[[package]] +name = "tower-balance" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a792277613b7052448851efcf98a2c433e6f1d01460832dc60bef676bc275d4c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project 0.4.27", + "rand 0.7.3", + "slab", + "tokio", + "tower-discover", + "tower-layer", + "tower-load", + "tower-make", + "tower-ready-cache", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-buffer" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4887dc2a65d464c8b9b66e0e4d51c2fd6cf5b3373afc72805b0a60bce00446a" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-discover" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6b5000c3c54d269cc695dff28136bb33d08cbf1df2c48129e143ab65bf3c2a" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35d656f2638b288b33495d1053ea74c40dc05ec0b92084dd71ca5566c4ed1dc" + +[[package]] +name = "tower-limit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c3040c5dbed68abffaa0d4517ac1a454cd741044f33ab0eefab6b8d1361404" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tokio", + "tower-layer", + "tower-load", + "tower-service", +] + +[[package]] +name = "tower-load" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc79fc3afd07492b7966d7efa7c6c50f8ed58d768a6075dd7ae6591c5d2017b" +dependencies = [ + "futures-core", + "log", + "pin-project 0.4.27", + "tokio", + "tower-discover", + "tower-service", +] + +[[package]] +name = "tower-load-shed" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f021e23900173dc315feb4b6922510dae3e79c689b74c089112066c11f0ae4e" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-make" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce50370d644a0364bf4877ffd4f76404156a248d104e2cc234cd391ea5cdc965" +dependencies = [ + "tokio", + "tower-service", +] + +[[package]] +name = "tower-ready-cache" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eabb6620e5481267e2ec832c780b31cad0c15dcb14ed825df5076b26b591e1f" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "log", + "tokio", + "tower-service", +] + +[[package]] +name = "tower-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6727956aaa2f8957d4d9232b308fe8e4e65d99db30f42b225646e86c9b6a952" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" + +[[package]] +name = "tower-timeout" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127b8924b357be938823eaaec0608c482d40add25609481027b96198b2e4b31e" +dependencies = [ + "pin-project 0.4.27", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" +dependencies = [ + "futures-core", + "futures-util", + "pin-project 0.4.27", + "tower-service", +] + +[[package]] +name = "tracing" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" +dependencies = [ + "cfg-if 0.1.10", + "log", + "pin-project-lite 0.1.11", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", +] + +[[package]] +name = "treediff" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" +dependencies = [ + "serde_json", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" +dependencies = [ + "base64 0.12.3", + "byteorder", + "bytes 0.5.6", + "http 0.2.1", + "httparse", + "input_buffer", + "log", + "rand 0.7.3", + "sha-1 0.9.2", + "url 2.2.0", + "utf-8", +] + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +dependencies = [ + "version_check 0.1.5", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check 0.9.2", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", +] + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna 0.2.0", + "matches", + "percent-encoding 2.1.0", +] + +[[package]] +name = "urlencoding" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9232eb53352b4442e40d7900465dfc534e8cb2dc8f18656fcb2ac16112b5593" + +[[package]] +name = "utf-8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +dependencies = [ + "rand 0.7.3", +] + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi 0.3.9", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "warp" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41be6df54c97904af01aa23e613d4521eed7ab23537cede692d4058f6449407" +dependencies = [ + "bytes 0.5.6", + "futures", + "headers", + "http 0.2.1", + "hyper", + "log", + "mime", + "mime_guess", + "multipart", + "pin-project 0.4.27", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded 0.6.1", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "tower-service", + "tracing", + "tracing-futures", + "urlencoding", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" +dependencies = [ + "cfg-if 0.1.10", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da" +dependencies = [ + "cfg-if 0.1.10", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d1cdc8b98a557f24733d50a1199c4b0635e465eecba9c45b214544da197f64" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fb9c67be7439ee8ab1b7db502a49c05e51e2835b66796c705134d9b8e1a585" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "web-sys" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "www-authenticate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c62efb8259cda4e4c732287397701237b78daa4c43edcf3e613c8503a6c07dd" +dependencies = [ + "hyperx", + "unicase 1.4.2", + "url 1.7.2", +] + +[[package]] +name = "xattr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +dependencies = [ + "libc", +] + +[[package]] +name = "yaml-rust" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yasna" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de7bff972b4f2a06c85f6d8454b09df153af7e3a4ec2aac81db1b105b684ddb" +dependencies = [ + "chrono", +] From e7de3a0db973e94e3fd05862594e1e13ea974973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Tue, 24 Nov 2020 17:36:13 +0100 Subject: [PATCH 03/22] Added current version of code and started refactoring to remove warnings. --- Cargo.lock | 1 + Cargo.toml | 4 +- src/main.rs | 11 +- src/provider/error.rs | 6 +- src/provider/mod.rs | 38 +++--- src/provider/repository/mod.rs | 14 ++- src/provider/repository/package.rs | 9 +- src/provider/repository/repository.rs | 6 +- .../repository/stackablerepository.rs | 17 ++- src/provider/states/create_config.rs | 110 +++++++++++------- src/provider/states/create_service.rs | 8 +- src/provider/states/download_package.rs | 22 ++-- .../states/download_package_backoff.rs | 8 +- src/provider/states/failed.rs | 36 ++++-- src/provider/states/install_package.rs | 22 ++-- src/provider/states/running.rs | 53 ++++++--- src/provider/states/setup_failed.rs | 2 +- src/provider/states/starting.rs | 99 +++++++++++++++- src/provider/states/stopped.rs | 5 +- src/provider/states/stopping.rs | 3 +- src/provider/states/waiting_config.rs | 8 +- 21 files changed, 320 insertions(+), 162 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c50488..660531e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2420,6 +2420,7 @@ dependencies = [ "tar", "thiserror", "tokio", + "url 2.2.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 85081a3..3b7eb79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ edition = "2018" [dependencies] kubelet = { git="https://github.com/deislabs/krustlet.git", rev="ac218b38ba564de806568e49d9e38aaef9f41537", default-features = true, features= ["derive", "cli"] } oci-distribution = { git="https://github.com/deislabs/krustlet.git", rev="ac218b38ba564de806568e49d9e38aaef9f41537"} -#kubelet = { version="0.5", default-features = true, features= ["derive", "cli"] } k8s-openapi = { version = "0.9", default-features = false, features = ["v1_18"] } kube = { version= "0.42", default-features = false, features = ["native-tls"] } kube-derive = "0.43" @@ -23,4 +22,5 @@ reqwest = "0.10" flate2 = "1.0" tar = "0.4" handlebars = "3.5" -thiserror = "1.0" \ No newline at end of file +thiserror = "1.0" +url = "2.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index afa9a70..b45fa9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ +use std::path::PathBuf; + +use kube::config::Config as KubeConfig; +use kube::config::KubeConfigOptions; use kubelet::config::Config; -use kubelet::store::composite::ComposableStore; -use kubelet::store::oci::FileStore; use kubelet::Kubelet; -use std::sync::Arc; + use crate::provider::StackableProvider; -use kube::config::KubeConfigOptions; -use kube::config::Config as KubeConfig; -use std::path::PathBuf; mod provider; diff --git a/src/provider/error.rs b/src/provider/error.rs index 9698a7d..c622ea3 100644 --- a/src/provider/error.rs +++ b/src/provider/error.rs @@ -1,8 +1,8 @@ -use thiserror::Error; -use k8s_openapi::url; -use crate::provider::repository::package::Package; use handlebars::{RenderError, TemplateError}; +use k8s_openapi::url; +use thiserror::Error; +use crate::provider::repository::package::Package; #[derive(Error, Debug)] pub enum StackableError { diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 1dc727b..b656f3e 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -1,24 +1,22 @@ -use kubelet::provider::Provider; -use kubelet::log::Sender; -use kubelet::pod::Pod; +use std::convert::TryFrom; +use std::fs; +use std::path::PathBuf; +use std::process::Child; -use crate::provider::states::failed::Failed; +use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition; +use kube::{Api, Client}; use kubelet::backoff::ExponentialBackoffStrategy; +use kubelet::log::Sender; use kubelet::node::Builder; -use crate::provider::states::terminated::Terminated; -use crate::provider::states::download_package::Downloading; -use kube::{Client, Api}; +use kubelet::pod::Pod; +use kubelet::provider::Provider; +use log::{debug, error}; + use crate::provider::error::StackableError; -use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition; use crate::provider::error::StackableError::{CrdMissing, PodValidationError}; -use log::{debug, info, error}; -use std::path::PathBuf; -use std::fs; use crate::provider::repository::package::Package; -use std::convert::TryFrom; -use std::sync::Arc; -use tokio::sync::Notify; -use oci_distribution::Reference; +use crate::provider::states::download_package::Downloading; +use crate::provider::states::terminated::Terminated; pub struct StackableProvider { client: Client, @@ -40,6 +38,15 @@ pub struct PodState { config_directory: PathBuf, package_download_backoff_strategy: ExponentialBackoffStrategy, package: Package, + process_handle: Option, +} + +impl PodState { + pub fn take_handle(mut self) -> Option { + let result = self.process_handle; + self.process_handle = None; + result + } } impl StackableProvider { @@ -138,6 +145,7 @@ impl Provider for StackableProvider { config_directory: self.config_directory.clone(), package_download_backoff_strategy: ExponentialBackoffStrategy::default(), package, + process_handle: None, }) } diff --git a/src/provider/repository/mod.rs b/src/provider/repository/mod.rs index 7bbc101..0b71d7d 100644 --- a/src/provider/repository/mod.rs +++ b/src/provider/repository/mod.rs @@ -1,11 +1,13 @@ -use crate::provider::repository::package::Package; -use crate::provider::repository::stackablerepository::StackableRepoProvider; -use kube::{Client, Api}; -use crate::provider::error::StackableError; -use kube::api::ListParams; use std::convert::TryFrom; -use log::{trace, debug, info, error}; + +use kube::{Api, Client}; +use kube::api::ListParams; +use log::{debug, error, info, trace}; + +use crate::provider::error::StackableError; +use crate::provider::repository::package::Package; use crate::provider::repository::repository::Repository; +use crate::provider::repository::stackablerepository::StackableRepoProvider; pub mod package; pub mod repository; diff --git a/src/provider/repository/package.rs b/src/provider/repository/package.rs index 4e33703..0cd9baf 100644 --- a/src/provider/repository/package.rs +++ b/src/provider/repository/package.rs @@ -1,12 +1,13 @@ use std::collections::HashMap; use std::convert::TryFrom; +use std::fmt; + use k8s_openapi::serde_value::DeserializerError; -use serde::{Deserialize, Serialize}; -use crate::provider::error::StackableError::PackageParseError; use oci_distribution::Reference; -use crate::provider::error::StackableError; -use std::fmt; +use serde::{Deserialize, Serialize}; +use crate::provider::error::StackableError; +use crate::provider::error::StackableError::PackageParseError; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Package { diff --git a/src/provider/repository/repository.rs b/src/provider/repository/repository.rs index 1db32f9..24af176 100644 --- a/src/provider/repository/repository.rs +++ b/src/provider/repository/repository.rs @@ -1,7 +1,9 @@ -use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use crate::provider::repository::package::Package; + use kube_derive::CustomResource; +use serde::{Deserialize, Serialize}; + +use crate::provider::repository::package::Package; #[derive(CustomResource, Serialize, Deserialize, Default, Clone, Debug)] #[kube( diff --git a/src/provider/repository/stackablerepository.rs b/src/provider/repository/stackablerepository.rs index 1520b60..41ff962 100644 --- a/src/provider/repository/stackablerepository.rs +++ b/src/provider/repository/stackablerepository.rs @@ -1,21 +1,20 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; +use std::fmt; +use std::fs::File; use std::hash::{Hash, Hasher}; +use std::io::{copy, Cursor}; +use std::path::PathBuf; use kube::api::Meta; +use log::{debug, error, info, trace}; use serde::{Deserialize, Serialize}; -use k8s_openapi::url::{ParseError, Url}; +use url::{ParseError, Url}; -use std::path::PathBuf; -use std::fs::File; -use std::io::{Cursor, copy}; -use crate::provider::repository::package::Package; -use crate::provider::repository::repository::Repository; use crate::provider::error::StackableError; -use log::{trace, debug, info, error}; -use std::fmt; use crate::provider::error::StackableError::PackageNotFound; - +use crate::provider::repository::package::Package; +use crate::provider::repository::repository::Repository; #[derive(Debug, Clone)] pub struct StackableRepoProvider { diff --git a/src/provider/states/create_config.rs b/src/provider/states/create_config.rs index 5155571..ffa6b23 100644 --- a/src/provider/states/create_config.rs +++ b/src/provider/states/create_config.rs @@ -1,23 +1,25 @@ +use std::collections::BTreeMap; +use std::fs; +use std::fs::read_to_string; +use std::path::PathBuf; + +use handlebars::Handlebars; +use k8s_openapi::api::core::v1::ConfigMap; +use kube::{Api, Client}; +use kube::api::ListParams; +use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; +use kubelet::state::prelude::*; +use log::{debug, error, info, trace, warn}; + +use crate::fail_fatal; use crate::provider::error::StackableError; use crate::provider::error::StackableError::{PodValidationError, RuntimeError}; -use crate::fail_fatal; +use crate::provider::PodState; use crate::provider::states::create_service::CreatingService; use crate::provider::states::failed::Failed; use crate::provider::states::running::Running; use crate::provider::states::setup_failed::SetupFailed; -use crate::provider::PodState; -use handlebars::{Handlebars, RenderError}; -use k8s_openapi::api::core::v1::{ConfigMap, Volume, VolumeMount}; -use kube::api::ListParams; -use kube::{Api, Client}; -use kubelet::pod::Pod; -use kubelet::state::prelude::*; -use kubelet::state::{State, Transition}; -use log::{trace, debug, error, info, warn}; -use std::collections::{BTreeMap, HashMap}; -use std::fs; -use std::path::{PathBuf, Path}; -use std::fs::read_to_string; use crate::provider::states::waiting_config::WaitingConfigMap; #[derive(Default, Debug, TransitionTo)] @@ -27,7 +29,7 @@ pub struct CreatingConfig { } impl CreatingConfig { - fn render_config_template( + pub fn render_config_template( data: BTreeMap, template: String, ) -> Result { @@ -44,24 +46,28 @@ impl CreatingConfig { Ok(handlebars.render("t1", &data)?) } - fn create_render_data(pod_state: &PodState) -> BTreeMap { + pub fn create_render_data(pod_state: &PodState) -> BTreeMap { let mut render_data = BTreeMap::new(); let directory_name = pod_state.package.get_directory_name(); - if let Ok(package_dir) = &pod_state.parcel_directory.join(&directory_name).into_os_string().into_string() { - render_data.insert( - String::from("packageroot"), - String::from(package_dir), - ); + if let Ok(package_dir) = &pod_state + .parcel_directory + .join(&directory_name) + .into_os_string() + .into_string() + { + render_data.insert(String::from("packageroot"), String::from(package_dir)); } else { warn!("Unable to parse value for package directory as UTF8") } - if let Ok(conf_dir) = &pod_state.config_directory.join(&directory_name).into_os_string().into_string() { - render_data.insert( - String::from("configroot"), - String::from(conf_dir), - ); + if let Ok(conf_dir) = &pod_state + .config_directory + .join(&directory_name) + .into_os_string() + .into_string() + { + render_data.insert(String::from("configroot"), String::from(conf_dir)); } else { warn!("Unable to parse value for config directory as UTF8"); } @@ -121,26 +127,28 @@ impl CreatingConfig { map: ConfigMap, target_directory: PathBuf, template_data: &BTreeMap, - ) -> Result<(), StackableError> - { + ) -> Result<(), StackableError> { + let config_map_name = map.metadata.name.unwrap_or(String::from("undefined")); debug!( "applying configmap {} to directory {:?}", - map.metadata.name.unwrap_or(String::from("undefined")), - target_directory + &config_map_name, target_directory ); if !(&target_directory.is_dir()) { info!("creating config directory {:?}", target_directory); fs::create_dir_all(&target_directory)?; } if let Some(data) = map.data { + debug!("Map contained keys: {:?}", &data.keys()); for key in data.keys() { - debug!("found key: {} in configmap", key); + debug!("found key: {} in configmap {}", key, &config_map_name); if let Some(content) = data.get(key) { trace!("content of key: {}", &content); + debug!("rendering"); let rendered_content = CreatingConfig::render_config_template( template_data.clone(), content.clone(), )?; + debug!("done rendering"); let target_file = target_directory.join(&key); // TODO: compare existing file with intended state @@ -161,13 +169,23 @@ impl CreatingConfig { info!("No content found for key {}", key); } } + } else { + debug!("No data found in ConfigMap.."); } Ok(()) } fn needs_update(target_file: &PathBuf, content: &str) -> Result { - let current_content = read_to_string(target_file)?; - Ok(current_content.ne(content)) + if target_file.is_file() { + let current_content = read_to_string(target_file)?; + debug!("Compared config file {:?} with result of", target_file); + return Ok(current_content.ne(content)); + } + debug!( + "Target config file {:?} doesn't exist, no need to compare.", + target_file + ); + Ok(true) } } @@ -187,15 +205,20 @@ impl State for CreatingConfig { // Check if all required config maps have been created in the api-server let referenced_config_maps = self.get_config_maps(_pod).await; - let missing_config_maps = self.missing_config_maps(client.clone(), referenced_config_maps).await; + let missing_config_maps = self + .missing_config_maps(client.clone(), referenced_config_maps) + .await; if !missing_config_maps.is_empty() { // not all configmaps are present info!("Missing config maps, waiting.."); - return Transition::next(self, WaitingConfigMap { missing_config_maps }) - + return Transition::next( + self, + WaitingConfigMap { + missing_config_maps, + }, + ); } - debug!("Entering state \"creating config\" for service {}", name); let containers = _pod.containers(); if containers.len().ne(&1) { @@ -215,16 +238,19 @@ impl State for CreatingConfig { for mount in mounts { for volume in volumes { if mount.name.eq(&volume.name) { - let target_dir = - target_directory.join(&mount.mount_path.trim_start_matches('/')); + let target_dir = target_directory.join(&mount.mount_path); if let Some(config_map) = &volume.config_map { if let Some(map_name) = &config_map.name { if let Ok(map) = self .retrieve_config_map(client.clone(), map_name.to_string()) .await { - debug!("found config map: {:?}", config_map); - self.apply_config_map(map, target_dir, &CreatingConfig::create_render_data(pod_state)); + debug!("found config map: {:?} - applying", config_map); + self.apply_config_map( + map, + target_dir, + &CreatingConfig::create_render_data(pod_state), + ); } } } else { @@ -235,7 +261,7 @@ impl State for CreatingConfig { } }; } - + debug!("Transitioning to service creation"); Transition::next(self, CreatingService) } diff --git a/src/provider/states/create_service.rs b/src/provider/states/create_service.rs index 1ecd5c4..f96e815 100644 --- a/src/provider/states/create_service.rs +++ b/src/provider/states/create_service.rs @@ -1,12 +1,12 @@ -use kubelet::state::{State, Transition}; use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; + use crate::provider::PodState; -use crate::provider::states::running::Running; use crate::provider::states::failed::Failed; -use crate::provider::states::starting::Starting; +use crate::provider::states::running::Running; use crate::provider::states::setup_failed::SetupFailed; - +use crate::provider::states::starting::Starting; #[derive(Default, Debug, TransitionTo)] #[transition_to(Starting, SetupFailed)] diff --git a/src/provider/states/download_package.rs b/src/provider/states/download_package.rs index 849761b..0d3d3d3 100644 --- a/src/provider/states/download_package.rs +++ b/src/provider/states/download_package.rs @@ -1,23 +1,15 @@ -use kubelet::state::{State, Transition}; +use std::path::{Path, PathBuf}; + use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use log::{debug, error, info, warn}; + use crate::provider::PodState; -use crate::provider::states::running::Running; -use crate::provider::states::failed::Failed; -use crate::provider::states::install_package::Installing; -use crate::provider::states::setup_failed::SetupFailed; -use crate::provider::error::StackableError::PodValidationError; -use crate::fail_fatal; -use kube::api::Meta; -use k8s_openapi::api::core::v1::PodSpec; -use crate::provider::repository::package::Package; -use crate::provider::error::StackableError; -use kubelet::container::Container; -use std::convert::TryFrom; -use log::{debug, info, warn, error}; use crate::provider::repository::find_repository; +use crate::provider::repository::package::Package; use crate::provider::states::download_package_backoff::DownloadingBackoff; -use std::path::{Path, PathBuf}; +use crate::provider::states::install_package::Installing; #[derive(Default, Debug, TransitionTo)] #[transition_to(Installing, DownloadingBackoff)] diff --git a/src/provider/states/download_package_backoff.rs b/src/provider/states/download_package_backoff.rs index 531946c..4c554ba 100644 --- a/src/provider/states/download_package_backoff.rs +++ b/src/provider/states/download_package_backoff.rs @@ -1,11 +1,11 @@ +use kubelet::backoff::BackoffStrategy; use kubelet::state::prelude::*; +use log::{debug, error, info}; use crate::provider::PodState; -use crate::provider::states::install_package::Installing; -use crate::provider::states::download_package::Downloading; -use kubelet::backoff::BackoffStrategy; use crate::provider::repository::package::Package; -use log::{debug, info, error}; +use crate::provider::states::download_package::Downloading; +use crate::provider::states::install_package::Installing; #[derive(Debug, TransitionTo)] #[transition_to(Downloading)] diff --git a/src/provider/states/failed.rs b/src/provider/states/failed.rs index b130c8f..80f94de 100644 --- a/src/provider/states/failed.rs +++ b/src/provider/states/failed.rs @@ -1,26 +1,46 @@ use kubelet::state::prelude::*; - +use log::{debug, error, info, trace, warn}; use crate::provider::PodState; use crate::provider::states::install_package::Installing; +use crate::provider::states::starting::Starting; #[derive(Default, Debug, TransitionTo)] -#[transition_to(Installing)] +#[transition_to(Starting, Installing)] /// The Pod failed to run. // If we manually implement, we can allow for arguments. pub struct Failed { pub message: String, } +impl Failed { + fn restart_enabled(&self, pod : &Pod) -> bool { + if let Some(pod_spec) = &pod.as_kube_pod().spec { + if let Some(restart_policy) = &pod_spec.restart_policy { + return restart_policy.eq("Always"); + } + } + false + } +} + #[async_trait::async_trait] impl State for Failed { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - println!("failed"); - Transition::next(self, Installing{ - download_directory: pod_state.download_directory.clone(), - parcel_directory: pod_state.parcel_directory.clone(), - package: pod_state.package.clone() - }) + info!("Process entered failed state"); + if self.restart_enabled(_pod) { + debug!("Restart poliy is set to restart, starting..."); + return Transition::next(self, Starting {}); + } else { + debug!("Restart is disabled for process."); + } + //tokio::time::delay_for(std::time::Duration::from_secs(2)).await; + // T//ransition::next(self, Installing{ + // download_directory: pod_state.download_directory.clone(), + // parcel_directory: pod_state.parcel_directory.clone(), + // package: pod_state.package.clone() + //}) + Transition::Complete(Ok(())) } async fn json_status( diff --git a/src/provider/states/install_package.rs b/src/provider/states/install_package.rs index 4acc2eb..e5492ed 100644 --- a/src/provider/states/install_package.rs +++ b/src/provider/states/install_package.rs @@ -1,20 +1,18 @@ -use kubelet::state::{State, Transition}; +use std::fs::File; +use std::path::{Path, PathBuf}; + +use flate2::read::GzDecoder; use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use log::{debug, info}; +use tar::Archive; + +use crate::provider::error::StackableError; use crate::provider::PodState; -use crate::provider::states::running::Running; -use crate::provider::states::failed::Failed; +use crate::provider::repository::package::Package; use crate::provider::states::create_config::CreatingConfig; use crate::provider::states::setup_failed::SetupFailed; -use log::{debug, info}; -use kube::api::Meta; -use k8s_openapi::api::core::v1::PodSpec; -use crate::provider::repository::package::Package; -use std::path::{Path, PathBuf}; -use crate::provider::error::StackableError; -use std::fs::File; -use flate2::read::GzDecoder; -use tar::Archive; #[derive(Debug, TransitionTo)] #[transition_to(CreatingConfig, SetupFailed)] diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs index 1151b6d..dc559d2 100644 --- a/src/provider/states/running.rs +++ b/src/provider/states/running.rs @@ -1,44 +1,63 @@ -use kubelet::state::{State, Transition}; +use std::process::Child; +use std::sync::Arc; +use std::time::Duration; + +use kubelet::container::ContainerKey; use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use log::{debug, error, info, warn}; +use tokio::time::timeout; + +use crate::provider::error::StackableError; use crate::provider::PodState; use crate::provider::states::failed::Failed; -use crate::provider::states::stopping::Stopping; use crate::provider::states::install_package::Installing; -use kubelet::container::ContainerKey; -use log::{debug, info, warn, error}; -use std::sync::Arc; -use std::time::Duration; -use tokio::time::timeout; +use crate::provider::states::stopping::Stopping; -#[derive(Default, Debug, TransitionTo)] +#[derive(Debug, TransitionTo)] #[transition_to(Stopping, Failed, Running, Installing)] -pub struct Running; - +pub struct Running { + pub process_handle: Option, +} #[async_trait::async_trait] impl State for Running { - async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + async fn next(mut self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + + debug!("waiting"); + let mut handle = std::mem::replace(&mut self.process_handle, None).unwrap(); /*while let Ok(_) = timeout(Duration::from_millis(100), changed.notified()).await { debug!("drained a waiting notification"); - } - debug!("done draining"); - */ + }*/ + // debug!("done draining"); + loop { println!("running"); tokio::select! { - _ = tokio::time::delay_for(std::time::Duration::from_secs(10)) => { + /*_ = changed.notified() => { + debug!("pod changed"); + break; + },*/ + _ = tokio::time::delay_for(std::time::Duration::from_secs(1)) => { debug!("timer expired"); - continue; } } + match handle.try_wait() { + Ok(None) => debug!("Still running"), + _ => { + error!("died"); + return Transition::next(self, Failed { message: "process died".to_string() }) + } + + } } Transition::next(self, Installing{ download_directory: pod_state.download_directory.clone(), parcel_directory: pod_state.parcel_directory.clone(), package: pod_state.package.clone() }) - } + } async fn json_status( &self, diff --git a/src/provider/states/setup_failed.rs b/src/provider/states/setup_failed.rs index 4f607b8..3903209 100644 --- a/src/provider/states/setup_failed.rs +++ b/src/provider/states/setup_failed.rs @@ -1,8 +1,8 @@ use kubelet::state::prelude::*; +use log::{debug, error, info}; use crate::provider::PodState; use crate::provider::states::download_package::Downloading; -use log::{debug, info, error}; #[derive(Default, Debug, TransitionTo)] #[transition_to(Downloading)] diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 7618b0e..26ea089 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -1,7 +1,14 @@ -use kubelet::state::{State, Transition}; +use std::ffi::OsStr; +use std::process::{Command, Stdio}; + use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use log::{debug, error, info, trace, warn}; +use tokio::time::Duration; + use crate::provider::PodState; +use crate::provider::states::create_config::CreatingConfig; use crate::provider::states::failed::Failed; use crate::provider::states::running::Running; @@ -9,12 +16,94 @@ use crate::provider::states::running::Running; #[transition_to(Running, Failed)] pub struct Starting; - #[async_trait::async_trait] impl State for Starting { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - println!("starting"); - Transition::next(self, Running) + let container = _pod.containers()[0].clone(); + let template_data = CreatingConfig::create_render_data(&pod_state); + if let Some(mut command) = container.command().clone() { + debug!("Reversing {:?}", &command); + command.reverse(); + debug!("Processing {:?}", &command); + if let Some(binary) = command.pop() { + let binary = pod_state + .parcel_directory + .join(pod_state.package.clone().get_directory_name()) + .join(binary); + + let binary = OsStr::new(&binary); + command.reverse(); + + let os_args: Vec = command + .iter() + .map(|s| { + CreatingConfig::render_config_template( + template_data.clone(), + String::from(s), + ) + .unwrap() + }) + .collect(); + + debug!( + "Starting command: {:?} with arguments {:?}", + binary, os_args + ); + let start_result = Command::new(binary) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .args(&os_args) + .spawn(); + + match start_result { + Ok(mut child) => { + info!( + "Successfully executed command \"{:?}\" with args {:?}", + binary, &os_args + ); + debug!("Waiting if startup fails.."); + for i in 1..10 { + tokio::time::delay_for(Duration::from_secs(1)).await; + if let Ok(None) = child.try_wait() { + trace!("Process still alive after {} seconds ..", i); + } else { + error!("Process died after {} seconds during startup!", i); + return Transition::next( + self, + Failed { + message: "process failed during startup".to_string(), + }, + ); + } + } + //pod_state.process_handle = Some(child); + return Transition::next( + self, + Running { + process_handle: Some(child), + }, + ); + } + Err(error) => { + let error_message = format!("Failed to start process with error {}", error); + error!("{}", error_message); + return Transition::next( + self, + Failed { + message: error_message, + }, + ); + } + } + } + } + error!("No command found, not starting anything.."); + return Transition::next( + self, + Failed { + message: "no command object present, failing process".to_string(), + }, + ); } async fn json_status( @@ -24,4 +113,4 @@ impl State for Starting { ) -> anyhow::Result { make_status(Phase::Pending, &"status:running") } -} \ No newline at end of file +} diff --git a/src/provider/states/stopped.rs b/src/provider/states/stopped.rs index 14357ca..00f21dd 100644 --- a/src/provider/states/stopped.rs +++ b/src/provider/states/stopped.rs @@ -1,10 +1,11 @@ -use kubelet::state::{State, Transition}; use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; + use crate::provider::PodState; use crate::provider::states::failed::Failed; -use crate::provider::states::stopping::Stopping; use crate::provider::states::starting::Starting; +use crate::provider::states::stopping::Stopping; #[derive(Default, Debug, TransitionTo)] #[transition_to(Starting)] diff --git a/src/provider/states/stopping.rs b/src/provider/states/stopping.rs index 458c38d..00b2c3c 100644 --- a/src/provider/states/stopping.rs +++ b/src/provider/states/stopping.rs @@ -1,6 +1,7 @@ -use kubelet::state::{State, Transition}; use kubelet::pod::Pod; +use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; + use crate::provider::PodState; use crate::provider::states::failed::Failed; use crate::provider::states::stopped::Stopped; diff --git a/src/provider/states/waiting_config.rs b/src/provider/states/waiting_config.rs index f72af78..0b20dbb 100644 --- a/src/provider/states/waiting_config.rs +++ b/src/provider/states/waiting_config.rs @@ -1,12 +1,12 @@ +use kubelet::backoff::BackoffStrategy; use kubelet::state::prelude::*; +use log::{debug, error, info}; use crate::provider::PodState; -use crate::provider::states::install_package::Installing; -use crate::provider::states::download_package::Downloading; -use kubelet::backoff::BackoffStrategy; use crate::provider::repository::package::Package; -use log::{debug, info, error}; use crate::provider::states::create_config::CreatingConfig; +use crate::provider::states::download_package::Downloading; +use crate::provider::states::install_package::Installing; #[derive(Debug, TransitionTo)] #[transition_to(CreatingConfig)] From cc38098f8d2313176c7609e59196c5655269d399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Tue, 24 Nov 2020 17:49:29 +0100 Subject: [PATCH 04/22] Fixed majority of linter warnings. --- src/main.rs | 5 --- src/provider/mod.rs | 42 ++++++++++--------- src/provider/repository/mod.rs | 2 +- src/provider/repository/package.rs | 3 -- src/provider/repository/repository.rs | 2 - .../repository/stackablerepository.rs | 8 ++-- src/provider/states/create_config.rs | 7 +--- src/provider/states/create_service.rs | 2 - .../states/download_package_backoff.rs | 3 +- src/provider/states/failed.rs | 2 +- src/provider/states/running.rs | 12 +----- src/provider/states/setup_failed.rs | 2 +- src/provider/states/starting.rs | 2 +- src/provider/states/stopped.rs | 2 - src/provider/states/terminated.rs | 1 - src/provider/states/waiting_config.rs | 5 +-- 16 files changed, 35 insertions(+), 65 deletions(-) diff --git a/src/main.rs b/src/main.rs index b45fa9c..c7d60aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,8 +32,3 @@ async fn main() -> anyhow::Result<()> { let kubelet = Kubelet::new(provider, kubeconfig, config).await?; kubelet.start().await } - - -fn notify_bootstrap(message: String) { - println!("BOOTSTRAP: {}", message); -} diff --git a/src/provider/mod.rs b/src/provider/mod.rs index b656f3e..28339db 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -26,10 +26,9 @@ pub struct StackableProvider { pub const CRDS: &'static [&'static str] = &["repositories.stable.stackable.de"]; - -mod states; -mod repository; mod error; +mod repository; +mod states; pub struct PodState { client: Client, @@ -41,16 +40,12 @@ pub struct PodState { process_handle: Option, } -impl PodState { - pub fn take_handle(mut self) -> Option { - let result = self.process_handle; - self.process_handle = None; - result - } -} - impl StackableProvider { - pub async fn new(client: Client, parcel_directory: PathBuf, config_directory: PathBuf) -> Result { + pub async fn new( + client: Client, + parcel_directory: PathBuf, + config_directory: PathBuf, + ) -> Result { let provider = StackableProvider { client, parcel_directory, @@ -68,15 +63,19 @@ impl StackableProvider { fn get_package(&self, pod: &Pod) -> Result { let containers = pod.containers(); - if (containers.len().ne(&1)) { - let e = PodValidationError { msg: String::from("Size of containers list in PodSpec has to be exactly 1") }; + if containers.len().ne(&1) { + let e = PodValidationError { + msg: String::from("Size of containers list in PodSpec has to be exactly 1"), + }; return Err(e); } else { // List has exactly one value, try to parse this if let Ok(Some(reference)) = containers[0].image() { return Package::try_from(reference); } else { - let e = PodValidationError { msg: String::from("Unable to get package reference from pod") }; + let e = PodValidationError { + msg: String::from("Unable to get package reference from pod"), + }; return Err(e); } } @@ -94,9 +93,7 @@ impl StackableProvider { error!("Missing required CRD: \"{}\"", crd); missing_crds.push(String::from(*crd)) } - _ => { - debug!("Found registered crd: {}", crd) - } + _ => debug!("Found registered crd: {}", crd), } } missing_crds @@ -115,7 +112,6 @@ impl Provider for StackableProvider { type InitialState = Downloading; type TerminatedState = Terminated; - const ARCH: &'static str = "stackable-linux"; async fn node(&self, builder: &mut Builder) -> anyhow::Result<()> { @@ -149,7 +145,13 @@ impl Provider for StackableProvider { }) } - async fn logs(&self, namespace: String, pod: String, container: String, sender: Sender) -> anyhow::Result<()> { + async fn logs( + &self, + namespace: String, + pod: String, + container: String, + sender: Sender, + ) -> anyhow::Result<()> { Ok(()) } } diff --git a/src/provider/repository/mod.rs b/src/provider/repository/mod.rs index 0b71d7d..b18a2a1 100644 --- a/src/provider/repository/mod.rs +++ b/src/provider/repository/mod.rs @@ -2,7 +2,7 @@ use std::convert::TryFrom; use kube::{Api, Client}; use kube::api::ListParams; -use log::{debug, error, info, trace}; +use log::{debug, trace}; use crate::provider::error::StackableError; use crate::provider::repository::package::Package; diff --git a/src/provider/repository/package.rs b/src/provider/repository/package.rs index 0cd9baf..ce56be3 100644 --- a/src/provider/repository/package.rs +++ b/src/provider/repository/package.rs @@ -1,13 +1,10 @@ -use std::collections::HashMap; use std::convert::TryFrom; use std::fmt; -use k8s_openapi::serde_value::DeserializerError; use oci_distribution::Reference; use serde::{Deserialize, Serialize}; use crate::provider::error::StackableError; -use crate::provider::error::StackableError::PackageParseError; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Package { diff --git a/src/provider/repository/repository.rs b/src/provider/repository/repository.rs index 24af176..22b3c16 100644 --- a/src/provider/repository/repository.rs +++ b/src/provider/repository/repository.rs @@ -3,8 +3,6 @@ use std::collections::HashMap; use kube_derive::CustomResource; use serde::{Deserialize, Serialize}; -use crate::provider::repository::package::Package; - #[derive(CustomResource, Serialize, Deserialize, Default, Clone, Debug)] #[kube( kind = "Repository", diff --git a/src/provider/repository/stackablerepository.rs b/src/provider/repository/stackablerepository.rs index 41ff962..85c1e09 100644 --- a/src/provider/repository/stackablerepository.rs +++ b/src/provider/repository/stackablerepository.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::fmt; use std::fs::File; use std::hash::{Hash, Hasher}; @@ -7,9 +7,9 @@ use std::io::{copy, Cursor}; use std::path::PathBuf; use kube::api::Meta; -use log::{debug, error, info, trace}; +use log::{debug, trace}; use serde::{Deserialize, Serialize}; -use url::{ParseError, Url}; +use url::Url; use crate::provider::error::StackableError; use crate::provider::error::StackableError::PackageNotFound; @@ -92,7 +92,7 @@ impl StackableRepoProvider { let stackable_package = self.get_package(package.clone()).await?; let download_link = Url::parse(&stackable_package.link)?; - let mut response = reqwest::get(download_link).await?; + let response = reqwest::get(download_link).await?; let mut content = Cursor::new(response.bytes().await?); diff --git a/src/provider/states/create_config.rs b/src/provider/states/create_config.rs index ffa6b23..1843649 100644 --- a/src/provider/states/create_config.rs +++ b/src/provider/states/create_config.rs @@ -6,7 +6,6 @@ use std::path::PathBuf; use handlebars::Handlebars; use k8s_openapi::api::core::v1::ConfigMap; use kube::{Api, Client}; -use kube::api::ListParams; use kubelet::pod::Pod; use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; @@ -14,11 +13,9 @@ use log::{debug, error, info, trace, warn}; use crate::fail_fatal; use crate::provider::error::StackableError; -use crate::provider::error::StackableError::{PodValidationError, RuntimeError}; +use crate::provider::error::StackableError::PodValidationError; use crate::provider::PodState; use crate::provider::states::create_service::CreatingService; -use crate::provider::states::failed::Failed; -use crate::provider::states::running::Running; use crate::provider::states::setup_failed::SetupFailed; use crate::provider::states::waiting_config::WaitingConfigMap; @@ -84,7 +81,7 @@ impl CreatingConfig { match result { Ok(configmap) => {} Err(e) => { - debug!("ConfigMap {} not found", &map); + debug!("ConfigMap {} not found due to error {:?}", &map, e); missing_configmaps.push(String::from(map)); } } diff --git a/src/provider/states/create_service.rs b/src/provider/states/create_service.rs index f96e815..230b39a 100644 --- a/src/provider/states/create_service.rs +++ b/src/provider/states/create_service.rs @@ -3,8 +3,6 @@ use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; use crate::provider::PodState; -use crate::provider::states::failed::Failed; -use crate::provider::states::running::Running; use crate::provider::states::setup_failed::SetupFailed; use crate::provider::states::starting::Starting; diff --git a/src/provider/states/download_package_backoff.rs b/src/provider/states/download_package_backoff.rs index 4c554ba..405da98 100644 --- a/src/provider/states/download_package_backoff.rs +++ b/src/provider/states/download_package_backoff.rs @@ -1,11 +1,10 @@ use kubelet::backoff::BackoffStrategy; use kubelet::state::prelude::*; -use log::{debug, error, info}; +use log::info; use crate::provider::PodState; use crate::provider::repository::package::Package; use crate::provider::states::download_package::Downloading; -use crate::provider::states::install_package::Installing; #[derive(Debug, TransitionTo)] #[transition_to(Downloading)] diff --git a/src/provider/states/failed.rs b/src/provider/states/failed.rs index 80f94de..8ca0310 100644 --- a/src/provider/states/failed.rs +++ b/src/provider/states/failed.rs @@ -1,5 +1,5 @@ use kubelet::state::prelude::*; -use log::{debug, error, info, trace, warn}; +use log::{debug, info}; use crate::provider::PodState; use crate::provider::states::install_package::Installing; diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs index dc559d2..58ef6bb 100644 --- a/src/provider/states/running.rs +++ b/src/provider/states/running.rs @@ -1,15 +1,10 @@ use std::process::Child; -use std::sync::Arc; -use std::time::Duration; -use kubelet::container::ContainerKey; use kubelet::pod::Pod; use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; -use log::{debug, error, info, warn}; -use tokio::time::timeout; +use log::{debug, error}; -use crate::provider::error::StackableError; use crate::provider::PodState; use crate::provider::states::failed::Failed; use crate::provider::states::install_package::Installing; @@ -52,11 +47,6 @@ impl State for Running { } } - Transition::next(self, Installing{ - download_directory: pod_state.download_directory.clone(), - parcel_directory: pod_state.parcel_directory.clone(), - package: pod_state.package.clone() - }) } async fn json_status( diff --git a/src/provider/states/setup_failed.rs b/src/provider/states/setup_failed.rs index 3903209..b6585a4 100644 --- a/src/provider/states/setup_failed.rs +++ b/src/provider/states/setup_failed.rs @@ -1,5 +1,5 @@ use kubelet::state::prelude::*; -use log::{debug, error, info}; +use log::{error, info}; use crate::provider::PodState; use crate::provider::states::download_package::Downloading; diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 26ea089..8e06c1a 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -4,7 +4,7 @@ use std::process::{Command, Stdio}; use kubelet::pod::Pod; use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; -use log::{debug, error, info, trace, warn}; +use log::{debug, error, info, trace}; use tokio::time::Duration; use crate::provider::PodState; diff --git a/src/provider/states/stopped.rs b/src/provider/states/stopped.rs index 00f21dd..f789cd2 100644 --- a/src/provider/states/stopped.rs +++ b/src/provider/states/stopped.rs @@ -3,9 +3,7 @@ use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; use crate::provider::PodState; -use crate::provider::states::failed::Failed; use crate::provider::states::starting::Starting; -use crate::provider::states::stopping::Stopping; #[derive(Default, Debug, TransitionTo)] #[transition_to(Starting)] diff --git a/src/provider/states/terminated.rs b/src/provider/states/terminated.rs index 5b2ce69..145598c 100644 --- a/src/provider/states/terminated.rs +++ b/src/provider/states/terminated.rs @@ -1,7 +1,6 @@ use kubelet::state::prelude::*; use crate::provider::PodState; -use crate::provider::states::install_package::Installing; #[derive(Default, Debug)] /// The Pod failed to run. diff --git a/src/provider/states/waiting_config.rs b/src/provider/states/waiting_config.rs index 0b20dbb..1dab67b 100644 --- a/src/provider/states/waiting_config.rs +++ b/src/provider/states/waiting_config.rs @@ -1,12 +1,9 @@ use kubelet::backoff::BackoffStrategy; use kubelet::state::prelude::*; -use log::{debug, error, info}; +use log::info; use crate::provider::PodState; -use crate::provider::repository::package::Package; use crate::provider::states::create_config::CreatingConfig; -use crate::provider::states::download_package::Downloading; -use crate::provider::states::install_package::Installing; #[derive(Debug, TransitionTo)] #[transition_to(CreatingConfig)] From 6570313a73b261f5c359b7e0f2cda1bb806578c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Tue, 24 Nov 2020 21:50:15 +0100 Subject: [PATCH 05/22] Fixed rest of linter warnings except "unused variable" ones. --- src/main.rs | 4 +- src/provider/mod.rs | 3 + src/provider/states/create_config.rs | 20 ++++- src/provider/states/create_service.rs | 12 ++- src/provider/states/download_package.rs | 98 ++++++++++++++++++------- src/provider/states/failed.rs | 12 +-- src/provider/states/install_package.rs | 43 ++++++++--- src/provider/states/running.rs | 37 ++++++---- src/provider/states/stopped.rs | 20 +++-- src/provider/states/stopping.rs | 16 ++-- src/provider/states/terminated.rs | 3 +- 11 files changed, 190 insertions(+), 78 deletions(-) diff --git a/src/main.rs b/src/main.rs index c7d60aa..1c5c605 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,8 +23,8 @@ async fn main() -> anyhow::Result<()> { .await .expect("Failed to create Kubernetes Client!"); - let parcel_directory = PathBuf::from("/home/sliebau/IdeaProjects/krustlet/work/parcels"); - let config_directory = PathBuf::from("/home/sliebau/IdeaProjects/krustlet/work/config"); + let parcel_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/parcels"); + let config_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/config"); let provider = StackableProvider::new(kube::Client::new(kubeconfig.clone()), parcel_directory, config_directory) .await .expect("Error initializing provider."); diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 28339db..6a9e586 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -36,6 +36,7 @@ pub struct PodState { download_directory: PathBuf, config_directory: PathBuf, package_download_backoff_strategy: ExponentialBackoffStrategy, + service_name: String, package: Package, process_handle: Option, } @@ -122,6 +123,7 @@ impl Provider for StackableProvider { } async fn initialize_pod_state(&self, pod: &Pod) -> anyhow::Result { + let service_name = pod.name(); let parcel_directory = self.parcel_directory.clone(); let download_directory = parcel_directory.join("_download"); let config_directory = self.config_directory.clone(); @@ -140,6 +142,7 @@ impl Provider for StackableProvider { download_directory, config_directory: self.config_directory.clone(), package_download_backoff_strategy: ExponentialBackoffStrategy::default(), + service_name: String::from(service_name), package, process_handle: None, }) diff --git a/src/provider/states/create_config.rs b/src/provider/states/create_config.rs index 1843649..65abde6 100644 --- a/src/provider/states/create_config.rs +++ b/src/provider/states/create_config.rs @@ -7,17 +7,17 @@ use handlebars::Handlebars; use k8s_openapi::api::core::v1::ConfigMap; use kube::{Api, Client}; use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; use log::{debug, error, info, trace, warn}; use crate::fail_fatal; use crate::provider::error::StackableError; use crate::provider::error::StackableError::PodValidationError; -use crate::provider::PodState; use crate::provider::states::create_service::CreatingService; use crate::provider::states::setup_failed::SetupFailed; use crate::provider::states::waiting_config::WaitingConfigMap; +use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(CreatingService, SetupFailed, WaitingConfigMap)] @@ -243,11 +243,23 @@ impl State for CreatingConfig { .await { debug!("found config map: {:?} - applying", config_map); - self.apply_config_map( + match self.apply_config_map( map, target_dir, &CreatingConfig::create_render_data(pod_state), - ); + ) { + Err(e) => { + return Transition::next( + self, + SetupFailed { + message: + "Failed to create file from config map!" + .to_string(), + }, + ); + } + _ => {} + } } } } else { diff --git a/src/provider/states/create_service.rs b/src/provider/states/create_service.rs index 230b39a..a773a2f 100644 --- a/src/provider/states/create_service.rs +++ b/src/provider/states/create_service.rs @@ -1,10 +1,11 @@ use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; +use log::info; -use crate::provider::PodState; use crate::provider::states::setup_failed::SetupFailed; use crate::provider::states::starting::Starting; +use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(Starting, SetupFailed)] @@ -13,7 +14,10 @@ pub struct CreatingService; #[async_trait::async_trait] impl State for CreatingService { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - println!("creating service"); + info!( + "Creating service unit for service {}", + &pod_state.service_name + ); Transition::next(self, Starting) } @@ -24,4 +28,4 @@ impl State for CreatingService { ) -> anyhow::Result { make_status(Phase::Pending, &"status:initializing") } -} \ No newline at end of file +} diff --git a/src/provider/states/download_package.rs b/src/provider/states/download_package.rs index 0d3d3d3..e98272b 100644 --- a/src/provider/states/download_package.rs +++ b/src/provider/states/download_package.rs @@ -1,25 +1,32 @@ use std::path::{Path, PathBuf}; use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; use log::{debug, error, info, warn}; -use crate::provider::PodState; use crate::provider::repository::find_repository; use crate::provider::repository::package::Package; use crate::provider::states::download_package_backoff::DownloadingBackoff; use crate::provider::states::install_package::Installing; +use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(Installing, DownloadingBackoff)] pub struct Downloading; impl Downloading { - fn package_downloaded>(&self, package: T, download_directory: PathBuf) -> bool { + fn package_downloaded>( + &self, + package: T, + download_directory: PathBuf, + ) -> bool { let package = package.into(); let package_file_name = download_directory.join(package.get_file_name()); - debug!("Checking if package {} has already been downloaded to {:?}", package, package_file_name); + debug!( + "Checking if package {} has already been downloaded to {:?}", + package, package_file_name + ); Path::new(&package_file_name).exists() } } @@ -30,14 +37,23 @@ impl State for Downloading { let package = pod_state.package.clone(); info!("Looking for package: {} in known repositories", &package); - debug!("Checking if package {} has already been downloaded.", package); + debug!( + "Checking if package {} has already been downloaded.", + package + ); if self.package_downloaded(package.clone(), pod_state.download_directory.clone()) { - info!("Package {} has already been downloaded to {:?}, continuing with installation", package, pod_state.download_directory); - return Transition::next(self, Installing { - download_directory: pod_state.download_directory.clone(), - parcel_directory: pod_state.parcel_directory.clone(), - package: package.clone(), - }); + info!( + "Package {} has already been downloaded to {:?}, continuing with installation", + package, pod_state.download_directory + ); + return Transition::next( + self, + Installing { + download_directory: pod_state.download_directory.clone(), + parcel_directory: pod_state.parcel_directory.clone(), + package: package.clone(), + }, + ); } let repo = find_repository(pod_state.client.clone(), &package, None).await; match repo { @@ -45,35 +61,68 @@ impl State for Downloading { // We found a repository providing the package, proceed with download // The repository has already downloaded its metadata it this time, as that // was used to check whether it provides the package - info!("Starting download of package {} from repository {}", &package, &repo); + info!( + "Starting download of package {} from repository {}", + &package, &repo + ); let download_directory = pod_state.download_directory.clone(); - let download_result = repo.download_package(&package, download_directory.clone()).await; + let download_result = repo + .download_package(&package, download_directory.clone()) + .await; match download_result { Ok(()) => { - info!("Successfully downloaded package {} to {:?}", package, download_directory.clone()); - return Transition::next(self, Installing { - download_directory: pod_state.download_directory.clone(), - parcel_directory: pod_state.parcel_directory.clone(), - package: package.clone(), - }); + info!( + "Successfully downloaded package {} to {:?}", + package, + download_directory.clone() + ); + return Transition::next( + self, + Installing { + download_directory: pod_state.download_directory.clone(), + parcel_directory: pod_state.parcel_directory.clone(), + package: package.clone(), + }, + ); } Err(e) => { warn!("Download of package {} failed: {}", package, e); - return Transition::next(self, DownloadingBackoff { package: package.clone() }); + return Transition::next( + self, + DownloadingBackoff { + package: package.clone(), + }, + ); } } } Ok(None) => { // No repository was found that provides this package - let message = format!("Cannot find package {} in any repository, aborting ..", &package); + let message = format!( + "Cannot find package {} in any repository, aborting ..", + &package + ); error!("{}", &message); - return Transition::next(self, DownloadingBackoff { package: package.clone() }); + return Transition::next( + self, + DownloadingBackoff { + package: package.clone(), + }, + ); } Err(e) => { // An error occurred when looking for a repository providing this package - let message = format!("Error occurred trying to find package {}: {:?}", &package, e); + let message = format!( + "Error occurred trying to find package {}: {:?}", + &package, e + ); error!("{}", &message); - return Transition::next(self, DownloadingBackoff { package: package.clone() }); + return Transition::next( + self, + DownloadingBackoff { + package: package.clone(), + }, + ); } } } @@ -86,4 +135,3 @@ impl State for Downloading { make_status(Phase::Pending, &"status:initializing") } } - diff --git a/src/provider/states/failed.rs b/src/provider/states/failed.rs index 8ca0310..48b65c0 100644 --- a/src/provider/states/failed.rs +++ b/src/provider/states/failed.rs @@ -1,9 +1,9 @@ use kubelet::state::prelude::*; use log::{debug, info}; -use crate::provider::PodState; use crate::provider::states::install_package::Installing; use crate::provider::states::starting::Starting; +use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(Starting, Installing)] @@ -14,8 +14,8 @@ pub struct Failed { } impl Failed { - fn restart_enabled(&self, pod : &Pod) -> bool { - if let Some(pod_spec) = &pod.as_kube_pod().spec { + fn restart_enabled(&self, pod: &Pod) -> bool { + if let Some(pod_spec) = &pod.as_kube_pod().spec { if let Some(restart_policy) = &pod_spec.restart_policy { return restart_policy.eq("Always"); } @@ -35,10 +35,10 @@ impl State for Failed { debug!("Restart is disabled for process."); } //tokio::time::delay_for(std::time::Duration::from_secs(2)).await; - // T//ransition::next(self, Installing{ + // T//ransition::next(self, Installing{ // download_directory: pod_state.download_directory.clone(), - // parcel_directory: pod_state.parcel_directory.clone(), - // package: pod_state.package.clone() + // parcel_directory: pod_state.parcel_directory.clone(), + // package: pod_state.package.clone() //}) Transition::Complete(Ok(())) } diff --git a/src/provider/states/install_package.rs b/src/provider/states/install_package.rs index e5492ed..4b07929 100644 --- a/src/provider/states/install_package.rs +++ b/src/provider/states/install_package.rs @@ -3,16 +3,16 @@ use std::path::{Path, PathBuf}; use flate2::read::GzDecoder; use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; use log::{debug, info}; use tar::Archive; use crate::provider::error::StackableError; -use crate::provider::PodState; use crate::provider::repository::package::Package; use crate::provider::states::create_config::CreatingConfig; use crate::provider::states::setup_failed::SetupFailed; +use crate::provider::PodState; #[derive(Debug, TransitionTo)] #[transition_to(CreatingConfig, SetupFailed)] @@ -27,7 +27,10 @@ impl Installing { let package = package.into(); let package_file_name = self.parcel_directory.join(package.get_directory_name()); - debug!("Checking if package {:?} has already been installed to {:?}", package, package_file_name); + debug!( + "Checking if package {:?} has already been installed to {:?}", + package, package_file_name + ); Path::new(&package_file_name).exists() } @@ -46,7 +49,10 @@ impl Installing { let target_directory = self.get_target_directory(package.clone()); - println!("Installing package: {:?} from {:?} into {:?}", package, archive_path, target_directory); + info!( + "Installing package: {:?} from {:?} into {:?}", + package, archive_path, target_directory + ); archive.unpack(self.parcel_directory.join(package.get_directory_name()))?; Ok(()) } @@ -58,15 +64,34 @@ impl State for Installing { let package = self.package.clone(); if self.package_installed(package.clone()) { info!("Package {} has already been installed", package); - return Transition::next(self, CreatingConfig{ target_directory: None }); + return Transition::next( + self, + CreatingConfig { + target_directory: None, + }, + ); } else { info!("Installing package {}", package); - self.install_package(package.clone()); + match self.install_package(package.clone()) { + Ok(()) => {} + Err(e) => { + return Transition::next( + self, + SetupFailed { + message: "".to_string(), + }, + ); + } + } } - debug!("installing package"); - Transition::next(self, CreatingConfig{ target_directory: None }) + Transition::next( + self, + CreatingConfig { + target_directory: None, + }, + ) } async fn json_status( @@ -76,4 +101,4 @@ impl State for Installing { ) -> anyhow::Result { make_status(Phase::Pending, &"status:initializing") } -} \ No newline at end of file +} diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs index 58ef6bb..b8a5e00 100644 --- a/src/provider/states/running.rs +++ b/src/provider/states/running.rs @@ -1,14 +1,14 @@ use std::process::Child; use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; -use log::{debug, error}; +use kubelet::state::{State, Transition}; +use log::{debug, error, trace}; -use crate::provider::PodState; use crate::provider::states::failed::Failed; use crate::provider::states::install_package::Installing; use crate::provider::states::stopping::Stopping; +use crate::provider::PodState; #[derive(Debug, TransitionTo)] #[transition_to(Stopping, Failed, Running, Installing)] @@ -18,36 +18,45 @@ pub struct Running { #[async_trait::async_trait] impl State for Running { - async fn next(mut self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - + async fn next( + mut self: Box, + pod_state: &mut PodState, + _pod: &Pod, + ) -> Transition { debug!("waiting"); let mut handle = std::mem::replace(&mut self.process_handle, None).unwrap(); /*while let Ok(_) = timeout(Duration::from_millis(100), changed.notified()).await { debug!("drained a waiting notification"); }*/ - // debug!("done draining"); + // debug!("done draining"); loop { - println!("running"); tokio::select! { /*_ = changed.notified() => { debug!("pod changed"); break; },*/ _ = tokio::time::delay_for(std::time::Duration::from_secs(1)) => { - debug!("timer expired"); + trace!("Checking if service {} is still running.", &pod_state.service_name); } } match handle.try_wait() { - Ok(None) => debug!("Still running"), + Ok(None) => debug!("Service {} is still running", &pod_state.service_name), _ => { - error!("died"); - return Transition::next(self, Failed { message: "process died".to_string() }) + error!( + "Service {} died unexpectedly, moving to failed state", + pod_state.service_name + ); + return Transition::next( + self, + Failed { + message: "Process died unexpectedly!".to_string(), + }, + ); } - } } - } + } async fn json_status( &self, @@ -56,4 +65,4 @@ impl State for Running { ) -> anyhow::Result { make_status(Phase::Running, &"status:running") } -} \ No newline at end of file +} diff --git a/src/provider/states/stopped.rs b/src/provider/states/stopped.rs index f789cd2..0a931c3 100644 --- a/src/provider/states/stopped.rs +++ b/src/provider/states/stopped.rs @@ -1,22 +1,26 @@ use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; +use log::info; +use tokio::time::Duration; -use crate::provider::PodState; use crate::provider::states::starting::Starting; +use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(Starting)] pub struct Stopped; - #[async_trait::async_trait] impl State for Stopped { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - for i in 1..8 { - tokio::time::delay_for(std::time::Duration::from_secs(2)).await; - println!("stopped"); - } + let delay = Duration::from_secs(2); + info!( + "Service {} stopped, waiting {} seconds before restart.", + pod_state.service_name, + delay.as_secs() + ); + tokio::time::delay_for(delay).await; Transition::next(self, Starting) } @@ -27,4 +31,4 @@ impl State for Stopped { ) -> anyhow::Result { make_status(Phase::Pending, &"status:running") } -} \ No newline at end of file +} diff --git a/src/provider/states/stopping.rs b/src/provider/states/stopping.rs index 00b2c3c..658d471 100644 --- a/src/provider/states/stopping.rs +++ b/src/provider/states/stopping.rs @@ -1,20 +1,26 @@ use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; +use log::info; -use crate::provider::PodState; use crate::provider::states::failed::Failed; use crate::provider::states::stopped::Stopped; +use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(Stopped, Failed)] pub struct Stopping; - #[async_trait::async_trait] impl State for Stopping { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - println!("stopping"); + if let Some(child) = &pod_state.process_handle { + let pid = child.id(); + info!( + "Received stop command for service {}, stopping process with pid {}", + pod_state.service_name, pid + ); + } Transition::next(self, Stopped) } @@ -25,4 +31,4 @@ impl State for Stopping { ) -> anyhow::Result { make_status(Phase::Pending, &"status:running") } -} \ No newline at end of file +} diff --git a/src/provider/states/terminated.rs b/src/provider/states/terminated.rs index 145598c..a553757 100644 --- a/src/provider/states/terminated.rs +++ b/src/provider/states/terminated.rs @@ -1,4 +1,5 @@ use kubelet::state::prelude::*; +use log::info; use crate::provider::PodState; @@ -12,7 +13,7 @@ pub struct Terminated { #[async_trait::async_trait] impl State for Terminated { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - println!("terminated"); + info!("Service {} was terminated!", &pod_state.service_name); Transition::Complete(Ok(())) } From b7cae72f474baf255e2ee6ae7d9f19204bb51336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Tue, 24 Nov 2020 23:15:11 +0100 Subject: [PATCH 06/22] Added code to look up the default IP. --- Cargo.lock | 289 +++++++++++++++++++++++++++++++++++++++++++++------- Cargo.toml | 4 +- src/main.rs | 41 +++++++- 3 files changed, 295 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 660531e..1d388cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,6 +183,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bitflags" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23" + [[package]] name = "bitflags" version = "1.2.1" @@ -230,6 +236,17 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "bstr" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + [[package]] name = "buf_redux" version = "0.8.4" @@ -314,7 +331,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.2.1", "strsim", "term_size", "textwrap", @@ -328,7 +345,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.2.1", ] [[package]] @@ -534,7 +551,7 @@ checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "atty", "humantime", - "log", + "log 0.4.11", "regex", "termcolor", ] @@ -612,7 +629,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97f347202c95c98805c216f9e1df210e8ebaec9fdb2365700a43c10797a35e63" dependencies = [ - "bitflags", + "bitflags 1.2.1", "fsevent-sys", ] @@ -637,7 +654,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags", + "bitflags 1.2.1", "fuchsia-zircon-sys", ] @@ -783,6 +800,12 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "glob" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" + [[package]] name = "h2" version = "0.2.7" @@ -809,7 +832,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2764f9796c0ddca4b82c07f25dd2cb3db30b9a8f47940e78e1c883d9e95c3db9" dependencies = [ - "log", + "log 0.4.11", "pest", "pest_derive", "quick-error 2.0.0", @@ -830,7 +853,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f" dependencies = [ "base64 0.12.3", - "bitflags", + "bitflags 1.2.1", "bytes 0.5.6", "headers-core", "http 0.2.1", @@ -978,7 +1001,7 @@ dependencies = [ "http 0.1.21", "httparse", "language-tags", - "log", + "log 0.4.11", "mime", "percent-encoding 1.0.1", "time 0.1.44", @@ -1023,7 +1046,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46dd0a94b393c730779ccfd2a872b67b1eb67be3fc33082e733bdb38b5fde4d4" dependencies = [ - "bitflags", + "bitflags 1.2.1", "inotify-sys", "libc", ] @@ -1061,6 +1084,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +[[package]] +name = "ipnetwork" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8eca9f51da27bc908ef3dd85c21e1bbba794edaf94d7841e37356275b82d31e" +dependencies = [ + "serde", +] + [[package]] name = "itertools" version = "0.8.2" @@ -1139,7 +1171,7 @@ dependencies = [ "futures-util", "http 0.2.1", "k8s-openapi", - "log", + "log 0.4.11", "openssl", "pem", "reqwest", @@ -1208,7 +1240,7 @@ dependencies = [ "kubelet-derive", "lazy_static", "lazycell", - "log", + "log 0.4.11", "mio", "miow", "notify", @@ -1272,6 +1304,15 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.11", +] + [[package]] name = "log" version = "0.4.11" @@ -1349,7 +1390,7 @@ dependencies = [ "iovec", "kernel32-sys", "libc", - "log", + "log 0.4.11", "miow", "net2", "slab", @@ -1363,7 +1404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" dependencies = [ "lazycell", - "log", + "log 0.4.11", "mio", "slab", ] @@ -1405,7 +1446,7 @@ checksum = "8209c33c951f07387a8497841122fc6f712165e3f9bda3e6be4645b58188f676" dependencies = [ "buf_redux", "httparse", - "log", + "log 0.4.11", "mime", "mime_guess", "quick-error 1.2.3", @@ -1423,7 +1464,7 @@ checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" dependencies = [ "lazy_static", "libc", - "log", + "log 0.4.11", "openssl", "openssl-probe", "openssl-sys", @@ -1451,7 +1492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8b946889dfdad884379cd56367d93b6d0ce8889cc027d26a69a3a31c0a03bb5" dependencies = [ "anymap", - "bitflags", + "bitflags 1.2.1", "crossbeam-channel", "filetime", "fsevent", @@ -1502,7 +1543,7 @@ dependencies = [ "futures-util", "hyperx", "lazy_static", - "log", + "log 0.4.11", "regex", "reqwest", "serde", @@ -1535,7 +1576,7 @@ version = "0.10.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" dependencies = [ - "bitflags", + "bitflags 1.2.1", "cfg-if 0.1.10", "foreign-types", "lazy_static", @@ -1711,6 +1752,95 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "pnet" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62df42dcd72f6f2a658bcf38509f1027df1440ac85f1af4badbe034418302dc" +dependencies = [ + "ipnetwork", + "pnet_base", + "pnet_datalink", + "pnet_packet", + "pnet_sys", + "pnet_transport", +] + +[[package]] +name = "pnet_base" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cd5f7e15220afa66b0a9a62841ea10089f39dcaa1c29752c0b22dfc03111b5" + +[[package]] +name = "pnet_datalink" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7318ae1d6e0b7fa1e49933233c9473f2b72d3d18b97e70e2716c6415dde5f915" +dependencies = [ + "ipnetwork", + "libc", + "pnet_base", + "pnet_sys", + "winapi 0.2.8", +] + +[[package]] +name = "pnet_macros" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbd5c52c6e04aa720400f9c71cd0e8bcb38cd13421d5caabd9035e9efa47de9" +dependencies = [ + "regex", + "syntex", + "syntex_syntax", +] + +[[package]] +name = "pnet_macros_support" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf9c5c0c36766d0a4da9ab268c0700771b8ec367b9463fd678109fa28463c5b" +dependencies = [ + "pnet_base", +] + +[[package]] +name = "pnet_packet" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e26a864d71d0ac51a549cf40283c44ed1b8f98168545638a4730ef9f560283" +dependencies = [ + "glob", + "pnet_base", + "pnet_macros", + "pnet_macros_support", + "syntex", +] + +[[package]] +name = "pnet_sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f0de0c52609f157b25d79ce24d9016ab1bbf10cde761397200d634a833872c" +dependencies = [ + "libc", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "pnet_transport" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6712ab76534340494d849e3c51c64a6261e4b451337b7c05bd3681e384c48b10" +dependencies = [ + "libc", + "pnet_base", + "pnet_packet", + "pnet_sys", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1759,7 +1889,7 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.1", ] [[package]] @@ -1781,7 +1911,7 @@ dependencies = [ "bytes 0.5.6", "heck", "itertools", - "log", + "log 0.4.11", "multimap", "petgraph", "prost", @@ -2041,6 +2171,15 @@ dependencies = [ "thread_local", ] +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", +] + [[package]] name = "regex-syntax" version = "0.6.21" @@ -2075,7 +2214,7 @@ dependencies = [ "ipnet", "js-sys", "lazy_static", - "log", + "log 0.4.11", "mime", "mime_guess", "native-tls", @@ -2121,6 +2260,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2137,7 +2282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" dependencies = [ "base64 0.12.3", - "log", + "log 0.4.11", "ring", "sct", "webpki", @@ -2196,7 +2341,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" dependencies = [ - "bitflags", + "bitflags 1.2.1", "core-foundation", "core-foundation-sys", "libc", @@ -2411,18 +2556,29 @@ dependencies = [ "kube", "kube-derive", "kubelet", - "log", + "log 0.4.11", "oci-distribution", + "pnet", "reqwest", "serde", "serde_derive", "serde_json", + "stackable_config", "tar", "thiserror", "tokio", "url 2.2.0", ] +[[package]] +name = "stackable_config" +version = "0.1.0" +dependencies = [ + "anyhow", + "bstr", + "clap", +] + [[package]] name = "standback" version = "0.2.13" @@ -2525,7 +2681,56 @@ checksum = "443b4178719c5a851e1bde36ce12da21d74a0e60b4d982ec3385a933c812f0f6" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-xid 0.2.1", +] + +[[package]] +name = "syntex" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a30b08a6b383a22e5f6edc127d169670d48f905bb00ca79a00ea3e442ebe317" +dependencies = [ + "syntex_errors", + "syntex_syntax", +] + +[[package]] +name = "syntex_errors" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c48f32867b6114449155b2a82114b86d4b09e1bddb21c47ff104ab9172b646" +dependencies = [ + "libc", + "log 0.3.9", + "rustc-serialize", + "syntex_pos", + "term", + "unicode-xid 0.0.3", +] + +[[package]] +name = "syntex_pos" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd49988e52451813c61fecbe9abb5cfd4e1b7bb6cdbb980a6fbcbab859171a6" +dependencies = [ + "rustc-serialize", +] + +[[package]] +name = "syntex_syntax" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7628a0506e8f9666fdabb5f265d0059b059edac9a3f810bda077abb5d826bd8d" +dependencies = [ + "bitflags 0.5.0", + "libc", + "log 0.3.9", + "rustc-serialize", + "syntex_errors", + "syntex_pos", + "term", + "unicode-xid 0.0.3", ] [[package]] @@ -2554,6 +2759,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "term" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" +dependencies = [ + "kernel32-sys", + "winapi 0.2.8", +] + [[package]] name = "term_size" version = "0.3.2" @@ -2747,7 +2962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c" dependencies = [ "futures-util", - "log", + "log 0.4.11", "pin-project 0.4.27", "tokio", "tungstenite", @@ -2762,7 +2977,7 @@ dependencies = [ "bytes 0.5.6", "futures-core", "futures-sink", - "log", + "log 0.4.11", "pin-project-lite 0.1.11", "tokio", ] @@ -2901,7 +3116,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cc79fc3afd07492b7966d7efa7c6c50f8ed58d768a6075dd7ae6591c5d2017b" dependencies = [ "futures-core", - "log", + "log 0.4.11", "pin-project 0.4.27", "tokio", "tower-discover", @@ -2939,7 +3154,7 @@ dependencies = [ "futures-core", "futures-util", "indexmap", - "log", + "log 0.4.11", "tokio", "tower-service", ] @@ -2994,7 +3209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" dependencies = [ "cfg-if 0.1.10", - "log", + "log 0.4.11", "pin-project-lite 0.1.11", "tracing-attributes", "tracing-core", @@ -3057,7 +3272,7 @@ dependencies = [ "http 0.2.1", "httparse", "input_buffer", - "log", + "log 0.4.11", "rand 0.7.3", "sha-1 0.9.2", "url 2.2.0", @@ -3133,6 +3348,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +[[package]] +name = "unicode-xid" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" + [[package]] name = "unicode-xid" version = "0.2.1" @@ -3230,7 +3451,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log", + "log 0.4.11", "try-lock", ] @@ -3245,7 +3466,7 @@ dependencies = [ "headers", "http 0.2.1", "hyper", - "log", + "log 0.4.11", "mime", "mime_guess", "multipart", @@ -3295,7 +3516,7 @@ checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" dependencies = [ "bumpalo", "lazy_static", - "log", + "log 0.4.11", "proc-macro2", "quote", "syn", diff --git a/Cargo.toml b/Cargo.toml index 3b7eb79..04c0cbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,6 @@ flate2 = "1.0" tar = "0.4" handlebars = "3.5" thiserror = "1.0" -url = "2.2" \ No newline at end of file +url = "2.2" +pnet = "0.26.0" +stackable_config = { path = "../common/config"} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 1c5c605..e1b000d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use kubelet::config::Config; use kubelet::Kubelet; use crate::provider::StackableProvider; +use pnet::datalink; +use std::net::IpAddr; mod provider; @@ -13,7 +15,14 @@ mod provider; async fn main() -> anyhow::Result<()> { // The provider is responsible for all the "back end" logic. If you are creating // a new Kubelet, all you need to implement is a provider. - let config = Config::new_from_file_and_flags(env!("CARGO_PKG_VERSION"), None); + + // Lookup the default IP Adress and export this to the environment for the Krustlet config + // to pick up + if let Some(default_ipv4_address) = get_default_ipaddress() { + std::env::set_var("KRUSTLET_NODE_IP", default_ipv4_address.to_string()); + } + + let config = Config::new_from_flags(env!("CARGO_PKG_VERSION")); // Initialize the logger env_logger::init(); @@ -25,10 +34,34 @@ async fn main() -> anyhow::Result<()> { let parcel_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/parcels"); let config_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/config"); - let provider = StackableProvider::new(kube::Client::new(kubeconfig.clone()), parcel_directory, config_directory) - .await - .expect("Error initializing provider."); + let provider = StackableProvider::new( + kube::Client::new(kubeconfig.clone()), + parcel_directory, + config_directory, + ) + .await + .expect("Error initializing provider."); let kubelet = Kubelet::new(provider, kubeconfig, config).await?; kubelet.start().await } + +fn get_default_ipaddress() -> Option { + let all_interfaces = datalink::interfaces(); + + let default_interface = all_interfaces + .iter() + .filter(|e| e.is_up() && !e.is_loopback() && e.ips.len() > 0) + .next(); + + match default_interface { + Some(interface) => { + println!("Found default interface with [{:?}].", interface.ips); + if let ipv4_network = interface.ips[0] { + return Some(ipv4_network.ip()); + } + } + None => println!("Error while finding the default interface."), + }; + return None; +} From f1d3e8fb0f1980b1edc3af241548b20d882d152c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 25 Nov 2020 12:35:35 +0100 Subject: [PATCH 07/22] Added interim command line parsing. This is not perfect yet due to limitations of the config parsing that Krustlet does. Krustlet does not allow building a config from scratch by just specifying a few values, so we override some values by setting environment variables from our config - BUT: it is not currently possible to specify command line parameters when starting the agent, as the Krustlet will also try to parse these and doesn't know about them, so the only possible way to pass arguments is currently via exporting a config file in the CONFIG_FILE env variable and specifying all arguments in there. An example has been added in config/agent_config --- src/agentconfig.rs | 326 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 76 ++++++----- 2 files changed, 372 insertions(+), 30 deletions(-) create mode 100644 src/agentconfig.rs diff --git a/src/agentconfig.rs b/src/agentconfig.rs new file mode 100644 index 0000000..dd326b5 --- /dev/null +++ b/src/agentconfig.rs @@ -0,0 +1,326 @@ +use std::collections::hash_map::RandomState; +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::net::IpAddr; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use log::{debug, error, info, trace}; +use pnet::datalink; +use stackable_config::{ConfigOption, Configurable, Configuration}; +use thiserror::Error; + +use crate::agentconfig::AgentConfigError::WrongArgumentCount; + +#[derive(Error, Debug)] +pub enum AgentConfigError { + #[error("Wrong number of arguments found for config option {}!", .option.name)] + WrongArgumentCount { option: ConfigOption }, +} + +#[derive(Clone)] +pub struct AgentConfig { + pub parcel_directory: PathBuf, + pub config_directory: PathBuf, + pub log_directory: PathBuf, + pub server_ip_address: IpAddr, + pub server_cert_file: Option, + pub server_key_file: Option, + pub tags: HashMap, +} + +impl AgentConfig { + pub const SERVER_IP_ADDRESS: ConfigOption = ConfigOption { + name: "server-bind-ip", + default: None, + required: false, + takes_argument: true, + help: "The local IP to register as the node's ip with the apiserver. Will be automatically set to the first address of the first non-loopback interface if not specified.", + documentation: "", + list: false, + }; + + pub const SERVER_CERT_FILE: ConfigOption = ConfigOption { + name: "server-cert-file", + default: None, + required: false, + takes_argument: true, + help: "The local IP to bind the callback webserver to. Will be automatically set to the first address of the first non-loopback interface if not specified.", + documentation: "", + list: false, + }; + + pub const SERVER_KEY_FILE: ConfigOption = ConfigOption { + name: "server-key-file", + default: None, + required: false, + takes_argument: true, + help: "The local IP to bind the callback webserver to. Will be automatically set to the first address of the first non-loopback interface if not specified.", + documentation: "", + list: false, + }; + + pub const PARCEL_DIR: ConfigOption = ConfigOption { + name: "parcel-directory", + default: Some("/opt/stackable/parcels"), + required: false, + takes_argument: true, + help: "The base directory under which installed parcels will be stored.", + documentation: "Yak Yak!", + list: false, + }; + + pub const CONFIG_DIR: ConfigOption = ConfigOption { + name: "config-directory", + default: Some("/opt/stackable/config"), + required: false, + takes_argument: true, + help: "The base directory under which configuration will be generated for all executed services.", + documentation: "Yak Yak!", + list: false, + }; + + pub const LOG_DIR: ConfigOption = ConfigOption { + name: "log-directory", + default: Some("/opt/stackable/logs"), + required: false, + takes_argument: true, + help: "The base directory under which log files will be placed for all services.", + documentation: "Yak Yak!", + list: false, + }; + + pub const NO_CONFIG: ConfigOption = ConfigOption { + name: "no-config", + default: None, + required: false, + takes_argument: false, + help: "If this option is specified, any file referenced in AGENT_CONF environment variable will be ignored.", + documentation: "", + list: false, + }; + + pub const TAG: ConfigOption = ConfigOption { + name: "tag", + default: None, + required: false, + takes_argument: true, + help: "A \"key=value\" pair that should be assigned to this agent as tag. This can be specified multiple times to assign additional tags.", + documentation: "Tags are the main way of identifying nodes to assign services to later on.", + list: true + }; + + fn get_options() -> HashSet { + [ + AgentConfig::SERVER_IP_ADDRESS, + AgentConfig::SERVER_CERT_FILE, + AgentConfig::SERVER_KEY_FILE, + AgentConfig::PARCEL_DIR, + AgentConfig::CONFIG_DIR, + AgentConfig::LOG_DIR, + AgentConfig::NO_CONFIG, + AgentConfig::TAG, + ] + .iter() + .cloned() + .collect() + } + + fn get_exactly_one_string( + parsed_values: &HashMap>>, + option: &ConfigOption, + ) -> Result { + debug!( + "Trying to obtain exactly one value for ConfigOption {}", + option.name + ); + trace!( + "Parsed values for {} from commandline: {:?}", + option.name, + parsed_values.get(option) + ); + if let Some(Some(list_value)) = parsed_values.get(option) { + if list_value.len() != 1 { + //panic!(&format!("Expected exactly one value to be specified for parameter {} but got {} instead.", option.name.clone(), list_value.len().clone())); + error!("Got additional, unexpected values for parameter!"); + } else { + // We've checked that the list has exactly one value at this point, so no errors should + // occur after this point - but you never know + return Ok(list_value[0].to_string()); + } + } + Err(WrongArgumentCount { + option: option.clone(), + }) + } + + fn get_at_least_one_string( + parsed_values: &HashMap>>, + option: &ConfigOption, + ) -> Vec { + if let Some(Some(list_value)) = parsed_values.get(option) { + if list_value.len() > 0 { + return list_value.clone(); + } + panic!("Unexpectedly got empty list of values for parameter".to_string()); + } else { + panic!("Parameter was not specified but a value is required!".to_string()); + } + } + + fn get_default_ipaddress() -> Option { + let all_interfaces = datalink::interfaces(); + + let default_interface = all_interfaces + .iter() + .filter(|e| e.is_up() && !e.is_loopback() && e.ips.len() > 0) + .next(); + + match default_interface { + Some(interface) => { + debug!( + "Found default interface {} with following ips: [{:?}].", + interface, interface.ips + ); + if let ipv4_network = interface.ips[0] { + return Some(ipv4_network.ip()); + } + } + None => error!( + "Error while finding the default interface - delegating ip retrieval to Kubelet." + ), + }; + return None; + } +} + +impl Configurable for AgentConfig { + fn get_config_description() -> Configuration { + Configuration { + name: "Stackable Agent", + version: "0.1", + about: "Manages local state according to what the central orchestrator defines.", + options: AgentConfig::get_options(), + } + } + + fn parse_values( + parsed_values: HashMap>, RandomState>, + ) -> Result, anyhow::Error> { + // Parse IP Address or lookup default + let final_ip = if let Ok(ip) = + AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::SERVER_IP_ADDRESS) + { + IpAddr::from_str(&ip).expect(&format!("Couldn't parse {} as a valid ip address!", ip)) + } else { + AgentConfig::get_default_ipaddress().expect(&format!( + "Error getting default ip address, please specify it explicitly!" + )) + }; + info!("Selected {} as local address to listen on.", final_ip); + + // Parse log directory + let final_log_dir = if let Ok(log_dir) = + AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::LOG_DIR) + { + PathBuf::from_str(&log_dir).expect(&format!( + "Error parsing valid log directory from string: {}", + log_dir + )) + } else { + PathBuf::from_str( + AgentConfig::LOG_DIR + .default + .expect("Invalid default value for log directory option!"), + ) + .expect(&format!("Unable to get log directory from options!")) + }; + + // Parse config directory + let final_config_dir = if let Ok(config_dir) = + AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::CONFIG_DIR) + { + PathBuf::from_str(&config_dir).expect(&format!( + "Error parsing valid config directory from string: {}", + config_dir + )) + } else { + PathBuf::from_str( + AgentConfig::CONFIG_DIR + .default + .expect("Invalid default value for config directory option!"), + ) + .expect(&format!("Unable to get config directory from options!")) + }; + + // Parse parcel directory + let final_parcel_dir = if let Ok(parcel_dir) = + AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::PARCEL_DIR) + { + PathBuf::from_str(&parcel_dir).expect(&format!( + "Error parsing valid parcel directory from string: {}", + parcel_dir + )) + } else { + PathBuf::from_str( + AgentConfig::PARCEL_DIR + .default + .expect("Invalid default value for parcel directory option!"), + ) + .expect(&format!("Unable to get parcel directory from options!")) + }; + + // Parse cert file + let final_server_cert_file = if let Ok(server_cert_file) = + AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::SERVER_CERT_FILE) + { + Some(PathBuf::from_str(&server_cert_file).expect(&format!( + "Error parsing valid server cert file directory from string: {}", + server_cert_file + ))) + } else { + None + }; + + // Parse key file + let final_server_key_file = if let Ok(server_key_file) = + AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::SERVER_KEY_FILE) + { + Some(PathBuf::from_str(&server_key_file).expect(&format!( + "Error parsing valid server key file directory from string: {}", + server_key_file + ))) + } else { + None + }; + + let mut final_tags: HashMap = HashMap::new(); + if let Some(Some(tags)) = parsed_values.get(&AgentConfig::TAG) { + for tag in tags { + let split: Vec<&str> = tag.split("=").collect(); + if split.len() != 2 { + // We want to avoid any "unpredictable" behavior like ignoring a malformed + // key=value pair with just a log message -> so we panic if this can't be + // parsed + panic!(format!( + "Unable to parse value {} for option --tag as key=value pair!", + tag + )) + } else { + // This might panic, but really shouldn't, as we've checked the size of the array + final_tags.insert(split[0].to_string(), split[1].to_string()); + } + } + } + + Ok(Box::new(AgentConfig { + parcel_directory: final_parcel_dir, + config_directory: final_config_dir, + log_directory: final_log_dir, + server_ip_address: final_ip, + server_cert_file: final_server_cert_file, + server_key_file: final_server_key_file, + tags: final_tags, + })) + } +} diff --git a/src/main.rs b/src/main.rs index e1b000d..9824eac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,62 @@ +use std::env; +use std::ffi::OsString; +use std::net::IpAddr; use std::path::PathBuf; use kube::config::Config as KubeConfig; use kube::config::KubeConfigOptions; -use kubelet::config::Config; +use kubelet::config::{Config, ServerConfig}; use kubelet::Kubelet; +use log::{debug, info, warn}; +use pnet::datalink; +use stackable_config::ConfigBuilder; +use crate::agentconfig::AgentConfig; use crate::provider::StackableProvider; -use pnet::datalink; -use std::net::IpAddr; +mod agentconfig; mod provider; #[tokio::main(threaded_scheduler)] async fn main() -> anyhow::Result<()> { + // Initialize the logger + env_logger::init(); + // The provider is responsible for all the "back end" logic. If you are creating // a new Kubelet, all you need to implement is a provider. - // Lookup the default IP Adress and export this to the environment for the Krustlet config - // to pick up - if let Some(default_ipv4_address) = get_default_ipaddress() { - std::env::set_var("KRUSTLET_NODE_IP", default_ipv4_address.to_string()); - } + let agent_config: AgentConfig = + *ConfigBuilder::build(env::args_os().collect::>(), "CONFIG_FILE") + .expect("Error initializing Configuration!"); - let config = Config::new_from_flags(env!("CARGO_PKG_VERSION")); + export_env( + "KRUSTLET_NODE_IP", + &agent_config.server_ip_address.to_string(), + ); - // Initialize the logger - env_logger::init(); + // Convert node tags to string in the form of key=value,key=value,... + let node_labels = agent_config + .tags + .iter() + .map(|(k, v)| format!("{}={}", String::from(k), String::from(v))) + .collect::>() + .join(","); + + export_env("NODE_LABELS", &node_labels); + + if let Some(cert_file_path) = agent_config.server_cert_file { + export_env("KRUSTLET_CERT_FILE", cert_file_path.to_str().unwrap()); + } else { + warn!("Not exporting server cert file path, as non was specified that could be converted to a String."); + } + + if let Some(key_file_path) = agent_config.server_key_file { + export_env("KRUSTLET_KEY_FILE", key_file_path.to_str().unwrap()); + } else { + warn!("Not exporting server key file path, as non was specified that could be converted to a String."); + } + info!("args: {:?}", env::args()); + let krustlet_config = Config::new_from_flags(env!("CARGO_PKG_VERSION")); //let kubeconfig = kubelet::bootstrap(&config, &config.bootstrap_file, notify_bootstrap).await?; let kubeconfig = KubeConfig::from_kubeconfig(&KubeConfigOptions::default()) @@ -42,26 +73,11 @@ async fn main() -> anyhow::Result<()> { .await .expect("Error initializing provider."); - let kubelet = Kubelet::new(provider, kubeconfig, config).await?; + let kubelet = Kubelet::new(provider, kubeconfig, krustlet_config).await?; kubelet.start().await } -fn get_default_ipaddress() -> Option { - let all_interfaces = datalink::interfaces(); - - let default_interface = all_interfaces - .iter() - .filter(|e| e.is_up() && !e.is_loopback() && e.ips.len() > 0) - .next(); - - match default_interface { - Some(interface) => { - println!("Found default interface with [{:?}].", interface.ips); - if let ipv4_network = interface.ips[0] { - return Some(ipv4_network.ip()); - } - } - None => println!("Error while finding the default interface."), - }; - return None; +fn export_env(var_name: &str, var_value: &str) { + info!("Exporting {}={}", var_name, var_value); + std::env::set_var(var_name, var_value); } From bf37c2a78dc6501a0b01c339ab47fd8ddb2941b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Thu, 26 Nov 2020 00:21:59 +0100 Subject: [PATCH 08/22] Added interim command line parsing. This is not perfect yet due to limitations of the config parsing that Krustlet does. Krustlet does not allow building a config from scratch by just specifying a few values, so we override some values by setting environment variables from our config - BUT: it is not currently possible to specify command line parameters when starting the agent, as the Krustlet will also try to parse these and doesn't know about them, so the only possible way to pass arguments is currently via exporting a config file in the CONFIG_FILE env variable and specifying all arguments in there. An example has been added in config/agent_config --- Cargo.lock | 1 + Cargo.toml | 3 ++- config/agent_config | 6 ++++++ src/main.rs | 11 ++++++----- src/provider/mod.rs | 21 +++++++++++++++++++++ src/provider/states/create_config.rs | 24 ++++++++++++++++++------ src/provider/states/starting.rs | 10 +++++++--- 7 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 config/agent_config diff --git a/Cargo.lock b/Cargo.lock index 1d388cf..edaee86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2573,6 +2573,7 @@ dependencies = [ [[package]] name = "stackable_config" version = "0.1.0" +source = "git+https://github.com/soenkeliebau/common.git?branch=generic_of_main#ddb6f81c3ae850da19bcb7d1a3b2e0c1ddece33f" dependencies = [ "anyhow", "bstr", diff --git a/Cargo.toml b/Cargo.toml index 04c0cbc..612c708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,4 +25,5 @@ handlebars = "3.5" thiserror = "1.0" url = "2.2" pnet = "0.26.0" -stackable_config = { path = "../common/config"} \ No newline at end of file +#stackable_config = { path = "../common/config" } +stackable_config = { git = "https://github.com/soenkeliebau/common.git", branch = "generic_of_main" } diff --git a/config/agent_config b/config/agent_config new file mode 100644 index 0000000..937eaf3 --- /dev/null +++ b/config/agent_config @@ -0,0 +1,6 @@ +--tag=test=test1 +--tag=test2=test2 +--parcel-directory=/home/sliebau/IdeaProjects/agent/work/parcels +--config-directory=/home/sliebau/IdeaProjects/agent/work/config +--server-cert-file=/home/sliebau/Downloads/EasyRSA-3.0.8/pki/issued/krustlet1.crt +--server-key-file=/home/sliebau/Downloads/EasyRSA-3.0.8/pki/private/pkcs8.key \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9824eac..d22f77e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,7 +51,7 @@ async fn main() -> anyhow::Result<()> { } if let Some(key_file_path) = agent_config.server_key_file { - export_env("KRUSTLET_KEY_FILE", key_file_path.to_str().unwrap()); + export_env("KRUSTLET_PRIVATE_KEY_FILE", key_file_path.to_str().unwrap()); } else { warn!("Not exporting server key file path, as non was specified that could be converted to a String."); } @@ -63,12 +63,13 @@ async fn main() -> anyhow::Result<()> { .await .expect("Failed to create Kubernetes Client!"); - let parcel_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/parcels"); - let config_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/config"); + //let parcel_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/parcels"); + //let config_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/config"); let provider = StackableProvider::new( kube::Client::new(kubeconfig.clone()), - parcel_directory, - config_directory, + agent_config.parcel_directory.clone(), + agent_config.config_directory.clone(), + agent_config.log_directory.clone(), ) .await .expect("Error initializing provider."); diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 6a9e586..47aba24 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -22,6 +22,7 @@ pub struct StackableProvider { client: Client, parcel_directory: PathBuf, config_directory: PathBuf, + log_directory: PathBuf, } pub const CRDS: &'static [&'static str] = &["repositories.stable.stackable.de"]; @@ -35,22 +36,40 @@ pub struct PodState { parcel_directory: PathBuf, download_directory: PathBuf, config_directory: PathBuf, + log_directory: PathBuf, package_download_backoff_strategy: ExponentialBackoffStrategy, service_name: String, package: Package, process_handle: Option, } +impl PodState { + pub fn get_service_config_directory(&self) -> PathBuf { + self.config_directory.join(&self.service_name) + } + + pub fn get_service_package_directory(&self) -> PathBuf { + self.parcel_directory + .join(&self.package.get_directory_name()) + } + + pub fn get_service_log_directory(&self) -> PathBuf { + self.log_directory.join(&self.service_name) + } +} + impl StackableProvider { pub async fn new( client: Client, parcel_directory: PathBuf, config_directory: PathBuf, + log_directory: PathBuf, ) -> Result { let provider = StackableProvider { client, parcel_directory, config_directory, + log_directory, }; let missing_crds = provider.check_crds().await; if missing_crds.is_empty() { @@ -127,6 +146,7 @@ impl Provider for StackableProvider { let parcel_directory = self.parcel_directory.clone(); let download_directory = parcel_directory.join("_download"); let config_directory = self.config_directory.clone(); + let log_directory = self.log_directory.clone(); let package = self.get_package(pod)?; if !(&download_directory.is_dir()) { @@ -140,6 +160,7 @@ impl Provider for StackableProvider { client: self.client.clone(), parcel_directory, download_directory, + log_directory, config_directory: self.config_directory.clone(), package_download_backoff_strategy: ExponentialBackoffStrategy::default(), service_name: String::from(service_name), diff --git a/src/provider/states/create_config.rs b/src/provider/states/create_config.rs index 65abde6..03866d4 100644 --- a/src/provider/states/create_config.rs +++ b/src/provider/states/create_config.rs @@ -48,8 +48,7 @@ impl CreatingConfig { let directory_name = pod_state.package.get_directory_name(); if let Ok(package_dir) = &pod_state - .parcel_directory - .join(&directory_name) + .get_service_package_directory() .into_os_string() .into_string() { @@ -59,8 +58,7 @@ impl CreatingConfig { } if let Ok(conf_dir) = &pod_state - .config_directory - .join(&directory_name) + .get_service_config_directory() .into_os_string() .into_string() { @@ -68,6 +66,19 @@ impl CreatingConfig { } else { warn!("Unable to parse value for config directory as UTF8"); } + + // Add {{logroot}} variable, this will resolve to the base-logdirectory/podname + if let Ok(log_dir) = &pod_state + .get_service_log_directory() + .into_os_string() + .into_string() + { + render_data.insert(String::from("logroot"), String::from(log_dir)); + } else { + warn!("Unable to parse value for log directory as UTF8"); + } + + // Return all template data render_data } @@ -197,8 +208,9 @@ impl State for CreatingConfig { let client = pod_state.client.clone(); let package = pod_state.package.clone(); let config_directory = pod_state.config_directory.clone(); - self.target_directory = Some(config_directory.join(package.get_directory_name())); - let target_directory = self.target_directory.clone().unwrap(); + //self.target_directory = Some(config_directory.join(package.get_directory_name())); + //let target_directory = self.target_directory.clone().unwrap(); + let target_directory = pod_state.get_service_config_directory(); // Check if all required config maps have been created in the api-server let referenced_config_maps = self.get_config_maps(_pod).await; diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 8e06c1a..87f1bb0 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -2,15 +2,15 @@ use std::ffi::OsStr; use std::process::{Command, Stdio}; use kubelet::pod::Pod; -use kubelet::state::{State, Transition}; use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; use log::{debug, error, info, trace}; use tokio::time::Duration; -use crate::provider::PodState; use crate::provider::states::create_config::CreatingConfig; use crate::provider::states::failed::Failed; use crate::provider::states::running::Running; +use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(Running, Failed)] @@ -61,13 +61,17 @@ impl State for Starting { "Successfully executed command \"{:?}\" with args {:?}", binary, &os_args ); + debug!("Waiting if startup fails.."); for i in 1..10 { tokio::time::delay_for(Duration::from_secs(1)).await; if let Ok(None) = child.try_wait() { trace!("Process still alive after {} seconds ..", i); } else { - error!("Process died after {} seconds during startup!", i); + error!( + "Process died {} after {} seconds during startup!", + pod_state.service_name, i + ); return Transition::next( self, Failed { From f572bd2cc404d000e819f1a60f189b69ad78c25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Thu, 26 Nov 2020 11:53:08 +0100 Subject: [PATCH 09/22] Added code to properly set containers to _ready_ when the process is started. --- src/provider/states/running.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs index b8a5e00..17a98d3 100644 --- a/src/provider/states/running.rs +++ b/src/provider/states/running.rs @@ -9,6 +9,10 @@ use crate::provider::states::failed::Failed; use crate::provider::states::install_package::Installing; use crate::provider::states::stopping::Stopping; use crate::provider::PodState; +use k8s_openapi::api::core::v1::{ + ContainerState, ContainerStateRunning, ContainerStateWaiting, + ContainerStatus as KubeContainerStatus, +}; #[derive(Debug, TransitionTo)] #[transition_to(Stopping, Failed, Running, Installing)] @@ -63,6 +67,25 @@ impl State for Running { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Running, &"status:running") + let state = ContainerState { + running: Some(ContainerStateRunning { started_at: None }), + ..Default::default() + }; + + let mut container = &_pod.containers()[0]; + let mut container_status = vec![]; + container_status.push(KubeContainerStatus { + name: container.name().to_string(), + ready: true, + started: Some(false), + state: Some(state), + ..Default::default() + }); + Ok(make_status_with_containers( + Phase::Running, + &"status:running", + container_status, + vec![], + )) } } From 9cbbae6e4b8f09fb5cd6161c47d713f6ba1e73dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Thu, 26 Nov 2020 17:33:11 +0100 Subject: [PATCH 10/22] Added handling of environment variables in container section of pod. --- src/provider/states/starting.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 87f1bb0..98119cc 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -49,9 +49,23 @@ impl State for Starting { "Starting command: {:?} with arguments {:?}", binary, os_args ); + let env_variables = if let Some(vars) = container.env() { + vars.into_iter() + .map(|env_var| { + ( + String::from(&env_var.name), + String::from(&env_var.value.clone().unwrap_or(String::from(""))), + ) + }) + .collect::>() + } else { + vec![] + }; + let start_result = Command::new(binary) .stdout(Stdio::null()) .stderr(Stdio::null()) + .envs(env_variables) .args(&os_args) .spawn(); From 2cf02079aa5a18916caad7357097573387b52069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Thu, 26 Nov 2020 17:41:13 +0100 Subject: [PATCH 11/22] Add debug statement for env vars. --- src/provider/states/starting.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 98119cc..18c7648 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -61,6 +61,10 @@ impl State for Starting { } else { vec![] }; + debug!( + "Setting environment for service {} to {:?}", + pod_state.service_name, &env_variables + ); let start_result = Command::new(binary) .stdout(Stdio::null()) From 03ce6b1027113a838b828676dcd5151abf7e8bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Thu, 26 Nov 2020 17:52:15 +0100 Subject: [PATCH 12/22] More debug statements. --- src/provider/states/starting.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 18c7648..7cb7ffc 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -50,6 +50,10 @@ impl State for Starting { binary, os_args ); let env_variables = if let Some(vars) = container.env() { + debug!( + "Got environment vars: {:?} service {}", + vars, pod_state.service_name + ); vars.into_iter() .map(|env_var| { ( @@ -59,6 +63,10 @@ impl State for Starting { }) .collect::>() } else { + debug!( + "No environment vars set for service {}", + pod_state.service_name + ); vec![] }; debug!( From 3847d0d6b6faabba008170c41bd1cb4b45b8f8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Tue, 1 Dec 2020 16:36:45 +0100 Subject: [PATCH 13/22] Addressed comments from Lars. --- Cargo.lock | 2 +- Cargo.toml | 7 +- config/agent_config | 2 +- src/agentconfig.rs | 87 +-- src/main.rs | 2 +- src/provider/error.rs | 29 +- src/provider/mod.rs | 19 +- src/provider/repository/mod.rs | 13 +- src/provider/repository/package.rs | 9 +- .../repository/stackablerepository.rs | 112 +++- src/provider/states.rs | 31 +- src/provider/states/create_config.rs | 296 ----------- src/provider/states/creating_config.rs | 503 ++++++++++++++++++ .../{download_package.rs => downloading.rs} | 39 +- ...kage_backoff.rs => downloading_backoff.rs} | 14 +- src/provider/states/failed.rs | 8 +- .../{install_package.rs => installing.rs} | 30 +- src/provider/states/running.rs | 7 +- src/provider/states/setup_failed.rs | 20 +- src/provider/states/starting.rs | 31 +- ...aiting_config.rs => waiting_config_map.rs} | 23 +- 21 files changed, 798 insertions(+), 486 deletions(-) delete mode 100644 src/provider/states/create_config.rs create mode 100644 src/provider/states/creating_config.rs rename src/provider/states/{download_package.rs => downloading.rs} (84%) rename src/provider/states/{download_package_backoff.rs => downloading_backoff.rs} (71%) rename src/provider/states/{install_package.rs => installing.rs} (80%) rename src/provider/states/{waiting_config.rs => waiting_config_map.rs} (50%) diff --git a/Cargo.lock b/Cargo.lock index edaee86..acd6832 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2573,7 +2573,7 @@ dependencies = [ [[package]] name = "stackable_config" version = "0.1.0" -source = "git+https://github.com/soenkeliebau/common.git?branch=generic_of_main#ddb6f81c3ae850da19bcb7d1a3b2e0c1ddece33f" +source = "git+https://github.com/stackabletech/common.git?branch=main#5285f8464ad55bbd11c3e96892759efe12fcdf7e" dependencies = [ "anyhow", "bstr", diff --git a/Cargo.toml b/Cargo.toml index 612c708..9ea69a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,12 @@ authors = ["Sönke Liebau "] edition = "2018" [dependencies] +# We are currently referencing the Krustlet directly from the repository, because some features that we are using have +# (exponential backoff most prominently) have not yet been included in a release +# We will look to move this to officially released versions as soon as possible kubelet = { git="https://github.com/deislabs/krustlet.git", rev="ac218b38ba564de806568e49d9e38aaef9f41537", default-features = true, features= ["derive", "cli"] } oci-distribution = { git="https://github.com/deislabs/krustlet.git", rev="ac218b38ba564de806568e49d9e38aaef9f41537"} + k8s-openapi = { version = "0.9", default-features = false, features = ["v1_18"] } kube = { version= "0.42", default-features = false, features = ["native-tls"] } kube-derive = "0.43" @@ -25,5 +29,4 @@ handlebars = "3.5" thiserror = "1.0" url = "2.2" pnet = "0.26.0" -#stackable_config = { path = "../common/config" } -stackable_config = { git = "https://github.com/soenkeliebau/common.git", branch = "generic_of_main" } +stackable_config = { git = "https://github.com/stackabletech/common.git", branch = "main" } diff --git a/config/agent_config b/config/agent_config index 937eaf3..e60cf79 100644 --- a/config/agent_config +++ b/config/agent_config @@ -1,6 +1,6 @@ --tag=test=test1 --tag=test2=test2 ---parcel-directory=/home/sliebau/IdeaProjects/agent/work/parcels +--package-directory=/home/sliebau/IdeaProjects/agent/work/packages --config-directory=/home/sliebau/IdeaProjects/agent/work/config --server-cert-file=/home/sliebau/Downloads/EasyRSA-3.0.8/pki/issued/krustlet1.crt --server-key-file=/home/sliebau/Downloads/EasyRSA-3.0.8/pki/private/pkcs8.key \ No newline at end of file diff --git a/src/agentconfig.rs b/src/agentconfig.rs index dd326b5..65fcaf9 100644 --- a/src/agentconfig.rs +++ b/src/agentconfig.rs @@ -1,8 +1,7 @@ use std::collections::hash_map::RandomState; use std::collections::{HashMap, HashSet}; -use std::fs; use std::net::IpAddr; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::str::FromStr; use log::{debug, error, info, trace}; @@ -45,7 +44,7 @@ impl AgentConfig { default: None, required: false, takes_argument: true, - help: "The local IP to bind the callback webserver to. Will be automatically set to the first address of the first non-loopback interface if not specified.", + help: "The certificate file for the local webserver which the Krustlet starts.", documentation: "", list: false, }; @@ -55,18 +54,24 @@ impl AgentConfig { default: None, required: false, takes_argument: true, - help: "The local IP to bind the callback webserver to. Will be automatically set to the first address of the first non-loopback interface if not specified.", + help: + "Private key file (in PKCS8 format) to use for the local webserver the Krustlet starts.", documentation: "", list: false, }; - pub const PARCEL_DIR: ConfigOption = ConfigOption { - name: "parcel-directory", - default: Some("/opt/stackable/parcels"), + pub const PACKAGE_DIR: ConfigOption = ConfigOption { + name: "package-directory", + default: Some("/opt/stackable/packages"), required: false, takes_argument: true, - help: "The base directory under which installed parcels will be stored.", - documentation: "Yak Yak!", + help: "The base directory under which installed packages will be stored.", + documentation: "This directory will serve as starting point for packages that are needed by \ + pods assigned to this node.\n Packages will be downloaded into the \"_download\" folder at the +top level of this folder as archives and remain there for potential future use.\n\ + Archives will the be extracted directly into this folder in subdirectories following the naming +scheme of \"productname-productversion\". + The agent will need full access to this directory and tries to create it if it does not exist.", list: false, }; @@ -76,7 +81,12 @@ impl AgentConfig { required: false, takes_argument: true, help: "The base directory under which configuration will be generated for all executed services.", - documentation: "Yak Yak!", + documentation: "This directory will serve as starting point for all log files which this service creates.\ + Every service will get its own subdirectories created within this directory - for every service start a \ + new subdirectory will be created to show a full history of configuration that was used for this service.\n + ConfigMaps that are mounted into the pod that describes this service will be created relative to these run \ + directories - unless the mounts specify an absolute path, in which case it is allowed to break out of this directory.\n\n\ + The agent will need full access to this directory and tries to create it if it does not exist.", list: false, }; @@ -86,7 +96,10 @@ impl AgentConfig { required: false, takes_argument: true, help: "The base directory under which log files will be placed for all services.", - documentation: "Yak Yak!", + documentation: "This directory will serve as starting point for all log files which this service creates.\ + Every service will get its own subdirectory created within this directory.\n + Anything that is then specified in the log4j config or similar files will be resolved relatively to this directory.\n\n\ + The agent will need full access to this directory and tries to create it if it does not exist.", list: false, }; @@ -115,7 +128,7 @@ impl AgentConfig { AgentConfig::SERVER_IP_ADDRESS, AgentConfig::SERVER_CERT_FILE, AgentConfig::SERVER_KEY_FILE, - AgentConfig::PARCEL_DIR, + AgentConfig::PACKAGE_DIR, AgentConfig::CONFIG_DIR, AgentConfig::LOG_DIR, AgentConfig::NO_CONFIG, @@ -126,6 +139,14 @@ impl AgentConfig { .collect() } + /// Helper method to ensure that for config options which only allow one value only one value + /// was specified. + /// In theory this should be unnecessary, as clap already enforces this check, but we still get + /// a vec, so in theory could have too many values in there - or none (in which case the default + /// should have been inserted by clap). + /// + /// If we get an incorrect number of arguments, a WrongArgumentCount error is returned which will + /// cause config parsing to panic. fn get_exactly_one_string( parsed_values: &HashMap>>, option: &ConfigOption, @@ -141,7 +162,6 @@ impl AgentConfig { ); if let Some(Some(list_value)) = parsed_values.get(option) { if list_value.len() != 1 { - //panic!(&format!("Expected exactly one value to be specified for parameter {} but got {} instead.", option.name.clone(), list_value.len().clone())); error!("Got additional, unexpected values for parameter!"); } else { // We've checked that the list has exactly one value at this point, so no errors should @@ -154,26 +174,16 @@ impl AgentConfig { }) } - fn get_at_least_one_string( - parsed_values: &HashMap>>, - option: &ConfigOption, - ) -> Vec { - if let Some(Some(list_value)) = parsed_values.get(option) { - if list_value.len() > 0 { - return list_value.clone(); - } - panic!("Unexpectedly got empty list of values for parameter".to_string()); - } else { - panic!("Parameter was not specified but a value is required!".to_string()); - } - } - + /// This tries to find the first non loopback interface with an ip address assigned. + /// This should usually be the default interface according to: + /// + /// https://docs.rs/pnet/0.27.2/pnet/datalink/fn.interfaces.html fn get_default_ipaddress() -> Option { let all_interfaces = datalink::interfaces(); let default_interface = all_interfaces .iter() - .filter(|e| e.is_up() && !e.is_loopback() && e.ips.len() > 0) + .filter(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty()) .next(); match default_interface { @@ -182,7 +192,7 @@ impl AgentConfig { "Found default interface {} with following ips: [{:?}].", interface, interface.ips ); - if let ipv4_network = interface.ips[0] { + if let Some(ipv4_network) = interface.ips.get(0) { return Some(ipv4_network.ip()); } } @@ -190,7 +200,7 @@ impl AgentConfig { "Error while finding the default interface - delegating ip retrieval to Kubelet." ), }; - return None; + None } } @@ -206,16 +216,15 @@ impl Configurable for AgentConfig { fn parse_values( parsed_values: HashMap>, RandomState>, - ) -> Result, anyhow::Error> { + ) -> Result { // Parse IP Address or lookup default let final_ip = if let Ok(ip) = AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::SERVER_IP_ADDRESS) { IpAddr::from_str(&ip).expect(&format!("Couldn't parse {} as a valid ip address!", ip)) } else { - AgentConfig::get_default_ipaddress().expect(&format!( - "Error getting default ip address, please specify it explicitly!" - )) + AgentConfig::get_default_ipaddress() + .expect("Error getting default ip address, please specify it explicitly!") }; info!("Selected {} as local address to listen on.", final_ip); @@ -255,7 +264,7 @@ impl Configurable for AgentConfig { // Parse parcel directory let final_parcel_dir = if let Ok(parcel_dir) = - AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::PARCEL_DIR) + AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::PACKAGE_DIR) { PathBuf::from_str(&parcel_dir).expect(&format!( "Error parsing valid parcel directory from string: {}", @@ -263,7 +272,7 @@ impl Configurable for AgentConfig { )) } else { PathBuf::from_str( - AgentConfig::PARCEL_DIR + AgentConfig::PACKAGE_DIR .default .expect("Invalid default value for parcel directory option!"), ) @@ -303,7 +312,7 @@ impl Configurable for AgentConfig { // key=value pair with just a log message -> so we panic if this can't be // parsed panic!(format!( - "Unable to parse value {} for option --tag as key=value pair!", + "Unable to parse value [{}] for option --tag as key=value pair!", tag )) } else { @@ -313,7 +322,7 @@ impl Configurable for AgentConfig { } } - Ok(Box::new(AgentConfig { + Ok(AgentConfig { parcel_directory: final_parcel_dir, config_directory: final_config_dir, log_directory: final_log_dir, @@ -321,6 +330,6 @@ impl Configurable for AgentConfig { server_cert_file: final_server_cert_file, server_key_file: final_server_key_file, tags: final_tags, - })) + }) } } diff --git a/src/main.rs b/src/main.rs index d22f77e..4affeb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ async fn main() -> anyhow::Result<()> { // a new Kubelet, all you need to implement is a provider. let agent_config: AgentConfig = - *ConfigBuilder::build(env::args_os().collect::>(), "CONFIG_FILE") + ConfigBuilder::build(env::args_os().collect::>(), "CONFIG_FILE") .expect("Error initializing Configuration!"); export_env( diff --git a/src/provider/error.rs b/src/provider/error.rs index c622ea3..2210426 100644 --- a/src/provider/error.rs +++ b/src/provider/error.rs @@ -3,6 +3,7 @@ use k8s_openapi::url; use thiserror::Error; use crate::provider::repository::package::Package; +use std::ffi::OsString; #[derive(Error, Debug)] pub enum StackableError { @@ -17,17 +18,31 @@ pub enum StackableError { #[error("error parsing package from containerimage string, has to be in the form of: \"repositoryname/package:version\"")] PackageParseError, #[error("Invalid content in pod object: {msg}")] - PodValidationError{msg: String}, - #[error(transparent)] - Kube(#[from] kube::Error), + PodValidationError { msg: String }, + #[error("Kubernetes reported error: {source}")] + KubeError { + #[from] + source: kube::Error, + }, #[error(transparent)] TemplateRenderError(#[from] RenderError), #[error(transparent)] TemplateError(#[from] TemplateError), #[error("A required CRD has not been registered: {missing_crds:?}")] - CrdMissing{missing_crds: Vec}, + CrdMissing { missing_crds: Vec }, #[error("Package {package} not found in repository")] - PackageNotFound{package: Package}, + PackageNotFound { package: Package }, #[error("{msg}")] - RuntimeError{msg: String} -} \ No newline at end of file + RuntimeError { msg: String }, + #[error("Unable to parse data for {target} from non-UTF8 String: {original:?}")] + DirectoryParseError { target: String, original: OsString }, + #[error("An error ocurred trying to write Config Map {config_map} to file {target_file}")] + ConfigFileWriteError { + target_file: String, + config_map: String, + }, + #[error( + "The following config maps were specified in a pod but not found: {missing_config_maps:?}" + )] + MissingConfigMapsError { missing_config_maps: Vec }, +} diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 47aba24..d3f4759 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -15,7 +15,7 @@ use log::{debug, error}; use crate::provider::error::StackableError; use crate::provider::error::StackableError::{CrdMissing, PodValidationError}; use crate::provider::repository::package::Package; -use crate::provider::states::download_package::Downloading; +use crate::provider::states::downloading::Downloading; use crate::provider::states::terminated::Terminated; pub struct StackableProvider { @@ -81,24 +81,24 @@ impl StackableProvider { } } - fn get_package(&self, pod: &Pod) -> Result { + fn get_package(pod: &Pod) -> Result { let containers = pod.containers(); - if containers.len().ne(&1) { + return if containers.len().ne(&1) { let e = PodValidationError { msg: String::from("Size of containers list in PodSpec has to be exactly 1"), }; - return Err(e); + Err(e) } else { // List has exactly one value, try to parse this if let Ok(Some(reference)) = containers[0].image() { - return Package::try_from(reference); + Package::try_from(reference) } else { let e = PodValidationError { - msg: String::from("Unable to get package reference from pod"), + msg: format!("Unable to get package reference from pod: {}", &pod.name()), }; - return Err(e); + Err(e) } - } + }; } async fn check_crds(&self) -> Vec { @@ -144,11 +144,12 @@ impl Provider for StackableProvider { async fn initialize_pod_state(&self, pod: &Pod) -> anyhow::Result { let service_name = pod.name(); let parcel_directory = self.parcel_directory.clone(); + // TODO: make this configurable let download_directory = parcel_directory.join("_download"); let config_directory = self.config_directory.clone(); let log_directory = self.log_directory.clone(); - let package = self.get_package(pod)?; + let package = Self::get_package(pod)?; if !(&download_directory.is_dir()) { fs::create_dir_all(&download_directory)?; } diff --git a/src/provider/repository/mod.rs b/src/provider/repository/mod.rs index b18a2a1..495e7be 100644 --- a/src/provider/repository/mod.rs +++ b/src/provider/repository/mod.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; -use kube::{Api, Client}; use kube::api::ListParams; +use kube::{Api, Client}; use log::{debug, trace}; use crate::provider::error::StackableError; @@ -13,7 +13,11 @@ pub mod package; pub mod repository; pub mod stackablerepository; -pub async fn find_repository(client: Client, package: &Package, repository_reference: Option) -> Result, StackableError> { +pub async fn find_repository( + client: Client, + package: &Package, + repository_reference: Option, +) -> Result, StackableError> { let repositories: Api = Api::namespaced(client.clone(), "default"); if let Some(repository_name) = repository_reference { // A repository name was provided, just check that exact repository for the package @@ -21,9 +25,8 @@ pub async fn find_repository(client: Client, package: &Package, repository_refer let mut repo = StackableRepoProvider::try_from(&repo)?; if repo.provides_package(package.clone()).await? { return Ok(Some(repo)); - } else { - return Ok(None); } + return Ok(None); } else { // No repository name was provided, retrieve all repositories from the orchestrator/apiserver // and check which one provides the package @@ -45,4 +48,4 @@ pub async fn find_repository(client: Client, package: &Package, repository_refer } } Ok(None) -} \ No newline at end of file +} diff --git a/src/provider/repository/package.rs b/src/provider/repository/package.rs index ce56be3..3fb2feb 100644 --- a/src/provider/repository/package.rs +++ b/src/provider/repository/package.rs @@ -13,10 +13,18 @@ pub struct Package { } impl Package { + /// Derive a standardized archive name to use when downloading this package into the + /// _download folder. + /// This helps with not downloading the same version of a product twice simply due to + /// different archive names. + /// Currently this assumes all archives to be in .tar.gz format, we might revisit this at + /// a later stage. pub fn get_file_name(&self) -> String { format!("{}.tar.gz", self.get_directory_name()) } + /// Derive a standardized name for the folder that this package should be installed to. + /// This helps avoiding duplicate binary installations due to different folder names. pub fn get_directory_name(&self) -> String { format!("{}-{}", self.product, self.version) } @@ -38,4 +46,3 @@ impl fmt::Display for Package { write!(f, "{}:{}", self.product, self.version) } } - diff --git a/src/provider/repository/stackablerepository.rs b/src/provider/repository/stackablerepository.rs index 85c1e09..8669ebe 100644 --- a/src/provider/repository/stackablerepository.rs +++ b/src/provider/repository/stackablerepository.rs @@ -26,7 +26,7 @@ pub struct StackableRepoProvider { #[derive(Serialize, Deserialize, Debug)] struct RepoData { version: String, - parcels: HashMap>, + packages: HashMap>, } #[derive(Serialize, Deserialize, Debug)] @@ -39,7 +39,7 @@ struct Product { #[derive(Serialize, Deserialize, Debug, Clone)] struct RepositoryContent { pub version: String, - pub parcels: HashMap>, + pub packages: HashMap>, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -54,15 +54,25 @@ impl StackableRepoProvider { pub fn new(name: String, base_url: String) -> Result { let base_url = Url::parse(&base_url)?; - Ok(StackableRepoProvider { base_url, name, content: None }) + Ok(StackableRepoProvider { + base_url, + name, + content: None, + }) } - pub async fn provides_package>(&mut self, package: T) -> Result { - debug!("Starting metadata refresh for repository of type {} at location {}", "StackableRepo", self.base_url); + pub async fn provides_package>( + &mut self, + package: T, + ) -> Result { + debug!( + "Starting metadata refresh for repository of type {} at location {}", + "StackableRepo", self.base_url + ); let package = package.into(); let metadata = self.get_repo_metadata().await?; debug!("Repository provides the following products: {:?}", metadata); - if let Some(product) = metadata.parcels.get(&package.product) { + if let Some(product) = metadata.packages.get(&package.product) { return Ok(product.contains_key(&package.version)); } Ok(false) @@ -73,7 +83,7 @@ impl StackableRepoProvider { self.get_repo_metadata().await?; } if let Some(content) = &self.content { - let parcels = &content.parcels; + let parcels = &content.packages; if let Some(product) = parcels.get(&package.product) { // product exists in repo if let Some(version) = product.get(&package.version) { @@ -82,10 +92,14 @@ impl StackableRepoProvider { } }; } - Err(PackageNotFound {package}) + Err(PackageNotFound { package }) } - pub async fn download_package(&mut self, package: &Package, target_path: PathBuf) -> Result<(), StackableError> { + pub async fn download_package( + &mut self, + package: &Package, + target_path: PathBuf, + ) -> Result<(), StackableError> { if self.content.is_none() { let _content = self.get_repo_metadata(); } @@ -94,7 +108,7 @@ impl StackableRepoProvider { let download_link = Url::parse(&stackable_package.link)?; let response = reqwest::get(download_link).await?; - let mut content = Cursor::new(response.bytes().await?); + let mut content = Cursor::new(response.bytes().await?); let mut out = File::create(target_path.join(package.get_file_name()))?; copy(&mut content, &mut out)?; @@ -119,8 +133,8 @@ impl StackableRepoProvider { debug!("Got repository metadata: {:?}", repo_data); - let mut parcels: HashMap> = HashMap::new(); - for (product, versions) in repo_data.parcels { + let mut packages: HashMap> = HashMap::new(); + for (product, versions) in repo_data.packages { let mut versionlist = HashMap::new(); for version in versions { versionlist.insert( @@ -129,22 +143,31 @@ impl StackableRepoProvider { product: product.clone(), version: version.version, link: self.resolve_url(version.path.clone())?, - hashes: version.hashes.clone() + hashes: version.hashes.clone(), }, ); } - parcels.insert(product, versionlist); + packages.insert(product, versionlist); } let repo_content: RepositoryContent = RepositoryContent { version: repo_data.version, - parcels, + packages, }; self.content = Some(repo_content.clone()); Ok(repo_content) } - fn resolve_url(&self, path: String) -> Result { - if let Result::Ok(absolute_link) = Url::parse(&path) { + /// Resolves relative paths that are defined for elements in this repository against + /// the repo's base URL. + /// Unless the element has an absolute URL defined, in this case the base URL is ignored + /// an the absolute URL returned unchanged. + /// + /// Public for testing + pub fn resolve_url(&self, path: String) -> Result { + if let Result::Ok(_) = Url::parse(&path) { + // The URL defined for this element is an absolute URL, so we won't + // resolve that agains the base url of the repository but simply + // return it unchanged return Ok(path); } let resolved_path = self.base_url.join(&path)?; @@ -164,10 +187,14 @@ impl TryFrom<&Repository> for StackableRepoProvider { fn try_from(value: &Repository) -> Result { let properties: HashMap = value.clone().spec.properties; let path = properties.get("url"); - match path { - Some(gna) => return Ok(StackableRepoProvider { name: Meta::name(value), base_url: Url::parse(gna)?, content: None }), - None => return Err(StackableError::RepositoryConversionError) + if let Some(valid_path) = path { + return Ok(StackableRepoProvider { + name: Meta::name(value), + base_url: Url::parse(valid_path)?, + content: None, + }); } + return Err(StackableError::RepositoryConversionError); } } @@ -187,11 +214,52 @@ impl Hash for StackableRepoProvider { #[cfg(test)] mod tests { + use crate::provider::repository::repository::RepoType::StackableRepo; + use crate::provider::repository::repository::{Repository, RepositorySpec}; + use crate::provider::repository::stackablerepository::StackableRepoProvider; + use std::collections::HashMap; + use std::convert::TryFrom; use url::Url; #[test] fn test_url_functions() { - assert!(true); + let repo = + StackableRepoProvider::new(String::from("test"), String::from("http://localhost:8000")) + .unwrap(); + + // Check that a relative URL is correctly resolved against the repo's baseurl + assert_eq!( + repo.resolve_url(String::from("test")).unwrap(), + "http://localhost:8000/test" + ); + + // Test that an absolute URL is correctly returned without change + assert_eq!( + repo.resolve_url(String::from("http://test.com/test")) + .unwrap(), + "http://test.com/test" + ); } -} + #[test] + fn test_repository_try_from() { + let mut props = HashMap::new(); + props.insert( + String::from("url"), + String::from("http://monitoring.stackable.demo:8000"), + ); + let test_repo_crd = Repository::new( + "test", + RepositorySpec { + repo_type: Default::default(), + properties: props, + }, + ); + let converted_repo = StackableRepoProvider::try_from(&test_repo_crd).unwrap(); + assert_eq!(converted_repo.name, "test"); + assert_eq!( + converted_repo.base_url.as_str(), + "http://monitoring.stackable.demo:8000/" + ); + } +} diff --git a/src/provider/states.rs b/src/provider/states.rs index 2eefe12..beef851 100644 --- a/src/provider/states.rs +++ b/src/provider/states.rs @@ -1,31 +1,16 @@ -pub(crate) mod download_package; -pub(crate) mod install_package; -pub(crate) mod create_config; -pub(crate) mod waiting_config; pub(crate) mod create_service; -pub(crate) mod download_package_backoff; +pub(crate) mod creating_config; +pub(crate) mod downloading; +pub(crate) mod downloading_backoff; +pub(crate) mod failed; +pub(crate) mod installing; +pub(crate) mod running; pub(crate) mod setup_failed; pub(crate) mod starting; -pub(crate) mod running; -pub(crate) mod stopping; pub(crate) mod stopped; -pub(crate) mod failed; +pub(crate) mod stopping; pub(crate) mod terminated; - - -/// When called in a state's `next` function, exits the current state -/// and transitions to the Error state. -#[macro_export] -macro_rules! transition_to_error { - ($slf:ident, $err:ident) => {{ - let aerr = anyhow::Error::from($err); - log::error!("{:?}", aerr); - let error_state = super::error::Error { - message: aerr.to_string(), - }; - return Transition::next($slf, error_state); - }}; -} +pub(crate) mod waiting_config_map; /// When called in a state's `next` function, exits the state machine /// returns a fatal error to the kubelet. diff --git a/src/provider/states/create_config.rs b/src/provider/states/create_config.rs deleted file mode 100644 index 03866d4..0000000 --- a/src/provider/states/create_config.rs +++ /dev/null @@ -1,296 +0,0 @@ -use std::collections::BTreeMap; -use std::fs; -use std::fs::read_to_string; -use std::path::PathBuf; - -use handlebars::Handlebars; -use k8s_openapi::api::core::v1::ConfigMap; -use kube::{Api, Client}; -use kubelet::pod::Pod; -use kubelet::state::prelude::*; -use kubelet::state::{State, Transition}; -use log::{debug, error, info, trace, warn}; - -use crate::fail_fatal; -use crate::provider::error::StackableError; -use crate::provider::error::StackableError::PodValidationError; -use crate::provider::states::create_service::CreatingService; -use crate::provider::states::setup_failed::SetupFailed; -use crate::provider::states::waiting_config::WaitingConfigMap; -use crate::provider::PodState; - -#[derive(Default, Debug, TransitionTo)] -#[transition_to(CreatingService, SetupFailed, WaitingConfigMap)] -pub struct CreatingConfig { - pub target_directory: Option, -} - -impl CreatingConfig { - pub fn render_config_template( - data: BTreeMap, - template: String, - ) -> Result { - let mut handlebars = Handlebars::new(); - debug!("Rendering template with context: {:?}", data); - - // register the template. The template string will be verified and compiled. - handlebars.register_template_string("t1", template)?; - - // Set strict mode, so that we fail with an error if any non-existent fields are accessed - handlebars.set_strict_mode(true); - - // Render the template with the provided data and return the resulting String - Ok(handlebars.render("t1", &data)?) - } - - pub fn create_render_data(pod_state: &PodState) -> BTreeMap { - let mut render_data = BTreeMap::new(); - let directory_name = pod_state.package.get_directory_name(); - - if let Ok(package_dir) = &pod_state - .get_service_package_directory() - .into_os_string() - .into_string() - { - render_data.insert(String::from("packageroot"), String::from(package_dir)); - } else { - warn!("Unable to parse value for package directory as UTF8") - } - - if let Ok(conf_dir) = &pod_state - .get_service_config_directory() - .into_os_string() - .into_string() - { - render_data.insert(String::from("configroot"), String::from(conf_dir)); - } else { - warn!("Unable to parse value for config directory as UTF8"); - } - - // Add {{logroot}} variable, this will resolve to the base-logdirectory/podname - if let Ok(log_dir) = &pod_state - .get_service_log_directory() - .into_os_string() - .into_string() - { - render_data.insert(String::from("logroot"), String::from(log_dir)); - } else { - warn!("Unable to parse value for log directory as UTF8"); - } - - // Return all template data - render_data - } - - async fn missing_config_maps(&self, client: Client, configmaps: Vec) -> Vec { - // TODO: distinguish between an actually missing configmap and an error when talking to - // the apiserver - let configmaps_api: Api = Api::namespaced(client.clone(), "default"); - let mut missing_configmaps = vec![]; - for map in configmaps { - let result = configmaps_api.get(&map).await; - match result { - Ok(configmap) => {} - Err(e) => { - debug!("ConfigMap {} not found due to error {:?}", &map, e); - missing_configmaps.push(String::from(map)); - } - } - } - missing_configmaps - } - - async fn get_config_maps(&self, pod: &Pod) -> Vec { - let mut get_config_maps = vec![]; - - if let Some(volumes) = pod.volumes() { - for volume in volumes { - if let Some(config_map) = &volume.config_map { - // config map was present, check if a name was set - // not sure when it would not be set, but it is a valid possibility, so we need - // to handle it - if no name is present, we'll just ignore this map, not sure - // how to retrieve it otherwise - if let Some(config_map_name) = &config_map.name { - debug!("Found reference to config map {}", &config_map_name); - get_config_maps.push(String::from(config_map_name)); - } - } - } - } - get_config_maps - } - - async fn retrieve_config_map( - &self, - client: Client, - name: String, - ) -> Result { - let config_maps: Api = Api::namespaced(client.clone(), "default"); - - Ok(config_maps.get(&name).await?) - } - - fn apply_config_map( - &self, - map: ConfigMap, - target_directory: PathBuf, - template_data: &BTreeMap, - ) -> Result<(), StackableError> { - let config_map_name = map.metadata.name.unwrap_or(String::from("undefined")); - debug!( - "applying configmap {} to directory {:?}", - &config_map_name, target_directory - ); - if !(&target_directory.is_dir()) { - info!("creating config directory {:?}", target_directory); - fs::create_dir_all(&target_directory)?; - } - if let Some(data) = map.data { - debug!("Map contained keys: {:?}", &data.keys()); - for key in data.keys() { - debug!("found key: {} in configmap {}", key, &config_map_name); - if let Some(content) = data.get(key) { - trace!("content of key: {}", &content); - debug!("rendering"); - let rendered_content = CreatingConfig::render_config_template( - template_data.clone(), - content.clone(), - )?; - debug!("done rendering"); - let target_file = target_directory.join(&key); - - // TODO: compare existing file with intended state - if CreatingConfig::needs_update(&target_file, &rendered_content)? { - debug!( - "writing content of map entry {} to file {:?}", - key, target_file - ); - let write_result = fs::write(target_directory.join(&key), rendered_content); - match write_result { - Ok(()) => debug!("write of file {:?} successful!", target_file), - Err(e) => error!("write of file {:?} failed: {}", target_file, e), - } - } else { - debug!("No update needed for {:?}", target_file); - } - } else { - info!("No content found for key {}", key); - } - } - } else { - debug!("No data found in ConfigMap.."); - } - Ok(()) - } - - fn needs_update(target_file: &PathBuf, content: &str) -> Result { - if target_file.is_file() { - let current_content = read_to_string(target_file)?; - debug!("Compared config file {:?} with result of", target_file); - return Ok(current_content.ne(content)); - } - debug!( - "Target config file {:?} doesn't exist, no need to compare.", - target_file - ); - Ok(true) - } -} - -#[async_trait::async_trait] -impl State for CreatingConfig { - async fn next( - mut self: Box, - pod_state: &mut PodState, - _pod: &Pod, - ) -> Transition { - let name = _pod.name(); - let client = pod_state.client.clone(); - let package = pod_state.package.clone(); - let config_directory = pod_state.config_directory.clone(); - //self.target_directory = Some(config_directory.join(package.get_directory_name())); - //let target_directory = self.target_directory.clone().unwrap(); - let target_directory = pod_state.get_service_config_directory(); - - // Check if all required config maps have been created in the api-server - let referenced_config_maps = self.get_config_maps(_pod).await; - let missing_config_maps = self - .missing_config_maps(client.clone(), referenced_config_maps) - .await; - if !missing_config_maps.is_empty() { - // not all configmaps are present - info!("Missing config maps, waiting.."); - return Transition::next( - self, - WaitingConfigMap { - missing_config_maps, - }, - ); - } - - debug!("Entering state \"creating config\" for service {}", name); - let containers = _pod.containers(); - if containers.len().ne(&1) { - let e = PodValidationError { - msg: "Only pods containing exactly one container element are supported!" - .to_string(), - }; - fail_fatal!(e); - } - let container = containers[0].clone(); - - if let Some(volumes) = _pod.volumes() { - debug!("Found {} volumes in pod {}", volumes.len(), _pod.name()); - if let Some(mounts) = container.volume_mounts() { - debug!("Found {} mounts in pod {}", mounts.len(), _pod.name()); - // Got mounts and volumes, we can now decide which ones we need to act upon - for mount in mounts { - for volume in volumes { - if mount.name.eq(&volume.name) { - let target_dir = target_directory.join(&mount.mount_path); - if let Some(config_map) = &volume.config_map { - if let Some(map_name) = &config_map.name { - if let Ok(map) = self - .retrieve_config_map(client.clone(), map_name.to_string()) - .await - { - debug!("found config map: {:?} - applying", config_map); - match self.apply_config_map( - map, - target_dir, - &CreatingConfig::create_render_data(pod_state), - ) { - Err(e) => { - return Transition::next( - self, - SetupFailed { - message: - "Failed to create file from config map!" - .to_string(), - }, - ); - } - _ => {} - } - } - } - } else { - warn!("Skipping volume {} - it is not a config map", volume.name); - } - } - } - } - }; - } - debug!("Transitioning to service creation"); - Transition::next(self, CreatingService) - } - - async fn json_status( - &self, - _pod_state: &mut PodState, - _pod: &Pod, - ) -> anyhow::Result { - make_status(Phase::Pending, &"status:initializing") - } -} diff --git a/src/provider/states/creating_config.rs b/src/provider/states/creating_config.rs new file mode 100644 index 0000000..7c59687 --- /dev/null +++ b/src/provider/states/creating_config.rs @@ -0,0 +1,503 @@ +use std::collections::{BTreeMap, HashMap}; +use std::fs; +use std::fs::read_to_string; +use std::path::PathBuf; + +use handlebars::Handlebars; +use k8s_openapi::api::core::v1::ConfigMap; +use kube::{Api, Client}; +use kubelet::pod::Pod; +use kubelet::state::prelude::*; +use kubelet::state::{State, Transition}; +use log::{debug, error, info, trace, warn}; + +use crate::fail_fatal; +use crate::provider::error::StackableError; +use crate::provider::error::StackableError::{ + ConfigFileWriteError, DirectoryParseError, MissingConfigMapsError, PodValidationError, + RuntimeError, +}; +use crate::provider::states::create_service::CreatingService; +use crate::provider::states::setup_failed::SetupFailed; +use crate::provider::states::waiting_config_map::WaitingConfigMap; +use crate::provider::PodState; +use kube::api::Meta; +use kube::error::ErrorResponse; + +#[derive(Default, Debug, TransitionTo)] +#[transition_to(CreatingService, SetupFailed, WaitingConfigMap)] +pub struct CreatingConfig { + pub target_directory: Option, +} + +impl CreatingConfig { + pub fn render_config_template( + data: &BTreeMap, + template: &str, + ) -> Result { + let mut handlebars = Handlebars::new(); + debug!("Rendering template with context: {:?}", data); + + // register the template. The template string will be verified and compiled. + handlebars.register_template_string("t1", template)?; + + // Set strict mode, so that we fail with an error if any non-existent fields are accessed + handlebars.set_strict_mode(true); + + // Render the template with the provided data and return the resulting String + Ok(handlebars.render("t1", &data)?) + } + + pub fn create_render_data( + pod_state: &PodState, + ) -> Result, StackableError> { + let mut render_data = BTreeMap::new(); + + render_data.insert( + String::from("packageroot"), + CreatingConfig::pathbuf_to_string( + "service package directory", + pod_state.get_service_package_directory(), + )?, + ); + render_data.insert( + String::from("configroot"), + CreatingConfig::pathbuf_to_string( + "service config directory", + pod_state.get_service_config_directory(), + )?, + ); + render_data.insert( + String::from("packageroot"), + CreatingConfig::pathbuf_to_string( + "service log directory", + pod_state.get_service_log_directory(), + )?, + ); + + // Return all template data + Ok(render_data) + } + + // Public for testing + pub fn pathbuf_to_string(target_field: &str, path: PathBuf) -> Result { + let path_as_string = path.into_os_string().into_string(); + match path_as_string { + Ok(valid_string) => { + return Ok(valid_string); + } + Err(non_utf8) => { + return Err(DirectoryParseError { + target: "package directory".to_string(), + original: non_utf8.clone(), + }) + } + } + } + + async fn retrieve_config_maps( + client: Client, + ns: &str, + configmaps: Vec, + ) -> Result, StackableError> { + // TODO: distinguish between an actually missing configmap and an error when talking to + // the apiserver + let configmaps_api: Api = Api::namespaced(client.clone(), ns); + let mut missing_configmaps = vec![]; + let mut found_configmaps = HashMap::new(); + for map in configmaps { + /* + match api.get(T::RESOURCE_NAME).await { + Ok(_) => { Ok(true) } + Err(kube::error::Error::Api(ErrorResponse {reason, .. })) if reason == "NotFound" => { println!("foo"); Ok(false) } + Err(err) => Err(error::Error::from(err)) + } + */ + match configmaps_api.get(&map).await { + Ok(config_map) => { + if let Some(map_name) = &config_map.metadata.name { + found_configmaps.insert(String::from(map_name), config_map); + } else { + warn!("Got config map {} with no name in metadata, this should never have happened!", map); + missing_configmaps.push(String::from(map)); + } + } + Err(kube::error::Error::Api(ErrorResponse { reason, .. })) + if reason == "NotFound" => + { + // ConfigMap was not created, add it to the list of missing config maps + debug!("ConfigMap {} not found", &map); + missing_configmaps.push(String::from(map)); + } + Err(e) => { + // An error occurred when communicating with the api server + // return immediately + debug!("Unable to retrieve config maps due to {:?}", e); + return Err(StackableError::from(e)); + } + } + } + if missing_configmaps.is_empty() { + return Ok(found_configmaps); + } + Err(MissingConfigMapsError { + missing_config_maps: missing_configmaps, + }) + } + + async fn get_config_maps(pod: &Pod) -> Vec { + let mut get_config_maps = vec![]; + + if let Some(volumes) = pod.volumes() { + for volume in volumes { + if let Some(config_map) = &volume.config_map { + // config map was present, check if a name was set + // not sure when it would not be set, but it is a valid possibility, so we need + // to handle it - if no name is present, we'll just ignore this map, not sure + // how to retrieve it otherwise + if let Some(config_map_name) = &config_map.name { + debug!("Found reference to config map {}", &config_map_name); + get_config_maps.push(String::from(config_map_name)); + } + } + } + } + get_config_maps + } + + /// + /// + fn apply_config_map( + map: &ConfigMap, + target_directory: PathBuf, + template_data: &BTreeMap, + ) -> Result<(), StackableError> { + if map.metadata.name.is_none() { + return Err(RuntimeError { + msg: String::from( + "Found ConfigMap with no Name set, this should never have happened", + ), + }); + } + let map = map.clone(); + let config_map_name = &map.metadata.name.expect("Got object with no name from K8s, even though we checked for this one line ago - something went seriously wrong!").clone(); + debug!( + "applying configmap {} to directory {:?}", + &config_map_name, target_directory + ); + if !(&target_directory.is_dir()) { + info!("creating config directory {:?}", target_directory); + fs::create_dir_all(&target_directory)?; + } + if let Some(data) = map.data.clone() { + debug!("Map contained keys: {:?}", &data.keys()); + for key in data.keys() { + debug!("found key: {} in configmap {}", key, &config_map_name); + if let Some(content) = data.get(key) { + trace!("content of key: {}", &content); + debug!("rendering"); + let rendered_content = + CreatingConfig::render_config_template(template_data, content)?; + debug!("done rendering"); + let target_file = target_directory.join(&key); + + // TODO: compare existing file with intended state + if CreatingConfig::needs_update(&target_file, &rendered_content)? { + debug!( + "writing content of map entry {} to file {:?}", + key, target_file + ); + let write_result = fs::write(target_directory.join(&key), rendered_content); + match write_result { + Ok(()) => debug!("write of file {:?} successful!", target_file), + Err(e) => { + error!("write of file {:?} failed: {}", target_file, e); + return Err(ConfigFileWriteError { + target_file: target_file.to_str().unwrap().to_string(), + config_map: config_map_name.clone(), + }); + } + } + } else { + debug!("No update needed for {:?}", target_file); + } + } else { + info!("No content found for key {}", key); + } + } + } else { + debug!("No data found in ConfigMap.."); + } + Ok(()) + } + + fn needs_update(target_file: &PathBuf, content: &str) -> Result { + if target_file.is_file() { + let current_content = read_to_string(target_file)?; + debug!("Compared config file {:?} with result of", target_file); + return Ok(current_content.ne(content)); + } + debug!( + "Target config file {:?} doesn't exist, no need to compare.", + target_file + ); + Ok(true) + } +} + +#[async_trait::async_trait] +impl State for CreatingConfig { + async fn next( + mut self: Box, + pod_state: &mut PodState, + pod: &Pod, + ) -> Transition { + // TODO: this entire function needs to be heavily refactored + let name = pod.name(); + let client = pod_state.client.clone(); + let target_directory = pod_state.get_service_config_directory(); + + // Check size of containers array, we currently only allow one container to be present, this + // might change in the future + debug!( + "Found all relevant config maps for service for service {}, writing config files.", + name + ); + let container = if pod.containers().len().ne(&1) { + let e = PodValidationError { + msg: "Only pods containing exactly one container element are supported!" + .to_string(), + }; + fail_fatal!(e); + } else { + pod.containers().get(0).unwrap().clone() + }; + + // Check if the container has mounts defined + let mounts = if let Some(mounts) = container.volume_mounts() { + // At least one mount is defined which is fine for now + mounts + } else { + // No mount defined, nothing to do for us + info!( + "No mounts defined for service {} - skipping create config step", + pod_state.service_name + ); + return Transition::next(self, CreatingService); + }; + + // Check if there are volumes defined for every mount + let volume_mounts = if let Some(volumes) = pod.volumes() { + debug!("Found {} volumes in pod {}", volumes.len(), name); + let mut result = HashMap::new(); + for mount in mounts { + for volume in volumes { + if mount.name.eq(&volume.name) { + // This mount references this volume, check if it is a config map volume + if let Some(map) = volume.config_map.clone() { + let map_name = map.name.unwrap().clone(); + result.insert(mount.mount_path.clone(), map_name); + } + } + } + } + result + } else { + warn!( + "No volumes found in service {}, but it had mounts defined. This is most probably an error that should have been caught by Kubernetes, but we'll try our best to continue!", + pod_state.service_name + ); + return Transition::next(self, CreatingService); + }; + + // We now have a map of directories to volumes and need to check if all config maps have + // been created in the api server + + // Retrieve all config map names that are referenced in the pods volume mounts + // TODO: refactor this to use the map created above + let referenced_config_maps = CreatingConfig::get_config_maps(pod).await; + + // Check if all required config maps have been created in the api-server + // Transition pod to retry state if some are missing or we geta kube error when + // communicating with the api server + let config_map_data = match CreatingConfig::retrieve_config_maps( + client.clone(), + pod.namespace(), + referenced_config_maps, + ) + .await + { + Ok(config_maps) => config_maps, + Err(MissingConfigMapsError { + missing_config_maps, + }) => { + warn!( + "Unable to find all required config maps for service {}, missing: {:?}", + pod_state.service_name, &missing_config_maps + ); + return Transition::next( + self, + WaitingConfigMap { + missing_config_maps, + }, + ); + } + Err(e) => { + // Not sure, shouldn't really happen, just do what we know: wait + return Transition::next( + self, + WaitingConfigMap { + missing_config_maps: vec![format!( + "An unexepected error occurred: {:?}", + e + )], + }, + ); + } + }; + + // At this point we have all config maps and their content that we need, otherwise the + // error cases in the above match statement would have moved the pod to the waiting for + // configmap state already + + let template_data = if let Ok(data) = CreatingConfig::create_render_data(&pod_state) { + data + } else { + error!("Unable to parse directories for command template as UTF8"); + return Transition::next( + self, + SetupFailed { + message: "Unable to parse directories for command template as UTF8".to_string(), + }, + ); + }; + + // Write the config files + let config_dir = pod_state.get_service_config_directory(); + for (target_path, volume) in volume_mounts { + let volume = volume.clone(); + let joined_target_path = config_dir.join(&target_path); + debug!("Applying config map {} to {}", volume, target_path); + if let Some(volume_content) = config_map_data.get(&volume) { + if let Err(e) = CreatingConfig::apply_config_map( + volume_content, + joined_target_path, + &template_data, + ) { + // Creation of config file failed! + } + } + + // Creation went well, carry on + } + + /* + if let Some(mounts) = container.volume_mounts() { + debug!("Found {} mounts in pod {}", mounts.len(), pod.name()); + // Got mounts and volumes, we can now decide which ones we need to act upon + let mut unexpectedly_missing_config_maps = vec![]; // track maps that could not be retrieved + for mount in mounts { + for volume in volumes { + if mount.name.eq(&volume.name) { + let target_dir = target_directory.join(&mount.mount_path); + if let Some(config_map) = &volume.config_map { + if let Some(map_name) = &config_map.name { + if let Some(map) = config_map_data.get(map) { + debug!("found config map: {:?} - applying", config_map); + match CreatingConfig::apply_config_map( + map, + target_dir, + &template_data, + ) { + Err(e) => { + return Transition::next( + self, + SetupFailed { + message: + "Failed to create file from config map!" + .to_string(), + }, + ); + } + _ => {} + } + } else { + // Config map not found in the previously retrieved data, this + // should not happen + warn!("Config map {} unexpectedly missing - this means that the pod object was created with a mount referencing a missing volume, which should have been caught by Kubernetes", map_name); + + // Add this map to the list of missing maps, this will trigger + // a transition to config map missing state after the loop + // Note: this may create a partial state on disk, but again, + // this should not happen, so I'm not too worried about it + unexpectedly_missing_config_maps.push(map_name.clone()); + } + } + } else { + warn!("Skipping volume {} - it is not a config map", volume.name); + } + } + } + } + if !unexpectedly_missing_config_maps.is_empty() { + // We were unable to retrieve a config map that was present + // beforehand - transition to waiting for config maps state + return Transition::next( + self, + WaitingConfigMap { + missing_config_maps: unexpectedly_missing_config_maps, + }, + ); + } + }; + */ + debug!("Transitioning to service creation"); + Transition::next(self, CreatingService) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, "status: creating config") + } +} + +#[cfg(test)] +mod tests { + use crate::provider::states::creating_config::CreatingConfig; + use std::collections::BTreeMap; + use std::path::PathBuf; + + #[test] + fn test_render_template() { + let mut context = BTreeMap::new(); + context.insert(String::from("var1"), String::from("test")); + context.insert(String::from("var2"), String::from("test2")); + context.insert(String::from("var3"), String::from("test3")); + + let template = "{{var1}}test{{var2}}test2{{var3}}test3"; + let rendered_string = "testtesttest2test2test3test3"; + + let test = CreatingConfig::render_config_template(&context, template).unwrap(); + + // Test if string is rendered correctly + assert_eq!(test, rendered_string); + + // Test if an undefined variable leads to an error + let template_with_undefined_var = "{{var4}}test"; + match CreatingConfig::render_config_template(&context, template_with_undefined_var) { + Ok(_) => assert!(false), + Err(_) => assert!(true), + } + } + + #[test] + fn test_pathbuf_string_conversion() { + let input_path_string = "/home/test/.kube/config"; + let legal_path = PathBuf::from(input_path_string); + let legal_path_string = CreatingConfig::pathbuf_to_string("testfield", legal_path).unwrap(); + assert_eq!(input_path_string, legal_path_string); + } +} diff --git a/src/provider/states/download_package.rs b/src/provider/states/downloading.rs similarity index 84% rename from src/provider/states/download_package.rs rename to src/provider/states/downloading.rs index e98272b..8154232 100644 --- a/src/provider/states/download_package.rs +++ b/src/provider/states/downloading.rs @@ -7,8 +7,8 @@ use log::{debug, error, info, warn}; use crate::provider::repository::find_repository; use crate::provider::repository::package::Package; -use crate::provider::states::download_package_backoff::DownloadingBackoff; -use crate::provider::states::install_package::Installing; +use crate::provider::states::downloading_backoff::DownloadingBackoff; +use crate::provider::states::installing::Installing; use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] @@ -16,11 +16,7 @@ use crate::provider::PodState; pub struct Downloading; impl Downloading { - fn package_downloaded>( - &self, - package: T, - download_directory: PathBuf, - ) -> bool { + fn package_downloaded>(package: T, download_directory: &PathBuf) -> bool { let package = package.into(); let package_file_name = download_directory.join(package.get_file_name()); debug!( @@ -41,7 +37,7 @@ impl State for Downloading { "Checking if package {} has already been downloaded.", package ); - if self.package_downloaded(package.clone(), pod_state.download_directory.clone()) { + if Downloading::package_downloaded(package.clone(), &pod_state.download_directory) { info!( "Package {} has already been downloaded to {:?}, continuing with installation", package, pod_state.download_directory @@ -56,10 +52,10 @@ impl State for Downloading { ); } let repo = find_repository(pod_state.client.clone(), &package, None).await; - match repo { + return match repo { Ok(Some(mut repo)) => { // We found a repository providing the package, proceed with download - // The repository has already downloaded its metadata it this time, as that + // The repository has already downloaded its metadata at this time, as that // was used to check whether it provides the package info!( "Starting download of package {} from repository {}", @@ -76,23 +72,23 @@ impl State for Downloading { package, download_directory.clone() ); - return Transition::next( + Transition::next( self, Installing { download_directory: pod_state.download_directory.clone(), parcel_directory: pod_state.parcel_directory.clone(), package: package.clone(), }, - ); + ) } Err(e) => { warn!("Download of package {} failed: {}", package, e); - return Transition::next( + Transition::next( self, DownloadingBackoff { package: package.clone(), }, - ); + ) } } } @@ -103,28 +99,27 @@ impl State for Downloading { &package ); error!("{}", &message); - return Transition::next( + Transition::next( self, DownloadingBackoff { package: package.clone(), }, - ); + ) } Err(e) => { // An error occurred when looking for a repository providing this package - let message = format!( + error!( "Error occurred trying to find package {}: {:?}", &package, e ); - error!("{}", &message); - return Transition::next( + Transition::next( self, DownloadingBackoff { package: package.clone(), }, - ); + ) } - } + }; } async fn json_status( @@ -132,6 +127,6 @@ impl State for Downloading { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:initializing") + make_status(Phase::Pending, &"Downloading package") } } diff --git a/src/provider/states/download_package_backoff.rs b/src/provider/states/downloading_backoff.rs similarity index 71% rename from src/provider/states/download_package_backoff.rs rename to src/provider/states/downloading_backoff.rs index 405da98..edec145 100644 --- a/src/provider/states/download_package_backoff.rs +++ b/src/provider/states/downloading_backoff.rs @@ -2,14 +2,13 @@ use kubelet::backoff::BackoffStrategy; use kubelet::state::prelude::*; use log::info; -use crate::provider::PodState; use crate::provider::repository::package::Package; -use crate::provider::states::download_package::Downloading; +use crate::provider::states::downloading::Downloading; +use crate::provider::PodState; #[derive(Debug, TransitionTo)] #[transition_to(Downloading)] -/// The Pod failed to run. -// If we manually implement, we can allow for arguments. +/// A setup step for the service failed. pub struct DownloadingBackoff { pub package: Package, } @@ -17,7 +16,10 @@ pub struct DownloadingBackoff { #[async_trait::async_trait] impl State for DownloadingBackoff { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - info!("Backing of before retrying download of package {}", self.package); + info!( + "Backing of before retrying download of package {}", + self.package + ); pod_state.package_download_backoff_strategy.wait().await; Transition::next(self, Downloading) } @@ -27,6 +29,6 @@ impl State for DownloadingBackoff { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:running") + make_status(Phase::Pending, &"Waiting to retry download of package") } } diff --git a/src/provider/states/failed.rs b/src/provider/states/failed.rs index 48b65c0..b8d7500 100644 --- a/src/provider/states/failed.rs +++ b/src/provider/states/failed.rs @@ -1,14 +1,12 @@ use kubelet::state::prelude::*; use log::{debug, info}; -use crate::provider::states::install_package::Installing; +use crate::provider::states::installing::Installing; use crate::provider::states::starting::Starting; use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] #[transition_to(Starting, Installing)] -/// The Pod failed to run. -// If we manually implement, we can allow for arguments. pub struct Failed { pub message: String, } @@ -26,10 +24,10 @@ impl Failed { #[async_trait::async_trait] impl State for Failed { - async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + async fn next(self: Box, _pod_state: &mut PodState, _pod: &Pod) -> Transition { info!("Process entered failed state"); if self.restart_enabled(_pod) { - debug!("Restart poliy is set to restart, starting..."); + debug!("Restart policy is set to restart, starting..."); return Transition::next(self, Starting {}); } else { debug!("Restart is disabled for process."); diff --git a/src/provider/states/install_package.rs b/src/provider/states/installing.rs similarity index 80% rename from src/provider/states/install_package.rs rename to src/provider/states/installing.rs index 4b07929..1366aba 100644 --- a/src/provider/states/install_package.rs +++ b/src/provider/states/installing.rs @@ -10,7 +10,7 @@ use tar::Archive; use crate::provider::error::StackableError; use crate::provider::repository::package::Package; -use crate::provider::states::create_config::CreatingConfig; +use crate::provider::states::creating_config::CreatingConfig; use crate::provider::states::setup_failed::SetupFailed; use crate::provider::PodState; @@ -40,7 +40,6 @@ impl Installing { fn install_package>(&self, package: T) -> Result<(), StackableError> { let package: Package = package.into(); - // To be on the safe side, check if the package is actually there let archive_path = self.download_directory.join(package.get_file_name()); let tar_gz = File::open(&archive_path)?; @@ -60,9 +59,9 @@ impl Installing { #[async_trait::async_trait] impl State for Installing { - async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { + async fn next(self: Box, _pod_state: &mut PodState, _pod: &Pod) -> Transition { let package = self.package.clone(); - if self.package_installed(package.clone()) { + return if self.package_installed(package.clone()) { info!("Package {} has already been installed", package); return Transition::next( self, @@ -73,25 +72,22 @@ impl State for Installing { } else { info!("Installing package {}", package); match self.install_package(package.clone()) { - Ok(()) => {} + Ok(()) => Transition::next( + self, + CreatingConfig { + target_directory: None, + }, + ), Err(e) => { return Transition::next( self, SetupFailed { - message: "".to_string(), + message: e.to_string(), }, - ); + ) } } - } - - debug!("installing package"); - Transition::next( - self, - CreatingConfig { - target_directory: None, - }, - ) + }; } async fn json_status( @@ -99,6 +95,6 @@ impl State for Installing { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:initializing") + make_status(Phase::Pending, &"Installing packages") } } diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs index 17a98d3..abeafa7 100644 --- a/src/provider/states/running.rs +++ b/src/provider/states/running.rs @@ -6,12 +6,11 @@ use kubelet::state::{State, Transition}; use log::{debug, error, trace}; use crate::provider::states::failed::Failed; -use crate::provider::states::install_package::Installing; +use crate::provider::states::installing::Installing; use crate::provider::states::stopping::Stopping; use crate::provider::PodState; use k8s_openapi::api::core::v1::{ - ContainerState, ContainerStateRunning, ContainerStateWaiting, - ContainerStatus as KubeContainerStatus, + ContainerState, ContainerStateRunning, ContainerStatus as KubeContainerStatus, }; #[derive(Debug, TransitionTo)] @@ -83,7 +82,7 @@ impl State for Running { }); Ok(make_status_with_containers( Phase::Running, - &"status:running", + "Running", container_status, vec![], )) diff --git a/src/provider/states/setup_failed.rs b/src/provider/states/setup_failed.rs index b6585a4..31afe99 100644 --- a/src/provider/states/setup_failed.rs +++ b/src/provider/states/setup_failed.rs @@ -1,22 +1,30 @@ use kubelet::state::prelude::*; use log::{error, info}; +use crate::provider::states::downloading::Downloading; use crate::provider::PodState; -use crate::provider::states::download_package::Downloading; #[derive(Default, Debug, TransitionTo)] #[transition_to(Downloading)] -/// The Pod failed to run. -// If we manually implement, we can allow for arguments. +/// A setup step for the service failed, this can be one of the following: +/// - Download Package +/// - Install Package +/// - Create Config +/// - Create Service pub struct SetupFailed { pub message: String, } #[async_trait::async_trait] impl State for SetupFailed { - async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - error!("setup failed for pod {} due to: {}", _pod.name(), self.message); + async fn next(self: Box, _pod_state: &mut PodState, pod: &Pod) -> Transition { + error!( + "setup failed for pod {} due to: {}", + pod.name(), + self.message + ); info!("Waiting for {} seconds before retrying..", 10); + // TODO: make this configurable tokio::time::delay_for(std::time::Duration::from_secs(10)).await; Transition::next(self, Downloading) } @@ -26,6 +34,6 @@ impl State for SetupFailed { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &self.message) + make_status(Phase::Pending, "SetupFailed") } } diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 7cb7ffc..e7640e0 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -7,21 +7,34 @@ use kubelet::state::{State, Transition}; use log::{debug, error, info, trace}; use tokio::time::Duration; -use crate::provider::states::create_config::CreatingConfig; +use crate::provider::states::creating_config::CreatingConfig; use crate::provider::states::failed::Failed; use crate::provider::states::running::Running; +use crate::provider::states::setup_failed::SetupFailed; use crate::provider::PodState; #[derive(Default, Debug, TransitionTo)] -#[transition_to(Running, Failed)] +#[transition_to(Running, Failed, SetupFailed)] pub struct Starting; #[async_trait::async_trait] impl State for Starting { - async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - let container = _pod.containers()[0].clone(); - let template_data = CreatingConfig::create_render_data(&pod_state); + async fn next(self: Box, pod_state: &mut PodState, pod: &Pod) -> Transition { + let container = pod.containers()[0].clone(); + let template_data = if let Ok(data) = CreatingConfig::create_render_data(&pod_state) { + data + } else { + error!("Unable to parse directories for command template as UTF8"); + return Transition::next( + self, + SetupFailed { + message: "Unable to parse directories for command template as UTF8".to_string(), + }, + ); + }; if let Some(mut command) = container.command().clone() { + // We need to reverse the vec here, because pop works on the wrong "end" of + // the vec for our purposes debug!("Reversing {:?}", &command); command.reverse(); debug!("Processing {:?}", &command); @@ -36,13 +49,7 @@ impl State for Starting { let os_args: Vec = command .iter() - .map(|s| { - CreatingConfig::render_config_template( - template_data.clone(), - String::from(s), - ) - .unwrap() - }) + .map(|s| CreatingConfig::render_config_template(&template_data, s).unwrap()) .collect(); debug!( diff --git a/src/provider/states/waiting_config.rs b/src/provider/states/waiting_config_map.rs similarity index 50% rename from src/provider/states/waiting_config.rs rename to src/provider/states/waiting_config_map.rs index 1dab67b..01fe39d 100644 --- a/src/provider/states/waiting_config.rs +++ b/src/provider/states/waiting_config_map.rs @@ -2,24 +2,33 @@ use kubelet::backoff::BackoffStrategy; use kubelet::state::prelude::*; use log::info; +use crate::provider::states::creating_config::CreatingConfig; use crate::provider::PodState; -use crate::provider::states::create_config::CreatingConfig; #[derive(Debug, TransitionTo)] #[transition_to(CreatingConfig)] -/// The Pod failed to run. -// If we manually implement, we can allow for arguments. +/// A config map that was specified in the pod has not yet been created in the apiserver, back off +/// until this has been created +/// TODO: make this a watch instead of delay pub struct WaitingConfigMap { - pub missing_config_maps: Vec , + pub missing_config_maps: Vec, } #[async_trait::async_trait] impl State for WaitingConfigMap { async fn next(self: Box, pod_state: &mut PodState, _pod: &Pod) -> Transition { - info!("Delaying execution due to missing configmaps: {:?}", &self.missing_config_maps); + info!( + "Delaying execution due to missing configmaps: {:?}", + &self.missing_config_maps + ); pod_state.package_download_backoff_strategy.wait().await; - Transition::next(self, CreatingConfig { target_directory: None }) + Transition::next( + self, + CreatingConfig { + target_directory: None, + }, + ) } async fn json_status( @@ -27,6 +36,6 @@ impl State for WaitingConfigMap { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:running") + make_status(Phase::Pending, &"WaitingConfigMap") } } From 4115e5c7f09e9b09f742e7f77b7f4536b3baacbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Tue, 1 Dec 2020 16:47:21 +0100 Subject: [PATCH 14/22] Forgot to address comments in main.rs. --- src/main.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4affeb7..0c1c027 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,11 @@ use std::env; use std::ffi::OsString; -use std::net::IpAddr; -use std::path::PathBuf; use kube::config::Config as KubeConfig; use kube::config::KubeConfigOptions; -use kubelet::config::{Config, ServerConfig}; +use kubelet::config::Config; use kubelet::Kubelet; -use log::{debug, info, warn}; +use log::{info, warn}; use pnet::datalink; use stackable_config::ConfigBuilder; @@ -22,19 +20,24 @@ async fn main() -> anyhow::Result<()> { // Initialize the logger env_logger::init(); - // The provider is responsible for all the "back end" logic. If you are creating - // a new Kubelet, all you need to implement is a provider. - let agent_config: AgentConfig = ConfigBuilder::build(env::args_os().collect::>(), "CONFIG_FILE") .expect("Error initializing Configuration!"); + // Currently the only way to _properly_ configure the Krustlet is via these environment exports, + // as their config object only offers methods that parse from command line flags (or combinations + // of those flags with other things). + // Since we have our own command line flags that are not compatible with the Krustlet's we + // configure the agent via a file from the environment variable (CONFIG_FILE), extract what + // is needed for the Krustlet and pass it via environment variables. + // This is an ugly hack for now, until we've had time to take a proper look at Krustlet's config export_env( "KRUSTLET_NODE_IP", &agent_config.server_ip_address.to_string(), ); // Convert node tags to string in the form of key=value,key=value,... + // TODO: check for commas in the key value pairs themselves let node_labels = agent_config .tags .iter() @@ -58,13 +61,10 @@ async fn main() -> anyhow::Result<()> { info!("args: {:?}", env::args()); let krustlet_config = Config::new_from_flags(env!("CARGO_PKG_VERSION")); - //let kubeconfig = kubelet::bootstrap(&config, &config.bootstrap_file, notify_bootstrap).await?; let kubeconfig = KubeConfig::from_kubeconfig(&KubeConfigOptions::default()) .await .expect("Failed to create Kubernetes Client!"); - //let parcel_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/parcels"); - //let config_directory = PathBuf::from("/home/sliebau/IdeaProjects/agent/work/config"); let provider = StackableProvider::new( kube::Client::new(kubeconfig.clone()), agent_config.parcel_directory.clone(), From 68dfb0f9bc0bc131cfb70579b2df389580adaf12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 10:04:15 +0100 Subject: [PATCH 15/22] Second round of review comments by Lars addressed. --- config/agent_config | 6 - src/main.rs | 1 - src/provider/error.rs | 2 - src/provider/mod.rs | 34 ++++-- src/provider/repository/mod.rs | 10 +- src/provider/repository/package.rs | 3 + .../repository/stackablerepository.rs | 2 - src/provider/states/creating_config.rs | 109 ++++-------------- src/provider/states/downloading_backoff.rs | 2 +- src/provider/states/installing.rs | 16 ++- src/provider/states/running.rs | 4 +- 11 files changed, 65 insertions(+), 124 deletions(-) delete mode 100644 config/agent_config diff --git a/config/agent_config b/config/agent_config deleted file mode 100644 index e60cf79..0000000 --- a/config/agent_config +++ /dev/null @@ -1,6 +0,0 @@ ---tag=test=test1 ---tag=test2=test2 ---package-directory=/home/sliebau/IdeaProjects/agent/work/packages ---config-directory=/home/sliebau/IdeaProjects/agent/work/config ---server-cert-file=/home/sliebau/Downloads/EasyRSA-3.0.8/pki/issued/krustlet1.crt ---server-key-file=/home/sliebau/Downloads/EasyRSA-3.0.8/pki/private/pkcs8.key \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 0c1c027..5a3bd37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ use kube::config::KubeConfigOptions; use kubelet::config::Config; use kubelet::Kubelet; use log::{info, warn}; -use pnet::datalink; use stackable_config::ConfigBuilder; use crate::agentconfig::AgentConfig; diff --git a/src/provider/error.rs b/src/provider/error.rs index 2210426..b7e61b5 100644 --- a/src/provider/error.rs +++ b/src/provider/error.rs @@ -15,8 +15,6 @@ pub enum StackableError { IO(#[from] std::io::Error), #[error("unable to create repository from received repo object")] RepositoryConversionError, - #[error("error parsing package from containerimage string, has to be in the form of: \"repositoryname/package:version\"")] - PackageParseError, #[error("Invalid content in pod object: {msg}")] PodValidationError { msg: String }, #[error("Kubernetes reported error: {source}")] diff --git a/src/provider/mod.rs b/src/provider/mod.rs index d3f4759..f4210c9 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -13,10 +13,11 @@ use kubelet::provider::Provider; use log::{debug, error}; use crate::provider::error::StackableError; -use crate::provider::error::StackableError::{CrdMissing, PodValidationError}; +use crate::provider::error::StackableError::{CrdMissing, KubeError, PodValidationError}; use crate::provider::repository::package::Package; use crate::provider::states::downloading::Downloading; use crate::provider::states::terminated::Terminated; +use kube::error::ErrorResponse; pub struct StackableProvider { client: Client, @@ -71,7 +72,7 @@ impl StackableProvider { config_directory, log_directory, }; - let missing_crds = provider.check_crds().await; + let missing_crds = provider.check_crds().await?; if missing_crds.is_empty() { debug!("All required CRDS present!"); return Ok(provider); @@ -101,22 +102,31 @@ impl StackableProvider { }; } - async fn check_crds(&self) -> Vec { + async fn check_crds(&self) -> Result, StackableError> { let mut missing_crds = vec![]; let crds: Api = Api::all(self.client.clone()); // Check all CRDS for crd in CRDS.into_iter() { - debug!("Checking if CRD \"{}\" is registered", crd); + debug!("Checking if CRD [{}] is registered", crd); match crds.get(crd).await { - Err(e) => { - error!("Missing required CRD: \"{}\"", crd); + Err(kube::error::Error::Api(ErrorResponse { reason, .. })) + if reason == "NotFound" => + { + error!("Missing required CRD: [{}]", crd); missing_crds.push(String::from(*crd)) } - _ => debug!("Found registered crd: {}", crd), + Err(e) => { + error!( + "An error ocurred when checking if CRD [{}] is registered: \"{}\"", + crd, e + ); + return Err(KubeError { source: e }); + } + _ => debug!("Found registered crd: [{}]", crd), } } - missing_crds + Ok(missing_crds) } } @@ -172,10 +182,10 @@ impl Provider for StackableProvider { async fn logs( &self, - namespace: String, - pod: String, - container: String, - sender: Sender, + _namespace: String, + _pod: String, + _container: String, + _sender: Sender, ) -> anyhow::Result<()> { Ok(()) } diff --git a/src/provider/repository/mod.rs b/src/provider/repository/mod.rs index 495e7be..9d44c22 100644 --- a/src/provider/repository/mod.rs +++ b/src/provider/repository/mod.rs @@ -33,17 +33,19 @@ pub async fn find_repository( let list_params = ListParams::default(); let repos = repositories.list(&list_params).await?; for repository in repos.iter() { - let repo: &Repository = repository; - debug!("got repo definition: {:?}", repository); + debug!("got repo definition: [{:?}]", repository); // Convert repository to object implementing our trait // TODO: add generic implementation here to support different types of repository let mut repo = StackableRepoProvider::try_from(repository)?; trace!("converted to stackable repo: {:?}", repository); if repo.provides_package(package.clone()).await? { - debug!("Found package {} in repository {}", &package, repo); + debug!("Found package [{}] in repository [{}]", &package, repo); return Ok(Some(repo)); } else { - debug!("Package {} not provided by repository {}", &package, repo); + debug!( + "Package [{}] not provided by repository [{}]", + &package, repo + ); } } } diff --git a/src/provider/repository/package.rs b/src/provider/repository/package.rs index 3fb2feb..c18d6cb 100644 --- a/src/provider/repository/package.rs +++ b/src/provider/repository/package.rs @@ -33,6 +33,9 @@ impl Package { impl TryFrom for Package { type Error = StackableError; + // Converts from an oci reference to a package representation + // The oci tag (anything after the \":\" in the string) is used as + // version by this code and needs to be present fn try_from(value: Reference) -> Result { Ok(Package { product: String::from(value.repository()), diff --git a/src/provider/repository/stackablerepository.rs b/src/provider/repository/stackablerepository.rs index 8669ebe..5f186b1 100644 --- a/src/provider/repository/stackablerepository.rs +++ b/src/provider/repository/stackablerepository.rs @@ -214,12 +214,10 @@ impl Hash for StackableRepoProvider { #[cfg(test)] mod tests { - use crate::provider::repository::repository::RepoType::StackableRepo; use crate::provider::repository::repository::{Repository, RepositorySpec}; use crate::provider::repository::stackablerepository::StackableRepoProvider; use std::collections::HashMap; use std::convert::TryFrom; - use url::Url; #[test] fn test_url_functions() { diff --git a/src/provider/states/creating_config.rs b/src/provider/states/creating_config.rs index 7c59687..1e84ead 100644 --- a/src/provider/states/creating_config.rs +++ b/src/provider/states/creating_config.rs @@ -21,7 +21,6 @@ use crate::provider::states::create_service::CreatingService; use crate::provider::states::setup_failed::SetupFailed; use crate::provider::states::waiting_config_map::WaitingConfigMap; use crate::provider::PodState; -use kube::api::Meta; use kube::error::ErrorResponse; #[derive(Default, Debug, TransitionTo)] @@ -82,17 +81,13 @@ impl CreatingConfig { // Public for testing pub fn pathbuf_to_string(target_field: &str, path: PathBuf) -> Result { let path_as_string = path.into_os_string().into_string(); - match path_as_string { - Ok(valid_string) => { - return Ok(valid_string); - } - Err(non_utf8) => { - return Err(DirectoryParseError { - target: "package directory".to_string(), - original: non_utf8.clone(), - }) - } - } + return match path_as_string { + Ok(valid_string) => return Ok(valid_string), + Err(non_utf8) => Err(DirectoryParseError { + target: target_field.to_string(), + original: non_utf8.clone(), + }), + }; } async fn retrieve_config_maps( @@ -106,13 +101,6 @@ impl CreatingConfig { let mut missing_configmaps = vec![]; let mut found_configmaps = HashMap::new(); for map in configmaps { - /* - match api.get(T::RESOURCE_NAME).await { - Ok(_) => { Ok(true) } - Err(kube::error::Error::Api(ErrorResponse {reason, .. })) if reason == "NotFound" => { println!("foo"); Ok(false) } - Err(err) => Err(error::Error::from(err)) - } - */ match configmaps_api.get(&map).await { Ok(config_map) => { if let Some(map_name) = &config_map.metadata.name { @@ -169,7 +157,7 @@ impl CreatingConfig { /// fn apply_config_map( map: &ConfigMap, - target_directory: PathBuf, + target_directory: &PathBuf, template_data: &BTreeMap, ) -> Result<(), StackableError> { if map.metadata.name.is_none() { @@ -255,7 +243,6 @@ impl State for CreatingConfig { // TODO: this entire function needs to be heavily refactored let name = pod.name(); let client = pod_state.client.clone(); - let target_directory = pod_state.get_service_config_directory(); // Check size of containers array, we currently only allow one container to be present, this // might change in the future @@ -377,80 +364,32 @@ impl State for CreatingConfig { for (target_path, volume) in volume_mounts { let volume = volume.clone(); let joined_target_path = config_dir.join(&target_path); + debug!("Applying config map {} to {}", volume, target_path); if let Some(volume_content) = config_map_data.get(&volume) { if let Err(e) = CreatingConfig::apply_config_map( volume_content, - joined_target_path, + &joined_target_path, &template_data, ) { // Creation of config file failed! + let error_message = format!( + "Failed to create config file [{:?}] from config map [{}] due to: {:?}", + &joined_target_path.to_str(), + volume, + e + ); + error!("{}", &error_message); + return Transition::next( + self, + SetupFailed { + message: error_message, + }, + ); } } - // Creation went well, carry on } - - /* - if let Some(mounts) = container.volume_mounts() { - debug!("Found {} mounts in pod {}", mounts.len(), pod.name()); - // Got mounts and volumes, we can now decide which ones we need to act upon - let mut unexpectedly_missing_config_maps = vec![]; // track maps that could not be retrieved - for mount in mounts { - for volume in volumes { - if mount.name.eq(&volume.name) { - let target_dir = target_directory.join(&mount.mount_path); - if let Some(config_map) = &volume.config_map { - if let Some(map_name) = &config_map.name { - if let Some(map) = config_map_data.get(map) { - debug!("found config map: {:?} - applying", config_map); - match CreatingConfig::apply_config_map( - map, - target_dir, - &template_data, - ) { - Err(e) => { - return Transition::next( - self, - SetupFailed { - message: - "Failed to create file from config map!" - .to_string(), - }, - ); - } - _ => {} - } - } else { - // Config map not found in the previously retrieved data, this - // should not happen - warn!("Config map {} unexpectedly missing - this means that the pod object was created with a mount referencing a missing volume, which should have been caught by Kubernetes", map_name); - - // Add this map to the list of missing maps, this will trigger - // a transition to config map missing state after the loop - // Note: this may create a partial state on disk, but again, - // this should not happen, so I'm not too worried about it - unexpectedly_missing_config_maps.push(map_name.clone()); - } - } - } else { - warn!("Skipping volume {} - it is not a config map", volume.name); - } - } - } - } - if !unexpectedly_missing_config_maps.is_empty() { - // We were unable to retrieve a config map that was present - // beforehand - transition to waiting for config maps state - return Transition::next( - self, - WaitingConfigMap { - missing_config_maps: unexpectedly_missing_config_maps, - }, - ); - } - }; - */ debug!("Transitioning to service creation"); Transition::next(self, CreatingService) } @@ -460,7 +399,7 @@ impl State for CreatingConfig { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, "status: creating config") + make_status(Phase::Pending, "CreatingConfig") } } diff --git a/src/provider/states/downloading_backoff.rs b/src/provider/states/downloading_backoff.rs index edec145..5563c22 100644 --- a/src/provider/states/downloading_backoff.rs +++ b/src/provider/states/downloading_backoff.rs @@ -29,6 +29,6 @@ impl State for DownloadingBackoff { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"Waiting to retry download of package") + make_status(Phase::Pending, &"DownloadingBackoff") } } diff --git a/src/provider/states/installing.rs b/src/provider/states/installing.rs index 1366aba..ddfc281 100644 --- a/src/provider/states/installing.rs +++ b/src/provider/states/installing.rs @@ -78,14 +78,12 @@ impl State for Installing { target_directory: None, }, ), - Err(e) => { - return Transition::next( - self, - SetupFailed { - message: e.to_string(), - }, - ) - } + Err(e) => Transition::next( + self, + SetupFailed { + message: e.to_string(), + }, + ), } }; } @@ -95,6 +93,6 @@ impl State for Installing { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"Installing packages") + make_status(Phase::Pending, &"Installing") } } diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs index abeafa7..31271d9 100644 --- a/src/provider/states/running.rs +++ b/src/provider/states/running.rs @@ -64,14 +64,14 @@ impl State for Running { async fn json_status( &self, _pod_state: &mut PodState, - _pod: &Pod, + pod: &Pod, ) -> anyhow::Result { let state = ContainerState { running: Some(ContainerStateRunning { started_at: None }), ..Default::default() }; - let mut container = &_pod.containers()[0]; + let container = &pod.containers()[0]; let mut container_status = vec![]; container_status.push(KubeContainerStatus { name: container.name().to_string(), From 3852c54f35700258cfed4c0d2a192b3c5c18911e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 10:29:51 +0100 Subject: [PATCH 16/22] Fix rustfmt complaints. --- src/provider/repository/repository.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/provider/repository/repository.rs b/src/provider/repository/repository.rs index 22b3c16..16ac7d1 100644 --- a/src/provider/repository/repository.rs +++ b/src/provider/repository/repository.rs @@ -5,10 +5,10 @@ use serde::{Deserialize, Serialize}; #[derive(CustomResource, Serialize, Deserialize, Default, Clone, Debug)] #[kube( -kind = "Repository", -group = "stable.stackable.de", -version = "v1", -namespaced + kind = "Repository", + group = "stable.stackable.de", + version = "v1", + namespaced )] pub struct RepositorySpec { pub repo_type: RepoType, From 51ba04418c7584867944e32bd321542e0c8b7d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 12:11:03 +0100 Subject: [PATCH 17/22] Hopefully last remnants --- .github/workflows/rust.yml | 2 +- crd/repository.crd | 30 +++++++++++++++++++ src/provider/mod.rs | 10 +++---- src/provider/states.rs | 2 +- src/provider/states/creating_config.rs | 2 +- ...{create_service.rs => creating_service.rs} | 2 +- src/provider/states/downloading.rs | 4 +-- src/provider/states/failed.rs | 6 ---- src/provider/states/starting.rs | 2 +- src/provider/states/stopped.rs | 2 +- src/provider/states/stopping.rs | 2 +- 11 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 crd/repository.crd rename src/provider/states/{create_service.rs => creating_service.rs} (93%) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f4021f5..7ac2168 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,7 +25,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: toolchain: nightly - components: clippy + components: clippy, rustfmt override: true - uses: actions-rs/clippy-check@v1 with: diff --git a/crd/repository.crd b/crd/repository.crd new file mode 100644 index 0000000..452810b --- /dev/null +++ b/crd/repository.crd @@ -0,0 +1,30 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: repositories.stable.stackable.de +spec: + group: stable.stackable.de + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + repo_type: + type: string + properties: + type: object + additionalProperties: + type: string + scope: Namespaced + names: + plural: repositories + singular: repository + kind: Repository + shortNames: + - repo \ No newline at end of file diff --git a/src/provider/mod.rs b/src/provider/mod.rs index f4210c9..d7962df 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -73,13 +73,13 @@ impl StackableProvider { log_directory, }; let missing_crds = provider.check_crds().await?; - if missing_crds.is_empty() { + return if missing_crds.is_empty() { debug!("All required CRDS present!"); - return Ok(provider); + Ok(provider) } else { - debug!("Missing required CDRS"); - return Err(CrdMissing { missing_crds }); - } + debug!("Missing required CDRS: [{:?}]", &missing_crds); + Err(CrdMissing { missing_crds }) + }; } fn get_package(pod: &Pod) -> Result { diff --git a/src/provider/states.rs b/src/provider/states.rs index beef851..b1d8fe3 100644 --- a/src/provider/states.rs +++ b/src/provider/states.rs @@ -1,5 +1,5 @@ -pub(crate) mod create_service; pub(crate) mod creating_config; +pub(crate) mod creating_service; pub(crate) mod downloading; pub(crate) mod downloading_backoff; pub(crate) mod failed; diff --git a/src/provider/states/creating_config.rs b/src/provider/states/creating_config.rs index 1e84ead..93d0a0c 100644 --- a/src/provider/states/creating_config.rs +++ b/src/provider/states/creating_config.rs @@ -17,7 +17,7 @@ use crate::provider::error::StackableError::{ ConfigFileWriteError, DirectoryParseError, MissingConfigMapsError, PodValidationError, RuntimeError, }; -use crate::provider::states::create_service::CreatingService; +use crate::provider::states::creating_service::CreatingService; use crate::provider::states::setup_failed::SetupFailed; use crate::provider::states::waiting_config_map::WaitingConfigMap; use crate::provider::PodState; diff --git a/src/provider/states/create_service.rs b/src/provider/states/creating_service.rs similarity index 93% rename from src/provider/states/create_service.rs rename to src/provider/states/creating_service.rs index a773a2f..05e045c 100644 --- a/src/provider/states/create_service.rs +++ b/src/provider/states/creating_service.rs @@ -26,6 +26,6 @@ impl State for CreatingService { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:initializing") + make_status(Phase::Pending, &"CreatingService") } } diff --git a/src/provider/states/downloading.rs b/src/provider/states/downloading.rs index 8154232..23bb2cb 100644 --- a/src/provider/states/downloading.rs +++ b/src/provider/states/downloading.rs @@ -109,7 +109,7 @@ impl State for Downloading { Err(e) => { // An error occurred when looking for a repository providing this package error!( - "Error occurred trying to find package {}: {:?}", + "Error occurred trying to find package [{}]: [{:?}]", &package, e ); Transition::next( @@ -127,6 +127,6 @@ impl State for Downloading { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"Downloading package") + make_status(Phase::Pending, &"Downloading") } } diff --git a/src/provider/states/failed.rs b/src/provider/states/failed.rs index b8d7500..6e3e4b9 100644 --- a/src/provider/states/failed.rs +++ b/src/provider/states/failed.rs @@ -32,12 +32,6 @@ impl State for Failed { } else { debug!("Restart is disabled for process."); } - //tokio::time::delay_for(std::time::Duration::from_secs(2)).await; - // T//ransition::next(self, Installing{ - // download_directory: pod_state.download_directory.clone(), - // parcel_directory: pod_state.parcel_directory.clone(), - // package: pod_state.package.clone() - //}) Transition::Complete(Ok(())) } diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index e7640e0..68e687a 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -148,6 +148,6 @@ impl State for Starting { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:running") + make_status(Phase::Pending, &"Starting") } } diff --git a/src/provider/states/stopped.rs b/src/provider/states/stopped.rs index 0a931c3..f0a9538 100644 --- a/src/provider/states/stopped.rs +++ b/src/provider/states/stopped.rs @@ -29,6 +29,6 @@ impl State for Stopped { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:running") + make_status(Phase::Pending, &"Stopped") } } diff --git a/src/provider/states/stopping.rs b/src/provider/states/stopping.rs index 658d471..75271c2 100644 --- a/src/provider/states/stopping.rs +++ b/src/provider/states/stopping.rs @@ -29,6 +29,6 @@ impl State for Stopping { _pod_state: &mut PodState, _pod: &Pod, ) -> anyhow::Result { - make_status(Phase::Pending, &"status:running") + make_status(Phase::Pending, &"Stopping") } } From cc6781cd6c3d779a154d9c0f9262352b1f8bc68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 12:26:04 +0100 Subject: [PATCH 18/22] Removed unneeded static annotation --- src/provider/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/provider/mod.rs b/src/provider/mod.rs index d7962df..00a5865 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -26,7 +26,7 @@ pub struct StackableProvider { log_directory: PathBuf, } -pub const CRDS: &'static [&'static str] = &["repositories.stable.stackable.de"]; +pub const CRDS: &[&str] = &["repositories.stable.stackable.de"]; mod error; mod repository; From 1f0b7a8477e5581b51373087802ea4f1c8b4daf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 12:41:07 +0100 Subject: [PATCH 19/22] Fixed clippy suggestions. One remaining for dead code, but this is called from a test case. --- src/agentconfig.rs | 61 +++++++++++-------- src/provider/mod.rs | 2 +- src/provider/repository/mod.rs | 4 +- .../{repository.rs => repository_spec.rs} | 0 .../repository/stackablerepository.rs | 8 +-- src/provider/states/creating_config.rs | 16 ++--- src/provider/states/installing.rs | 2 +- src/provider/states/starting.rs | 6 +- 8 files changed, 54 insertions(+), 45 deletions(-) rename src/provider/repository/{repository.rs => repository_spec.rs} (100%) diff --git a/src/agentconfig.rs b/src/agentconfig.rs index 65fcaf9..e455176 100644 --- a/src/agentconfig.rs +++ b/src/agentconfig.rs @@ -183,8 +183,7 @@ scheme of \"productname-productversion\". let default_interface = all_interfaces .iter() - .filter(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty()) - .next(); + .find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty()); match default_interface { Some(interface) => { @@ -221,7 +220,8 @@ impl Configurable for AgentConfig { let final_ip = if let Ok(ip) = AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::SERVER_IP_ADDRESS) { - IpAddr::from_str(&ip).expect(&format!("Couldn't parse {} as a valid ip address!", ip)) + IpAddr::from_str(&ip) + .unwrap_or_else(|_| panic!("Couldn't parse {} as a valid ip address!", ip)) } else { AgentConfig::get_default_ipaddress() .expect("Error getting default ip address, please specify it explicitly!") @@ -232,61 +232,66 @@ impl Configurable for AgentConfig { let final_log_dir = if let Ok(log_dir) = AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::LOG_DIR) { - PathBuf::from_str(&log_dir).expect(&format!( - "Error parsing valid log directory from string: {}", - log_dir - )) + PathBuf::from_str(&log_dir).unwrap_or_else(|_| { + panic!("Error parsing valid log directory from string: {}", log_dir) + }) } else { PathBuf::from_str( AgentConfig::LOG_DIR .default .expect("Invalid default value for log directory option!"), ) - .expect(&format!("Unable to get log directory from options!")) + .unwrap_or_else(|_| panic!("Unable to get log directory from options!".to_string())) }; // Parse config directory let final_config_dir = if let Ok(config_dir) = AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::CONFIG_DIR) { - PathBuf::from_str(&config_dir).expect(&format!( - "Error parsing valid config directory from string: {}", - config_dir - )) + PathBuf::from_str(&config_dir).unwrap_or_else(|_| { + panic!( + "Error parsing valid config directory from string: {}", + config_dir + ) + }) } else { PathBuf::from_str( AgentConfig::CONFIG_DIR .default .expect("Invalid default value for config directory option!"), ) - .expect(&format!("Unable to get config directory from options!")) + .unwrap_or_else(|_| panic!("Unable to get config directory from options!".to_string())) }; // Parse parcel directory let final_parcel_dir = if let Ok(parcel_dir) = AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::PACKAGE_DIR) { - PathBuf::from_str(&parcel_dir).expect(&format!( - "Error parsing valid parcel directory from string: {}", - parcel_dir - )) + PathBuf::from_str(&parcel_dir).unwrap_or_else(|_| { + panic!( + "Error parsing valid parcel directory from string: {}", + parcel_dir + ) + }) } else { PathBuf::from_str( AgentConfig::PACKAGE_DIR .default .expect("Invalid default value for parcel directory option!"), ) - .expect(&format!("Unable to get parcel directory from options!")) + .unwrap_or_else(|_| panic!("Unable to get parcel directory from options!".to_string())) }; // Parse cert file let final_server_cert_file = if let Ok(server_cert_file) = AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::SERVER_CERT_FILE) { - Some(PathBuf::from_str(&server_cert_file).expect(&format!( - "Error parsing valid server cert file directory from string: {}", - server_cert_file - ))) + Some(PathBuf::from_str(&server_cert_file).unwrap_or_else(|_| { + panic!( + "Error parsing valid server cert file directory from string: {}", + server_cert_file + ) + })) } else { None }; @@ -295,10 +300,12 @@ impl Configurable for AgentConfig { let final_server_key_file = if let Ok(server_key_file) = AgentConfig::get_exactly_one_string(&parsed_values, &AgentConfig::SERVER_KEY_FILE) { - Some(PathBuf::from_str(&server_key_file).expect(&format!( - "Error parsing valid server key file directory from string: {}", - server_key_file - ))) + Some(PathBuf::from_str(&server_key_file).unwrap_or_else(|_| { + panic!( + "Error parsing valid server key file directory from string: {}", + server_key_file + ) + })) } else { None }; @@ -306,7 +313,7 @@ impl Configurable for AgentConfig { let mut final_tags: HashMap = HashMap::new(); if let Some(Some(tags)) = parsed_values.get(&AgentConfig::TAG) { for tag in tags { - let split: Vec<&str> = tag.split("=").collect(); + let split: Vec<&str> = tag.split('=').collect(); if split.len() != 2 { // We want to avoid any "unpredictable" behavior like ignoring a malformed // key=value pair with just a log message -> so we panic if this can't be diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 00a5865..cacb5b1 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -107,7 +107,7 @@ impl StackableProvider { let crds: Api = Api::all(self.client.clone()); // Check all CRDS - for crd in CRDS.into_iter() { + for crd in CRDS.iter() { debug!("Checking if CRD [{}] is registered", crd); match crds.get(crd).await { Err(kube::error::Error::Api(ErrorResponse { reason, .. })) diff --git a/src/provider/repository/mod.rs b/src/provider/repository/mod.rs index 9d44c22..5946393 100644 --- a/src/provider/repository/mod.rs +++ b/src/provider/repository/mod.rs @@ -6,11 +6,11 @@ use log::{debug, trace}; use crate::provider::error::StackableError; use crate::provider::repository::package::Package; -use crate::provider::repository::repository::Repository; +use crate::provider::repository::repository_spec::Repository; use crate::provider::repository::stackablerepository::StackableRepoProvider; pub mod package; -pub mod repository; +pub mod repository_spec; pub mod stackablerepository; pub async fn find_repository( diff --git a/src/provider/repository/repository.rs b/src/provider/repository/repository_spec.rs similarity index 100% rename from src/provider/repository/repository.rs rename to src/provider/repository/repository_spec.rs diff --git a/src/provider/repository/stackablerepository.rs b/src/provider/repository/stackablerepository.rs index 5f186b1..95a13d3 100644 --- a/src/provider/repository/stackablerepository.rs +++ b/src/provider/repository/stackablerepository.rs @@ -14,7 +14,7 @@ use url::Url; use crate::provider::error::StackableError; use crate::provider::error::StackableError::PackageNotFound; use crate::provider::repository::package::Package; -use crate::provider::repository::repository::Repository; +use crate::provider::repository::repository_spec::Repository; #[derive(Debug, Clone)] pub struct StackableRepoProvider { @@ -164,7 +164,7 @@ impl StackableRepoProvider { /// /// Public for testing pub fn resolve_url(&self, path: String) -> Result { - if let Result::Ok(_) = Url::parse(&path) { + if Url::parse(&path).is_ok() { // The URL defined for this element is an absolute URL, so we won't // resolve that agains the base url of the repository but simply // return it unchanged @@ -194,7 +194,7 @@ impl TryFrom<&Repository> for StackableRepoProvider { content: None, }); } - return Err(StackableError::RepositoryConversionError); + Err(StackableError::RepositoryConversionError) } } @@ -214,7 +214,7 @@ impl Hash for StackableRepoProvider { #[cfg(test)] mod tests { - use crate::provider::repository::repository::{Repository, RepositorySpec}; + use crate::provider::repository::repository_spec::{Repository, RepositorySpec}; use crate::provider::repository::stackablerepository::StackableRepoProvider; use std::collections::HashMap; use std::convert::TryFrom; diff --git a/src/provider/states/creating_config.rs b/src/provider/states/creating_config.rs index 93d0a0c..b072267 100644 --- a/src/provider/states/creating_config.rs +++ b/src/provider/states/creating_config.rs @@ -81,13 +81,13 @@ impl CreatingConfig { // Public for testing pub fn pathbuf_to_string(target_field: &str, path: PathBuf) -> Result { let path_as_string = path.into_os_string().into_string(); - return match path_as_string { - Ok(valid_string) => return Ok(valid_string), + match path_as_string { + Ok(valid_string) => Ok(valid_string), Err(non_utf8) => Err(DirectoryParseError { target: target_field.to_string(), - original: non_utf8.clone(), + original: non_utf8, }), - }; + } } async fn retrieve_config_maps( @@ -107,7 +107,7 @@ impl CreatingConfig { found_configmaps.insert(String::from(map_name), config_map); } else { warn!("Got config map {} with no name in metadata, this should never have happened!", map); - missing_configmaps.push(String::from(map)); + missing_configmaps.push(map); } } Err(kube::error::Error::Api(ErrorResponse { reason, .. })) @@ -115,7 +115,7 @@ impl CreatingConfig { { // ConfigMap was not created, add it to the list of missing config maps debug!("ConfigMap {} not found", &map); - missing_configmaps.push(String::from(map)); + missing_configmaps.push(map); } Err(e) => { // An error occurred when communicating with the api server @@ -168,7 +168,7 @@ impl CreatingConfig { }); } let map = map.clone(); - let config_map_name = &map.metadata.name.expect("Got object with no name from K8s, even though we checked for this one line ago - something went seriously wrong!").clone(); + let config_map_name = &map.metadata.name.expect("Got object with no name from K8s, even though we checked for this one line ago - something went seriously wrong!"); debug!( "applying configmap {} to directory {:?}", &config_map_name, target_directory @@ -177,7 +177,7 @@ impl CreatingConfig { info!("creating config directory {:?}", target_directory); fs::create_dir_all(&target_directory)?; } - if let Some(data) = map.data.clone() { + if let Some(data) = map.data { debug!("Map contained keys: {:?}", &data.keys()); for key in data.keys() { debug!("found key: {} in configmap {}", key, &config_map_name); diff --git a/src/provider/states/installing.rs b/src/provider/states/installing.rs index ddfc281..0daaa6e 100644 --- a/src/provider/states/installing.rs +++ b/src/provider/states/installing.rs @@ -71,7 +71,7 @@ impl State for Installing { ); } else { info!("Installing package {}", package); - match self.install_package(package.clone()) { + match self.install_package(package) { Ok(()) => Transition::next( self, CreatingConfig { diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 68e687a..095eff7 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -61,11 +61,13 @@ impl State for Starting { "Got environment vars: {:?} service {}", vars, pod_state.service_name ); - vars.into_iter() + vars.iter() .map(|env_var| { ( String::from(&env_var.name), - String::from(&env_var.value.clone().unwrap_or(String::from(""))), + String::from( + &env_var.value.clone().unwrap_or_else(|| String::from("")), + ), ) }) .collect::>() From 6f44e101dc69579409d7065aa225bba20345f873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 15:58:27 +0100 Subject: [PATCH 20/22] Changed a few occurrences of illegal container status reasons, removed some dead code. --- src/provider/states/creating_config.rs | 7 +++---- src/provider/states/installing.rs | 2 +- src/provider/states/running.rs | 10 +--------- src/provider/states/starting.rs | 8 ++++---- src/provider/states/terminated.rs | 3 +-- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/provider/states/creating_config.rs b/src/provider/states/creating_config.rs index b072267..6cc0b25 100644 --- a/src/provider/states/creating_config.rs +++ b/src/provider/states/creating_config.rs @@ -354,7 +354,7 @@ impl State for CreatingConfig { return Transition::next( self, SetupFailed { - message: "Unable to parse directories for command template as UTF8".to_string(), + message: "DirectoryParseError".to_string(), }, ); }; @@ -373,17 +373,16 @@ impl State for CreatingConfig { &template_data, ) { // Creation of config file failed! - let error_message = format!( + error!( "Failed to create config file [{:?}] from config map [{}] due to: {:?}", &joined_target_path.to_str(), volume, e ); - error!("{}", &error_message); return Transition::next( self, SetupFailed { - message: error_message, + message: "FailedToCreateConfigFile".to_string(), }, ); } diff --git a/src/provider/states/installing.rs b/src/provider/states/installing.rs index 0daaa6e..e485306 100644 --- a/src/provider/states/installing.rs +++ b/src/provider/states/installing.rs @@ -81,7 +81,7 @@ impl State for Installing { Err(e) => Transition::next( self, SetupFailed { - message: e.to_string(), + message: "PackageInstallationFailed".to_string(), }, ), } diff --git a/src/provider/states/running.rs b/src/provider/states/running.rs index 31271d9..8ba6b27 100644 --- a/src/provider/states/running.rs +++ b/src/provider/states/running.rs @@ -28,17 +28,9 @@ impl State for Running { ) -> Transition { debug!("waiting"); let mut handle = std::mem::replace(&mut self.process_handle, None).unwrap(); - /*while let Ok(_) = timeout(Duration::from_millis(100), changed.notified()).await { - debug!("drained a waiting notification"); - }*/ - // debug!("done draining"); loop { tokio::select! { - /*_ = changed.notified() => { - debug!("pod changed"); - break; - },*/ _ = tokio::time::delay_for(std::time::Duration::from_secs(1)) => { trace!("Checking if service {} is still running.", &pod_state.service_name); } @@ -53,7 +45,7 @@ impl State for Running { return Transition::next( self, Failed { - message: "Process died unexpectedly!".to_string(), + message: "ProcessDiedUnexpectedly".to_string(), }, ); } diff --git a/src/provider/states/starting.rs b/src/provider/states/starting.rs index 095eff7..7c743a1 100644 --- a/src/provider/states/starting.rs +++ b/src/provider/states/starting.rs @@ -28,7 +28,7 @@ impl State for Starting { return Transition::next( self, SetupFailed { - message: "Unable to parse directories for command template as UTF8".to_string(), + message: "DirectoryParseError".to_string(), }, ); }; @@ -110,7 +110,7 @@ impl State for Starting { return Transition::next( self, Failed { - message: "process failed during startup".to_string(), + message: "ProcessFailedDuringStartup".to_string(), }, ); } @@ -129,7 +129,7 @@ impl State for Starting { return Transition::next( self, Failed { - message: error_message, + message: "ProcessStartFailed".to_string(), }, ); } @@ -140,7 +140,7 @@ impl State for Starting { return Transition::next( self, Failed { - message: "no command object present, failing process".to_string(), + message: "MissingCommandObject".to_string(), }, ); } diff --git a/src/provider/states/terminated.rs b/src/provider/states/terminated.rs index a553757..651ef91 100644 --- a/src/provider/states/terminated.rs +++ b/src/provider/states/terminated.rs @@ -4,8 +4,7 @@ use log::info; use crate::provider::PodState; #[derive(Default, Debug)] -/// The Pod failed to run. -// If we manually implement, we can allow for arguments. +/// The pod object was deleted in Kubernetes pub struct Terminated { pub message: String, } From 5a7245b1fef4ebd8627befd9b9640937baeb3e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 16:00:44 +0100 Subject: [PATCH 21/22] Fixed unused variable --- src/provider/states/installing.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/provider/states/installing.rs b/src/provider/states/installing.rs index e485306..8403f29 100644 --- a/src/provider/states/installing.rs +++ b/src/provider/states/installing.rs @@ -5,7 +5,7 @@ use flate2::read::GzDecoder; use kubelet::pod::Pod; use kubelet::state::prelude::*; use kubelet::state::{State, Transition}; -use log::{debug, info}; +use log::{debug, error, info}; use tar::Archive; use crate::provider::error::StackableError; @@ -61,6 +61,7 @@ impl Installing { impl State for Installing { async fn next(self: Box, _pod_state: &mut PodState, _pod: &Pod) -> Transition { let package = self.package.clone(); + let package_name = &package.get_directory_name(); return if self.package_installed(package.clone()) { info!("Package {} has already been installed", package); return Transition::next( @@ -78,12 +79,18 @@ impl State for Installing { target_directory: None, }, ), - Err(e) => Transition::next( - self, - SetupFailed { - message: "PackageInstallationFailed".to_string(), - }, - ), + Err(e) => { + error!( + "Failed to install package [{}] due to: [{:?}]", + &package_name, e + ); + Transition::next( + self, + SetupFailed { + message: "PackageInstallationFailed".to_string(), + }, + ) + } } }; } From 26b78245b833d8b4c50768af674c04f477c32fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Liebau?= Date: Wed, 2 Dec 2020 16:08:04 +0100 Subject: [PATCH 22/22] Linter warning --- src/provider/states/creating_config.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/provider/states/creating_config.rs b/src/provider/states/creating_config.rs index 6cc0b25..c733edc 100644 --- a/src/provider/states/creating_config.rs +++ b/src/provider/states/creating_config.rs @@ -425,9 +425,11 @@ mod tests { // Test if an undefined variable leads to an error let template_with_undefined_var = "{{var4}}test"; + let result = CreatingConfig::render_config_template(&context, template_with_undefined_var); + match CreatingConfig::render_config_template(&context, template_with_undefined_var) { Ok(_) => assert!(false), - Err(_) => assert!(true), + Err(_) => {} } }