Skip to content

Commit c1f48d0

Browse files
authored
Merge pull request #1380 from estroz/feature/plugins-enforce-version-format
plugins: validate project and plugin versions
2 parents ebd0d8c + b3d7e23 commit c1f48d0

File tree

12 files changed

+238
-165
lines changed

12 files changed

+238
-165
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module sigs.k8s.io/kubebuilder
33
go 1.13
44

55
require (
6+
github.com/blang/semver v3.5.1+incompatible
67
github.com/gobuffalo/flect v0.1.5
78
github.com/golang/protobuf v1.3.1 // indirect
89
github.com/kr/pretty v0.1.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
22
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
3+
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
4+
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
35
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
46
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
57
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=

pkg/cli/cli.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/spf13/cobra"
2626

2727
internalconfig "sigs.k8s.io/kubebuilder/internal/config"
28+
"sigs.k8s.io/kubebuilder/pkg/internal/validation"
2829
"sigs.k8s.io/kubebuilder/pkg/model/config"
2930
"sigs.k8s.io/kubebuilder/pkg/plugin"
3031
)
@@ -146,6 +147,12 @@ func (c *cli) initialize() error {
146147
return fmt.Errorf("failed to read config: %v", err)
147148
}
148149

150+
// Validate after setting projectVersion but before buildRootCmd so we error
151+
// out before an error resulting from an incorrect cli is returned downstream.
152+
if err = c.validate(); err != nil {
153+
return err
154+
}
155+
149156
c.cmd = c.buildRootCmd()
150157

151158
// Add extra commands injected by options.
@@ -175,6 +182,37 @@ func (c *cli) initialize() error {
175182
return nil
176183
}
177184

185+
// validate validates fields in a cli.
186+
func (c cli) validate() error {
187+
// Validate project versions.
188+
if err := validation.ValidateProjectVersion(c.defaultProjectVersion); err != nil {
189+
return fmt.Errorf("failed to validate default project version %q: %v", c.defaultProjectVersion, err)
190+
}
191+
if err := validation.ValidateProjectVersion(c.projectVersion); err != nil {
192+
return fmt.Errorf("failed to validate project version %q: %v", c.projectVersion, err)
193+
}
194+
195+
// Validate plugin versions and name.
196+
for _, versionedPlugins := range c.plugins {
197+
for _, versionedPlugin := range versionedPlugins {
198+
pluginName := versionedPlugin.Name()
199+
pluginVersion := versionedPlugin.Version()
200+
if err := plugin.ValidateVersion(pluginVersion); err != nil {
201+
return fmt.Errorf("failed to validate plugin %q version %q: %v",
202+
pluginName, pluginVersion, err)
203+
}
204+
for _, projectVersion := range versionedPlugin.SupportedProjectVersions() {
205+
if err := validation.ValidateProjectVersion(projectVersion); err != nil {
206+
return fmt.Errorf("failed to validate plugin %q supported project version %q: %v",
207+
pluginName, projectVersion, err)
208+
}
209+
}
210+
}
211+
}
212+
213+
return nil
214+
}
215+
178216
// buildRootCmd returns a root command with a subcommand tree reflecting the
179217
// current project's state.
180218
func (c cli) buildRootCmd() *cobra.Command {

pkg/internal/validation/dns.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
Copyright 2018 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 validation
18+
19+
import (
20+
"fmt"
21+
"regexp"
22+
)
23+
24+
// This file's code was modified from "k8s.io/apimachinery/pkg/util/validation"
25+
// to avoid package dependencies. In case of additional functionality from
26+
// "k8s.io/apimachinery" is needed, re-consider whether to add the dependency.
27+
28+
const (
29+
dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
30+
dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
31+
dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
32+
)
33+
34+
type dnsValidationConfig struct {
35+
format string
36+
maxLen int
37+
re *regexp.Regexp
38+
errMsg string
39+
examples []string
40+
}
41+
42+
var dns1123LabelConfig = dnsValidationConfig{
43+
format: dns1123LabelFmt,
44+
maxLen: 56, // = 63 - len("-system")
45+
re: regexp.MustCompile("^" + dns1123LabelFmt + "$"),
46+
errMsg: "a DNS-1123 label must consist of lower case alphanumeric characters or '-'",
47+
examples: []string{"example.com"},
48+
}
49+
50+
var dns1123SubdomainConfig = dnsValidationConfig{
51+
format: dns1123SubdomainFmt,
52+
maxLen: 253, // a subdomain's max length in DNS (RFC 1123).
53+
re: regexp.MustCompile("^" + dns1123SubdomainFmt + "$"),
54+
errMsg: "a DNS-1123 subdomain must consist of lower case alphanumeric characters, " +
55+
"'-' or '.', and must start and end with an alphanumeric character",
56+
examples: []string{"my-name", "abc-123"},
57+
}
58+
59+
var dns1035LabelConfig = dnsValidationConfig{
60+
format: dns1035LabelFmt,
61+
maxLen: 63, // a label's max length in DNS (RFC 1035).
62+
re: regexp.MustCompile("^" + dns1035LabelFmt + "$"),
63+
errMsg: "a DNS-1035 label must consist of lower case alphanumeric characters or '-', " +
64+
"start with an alphabetic character, and end with an alphanumeric character",
65+
examples: []string{"my-name", "123-abc"},
66+
}
67+
68+
func (c dnsValidationConfig) check(value string) (errs []string) {
69+
if len(value) > c.maxLen {
70+
errs = append(errs, maxLenError(c.maxLen))
71+
}
72+
if !c.re.MatchString(value) {
73+
errs = append(errs, regexError(c.errMsg, c.format, c.examples...))
74+
}
75+
return errs
76+
}
77+
78+
// IsDNS1123Subdomain tests for a string that conforms to the definition of a
79+
// subdomain in DNS (RFC 1123).
80+
func IsDNS1123Subdomain(value string) []string {
81+
return dns1123SubdomainConfig.check(value)
82+
}
83+
84+
// IsDNS1123Label tests for a string that conforms to the definition of a label in DNS (RFC 1123).
85+
func IsDNS1123Label(value string) []string {
86+
return dns1123LabelConfig.check(value)
87+
}
88+
89+
// IsDNS1035Label tests for a string that conforms to the definition of a label in DNS (RFC 1035).
90+
func IsDNS1035Label(value string) []string {
91+
return dns1035LabelConfig.check(value)
92+
}
93+
94+
// maxLenError returns a string explanation of a "string too long" validation
95+
// failure.
96+
func maxLenError(length int) string {
97+
return fmt.Sprintf("must be no more than %d characters", length)
98+
}
99+
100+
// regexError returns a string explanation of a regex validation failure.
101+
func regexError(msg string, fmt string, examples ...string) string {
102+
if len(examples) == 0 {
103+
return msg + " (regex used for validation is '" + fmt + "')"
104+
}
105+
msg += " (e.g. "
106+
for i := range examples {
107+
if i > 0 {
108+
msg += " or "
109+
}
110+
msg += "'" + examples[i] + "', "
111+
}
112+
msg += "regex used for validation is '" + fmt + "')"
113+
return msg
114+
}

pkg/internal/validation/project.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 validation
18+
19+
import (
20+
"errors"
21+
"regexp"
22+
)
23+
24+
// projectVersionFmt defines the project version format from a project config.
25+
const projectVersionFmt string = "[1-9][0-9]*(-(alpha|beta))?"
26+
27+
var projectVersionRe = regexp.MustCompile("^" + projectVersionFmt + "$")
28+
29+
// ValidateProjectVersion ensures version adheres to the project version format.
30+
func ValidateProjectVersion(version string) error {
31+
if version == "" {
32+
return errors.New("project version is empty")
33+
}
34+
if !projectVersionRe.MatchString(version) {
35+
return errors.New(regexError("invalid value for project version", projectVersionFmt))
36+
}
37+
return nil
38+
}

pkg/model/resource/options.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/gobuffalo/flect"
2828

29+
"sigs.k8s.io/kubebuilder/pkg/internal/validation"
2930
"sigs.k8s.io/kubebuilder/pkg/model/config"
3031
)
3132

@@ -116,7 +117,7 @@ func (opts *Options) Validate() error {
116117
}
117118

118119
// Check if the Group has a valid DNS1123 subdomain value
119-
if err := IsDNS1123Subdomain(opts.Group); err != nil {
120+
if err := validation.IsDNS1123Subdomain(opts.Group); err != nil {
120121
return fmt.Errorf("group name is invalid: (%v)", err)
121122
}
122123

@@ -133,7 +134,7 @@ func (opts *Options) Validate() error {
133134
validationErrors = append(validationErrors, "Kind must start with an uppercase character")
134135
}
135136

136-
validationErrors = append(validationErrors, isDNS1035Label(strings.ToLower(opts.Kind))...)
137+
validationErrors = append(validationErrors, validation.IsDNS1035Label(strings.ToLower(opts.Kind))...)
137138

138139
if len(validationErrors) != 0 {
139140
return fmt.Errorf("Invalid Kind: %#v", validationErrors)

pkg/model/resource/validation.go

Lines changed: 0 additions & 92 deletions
This file was deleted.

0 commit comments

Comments
 (0)