Skip to content

Commit

Permalink
improve info commands
Browse files Browse the repository at this point in the history
  • Loading branch information
cbodonnell committed Apr 8, 2023
1 parent cf74892 commit 6084839
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 34 deletions.
27 changes: 13 additions & 14 deletions cmd/tfarm/commands/info.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package commands

import (
"encoding/json"
"fmt"

"github.com/cbodonnell/tfarm/pkg/term"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -31,23 +31,22 @@ func Info(outputFormat string) error {
return fmt.Errorf("error creating client: %s", err)
}

info, err := client.Info()
if err != nil {
return fmt.Errorf("error getting info: %s", err)
}
info := client.Info()

switch outputFormat {
// TODO: make this yaml so it can be more dynamic
case "text":
fmt.Println("Client Version:")
fmt.Println(" ", info.ClientVersion)
fmt.Println("Server Version:")
fmt.Println(" ", info.ServerVersion)
fmt.Println("Server Endpoint:")
fmt.Println(" ", info.ServerEndpoint)
fmt.Println("Configuration:")
fmt.Println(" ", info.ConfigDir)
fmt.Println("Client:")
fmt.Println(" Version:", info.Client.Version)
fmt.Println(" Config:", info.Client.Config)
fmt.Println("Server:")
fmt.Println(" Version:", info.Server.Version)
fmt.Println(" Endpoint", info.Server.Endpoint)
if info.Server.Error != "" {
fmt.Println(" Error:", info.Server.Error)
}
case "json":
b, err := json.Marshal(info)
b, err := term.PrettyJSON(info)
if err != nil {
return fmt.Errorf("error marshaling info to json: %s", err)
}
Expand Down
38 changes: 30 additions & 8 deletions cmd/tfarm/commands/ranch/info.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,58 @@
package ranch

import (
"encoding/json"
"fmt"
"net/http"

"github.com/cbodonnell/tfarm/pkg/info"
"github.com/cbodonnell/tfarm/pkg/ranch/api"
"github.com/cbodonnell/tfarm/pkg/term"
"github.com/spf13/cobra"
)

func InfoCmd(tokenDir, endpoint string) *cobra.Command {
var outputFormat string

infoCmd := &cobra.Command{
Use: "info",
Short: "Print ranch info",
SilenceUsage: true,
SilenceErrors: false,
RunE: func(cmd *cobra.Command, args []string) error {
return Info(tokenDir, endpoint)
return Info(tokenDir, endpoint, outputFormat)
},
}

infoCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "Output format (text, json)")

return infoCmd
}

func Info(tokenDir, endpoint string) error {
func Info(tokenDir, endpoint, outputFormat string) error {
info := getInfo(tokenDir, endpoint)
b, err := json.Marshal(info)
if err != nil {
return fmt.Errorf("error marshaling clients: %s", err)
}

fmt.Print(string(b))
switch outputFormat {
// TODO: make this yaml so it can be more dynamic
case "text":
fmt.Println("Ready:", info.Ready)
if info.Error != "" {
fmt.Println("Error:", info.Error)
}
fmt.Println("Version:", info.Version)
fmt.Println("Config:", info.TokenDir)
fmt.Println("Endpoint", info.Endpoint)
fmt.Println("OIDC:")
fmt.Println(" Issuer:", info.OIDC.Issuer)
fmt.Println(" Client ID:", info.OIDC.ClientID)
case "json":
b, err := term.PrettyJSON(info)
if err != nil {
return fmt.Errorf("error marshaling info to json: %s", err)
}
fmt.Println(string(b))
default:
return fmt.Errorf("invalid output format: %s", outputFormat)
}

return nil
}
Expand All @@ -45,6 +66,7 @@ func getInfo(tokenDir, endpoint string) *info.Info {
apiClient := api.NewClient(http.DefaultClient, endpoint)
res, err := apiClient.GetInfo(&api.APIRequestParams{})
if err != nil {
info.Error = err.Error()
return &info
}

Expand Down
69 changes: 57 additions & 12 deletions pkg/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,25 @@ type APIClient struct {
configDir string
}

type APIInfo struct {
ClientVersion string `json:"client_version"`
ServerVersion string `json:"server_version"`
ServerEndpoint string `json:"server_endpoint"`
ConfigDir string `json:"config_dir"`
// TODO: move this to the info package and differentiate between tfarm and ranch info
type Info struct {
Client ClientInfo `json:"client"`
Server ServerInfo `json:"server"`
}

type ClientInfo struct {
Version string `json:"version"`
Config string `json:"config"`
}

type ServerInfo struct {
Version string `json:"version"`
Endpoint string `json:"endpoint"`
Error string `json:"error,omitempty"`
}

type ServerInfoResponse struct {
Version string `json:"version"`
}

type APIResponse struct {
Expand Down Expand Up @@ -83,13 +97,44 @@ func NewClient(endpoint string, configDir string) (*APIClient, error) {
}, nil
}

func (c *APIClient) Info() (*APIInfo, error) {
return &APIInfo{
ClientVersion: version.Version,
ServerVersion: "TODO",
ServerEndpoint: c.endpoint,
ConfigDir: c.configDir,
}, nil
func (c *APIClient) Info() *Info {
info := &Info{
Client: ClientInfo{
Version: version.Version,
Config: c.configDir,
},
Server: ServerInfo{
Endpoint: c.endpoint,
},
}

if serverInfo, err := c.getServerInfo(); err != nil {
info.Server.Error = fmt.Sprintf("error getting info: %s", err)
} else {
info.Server.Version = serverInfo.Version
}

return info
}

func (c *APIClient) getServerInfo() (*ServerInfoResponse, error) {
resp, err := c.httpClient.Get(c.endpoint + "/api/info")
if err != nil {
return nil, err
}
defer resp.Body.Close()

var response APIResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response with status code %d: %s", resp.StatusCode, err)
}

serverInfo := &ServerInfoResponse{}
if err := json.Unmarshal([]byte(response.Message), serverInfo); err != nil {
return nil, fmt.Errorf("failed to unmarshal response message: %s", err)
}

return serverInfo, nil
}

// TODO: Refactor this to use a generic Do method
Expand Down
1 change: 1 addition & 0 deletions pkg/handlers/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func NewMuxHandler(f *frpc.Frpc) http.Handler {

// pre-configure routes
preConfigure := r.NewRoute().Subrouter()
preConfigure.HandleFunc("/api/info", HandleInfo()).Methods("GET")
preConfigure.HandleFunc("/api/configure", HandleConfigure(f)).Methods("PUT")

// post-configure routes
Expand Down
25 changes: 25 additions & 0 deletions pkg/handlers/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package handlers

import (
"encoding/json"
"log"
"net/http"

"github.com/cbodonnell/tfarm/pkg/api"
"github.com/cbodonnell/tfarm/pkg/version"
)

func HandleInfo() func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
info := &api.ServerInfoResponse{
Version: version.Version,
}
output, err := json.Marshal(info)
if err != nil {
log.Printf("failed to marshal info: %s", err)
api.RespondWithError(w, http.StatusInternalServerError, "failed to marshal info")
return
}
api.RespondWithSuccess(w, string(output))
}
}
1 change: 1 addition & 0 deletions pkg/info/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package info

type Info struct {
Ready bool `json:"ready"`
Error string `json:"error,omitempty"`
Version string `json:"version"`
TokenDir string `json:"token_dir"`
Endpoint string `json:"endpoint"`
Expand Down
14 changes: 14 additions & 0 deletions pkg/term/term.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package term

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"os"
"strings"
Expand Down Expand Up @@ -35,3 +37,15 @@ func PasswordPrompt(label string) string {
fmt.Println()
return s
}

func PrettyJSON(v any) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("error marshaling info to json: %s", err)
}
var out bytes.Buffer
if err := json.Indent(&out, b, "", " "); err != nil {
return nil, fmt.Errorf("error indenting json: %s", err)
}
return out.Bytes(), nil
}

0 comments on commit 6084839

Please sign in to comment.