Browse Source

Merge pull request #1846 from gravitl/release_v0.17.1

Release v0.17.1
dcarns 2 years ago
parent
commit
8133d9ab29
100 changed files with 3144 additions and 33 deletions
  1. 1 0
      .github/ISSUE_TEMPLATE/bug-report.yml
  2. 8 8
      .github/workflows/buildandrelease.yml
  3. 5 5
      .github/workflows/publish-docker.yml
  4. 7 6
      .github/workflows/test.yml
  5. 2 0
      .gitignore
  6. 1 1
      Dockerfile
  7. 1 1
      README.md
  8. 13 11
      auth/auth.go
  9. 1 1
      auth/nodecallback.go
  10. 46 0
      cli/cmd/acl/allow.go
  11. 46 0
      cli/cmd/acl/deny.go
  12. 46 0
      cli/cmd/acl/list.go
  13. 38 0
      cli/cmd/acl/root.go
  14. 20 0
      cli/cmd/context/delete.go
  15. 20 0
      cli/cmd/context/list.go
  16. 38 0
      cli/cmd/context/root.go
  17. 44 0
      cli/cmd/context/set.go
  18. 20 0
      cli/cmd/context/use.go
  19. 33 0
      cli/cmd/dns/create.go
  20. 20 0
      cli/cmd/dns/delete.go
  21. 9 0
      cli/cmd/dns/flags.go
  22. 47 0
      cli/cmd/dns/list.go
  23. 22 0
      cli/cmd/dns/push.go
  24. 38 0
      cli/cmd/dns/root.go
  25. 22 0
      cli/cmd/ext_client/config.go
  26. 26 0
      cli/cmd/ext_client/create.go
  27. 20 0
      cli/cmd/ext_client/delete.go
  28. 20 0
      cli/cmd/ext_client/get.go
  29. 40 0
      cli/cmd/ext_client/list.go
  30. 38 0
      cli/cmd/ext_client/root.go
  31. 71 0
      cli/cmd/ext_client/update.go
  32. 35 0
      cli/cmd/keys/create.go
  33. 23 0
      cli/cmd/keys/delete.go
  34. 20 0
      cli/cmd/keys/list.go
  35. 38 0
      cli/cmd/keys/root.go
  36. 22 0
      cli/cmd/logs.go
  37. 20 0
      cli/cmd/metrics/all.go
  38. 20 0
      cli/cmd/metrics/network.go
  39. 20 0
      cli/cmd/metrics/network_ext.go
  40. 20 0
      cli/cmd/metrics/node.go
  41. 38 0
      cli/cmd/metrics/root.go
  42. 85 0
      cli/cmd/network/create.go
  43. 22 0
      cli/cmd/network/delete.go
  44. 22 0
      cli/cmd/network/flags.go
  45. 20 0
      cli/cmd/network/get.go
  46. 32 0
      cli/cmd/network/list.go
  47. 27 0
      cli/cmd/network/node_limit.go
  48. 20 0
      cli/cmd/network/refresh_keys.go
  49. 40 0
      cli/cmd/network/root.go
  50. 86 0
      cli/cmd/network/update.go
  51. 43 0
      cli/cmd/network_user/create.go
  52. 23 0
      cli/cmd/network_user/delete.go
  53. 10 0
      cli/cmd/network_user/flags.go
  54. 27 0
      cli/cmd/network_user/get.go
  55. 27 0
      cli/cmd/network_user/list.go
  56. 38 0
      cli/cmd/network_user/root.go
  57. 43 0
      cli/cmd/network_user/update.go
  58. 34 0
      cli/cmd/node/create_egress.go
  59. 21 0
      cli/cmd/node/create_ingress.go
  60. 22 0
      cli/cmd/node/create_relay.go
  61. 20 0
      cli/cmd/node/delete.go
  62. 20 0
      cli/cmd/node/delete_egress.go
  63. 20 0
      cli/cmd/node/delete_ingress.go
  64. 20 0
      cli/cmd/node/delete_relay.go
  65. 28 0
      cli/cmd/node/flags.go
  66. 20 0
      cli/cmd/node/get.go
  67. 47 0
      cli/cmd/node/list.go
  68. 38 0
      cli/cmd/node/root.go
  69. 22 0
      cli/cmd/node/uncordon.go
  70. 100 0
      cli/cmd/node/update.go
  71. 69 0
      cli/cmd/root.go
  72. 20 0
      cli/cmd/server/config.go
  73. 22 0
      cli/cmd/server/has_admin.go
  74. 20 0
      cli/cmd/server/health.go
  75. 20 0
      cli/cmd/server/info.go
  76. 38 0
      cli/cmd/server/root.go
  77. 37 0
      cli/cmd/user/create.go
  78. 20 0
      cli/cmd/user/delete.go
  79. 9 0
      cli/cmd/user/flags.go
  80. 20 0
      cli/cmd/user/get.go
  81. 30 0
      cli/cmd/user/list.go
  82. 38 0
      cli/cmd/user/root.go
  83. 35 0
      cli/cmd/user/update.go
  84. 23 0
      cli/cmd/usergroup/create.go
  85. 23 0
      cli/cmd/usergroup/delete.go
  86. 20 0
      cli/cmd/usergroup/get.go
  87. 38 0
      cli/cmd/usergroup/root.go
  88. 144 0
      cli/config/config.go
  89. 18 0
      cli/functions/acl.go
  90. 43 0
      cli/functions/dns.go
  91. 49 0
      cli/functions/ext_client.go
  92. 118 0
      cli/functions/http_client.go
  93. 23 0
      cli/functions/keys.go
  94. 28 0
      cli/functions/metrics.go
  95. 45 0
      cli/functions/network.go
  96. 44 0
      cli/functions/network_user.go
  97. 73 0
      cli/functions/node.go
  98. 16 0
      cli/functions/pretty_print.go
  99. 28 0
      cli/functions/server.go
  100. 37 0
      cli/functions/user.go

+ 1 - 0
.github/ISSUE_TEMPLATE/bug-report.yml

@@ -31,6 +31,7 @@ body:
       label: Version
       description: What version are you running?
       options:
+        - v0.17.1      
         - v0.17.0
         - v0.16.3
         - v0.16.2

+ 8 - 8
.github/workflows/buildandrelease.yml

@@ -55,7 +55,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -84,7 +84,7 @@ jobs:
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
 
@@ -189,7 +189,7 @@ jobs:
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -289,7 +289,7 @@ jobs:
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -351,7 +351,7 @@ jobs:
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -413,7 +413,7 @@ jobs:
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -488,7 +488,7 @@ jobs:
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -575,7 +575,7 @@ jobs:
     needs: [version, netclient-x86, netclient-arm]
     steps:
       - name: Repository Dispatch
-        uses: peter-evans/repository-dispatch@v2.0.0
+        uses: peter-evans/repository-dispatch@v2.1.1
         with:
           token: ${{ secrets.PERS_TOKEN_FOR_NETMAKER_DEVOPS}}
           repository: gravitl/netmaker-devops

+ 5 - 5
.github/workflows/publish-docker.yml

@@ -47,7 +47,9 @@ jobs:
           platforms: linux/amd64, linux/arm64, linux/arm/v7
           push: true
           tags: ${{ github.repository }}:${{ env.TAG }}, ${{ github.repository }}:latest
-          build-args: version=${{ env.TAG }}
+          build-args: | 
+            version=${{ env.TAG }}
+            tags=ce
 
   docker-ee:
     runs-on: ubuntu-latest
@@ -56,7 +58,7 @@ jobs:
         name: Set tag
         run: |
             if [[ -n "${{ github.event.inputs.tag }}" ]]; then
-               TAG=${{ github.event.inputs.tag }}
+              TAG=${{ github.event.inputs.tag }}
             elif [[ "${{ github.ref_name }}" == 'master' ]]; then
               TAG="latest"
             else
@@ -72,8 +74,7 @@ jobs:
       -
         name: Set up Docker Buildx
         uses: docker/setup-buildx-action@v2
-      -
-
+      - 
         name: Login to DockerHub
         uses: docker/login-action@v2
         with:
@@ -90,4 +91,3 @@ jobs:
           build-args: |
             version=${{ env.TAG }}
             tags=ee
-

+ 7 - 6
.github/workflows/test.yml

@@ -1,8 +1,9 @@
 name: Integration Test
 
 on:
+  workflow_dispatch:
   pull_request:
-    types: [opened, reopened]
+    types: [opened, synchronize, reopened]
 
 jobs:
   build:
@@ -11,7 +12,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -29,7 +30,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build
@@ -43,7 +44,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Build mac
@@ -55,7 +56,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: Mysys2 setup
@@ -75,7 +76,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19
       - name: run tests

+ 2 - 0
.gitignore

@@ -3,6 +3,7 @@ netmaker-arm
 netmaker-arm64
 netmaker-32
 netmaker-amd64
+cli/nmctl
 netclient/netclient
 netclient/netclient.syso
 netclient/build
@@ -22,3 +23,4 @@ data/
 .vscode/
 .idea/
 netmaker.exe
+netmaker.code-workspace

+ 1 - 1
Dockerfile

@@ -7,7 +7,7 @@ COPY . .
 ENV GO111MODULE=auto
 
 RUN apk add git
-RUN GOOS=linux CGO_ENABLED=1 go build -tags ${tags} -ldflags="-s -X 'main.version=${version}'" .
+RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -X 'main.version=${version}'" -tags ${tags} .
 # RUN go build -tags=ee . -o netmaker main.go
 FROM alpine:3.16.2
 

+ 1 - 1
README.md

@@ -17,7 +17,7 @@
 
 <p align="center">
   <a href="https://github.com/gravitl/netmaker/releases">
-    <img src="https://img.shields.io/badge/Version-0.17.0-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.17.1-informational?style=flat-square" />
   </a>
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />

+ 13 - 11
auth/auth.go

@@ -3,17 +3,19 @@ package auth
 import (
 	"encoding/base64"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"net/http"
 	"strings"
 
+	"golang.org/x/crypto/bcrypt"
+	"golang.org/x/oauth2"
+
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/pro/netcache"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
-	"golang.org/x/crypto/bcrypt"
-	"golang.org/x/oauth2"
 )
 
 // == consts ==
@@ -94,12 +96,12 @@ func InitializeAuthProvider() string {
 	return authInfo[0]
 }
 
-// Not included in API reference as part of the OAuth process itself.
 // HandleAuthCallback - handles oauth callback
+// Note: not included in API reference as part of the OAuth process itself.
 func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
 	if auth_provider == nil {
 		w.Header().Set("Content-Type", "text/html; charset=utf-8")
-		fmt.Fprintln(w, oauthNotConfigured)
+		_, _ = fmt.Fprintln(w, oauthNotConfigured)
 		return
 	}
 	var functions = getCurrentAuthFunctions()
@@ -108,7 +110,7 @@ func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
 	}
 	state, _ := getStateAndCode(r)
 	_, err := netcache.Get(state) // if in netcache proceeed with node registration login
-	if err == nil || len(state) == node_signin_length || (err != nil && strings.Contains(err.Error(), "expired")) {
+	if err == nil || len(state) == node_signin_length || errors.Is(err, netcache.ErrExpired) {
 		logger.Log(0, "proceeding with node SSO callback")
 		HandleNodeSSOCallback(w, r)
 	} else { // handle normal login
@@ -120,10 +122,10 @@ func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
 //
 // Handles OAuth login.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 func HandleAuthLogin(w http.ResponseWriter, r *http.Request) {
 	if auth_provider == nil {
 		var referer = r.Header.Get("referer")
@@ -132,7 +134,7 @@ func HandleAuthLogin(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 		w.Header().Set("Content-Type", "text/html; charset=utf-8")
-		fmt.Fprintln(w, oauthNotConfigured)
+		_, _ = fmt.Fprintln(w, oauthNotConfigured)
 		return
 	}
 	var functions = getCurrentAuthFunctions()
@@ -169,7 +171,7 @@ func addUser(email string) error {
 		Password: newPass,
 	}
 	if !hasAdmin { // must be first attempt, create an admin
-		if newUser, err = logic.CreateAdmin(newUser); err != nil {
+		if err = logic.CreateAdmin(&newUser); err != nil {
 			logger.Log(1, "error creating admin from user,", email, "; user not added")
 		} else {
 			logger.Log(1, "admin created from user,", email, "; was first user added")
@@ -177,7 +179,7 @@ func addUser(email string) error {
 	} else { // otherwise add to db as admin..?
 		// TODO: add ability to add users with preemptive permissions
 		newUser.IsAdmin = false
-		if newUser, err = logic.CreateUser(newUser); err != nil {
+		if err = logic.CreateUser(&newUser); err != nil {
 			logger.Log(1, "error creating user,", email, "; user not added")
 		} else {
 			logger.Log(0, "user created from ", email)

+ 1 - 1
auth/nodecallback.go

@@ -257,5 +257,5 @@ func isUserIsAllowed(username, network string, shouldAddUser bool) (*models.User
 		}
 	}
 
-	return &user, nil
+	return user, nil
 }

+ 46 - 0
cli/cmd/acl/allow.go

@@ -0,0 +1,46 @@
+package acl
+
+import (
+	"fmt"
+	"log"
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/spf13/cobra"
+)
+
+var aclAllowCmd = &cobra.Command{
+	Use:   "allow [NETWORK NAME] [FROM_NODE_NAME] [TO_NODE_NAME]",
+	Args:  cobra.ExactArgs(3),
+	Short: "Allow access from one node to another",
+	Long:  `Allow access from one node to another`,
+	Run: func(cmd *cobra.Command, args []string) {
+		nameIDMap := make(map[string]string)
+		for _, node := range *functions.GetNodes(args[0]) {
+			nameIDMap[strings.ToLower(node.Name)] = node.ID
+		}
+		fromNodeID, ok := nameIDMap[strings.ToLower(args[1])]
+		if !ok {
+			log.Fatalf("Node %s doesn't exist", args[1])
+		}
+		toNodeID, ok := nameIDMap[strings.ToLower(args[2])]
+		if !ok {
+			log.Fatalf("Node %s doesn't exist", args[2])
+		}
+		payload := acls.ACLContainer(map[acls.AclID]acls.ACL{
+			acls.AclID(fromNodeID): map[acls.AclID]byte{
+				acls.AclID(toNodeID): acls.Allowed,
+			},
+			acls.AclID(toNodeID): map[acls.AclID]byte{
+				acls.AclID(fromNodeID): acls.Allowed,
+			},
+		})
+		functions.UpdateACL(args[0], &payload)
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(aclAllowCmd)
+}

+ 46 - 0
cli/cmd/acl/deny.go

@@ -0,0 +1,46 @@
+package acl
+
+import (
+	"fmt"
+	"log"
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/spf13/cobra"
+)
+
+var aclDenyCmd = &cobra.Command{
+	Use:   "deny [NETWORK NAME] [FROM_NODE_NAME] [TO_NODE_NAME]",
+	Args:  cobra.ExactArgs(3),
+	Short: "Deny access from one node to another",
+	Long:  `Deny access from one node to another`,
+	Run: func(cmd *cobra.Command, args []string) {
+		nameIDMap := make(map[string]string)
+		for _, node := range *functions.GetNodes(args[0]) {
+			nameIDMap[strings.ToLower(node.Name)] = node.ID
+		}
+		fromNodeID, ok := nameIDMap[strings.ToLower(args[1])]
+		if !ok {
+			log.Fatalf("Node %s doesn't exist", args[1])
+		}
+		toNodeID, ok := nameIDMap[strings.ToLower(args[2])]
+		if !ok {
+			log.Fatalf("Node %s doesn't exist", args[2])
+		}
+		payload := acls.ACLContainer(map[acls.AclID]acls.ACL{
+			acls.AclID(fromNodeID): map[acls.AclID]byte{
+				acls.AclID(toNodeID): acls.NotAllowed,
+			},
+			acls.AclID(toNodeID): map[acls.AclID]byte{
+				acls.AclID(fromNodeID): acls.NotAllowed,
+			},
+		})
+		functions.UpdateACL(args[0], &payload)
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(aclDenyCmd)
+}

+ 46 - 0
cli/cmd/acl/list.go

@@ -0,0 +1,46 @@
+package acl
+
+import (
+	"os"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/guumaster/tablewriter"
+	"github.com/spf13/cobra"
+)
+
+var aclListCmd = &cobra.Command{
+	Use:   "list [NETWORK NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "List all ACLs associated with a network",
+	Long:  `List all ACLs associated with a network`,
+	Run: func(cmd *cobra.Command, args []string) {
+		aclSource := (map[acls.AclID]acls.ACL)(*functions.GetACL(args[0]))
+		nodes := functions.GetNodes(args[0])
+		idNameMap := make(map[string]string)
+		for _, node := range *nodes {
+			idNameMap[node.ID] = node.Name
+		}
+		table := tablewriter.NewWriter(os.Stdout)
+		table.SetHeader([]string{"From", "To", "Status"})
+		for id, acl := range aclSource {
+			for k, v := range (map[acls.AclID]byte)(acl) {
+				row := []string{idNameMap[string(id)], idNameMap[string(k)]}
+				switch v {
+				case acls.NotAllowed:
+					row = append(row, "Not Allowed")
+				case acls.NotPresent:
+					row = append(row, "Not Present")
+				case acls.Allowed:
+					row = append(row, "Allowed")
+				}
+				table.Append(row)
+			}
+		}
+		table.Render()
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(aclListCmd)
+}

+ 38 - 0
cli/cmd/acl/root.go

@@ -0,0 +1,38 @@
+package acl
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "acl",
+	Short: "Manage Access Control Lists (ACLs)",
+	Long:  `Manage Access Control Lists (ACLs)`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 20 - 0
cli/cmd/context/delete.go

@@ -0,0 +1,20 @@
+package context
+
+import (
+	"github.com/gravitl/netmaker/cli/config"
+	"github.com/spf13/cobra"
+)
+
+var contextDeleteCmd = &cobra.Command{
+	Use:   "delete [NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Delete a context",
+	Long:  `Delete a context`,
+	Run: func(cmd *cobra.Command, args []string) {
+		config.DeleteContext(args[0])
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(contextDeleteCmd)
+}

+ 20 - 0
cli/cmd/context/list.go

@@ -0,0 +1,20 @@
+package context
+
+import (
+	"github.com/gravitl/netmaker/cli/config"
+	"github.com/spf13/cobra"
+)
+
+var contextListCmd = &cobra.Command{
+	Use:   "list",
+	Args:  cobra.NoArgs,
+	Short: "List all contexts",
+	Long:  `List all contexts`,
+	Run: func(cmd *cobra.Command, args []string) {
+		config.ListAll()
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(contextListCmd)
+}

+ 38 - 0
cli/cmd/context/root.go

@@ -0,0 +1,38 @@
+package context
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "context",
+	Short: "Manage various netmaker server configurations",
+	Long:  `Manage various netmaker server configurations`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 44 - 0
cli/cmd/context/set.go

@@ -0,0 +1,44 @@
+package context
+
+import (
+	"log"
+
+	"github.com/gravitl/netmaker/cli/config"
+	"github.com/spf13/cobra"
+)
+
+var (
+	endpoint  string
+	username  string
+	password  string
+	masterKey string
+)
+
+var contextSetCmd = &cobra.Command{
+	Use:   "set [NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Create a context or update an existing one",
+	Long:  `Create a context or update an existing one`,
+	Run: func(cmd *cobra.Command, args []string) {
+		ctx := config.Context{
+			Endpoint:  endpoint,
+			Username:  username,
+			Password:  password,
+			MasterKey: masterKey,
+		}
+		if ctx.Username == "" && ctx.MasterKey == "" {
+			cmd.Usage()
+			log.Fatal("Either username/password or master key is required")
+		}
+		config.SetContext(args[0], ctx)
+	},
+}
+
+func init() {
+	contextSetCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the API Server")
+	contextSetCmd.Flags().StringVar(&username, "username", "", "Username")
+	contextSetCmd.Flags().StringVar(&password, "password", "", "Password")
+	contextSetCmd.MarkFlagsRequiredTogether("username", "password")
+	contextSetCmd.Flags().StringVar(&masterKey, "master_key", "", "Master Key")
+	rootCmd.AddCommand(contextSetCmd)
+}

+ 20 - 0
cli/cmd/context/use.go

@@ -0,0 +1,20 @@
+package context
+
+import (
+	"github.com/gravitl/netmaker/cli/config"
+	"github.com/spf13/cobra"
+)
+
+var contextUseCmd = &cobra.Command{
+	Use:   "use [NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Set the current context",
+	Long:  `Set the current context`,
+	Run: func(cmd *cobra.Command, args []string) {
+		config.SetCurrentContext(args[0])
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(contextUseCmd)
+}

+ 33 - 0
cli/cmd/dns/create.go

@@ -0,0 +1,33 @@
+package dns
+
+import (
+	"log"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var dnsCreateCmd = &cobra.Command{
+	Use:   "create",
+	Args:  cobra.NoArgs,
+	Short: "Create a DNS entry",
+	Long:  `Create a DNS entry`,
+	Run: func(cmd *cobra.Command, args []string) {
+		if address == "" && address6 == "" {
+			log.Fatal("Either IPv4 or IPv6 address is required")
+		}
+		dnsEntry := &models.DNSEntry{Name: dnsName, Address: address, Address6: address6, Network: networkName}
+		functions.PrettyPrint(functions.CreateDNS(networkName, dnsEntry))
+	},
+}
+
+func init() {
+	dnsCreateCmd.Flags().StringVar(&dnsName, "name", "", "Name of the DNS entry")
+	dnsCreateCmd.MarkFlagRequired("name")
+	dnsCreateCmd.Flags().StringVar(&networkName, "network", "", "Name of the Network")
+	dnsCreateCmd.MarkFlagRequired("network")
+	dnsCreateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 Address")
+	dnsCreateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 Address")
+	rootCmd.AddCommand(dnsCreateCmd)
+}

+ 20 - 0
cli/cmd/dns/delete.go

@@ -0,0 +1,20 @@
+package dns
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var dnsDeleteCmd = &cobra.Command{
+	Use:   "delete [NETWORK NAME] [DOMAIN NAME]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete a DNS entry",
+	Long:  `Delete a DNS entry`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.DeleteDNS(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(dnsDeleteCmd)
+}

+ 9 - 0
cli/cmd/dns/flags.go

@@ -0,0 +1,9 @@
+package dns
+
+var (
+	dnsName     string
+	address     string
+	address6    string
+	networkName string
+	dnsType     string
+)

+ 47 - 0
cli/cmd/dns/list.go

@@ -0,0 +1,47 @@
+package dns
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/guumaster/tablewriter"
+	"github.com/spf13/cobra"
+)
+
+var dnsListCmd = &cobra.Command{
+	Use:   "list",
+	Args:  cobra.NoArgs,
+	Short: "List DNS entries",
+	Long:  `List DNS entries`,
+	Run: func(cmd *cobra.Command, args []string) {
+		var data []models.DNSEntry
+		if networkName != "" {
+			switch dnsType {
+			case "node":
+				data = *functions.GetNodeDNS(networkName)
+			case "custom":
+				data = *functions.GetCustomDNS(networkName)
+			case "network", "":
+				data = *functions.GetNetworkDNS(networkName)
+			default:
+				fmt.Println("Invalid DNS type provided ", dnsType)
+			}
+		} else {
+			data = *functions.GetDNS()
+		}
+		table := tablewriter.NewWriter(os.Stdout)
+		table.SetHeader([]string{"Name", "Network", "IPv4 Address", "IPv6 Address"})
+		for _, d := range data {
+			table.Append([]string{d.Name, d.Network, d.Address, d.Address6})
+		}
+		table.Render()
+	},
+}
+
+func init() {
+	dnsListCmd.Flags().StringVar(&networkName, "network", "", "Network name")
+	dnsListCmd.Flags().StringVar(&dnsType, "type", "", "Type of DNS records to fetch ENUM(node, custom, network)")
+	rootCmd.AddCommand(dnsListCmd)
+}

+ 22 - 0
cli/cmd/dns/push.go

@@ -0,0 +1,22 @@
+package dns
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var dnsPushCmd = &cobra.Command{
+	Use:   "push",
+	Args:  cobra.NoArgs,
+	Short: "Push latest DNS entries",
+	Long:  `Push latest DNS entries`,
+	Run: func(cmd *cobra.Command, args []string) {
+		fmt.Println(*functions.PushDNS())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(dnsPushCmd)
+}

+ 38 - 0
cli/cmd/dns/root.go

@@ -0,0 +1,38 @@
+package dns
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "dns",
+	Short: "Manage DNS entries associated with a network",
+	Long:  `Manage DNS entries associated with a network`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 22 - 0
cli/cmd/ext_client/config.go

@@ -0,0 +1,22 @@
+package ext_client
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var extClientConfigCmd = &cobra.Command{
+	Use:   "config [NETWORK NAME] [EXTERNAL CLIENT ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Get an External Client Configuration",
+	Long:  `Get an External Client Configuration`,
+	Run: func(cmd *cobra.Command, args []string) {
+		fmt.Println(functions.GetExtClientConfig(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(extClientConfigCmd)
+}

+ 26 - 0
cli/cmd/ext_client/create.go

@@ -0,0 +1,26 @@
+package ext_client
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var extClientID string
+
+var extClientCreateCmd = &cobra.Command{
+	Use:   "create [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Create an External Client",
+	Long:  `Create an External Client`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.CreateExtClient(args[0], args[1], extClientID)
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	extClientCreateCmd.Flags().StringVar(&extClientID, "id", "", "ID of the external client")
+	rootCmd.AddCommand(extClientCreateCmd)
+}

+ 20 - 0
cli/cmd/ext_client/delete.go

@@ -0,0 +1,20 @@
+package ext_client
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var extClientDeleteCmd = &cobra.Command{
+	Use:   "delete [NETWORK NAME] [EXTERNAL CLIENT ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete an External Client",
+	Long:  `Delete an External Client`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.DeleteExtClient(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(extClientDeleteCmd)
+}

+ 20 - 0
cli/cmd/ext_client/get.go

@@ -0,0 +1,20 @@
+package ext_client
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var extClientGetCmd = &cobra.Command{
+	Use:   "get [NETWORK NAME] [EXTERNAL CLIENT ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Get an External Client",
+	Long:  `Get an External Client`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetExtClient(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(extClientGetCmd)
+}

+ 40 - 0
cli/cmd/ext_client/list.go

@@ -0,0 +1,40 @@
+package ext_client
+
+import (
+	"os"
+	"strconv"
+	"time"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/guumaster/tablewriter"
+	"github.com/spf13/cobra"
+)
+
+var networkName string
+
+var extClientListCmd = &cobra.Command{
+	Use:   "list",
+	Args:  cobra.NoArgs,
+	Short: "List External Clients",
+	Long:  `List External Clients`,
+	Run: func(cmd *cobra.Command, args []string) {
+		var data []models.ExtClient
+		if networkName != "" {
+			data = *functions.GetNetworkExtClients(networkName)
+		} else {
+			data = *functions.GetAllExtClients()
+		}
+		table := tablewriter.NewWriter(os.Stdout)
+		table.SetHeader([]string{"Client ID", "Network", "IPv4 Address", "IPv6 Address", "Enabled", "Last Modified"})
+		for _, d := range data {
+			table.Append([]string{d.ClientID, d.Network, d.Address, d.Address6, strconv.FormatBool(d.Enabled), time.Unix(d.LastModified, 0).String()})
+		}
+		table.Render()
+	},
+}
+
+func init() {
+	extClientListCmd.Flags().StringVar(&networkName, "network", "", "Network name")
+	rootCmd.AddCommand(extClientListCmd)
+}

+ 38 - 0
cli/cmd/ext_client/root.go

@@ -0,0 +1,38 @@
+package ext_client
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "ext_client",
+	Short: "Manage External Clients",
+	Long:  `Manage External Clients`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 71 - 0
cli/cmd/ext_client/update.go

@@ -0,0 +1,71 @@
+package ext_client
+
+import (
+	"encoding/json"
+	"log"
+	"os"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var (
+	extClientUpdateFile    string
+	description            string
+	privateKey             string
+	publicKey              string
+	address                string
+	address6               string
+	ingressGatewayID       string
+	ingressGatewayEndpoint string
+	ownerID                string
+)
+
+var extClientUpdateCmd = &cobra.Command{
+	Use:   "update [NETWORK NAME] [EXTERNAL CLIENT ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Update an External Client",
+	Long:  `Update an External Client`,
+	Run: func(cmd *cobra.Command, args []string) {
+		var (
+			network   = args[0]
+			clientID  = args[1]
+			extClient = &models.ExtClient{}
+		)
+		if extClientUpdateFile != "" {
+			content, err := os.ReadFile(extClientUpdateFile)
+			if err != nil {
+				log.Fatal("Error when opening file: ", err)
+			}
+			if err := json.Unmarshal(content, extClient); err != nil {
+				log.Fatal(err)
+			}
+		} else {
+			extClient.ClientID = clientID
+			extClient.Description = description
+			extClient.PrivateKey = privateKey
+			extClient.PublicKey = publicKey
+			extClient.Network = network
+			extClient.Address = address
+			extClient.Address6 = address6
+			extClient.IngressGatewayID = ingressGatewayID
+			extClient.IngressGatewayEndpoint = ingressGatewayEndpoint
+			extClient.OwnerID = ownerID
+		}
+		functions.PrettyPrint(functions.UpdateExtClient(network, clientID, extClient))
+	},
+}
+
+func init() {
+	extClientUpdateCmd.Flags().StringVar(&extClientUpdateFile, "file", "", "Filepath of updated external client definition in JSON")
+	extClientUpdateCmd.Flags().StringVar(&description, "desc", "", "Description of the external client")
+	extClientUpdateCmd.Flags().StringVar(&privateKey, "private_key", "", "Filepath of updated external client definition in JSON")
+	extClientUpdateCmd.Flags().StringVar(&publicKey, "public_key", "", "Filepath of updated external client definition in JSON")
+	extClientUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the external client")
+	extClientUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the external client")
+	extClientUpdateCmd.Flags().StringVar(&ingressGatewayID, "ingress_gateway_id", "", "ID of the ingress gateway")
+	extClientUpdateCmd.Flags().StringVar(&ingressGatewayEndpoint, "ingress_gateway_endpoint", "", "Endpoint of the ingress gateway")
+	extClientUpdateCmd.Flags().StringVar(&ownerID, "owner_id", "", "External Client owner's ID")
+	rootCmd.AddCommand(extClientUpdateCmd)
+}

+ 35 - 0
cli/cmd/keys/create.go

@@ -0,0 +1,35 @@
+package keys
+
+import (
+	"log"
+	"strconv"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var keyName string
+
+var keysCreateCmd = &cobra.Command{
+	Use:   "create [NETWORK NAME] [NUM USES]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Create an access key",
+	Long:  `Create an access key`,
+	Run: func(cmd *cobra.Command, args []string) {
+		keyUses, err := strconv.ParseInt(args[1], 10, 64)
+		if err != nil {
+			log.Fatal(err)
+		}
+		key := &models.AccessKey{Uses: int(keyUses)}
+		if keyName != "" {
+			key.Name = keyName
+		}
+		functions.PrettyPrint(functions.CreateKey(args[0], key))
+	},
+}
+
+func init() {
+	keysCreateCmd.Flags().StringVar(&keyName, "name", "", "Name of the key")
+	rootCmd.AddCommand(keysCreateCmd)
+}

+ 23 - 0
cli/cmd/keys/delete.go

@@ -0,0 +1,23 @@
+package keys
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var keysDeleteCmd = &cobra.Command{
+	Use:   "delete [NETWORK NAME] [KEY NAME]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete a key",
+	Long:  `Delete a key`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.DeleteKey(args[0], args[1])
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(keysDeleteCmd)
+}

+ 20 - 0
cli/cmd/keys/list.go

@@ -0,0 +1,20 @@
+package keys
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var keysListCmd = &cobra.Command{
+	Use:   "list [NETWORK NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "List all keys associated with a network",
+	Long:  `List all keys associated with a network`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetKeys(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(keysListCmd)
+}

+ 38 - 0
cli/cmd/keys/root.go

@@ -0,0 +1,38 @@
+package keys
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "keys",
+	Short: "Manage access keys associated with a network",
+	Long:  `Manage access keys associated with a network`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 22 - 0
cli/cmd/logs.go

@@ -0,0 +1,22 @@
+package cmd
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var getLogsCmd = &cobra.Command{
+	Use:   "logs",
+	Args:  cobra.NoArgs,
+	Short: "Retrieve server logs",
+	Long:  `Retrieve server logs`,
+	Run: func(cmd *cobra.Command, args []string) {
+		fmt.Println(functions.GetLogs())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(getLogsCmd)
+}

+ 20 - 0
cli/cmd/metrics/all.go

@@ -0,0 +1,20 @@
+package metrics
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var metricsAllCmd = &cobra.Command{
+	Use:   "all",
+	Args:  cobra.NoArgs,
+	Short: "Retrieve all metrics",
+	Long:  `Retrieve all metrics`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetAllMetrics())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(metricsAllCmd)
+}

+ 20 - 0
cli/cmd/metrics/network.go

@@ -0,0 +1,20 @@
+package metrics
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var metricsNetworkCmd = &cobra.Command{
+	Use:   "network [NETWORK NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Retrieve network metrics",
+	Long:  `Retrieve network metrics`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetNetworkNodeMetrics(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(metricsNetworkCmd)
+}

+ 20 - 0
cli/cmd/metrics/network_ext.go

@@ -0,0 +1,20 @@
+package metrics
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var metricsNetworkExtCmd = &cobra.Command{
+	Use:   "network_ext [NETWORK NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Retrieve metrics of external clients on a given network",
+	Long:  `Retrieve metrics of external clients on a given network`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetNetworkExtMetrics(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(metricsNetworkExtCmd)
+}

+ 20 - 0
cli/cmd/metrics/node.go

@@ -0,0 +1,20 @@
+package metrics
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var metricsNodeCmd = &cobra.Command{
+	Use:   "node [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Retrieve node metrics",
+	Long:  `Retrieve node metrics`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetNodeMetrics(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(metricsNodeCmd)
+}

+ 38 - 0
cli/cmd/metrics/root.go

@@ -0,0 +1,38 @@
+package metrics
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "metrics",
+	Short: "Fetch metrics of nodes/networks",
+	Long:  `Fetch metrics of nodes/networks`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 85 - 0
cli/cmd/network/create.go

@@ -0,0 +1,85 @@
+package network
+
+import (
+	"encoding/json"
+	"log"
+	"os"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var networkCreateCmd = &cobra.Command{
+	Use:   "create",
+	Short: "Create a Network",
+	Long:  `Create a Network`,
+	Args:  cobra.NoArgs,
+	Run: func(cmd *cobra.Command, args []string) {
+		network := &models.Network{}
+		if networkDefinitionFilePath != "" {
+			content, err := os.ReadFile(networkDefinitionFilePath)
+			if err != nil {
+				log.Fatal("Error when opening file: ", err)
+			}
+			if err := json.Unmarshal(content, network); err != nil {
+				log.Fatal(err)
+			}
+		} else {
+			network.NetID = netID
+			network.AddressRange = address
+			if address6 != "" {
+				network.AddressRange6 = address6
+				network.IsIPv6 = "yes"
+			}
+			if udpHolePunch {
+				network.DefaultUDPHolePunch = "yes"
+			}
+			if localNetwork {
+				network.IsLocal = "yes"
+			}
+			if defaultACL {
+				network.DefaultACL = "yes"
+			}
+			if pointToSite {
+				network.IsPointToSite = "yes"
+			}
+			network.DefaultInterface = defaultInterface
+			network.DefaultListenPort = int32(defaultListenPort)
+			network.NodeLimit = int32(nodeLimit)
+			network.DefaultPostUp = defaultPostUp
+			network.DefaultPostDown = defaultPostDown
+			network.DefaultKeepalive = int32(defaultKeepalive)
+			if allowManualSignUp {
+				network.AllowManualSignUp = "yes"
+			}
+			network.LocalRange = localRange
+			network.DefaultExtClientDNS = defaultExtClientDNS
+			network.DefaultMTU = int32(defaultMTU)
+		}
+		functions.PrettyPrint(functions.CreateNetwork(network))
+	},
+}
+
+func init() {
+	networkCreateCmd.Flags().StringVar(&networkDefinitionFilePath, "file", "", "Path to network_definition.json")
+	networkCreateCmd.Flags().StringVar(&netID, "name", "", "Name of the network")
+	networkCreateCmd.MarkFlagsMutuallyExclusive("file", "name")
+	networkCreateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the network")
+	networkCreateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the network")
+	networkCreateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
+	networkCreateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
+	networkCreateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
+	networkCreateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
+	networkCreateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
+	networkCreateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
+	networkCreateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")
+	networkCreateCmd.Flags().StringVar(&localRange, "local_range", "", "Local CIDR range")
+	networkCreateCmd.Flags().StringVar(&defaultExtClientDNS, "ext_client_dns", "", "IPv4 address of DNS server to be used by external clients")
+	networkCreateCmd.Flags().IntVar(&defaultListenPort, "listen_port", 51821, "Default wireguard port each node will attempt to use")
+	networkCreateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be associated with this network")
+	networkCreateCmd.Flags().IntVar(&defaultKeepalive, "keep_alive", 20, "Keep Alive in seconds")
+	networkCreateCmd.Flags().IntVar(&defaultMTU, "mtu", 1280, "MTU size")
+	networkCreateCmd.Flags().BoolVar(&allowManualSignUp, "manual_signup", false, "Allow manual signup ?")
+	rootCmd.AddCommand(networkCreateCmd)
+}

+ 22 - 0
cli/cmd/network/delete.go

@@ -0,0 +1,22 @@
+package network
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var networkDeleteCmd = &cobra.Command{
+	Use:   "delete [NAME]",
+	Short: "Delete a Network",
+	Long:  `Delete a Network`,
+	Args:  cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		fmt.Println(*functions.DeleteNetwork(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(networkDeleteCmd)
+}

+ 22 - 0
cli/cmd/network/flags.go

@@ -0,0 +1,22 @@
+package network
+
+var (
+	networkDefinitionFilePath string
+	netID                     string
+	address                   string
+	address6                  string
+	udpHolePunch              bool
+	localNetwork              bool
+	defaultACL                bool
+	pointToSite               bool
+	defaultInterface          string
+	defaultListenPort         int
+	nodeLimit                 int
+	defaultPostUp             string
+	defaultPostDown           string
+	defaultKeepalive          int
+	allowManualSignUp         bool
+	localRange                string
+	defaultExtClientDNS       string
+	defaultMTU                int
+)

+ 20 - 0
cli/cmd/network/get.go

@@ -0,0 +1,20 @@
+package network
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var networkGetCmd = &cobra.Command{
+	Use:   "get [NETWORK NAME]",
+	Short: "Get a Network",
+	Long:  `Get a Network`,
+	Args:  cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetNetwork(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(networkGetCmd)
+}

+ 32 - 0
cli/cmd/network/list.go

@@ -0,0 +1,32 @@
+package network
+
+import (
+	"os"
+	"time"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/olekukonko/tablewriter"
+	"github.com/spf13/cobra"
+)
+
+var networkListCmd = &cobra.Command{
+	Use:   "list",
+	Short: "List all Networks",
+	Long:  `List all Networks`,
+	Args:  cobra.NoArgs,
+	Run: func(cmd *cobra.Command, args []string) {
+		networks := functions.GetNetworks()
+		table := tablewriter.NewWriter(os.Stdout)
+		table.SetHeader([]string{"NetId", "Address Range (IPv4)", "Address Range (IPv6)", "Network Last Modified", "Nodes Last Modified"})
+		for _, n := range *networks {
+			networkLastModified := time.Unix(n.NetworkLastModified, 0).Format(time.RFC3339)
+			nodesLastModified := time.Unix(n.NodesLastModified, 0).Format(time.RFC3339)
+			table.Append([]string{n.NetID, n.AddressRange, n.AddressRange6, networkLastModified, nodesLastModified})
+		}
+		table.Render()
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(networkListCmd)
+}

+ 27 - 0
cli/cmd/network/node_limit.go

@@ -0,0 +1,27 @@
+package network
+
+import (
+	"log"
+	"strconv"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var networkNodeLimitCmd = &cobra.Command{
+	Use:   "node_limit [NETWORK NAME] [NEW LIMIT]",
+	Short: "Update network nodel limit",
+	Long:  `Update network nodel limit`,
+	Args:  cobra.ExactArgs(2),
+	Run: func(cmd *cobra.Command, args []string) {
+		nodelimit, err := strconv.ParseInt(args[1], 10, 32)
+		if err != nil {
+			log.Fatal(err)
+		}
+		functions.PrettyPrint(functions.UpdateNetworkNodeLimit(args[0], int32(nodelimit)))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(networkNodeLimitCmd)
+}

+ 20 - 0
cli/cmd/network/refresh_keys.go

@@ -0,0 +1,20 @@
+package network
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var networkRefreshKeysCmd = &cobra.Command{
+	Use:   "refresh_keys [NETWORK NAME]",
+	Short: "Refresh public and private key pairs of a network",
+	Long:  `Refresh public and private key pairs of a network`,
+	Args:  cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.RefreshKeys(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(networkRefreshKeysCmd)
+}

+ 40 - 0
cli/cmd/network/root.go

@@ -0,0 +1,40 @@
+package network
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "network",
+	Short: "Manage Netmaker Networks",
+	Long:  `Manage Netmaker Networks`,
+	// Uncomment the following line if your bare application
+	// has an action associated with it:
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 86 - 0
cli/cmd/network/update.go

@@ -0,0 +1,86 @@
+package network
+
+import (
+	"encoding/json"
+	"log"
+	"os"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var networkUpdateCmd = &cobra.Command{
+	Use:   "update [NETWORK NAME]",
+	Short: "Update a Network",
+	Long:  `Update a Network`,
+	Args:  cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		var (
+			networkName = args[0]
+			network     = &models.Network{}
+		)
+		if networkDefinitionFilePath != "" {
+			content, err := os.ReadFile(networkDefinitionFilePath)
+			if err != nil {
+				log.Fatal("Error when opening file: ", err)
+			}
+			if err := json.Unmarshal(content, network); err != nil {
+				log.Fatal(err)
+			}
+		} else {
+			network.NetID = networkName
+			network.AddressRange = address
+			if address6 != "" {
+				network.AddressRange6 = address6
+				network.IsIPv6 = "yes"
+			}
+			if udpHolePunch {
+				network.DefaultUDPHolePunch = "yes"
+			}
+			if localNetwork {
+				network.IsLocal = "yes"
+			}
+			if defaultACL {
+				network.DefaultACL = "yes"
+			}
+			if pointToSite {
+				network.IsPointToSite = "yes"
+			}
+			network.DefaultInterface = defaultInterface
+			network.DefaultListenPort = int32(defaultListenPort)
+			network.NodeLimit = int32(nodeLimit)
+			network.DefaultPostUp = defaultPostUp
+			network.DefaultPostDown = defaultPostDown
+			network.DefaultKeepalive = int32(defaultKeepalive)
+			if allowManualSignUp {
+				network.AllowManualSignUp = "yes"
+			}
+			network.LocalRange = localRange
+			network.DefaultExtClientDNS = defaultExtClientDNS
+			network.DefaultMTU = int32(defaultMTU)
+		}
+		functions.PrettyPrint(functions.UpdateNetwork(networkName, network))
+	},
+}
+
+func init() {
+	networkUpdateCmd.Flags().StringVar(&networkDefinitionFilePath, "file", "", "Path to network_definition.json")
+	networkUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the network")
+	networkUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the network")
+	networkUpdateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
+	networkUpdateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
+	networkUpdateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
+	networkUpdateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
+	networkUpdateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
+	networkUpdateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
+	networkUpdateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")
+	networkUpdateCmd.Flags().StringVar(&localRange, "local_range", "", "Local CIDR range")
+	networkUpdateCmd.Flags().StringVar(&defaultExtClientDNS, "ext_client_dns", "", "IPv4 address of DNS server to be used by external clients")
+	networkUpdateCmd.Flags().IntVar(&defaultListenPort, "listen_port", 0, "Default wireguard port each node will attempt to use")
+	networkUpdateCmd.Flags().IntVar(&nodeLimit, "node_limit", 0, "Maximum number of nodes that can be associated with this network")
+	networkUpdateCmd.Flags().IntVar(&defaultKeepalive, "keep_alive", 0, "Keep Alive in seconds")
+	networkUpdateCmd.Flags().IntVar(&defaultMTU, "mtu", 0, "MTU size")
+	networkUpdateCmd.Flags().BoolVar(&allowManualSignUp, "manual_signup", false, "Allow manual signup ?")
+	rootCmd.AddCommand(networkUpdateCmd)
+}

+ 43 - 0
cli/cmd/network_user/create.go

@@ -0,0 +1,43 @@
+package network_user
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models/promodels"
+	"github.com/spf13/cobra"
+)
+
+var networkuserCreateCmd = &cobra.Command{
+	Use:   "create [NETWORK NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Create a network user",
+	Long:  `Create a network user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		user := &promodels.NetworkUser{
+			AccessLevel: accessLevel,
+			ClientLimit: clientLimit,
+			NodeLimit:   nodeLimit, ID: promodels.NetworkUserID(id),
+		}
+		if clients != "" {
+			user.Clients = strings.Split(clients, ",")
+		}
+		if nodes != "" {
+			user.Nodes = strings.Split(nodes, ",")
+		}
+		functions.CreateNetworkUser(args[0], user)
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	networkuserCreateCmd.Flags().IntVar(&accessLevel, "access_level", 0, "Custom access level")
+	networkuserCreateCmd.Flags().IntVar(&clientLimit, "client_limit", 0, "Maximum number of external clients that can be created")
+	networkuserCreateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be attached to a network")
+	networkuserCreateCmd.Flags().StringVar(&clients, "clients", "", "Access to list of external clients (comma separated)")
+	networkuserCreateCmd.Flags().StringVar(&nodes, "nodes", "", "Access to list of nodes (comma separated)")
+	networkuserCreateCmd.Flags().StringVar(&id, "id", "", "ID of the network user")
+	networkuserCreateCmd.MarkFlagRequired("id")
+	rootCmd.AddCommand(networkuserCreateCmd)
+}

+ 23 - 0
cli/cmd/network_user/delete.go

@@ -0,0 +1,23 @@
+package network_user
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var networkuserDeleteCmd = &cobra.Command{
+	Use:   "delete [NETWORK NAME] [NETWORK USER NAME]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete a network user",
+	Long:  `Delete a network user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.DeleteNetworkUser(args[0], args[1])
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(networkuserDeleteCmd)
+}

+ 10 - 0
cli/cmd/network_user/flags.go

@@ -0,0 +1,10 @@
+package network_user
+
+var (
+	accessLevel int
+	clientLimit int
+	nodeLimit   int
+	clients     string
+	nodes       string
+	id          string
+)

+ 27 - 0
cli/cmd/network_user/get.go

@@ -0,0 +1,27 @@
+package network_user
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var data bool
+
+var networkuserGetCmd = &cobra.Command{
+	Use:   "get [NETWORK NAME] [NETWORK USER NAME]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Fetch a network user",
+	Long:  `Fetch a network user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		if data {
+			functions.PrettyPrint(functions.GetNetworkUserData(args[1]))
+		} else {
+			functions.PrettyPrint(functions.GetNetworkUser(args[0], args[1]))
+		}
+	},
+}
+
+func init() {
+	networkuserGetCmd.Flags().BoolVar(&data, "data", false, "Fetch entire data of a network user")
+	rootCmd.AddCommand(networkuserGetCmd)
+}

+ 27 - 0
cli/cmd/network_user/list.go

@@ -0,0 +1,27 @@
+package network_user
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var networkName string
+
+var networkuserListCmd = &cobra.Command{
+	Use:   "list",
+	Args:  cobra.NoArgs,
+	Short: "List network users",
+	Long:  `List network users`,
+	Run: func(cmd *cobra.Command, args []string) {
+		if networkName != "" {
+			functions.PrettyPrint(functions.GetNetworkUsers(networkName))
+		} else {
+			functions.PrettyPrint(functions.GetAllNetworkUsers())
+		}
+	},
+}
+
+func init() {
+	networkuserListCmd.Flags().StringVar(&networkName, "network", "", "Name of the network")
+	rootCmd.AddCommand(networkuserListCmd)
+}

+ 38 - 0
cli/cmd/network_user/root.go

@@ -0,0 +1,38 @@
+package network_user
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "network_user",
+	Short: "Manage Network Users",
+	Long:  `Manage Network Users`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 43 - 0
cli/cmd/network_user/update.go

@@ -0,0 +1,43 @@
+package network_user
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models/promodels"
+	"github.com/spf13/cobra"
+)
+
+var networkuserUpdateCmd = &cobra.Command{
+	Use:   "update [NETWORK NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Update a network user",
+	Long:  `Update a network user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		user := &promodels.NetworkUser{
+			AccessLevel: accessLevel,
+			ClientLimit: clientLimit,
+			NodeLimit:   nodeLimit, ID: promodels.NetworkUserID(id),
+		}
+		if clients != "" {
+			user.Clients = strings.Split(clients, ",")
+		}
+		if nodes != "" {
+			user.Nodes = strings.Split(nodes, ",")
+		}
+		functions.UpdateNetworkUser(args[0], user)
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	networkuserUpdateCmd.Flags().IntVar(&accessLevel, "access_level", 0, "Custom access level")
+	networkuserUpdateCmd.Flags().IntVar(&clientLimit, "client_limit", 0, "Maximum number of external clients that can be created")
+	networkuserUpdateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be attached to a network")
+	networkuserUpdateCmd.Flags().StringVar(&clients, "clients", "", "Access to list of external clients (comma separated)")
+	networkuserUpdateCmd.Flags().StringVar(&nodes, "nodes", "", "Access to list of nodes (comma separated)")
+	networkuserUpdateCmd.Flags().StringVar(&id, "id", "", "ID of the network user")
+	networkuserUpdateCmd.MarkFlagRequired("id")
+	rootCmd.AddCommand(networkuserUpdateCmd)
+}

+ 34 - 0
cli/cmd/node/create_egress.go

@@ -0,0 +1,34 @@
+package node
+
+import (
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var nodeCreateEgressCmd = &cobra.Command{
+	Use:   "create_egress [NETWORK NAME] [NODE ID] [EGRESS GATEWAY ADDRESSES (comma separated)]",
+	Args:  cobra.ExactArgs(3),
+	Short: "Turn a Node into a Egress",
+	Long:  `Turn a Node into a Egress`,
+	Run: func(cmd *cobra.Command, args []string) {
+		egress := &models.EgressGatewayRequest{
+			NetID:     args[0],
+			NodeID:    args[1],
+			Interface: networkInterface,
+			Ranges:    strings.Split(args[2], ","),
+		}
+		if natEnabled {
+			egress.NatEnabled = "yes"
+		}
+		functions.PrettyPrint(functions.CreateEgress(args[0], args[1], egress))
+	},
+}
+
+func init() {
+	nodeCreateEgressCmd.Flags().StringVar(&networkInterface, "interface", "", "Network interface name (ex:- eth0)")
+	nodeCreateEgressCmd.Flags().BoolVar(&natEnabled, "nat", false, "Enable NAT for Egress Traffic ?")
+	rootCmd.AddCommand(nodeCreateEgressCmd)
+}

+ 21 - 0
cli/cmd/node/create_ingress.go

@@ -0,0 +1,21 @@
+package node
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeCreateIngressCmd = &cobra.Command{
+	Use:   "create_ingress [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Turn a Node into a Ingress",
+	Long:  `Turn a Node into a Ingress`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.CreateIngress(args[0], args[1], failover))
+	},
+}
+
+func init() {
+	nodeCreateIngressCmd.Flags().BoolVar(&failover, "failover", false, "Enable FailOver ?")
+	rootCmd.AddCommand(nodeCreateIngressCmd)
+}

+ 22 - 0
cli/cmd/node/create_relay.go

@@ -0,0 +1,22 @@
+package node
+
+import (
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeCreateRelayCmd = &cobra.Command{
+	Use:   "create_relay [NETWORK NAME] [NODE ID] [RELAY ADDRESSES (comma separated)]",
+	Args:  cobra.ExactArgs(3),
+	Short: "Turn a Node into a Relay",
+	Long:  `Turn a Node into a Relay`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.CreateRelay(args[0], args[1], strings.Split(args[2], ",")))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(nodeCreateRelayCmd)
+}

+ 20 - 0
cli/cmd/node/delete.go

@@ -0,0 +1,20 @@
+package node
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeDeleteCmd = &cobra.Command{
+	Use:   "delete [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete a Node",
+	Long:  `Delete a Node`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.DeleteNode(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(nodeDeleteCmd)
+}

+ 20 - 0
cli/cmd/node/delete_egress.go

@@ -0,0 +1,20 @@
+package node
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeDeleteEgressCmd = &cobra.Command{
+	Use:   "delete_egress [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete Egress role from a Node",
+	Long:  `Delete Egress role from a Node`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.DeleteEgress(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(nodeDeleteEgressCmd)
+}

+ 20 - 0
cli/cmd/node/delete_ingress.go

@@ -0,0 +1,20 @@
+package node
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeDeleteIngressCmd = &cobra.Command{
+	Use:   "delete_ingress [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete Ingress role from a Node",
+	Long:  `Delete Ingress role from a Node`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.DeleteIngress(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(nodeDeleteIngressCmd)
+}

+ 20 - 0
cli/cmd/node/delete_relay.go

@@ -0,0 +1,20 @@
+package node
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeDeleteRelayCmd = &cobra.Command{
+	Use:   "delete_relay [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete Relay role from a Node",
+	Long:  `Delete Relay role from a Node`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.DeleteRelay(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(nodeDeleteRelayCmd)
+}

+ 28 - 0
cli/cmd/node/flags.go

@@ -0,0 +1,28 @@
+package node
+
+var (
+	networkInterface       string
+	natEnabled             bool
+	failover               bool
+	networkName            string
+	nodeDefinitionFilePath string
+	endpoint               string
+	listenPort             int
+	address                string
+	address6               string
+	localAddress           string
+	name                   string
+	postUp                 string
+	postDown               string
+	allowedIPs             string
+	keepAlive              int
+	relayAddrs             string
+	egressGatewayRanges    string
+	localRange             string
+	mtu                    int
+	expirationDateTime     int
+	defaultACL             bool
+	dnsOn                  bool
+	disconnect             bool
+	networkHub             bool
+)

+ 20 - 0
cli/cmd/node/get.go

@@ -0,0 +1,20 @@
+package node
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeGetCmd = &cobra.Command{
+	Use:   "get [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Get a node by ID",
+	Long:  `Get a node by ID`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetNodeByID(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(nodeGetCmd)
+}

+ 47 - 0
cli/cmd/node/list.go

@@ -0,0 +1,47 @@
+package node
+
+import (
+	"os"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/guumaster/tablewriter"
+	"github.com/spf13/cobra"
+)
+
+// nodeListCmd lists all nodes
+var nodeListCmd = &cobra.Command{
+	Use:   "list",
+	Args:  cobra.NoArgs,
+	Short: "List all nodes",
+	Long:  `List all nodes`,
+	Run: func(cmd *cobra.Command, args []string) {
+		var data []models.Node
+		if networkName != "" {
+			data = *functions.GetNodes(networkName)
+		} else {
+			data = *functions.GetNodes()
+		}
+		table := tablewriter.NewWriter(os.Stdout)
+		table.SetHeader([]string{"Name", "Addresses", "Version", "Network", "Egress", "Ingress", "Relay", "ID"})
+		for _, d := range data {
+			addresses := ""
+			if d.Address != "" {
+				addresses += d.Address
+			}
+			if d.Address6 != "" {
+				if d.Address != "" {
+					addresses += ", "
+				}
+				addresses += d.Address6
+			}
+			table.Append([]string{d.Name, addresses, d.Version, d.Network, d.IsEgressGateway, d.IsIngressGateway, d.IsRelay, d.ID})
+		}
+		table.Render()
+	},
+}
+
+func init() {
+	nodeListCmd.Flags().StringVar(&networkName, "network", "", "Network name specifier")
+	rootCmd.AddCommand(nodeListCmd)
+}

+ 38 - 0
cli/cmd/node/root.go

@@ -0,0 +1,38 @@
+package node
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "node",
+	Short: "Manage nodes associated with a network",
+	Long:  `Manage nodes associated with a network`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 22 - 0
cli/cmd/node/uncordon.go

@@ -0,0 +1,22 @@
+package node
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var nodeUncordonCmd = &cobra.Command{
+	Use:   "uncordon [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Get a node by ID",
+	Long:  `Get a node by ID`,
+	Run: func(cmd *cobra.Command, args []string) {
+		fmt.Println(*functions.UncordonNode(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(nodeUncordonCmd)
+}

+ 100 - 0
cli/cmd/node/update.go

@@ -0,0 +1,100 @@
+package node
+
+import (
+	"encoding/json"
+	"log"
+	"os"
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var nodeUpdateCmd = &cobra.Command{
+	Use:   "update [NETWORK NAME] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Update a Node",
+	Long:  `Update a Node`,
+	Run: func(cmd *cobra.Command, args []string) {
+		var (
+			node        = &models.Node{}
+			networkName = args[0]
+			nodeID      = args[1]
+		)
+		if nodeDefinitionFilePath != "" {
+			content, err := os.ReadFile(nodeDefinitionFilePath)
+			if err != nil {
+				log.Fatal("Error when opening file: ", err)
+			}
+			if err := json.Unmarshal(content, node); err != nil {
+				log.Fatal(err)
+			}
+		} else {
+			if endpoint != "" {
+				node.Endpoint = endpoint
+				node.IsStatic = "no"
+			}
+			node.ListenPort = int32(listenPort)
+			node.Address = address
+			node.Address6 = address6
+			node.LocalAddress = localAddress
+			node.Name = name
+			node.PostUp = postUp
+			node.PostDown = postDown
+			if allowedIPs != "" {
+				node.AllowedIPs = strings.Split(allowedIPs, ",")
+			}
+			node.PersistentKeepalive = int32(keepAlive)
+			if relayAddrs != "" {
+				node.RelayAddrs = strings.Split(relayAddrs, ",")
+			}
+			if egressGatewayRanges != "" {
+				node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",")
+			}
+			if localRange != "" {
+				node.LocalRange = localRange
+				node.IsLocal = "yes"
+			}
+			node.MTU = int32(mtu)
+			node.ExpirationDateTime = int64(expirationDateTime)
+			if defaultACL {
+				node.DefaultACL = "yes"
+			}
+			if dnsOn {
+				node.DNSOn = "yes"
+			}
+			if disconnect {
+				node.Connected = "no"
+			}
+			if networkHub {
+				node.IsHub = "yes"
+			}
+		}
+		functions.PrettyPrint(functions.UpdateNode(networkName, nodeID, node))
+	},
+}
+
+func init() {
+	nodeUpdateCmd.Flags().StringVar(&nodeDefinitionFilePath, "file", "", "Filepath of updated node definition in JSON")
+	nodeUpdateCmd.Flags().StringVar(&endpoint, "endpoint", "", "Public endpoint of the node")
+	nodeUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Default wireguard port for the node")
+	nodeUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the node")
+	nodeUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the node")
+	nodeUpdateCmd.Flags().StringVar(&localAddress, "local_addr", "", "Locally reachable address of the node")
+	nodeUpdateCmd.Flags().StringVar(&name, "name", "", "Node name")
+	nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated")
+	nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated")
+	nodeUpdateCmd.Flags().StringVar(&allowedIPs, "allowed_addrs", "", "Additional private addresses given to the node (comma separated)")
+	nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers")
+	nodeUpdateCmd.Flags().StringVar(&relayAddrs, "relay_addrs", "", "Addresses for relaying connections if node acts as a relay")
+	nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress")
+	nodeUpdateCmd.Flags().StringVar(&localRange, "local_range", "", "Local range in which the node will look for private addresses to use as an endpoint if `LocalNetwork` is enabled")
+	nodeUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "MTU size")
+	nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")
+	nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?")
+	nodeUpdateCmd.Flags().BoolVar(&dnsOn, "dns", false, "Setup DNS entries for peers locally ?")
+	nodeUpdateCmd.Flags().BoolVar(&disconnect, "disconnect", false, "Disconnect from the network ?")
+	nodeUpdateCmd.Flags().BoolVar(&networkHub, "hub", false, "On a point to site network, this node is the only one which all peers connect to ?")
+	rootCmd.AddCommand(nodeUpdateCmd)
+}

+ 69 - 0
cli/cmd/root.go

@@ -0,0 +1,69 @@
+package cmd
+
+import (
+	"os"
+
+	"github.com/gravitl/netmaker/cli/cmd/acl"
+	"github.com/gravitl/netmaker/cli/cmd/context"
+	"github.com/gravitl/netmaker/cli/cmd/dns"
+	"github.com/gravitl/netmaker/cli/cmd/ext_client"
+	"github.com/gravitl/netmaker/cli/cmd/keys"
+	"github.com/gravitl/netmaker/cli/cmd/metrics"
+	"github.com/gravitl/netmaker/cli/cmd/network"
+	"github.com/gravitl/netmaker/cli/cmd/network_user"
+	"github.com/gravitl/netmaker/cli/cmd/node"
+	"github.com/gravitl/netmaker/cli/cmd/server"
+	"github.com/gravitl/netmaker/cli/cmd/user"
+	"github.com/gravitl/netmaker/cli/cmd/usergroup"
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "netmaker",
+	Short: "CLI for interacting with Netmaker Server",
+	Long:  `CLI for interacting with Netmaker Server`,
+	// Uncomment the following line if your bare application
+	// has an action associated with it:
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root of all subcommands
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+
+	// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tctl.yaml)")
+
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+	// IMP: Bind subcommands here
+	rootCmd.AddCommand(network.GetRoot())
+	rootCmd.AddCommand(context.GetRoot())
+	rootCmd.AddCommand(keys.GetRoot())
+	rootCmd.AddCommand(acl.GetRoot())
+	rootCmd.AddCommand(node.GetRoot())
+	rootCmd.AddCommand(dns.GetRoot())
+	rootCmd.AddCommand(server.GetRoot())
+	rootCmd.AddCommand(ext_client.GetRoot())
+	rootCmd.AddCommand(user.GetRoot())
+	rootCmd.AddCommand(usergroup.GetRoot())
+	rootCmd.AddCommand(metrics.GetRoot())
+	rootCmd.AddCommand(network_user.GetRoot())
+}

+ 20 - 0
cli/cmd/server/config.go

@@ -0,0 +1,20 @@
+package server
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var serverConfigCmd = &cobra.Command{
+	Use:   "config",
+	Args:  cobra.NoArgs,
+	Short: "Retrieve server config",
+	Long:  `Retrieve server config`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetServerConfig())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(serverConfigCmd)
+}

+ 22 - 0
cli/cmd/server/has_admin.go

@@ -0,0 +1,22 @@
+package server
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var serverHasAdminCmd = &cobra.Command{
+	Use:   "has_admin",
+	Args:  cobra.NoArgs,
+	Short: "Check if server has an admin",
+	Long:  `Check if server has an admin`,
+	Run: func(cmd *cobra.Command, args []string) {
+		fmt.Println(*functions.HasAdmin())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(serverHasAdminCmd)
+}

+ 20 - 0
cli/cmd/server/health.go

@@ -0,0 +1,20 @@
+package server
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var serverHealthCmd = &cobra.Command{
+	Use:   "health",
+	Args:  cobra.NoArgs,
+	Short: "View server health",
+	Long:  `View server health`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetServerHealth())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(serverHealthCmd)
+}

+ 20 - 0
cli/cmd/server/info.go

@@ -0,0 +1,20 @@
+package server
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var serverInfoCmd = &cobra.Command{
+	Use:   "info",
+	Args:  cobra.NoArgs,
+	Short: "Retrieve server information",
+	Long:  `Retrieve server information`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetServerInfo())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(serverInfoCmd)
+}

+ 38 - 0
cli/cmd/server/root.go

@@ -0,0 +1,38 @@
+package server
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "server",
+	Short: "Get netmaker server information",
+	Long:  `Get netmaker server information`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 37 - 0
cli/cmd/user/create.go

@@ -0,0 +1,37 @@
+package user
+
+import (
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var userCreateCmd = &cobra.Command{
+	Use:   "create",
+	Args:  cobra.NoArgs,
+	Short: "Create a new user",
+	Long:  `Create a new user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		user := &models.User{UserName: username, Password: password, IsAdmin: admin}
+		if networks != "" {
+			user.Networks = strings.Split(networks, ",")
+		}
+		if groups != "" {
+			user.Groups = strings.Split(groups, ",")
+		}
+		functions.PrettyPrint(functions.CreateUser(user))
+	},
+}
+
+func init() {
+	userCreateCmd.Flags().StringVar(&username, "name", "", "Name of the user")
+	userCreateCmd.Flags().StringVar(&password, "password", "", "Password of the user")
+	userCreateCmd.MarkFlagRequired("name")
+	userCreateCmd.MarkFlagRequired("password")
+	userCreateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ?")
+	userCreateCmd.Flags().StringVar(&networks, "networks", "", "List of networks the user will access to (comma separated)")
+	userCreateCmd.Flags().StringVar(&groups, "groups", "", "List of user groups the user will be part of (comma separated)")
+	rootCmd.AddCommand(userCreateCmd)
+}

+ 20 - 0
cli/cmd/user/delete.go

@@ -0,0 +1,20 @@
+package user
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var userDeleteCmd = &cobra.Command{
+	Use:   "delete [USER NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Delete a user",
+	Long:  `Delete a user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(*functions.DeleteUser(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(userDeleteCmd)
+}

+ 9 - 0
cli/cmd/user/flags.go

@@ -0,0 +1,9 @@
+package user
+
+var (
+	username string
+	password string
+	admin    bool
+	networks string
+	groups   string
+)

+ 20 - 0
cli/cmd/user/get.go

@@ -0,0 +1,20 @@
+package user
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var userGetCmd = &cobra.Command{
+	Use:   "get [USER NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Get a user",
+	Long:  `Get a user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetUser(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(userGetCmd)
+}

+ 30 - 0
cli/cmd/user/list.go

@@ -0,0 +1,30 @@
+package user
+
+import (
+	"os"
+	"strconv"
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/guumaster/tablewriter"
+	"github.com/spf13/cobra"
+)
+
+var userListCmd = &cobra.Command{
+	Use:   "list",
+	Args:  cobra.NoArgs,
+	Short: "List all users",
+	Long:  `List all users`,
+	Run: func(cmd *cobra.Command, args []string) {
+		table := tablewriter.NewWriter(os.Stdout)
+		table.SetHeader([]string{"Name", "Admin", "Networks", "Groups"})
+		for _, d := range *functions.ListUsers() {
+			table.Append([]string{d.UserName, strconv.FormatBool(d.IsAdmin), strings.Join(d.Networks, ", "), strings.Join(d.Groups, ", ")})
+		}
+		table.Render()
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(userListCmd)
+}

+ 38 - 0
cli/cmd/user/root.go

@@ -0,0 +1,38 @@
+package user
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "user",
+	Short: "Manage users and permissions",
+	Long:  `Manage users and permissions`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 35 - 0
cli/cmd/user/update.go

@@ -0,0 +1,35 @@
+package user
+
+import (
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/spf13/cobra"
+)
+
+var userUpdateCmd = &cobra.Command{
+	Use:   "update [USER NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Update a user",
+	Long:  `Update a user`,
+	Run: func(cmd *cobra.Command, args []string) {
+		user := &models.User{UserName: args[0], IsAdmin: admin}
+		if networks != "" {
+			user.Networks = strings.Split(networks, ",")
+		}
+		if groups != "" {
+			user.Groups = strings.Split(groups, ",")
+		} else {
+			user.Groups = []string{"*"}
+		}
+		functions.PrettyPrint(functions.UpdateUser(user))
+	},
+}
+
+func init() {
+	userUpdateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ?")
+	userUpdateCmd.Flags().StringVar(&networks, "networks", "", "List of networks the user will access to (comma separated)")
+	userUpdateCmd.Flags().StringVar(&groups, "groups", "", "List of user groups the user will be part of (comma separated)")
+	rootCmd.AddCommand(userUpdateCmd)
+}

+ 23 - 0
cli/cmd/usergroup/create.go

@@ -0,0 +1,23 @@
+package usergroup
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var usergroupCreateCmd = &cobra.Command{
+	Use:   "create [GROUP NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Create a usergroup",
+	Long:  `Create a usergroup`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.CreateUsergroup(args[0])
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(usergroupCreateCmd)
+}

+ 23 - 0
cli/cmd/usergroup/delete.go

@@ -0,0 +1,23 @@
+package usergroup
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var usergroupDeleteCmd = &cobra.Command{
+	Use:   "delete [GROUP NAME]",
+	Args:  cobra.ExactArgs(1),
+	Short: "Delete a usergroup",
+	Long:  `Delete a usergroup`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.DeleteUsergroup(args[0])
+		fmt.Println("Success")
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(usergroupDeleteCmd)
+}

+ 20 - 0
cli/cmd/usergroup/get.go

@@ -0,0 +1,20 @@
+package usergroup
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var usergroupGetCmd = &cobra.Command{
+	Use:   "get",
+	Args:  cobra.NoArgs,
+	Short: "Fetch all usergroups",
+	Long:  `Fetch all usergroups`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.GetUsergroups())
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(usergroupGetCmd)
+}

+ 38 - 0
cli/cmd/usergroup/root.go

@@ -0,0 +1,38 @@
+package usergroup
+
+import (
+	"os"
+
+	"github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+	Use:   "usergroup",
+	Short: "Manage User Groups",
+	Long:  `Manage User Groups`,
+	// Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// GetRoot returns the root subcommand
+func GetRoot() *cobra.Command {
+	return rootCmd
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+	err := rootCmd.Execute()
+	if err != nil {
+		os.Exit(1)
+	}
+}
+
+func init() {
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+	// Cobra also supports local flags, which will only run
+	// when this action is called directly.
+	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}

+ 144 - 0
cli/config/config.go

@@ -0,0 +1,144 @@
+package config
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+
+	"gopkg.in/yaml.v3"
+)
+
+// Context maintains configuration for interaction with Netmaker API
+type Context struct {
+	Endpoint  string `yaml:"endpoint"`
+	Username  string `yaml:"username,omitempty"`
+	Password  string `yaml:"password,omitempty"`
+	MasterKey string `yaml:"masterkey,omitempty"`
+	Current   bool   `yaml:"current,omitempty"`
+	AuthToken string `yaml:"auth_token,omitempty"`
+}
+
+var (
+	contextMap     = map[string]Context{}
+	configFilePath string
+	filename       string
+)
+
+func createConfigPathIfNotExists() {
+	homeDir, err := os.UserHomeDir()
+	if err != nil {
+		log.Fatal(err)
+	}
+	configFilePath = filepath.Join(homeDir, ".netmaker")
+	// create directory if not exists
+	if err := os.MkdirAll(configFilePath, os.ModePerm); err != nil {
+		log.Fatal(err)
+	}
+	filename = filepath.Join(configFilePath, "config.yml")
+	// create file if not exists
+	if _, err := os.Stat(filename); err != nil {
+		if os.IsNotExist(err) {
+			if _, err := os.Create(filename); err != nil {
+				log.Fatalf("Unable to create file filename: %s", err)
+			}
+		} else {
+			log.Fatal(err)
+		}
+	}
+}
+
+func loadConfig() {
+	content, err := os.ReadFile(filename)
+	if err != nil {
+		log.Fatalf("Error reading config file: %s", err)
+	}
+	if err := yaml.Unmarshal(content, &contextMap); err != nil {
+		log.Fatalf("Unable to decode YAML into struct: %s", err)
+	}
+}
+
+func saveContext() {
+	bodyBytes, err := yaml.Marshal(&contextMap)
+	if err != nil {
+		log.Fatalf("Error marshalling into YAML %s", err)
+	}
+	file, err := os.Create(filename)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if _, err := file.Write(bodyBytes); err != nil {
+		log.Fatal(err)
+	}
+	if err := file.Close(); err != nil {
+		log.Fatal(err)
+	}
+}
+
+// GetCurrentContext - returns current set context
+func GetCurrentContext() (name string, ctx Context) {
+	for n, c := range contextMap {
+		if c.Current {
+			name, ctx = n, c
+			return
+		}
+	}
+	log.Fatalf("No current context set, do so via `netmaker context use <name>`")
+	return
+}
+
+// SetCurrentContext - sets a given context as current context
+func SetCurrentContext(ctxName string) {
+	if _, ok := contextMap[ctxName]; !ok {
+		log.Fatalf("No such context %s", ctxName)
+	}
+	for key, ctx := range contextMap {
+		ctx.Current = key == ctxName
+		contextMap[key] = ctx
+	}
+	saveContext()
+}
+
+// SetContext - updates an existing context or creates a new one
+func SetContext(ctxName string, ctx Context) {
+	if oldCtx, ok := contextMap[ctxName]; ok && oldCtx.Current {
+		ctx.Current = true
+	}
+	contextMap[ctxName] = ctx
+	saveContext()
+}
+
+// SetAuthToken - saves the auth token
+func SetAuthToken(authToken string) {
+	ctxName, _ := GetCurrentContext()
+	if ctx, ok := contextMap[ctxName]; ok {
+		ctx.AuthToken = authToken
+		contextMap[ctxName] = ctx
+		saveContext()
+	}
+}
+
+// DeleteContext - deletes a context
+func DeleteContext(ctxName string) {
+	if _, ok := contextMap[ctxName]; ok {
+		delete(contextMap, ctxName)
+		saveContext()
+	} else {
+		log.Fatalf("No such context %s", ctxName)
+	}
+}
+
+// ListAll - lists all contexts
+func ListAll() {
+	for key, ctx := range contextMap {
+		fmt.Print("\n", key, " -> ", ctx.Endpoint)
+		if ctx.Current {
+			fmt.Print(" (current)")
+		}
+	}
+}
+
+func init() {
+	createConfigPathIfNotExists()
+	loadConfig()
+}

+ 18 - 0
cli/functions/acl.go

@@ -0,0 +1,18 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/logic/acls"
+)
+
+// GetACL - fetch all ACLs associated with a network
+func GetACL(networkName string) *acls.ACLContainer {
+	return request[acls.ACLContainer](http.MethodGet, fmt.Sprintf("/api/networks/%s/acls", networkName), nil)
+}
+
+// UpdateACL - update an ACL
+func UpdateACL(networkName string, payload *acls.ACLContainer) *acls.ACLContainer {
+	return request[acls.ACLContainer](http.MethodPut, fmt.Sprintf("/api/networks/%s/acls", networkName), payload)
+}

+ 43 - 0
cli/functions/dns.go

@@ -0,0 +1,43 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetDNS - fetch all DNS entries
+func GetDNS() *[]models.DNSEntry {
+	return request[[]models.DNSEntry](http.MethodGet, "/api/dns", nil)
+}
+
+// GetNodeDNS - fetch all Node DNS entires
+func GetNodeDNS(networkName string) *[]models.DNSEntry {
+	return request[[]models.DNSEntry](http.MethodGet, fmt.Sprintf("/api/dns/adm/%s/nodes", networkName), nil)
+}
+
+// GetCustomDNS - fetch user defined DNS entriees
+func GetCustomDNS(networkName string) *[]models.DNSEntry {
+	return request[[]models.DNSEntry](http.MethodGet, fmt.Sprintf("/api/dns/adm/%s/custom", networkName), nil)
+}
+
+// GetNetworkDNS - fetch DNS entries associated with a network
+func GetNetworkDNS(networkName string) *[]models.DNSEntry {
+	return request[[]models.DNSEntry](http.MethodGet, "/api/dns/adm/"+networkName, nil)
+}
+
+// CreateDNS - create a DNS entry
+func CreateDNS(networkName string, payload *models.DNSEntry) *models.DNSEntry {
+	return request[models.DNSEntry](http.MethodPost, "/api/dns/"+networkName, payload)
+}
+
+// PushDNS - push a DNS entry to CoreDNS
+func PushDNS() *string {
+	return request[string](http.MethodPost, "/api/dns/adm/pushdns", nil)
+}
+
+// DeleteDNS - delete a DNS entry
+func DeleteDNS(networkName, domainName string) *string {
+	return request[string](http.MethodDelete, fmt.Sprintf("/api/dns/%s/%s", networkName, domainName), nil)
+}

+ 49 - 0
cli/functions/ext_client.go

@@ -0,0 +1,49 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetAllExtClients - fetch all external clients
+func GetAllExtClients() *[]models.ExtClient {
+	return request[[]models.ExtClient](http.MethodGet, "/api/extclients", nil)
+}
+
+// GetNetworkExtClients - fetch external clients associated with a network
+func GetNetworkExtClients(networkName string) *[]models.ExtClient {
+	return request[[]models.ExtClient](http.MethodGet, "/api/extclients/"+networkName, nil)
+}
+
+// GetExtClient - fetch a single external client
+func GetExtClient(networkName, clientID string) *models.ExtClient {
+	return request[models.ExtClient](http.MethodGet, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), nil)
+}
+
+// GetExtClientConfig - fetch a wireguard config of an external client
+func GetExtClientConfig(networkName, clientID string) string {
+	return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID))
+}
+
+// CreateExtClient - create an external client
+func CreateExtClient(networkName, nodeID, extClientID string) {
+	if extClientID != "" {
+		request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), &models.CustomExtClient{
+			ClientID: extClientID,
+		})
+	} else {
+		request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), nil)
+	}
+}
+
+// DeleteExtClient - delete an external client
+func DeleteExtClient(networkName, clientID string) *models.SuccessResponse {
+	return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), nil)
+}
+
+// UpdateExtClient - update an external client
+func UpdateExtClient(networkName, clientID string, payload *models.ExtClient) *models.ExtClient {
+	return request[models.ExtClient](http.MethodPut, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), payload)
+}

+ 118 - 0
cli/functions/http_client.go

@@ -0,0 +1,118 @@
+package functions
+
+import (
+	"bytes"
+	"encoding/json"
+	"io"
+	"log"
+	"net/http"
+
+	"github.com/gravitl/netmaker/cli/config"
+	"github.com/gravitl/netmaker/models"
+)
+
+func getAuthToken(ctx config.Context, force bool) string {
+	if !force && ctx.AuthToken != "" {
+		return ctx.AuthToken
+	}
+	authParams := &models.UserAuthParams{UserName: ctx.Username, Password: ctx.Password}
+	payload, err := json.Marshal(authParams)
+	if err != nil {
+		log.Fatal(err)
+	}
+	res, err := http.Post(ctx.Endpoint+"/api/users/adm/authenticate", "application/json", bytes.NewReader(payload))
+	if err != nil {
+		log.Fatal(err)
+	}
+	resBodyBytes, err := io.ReadAll(res.Body)
+	if err != nil {
+		log.Fatalf("Client could not read response body: %s", err)
+	}
+	if res.StatusCode != http.StatusOK {
+		log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
+	}
+	body := new(models.SuccessResponse)
+	if err := json.Unmarshal(resBodyBytes, body); err != nil {
+		log.Fatalf("Error unmarshalling JSON: %s", err)
+	}
+	authToken := body.Response.(map[string]any)["AuthToken"].(string)
+	config.SetAuthToken(authToken)
+	return authToken
+}
+
+func request[T any](method, route string, payload any) *T {
+	var (
+		_, ctx = config.GetCurrentContext()
+		req    *http.Request
+		err    error
+	)
+	if payload == nil {
+		req, err = http.NewRequest(method, ctx.Endpoint+route, nil)
+		if err != nil {
+			log.Fatalf("Client could not create request: %s", err)
+		}
+	} else {
+		payloadBytes, jsonErr := json.Marshal(payload)
+		if jsonErr != nil {
+			log.Fatalf("Error in request JSON marshalling: %s", err)
+		}
+		req, err = http.NewRequest(method, ctx.Endpoint+route, bytes.NewReader(payloadBytes))
+		if err != nil {
+			log.Fatalf("Client could not create request: %s", err)
+		}
+		req.Header.Set("Content-Type", "application/json")
+	}
+	if ctx.MasterKey != "" {
+		req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
+	} else {
+		req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, false))
+	}
+	retried := false
+retry:
+	res, err := http.DefaultClient.Do(req)
+	if err != nil {
+		log.Fatalf("Client error making http request: %s", err)
+	}
+	// refresh JWT token
+	if res.StatusCode == http.StatusUnauthorized && !retried && ctx.MasterKey == "" {
+		req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
+		retried = true
+		goto retry
+	}
+	resBodyBytes, err := io.ReadAll(res.Body)
+	if err != nil {
+		log.Fatalf("Client could not read response body: %s", err)
+	}
+	if res.StatusCode != http.StatusOK {
+		log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
+	}
+	body := new(T)
+	if len(resBodyBytes) > 0 {
+		if err := json.Unmarshal(resBodyBytes, body); err != nil {
+			log.Fatalf("Error unmarshalling JSON: %s", err)
+		}
+	}
+	return body
+}
+
+func get(route string) string {
+	_, ctx := config.GetCurrentContext()
+	req, err := http.NewRequest(http.MethodGet, ctx.Endpoint+route, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if ctx.MasterKey != "" {
+		req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
+	} else {
+		req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
+	}
+	res, err := http.DefaultClient.Do(req)
+	if err != nil {
+		log.Fatal(err)
+	}
+	bodyBytes, err := io.ReadAll(res.Body)
+	if err != nil {
+		log.Fatal(err)
+	}
+	return string(bodyBytes)
+}

+ 23 - 0
cli/functions/keys.go

@@ -0,0 +1,23 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetKeys - fetch all access keys of a network
+func GetKeys(networkName string) *[]models.AccessKey {
+	return request[[]models.AccessKey](http.MethodGet, fmt.Sprintf("/api/networks/%s/keys", networkName), nil)
+}
+
+// CreateKey - create an access key
+func CreateKey(networkName string, key *models.AccessKey) *models.AccessKey {
+	return request[models.AccessKey](http.MethodPost, fmt.Sprintf("/api/networks/%s/keys", networkName), key)
+}
+
+// DeleteKey - delete an access key
+func DeleteKey(networkName, keyName string) {
+	request[string](http.MethodDelete, fmt.Sprintf("/api/networks/%s/keys/%s", networkName, keyName), nil)
+}

+ 28 - 0
cli/functions/metrics.go

@@ -0,0 +1,28 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetNodeMetrics - fetch a single node's metrics
+func GetNodeMetrics(networkName, nodeID string) *models.Metrics {
+	return request[models.Metrics](http.MethodGet, fmt.Sprintf("/api/metrics/%s/%s", networkName, nodeID), nil)
+}
+
+// GetNetworkNodeMetrics - fetch an entire network's metrics
+func GetNetworkNodeMetrics(networkName string) *models.NetworkMetrics {
+	return request[models.NetworkMetrics](http.MethodGet, "/api/metrics/"+networkName, nil)
+}
+
+// GetAllMetrics - fetch all metrics
+func GetAllMetrics() *models.NetworkMetrics {
+	return request[models.NetworkMetrics](http.MethodGet, "/api/metrics", nil)
+}
+
+// GetNetworkExtMetrics - fetch external client metrics belonging to a network
+func GetNetworkExtMetrics(networkName string) *map[string]models.Metric {
+	return request[map[string]models.Metric](http.MethodGet, "/api/metrics-ext/"+networkName, nil)
+}

+ 45 - 0
cli/functions/network.go

@@ -0,0 +1,45 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// CreateNetwork - creates a network
+func CreateNetwork(payload *models.Network) *models.Network {
+	return request[models.Network](http.MethodPost, "/api/networks", payload)
+}
+
+// UpdateNetwork - updates a network
+func UpdateNetwork(name string, payload *models.Network) *models.Network {
+	return request[models.Network](http.MethodPut, "/api/networks/"+name, payload)
+}
+
+// UpdateNetworkNodeLimit - updates a network
+func UpdateNetworkNodeLimit(name string, nodeLimit int32) *models.Network {
+	return request[models.Network](http.MethodPut, fmt.Sprintf("/api/networks/%s/nodelimit", name), &models.Network{
+		NodeLimit: nodeLimit,
+	})
+}
+
+// GetNetworks - fetch all networks
+func GetNetworks() *[]models.Network {
+	return request[[]models.Network](http.MethodGet, "/api/networks", nil)
+}
+
+// GetNetwork - fetch a single network
+func GetNetwork(name string) *models.Network {
+	return request[models.Network](http.MethodGet, "/api/networks/"+name, nil)
+}
+
+// DeleteNetwork - delete a network
+func DeleteNetwork(name string) *string {
+	return request[string](http.MethodDelete, "/api/networks/"+name, nil)
+}
+
+// RefreshKeys - refresh public and private key pairs for a network
+func RefreshKeys(networkName string) *models.Network {
+	return request[models.Network](http.MethodPost, fmt.Sprintf("/api/networks/%s/keyupdate", networkName), nil)
+}

+ 44 - 0
cli/functions/network_user.go

@@ -0,0 +1,44 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/ee/ee_controllers"
+	"github.com/gravitl/netmaker/models/promodels"
+)
+
+// GetAllNetworkUsers - fetch all network users
+func GetAllNetworkUsers() *map[string][]promodels.NetworkUser {
+	return request[map[string][]promodels.NetworkUser](http.MethodGet, "/api/networkusers", nil)
+}
+
+// GetNetworkUsers - fetch network users belonging to a particular network
+func GetNetworkUsers(networkName string) *promodels.NetworkUserMap {
+	return request[promodels.NetworkUserMap](http.MethodGet, "/api/networkusers/"+networkName, nil)
+}
+
+// GetNetworkUser - fetch a single network user
+func GetNetworkUser(networkName, networkUserName string) *promodels.NetworkUser {
+	return request[promodels.NetworkUser](http.MethodGet, fmt.Sprintf("/api/networkusers/%s/%s", networkName, networkUserName), nil)
+}
+
+// CreateNetworkUser - create a network user
+func CreateNetworkUser(networkName string, payload *promodels.NetworkUser) {
+	request[any](http.MethodPost, "/api/networkusers/"+networkName, payload)
+}
+
+// UpdateNetworkUser - update a network user
+func UpdateNetworkUser(networkName string, payload *promodels.NetworkUser) {
+	request[any](http.MethodPut, "/api/networkusers/"+networkName, payload)
+}
+
+// GetNetworkUserData - fetch a network user's complete data
+func GetNetworkUserData(networkUserName string) *ee_controllers.NetworkUserDataMap {
+	return request[ee_controllers.NetworkUserDataMap](http.MethodGet, fmt.Sprintf("/api/networkusers/data/%s/me", networkUserName), nil)
+}
+
+// DeleteNetworkUser - delete a network user
+func DeleteNetworkUser(networkName, networkUserName string) {
+	request[any](http.MethodDelete, fmt.Sprintf("/api/networkusers/%s/%s", networkName, networkUserName), nil)
+}

+ 73 - 0
cli/functions/node.go

@@ -0,0 +1,73 @@
+package functions
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetNodes - fetch all nodes
+func GetNodes(networkName ...string) *[]models.Node {
+	if len(networkName) == 1 {
+		return request[[]models.Node](http.MethodGet, "/api/nodes/"+networkName[0], nil)
+	} else {
+		return request[[]models.Node](http.MethodGet, "/api/nodes", nil)
+	}
+}
+
+// GetNodeByID - fetch a single node by ID
+func GetNodeByID(networkName, nodeID string) *models.NodeGet {
+	return request[models.NodeGet](http.MethodGet, fmt.Sprintf("/api/nodes/%s/%s", networkName, nodeID), nil)
+}
+
+// UpdateNode - update a single node
+func UpdateNode(networkName, nodeID string, node *models.Node) *models.Node {
+	return request[models.Node](http.MethodPut, fmt.Sprintf("/api/nodes/%s/%s", networkName, nodeID), node)
+}
+
+// DeleteNode - delete a node
+func DeleteNode(networkName, nodeID string) *models.SuccessResponse {
+	return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s", networkName, nodeID), nil)
+}
+
+// CreateRelay - turn a node into a relay
+func CreateRelay(networkName, nodeID string, relayAddresses []string) *models.Node {
+	return request[models.Node](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createrelay", networkName, nodeID), &models.RelayRequest{
+		NetID:      networkName,
+		NodeID:     nodeID,
+		RelayAddrs: relayAddresses,
+	})
+}
+
+// DeleteRelay - remove relay role from a node
+func DeleteRelay(networkName, nodeID string) *models.Node {
+	return request[models.Node](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleterelay", networkName, nodeID), nil)
+}
+
+// CreateEgress - turn a node into an egress
+func CreateEgress(networkName, nodeID string, payload *models.EgressGatewayRequest) *models.Node {
+	return request[models.Node](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/creategateway", networkName, nodeID), payload)
+}
+
+// DeleteEgress - remove egress role from a node
+func DeleteEgress(networkName, nodeID string) *models.Node {
+	return request[models.Node](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deletegateway", networkName, nodeID), nil)
+}
+
+// CreateIngress - turn a node into an ingress
+func CreateIngress(networkName, nodeID string, failover bool) *models.Node {
+	return request[models.Node](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createingress", networkName, nodeID), &struct {
+		Failover bool `json:"failover"`
+	}{Failover: failover})
+}
+
+// DeleteIngress - remove ingress role from a node
+func DeleteIngress(networkName, nodeID string) *models.Node {
+	return request[models.Node](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleteingress", networkName, nodeID), nil)
+}
+
+// UncordonNode - uncordon a node
+func UncordonNode(networkName, nodeID string) *string {
+	return request[string](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/approve", networkName, nodeID), nil)
+}

+ 16 - 0
cli/functions/pretty_print.go

@@ -0,0 +1,16 @@
+package functions
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+)
+
+// PrettyPrint - print JSON with indentation
+func PrettyPrint(data any) {
+	body, err := json.MarshalIndent(data, "", "  ")
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println(string(body))
+}

+ 28 - 0
cli/functions/server.go

@@ -0,0 +1,28 @@
+package functions
+
+import (
+	"net/http"
+
+	cfg "github.com/gravitl/netmaker/config"
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetLogs - fetch Netmaker server logs
+func GetLogs() string {
+	return get("/api/logs")
+}
+
+// GetServerInfo - fetch minimal server info
+func GetServerInfo() *models.ServerConfig {
+	return request[models.ServerConfig](http.MethodGet, "/api/server/getserverinfo", nil)
+}
+
+// GetServerConfig - fetch entire server config including secrets
+func GetServerConfig() *cfg.ServerConfig {
+	return request[cfg.ServerConfig](http.MethodGet, "/api/server/getconfig", nil)
+}
+
+// GetServerHealth - fetch server current health status
+func GetServerHealth() string {
+	return get("/api/server/health")
+}

+ 37 - 0
cli/functions/user.go

@@ -0,0 +1,37 @@
+package functions
+
+import (
+	"net/http"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// HasAdmin - check if server has an admin user
+func HasAdmin() *bool {
+	return request[bool](http.MethodGet, "/api/users/adm/hasadmin", nil)
+}
+
+// CreateUser - create a user
+func CreateUser(payload *models.User) *models.User {
+	return request[models.User](http.MethodPost, "/api/users/"+payload.UserName, payload)
+}
+
+// UpdateUser - update a user
+func UpdateUser(payload *models.User) *models.User {
+	return request[models.User](http.MethodPut, "/api/users/networks/"+payload.UserName, payload)
+}
+
+// DeleteUser - delete a user
+func DeleteUser(username string) *string {
+	return request[string](http.MethodDelete, "/api/users/"+username, nil)
+}
+
+// GetUser - fetch a single user
+func GetUser(username string) *models.User {
+	return request[models.User](http.MethodGet, "/api/users/"+username, nil)
+}
+
+// ListUsers - fetch all users
+func ListUsers() *[]models.ReturnUser {
+	return request[[]models.ReturnUser](http.MethodGet, "/api/users", nil)
+}

Some files were not shown because too many files changed in this diff