diff --git a/cli/cmd/host/update.go b/cli/cmd/host/update.go index ef830191..d01ea104 100644 --- a/cli/cmd/host/update.go +++ b/cli/cmd/host/update.go @@ -40,16 +40,34 @@ var hostUpdateCmd = &cobra.Command{ log.Fatal(err) } } else { - apiHost.ID = args[0] - apiHost.EndpointIP = endpoint - apiHost.EndpointIPv6 = endpoint6 - apiHost.Name = name - apiHost.ListenPort = listenPort - apiHost.MTU = mtu - apiHost.IsStaticPort = isStaticPort - apiHost.IsStatic = isStatic - apiHost.IsDefault = isDefault - apiHost.PersistentKeepalive = keepAlive + apiHost = functions.GetHost(args[0]) + if cmd.Flags().Changed("endpoint") { + apiHost.EndpointIP = endpoint + } + if cmd.Flags().Changed("endpoint6") { + apiHost.EndpointIPv6 = endpoint6 + } + if cmd.Flags().Changed("name") { + apiHost.Name = name + } + if cmd.Flags().Changed("listen_port") { + apiHost.ListenPort = listenPort + } + if cmd.Flags().Changed("mtu") { + apiHost.MTU = mtu + } + if cmd.Flags().Changed("static_port") { + apiHost.IsStaticPort = isStaticPort + } + if cmd.Flags().Changed("static_endpoint") { + apiHost.IsStatic = isStatic + } + if cmd.Flags().Changed("default") { + apiHost.IsDefault = isDefault + } + if cmd.Flags().Changed("keep_alive") { + apiHost.PersistentKeepalive = keepAlive + } } functions.PrettyPrint(functions.UpdateHost(args[0], apiHost)) }, diff --git a/cli/functions/host.go b/cli/functions/host.go index 04346829..736d4e26 100644 --- a/cli/functions/host.go +++ b/cli/functions/host.go @@ -1,7 +1,9 @@ package functions import ( + "encoding/json" "fmt" + "log" "net/http" "github.com/gravitl/netmaker/models" @@ -21,6 +23,31 @@ func DeleteHost(hostID string, force bool) *models.ApiHost { return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s?force=%t", hostID, force), nil) } +// GetHost - fetches a host +func GetHost(hostID string) *models.ApiHost { + resp := request[models.SuccessResponse](http.MethodGet, fmt.Sprintf("/api/hosts/%s", hostID), nil) + if resp.Code != http.StatusOK { + log.Fatalf("Error Status: %d Response: %s", resp.Code, resp.Message) + } + + if resp.Response == nil { + log.Fatalf("Empty response") + } + + bytes, err := json.Marshal(resp.Response) + if err != nil { + log.Fatalf("Error reading Response: %s", err) + } + + var host models.ApiHost + err = json.Unmarshal(bytes, &host) + if err != nil { + log.Fatalf("Error unmarshalling JSON: %s", err) + } + + return &host +} + // UpdateHost - update a host func UpdateHost(hostID string, body *models.ApiHost) *models.ApiHost { return request[models.ApiHost](http.MethodPut, "/api/hosts/"+hostID, body) diff --git a/controllers/hosts.go b/controllers/hosts.go index 154faf9a..b00877f7 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -22,6 +22,7 @@ import ( "github.com/gravitl/netmaker/servercfg" "golang.org/x/crypto/bcrypt" "golang.org/x/exp/slog" + "gorm.io/gorm" ) func hostHandlers(r *mux.Router) { @@ -41,6 +42,8 @@ func hostHandlers(r *mux.Router) { Methods(http.MethodPost) r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))). Methods(http.MethodPut) + r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(getHost))). + Methods(http.MethodGet) // used by netclient r.HandleFunc("/api/hosts/{hostid}", AuthorizeHost(http.HandlerFunc(deleteHost))). Methods(http.MethodDelete) @@ -671,6 +674,41 @@ func deleteHost(w http.ResponseWriter, r *http.Request) { logic.ReturnSuccessResponseWithJson(w, r, apiHostData, "deleted host "+currHost.Name) } +// @Summary Fetches a Netclient host from Netmaker server +// @Router /api/hosts/{hostid} [get] +// @Tags Hosts +// @Security oauth +// @Produce json +// @Param hostid path string true "Host ID" +// @Success 200 {object} models.ApiHost +// @Failure 500 {object} models.ErrorResponse +func getHost(w http.ResponseWriter, r *http.Request) { + hostIDStr := mux.Vars(r)["hostid"] + hostID, err := uuid.Parse(hostIDStr) + if err != nil { + err = fmt.Errorf("failed to parse host id: %w", err) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq)) + return + } + host := &schema.Host{ + ID: hostID, + } + err = host.Get(r.Context()) + if err != nil { + logger.Log(0, r.Header.Get("user"), "failed to fetch a host:", err.Error()) + + apiErr := logic.Internal + if errors.Is(err, gorm.ErrRecordNotFound) { + apiErr = logic.NotFound + } + + logic.ReturnErrorResponse(w, r, logic.FormatError(err, apiErr)) + return + } + apiHostData := models.NewApiHostFromSchemaHost(host) + logic.ReturnSuccessResponseWithJson(w, r, apiHostData, "fetched host "+host.Name) +} + // @Summary Bulk delete hosts // @Router /api/v1/hosts/bulk [delete] // @Tags Hosts diff --git a/scripts/nm-quick.sh b/scripts/nm-quick.sh index d6c19c93..48782af1 100755 --- a/scripts/nm-quick.sh +++ b/scripts/nm-quick.sh @@ -183,7 +183,23 @@ configure_netclient() { echo "Host ID: $HOST_ID" # set as a default host set +e - nmctl host update $HOST_ID --default + GET_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET "https://api.${NETMAKER_BASE_DOMAIN}/api/hosts/${HOST_ID}" \ + -H "Authorization: Bearer ${MASTER_KEY}" \ + -H "Content-Type: application/json") + GET_HTTP_CODE=$(echo "$GET_RESPONSE" | tail -n1) + HOST_JSON=$(echo "$GET_RESPONSE" | head -n -1) + if [ "$GET_HTTP_CODE" != "200" ]; then + echo "Warning: failed to fetch host (HTTP $GET_HTTP_CODE), skipping set default" + else + UPDATED_HOST_JSON=$(echo "$HOST_JSON" | jq '.Response | .isdefault = true') + PUT_HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X PUT "https://api.${NETMAKER_BASE_DOMAIN}/api/hosts/${HOST_ID}" \ + -H "Authorization: Bearer ${MASTER_KEY}" \ + -H "Content-Type: application/json" \ + -d "$UPDATED_HOST_JSON") + if [ "$PUT_HTTP_CODE" != "200" ]; then + echo "Warning: failed to set host as default (HTTP $PUT_HTTP_CODE), skipping" + fi + fi sleep 5 # nmctl node create_remote_access_gateway netmaker $NODE_ID # sleep 2