mz_service/
secrets.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::path::PathBuf;
11use std::sync::Arc;
12
13use clap::ValueEnum;
14use mz_aws_secrets_controller::AwsSecretsClient;
15use mz_orchestrator_kubernetes::secrets::KubernetesSecretsReader;
16use mz_orchestrator_process::secrets::ProcessSecretsReader;
17use mz_secrets::SecretsReader;
18
19#[derive(clap::Parser, Clone, Debug)]
20pub struct SecretsReaderCliArgs {
21    /// The secrets reader implementation to use.
22    #[structopt(long, value_enum, env = "SECRETS_READER")]
23    pub secrets_reader: SecretsControllerKind,
24    /// When using the process secrets reader, the directory on the filesystem
25    /// where secrets are stored.
26    #[structopt(
27        long,
28        required_if_eq("secrets_reader", "local-file"),
29        env = "SECRETS_READER_LOCAL_FILE_DIR"
30    )]
31    pub secrets_reader_local_file_dir: Option<PathBuf>,
32    /// When using the Kubernetes secrets reader, the Kubernetes context to
33    /// load.
34    #[structopt(
35        long,
36        required_if_eq("secrets_reader", "kubernetes"),
37        env = "SECRETS_READER_KUBERNETES_CONTEXT"
38    )]
39    pub secrets_reader_kubernetes_context: Option<String>,
40    /// When using the AWS secrets reader, we need both of the following.
41    #[structopt(
42        long,
43        required_if_eq("secrets_reader", "aws-secrets-manager"),
44        env = "SECRETS_READER_AWS_PREFIX"
45    )]
46    pub secrets_reader_aws_prefix: Option<String>,
47    /// When using the Kubernetes secrets reader, the prefix to use for secret
48    /// names.
49    #[structopt(long, env = "SECRETS_READER_NAME_PREFIX")]
50    pub secrets_reader_name_prefix: Option<String>,
51}
52
53#[derive(ValueEnum, Debug, Clone, Copy)]
54pub enum SecretsControllerKind {
55    LocalFile,
56    Kubernetes,
57    AwsSecretsManager,
58}
59
60impl SecretsReaderCliArgs {
61    /// Loads the secrets reader specified by the command-line arguments.
62    pub async fn load(self) -> Result<Arc<dyn SecretsReader>, anyhow::Error> {
63        match self.secrets_reader {
64            SecretsControllerKind::LocalFile => {
65                let dir = self.secrets_reader_local_file_dir.expect("clap enforced");
66                Ok(Arc::new(ProcessSecretsReader::new(dir)))
67            }
68            SecretsControllerKind::Kubernetes => {
69                let context = self
70                    .secrets_reader_kubernetes_context
71                    .expect("clap enforced");
72                Ok(Arc::new(
73                    KubernetesSecretsReader::new(context, self.secrets_reader_name_prefix).await?,
74                ))
75            }
76            SecretsControllerKind::AwsSecretsManager => {
77                let prefix = self.secrets_reader_aws_prefix.expect("clap enforced");
78                Ok(Arc::new(AwsSecretsClient::new(&prefix).await))
79            }
80        }
81    }
82
83    /// Turn this struct back into arguments. Useful for passing through to other services.
84    ///
85    /// Expects the correct arguments to be filled in, based on the `clap` requirements.
86    pub fn to_flags(&self) -> Vec<String> {
87        match self.secrets_reader {
88            SecretsControllerKind::LocalFile => {
89                vec![
90                    "--secrets-reader=local-file".to_string(),
91                    format!(
92                        "--secrets-reader-local-file-dir={}",
93                        self.secrets_reader_local_file_dir
94                            .as_ref()
95                            .expect("initialized correctly")
96                            .display()
97                    ),
98                ]
99            }
100            SecretsControllerKind::Kubernetes => {
101                vec![
102                    "--secrets-reader=kubernetes".to_string(),
103                    format!(
104                        "--secrets-reader-kubernetes-context={}",
105                        self.secrets_reader_kubernetes_context
106                            .as_ref()
107                            .expect("initialized correctly")
108                    ),
109                ]
110            }
111            SecretsControllerKind::AwsSecretsManager => {
112                vec![
113                    "--secrets-reader=aws-secrets-manager".to_string(),
114                    format!(
115                        "--secrets-reader-aws-prefix={}",
116                        self.secrets_reader_aws_prefix
117                            .as_ref()
118                            .expect("initialized correctly")
119                    ),
120                ]
121            }
122        }
123    }
124}