Conversation
|
|
||
| [NOTE] | ||
| ==== | ||
| The uninstall command deletes the namespace the demo was installed in. Therefore it is not possible to uninstall demos in the `default` namespace. |
There was a problem hiding this comment.
I feel like this should be opt-in behaviour. Or rather, if we created the namespace, it gets deleted.
There could be existing namespaces that shouldn't be deleted.
There was a problem hiding this comment.
One such example is if we have the monitoring stack installed, and want to add and remove demos.
There was a problem hiding this comment.
According to #187 (comment) "Update after a discussion on 2025-11-26:" it was decided the first version provides no option to be less destructive. Not sure which meeting that was or if it makes sense to change the requirements now.
There was a problem hiding this comment.
There were also problems with deleting everything without deleting the namespace, see stackabletech/hdfs-operator#761 (comment). So maybe for the first version / MVP it is fine for now like this? But feel free to disagree
There was a problem hiding this comment.
I left an idea below in how we could do it: #429 (comment)
But of course, if this implements what we agreed on, that's fine.
It does feel a little dangerous though.
There was a problem hiding this comment.
Yes, I agree it feels dangerous, tried to avoid it at first but ran into the problems above, which unfortunately won't be solved by your idea I think. The manifests we would delete with that approach were also deleted with the help of the labels, so would be the same situation, unless I misunderstood something 😞
Another thing might be adding a confirmation dialogue like the one I added to the installation for the namespace. That it asks if the user really wants to uninstall since that would delete the namespace, and cancel the deletion otherwise.
There was a problem hiding this comment.
0f3cf7f (this PR) Maybe this addresses your concern a bit, let me know what you think :)
|
|
||
| [NOTE] | ||
| ==== | ||
| The uninstall command deletes the namespace the stack was installed in. Therefore it is not possible to uninstall stacks in the `default` namespace. |
There was a problem hiding this comment.
Same as my comment about demos.
NickLarsenNZ
left a comment
There was a problem hiding this comment.
Left a couple of comments about namespace deletion.
| // Delete demo namespace | ||
| client | ||
| .delete_object( | ||
| &uninstall_parameters.demo_namespace, | ||
| &ApiResource::from_gvk(&GroupVersionKind { | ||
| group: "".to_owned(), | ||
| version: "v1".to_owned(), | ||
| kind: "Namespace".to_owned(), | ||
| }), | ||
| None, | ||
| ) | ||
| .await | ||
| .context(DeleteObjectSnafu)?; |
There was a problem hiding this comment.
Here's where I think we should have a check to see if the namespace was "owned" by the demo (via labels/annotations).
There was a problem hiding this comment.
One thing we could consider is reading in the demo, get the GVKs and Helm charts, and uninstall in reverse order before uninstalling the namespace (if it was created by the demo).
Same would apply for stacks.
That could be a follow-up PR, but I think at least we should be careful with namespace deletion.
| // Delete stack namespace | ||
| client | ||
| .delete_object( | ||
| &uninstall_parameters.stack_namespace, | ||
| &ApiResource::from_gvk(&GroupVersionKind { | ||
| group: "".to_owned(), | ||
| version: "v1".to_owned(), | ||
| kind: "Namespace".to_owned(), | ||
| }), | ||
| None, | ||
| ) | ||
| .await | ||
| .context(DeleteObjectSnafu)?; |
Techassi
left a comment
There was a problem hiding this comment.
Left a few comments and questions.
| // Delete demo namespace | ||
| client | ||
| .delete_object( | ||
| &uninstall_parameters.demo_namespace, | ||
| &ApiResource::from_gvk(&GroupVersionKind { | ||
| group: "".to_owned(), | ||
| version: "v1".to_owned(), | ||
| kind: "Namespace".to_owned(), | ||
| }), | ||
| None, | ||
| ) | ||
| .await | ||
| .context(DeleteObjectSnafu)?; |
There was a problem hiding this comment.
note: I think we should abstract that away in an associated function called delete_namespace similar to what we have to create a namespace. See:
stackable-cockpit/rust/stackable-cockpit/src/utils/k8s/client.rs
Lines 443 to 460 in 32cf847
There was a problem hiding this comment.
Any particular reason why we are not using Api<Namespace> instead? I guess one upside to to doing it like currently implemented is that we don't need to clone the client for a single function call.
There was a problem hiding this comment.
The client is cloned anyway in the delete_object function called by delete_namespace. I think I didn't use Api<Namespace> was because I wanted to reuse the delete_object function (before everything was moved into delete_namespace). Could possibly also rewrite that one to use Api<DynamicObject> instead of ApiResource. I don't remember exactly anymore, but I think the resulting code just looked better using ApiResource and that's what the discovery already provided. But no strong opinions here.
While writing, tried implementing it since then the Api creation could be moved from delete_object to delete_all_object_with_label, so one client clone per object list, instead of object function call. This doc for DynamicObject:
/// A dynamic representation of a kubernetes object
///
/// This will work with any non-list type object.
made me think, that if a function accepts Api<DynamicObject>, I could pass it a Api<Namespace> (or any other Object type). But seems to be not compatible. So I think I'm abandoning that implementation again.
Could also not use delete_object instead, but then I would just re-implement what's in that function anyway.
Just noting down thoughts here while trying it out, not trying to argue against it. Here the non-working diff because of Api<DynamicObject> <-> Api<Namespace> incompatibility.
diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs
index 79f5338..779ec7f 100644
--- a/rust/stackable-cockpit/src/utils/k8s/client.rs
+++ b/rust/stackable-cockpit/src/utils/k8s/client.rs
@@ -358,13 +358,20 @@ impl Client {
continue;
};
+ let object_api = match namespace {
+ Some(namespace) => {
+ Api::<DynamicObject>::namespaced_with(self.client.clone(), namespace, &api_resource)
+ }
+ None => Api::<DynamicObject>::all_with(self.client.clone(), &api_resource),
+ };
+
for object in object_list {
if let Some(value) = object.labels().get(&label.key().to_string()) {
if value.eq(&label.value().to_string()) {
self.delete_object(
&object.metadata.name.unwrap(),
- &api_resource,
- object.metadata.namespace.as_deref(),
+ &api_resource.kind,
+ object_api.clone(),
)
.await?;
}
@@ -379,17 +386,10 @@ impl Client {
pub async fn delete_object(
&self,
object_name: &str,
- api_resource: &ApiResource,
- namespace: Option<&str>,
+ object_kind: &str,
+ object_api: Api<DynamicObject>,
) -> Result<(), Error> {
- Span::current().pb_set_message(&format!("Deleting {} {}", api_resource.kind, object_name));
-
- let object_api = match namespace {
- Some(namespace) => {
- Api::<DynamicObject>::namespaced_with(self.client.clone(), namespace, api_resource)
- }
- None => Api::<DynamicObject>::all_with(self.client.clone(), api_resource),
- };
+ Span::current().pb_set_message(&format!("Deleting {} {}", object_kind, object_name));
kube::runtime::wait::delete::delete_and_finalize(
object_api,
@@ -574,14 +574,11 @@ impl Client {
/// Deletes a [`Namespace`] with `name` in the cluster.
pub async fn delete_namespace(&self, name: String) -> Result<()> {
+ let namespace_api: Api<Namespace> = Api::all(self.client.clone());
self.delete_object(
&name,
- &ApiResource::from_gvk(&GroupVersionKind {
- group: "".to_owned(),
- version: "v1".to_owned(),
- kind: "Namespace".to_owned(),
- }),
- None,
+ "Namespace",
+ namespace_api,
)
.await
}
There was a problem hiding this comment.
The client is cloned anyway in the
delete_objectfunction called bydelete_namespace.
Ah I see. Then I think we should use the same style as in create_namespace via Api.
rust/stackablectl/src/cmds/demo.rs
Outdated
| let demo_namespace = tracing_indicatif::suspend_tracing_indicatif( | ||
| || -> Result<String, CmdError> { | ||
| if args.namespaces.namespace == DEFAULT_NAMESPACE { | ||
| if Confirm::new() | ||
| .with_prompt( | ||
| format!( | ||
| "Demos installed in the '{DEFAULT_NAMESPACE}' namespace cannot be uninstalled with stackablectl. Install the demo in the '{demo_namespace}' namespace instead?", | ||
| demo_namespace = args.demo_name.clone()) | ||
| ) | ||
| .default(true) | ||
| .interact() | ||
| .context(ConfirmDialogSnafu)? { | ||
| // User selected to install in suggested namespace | ||
| Ok(args.demo_name.clone()) | ||
| } else { | ||
| // User selected to install in default namespace | ||
| Ok(args.namespaces.namespace.clone()) | ||
| } | ||
| } else { | ||
| Ok(args.namespaces.namespace.clone()) | ||
| } | ||
| }, | ||
| )?; |
There was a problem hiding this comment.
note: Well that's a mouthful to read. Can we somehow make this easier to grasp?
There was a problem hiding this comment.
Maybe I looked at the code too long at this point and unable to see it, but could you give me more information on what is making it hard to grasp. At least I currently don't know how to make it shorter. Or are there explanations missing somewhere? Or just the whole "formatting" of it?
There was a problem hiding this comment.
0f3cf7f (this PR) Tried rearranging it a bit, let me know if know if that makes it any better
There was a problem hiding this comment.
I don't feel like it does make it better, sorry 🙈
What might help on the other hand is to define the closure outside of the tracing_indicatif::suspend_tracing_indicatif call.
If that also doesn't help, we can leave it as is.
There was a problem hiding this comment.
I already like this way better. I think we can now also switch to the previous style of assigning demo_namespace which should make it even cleaner.
This reverts commit 7dfe50e.
Co-authored-by: Techassi <git@techassi.dev>
Techassi
left a comment
There was a problem hiding this comment.
Only a few small comments left, mostly looks nice!
| }; | ||
|
|
||
| for object in object_list { | ||
| if let Some(value) = object.labels().get(&label.key().to_string()) { |
There was a problem hiding this comment.
note: This is kinda cumbersome to use, but could be improved by stackabletech/operator-rs#1182 (if we decide to move forward with it).
There was a problem hiding this comment.
The linked PR was merged btw, but not yet released.
Co-authored-by: Techassi <git@techassi.dev>
Description
Part of #187
This PR adds the
uninstallsubcommands todemoandstackcommands. It implements the MVP features mentioned here #187 (comment)The flag of skipping operators and CRD deletion was optional and I wasn't sure about the naming or if needs to be one command or two. Can also be just removed, added it because it was not much additional work.
Tested with all current demos/stacks. Some demos have edge cases in the deletion, will document that in the parent issue.
This PR also contains thestripped out into #432DEMO/STACKparameter addition for stackabletech/demos#374 (comment)Definition of Done Checklist
Author
Reviewer
Acceptance