Skip to content

Commit 0f933c1

Browse files
authored
Merge pull request #24 from cure/add-destroy-namespace-command
Add a DestroyNamespace command and tests for the Namespace functions.
2 parents 3cf599b + b20b664 commit 0f933c1

File tree

6 files changed

+175
-44
lines changed

6 files changed

+175
-44
lines changed

app_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package headscale
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"testing"
7+
8+
_ "github.com/jinzhu/gorm/dialects/sqlite" // sql driver
9+
10+
"gopkg.in/check.v1"
11+
)
12+
13+
func Test(t *testing.T) {
14+
check.TestingT(t)
15+
}
16+
17+
var _ = check.Suite(&Suite{})
18+
19+
type Suite struct{}
20+
21+
var tmpDir string
22+
var h Headscale
23+
24+
func (s *Suite) SetUpTest(c *check.C) {
25+
s.ResetDB(c)
26+
}
27+
28+
func (s *Suite) TearDownTest(c *check.C) {
29+
os.RemoveAll(tmpDir)
30+
}
31+
32+
func (s *Suite) ResetDB(c *check.C) {
33+
if len(tmpDir) != 0 {
34+
os.RemoveAll(tmpDir)
35+
}
36+
var err error
37+
tmpDir, err = ioutil.TempDir("", "autoygg-client-test")
38+
if err != nil {
39+
c.Fatal(err)
40+
}
41+
cfg := Config{}
42+
43+
h = Headscale{
44+
cfg: cfg,
45+
dbType: "sqlite3",
46+
dbString: tmpDir + "/headscale_test.db",
47+
}
48+
err = h.initDB()
49+
if err != nil {
50+
c.Fatal(err)
51+
}
52+
}

cmd/headscale/cli/namespaces.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,34 @@ var CreateNamespaceCmd = &cobra.Command{
4141
},
4242
}
4343

44+
var DestroyNamespaceCmd = &cobra.Command{
45+
Use: "destroy NAME",
46+
Short: "Destroys a namespace",
47+
Args: func(cmd *cobra.Command, args []string) error {
48+
if len(args) < 1 {
49+
return fmt.Errorf("Missing parameters")
50+
}
51+
return nil
52+
},
53+
Run: func(cmd *cobra.Command, args []string) {
54+
o, _ := cmd.Flags().GetString("output")
55+
h, err := getHeadscaleApp()
56+
if err != nil {
57+
log.Fatalf("Error initializing: %s", err)
58+
}
59+
err = h.DestroyNamespace(args[0])
60+
if strings.HasPrefix(o, "json") {
61+
JsonOutput(map[string]string{"Result": "Namespace destroyed"}, err, o)
62+
return
63+
}
64+
if err != nil {
65+
fmt.Printf("Error destroying namespace: %s\n", err)
66+
return
67+
}
68+
fmt.Printf("Namespace destroyed\n")
69+
},
70+
}
71+
4472
var ListNamespacesCmd = &cobra.Command{
4573
Use: "list",
4674
Short: "List all the namespaces",

cmd/headscale/headscale.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func main() {
115115

116116
cli.NamespaceCmd.AddCommand(cli.CreateNamespaceCmd)
117117
cli.NamespaceCmd.AddCommand(cli.ListNamespacesCmd)
118+
cli.NamespaceCmd.AddCommand(cli.DestroyNamespaceCmd)
118119

119120
cli.NodeCmd.AddCommand(cli.ListNodesCmd)
120121
cli.NodeCmd.AddCommand(cli.RegisterCmd)

namespaces.go

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package headscale
22

33
import (
4-
"fmt"
54
"log"
65
"time"
76

87
"github.com/jinzhu/gorm"
98
"tailscale.com/tailcfg"
109
)
1110

11+
const errorNamespaceExists = Error("Namespace already exists")
12+
const errorNamespaceNotFound = Error("Namespace not found")
13+
const errorNamespaceNotEmpty = Error("Namespace not empty")
14+
1215
// Namespace is the way Headscale implements the concept of users in Tailscale
1316
//
1417
// At the end of the day, users in Tailscale are some kind of 'bubbles' or namespaces
@@ -30,7 +33,7 @@ func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
3033

3134
n := Namespace{}
3235
if err := db.Where("name = ?", name).First(&n).Error; err == nil {
33-
return nil, fmt.Errorf("Namespace already exists")
36+
return nil, errorNamespaceExists
3437
}
3538
n.Name = name
3639
if err := db.Create(&n).Error; err != nil {
@@ -40,6 +43,37 @@ func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
4043
return &n, nil
4144
}
4245

46+
// DestroyNamespace destroys a Namespace. Returns error if the Namespace does
47+
// not exist or if there are machines associated with it.
48+
func (h *Headscale) DestroyNamespace(name string) error {
49+
db, err := h.db()
50+
if err != nil {
51+
log.Printf("Cannot open DB: %s", err)
52+
return err
53+
}
54+
defer db.Close()
55+
56+
n, err := h.GetNamespace(name)
57+
if err != nil {
58+
return errorNamespaceNotFound
59+
}
60+
61+
m, err := h.ListMachinesInNamespace(name)
62+
if err != nil {
63+
return err
64+
}
65+
if len(*m) > 0 {
66+
return errorNamespaceNotEmpty
67+
}
68+
69+
err = db.Unscoped().Delete(&n).Error
70+
if err != nil {
71+
return err
72+
}
73+
74+
return nil
75+
}
76+
4377
// GetNamespace fetches a namespace by name
4478
func (h *Headscale) GetNamespace(name string) (*Namespace, error) {
4579
db, err := h.db()
@@ -51,7 +85,7 @@ func (h *Headscale) GetNamespace(name string) (*Namespace, error) {
5185

5286
n := Namespace{}
5387
if db.First(&n, "name = ?", name).RecordNotFound() {
54-
return nil, fmt.Errorf("Namespace not found")
88+
return nil, errorNamespaceNotFound
5589
}
5690
return &n, nil
5791
}

namespaces_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package headscale
2+
3+
import (
4+
//_ "github.com/jinzhu/gorm/dialects/sqlite" // sql driver
5+
6+
"gopkg.in/check.v1"
7+
)
8+
9+
var _ = check.Suite(&Suite{})
10+
11+
func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) {
12+
n, err := h.CreateNamespace("test")
13+
c.Assert(err, check.IsNil)
14+
c.Assert(n.Name, check.Equals, "test")
15+
16+
ns, err := h.ListNamespaces()
17+
c.Assert(err, check.IsNil)
18+
c.Assert(len(*ns), check.Equals, 1)
19+
20+
err = h.DestroyNamespace("test")
21+
c.Assert(err, check.IsNil)
22+
23+
_, err = h.GetNamespace("test")
24+
c.Assert(err, check.NotNil)
25+
}
26+
27+
func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
28+
err := h.DestroyNamespace("test")
29+
c.Assert(err, check.Equals, errorNamespaceNotFound)
30+
31+
n, err := h.CreateNamespace("test")
32+
c.Assert(err, check.IsNil)
33+
34+
pak, err := h.CreatePreAuthKey(n.Name, false, nil)
35+
c.Assert(err, check.IsNil)
36+
37+
db, err := h.db()
38+
if err != nil {
39+
c.Fatal(err)
40+
}
41+
defer db.Close()
42+
m := Machine{
43+
ID: 0,
44+
MachineKey: "foo",
45+
NodeKey: "bar",
46+
DiscoKey: "faa",
47+
Name: "testmachine",
48+
NamespaceID: n.ID,
49+
Registered: true,
50+
RegisterMethod: "authKey",
51+
AuthKeyID: uint(pak.ID),
52+
}
53+
db.Save(&m)
54+
55+
err = h.DestroyNamespace("test")
56+
c.Assert(err, check.Equals, errorNamespaceNotEmpty)
57+
}

preauth_keys_test.go

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,11 @@
11
package headscale
22

33
import (
4-
"fmt"
5-
"io/ioutil"
6-
"os"
7-
"testing"
84
"time"
95

10-
_ "github.com/jinzhu/gorm/dialects/sqlite" // sql driver
11-
126
"gopkg.in/check.v1"
137
)
148

15-
func Test(t *testing.T) {
16-
check.TestingT(t)
17-
}
18-
19-
var _ = check.Suite(&Suite{})
20-
21-
type Suite struct{}
22-
23-
var tmpDir string
24-
var h Headscale
25-
26-
func (s *Suite) SetUpSuite(c *check.C) {
27-
var err error
28-
tmpDir, err = ioutil.TempDir("", "autoygg-client-test")
29-
if err != nil {
30-
c.Fatal(err)
31-
}
32-
fmt.Printf("tmpDir is %s\n", tmpDir)
33-
cfg := Config{}
34-
35-
h = Headscale{
36-
cfg: cfg,
37-
dbType: "sqlite3",
38-
dbString: tmpDir + "/headscale_test.db",
39-
}
40-
err = h.initDB()
41-
if err != nil {
42-
c.Fatal(err)
43-
}
44-
}
45-
46-
func (s *Suite) TearDownSuite(c *check.C) {
47-
os.RemoveAll(tmpDir)
48-
}
49-
509
func (*Suite) TestCreatePreAuthKey(c *check.C) {
5110
_, err := h.CreatePreAuthKey("bogus", true, nil)
5211

0 commit comments

Comments
 (0)