Skip to content

Commit 4e7398e

Browse files
carlmontanarihellt
andauthored
Refactor/cmd opts struct (#2761)
* refactor: create a clab.Save method, use that via cmd * feat: squash all init funcs in cmd * refactor: begin migration to structs for all cli opts, flatten version stuff up into cmd * fix: dont use global var for generate node registry * refactor: structs to hold all cmd options done (i think!) * fix: typo on api server host * chore: appease some deepsource nits * use runtime constant * GetOptions comment * remove extra comment * chore: cleanup some linter nits * fix: rebased and nits fixed --------- Co-authored-by: hellt <dodin.roman@gmail.com>
1 parent d61a2f9 commit 4e7398e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3402
-3104
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ __pycache__
5252
.env
5353

5454
.zed
55+
56+
# to keep robot things away
57+
venv/
58+
.venv/

.golangci.yml

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ linters:
3333
- lll
3434
- mnd
3535
- nestif
36+
- perfsprint
3637
- prealloc
3738
- revive
39+
- wrapcheck
3840
- wsl_v5
3941
settings:
4042
dupl:
@@ -47,11 +49,7 @@ linters:
4749
min-occurrences: 2
4850
gocritic:
4951
disabled-checks:
50-
- dupImport
51-
- ifElseChain
52-
- octalLiteral
5352
- whyNoLint
54-
- wrapperFunc
5553
enabled-tags:
5654
- diagnostic
5755
- experimental
@@ -84,12 +82,6 @@ linters:
8482
require-specific: false
8583
allow-unused: false
8684
exclusions:
87-
generated: lax
88-
presets:
89-
- comments
90-
- common-false-positives
91-
- legacy
92-
- std-error-handling
9385
rules:
9486
# we *care* about tests, but... like less :P
9587
- linters:
@@ -103,10 +95,6 @@ linters:
10395
- linters:
10496
- staticcheck
10597
text: could remove embedded field
106-
paths:
107-
- third_party$
108-
- builtin$
109-
- examples$
11098
formatters:
11199
enable:
112100
- gofmt

.goreleaser.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ builds:
1111
- -trimpath
1212
- -tags=podman exclude_graphdriver_btrfs btrfs_noversion exclude_graphdriver_devicemapper exclude_graphdriver_overlay containers_image_openpgp
1313
ldflags:
14-
- -s -w -X github.com/srl-labs/containerlab/cmd/version.Version={{.Version}} -X github.com/srl-labs/containerlab/cmd/version.commit={{.ShortCommit}} -X github.com/srl-labs/containerlab/cmd/version.date={{.Date}}
14+
- -s -w -X github.com/srl-labs/containerlab/cmd.Version={{.Version}} -X github.com/srl-labs/containerlab/cmd.commit={{.ShortCommit}} -X github.com/srl-labs/containerlab/cmd.date={{.Date}}
1515
goos:
1616
- linux
1717
goarch:

cmd/completion.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import (
1010
"github.com/spf13/cobra"
1111
)
1212

13-
// completionCmd represents the completion command.
14-
var completionCmd = &cobra.Command{
15-
Use: "completion [bash|zsh|fish]",
16-
Short: "generate completion script",
17-
Long: `To load completions:
13+
func completionCmd(_ *Options) (*cobra.Command, error) {
14+
c := &cobra.Command{
15+
Use: "completion [bash|zsh|fish]",
16+
Short: "generate completion script",
17+
Long: `To load completions:
1818
1919
Bash:
2020
@@ -45,21 +45,20 @@ fish:
4545
# To load completions for each session, execute once:
4646
$ containerlab completion fish > ~/.config/fish/completions/containerlab.fish
4747
`,
48-
DisableFlagsInUseLine: true,
49-
ValidArgs: []string{"bash", "zsh", "fish"},
50-
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
51-
Run: func(cmd *cobra.Command, args []string) {
52-
switch args[0] {
53-
case "bash":
54-
_ = cmd.Root().GenBashCompletion(os.Stdout)
55-
case "zsh":
56-
_ = cmd.Root().GenZshCompletion(os.Stdout)
57-
case "fish":
58-
_ = cmd.Root().GenFishCompletion(os.Stdout, true)
59-
}
60-
},
61-
}
62-
63-
func init() {
64-
RootCmd.AddCommand(completionCmd)
48+
DisableFlagsInUseLine: true,
49+
ValidArgs: []string{"bash", "zsh", "fish"},
50+
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
51+
Run: func(cmd *cobra.Command, args []string) {
52+
switch args[0] {
53+
case "bash":
54+
_ = cmd.Root().GenBashCompletion(os.Stdout)
55+
case "zsh":
56+
_ = cmd.Root().GenZshCompletion(os.Stdout)
57+
case "fish":
58+
_ = cmd.Root().GenFishCompletion(os.Stdout, true)
59+
}
60+
},
61+
}
62+
63+
return c, nil
6564
}

cmd/config.go

Lines changed: 157 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -14,61 +14,131 @@ import (
1414
"github.com/charmbracelet/log"
1515
)
1616

17-
// Node Filter for config.
18-
var configFilter []string
19-
20-
// configCmd represents the config command.
21-
var configCmd = &cobra.Command{
22-
Use: "config",
23-
Short: "configure a lab",
24-
Long: "configure a lab based on templates and variables from the topology definition file\nreference: https://containerlab.dev/cmd/config/",
25-
Aliases: []string{"conf"},
26-
ValidArgs: []string{"commit", "send", "compare", "template"},
27-
SilenceUsage: true,
28-
RunE: configRun,
29-
}
17+
func configCmd(o *Options) (*cobra.Command, error) {
18+
c := &cobra.Command{
19+
Use: "config",
20+
Short: "configure a lab",
21+
Long: "configure a lab based on templates and variables from the topology definition file\nreference: https://containerlab.dev/cmd/config/",
22+
Aliases: []string{"conf"},
23+
ValidArgs: []string{"commit", "send", "compare", "template"},
24+
SilenceUsage: true,
25+
RunE: func(cobraCmd *cobra.Command, args []string) error {
26+
return configRun(cobraCmd, args, o)
27+
},
28+
}
3029

31-
var configSendCmd = &cobra.Command{
32-
Use: "send",
33-
Short: "send raw configuration to a lab",
34-
SilenceUsage: true,
35-
RunE: func(cmd *cobra.Command, args []string) error {
36-
if len(args) > 0 {
37-
return fmt.Errorf("unexpected arguments: %s", args)
38-
}
39-
return configRun(cmd, []string{"send"})
40-
},
30+
c.Flags().StringSliceVarP(
31+
&clabcoreconfig.TemplatePaths,
32+
"template-path",
33+
"p",
34+
[]string{},
35+
"comma separated list of paths to search for templates",
36+
)
37+
38+
c.Flags().StringSliceVarP(
39+
&clabcoreconfig.TemplateNames,
40+
"template-list",
41+
"l",
42+
[]string{},
43+
"comma separated list of template names to render",
44+
)
45+
46+
c.Flags().StringSliceVarP(
47+
&o.Filter.LabelFilter,
48+
"filter",
49+
"f",
50+
o.Filter.LabelFilter,
51+
"comma separated list of nodes (by labels) to include",
52+
)
53+
54+
c.Flags().StringSliceVarP(
55+
&o.Filter.NodeFilter,
56+
"node-filter",
57+
"",
58+
o.Filter.NodeFilter,
59+
"comma separated list of nodes to include",
60+
)
61+
62+
c.Flags().SortFlags = false
63+
64+
err := c.MarkFlagDirname("template-path")
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
configSubCmds(c, o)
70+
71+
return c, nil
4172
}
4273

43-
var configCompareCmd = &cobra.Command{
44-
Use: "compare",
45-
Short: "compare configuration to a running lab",
46-
SilenceUsage: true,
47-
RunE: func(cmd *cobra.Command, args []string) error {
48-
if len(args) > 0 {
49-
return fmt.Errorf("unexpected arguments: %s", args)
50-
}
51-
return configRun(cmd, []string{"compare"})
52-
},
74+
func configSubCmds(c *cobra.Command, o *Options) {
75+
sendC := &cobra.Command{
76+
Use: "send",
77+
Short: "send raw configuration to a lab",
78+
SilenceUsage: true,
79+
RunE: func(cobraCmd *cobra.Command, args []string) error {
80+
if len(args) > 0 {
81+
return fmt.Errorf("unexpected arguments: %s", args)
82+
}
83+
84+
return configRun(cobraCmd, []string{"send"}, o)
85+
},
86+
}
87+
88+
c.AddCommand(sendC)
89+
sendC.Flags().AddFlagSet(c.Flags())
90+
91+
compareC := &cobra.Command{
92+
Use: "compare",
93+
Short: "compare configuration to a running lab",
94+
SilenceUsage: true,
95+
RunE: func(cobraCmd *cobra.Command, args []string) error {
96+
if len(args) > 0 {
97+
return fmt.Errorf("unexpected arguments: %s", args)
98+
}
99+
100+
return configRun(cobraCmd, []string{"compare"}, o)
101+
},
102+
}
103+
104+
c.AddCommand(compareC)
105+
compareC.Flags().AddFlagSet(c.Flags())
106+
107+
templateC := &cobra.Command{
108+
Use: "template",
109+
Short: "render a template",
110+
Long: "render a template based on variables from the topology definition file\nreference: https://containerlab.dev/cmd/config/template",
111+
Aliases: []string{"conf"},
112+
SilenceUsage: true,
113+
RunE: func(_ *cobra.Command, _ []string) error {
114+
return configTemplate(o)
115+
},
116+
}
117+
118+
c.AddCommand(templateC)
119+
templateC.Flags().AddFlagSet(c.Flags())
120+
templateC.Flags().BoolVarP(&o.Config.TemplateVarOnly, "vars", "v", o.Config.TemplateVarOnly,
121+
"show variable used for template rendering")
122+
templateC.Flags().SortFlags = false
53123
}
54124

55-
func configRun(_ *cobra.Command, args []string) error {
125+
func configRun(_ *cobra.Command, args []string, o *Options) error {
56126
var err error
57127

58-
transport.DebugCount = debugCount
59-
clabcoreconfig.DebugCount = debugCount
128+
transport.DebugCount = o.Global.DebugCount
129+
clabcoreconfig.DebugCount = o.Global.DebugCount
60130

61131
c, err := clabcore.NewContainerLab(
62-
clabcore.WithTimeout(timeout),
63-
clabcore.WithTopoPath(topoFile, varsFile),
64-
clabcore.WithNodeFilter(nodeFilter),
65-
clabcore.WithDebug(debug),
132+
clabcore.WithTimeout(o.Global.Timeout),
133+
clabcore.WithTopoPath(o.Global.TopologyFile, o.Global.VarsFile),
134+
clabcore.WithNodeFilter(o.Filter.NodeFilter),
135+
clabcore.WithDebug(o.Global.DebugCount > 0),
66136
)
67137
if err != nil {
68138
return err
69139
}
70140

71-
err = validateFilter(c.Nodes)
141+
err = validateFilter(c.Nodes, o)
72142
if err != nil {
73143
return err
74144
}
@@ -112,8 +182,8 @@ func configRun(_ *cobra.Command, args []string) error {
112182
log.Warnf("%s: %s", cs.TargetNode.ShortName, err)
113183
}
114184
}
115-
wg.Add(len(configFilter))
116-
for _, node := range configFilter {
185+
wg.Add(len(o.Filter.LabelFilter))
186+
for _, node := range o.Filter.LabelFilter {
117187
// On debug this will not be executed concurrently
118188
if log.GetLevel() == (log.DebugLevel) {
119189
deploy(node)
@@ -126,16 +196,56 @@ func configRun(_ *cobra.Command, args []string) error {
126196
return nil
127197
}
128198

129-
func validateFilter(nodes map[string]clabnodes.Node) error {
130-
if len(configFilter) == 0 {
199+
func configTemplate(o *Options) error {
200+
var err error
201+
202+
clabcoreconfig.DebugCount = o.Global.DebugCount
203+
204+
c, err := clabcore.NewContainerLab(
205+
clabcore.WithTimeout(o.Global.Timeout),
206+
clabcore.WithTopoPath(o.Global.TopologyFile, o.Global.VarsFile),
207+
clabcore.WithDebug(o.Global.DebugCount > 0),
208+
)
209+
if err != nil {
210+
return err
211+
}
212+
213+
err = validateFilter(c.Nodes, o)
214+
if err != nil {
215+
return err
216+
}
217+
218+
allConfig := clabcoreconfig.PrepareVars(c)
219+
if o.Config.TemplateVarOnly {
220+
for _, n := range o.Filter.LabelFilter {
221+
conf := allConfig[n]
222+
conf.Print(true, false)
223+
}
224+
return nil
225+
}
226+
227+
err = clabcoreconfig.RenderAll(allConfig)
228+
if err != nil {
229+
return err
230+
}
231+
232+
for _, n := range o.Filter.LabelFilter {
233+
allConfig[n].Print(false, true)
234+
}
235+
236+
return nil
237+
}
238+
239+
func validateFilter(nodes map[string]clabnodes.Node, o *Options) error {
240+
if len(o.Filter.LabelFilter) == 0 {
131241
for n := range nodes {
132-
configFilter = append(configFilter, n)
242+
o.Filter.LabelFilter = append(o.Filter.LabelFilter, n)
133243
}
134244
return nil
135245
}
136246

137247
var mis []string
138-
for _, nn := range configFilter {
248+
for _, nn := range o.Filter.LabelFilter {
139249
if _, ok := nodes[nn]; !ok {
140250
mis = append(mis, nn)
141251
}
@@ -145,24 +255,3 @@ func validateFilter(nodes map[string]clabnodes.Node) error {
145255
}
146256
return nil
147257
}
148-
149-
func init() {
150-
RootCmd.AddCommand(configCmd)
151-
configCmd.Flags().StringSliceVarP(&clabcoreconfig.TemplatePaths, "template-path", "p", []string{},
152-
"comma separated list of paths to search for templates")
153-
_ = configCmd.MarkFlagDirname("template-path")
154-
configCmd.Flags().StringSliceVarP(&clabcoreconfig.TemplateNames, "template-list", "l", []string{},
155-
"comma separated list of template names to render")
156-
configCmd.Flags().StringSliceVarP(&configFilter, "filter", "f", []string{},
157-
"comma separated list of nodes to include")
158-
configCmd.Flags().SortFlags = false
159-
160-
configCmd.Flags().StringSliceVarP(&nodeFilter, "node-filter", "", []string{},
161-
"comma separated list of nodes to include")
162-
163-
configCmd.AddCommand(configSendCmd)
164-
configSendCmd.Flags().AddFlagSet(configCmd.Flags())
165-
166-
configCmd.AddCommand(configCompareCmd)
167-
configCompareCmd.Flags().AddFlagSet(configCmd.Flags())
168-
}

0 commit comments

Comments
 (0)