diff --git a/internal/kubectl/create.go b/internal/kubectl/create.go index 314f2dd8b..3b8c48225 100644 --- a/internal/kubectl/create.go +++ b/internal/kubectl/create.go @@ -23,7 +23,7 @@ import ( ) var createCmd = &cobra.Command{ - Use: "create -n PARENT CHILD", + Use: "create -n PARENT CHILD [-a] key1=value1 [-l] key1=value1,key2=value2", Short: "Creates a subnamespace under the given parent.", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { @@ -32,12 +32,17 @@ var createCmd = &cobra.Command{ fmt.Println("Error: parent must be set via --namespace or -n") os.Exit(1) } + annotations, _ := cmd.Flags().GetStringToString("annotations") + labels, _ := cmd.Flags().GetStringToString("labels") + // Create the anchor, the custom resource representing the subnamespace. - client.createAnchor(parent, args[0]) + client.createAnchor(parent, args[0], annotations, labels) }, } func newCreateCmd() *cobra.Command { createCmd.Flags().StringP("namespace", "n", "", "The parent namespace for the new subnamespace") + createCmd.Flags().StringToStringP("annotations", "a", make(map[string]string, 0), "The annotations to add on subnamespace") + createCmd.Flags().StringToStringP("labels", "l", make(map[string]string, 0), "The labels to add on subnamespace") return createCmd } diff --git a/internal/kubectl/root.go b/internal/kubectl/root.go index 8e35e1ece..790ef60b5 100644 --- a/internal/kubectl/root.go +++ b/internal/kubectl/root.go @@ -48,7 +48,7 @@ type anchorStatus map[string]string type Client interface { getHierarchy(nnm string) *api.HierarchyConfiguration updateHierarchy(hier *api.HierarchyConfiguration, reason string) - createAnchor(nnm string, hnnm string) + createAnchor(nnm string, hnnm string, annotations, labels map[string]string) deleteAnchor(nnm string, hnnm string) getAnchorStatus(nnm string) anchorStatus getHNCConfig() *api.HNCConfiguration @@ -173,13 +173,19 @@ func (cl *realClient) updateHierarchy(hier *api.HierarchyConfiguration, reason s } } -func (cl *realClient) createAnchor(nnm string, hnnm string) { +func (cl *realClient) createAnchor(nnm string, hnnm string, annotations, labels map[string]string) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() anchor := &api.SubnamespaceAnchor{} anchor.Name = hnnm anchor.Namespace = nnm + if len(annotations) > 0 { + anchor.Spec.Annotations = mapToMetaKVP(annotations) + } + if len(labels) > 0 { + anchor.Spec.Labels = mapToMetaKVP(labels) + } err := hncClient.Post().Resource(api.Anchors).Namespace(nnm).Name(hnnm).Body(anchor).Do(ctx).Error() if err != nil { fmt.Printf("\nCould not create subnamespace anchor.\nReason: %s\n", err) @@ -253,3 +259,11 @@ func (cl *realClient) getHRQ(names []string, nnm string) *api.HierarchicalResour } return hrqList } + +func mapToMetaKVP(m map[string]string) []api.MetaKVP { + var kvps []api.MetaKVP + for k, v := range m { + kvps = append(kvps, api.MetaKVP{Key: k, Value: v}) + } + return kvps +} diff --git a/test/e2e/issues_test.go b/test/e2e/issues_test.go index 7280f7015..9b791f0cc 100644 --- a/test/e2e/issues_test.go +++ b/test/e2e/issues_test.go @@ -1,6 +1,7 @@ package e2e import ( + "fmt" "time" . "github.com/onsi/ginkgo/v2" @@ -12,6 +13,7 @@ var _ = Describe("Issues", func() { const ( nsParent = "parent" nsChild = "child" + nsChild2 = "child2" nsSub1 = "sub1" nsSub2 = "sub2" nsSub1Sub1 = "sub1-sub1" @@ -386,6 +388,21 @@ spec: MustRun("kubectl annotate secret my-creds -n", nsParent, "propagate.hnc.x-k8s.io/all-") RunShouldNotContain("my-creds", defTimeout, "kubectl -n", nsChild, "get secrets") }) + + // NB: this test requires managed labels and annotations to be set in the test environment - hnstest/.* + It("Should create namespace with annotations and labels while using kubetl hns - issue #305", func() { + CreateNamespace(nsParent) + // should let it run without the annotations and label flags + MustRun(fmt.Sprintf("kubectl hns create -n %s", nsParent), nsChild) + // should let it run with the annotations and label flags and create ns with provided annotations and labels + MustRun(fmt.Sprintf("kubectl hns create -n %s --annotations hnstest/foo=bar --labels hnstest/foo=bar", nsParent), nsChild2) + RunShouldContain("bar", defTimeout, "kubectl get ns", nsChild2, "-o=jsonpath='{.metadata.annotations.hnstest/foo}'") + RunShouldContain("bar", defTimeout, "kubectl get ns", nsChild2, "-o=jsonpath='{.metadata.labels.hnstest/foo}'") + // cleanup - set for deletion + MustRun("kubectl label --overwrite ns", nsChild, "hnc.x-k8s.io/testNamespace=true") + MustRun("kubectl label --overwrite ns", nsChild2, "hnc.x-k8s.io/testNamespace=true") + + }) }) var _ = Describe("Issues with bad anchors", func() {