/
configfile.go
161 lines (129 loc) · 4.48 KB
/
configfile.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package cmdutils
import (
"fmt"
"io/ioutil"
"os"
api "github.com/181192/ops-cli/pkg/apis/opscli.io/v1alpha1"
scheme "github.com/181192/ops-cli/pkg/generated/clientset/versioned/scheme"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/yaml"
)
// AddConfigFileFlag adds common --config-file flag
func AddConfigFileFlag(fs *pflag.FlagSet, path *string) {
fs.StringVarP(path, "config-file", "f", "", "Load configuration from a file (or stdin if set to '-')")
}
// ClusterConfigLoader is an interface that loaders should implement
type ClusterConfigLoader interface {
Load() error
}
type commonClusterConfigLoader struct {
*Cmd
flagsIncompatibleWithConfigFile sets.String
flagsIncompatibleWithoutConfigFile sets.String
validateWithConfigFile func() error
validateWithoutConfigFile func() error
}
var (
defaultFlagsIncompatibleWithConfigFile = sets.NewString(
"name",
"location",
"version",
"cluster",
"namepace",
)
defaultFlagsIncompatibleWithoutConfigFile = sets.NewString()
)
func newCommonClusterConfigLoader(cmd *Cmd) *commonClusterConfigLoader {
nilValidatorFunc := func() error { return nil }
return &commonClusterConfigLoader{
Cmd: cmd,
validateWithConfigFile: nilValidatorFunc,
flagsIncompatibleWithConfigFile: defaultFlagsIncompatibleWithConfigFile,
validateWithoutConfigFile: nilValidatorFunc,
flagsIncompatibleWithoutConfigFile: defaultFlagsIncompatibleWithoutConfigFile,
}
}
// Load ClusterConfig or use flags
func (l *commonClusterConfigLoader) Load() error {
if l.ClusterConfigFile == "" {
for f := range l.flagsIncompatibleWithoutConfigFile {
if flag := l.CobraCommand.Flag(f); flag != nil && flag.Changed {
return fmt.Errorf("cannot use --%s unless a config file is specified via --config-file/-f", f)
}
}
return l.validateWithoutConfigFile()
}
var err error
// The reference to ClusterConfig should only be reassigned if ClusterConfigFile is specified
// because other parts of the code store the pointer locally and access it directly instead of via
// the Cmd reference
if l.ClusterConfig, err = LoadConfigFromFile(l.ClusterConfigFile); err != nil {
return err
}
meta := l.ClusterConfig.ObjectMeta
spec := l.ClusterConfig.Spec
for f := range l.flagsIncompatibleWithConfigFile {
if flag := l.CobraCommand.Flag(f); flag != nil && flag.Changed {
return ErrCannotUseWithConfigFile(fmt.Sprintf("--%s", f))
}
}
if l.NameArg != "" {
return ErrCannotUseWithConfigFile(fmt.Sprintf("name argument %q", l.NameArg))
}
if meta.Name == "" {
return ErrMustBeSet("metadata.name")
}
if spec.Location == "" {
return ErrMustBeSet("spec.location")
}
return l.validateWithConfigFile()
}
func (l *commonClusterConfigLoader) validateMetadataWithoutConfigFile() error {
meta := l.ClusterConfig.ObjectMeta
if meta.Name != "" && l.NameArg != "" {
return ErrClusterFlagAndArg(l.Cmd, meta.Name, l.NameArg)
}
if l.NameArg != "" {
meta.Name = l.NameArg
}
if meta.Name == "" {
return ErrMustBeSet(ClusterNameFlag(l.Cmd))
}
return nil
}
// NewMetadataLoader handles loading of clusterConfigFile vs using flags for all commands that require only
// metadata fields, e.g. `eksctl delete cluster` or `eksctl utils update-kube-proxy` and other similar
// commands that do simple operations against existing clusters
func NewMetadataLoader(cmd *Cmd) ClusterConfigLoader {
l := newCommonClusterConfigLoader(cmd)
l.validateWithoutConfigFile = l.validateMetadataWithoutConfigFile
return l
}
// LoadConfigFromFile loads ClusterConfig from configFile
func LoadConfigFromFile(configFile string) (*api.ClusterConfig, error) {
data, err := readConfig(configFile)
if err != nil {
return nil, errors.Wrapf(err, "reading config file %q", configFile)
}
if err := yaml.UnmarshalStrict(data, &api.ClusterConfig{}); err != nil {
return nil, errors.Wrapf(err, "converting YAML to JSON when loading config file %q", configFile)
}
obj, err := runtime.Decode(scheme.Codecs.UniversalDeserializer(), data)
if err != nil {
return nil, errors.Wrapf(err, "decoding data into objects when loading config file %q", configFile)
}
cfg, ok := obj.(*api.ClusterConfig)
if !ok {
return nil, fmt.Errorf("expected to decode object of type %T; got %T", &api.ClusterConfig{}, cfg)
}
return cfg, nil
}
func readConfig(configFile string) ([]byte, error) {
if configFile == "-" {
return ioutil.ReadAll(os.Stdin)
}
return ioutil.ReadFile(configFile)
}