Skip to content

Commit

Permalink
Merge pull request #2 from serverscom/format-improvements
Browse files Browse the repository at this point in the history
Format improvements
  • Loading branch information
olegy89 authored Jan 29, 2025
2 parents fbee3ab + 6346c11 commit e6f4705
Show file tree
Hide file tree
Showing 32 changed files with 731 additions and 172 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
# srvctl
# srvctl

## Description

`srvctl` is a command line app to manage servers.com resources.

## Installation

Homebrew:

```bash
brew tap serverscom/serverscom
brew install srvctl
```
84 changes: 83 additions & 1 deletion cmd/base/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,30 @@ package base

import (
"fmt"
"html/template"
"os"
"strings"

"github.com/serverscom/srvctl/internal/client"
"github.com/serverscom/srvctl/internal/config"
"github.com/serverscom/srvctl/internal/output"
"github.com/serverscom/srvctl/internal/output/entities"
"github.com/spf13/cobra"
)

// InitCmdContext creates cmd
// CombinePreRunE combines multiple pre-run functions into one
func CombinePreRunE(funcs ...func(cmd *cobra.Command, args []string) error) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
for _, fn := range funcs {
if err := fn(cmd, args); err != nil {
return err
}
}
return nil
}
}

// InitCmdContext inits cmd context and sets up necessary dependencies
func InitCmdContext(cmdContext *CmdContext) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
configPath, err := cmd.Flags().GetString("config")
Expand All @@ -30,7 +47,72 @@ func InitCmdContext(cmdContext *CmdContext) func(cmd *cobra.Command, args []stri

cmdContext.manager = m
cmdContext.client = c
cmdContext.formatter = output.NewFormatter(cmd, m)

return nil
}
}

// CheckFormatterFlags checks flags related to formatter
func CheckFormatterFlags(cmdContext *CmdContext, entity entities.EntityInterface) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
if entity == nil {
return fmt.Errorf("entity is not initialized")
}
manager := cmdContext.GetManager()
formatter := cmdContext.GetOrCreateFormatter(cmd)

output := formatter.GetOutput()
if output == "json" || output == "yaml" {
return nil
}

tmpl := formatter.GetTemplateStr()
if tmpl != "" {
tmpl = strings.Trim(tmpl, " ")
r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
tmpl = r.Replace(tmpl)

t, err := template.New("").Parse(tmpl)
if err != nil {
return err
}
formatter.SetTemplate(t)
return nil
}

fieldList, err := manager.GetResolvedBoolValue(cmd, "field-list")
if err != nil {
return err
}

if fieldList {
formatter.ListEntityFields(entity.GetFields())
os.Exit(0)
}

fields, err := manager.GetResolvedStringSliceValue(cmd, "field")
if err != nil {
return err
}
if len(fields) > 0 {
if err := entity.Validate(fields); err != nil {
return err
}
}

return nil
}
}

// CheckEmptyContexts returns error if no contexts found
func CheckEmptyContexts(cmdContext *CmdContext) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
manager := cmdContext.GetManager()

if len(manager.GetContexts()) == 0 {
return fmt.Errorf("no contexts found")
}
return nil
}
}
6 changes: 2 additions & 4 deletions cmd/base/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"strings"

serverscom "github.com/serverscom/serverscom-go-client/pkg"
"github.com/serverscom/srvctl/internal/output"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -66,9 +65,8 @@ func NewListCmd[T any](entityName string, colFactory CollectionFactory[T], cmdCo
return err
}

outputFormat, _ := manager.GetResolvedStringValue(cmd, "output")
formatter := output.NewFormatter(cmd.OutOrStdout())
return formatter.FormatList(items, outputFormat)
formatter := cmdContext.GetOrCreateFormatter(cmd)
return formatter.Format(items)
},
}

Expand Down
49 changes: 26 additions & 23 deletions cmd/base/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,55 @@ import (

"github.com/serverscom/srvctl/internal/client"
"github.com/serverscom/srvctl/internal/config"
"github.com/serverscom/srvctl/internal/output"
"github.com/spf13/cobra"
)

// CmdContext represents the context for a command
type CmdContext struct {
manager *config.Manager
client *client.Client
manager *config.Manager
client *client.Client
formatter *output.Formatter
}

// NewCmdContext creates new cmd context with specified manager and client
func NewCmdContext(manager *config.Manager, client *client.Client) *CmdContext {
return &CmdContext{
manager: manager,
client: client,
}
}

// SetManagerConfig sets manager config
func (c *CmdContext) SetManagerConfig(config *config.Config) {
c.manager.SetConfig(config)
}

// GetManager returns the manager from cmd context
func (c *CmdContext) GetManager() *config.Manager {
if c.manager != nil {
return c.manager
}
return &config.Manager{}
}

// GetOrCreateFormatter returns formatter for the command
func (c *CmdContext) GetOrCreateFormatter(cmd *cobra.Command) *output.Formatter {
if c.formatter != nil {
return c.formatter
}
c.formatter = output.NewFormatter(cmd, c.manager)
return c.formatter
}

// GetClient returns the client from cmd context
func (c *CmdContext) GetClient() *client.Client {
if c.client != nil {
return c.client
}
return &client.Client{}
}

// NewCmdContext creates new cmd context with specified manager and client
func NewCmdContext(manager *config.Manager, client *client.Client) *CmdContext {
return &CmdContext{
manager: manager,
client: client,
}
}

// SetupContext returns context with timeout based on 'http-timeout' from config or cli flag
func SetupContext(cmd *cobra.Command, manager *config.Manager) (context.Context, context.CancelFunc) {
httpTimeout, err := manager.GetResolvedIntValue(cmd, "http-timeout")
Expand All @@ -73,7 +88,7 @@ func SetupProxy(cmd *cobra.Command, manager *config.Manager) {

// ReadInputJSON reads input from file and unmarshals it into the given struct.
// If path is "-", it reads from stdin.
func ReadInputJSON(path string, in io.Reader, input interface{}) error {
func ReadInputJSON(path string, in io.Reader, input any) error {
var inputReader io.Reader

if path != "" && path != "-" {
Expand All @@ -95,18 +110,6 @@ func ReadInputJSON(path string, in io.Reader, input interface{}) error {
return nil
}

// CheckEmptyContexts returns error if no contexts found
func CheckEmptyContexts(cmdContext *CmdContext) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
manager := cmdContext.GetManager()

if len(manager.GetContexts()) == 0 {
return fmt.Errorf("no contexts found")
}
return nil
}
}

// ParseLabels parses slice of labels and returns map
// expects that slice element would be: "foo=bar"
func ParseLabels(labels []string) (map[string]string, error) {
Expand Down
3 changes: 1 addition & 2 deletions cmd/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ func TestConfigFinalCmd(t *testing.T) {
},
}

testCmdContext := testutils.NewTestCmdContext(nil)

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)

testCmdContext := testutils.NewTestCmdContext(nil)
testCmdContext.SetManagerConfig(&tc.config)

configCmd := NewCmd(testCmdContext)
Expand Down
9 changes: 4 additions & 5 deletions cmd/config/final.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,16 @@ func newFinalCmd(cmdContext *base.CmdContext) *cobra.Command {
Config: finalConfig,
}

outputFormat, _ := manager.GetResolvedStringValue(cmd, "output")
formatter := output.NewFormatter(cmd.OutOrStdout())
return formatter.Format(cfgInfo, outputFormat)
formatter := cmdContext.GetOrCreateFormatter(cmd)
return formatter.Format(cfgInfo)
},
}

return cmd
}

func buildFinalConfig(cmd *cobra.Command, manager *config.Manager) map[string]interface{} {
finalConfig := make(map[string]interface{})
func buildFinalConfig(cmd *cobra.Command, manager *config.Manager) map[string]any {
finalConfig := make(map[string]any)

cmd.Flags().VisitAll(func(f *pflag.Flag) {
if !slices.Contains(KnownConfigFlags, f.Name) {
Expand Down
4 changes: 2 additions & 2 deletions cmd/config/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func newUpdateCmd(cmdContext *base.CmdContext) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
manager := cmdContext.GetManager()

configOptions := make(map[string]interface{})
configOptions := make(map[string]any)

cmd.Flags().Visit(func(f *pflag.Flag) {
fillConfigOptions(cmd, f, configOptions)
Expand Down Expand Up @@ -53,7 +53,7 @@ func newUpdateCmd(cmdContext *base.CmdContext) *cobra.Command {
}

// fillConfigOptions adds cmd flag to configOptions only if flag exists in KnownConfigFlags
func fillConfigOptions(cmd *cobra.Command, f *pflag.Flag, configOptions map[string]interface{}) {
func fillConfigOptions(cmd *cobra.Command, f *pflag.Flag, configOptions map[string]any) {
if strings.HasPrefix(f.Name, "no-") {
optionName := strings.TrimPrefix(f.Name, "no-")
configOptions[optionName] = nil
Expand Down
3 changes: 1 addition & 2 deletions cmd/context/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,11 @@ func TestContextListCmd(t *testing.T) {
},
}

testCmdContext := testutils.NewTestCmdContext(nil)

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)

testCmdContext := testutils.NewTestCmdContext(nil)
testCmdContext.SetManagerConfig(&tc.config)

contextCmd := NewCmd(testCmdContext)
Expand Down
2 changes: 1 addition & 1 deletion cmd/context/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func newListCmd(cmdContext *base.CmdContext) *cobra.Command {
contexts = output.FilterDefaultContexts(contexts, defaultContext, false)
}

formatter := output.NewFormatter(cmd.OutOrStdout())
formatter := cmdContext.GetOrCreateFormatter(cmd)
return formatter.FormatContexts(contexts, defaultContext)
},
}
Expand Down
6 changes: 2 additions & 4 deletions cmd/entities/ssh-keys/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

serverscom "github.com/serverscom/serverscom-go-client/pkg"
"github.com/serverscom/srvctl/cmd/base"
"github.com/serverscom/srvctl/internal/output"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -37,9 +36,8 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command {
}

if sshKey != nil {
outputFormat, _ := manager.GetResolvedStringValue(cmd, "output")
formatter := output.NewFormatter(cmd.OutOrStdout())
return formatter.Format([]serverscom.SSHKey{*sshKey}, outputFormat)
formatter := cmdContext.GetOrCreateFormatter(cmd)
return formatter.Format(sshKey)
}
return nil
},
Expand Down
9 changes: 3 additions & 6 deletions cmd/entities/ssh-keys/get.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package sshkeys

import (
serverscom "github.com/serverscom/serverscom-go-client/pkg"
"github.com/serverscom/srvctl/cmd/base"
"github.com/serverscom/srvctl/internal/output"
"github.com/spf13/cobra"
)

func NewGetCmd(cmdContext *base.CmdContext) *cobra.Command {
func newGetCmd(cmdContext *base.CmdContext) *cobra.Command {
cmd := &cobra.Command{
Use: "get <fingerprint>",
Short: "Get an ssh key",
Expand All @@ -30,9 +28,8 @@ func NewGetCmd(cmdContext *base.CmdContext) *cobra.Command {
}

if sshKey != nil {
outputFormat, _ := manager.GetResolvedStringValue(cmd, "output")
formatter := output.NewFormatter(cmd.OutOrStdout())
return formatter.Format([]serverscom.SSHKey{*sshKey}, outputFormat)
formatter := cmdContext.GetOrCreateFormatter(cmd)
return formatter.Format(sshKey)
}
return nil
},
Expand Down
Loading

0 comments on commit e6f4705

Please sign in to comment.