Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

kubectl-hns: Add "delete" command #311

Merged
merged 2 commits into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/user-guide/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,12 @@ Instead, you must delete its anchor (note that `subns` is a short form of
$ kubectl delete subns child -n parent
```

You can also use the `kubectl-hns` plugin to delete subnamespaces.

```
$ kubectl hns delete child -n parent
```

This _seems_ to imply that if you delete a _parent_ namespace, all its
subnamespace children (and their descendants) will be deleted as well, since all
objects in a namespace (such as anchors) are deleted along with the namespace.
Expand Down
11 changes: 11 additions & 0 deletions docs/user-guide/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,11 @@ kubectl delete subns service-3 -n team-a
Note that `subns` is a short form for `subnamespaceanchor` or
`subnamespaceanchor.hnc.x-k8s.io`.

You can also use the `kubectl-hns` plugin to delete subnamespaces.

```bash
kubectl hns delete service-3 -n team-a
```

Now try to delete `service-1` in the same way, but you'll see it doesn't work:

Expand Down Expand Up @@ -675,6 +680,12 @@ team-a
[s] indicates subnamespaces
```

You can also do the above steps in one command run with the `kubectl-hns` plugin:

```bash
kubectl hns delete service-1 -n team-a --allowCascadingDeletion
```

There's an important difference between subnamespaces and regular child
namespace, also known as a full namespace. A subnamespace is created by HNC due
to an anchor being created in the parent; when that anchor is deleted, the
Expand Down
63 changes: 63 additions & 0 deletions internal/kubectl/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*

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.
*/

package kubectl

import (
"fmt"
"os"

"github.com/spf13/cobra"
)

var deleteCmd = &cobra.Command{
Use: "delete -n PARENT CHILD",
Short: "Deletes a subnamespace under the given parent.",
Example: `# Delete the 'foo' anchor in the parent 'bar' namespace
kubectl hns delete foo -n bar

# Allow 'foo' to be cascading deleted and delete it
kubectl hns delete foo -n bar --allowCascadingDeletion`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
nnm := args[0]

parent, _ := cmd.Flags().GetString("namespace")
if parent == "" {
fmt.Println("Error: parent must be set via --namespace or -n")
os.Exit(1)
}

allowCD, _ := cmd.Flags().GetBool("allowCascadingDeletion")
if allowCD {
hc := client.getHierarchy(nnm)
if hc.Spec.AllowCascadingDeletion {
fmt.Printf("Cascading deletion for '%s' is already set to 'true'; unchanged\n", nnm)
} else {
fmt.Printf("Allowing cascading deletion on '%s'\n", nnm)
hc.Spec.AllowCascadingDeletion = true
client.updateHierarchy(hc, fmt.Sprintf("update the hierarchical configuration of %s", nnm))
}
}

client.deleteAnchor(parent, nnm)
},
}

func newDeleteCmd() *cobra.Command {
deleteCmd.Flags().StringP("namespace", "n", "", "The parent namespace for the new subnamespace")
deleteCmd.Flags().BoolP("allowCascadingDeletion", "a", false, "Allows cascading deletion of its subnamespaces.")
return deleteCmd
}
14 changes: 14 additions & 0 deletions internal/kubectl/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Client interface {
getHierarchy(nnm string) *api.HierarchyConfiguration
updateHierarchy(hier *api.HierarchyConfiguration, reason string)
createAnchor(nnm string, hnnm string)
deleteAnchor(nnm string, hnnm string)
getAnchorStatus(nnm string) anchorStatus
getHNCConfig() *api.HNCConfiguration
updateHNCConfig(*api.HNCConfiguration)
Expand Down Expand Up @@ -102,6 +103,7 @@ func init() {
rootCmd.AddCommand(newDescribeCmd())
rootCmd.AddCommand(newTreeCmd())
rootCmd.AddCommand(newCreateCmd())
rootCmd.AddCommand(newDeleteCmd())
rootCmd.AddCommand(newConfigCmd())
rootCmd.AddCommand(newVersionCmd())
rootCmd.AddCommand(newHrqCmd())
Expand Down Expand Up @@ -186,6 +188,18 @@ func (cl *realClient) createAnchor(nnm string, hnnm string) {
fmt.Printf("Successfully created %q subnamespace anchor in %q namespace\n", hnnm, nnm)
}

func (cl *realClient) deleteAnchor(nnm string, hnnm string) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

err := hncClient.Delete().Resource(api.Anchors).Namespace(nnm).Name(hnnm).Do(ctx).Error()
if err != nil {
fmt.Printf("\nCould not delete subnamespace anchor.\nReason: %s\n", err)
os.Exit(1)
}
fmt.Printf("Successfully deleted %q subnamespace anchor in %q namespace\n", hnnm, nnm)
}

func (cl *realClient) getHNCConfig() *api.HNCConfiguration {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
Expand Down
21 changes: 21 additions & 0 deletions test/e2e/quickstart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ spec:
// show how to delete a subns correctly
MustNotRun("kubectl delete ns", nsService3)
MustRun("kubectl delete subns", nsService3, "-n", nsTeamA)

// show how to delete a subns correctly with the kubectl-hns plugin
CreateSubnamespace(nsService4, nsTeamA)
MustNotRun("kubectl hns delete", nsService4)
MustRun("kubectl hns delete", nsService4, "-n", nsTeamA)

// This should not run because service-1 contains its own subnamespace that would be deleted with it,
MustNotRun("kubectl delete subns", nsService1, "-n", nsTeamA)

Expand All @@ -263,6 +269,21 @@ spec:
"└── [s] " + nsService2
RunShouldContain(expected, defTimeout, "kubectl hns tree", nsTeamA)

// cascading deletion with the kubectl-hns plugin
CreateSubnamespace(nsService1, nsTeamA)
CreateSubnamespace(nsService4, nsService1)
expected = "" +
nsService1 + "\n" +
"└── [s] " + nsService4
RunShouldContain(expected, defTimeout, "kubectl hns tree", nsService1, "-n", nsTeamA)
MustNotRun("kubectl hns delete", nsService1, "-n", nsTeamA)
MustRun("kubectl hns delete", nsService1, "-n", nsTeamA, "--allowCascadingDeletion")
expected = "" +
nsTeamA + "\n" +
"└── [s] " + nsService2
// cascading deletion of its subnamespaces takes time
RunShouldContain(expected, cleanupTimeout, "kubectl hns tree", nsTeamA)

// Show the difference of a subns and regular child ns
CreateSubnamespace(nsService4, nsTeamA)
expected = "" +
Expand Down