Skip to content

Commit 4a38d01

Browse files
refactor(flag): improve flag system architecture and extensibility (#8718)
Co-authored-by: DmitriyLewen <[email protected]>
1 parent e25de25 commit 4a38d01

28 files changed

+386
-575
lines changed

pkg/commands/app.go

+232-193
Large diffs are not rendered by default.

pkg/commands/app_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,9 @@ func TestFlags(t *testing.T) {
315315
rootCmd.SetOut(io.Discard)
316316

317317
flags := &flag.Flags{
318-
GlobalFlagGroup: globalFlags,
319-
ReportFlagGroup: flag.NewReportFlagGroup(),
320-
ScanFlagGroup: flag.NewScanFlagGroup(),
318+
globalFlags,
319+
flag.NewReportFlagGroup(),
320+
flag.NewScanFlagGroup(),
321321
}
322322
cmd := &cobra.Command{
323323
Use: "test",

pkg/flag/aws_flags.go

+4-6
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,14 @@ func (f *AWSFlagGroup) Flags() []Flagger {
7777
}
7878
}
7979

80-
func (f *AWSFlagGroup) ToOptions() (AWSOptions, error) {
81-
if err := parseFlags(f); err != nil {
82-
return AWSOptions{}, err
83-
}
84-
return AWSOptions{
80+
func (f *AWSFlagGroup) ToOptions(opts *Options) error {
81+
opts.AWSOptions = AWSOptions{
8582
Region: f.Region.Value(),
8683
Endpoint: f.Endpoint.Value(),
8784
Services: f.Services.Value(),
8885
SkipServices: f.SkipServices.Value(),
8986
Account: f.Account.Value(),
9087
ARN: f.ARN.Value(),
91-
}, nil
88+
}
89+
return nil
9290
}

pkg/flag/cache_flags.go

+5-7
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,15 @@ func (fg *CacheFlagGroup) Flags() []Flagger {
106106
}
107107
}
108108

109-
func (fg *CacheFlagGroup) ToOptions() (CacheOptions, error) {
110-
if err := parseFlags(fg); err != nil {
111-
return CacheOptions{}, err
112-
}
113-
114-
return CacheOptions{
109+
func (fg *CacheFlagGroup) ToOptions(opts *Options) error {
110+
opts.CacheOptions = CacheOptions{
111+
ClearCache: fg.ClearCache.Value(),
115112
CacheBackend: fg.CacheBackend.Value(),
116113
CacheTTL: fg.CacheTTL.Value(),
117114
RedisTLS: fg.RedisTLS.Value(),
118115
RedisCACert: fg.RedisCACert.Value(),
119116
RedisCert: fg.RedisCert.Value(),
120117
RedisKey: fg.RedisKey.Value(),
121-
}, nil
118+
}
119+
return nil
122120
}

pkg/flag/clean_flags.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,14 @@ func (fg *CleanFlagGroup) Flags() []Flagger {
7878
}
7979
}
8080

81-
func (fg *CleanFlagGroup) ToOptions() (CleanOptions, error) {
82-
if err := parseFlags(fg); err != nil {
83-
return CleanOptions{}, err
84-
}
85-
86-
return CleanOptions{
81+
func (fg *CleanFlagGroup) ToOptions(opts *Options) error {
82+
opts.CleanOptions = CleanOptions{
8783
CleanAll: fg.CleanAll.Value(),
8884
CleanVulnerabilityDB: fg.CleanVulnerabilityDB.Value(),
8985
CleanJavaDB: fg.CleanJavaDB.Value(),
9086
CleanChecksBundle: fg.CleanChecksBundle.Value(),
9187
CleanScanCache: fg.CleanScanCache.Value(),
9288
CleanVEXRepositories: fg.CleanVEXRepositories.Value(),
93-
}, nil
89+
}
90+
return nil
9491
}

pkg/flag/db_flags.go

+9-12
Original file line numberDiff line numberDiff line change
@@ -127,44 +127,40 @@ func (f *DBFlagGroup) Flags() []Flagger {
127127
}
128128
}
129129

130-
func (f *DBFlagGroup) ToOptions() (DBOptions, error) {
131-
if err := parseFlags(f); err != nil {
132-
return DBOptions{}, err
133-
}
134-
130+
func (f *DBFlagGroup) ToOptions(opts *Options) error {
135131
skipDBUpdate := f.SkipDBUpdate.Value()
136132
skipJavaDBUpdate := f.SkipJavaDBUpdate.Value()
137133
downloadDBOnly := f.DownloadDBOnly.Value()
138134
downloadJavaDBOnly := f.DownloadJavaDBOnly.Value()
139135

140136
if downloadDBOnly && downloadJavaDBOnly {
141-
return DBOptions{}, xerrors.New("--download-db-only and --download-java-db-only options can not be specified both")
137+
return xerrors.New("--download-db-only and --download-java-db-only options can not be specified both")
142138
}
143139
if downloadDBOnly && skipDBUpdate {
144-
return DBOptions{}, xerrors.New("--skip-db-update and --download-db-only options can not be specified both")
140+
return xerrors.New("--skip-db-update and --download-db-only options can not be specified both")
145141
}
146142
if downloadJavaDBOnly && skipJavaDBUpdate {
147-
return DBOptions{}, xerrors.New("--skip-java-db-update and --download-java-db-only options can not be specified both")
143+
return xerrors.New("--skip-java-db-update and --download-java-db-only options can not be specified both")
148144
}
149145

150146
var dbRepositories, javaDBRepositories []name.Reference
151147
for _, repo := range f.DBRepositories.Value() {
152148
ref, err := parseRepository(repo, db.SchemaVersion)
153149
if err != nil {
154-
return DBOptions{}, xerrors.Errorf("invalid DB repository: %w", err)
150+
return xerrors.Errorf("invalid DB repository: %w", err)
155151
}
156152
dbRepositories = append(dbRepositories, ref)
157153
}
158154

159155
for _, repo := range f.JavaDBRepositories.Value() {
160156
ref, err := parseRepository(repo, javadb.SchemaVersion)
161157
if err != nil {
162-
return DBOptions{}, xerrors.Errorf("invalid javadb repository: %w", err)
158+
return xerrors.Errorf("invalid javadb repository: %w", err)
163159
}
164160
javaDBRepositories = append(javaDBRepositories, ref)
165161
}
166162

167-
return DBOptions{
163+
opts.DBOptions = DBOptions{
168164
Reset: f.Reset.Value(),
169165
DownloadDBOnly: downloadDBOnly,
170166
SkipDBUpdate: skipDBUpdate,
@@ -173,7 +169,8 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) {
173169
NoProgress: f.NoProgress.Value(),
174170
DBRepositories: dbRepositories,
175171
JavaDBRepositories: javaDBRepositories,
176-
}, nil
172+
}
173+
return nil
177174
}
178175

179176
func parseRepository(repo string, dbSchemaVersion int) (name.Reference, error) {

pkg/flag/db_flags_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ func TestDBFlagGroup_ToOptions(t *testing.T) {
101101
DBRepositories: flag.DBRepositoryFlag.Clone(),
102102
JavaDBRepositories: flag.JavaDBRepositoryFlag.Clone(),
103103
}
104-
got, err := f.ToOptions()
104+
flags := flag.Flags{f}
105+
got, err := flags.ToOptions(nil)
105106
if tt.wantErr != "" {
106-
require.Error(t, err)
107107
assert.ErrorContains(t, err, tt.wantErr)
108108
return
109109
}
110110
require.NoError(t, err)
111111

112-
assert.EqualExportedValues(t, tt.want, got)
112+
assert.EqualExportedValues(t, tt.want, got.DBOptions)
113113

114114
// Assert log messages
115115
assert.Equal(t, tt.wantLogs, out.Messages(), tt.name)

pkg/flag/global_flags.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,13 @@ func (f *GlobalFlagGroup) Bind(cmd *cobra.Command) error {
137137
return nil
138138
}
139139

140-
func (f *GlobalFlagGroup) ToOptions() (GlobalOptions, error) {
141-
if err := parseFlags(f); err != nil {
142-
return GlobalOptions{}, err
143-
}
144-
140+
func (f *GlobalFlagGroup) ToOptions(opts *Options) error {
145141
// Keep TRIVY_NON_SSL for backward compatibility
146142
insecure := f.Insecure.Value() || os.Getenv("TRIVY_NON_SSL") != ""
147143

148144
log.Debug("Cache dir", log.String("dir", f.CacheDir.Value()))
149145

150-
return GlobalOptions{
146+
opts.GlobalOptions = GlobalOptions{
151147
ConfigFile: f.ConfigFile.Value(),
152148
ShowVersion: f.ShowVersion.Value(),
153149
Quiet: f.Quiet.Value(),
@@ -156,5 +152,6 @@ func (f *GlobalFlagGroup) ToOptions() (GlobalOptions, error) {
156152
Timeout: f.Timeout.Value(),
157153
CacheDir: f.CacheDir.Value(),
158154
GenerateDefaultConfig: f.GenerateDefaultConfig.Value(),
159-
}, nil
155+
}
156+
return nil
160157
}

pkg/flag/image_flags.go

+6-9
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,12 @@ func (f *ImageFlagGroup) Flags() []Flagger {
119119
}
120120
}
121121

122-
func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
123-
if err := parseFlags(f); err != nil {
124-
return ImageOptions{}, err
125-
}
126-
122+
func (f *ImageFlagGroup) ToOptions(opts *Options) error {
127123
var platform ftypes.Platform
128124
if p := f.Platform.Value(); p != "" {
129125
pl, err := v1.ParsePlatform(p)
130126
if err != nil {
131-
return ImageOptions{}, xerrors.Errorf("unable to parse platform: %w", err)
127+
return xerrors.Errorf("unable to parse platform: %w", err)
132128
}
133129
if pl.OS == "*" {
134130
pl.OS = "" // Empty OS means any OS
@@ -139,12 +135,12 @@ func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
139135
if value := f.MaxImageSize.Value(); value != "" {
140136
parsedSize, err := units.FromHumanSize(value)
141137
if err != nil {
142-
return ImageOptions{}, xerrors.Errorf("invalid max image size %q: %w", value, err)
138+
return xerrors.Errorf("invalid max image size %q: %w", value, err)
143139
}
144140
maxSize = parsedSize
145141
}
146142

147-
return ImageOptions{
143+
opts.ImageOptions = ImageOptions{
148144
Input: f.Input.Value(),
149145
ImageConfigScanners: xstrings.ToTSlice[types.Scanner](f.ImageConfigScanners.Value()),
150146
ScanRemovedPkgs: f.ScanRemovedPkgs.Value(),
@@ -153,5 +149,6 @@ func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
153149
PodmanHost: f.PodmanHost.Value(),
154150
ImageSources: xstrings.ToTSlice[ftypes.ImageSource](f.ImageSources.Value()),
155151
MaxImageSize: maxSize,
156-
}, nil
152+
}
153+
return nil
157154
}

pkg/flag/image_flags_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,14 @@ func TestImageFlagGroup_ToOptions(t *testing.T) {
7979
Platform: flag.PlatformFlag.Clone(),
8080
}
8181

82-
got, err := f.ToOptions()
82+
flags := flag.Flags{f}
83+
got, err := flags.ToOptions(nil)
8384
if tt.wantErr != "" {
8485
assert.ErrorContains(t, err, tt.wantErr)
8586
return
8687
}
8788
require.NoError(t, err)
88-
assert.EqualExportedValues(t, tt.want, got)
89+
assert.EqualExportedValues(t, tt.want, got.ImageOptions)
8990
})
9091
}
9192
}

pkg/flag/kubernetes_flags.go

+9-12
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package flag
22

33
import (
44
"errors"
5-
"fmt"
65
"strconv"
76
"strings"
87

98
"github.com/samber/lo"
9+
"golang.org/x/xerrors"
1010
corev1 "k8s.io/api/core/v1"
1111
)
1212

@@ -173,33 +173,29 @@ func (f *K8sFlagGroup) Flags() []Flagger {
173173
}
174174
}
175175

176-
func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) {
177-
if err := parseFlags(f); err != nil {
178-
return K8sOptions{}, err
179-
}
180-
176+
func (f *K8sFlagGroup) ToOptions(opts *Options) error {
181177
tolerations, err := optionToTolerations(f.Tolerations.Value())
182178
if err != nil {
183-
return K8sOptions{}, err
179+
return err
184180
}
185181

186182
exludeNodeLabels := make(map[string]string)
187183
exludeNodes := f.ExcludeNodes.Value()
188184
for _, exludeNodeValue := range exludeNodes {
189185
excludeNodeParts := strings.Split(exludeNodeValue, ":")
190186
if len(excludeNodeParts) != 2 {
191-
return K8sOptions{}, fmt.Errorf("exclude node %s must be a key:value", exludeNodeValue)
187+
return xerrors.Errorf("exclude node %s must be a key:value", exludeNodeValue)
192188
}
193189
exludeNodeLabels[excludeNodeParts[0]] = excludeNodeParts[1]
194190
}
195191
if len(f.ExcludeNamespaces.Value()) > 0 && len(f.IncludeNamespaces.Value()) > 0 {
196-
return K8sOptions{}, errors.New("include-namespaces and exclude-namespaces flags cannot be used together")
192+
return xerrors.New("include-namespaces and exclude-namespaces flags cannot be used together")
197193
}
198194
if len(f.ExcludeKinds.Value()) > 0 && len(f.IncludeKinds.Value()) > 0 {
199-
return K8sOptions{}, errors.New("include-kinds and exclude-kinds flags cannot be used together")
195+
return xerrors.New("include-kinds and exclude-kinds flags cannot be used together")
200196
}
201197

202-
return K8sOptions{
198+
opts.K8sOptions = K8sOptions{
203199
KubeConfig: f.KubeConfig.Value(),
204200
K8sVersion: f.K8sVersion.Value(),
205201
Tolerations: tolerations,
@@ -215,7 +211,8 @@ func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) {
215211
ExcludeNamespaces: f.ExcludeNamespaces.Value(),
216212
IncludeNamespaces: f.IncludeNamespaces.Value(),
217213
Burst: f.Burst.Value(),
218-
}, nil
214+
}
215+
return nil
219216
}
220217

221218
func optionToTolerations(tolerationsOptions []string) ([]corev1.Toleration, error) {

pkg/flag/license_flags.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,7 @@ func (f *LicenseFlagGroup) Flags() []Flagger {
115115
}
116116
}
117117

118-
func (f *LicenseFlagGroup) ToOptions() (LicenseOptions, error) {
119-
if err := parseFlags(f); err != nil {
120-
return LicenseOptions{}, err
121-
}
122-
118+
func (f *LicenseFlagGroup) ToOptions(opts *Options) error {
123119
licenseCategories := make(map[types.LicenseCategory][]string)
124120
licenseCategories[types.CategoryForbidden] = f.LicenseForbidden.Value()
125121
licenseCategories[types.CategoryRestricted] = f.LicenseRestricted.Value()
@@ -128,10 +124,11 @@ func (f *LicenseFlagGroup) ToOptions() (LicenseOptions, error) {
128124
licenseCategories[types.CategoryPermissive] = f.LicensePermissive.Value()
129125
licenseCategories[types.CategoryUnencumbered] = f.LicenseUnencumbered.Value()
130126

131-
return LicenseOptions{
127+
opts.LicenseOptions = LicenseOptions{
132128
LicenseFull: f.LicenseFull.Value(),
133129
IgnoredLicenses: f.IgnoredLicenses.Value(),
134130
LicenseConfidenceLevel: f.LicenseConfidenceLevel.Value(),
135131
LicenseCategories: licenseCategories,
136-
}, nil
132+
}
133+
return nil
137134
}

pkg/flag/misconf_flags.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,8 @@ func (f *MisconfFlagGroup) Flags() []Flagger {
204204
}
205205
}
206206

207-
func (f *MisconfFlagGroup) ToOptions() (MisconfOptions, error) {
208-
if err := parseFlags(f); err != nil {
209-
return MisconfOptions{}, err
210-
}
211-
212-
return MisconfOptions{
207+
func (f *MisconfFlagGroup) ToOptions(opts *Options) error {
208+
opts.MisconfOptions = MisconfOptions{
213209
IncludeNonFailures: f.IncludeNonFailures.Value(),
214210
ResetChecksBundle: f.ResetChecksBundle.Value(),
215211
ChecksBundleRepository: f.ChecksBundleRepository.Value(),
@@ -225,5 +221,6 @@ func (f *MisconfFlagGroup) ToOptions() (MisconfOptions, error) {
225221
MisconfigScanners: xstrings.ToTSlice[analyzer.Type](f.MisconfigScanners.Value()),
226222
ConfigFileSchemas: f.ConfigFileSchemas.Value(),
227223
RenderCause: xstrings.ToTSlice[types.ConfigType](f.RenderCause.Value()),
228-
}, nil
224+
}
225+
return nil
229226
}

pkg/flag/module_flags.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,10 @@ func (f *ModuleFlagGroup) Flags() []Flagger {
5858
}
5959
}
6060

61-
func (f *ModuleFlagGroup) ToOptions() (ModuleOptions, error) {
62-
if err := parseFlags(f); err != nil {
63-
return ModuleOptions{}, err
64-
}
65-
66-
return ModuleOptions{
61+
func (f *ModuleFlagGroup) ToOptions(opts *Options) error {
62+
opts.ModuleOptions = ModuleOptions{
6763
ModuleDir: f.Dir.Value(),
6864
EnabledModules: f.EnabledModules.Value(),
69-
}, nil
65+
}
66+
return nil
7067
}

0 commit comments

Comments
 (0)