-
Notifications
You must be signed in to change notification settings - Fork 590
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(secret): alter secret in catalog #19495
Changes from all commits
ea6821d
3cef5fb
b784e27
fd51880
61b57f7
dc6d3d8
90377f1
c7b2c11
b7624e7
bde3335
8b12e4a
a8e2a5a
d28fc1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,7 +74,23 @@ impl LocalSecretManager { | |
|
||
pub fn add_secret(&self, secret_id: SecretId, secret: Vec<u8>) { | ||
let mut secret_guard = self.secrets.write(); | ||
secret_guard.insert(secret_id, secret); | ||
if secret_guard.insert(secret_id, secret).is_some() { | ||
tracing::error!( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I tend to make it a real error i.e. The status quo is somehow weird to me - an error log is printed, but the action actually succeeded. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually intentional or something we have to do.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is still a little weird to me. I understand the |
||
secret_id = secret_id, | ||
"adding a secret but it already exists, overwriting it" | ||
); | ||
}; | ||
} | ||
|
||
pub fn update_secret(&self, secret_id: SecretId, secret: Vec<u8>) { | ||
let mut secret_guard = self.secrets.write(); | ||
if secret_guard.insert(secret_id, secret).is_none() { | ||
tracing::error!( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it trigger the existing actors to upgrade to the new secret? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No. Already doced this limitation in PR. |
||
secret_id = secret_id, | ||
"updating a secret but it does not exist, adding it" | ||
); | ||
} | ||
self.remove_secret_file_if_exist(&secret_id); | ||
} | ||
|
||
pub fn init_secrets(&self, secrets: Vec<PbSecret>) { | ||
|
@@ -174,14 +190,21 @@ impl LocalSecretManager { | |
} | ||
|
||
fn get_secret_value(pb_secret_bytes: &[u8]) -> SecretResult<Vec<u8>> { | ||
let pb_secret = risingwave_pb::secret::Secret::decode(pb_secret_bytes) | ||
.context("failed to decode secret")?; | ||
let secret_value = match pb_secret.get_secret_backend().unwrap() { | ||
let secret_value = match Self::get_pb_secret_backend(pb_secret_bytes)? { | ||
risingwave_pb::secret::secret::SecretBackend::Meta(backend) => backend.value.clone(), | ||
risingwave_pb::secret::secret::SecretBackend::HashicorpVault(_) => { | ||
return Err(anyhow!("hashicorp_vault backend is not implemented yet").into()) | ||
} | ||
}; | ||
Ok(secret_value) | ||
} | ||
|
||
/// Get the secret backend from the given decrypted secret bytes. | ||
pub fn get_pb_secret_backend( | ||
pb_secret_bytes: &[u8], | ||
) -> SecretResult<risingwave_pb::secret::secret::SecretBackend> { | ||
let pb_secret = risingwave_pb::secret::Secret::decode(pb_secret_bytes) | ||
.context("failed to decode secret")?; | ||
Ok(pb_secret.get_secret_backend().unwrap().clone()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright 2024 RisingWave Labs | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use anyhow::anyhow; | ||
use pgwire::pg_response::StatementType; | ||
use prost::Message; | ||
use risingwave_common::bail_not_implemented; | ||
use risingwave_common::license::Feature; | ||
use risingwave_common::secret::LocalSecretManager; | ||
use risingwave_pb::secret::secret; | ||
use risingwave_sqlparser::ast::{AlterSecretOperation, ObjectName, SqlOption}; | ||
|
||
use super::create_secret::{get_secret_payload, secret_to_str}; | ||
use super::drop_secret::fetch_secret_catalog_with_db_schema_id; | ||
use crate::error::Result; | ||
use crate::handler::{HandlerArgs, RwPgResponse}; | ||
use crate::WithOptions; | ||
|
||
pub async fn handle_alter_secret( | ||
handler_args: HandlerArgs, | ||
secret_name: ObjectName, | ||
sql_options: Vec<SqlOption>, | ||
operation: AlterSecretOperation, | ||
) -> Result<RwPgResponse> { | ||
Feature::SecretManagement | ||
.check_available() | ||
.map_err(|e| anyhow::anyhow!(e))?; | ||
|
||
let session = handler_args.session; | ||
|
||
if let Some((secret_catalog, _, _)) = | ||
fetch_secret_catalog_with_db_schema_id(&session, &secret_name, false)? | ||
{ | ||
let AlterSecretOperation::ChangeCredential { new_credential } = operation; | ||
|
||
let secret_id = secret_catalog.id.secret_id(); | ||
let secret_payload = if sql_options.is_empty() { | ||
let original_pb_secret_bytes = LocalSecretManager::global() | ||
.get_secret(secret_id) | ||
.ok_or(anyhow!( | ||
"Failed to get secret in secret manager, secret_id: {}", | ||
secret_id | ||
))?; | ||
let original_secret_backend = | ||
LocalSecretManager::get_pb_secret_backend(&original_pb_secret_bytes)?; | ||
match original_secret_backend { | ||
secret::SecretBackend::Meta(_) => { | ||
let new_secret_value_bytes = | ||
secret_to_str(&new_credential)?.as_bytes().to_vec(); | ||
let secret_payload = risingwave_pb::secret::Secret { | ||
secret_backend: Some(risingwave_pb::secret::secret::SecretBackend::Meta( | ||
risingwave_pb::secret::SecretMetaBackend { | ||
value: new_secret_value_bytes, | ||
}, | ||
)), | ||
}; | ||
secret_payload.encode_to_vec() | ||
} | ||
secret::SecretBackend::HashicorpVault(_) => { | ||
bail_not_implemented!("hashicorp_vault backend is not implemented yet") | ||
} | ||
} | ||
} else { | ||
let with_options = WithOptions::try_from(sql_options.as_ref() as &[SqlOption])?; | ||
get_secret_payload(new_credential, with_options)? | ||
}; | ||
|
||
let catalog_writer = session.catalog_writer()?; | ||
|
||
catalog_writer | ||
.alter_secret( | ||
secret_id, | ||
secret_catalog.name.clone(), | ||
secret_catalog.database_id, | ||
secret_catalog.schema_id, | ||
secret_catalog.owner, | ||
secret_payload, | ||
) | ||
.await?; | ||
|
||
Ok(RwPgResponse::empty_result(StatementType::ALTER_SECRET)) | ||
} else { | ||
Ok(RwPgResponse::builder(StatementType::ALTER_SECRET) | ||
.notice(format!( | ||
"secret \"{}\" does not exist, skipping", | ||
secret_name | ||
)) | ||
.into()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
backend
option should not appear in thealter secret
statement. To follow the convention of otheralter
commands, only the changed part should be provided. Here thebackend
option is obviously untouched, so users should not write it here.But, furthermore, I think
backend
shouldn't be a per-secret option. In my mind it should be a global-wise config and can't be changed after cluster initialized.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit off-topic. We can create a new issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the concern here. I was thinking the same. But there are 2 problems
So anyway, I think get rid of the WITH here is also a good choice. Will change this.