• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / kubebuilder / 7097865094

05 Dec 2023 08:08AM UTC coverage: 83.221%. Remained the same
7097865094

Pull #3702

github

web-flow
Bump github.com/onsi/ginkgo/v2 from 2.13.1 to 2.13.2

Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.13.1 to 2.13.2.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.13.1...v2.13.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #3702: 🌱 Bump github.com/onsi/ginkgo/v2 from 2.13.1 to 2.13.2

2465 of 2962 relevant lines covered (83.22%)

14.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.74
/pkg/cli/cmd_helpers.go
1
/*
2
Copyright 2020 The Kubernetes 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

17
package cli
18

19
import (
20
        "errors"
21
        "fmt"
22
        "os"
23

24
        "github.com/spf13/cobra"
25

26
        "sigs.k8s.io/kubebuilder/v3/pkg/config"
27
        "sigs.k8s.io/kubebuilder/v3/pkg/config/store"
28
        yamlstore "sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
29
        "sigs.k8s.io/kubebuilder/v3/pkg/machinery"
30
        "sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
31
        "sigs.k8s.io/kubebuilder/v3/pkg/plugin"
32
)
33

34
// noResolvedPluginError is returned by subcommands that require a plugin when none was resolved.
35
type noResolvedPluginError struct{}
36

37
// Error implements error interface.
38
func (e noResolvedPluginError) Error() string {
8✔
39
        return "no resolved plugin, please verify the project version and plugins specified in flags or configuration file"
8✔
40
}
8✔
41

42
// noAvailablePluginError is returned by subcommands that require a plugin when none of their specific type was found.
43
type noAvailablePluginError struct {
44
        subcommand string
45
}
46

47
// Error implements error interface.
48
func (e noAvailablePluginError) Error() string {
4✔
49
        return fmt.Sprintf("resolved plugins do not provide any %s subcommand", e.subcommand)
4✔
50
}
4✔
51

52
// cmdErr updates a cobra command to output error information when executed
53
// or used with the help flag.
54
func cmdErr(cmd *cobra.Command, err error) {
12✔
55
        cmd.Long = fmt.Sprintf("%s\nNote: %v", cmd.Long, err)
12✔
56
        cmd.RunE = errCmdFunc(err)
12✔
57
}
12✔
58

59
// errCmdFunc returns a cobra RunE function that returns the provided error
60
func errCmdFunc(err error) func(*cobra.Command, []string) error {
40✔
61
        return func(*cobra.Command, []string) error {
41✔
62
                return err
1✔
63
        }
1✔
64
}
65

66
// keySubcommandTuple represents a pairing of the key of a plugin with a plugin.Subcommand.
67
type keySubcommandTuple struct {
68
        key        string
69
        subcommand plugin.Subcommand
70

71
        // skip will be used to flag subcommands that should be skipped after any hook returned a plugin.ExitError.
72
        skip bool
73
}
74

75
// filterSubcommands returns a list of plugin keys and subcommands from a filtered list of resolved plugins.
76
func (c *CLI) filterSubcommands(
77
        filter func(plugin.Plugin) bool,
78
        extract func(plugin.Plugin) plugin.Subcommand,
79
) []keySubcommandTuple {
28✔
80
        // Unbundle plugins
28✔
81
        plugins := make([]plugin.Plugin, 0, len(c.resolvedPlugins))
28✔
82
        for _, p := range c.resolvedPlugins {
56✔
83
                if bundle, isBundle := p.(plugin.Bundle); isBundle {
28✔
84
                        plugins = append(plugins, bundle.Plugins()...)
×
85
                } else {
28✔
86
                        plugins = append(plugins, p)
28✔
87
                }
28✔
88
        }
89

90
        tuples := make([]keySubcommandTuple, 0, len(plugins))
28✔
91
        for _, p := range plugins {
56✔
92
                if filter(p) {
52✔
93
                        tuples = append(tuples, keySubcommandTuple{
24✔
94
                                key:        plugin.KeyFor(p),
24✔
95
                                subcommand: extract(p),
24✔
96
                        })
24✔
97
                }
24✔
98
        }
99
        return tuples
28✔
100
}
101

102
// applySubcommandHooks runs the initialization hooks and configures the commands pre-run,
103
// run, and post-run hooks with the appropriate execution hooks.
104
func (c *CLI) applySubcommandHooks(
105
        cmd *cobra.Command,
106
        subcommands []keySubcommandTuple,
107
        errorMessage string,
108
        createConfig bool,
109
) {
24✔
110
        // In case we create a new project configuration we need to compute the plugin chain.
24✔
111
        pluginChain := make([]string, 0, len(c.resolvedPlugins))
24✔
112
        if createConfig {
30✔
113
                // We extract the plugin keys again instead of using the ones obtained when filtering subcommands
6✔
114
                // as these plugins are unbundled but we want to keep bundle names in the plugin chain.
6✔
115
                for _, p := range c.resolvedPlugins {
12✔
116
                        pluginChain = append(pluginChain, plugin.KeyFor(p))
6✔
117
                }
6✔
118
        }
119

120
        options := initializationHooks(cmd, subcommands, c.metadata())
24✔
121

24✔
122
        factory := executionHooksFactory{
24✔
123
                fs:             c.fs,
24✔
124
                store:          yamlstore.New(c.fs),
24✔
125
                subcommands:    subcommands,
24✔
126
                errorMessage:   errorMessage,
24✔
127
                projectVersion: c.projectVersion,
24✔
128
                pluginChain:    pluginChain,
24✔
129
        }
24✔
130
        cmd.PreRunE = factory.preRunEFunc(options, createConfig)
24✔
131
        cmd.RunE = factory.runEFunc()
24✔
132
        cmd.PostRunE = factory.postRunEFunc()
24✔
133
}
134

135
// initializationHooks executes update metadata and bind flags plugin hooks.
136
func initializationHooks(
137
        cmd *cobra.Command,
138
        subcommands []keySubcommandTuple,
139
        meta plugin.CLIMetadata,
140
) *resourceOptions {
24✔
141
        // Update metadata hook.
24✔
142
        subcmdMeta := plugin.SubcommandMetadata{
24✔
143
                Description: cmd.Long,
24✔
144
                Examples:    cmd.Example,
24✔
145
        }
24✔
146
        for _, tuple := range subcommands {
48✔
147
                if subcommand, updatesMetadata := tuple.subcommand.(plugin.UpdatesMetadata); updatesMetadata {
48✔
148
                        subcommand.UpdateMetadata(meta, &subcmdMeta)
24✔
149
                }
24✔
150
        }
151
        cmd.Long = subcmdMeta.Description
24✔
152
        cmd.Example = subcmdMeta.Examples
24✔
153

24✔
154
        // Before binding specific plugin flags, bind common ones.
24✔
155
        requiresResource := false
24✔
156
        for _, tuple := range subcommands {
48✔
157
                if _, requiresResource = tuple.subcommand.(plugin.RequiresResource); requiresResource {
36✔
158
                        break
12✔
159
                }
160
        }
161
        var options *resourceOptions
24✔
162
        if requiresResource {
36✔
163
                options = bindResourceFlags(cmd.Flags())
12✔
164
        }
12✔
165

166
        // Bind flags hook.
167
        for _, tuple := range subcommands {
48✔
168
                if subcommand, hasFlags := tuple.subcommand.(plugin.HasFlags); hasFlags {
48✔
169
                        subcommand.BindFlags(cmd.Flags())
24✔
170
                }
24✔
171
        }
172

173
        return options
24✔
174
}
175

176
type executionHooksFactory struct {
177
        // fs is the filesystem abstraction to scaffold files to.
178
        fs machinery.Filesystem
179
        // store is the backend used to load/save the project configuration.
180
        store store.Store
181
        // subcommands are the tuples representing the set of subcommands provided by the resolved plugins.
182
        subcommands []keySubcommandTuple
183
        // errorMessage is prepended to returned errors.
184
        errorMessage string
185
        // projectVersion is the project version that will be used to create new project configurations.
186
        // It is only used for initialization.
187
        projectVersion config.Version
188
        // pluginChain is the plugin chain configured for this project.
189
        pluginChain []string
190
}
191

192
func (factory *executionHooksFactory) forEach(cb func(subcommand plugin.Subcommand) error, errorMessage string) error {
×
193
        for i, tuple := range factory.subcommands {
×
194
                if tuple.skip {
×
195
                        continue
×
196
                }
197

198
                err := cb(tuple.subcommand)
×
199

×
200
                var exitError plugin.ExitError
×
201
                switch {
×
202
                case err == nil:
×
203
                        // No error do nothing
204
                case errors.As(err, &exitError):
×
205
                        // Exit errors imply that no further hooks of this subcommand should be called, so we flag it to be skipped
×
206
                        factory.subcommands[i].skip = true
×
207
                        fmt.Printf("skipping remaining hooks of %q: %s\n", tuple.key, exitError.Reason)
×
208
                default:
×
209
                        // Any other error, wrap it
×
210
                        return fmt.Errorf("%s: %s %q: %w", factory.errorMessage, errorMessage, tuple.key, err)
×
211
                }
212
        }
213

214
        return nil
×
215
}
216

217
// preRunEFunc returns a cobra RunE function that loads the configuration, creates the resource,
218
// and executes inject config, inject resource, and pre-scaffold hooks.
219
func (factory *executionHooksFactory) preRunEFunc(
220
        options *resourceOptions,
221
        createConfig bool,
222
) func(*cobra.Command, []string) error {
24✔
223
        return func(*cobra.Command, []string) error {
24✔
224
                if createConfig {
×
225
                        // Check if a project configuration is already present.
×
226
                        if err := factory.store.Load(); err == nil || !errors.Is(err, os.ErrNotExist) {
×
227
                                return fmt.Errorf("%s: already initialized", factory.errorMessage)
×
228
                        }
×
229

230
                        // Initialize the project configuration.
231
                        if err := factory.store.New(factory.projectVersion); err != nil {
×
232
                                return fmt.Errorf("%s: error initializing project configuration: %w", factory.errorMessage, err)
×
233
                        }
×
234
                } else {
×
235
                        // Load the project configuration.
×
236
                        if err := factory.store.Load(); os.IsNotExist(err) {
×
237
                                return fmt.Errorf("%s: unable to find configuration file, project must be initialized",
×
238
                                        factory.errorMessage)
×
239
                        } else if err != nil {
×
240
                                return fmt.Errorf("%s: unable to load configuration file: %w", factory.errorMessage, err)
×
241
                        }
×
242
                }
243
                cfg := factory.store.Config()
×
244

×
245
                // Set the pluginChain field.
×
246
                if len(factory.pluginChain) != 0 {
×
247
                        _ = cfg.SetPluginChain(factory.pluginChain)
×
248
                }
×
249

250
                // Create the resource if non-nil options provided
251
                var res *resource.Resource
×
252
                if options != nil {
×
253
                        // TODO: offer a flag instead of hard-coding project-wide domain
×
254
                        options.Domain = cfg.GetDomain()
×
255
                        if err := options.validate(); err != nil {
×
256
                                return fmt.Errorf("%s: unable to create resource: %w", factory.errorMessage, err)
×
257
                        }
×
258
                        res = options.newResource()
×
259
                }
260

261
                // Inject config hook.
262
                if err := factory.forEach(func(subcommand plugin.Subcommand) error {
×
263
                        if subcommand, requiresConfig := subcommand.(plugin.RequiresConfig); requiresConfig {
×
264
                                return subcommand.InjectConfig(cfg)
×
265
                        }
×
266
                        return nil
×
267
                }, "unable to inject the configuration to"); err != nil {
×
268
                        return err
×
269
                }
×
270

271
                if res != nil {
×
272
                        // Inject resource hook.
×
273
                        if err := factory.forEach(func(subcommand plugin.Subcommand) error {
×
274
                                if subcommand, requiresResource := subcommand.(plugin.RequiresResource); requiresResource {
×
275
                                        return subcommand.InjectResource(res)
×
276
                                }
×
277
                                return nil
×
278
                        }, "unable to inject the resource to"); err != nil {
×
279
                                return err
×
280
                        }
×
281

282
                        if err := res.Validate(); err != nil {
×
283
                                return fmt.Errorf("%s: created invalid resource: %w", factory.errorMessage, err)
×
284
                        }
×
285
                }
286

287
                // Pre-scaffold hook.
288
                // nolint:revive
289
                if err := factory.forEach(func(subcommand plugin.Subcommand) error {
×
290
                        if subcommand, hasPreScaffold := subcommand.(plugin.HasPreScaffold); hasPreScaffold {
×
291
                                return subcommand.PreScaffold(factory.fs)
×
292
                        }
×
293
                        return nil
×
294
                }, "unable to run pre-scaffold tasks of"); err != nil {
×
295
                        return err
×
296
                }
×
297

298
                return nil
×
299
        }
300
}
301

302
// runEFunc returns a cobra RunE function that executes the scaffold hook.
303
func (factory *executionHooksFactory) runEFunc() func(*cobra.Command, []string) error {
24✔
304
        return func(*cobra.Command, []string) error {
24✔
305
                // Scaffold hook.
×
306
                // nolint:revive
×
307
                if err := factory.forEach(func(subcommand plugin.Subcommand) error {
×
308
                        return subcommand.Scaffold(factory.fs)
×
309
                }, "unable to scaffold with"); err != nil {
×
310
                        return err
×
311
                }
×
312

313
                return nil
×
314
        }
315
}
316

317
// postRunEFunc returns a cobra RunE function that saves the configuration
318
// and executes the post-scaffold hook.
319
func (factory *executionHooksFactory) postRunEFunc() func(*cobra.Command, []string) error {
24✔
320
        return func(*cobra.Command, []string) error {
24✔
321
                if err := factory.store.Save(); err != nil {
×
322
                        return fmt.Errorf("%s: unable to save configuration file: %w", factory.errorMessage, err)
×
323
                }
×
324

325
                // Post-scaffold hook.
326
                // nolint:revive
327
                if err := factory.forEach(func(subcommand plugin.Subcommand) error {
×
328
                        if subcommand, hasPostScaffold := subcommand.(plugin.HasPostScaffold); hasPostScaffold {
×
329
                                return subcommand.PostScaffold()
×
330
                        }
×
331
                        return nil
×
332
                }, "unable to run post-scaffold tasks of"); err != nil {
×
333
                        return err
×
334
                }
×
335

336
                return nil
×
337
        }
338
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc