Skip to content

Commit

Permalink
Implement client config (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
nt0xa authored Feb 17, 2023
1 parent 72643a6 commit bc9f9f3
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 47 deletions.
93 changes: 93 additions & 0 deletions cmd/client/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package main

import (
"context"
"net/url"
"regexp"

validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
)

func init() {
validation.ErrorTag = "mapstructure"
}

type Config struct {
Context Context `mapstructure:"context"`
Servers map[string]Server `mapstructure:"servers"`
}

func (c Config) ValidateWithContext(ctx context.Context) error {

servers := make([]interface{}, 0)

for s := range c.Servers {
servers = append(servers, s)
}

ctx = context.WithValue(ctx, "servers", servers)

return validation.ValidateStructWithContext(ctx, &c,
validation.Field(&c.Context),
validation.Field(&c.Servers, validation.Length(1, 0)),
)
}

type Context struct {
Server string `mapstructure:"server"`
}

func (c Context) ValidateWithContext(ctx context.Context) error {
servers, ok := ctx.Value("servers").([]interface{})
if !ok {
panic(`fail to find "servers" key in context`)
}

return validation.ValidateStructWithContext(ctx, &c,
validation.Field(&c.Server, validation.Required, validation.In(servers...)),
)
}

type Server struct {
Token string `mapstructure:"token"`
URL string `mapstructure:"url"`
Proxy *string `mapstructure:"proxy"`
Insecure bool `mapstructure:"insecure"`
}

func (c Server) ValidateWithContext(ctx context.Context) error {
return validation.ValidateStructWithContext(ctx, &c,
validation.Field(&c.Token,
validation.Required,
validation.Match(regexp.MustCompile("[a-f0-9]{32}")),
),
validation.Field(&c.URL, validation.Required, is.URL),
validation.Field(&c.Proxy, is.URL),
)
}

func (c *Server) BaseURL() *url.URL {
u, err := url.Parse(c.URL)
if err != nil {
// Already passed validation, must be valid URL.
panic(err)
}

return u
}


func (c *Server) ProxyURL() *url.URL {
if c.Proxy == nil {
return nil
}

u, err := url.Parse(*c.Proxy)
if err != nil {
// Already passed validation, must be valid URL.
panic(err)
}

return u
}
78 changes: 56 additions & 22 deletions cmd/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ package main

import (
"context"
"net/url"
"os"

"github.com/adrg/xdg"
"github.com/gookit/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/russtone/sonar/internal/actions"
"github.com/russtone/sonar/internal/cmd"
"github.com/russtone/sonar/internal/modules/api/apiclient"
"github.com/russtone/sonar/internal/utils/slice"
"github.com/spf13/cobra"
)

func main() {
Expand All @@ -29,41 +31,39 @@ func main() {
}

//
// URL & token
// Config
//

var (
baseURL, token string
insecure bool
proxy *string
)
var cfg Config

if baseURL = os.Getenv("SONAR_URL"); baseURL == "" {
fatal("Empty SONAR_URL")
configFilePath, err := xdg.ConfigFile("sonar/config.toml")
if err != nil {
fatalf("Fail to obtain config file path: %v", err)
}
viper.SetConfigFile(configFilePath)

u, err := url.Parse(baseURL)
if err != nil {
fatal(err)
if err := viper.ReadInConfig(); err != nil {
fatalf("Fail to read config: %v", err)
}

if token = os.Getenv("SONAR_TOKEN"); token == "" {
fatal("Empty SONAR_TOKEN")
if err := viper.Unmarshal(&cfg); err != nil {
fatalf("Fail to unmarshal config: %v", err)
}

if os.Getenv("SONAR_INSECURE") != "" {
insecure = true
if err := cfg.ValidateWithContext(context.Background()); err != nil {
fatalf("Config validation failed: %v", err)
}

if p := os.Getenv("SONAR_PROXY"); p != "" {
proxy = &p
srv, ok := cfg.Servers[cfg.Context.Server]
if !ok {
fatalf("Invalid server %q", cfg.Context.Server)
}

//
// API client
//

client := apiclient.New(baseURL, token, insecure, proxy)
client := apiclient.New(srv.URL, srv.Token, srv.Insecure, srv.Proxy)

//
// User
Expand All @@ -84,15 +84,15 @@ func main() {
if slice.StringsContains(os.Args, "--json") {
handler = &jsonHandler{os.Stdout}
} else {
handler = &terminalHandler{u.Hostname()}
handler = &terminalHandler{srv.BaseURL().Hostname()}
}

root := cmd.New(client, handler, nil).Root(user, true)
addJSONFlag(root)
addContextCmd(&cfg, root)
root.SilenceErrors = true
root.SilenceUsage = true


if err := root.Execute(); err != nil {
fatal(err)
}
Expand All @@ -112,10 +112,44 @@ func addJSONFlag(root *cobra.Command) {

cmd.Flags().BoolVar(&jsonOutput, "json", false, "JSON output")
}
}

func addContextCmd(cfg *Config, root *cobra.Command) {
var server string

cmd := &cobra.Command{
Use: "ctx",
Short: "Change current context parameters",
RunE: func(cmd *cobra.Command, args []string) error {

if err := viper.Unmarshal(&cfg); err != nil {
fatalf("Fail to unmarshal config: %v", err)
}

if err := cfg.ValidateWithContext(context.Background()); err != nil {
fatalf("Config validation failed: %v", err)
}

if err := viper.WriteConfig(); err != nil {
fatalf("Fail to update config: %v", err)
}

return nil
},
}

cmd.Flags().StringVarP(&server, "server", "s", "", "Server name from list of servers")
viper.BindPFlag("context.server", cmd.Flags().Lookup("server"))

root.AddCommand(cmd)
}

func fatal(data interface{}) {
color.Danger.Println(data)
os.Exit(1)
}

func fatalf(format string, a ...interface{}) {
color.Danger.Printf(format+"\n", a...)
os.Exit(1)
}
26 changes: 18 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.19

require (
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/adrg/xdg v0.4.0
github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b
github.com/fatih/structs v1.1.0
github.com/gavv/httpexpect v2.0.0+incompatible
Expand All @@ -25,6 +26,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1
)

Expand All @@ -37,25 +39,33 @@ require (
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/imkira/go-interpol v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-oci8 v0.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moul/http2curl v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.44.0 // indirect
Expand All @@ -67,14 +77,14 @@ require (
github.com/yudai/gojsondiff v1.0.0 // indirect
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
github.com/yudai/pp v2.0.1+incompatible // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/mod v0.5.0 // indirect
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/tools v0.1.12 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
Loading

0 comments on commit bc9f9f3

Please sign in to comment.