Skip to content

Commit 3b34f71

Browse files
committed
Adding support for JSON-formatted output 1/n
1 parent 4b3b484 commit 3b34f71

File tree

4 files changed

+87
-9
lines changed

4 files changed

+87
-9
lines changed

cmd/headscale/cli/namespaces.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cli
33
import (
44
"fmt"
55
"log"
6+
"strings"
67

78
"github.com/spf13/cobra"
89
)
@@ -22,34 +23,44 @@ var CreateNamespaceCmd = &cobra.Command{
2223
return nil
2324
},
2425
Run: func(cmd *cobra.Command, args []string) {
26+
o, _ := cmd.Flags().GetString("output")
2527
h, err := getHeadscaleApp()
2628
if err != nil {
2729
log.Fatalf("Error initializing: %s", err)
2830
}
29-
_, err = h.CreateNamespace(args[0])
31+
namespace, err := h.CreateNamespace(args[0])
32+
if strings.HasPrefix(o, "json") {
33+
jsonOutput(namespace, err, o)
34+
return
35+
}
3036
if err != nil {
31-
fmt.Println(err)
37+
fmt.Printf("Error creating namespace: %s\n", err)
3238
return
3339
}
34-
fmt.Printf("Ook.\n")
40+
fmt.Printf("Namespace created\n")
3541
},
3642
}
3743

3844
var ListNamespacesCmd = &cobra.Command{
3945
Use: "list",
4046
Short: "List all the namespaces",
4147
Run: func(cmd *cobra.Command, args []string) {
48+
o, _ := cmd.Flags().GetString("output")
4249
h, err := getHeadscaleApp()
4350
if err != nil {
4451
log.Fatalf("Error initializing: %s", err)
4552
}
46-
ns, err := h.ListNamespaces()
53+
namespaces, err := h.ListNamespaces()
54+
if strings.HasPrefix(o, "json") {
55+
jsonOutput(namespaces, err, o)
56+
return
57+
}
4758
if err != nil {
4859
fmt.Println(err)
4960
return
5061
}
5162
fmt.Printf("ID\tName\n")
52-
for _, n := range *ns {
63+
for _, n := range *namespaces {
5364
fmt.Printf("%d\t%s\n", n.ID, n.Name)
5465
}
5566
},

cmd/headscale/cli/nodes.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cli
33
import (
44
"fmt"
55
"log"
6+
"strings"
67

78
"github.com/spf13/cobra"
89
)
@@ -21,17 +22,22 @@ var RegisterCmd = &cobra.Command{
2122
if err != nil {
2223
log.Fatalf("Error getting namespace: %s", err)
2324
}
25+
o, _ := cmd.Flags().GetString("output")
2426

2527
h, err := getHeadscaleApp()
2628
if err != nil {
2729
log.Fatalf("Error initializing: %s", err)
2830
}
29-
err = h.RegisterMachine(args[0], n)
31+
m, err := h.RegisterMachine(args[0], n)
32+
if strings.HasPrefix(o, "json") {
33+
jsonOutput(m, err, o)
34+
return
35+
}
3036
if err != nil {
31-
fmt.Printf("Error: %s", err)
37+
fmt.Printf("Cannot register machine: %s\n", err)
3238
return
3339
}
34-
fmt.Println("Ook.")
40+
fmt.Printf("Machine registered\n")
3541
},
3642
}
3743

cmd/headscale/cli/utils.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cli
22

33
import (
4+
"encoding/json"
5+
"fmt"
46
"io"
57
"log"
68
"os"
@@ -13,6 +15,10 @@ import (
1315
"tailscale.com/tailcfg"
1416
)
1517

18+
type ErrorOutput struct {
19+
Error string
20+
}
21+
1622
func absPath(path string) string {
1723
// If a relative path is provided, prefix it with the the directory where
1824
// the config file was found.
@@ -72,3 +78,35 @@ func loadDerpMap(path string) (*tailcfg.DERPMap, error) {
7278
err = yaml.Unmarshal(b, &derpMap)
7379
return &derpMap, err
7480
}
81+
82+
func jsonOutput(result interface{}, errResult error, outputFormat string) {
83+
var j []byte
84+
var err error
85+
switch outputFormat {
86+
case "json":
87+
if errResult != nil {
88+
j, err = json.MarshalIndent(ErrorOutput{errResult.Error()}, "", "\t")
89+
if err != nil {
90+
log.Fatalln(err)
91+
}
92+
} else {
93+
j, err = json.MarshalIndent(result, "", "\t")
94+
if err != nil {
95+
log.Fatalln(err)
96+
}
97+
}
98+
case "json-line":
99+
if errResult != nil {
100+
j, err = json.Marshal(ErrorOutput{errResult.Error()})
101+
if err != nil {
102+
log.Fatalln(err)
103+
}
104+
} else {
105+
j, err = json.Marshal(result)
106+
if err != nil {
107+
log.Fatalln(err)
108+
}
109+
}
110+
}
111+
fmt.Println(string(j))
112+
}

cmd/headscale/headscale.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"encoding/json"
45
"errors"
56
"fmt"
67
"log"
@@ -19,7 +20,27 @@ var versionCmd = &cobra.Command{
1920
Short: "Print the version.",
2021
Long: "The version of headscale.",
2122
Run: func(cmd *cobra.Command, args []string) {
22-
fmt.Println(version)
23+
24+
o, _ := cmd.Flags().GetString("output")
25+
switch o {
26+
case "":
27+
fmt.Println(version)
28+
case "json":
29+
j, err := json.MarshalIndent(map[string]string{"version": version}, "", "\t")
30+
if err != nil {
31+
log.Fatalln(err)
32+
}
33+
fmt.Println(string(j))
34+
35+
case "json-line":
36+
j, err := json.Marshal(map[string]string{"version": version})
37+
if err != nil {
38+
log.Fatalln(err)
39+
}
40+
fmt.Println(string(j))
41+
default:
42+
fmt.Printf("Unknown format %s\n", o)
43+
}
2344
},
2445
}
2546

@@ -123,6 +144,8 @@ func main() {
123144
cli.CreatePreAuthKeyCmd.PersistentFlags().Bool("reusable", false, "Make the preauthkey reusable")
124145
cli.CreatePreAuthKeyCmd.Flags().StringP("expiration", "e", "", "Human-readable expiration of the key (30m, 24h, 365d...)")
125146

147+
headscaleCmd.PersistentFlags().StringP("output", "o", "", "Output format. Empty for human-readable, 'json' or 'json-line'")
148+
126149
if err := headscaleCmd.Execute(); err != nil {
127150
fmt.Println(err)
128151
os.Exit(-1)

0 commit comments

Comments
 (0)