Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format improvements #2

Merged
merged 10 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading