-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
core.rs
193 lines (158 loc) · 6.85 KB
/
core.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
use std::collections::HashMap;
use serde::Deserialize;
use async_trait::async_trait;
use anyhow;
use std::path::PathBuf;
use schemars::JsonSchema;
use crate::modules::aws::config::AwsConfig;
use crate::modules::hashivault::{
self,
config::HashivaultConfig,
kv2::HashiVaultKeyValueV2Input,
kv1::HashiVaultKeyValueV1Input,
};
use crate::modules::bitwarden;
use crate::modules::aws;
use crate::modules::gcloud;
use crate::modules::azure;
use crate::modules::files::FileInput;
use crate::modules::variables::VariableInput;
use crate::modules::sops;
/// Available environments. Keys are environment names.
type NovopsEnvironments = HashMap<String, NovopsEnvironmentInput>;
///
/// Main Novops config file
///
#[derive(Debug, Deserialize, Clone, PartialEq, JsonSchema, Default)]
pub struct NovopsConfigFile {
/// Application name. Informational only.
///
/// If not specified, use current directory name
pub name: Option<String>,
/// Source of truth defining files and variables loaded by Novops
///
/// Environments are named uniquely (such as "dev", "prod"...)
/// to allow for different configs to be loaded in various contexts
pub environments: NovopsEnvironments,
/// Global configurations for Novops and modules
pub config: Option<NovopsConfig>
}
///
/// Global Novops configuration defining behavior for modules
///
#[derive(Debug, Deserialize, Clone, PartialEq, JsonSchema, Default)]
pub struct NovopsConfig {
/// Novops default configurations
pub default: Option<NovopsConfigDefault>,
/// Hashicorp Vault module configs
pub hashivault: Option<HashivaultConfig>,
/// AWS module configs
pub aws: Option<AwsConfig>
}
#[derive(Debug, Deserialize, Clone, PartialEq, JsonSchema, Default)]
pub struct NovopsConfigDefault {
/// Default environment name, selected by default if no user input is provided
pub environment: Option<String>,
}
/// Modules to be loaded for an environment. Each module defines one or more Input
/// which will be resolved into Outputs (files & variables)
#[derive(Debug, Deserialize, Clone, PartialEq, JsonSchema, Default)]
pub struct NovopsEnvironmentInput {
/// Variables resolving to environment variables from provided source
pub variables: Option<Vec<VariableInput>>,
/// Files resolving to concrete files on local filesystem and environment variables pointing to file
pub files: Option<Vec<FileInput>>,
/// Assume an AWS Role from local config.
///
/// Outputs environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_SESSION_TOKEN`
/// with temporary credentials for IAM Role.
pub aws: Option<aws::config::AwsInput>,
/// Reference one or more Hashicorp Vault Secret Engines to generate either files or variables.
pub hashivault: Option<hashivault::config::HashiVaultInput>,
/// Reference SOPS encrypted file(s) as dotenv to load variables
pub sops_dotenv: Option<Vec<sops::SopsDotenvInput>>,
}
/// Context in which an environment is loaded. Passed to Inputs with ResolveTo() to generate related Output
#[derive(Debug, Deserialize, Clone, PartialEq, Default)]
pub struct NovopsContext {
/// environment name
pub env_name: String,
// application name
pub app_name: String,
/// working directory under which files are stored
pub workdir: PathBuf,
/// original config loaded at runtime
pub config_file_data: NovopsConfigFile,
/// path to sourceable environment variable file
pub env_var_filepath: PathBuf,
// enable dry run mode
pub dry_run: bool
}
/// Trait all Input are implement to generate their final Output value
#[async_trait]
pub trait ResolveTo<T> {
async fn resolve(&self, ctx: &NovopsContext) -> Result<T, anyhow::Error>;
}
/// All possible inputs resolving to a string value
#[derive(Debug, Deserialize, Clone, PartialEq, JsonSchema)]
#[serde(untagged)]
pub enum StringResolvableInput {
String(String),
BitwardeItemInput(bitwarden::BitwardenItemInput),
HashiVaultKeyValueV2Input(HashiVaultKeyValueV2Input),
HashiVaultKeyValueV1Input(HashiVaultKeyValueV1Input),
AwsSSMParamStoreInput(aws::ssm::AwsSSMParamStoreInput),
AwsSecretsManagerSecretInput(aws::secretsmanager::AwsSecretsManagerSecretInput),
GCloudSecretManagerSecretInput(gcloud::secretmanager::GCloudSecretManagerSecretInput),
AzureKeyvaultSecretInput(azure::vault::AzureKeyvaultSecretInput),
SopsValueInput(sops::SopsValueInput),
AwsS3ObjectInput(aws::s3::AwsS3ObjectInput)
}
/// String is the most simple Input to resolve: it resolve to itself
#[async_trait]
impl ResolveTo<String> for String {
async fn resolve(&self, _: &NovopsContext) -> Result<String, anyhow::Error> {
return Ok(self.clone());
}
}
#[async_trait]
impl ResolveTo<String> for StringResolvableInput {
async fn resolve(&self, ctx: &NovopsContext) -> Result<String, anyhow::Error> {
return match self {
StringResolvableInput::String(s) => Ok(s.clone()),
StringResolvableInput::BitwardeItemInput(bw) => bw.resolve(ctx).await,
StringResolvableInput::HashiVaultKeyValueV2Input(hv) => hv.resolve(ctx).await,
StringResolvableInput::HashiVaultKeyValueV1Input(hv) => hv.resolve(ctx).await,
StringResolvableInput::AwsSSMParamStoreInput(p) => p.resolve(ctx).await,
StringResolvableInput::AwsSecretsManagerSecretInput(s) => s.resolve(ctx).await,
StringResolvableInput::GCloudSecretManagerSecretInput(s) => s.resolve(ctx).await,
StringResolvableInput::AzureKeyvaultSecretInput(z) => z.resolve(ctx).await,
StringResolvableInput::SopsValueInput(s) => s.resolve(ctx).await,
StringResolvableInput::AwsS3ObjectInput(s) => s.resolve(ctx).await,
}
}
}
/// Any input to be used for file content.
#[derive(Debug, Deserialize, Clone, PartialEq, JsonSchema)]
#[serde(untagged)]
pub enum BytesResolvableInput {
AwsSecretsManagerSecretInput(aws::secretsmanager::AwsSecretsManagerSecretInput),
GCloudSecretManagerSecretInput(gcloud::secretmanager::GCloudSecretManagerSecretInput),
StringResolvableInput(StringResolvableInput),
// skip for schema doc generation as it's useless for human user
// only useful for internal transformation of blobs into strings
#[schemars(skip)]
ByteVec(Vec<u8>)
}
#[async_trait]
impl ResolveTo<Vec<u8>> for BytesResolvableInput {
async fn resolve(&self, ctx: &NovopsContext) -> Result<Vec<u8>, anyhow::Error> {
let result = match self {
BytesResolvableInput::ByteVec(z) => Ok(z.clone()),
BytesResolvableInput::AwsSecretsManagerSecretInput(z) => z.resolve(ctx).await,
BytesResolvableInput::GCloudSecretManagerSecretInput(z) => z.resolve(ctx).await,
BytesResolvableInput::StringResolvableInput(z) => z.resolve(ctx).await.map(|x| x.into_bytes()),
};
return result;
}
}