diff --git a/cmd/headscale/cli/namespaces.go b/cmd/headscale/cli/namespaces.go index c35c02cc..42870370 100644 --- a/cmd/headscale/cli/namespaces.go +++ b/cmd/headscale/cli/namespaces.go @@ -15,6 +15,7 @@ func init() { namespaceCmd.AddCommand(createNamespaceCmd) namespaceCmd.AddCommand(listNamespacesCmd) namespaceCmd.AddCommand(destroyNamespaceCmd) + namespaceCmd.AddCommand(renameNamespaceCmd) } var namespaceCmd = &cobra.Command{ @@ -107,3 +108,31 @@ var listNamespacesCmd = &cobra.Command{ } }, } + +var renameNamespaceCmd = &cobra.Command{ + Use: "rename OLD_NAME NEW_NAME", + Short: "Renames a namespace", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return fmt.Errorf("Missing parameters") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + o, _ := cmd.Flags().GetString("output") + h, err := getHeadscaleApp() + if err != nil { + log.Fatalf("Error initializing: %s", err) + } + err = h.RenameNamespace(args[0], args[1]) + if strings.HasPrefix(o, "json") { + JsonOutput(map[string]string{"Result": "Namespace renamed"}, err, o) + return + } + if err != nil { + fmt.Printf("Error renaming namespace: %s\n", err) + return + } + fmt.Printf("Namespace renamed\n") + }, +} diff --git a/namespaces.go b/namespaces.go index dfe42976..c350e8c8 100644 --- a/namespaces.go +++ b/namespaces.go @@ -59,6 +59,35 @@ func (h *Headscale) DestroyNamespace(name string) error { } if result := h.db.Unscoped().Delete(&n); result.Error != nil { + return result.Error + } + + return nil +} + +// RenameNamespace renames a Namespace. Returns error if the Namespace does +// not exist or if another Namespace exists with the new name. +func (h *Headscale) RenameNamespace(oldName, newName string) error { + n, err := h.GetNamespace(oldName) + if err != nil { + return err + } + _, err = h.GetNamespace(newName) + if err == nil { + return errorNamespaceExists + } + if !errors.Is(err, errorNamespaceNotFound) { + return err + } + + n.Name = newName + + if result := h.db.Save(&n); result.Error != nil { + return result.Error + } + + err = h.RequestMapUpdates(n.ID) + if err != nil { return err } diff --git a/namespaces_test.go b/namespaces_test.go index 5350576c..2a211da9 100644 --- a/namespaces_test.go +++ b/namespaces_test.go @@ -48,6 +48,35 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { c.Assert(err, check.Equals, errorNamespaceNotEmpty) } +func (s *Suite) TestRenameNamespace(c *check.C) { + n, err := h.CreateNamespace("test") + c.Assert(err, check.IsNil) + c.Assert(n.Name, check.Equals, "test") + + ns, err := h.ListNamespaces() + c.Assert(err, check.IsNil) + c.Assert(len(*ns), check.Equals, 1) + + err = h.RenameNamespace("test", "test_renamed") + c.Assert(err, check.IsNil) + + _, err = h.GetNamespace("test") + c.Assert(err, check.Equals, errorNamespaceNotFound) + + _, err = h.GetNamespace("test_renamed") + c.Assert(err, check.IsNil) + + err = h.RenameNamespace("test_does_not_exit", "test") + c.Assert(err, check.Equals, errorNamespaceNotFound) + + n2, err := h.CreateNamespace("test2") + c.Assert(err, check.IsNil) + c.Assert(n2.Name, check.Equals, "test2") + + err = h.RenameNamespace("test2", "test_renamed") + c.Assert(err, check.Equals, errorNamespaceExists) +} + func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { n1, err := h.CreateNamespace("shared1") c.Assert(err, check.IsNil)