Skip to content

Commit f9b9f8d

Browse files
leogrpoiana
authored andcommitted
chore(cmd): basic config options with validation
Signed-off-by: Leonardo Grasso <[email protected]>
1 parent 4e5c20d commit f9b9f8d

File tree

5 files changed

+163
-39
lines changed

5 files changed

+163
-39
lines changed

cmd/config_options.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
Copyright © 2019 The Falco Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"fmt"
20+
21+
"github.com/creasty/defaults"
22+
"github.com/falcosecurity/falcoctl/validate"
23+
"github.com/go-playground/validator/v10"
24+
logger "github.com/kris-nova/logger"
25+
)
26+
27+
// ConfigOptions represent the persistent configuration flags of falcoctl.
28+
type ConfigOptions struct {
29+
ConfigFile string `name:"config"`
30+
Verbose int `validate:"number,min=0,max=4" default:"3" name:"verbose"`
31+
Fabulous bool `name:"fab"`
32+
}
33+
34+
// NewConfigOptions creates an instance of ConfigOptions.
35+
func NewConfigOptions() *ConfigOptions {
36+
o := &ConfigOptions{}
37+
if err := defaults.Set(o); err != nil {
38+
logger.Critical("error setting falcoctl options default")
39+
}
40+
return o
41+
}
42+
43+
// Validate validates the ConfigOptions fields.
44+
func (co *ConfigOptions) Validate() []error {
45+
if err := validate.V.Struct(co); err != nil {
46+
errors := err.(validator.ValidationErrors)
47+
errArr := []error{}
48+
for _, e := range errors {
49+
// Translate each error one at a time
50+
errArr = append(errArr, fmt.Errorf(e.Translate(validate.T)))
51+
}
52+
return errArr
53+
}
54+
return nil
55+
}

cmd/root.go

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,23 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20+
"os"
2021
"strings"
2122

2223
"k8s.io/cli-runtime/pkg/genericclioptions"
2324

24-
"github.com/falcosecurity/falcoctl/pkg/kubernetes/factory"
2525
"github.com/kris-nova/logger"
26+
"github.com/mitchellh/go-homedir"
2627
"github.com/spf13/cobra"
2728
"github.com/spf13/viper"
2829
)
2930

30-
// RootOptions represents the base command options
31-
type RootOptions struct {
32-
configFlags *genericclioptions.ConfigFlags
33-
34-
genericclioptions.IOStreams
35-
36-
fabulous bool
37-
}
38-
39-
// Validate validates the base command options
40-
func (o RootOptions) Validate(c *cobra.Command, args []string) error {
41-
return nil
42-
}
43-
44-
// NewRootOptions instantiates the base command options
45-
func NewRootOptions(streams genericclioptions.IOStreams) CommandOptions {
46-
return &RootOptions{
47-
configFlags: genericclioptions.NewConfigFlags(false),
48-
IOStreams: streams,
49-
}
50-
}
51-
52-
// New creates the command
31+
// New creates the faloctl root command
5332
func New(streams genericclioptions.IOStreams) *cobra.Command {
54-
o := NewRootOptions(streams).(*RootOptions)
33+
configOptions := NewConfigOptions()
34+
cobra.OnInitialize(func() {
35+
initConfig(configOptions)
36+
})
5537

5638
cmd := &cobra.Command{
5739
Use: "falcoctl",
@@ -62,34 +44,65 @@ func New(streams genericclioptions.IOStreams) *cobra.Command {
6244
// Set destination for usage and error messages
6345
c.SetOutput(streams.ErrOut)
6446
// Be fabulous
65-
if o.fabulous {
47+
if configOptions.Fabulous {
6648
logger.Fabulous = true
6749
logger.Color = false
6850
}
51+
logger.Level = configOptions.Verbose
6952
},
7053
Run: func(c *cobra.Command, args []string) {
7154
cobra.NoArgs(c, args)
7255
c.Help()
7356
},
7457
}
7558

76-
cmd.PersistentFlags().BoolVarP(&o.fabulous, "fab", "f", o.fabulous, "Enable rainbow logs")
77-
cmd.PersistentFlags().IntVarP(&logger.Level, "verbose", "v", 3, "Verbosity 0 (off) 4 (most)")
59+
cmd.PersistentFlags().StringVarP(&configOptions.ConfigFile, "config", "c", configOptions.ConfigFile, "config file path (default $HOME/.falcoctl.yaml if exists)")
60+
cmd.PersistentFlags().BoolVarP(&configOptions.Fabulous, "fab", "f", configOptions.Fabulous, "enable rainbow logs")
61+
cmd.PersistentFlags().IntVarP(&configOptions.Verbose, "verbose", "v", configOptions.Verbose, "verbosity 0 (off) 4 (most)")
62+
63+
cmd.AddCommand(Install(streams))
64+
cmd.AddCommand(Delete(streams))
65+
cmd.AddCommand(Convert(streams))
7866

79-
flags := cmd.Flags()
80-
o.configFlags.AddFlags(flags)
67+
return cmd
68+
}
8169

82-
matchVersionFlags := factory.MatchVersion(o.configFlags)
83-
matchVersionFlags.AddFlags(flags)
84-
f := factory.New(matchVersionFlags)
70+
// initConfig reads in config file and ENV variables if set.
71+
func initConfig(configOptions *ConfigOptions) {
72+
if errs := configOptions.Validate(); errs != nil {
73+
for _, err := range errs {
74+
logger.Critical("error validating config options: %s", err)
75+
}
76+
os.Exit(1)
77+
}
78+
if configOptions.ConfigFile != "" {
79+
viper.SetConfigFile(configOptions.ConfigFile)
80+
} else {
81+
// Find home directory.
82+
home, err := homedir.Dir()
83+
if err != nil {
84+
logger.Critical("error getting the home directory: %s", err)
85+
}
86+
87+
viper.AddConfigPath(home)
88+
viper.SetConfigName(".falcoctl")
89+
}
8590

8691
viper.AutomaticEnv()
8792
viper.SetEnvPrefix("falcoctl")
8893
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
89-
90-
cmd.AddCommand(Install(streams, f))
91-
cmd.AddCommand(Delete(streams, f))
92-
cmd.AddCommand(Convert(streams))
93-
94-
return cmd
94+
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
95+
96+
// If a config file is found, read it in.
97+
if err := viper.ReadInConfig(); err == nil {
98+
logger.Info("using config file: %s", viper.ConfigFileUsed())
99+
} else {
100+
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
101+
// Config file not found, ignore ...
102+
logger.Debug("running without a configuration file")
103+
} else {
104+
// Config file was found but another error was produced
105+
logger.Critical("error running with config file: %s", err)
106+
}
107+
}
95108
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ module github.com/falcosecurity/falcoctl
33
go 1.13
44

55
require (
6+
github.com/creasty/defaults v1.3.0
67
github.com/evanphx/json-patch v4.5.0+incompatible // indirect
78
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
89
github.com/fatih/color v1.7.0 // indirect
910
github.com/ghodss/yaml v1.0.0
1011
github.com/go-openapi/spec v0.19.3 // indirect
12+
github.com/go-playground/locales v0.13.0
13+
github.com/go-playground/universal-translator v0.17.0
14+
github.com/go-playground/validator/v10 v10.2.0
1115
github.com/gogo/protobuf v1.3.0 // indirect
1216
github.com/google/btree v1.0.0 // indirect
1317
github.com/googleapis/gnostic v0.3.1 // indirect
@@ -18,6 +22,7 @@ require (
1822
github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b // indirect
1923
github.com/mattn/go-colorable v0.1.4 // indirect
2024
github.com/mattn/go-isatty v0.0.10 // indirect
25+
github.com/mitchellh/go-homedir v1.1.0
2126
github.com/modern-go/reflect2 v1.0.1 // indirect
2227
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
2328
github.com/pkg/errors v0.8.1 // indirect

go.sum

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
1616
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
1717
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
1818
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
19+
github.com/creasty/defaults v1.3.0 h1:uG+RAxYbJgOPCOdKEcec9ZJXeva7Y6mj/8egdzwmLtw=
20+
github.com/creasty/defaults v1.3.0/go.mod h1:CIEEvs7oIVZm30R8VxtFJs+4k201gReYyuYHJxZc68I=
1921
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2022
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2123
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -54,6 +56,14 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
5456
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
5557
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
5658
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
59+
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
60+
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
61+
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
62+
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
63+
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
64+
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
65+
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
66+
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
5767
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
5868
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
5969
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -92,6 +102,8 @@ github.com/kris-nova/logger v0.0.0-20181127235838-fd0d87064b06 h1:vN4d3jSss3ExzU
92102
github.com/kris-nova/logger v0.0.0-20181127235838-fd0d87064b06/go.mod h1:++9BgZujZd4v0ZTZCb5iPsaomXdZWyxotIAh1IiDm44=
93103
github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b h1:xYEM2oBUhBEhQjrV+KJ9lEWDWYZoNVZUaBF++Wyljq4=
94104
github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b/go.mod h1:V0HF/ZBlN86HqewcDC/cVxMmYDiRukWjSrgKLUAn9Js=
105+
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
106+
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
95107
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
96108
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
97109
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY=
@@ -150,6 +162,8 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
150162
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
151163
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
152164
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
165+
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
166+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
153167
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
154168
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
155169
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=

validate/validate.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package validate
2+
3+
import (
4+
"reflect"
5+
"strings"
6+
7+
"github.com/go-playground/locales/en"
8+
ut "github.com/go-playground/universal-translator"
9+
"github.com/go-playground/validator/v10"
10+
en_translations "github.com/go-playground/validator/v10/translations/en"
11+
)
12+
13+
// V is the validator single instance.
14+
//
15+
// It is a singleton so to cache the structs info.
16+
var V *validator.Validate
17+
18+
// T is the universal translator for validatiors.
19+
var T ut.Translator
20+
21+
func init() {
22+
V = validator.New()
23+
24+
// Register a function to get the field name from "name" tags.
25+
V.RegisterTagNameFunc(func(fld reflect.StructField) string {
26+
name := strings.SplitN(fld.Tag.Get("name"), ",", 2)[0]
27+
if name == "-" {
28+
return ""
29+
}
30+
return name
31+
})
32+
33+
eng := en.New()
34+
uni := ut.New(eng, eng)
35+
T, _ = uni.GetTranslator("en")
36+
en_translations.RegisterDefaultTranslations(V, T)
37+
}

0 commit comments

Comments
 (0)