sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/plugin/helpers.go (about) 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 plugin 18 19 import ( 20 "fmt" 21 "path" 22 "sort" 23 "strings" 24 25 "sigs.k8s.io/kubebuilder/v3/pkg/config" 26 "sigs.k8s.io/kubebuilder/v3/pkg/internal/validation" 27 ) 28 29 // KeyFor returns a Plugin's unique identifying string. 30 func KeyFor(p Plugin) string { 31 return path.Join(p.Name(), p.Version().String()) 32 } 33 34 // SplitKey returns a name and version for a plugin key. 35 func SplitKey(key string) (string, string) { 36 if !strings.Contains(key, "/") { 37 return key, "" 38 } 39 keyParts := strings.SplitN(key, "/", 2) 40 return keyParts[0], keyParts[1] 41 } 42 43 // GetShortName returns plugin's short name (name before domain) if name 44 // is fully qualified (has a domain suffix), otherwise GetShortName returns name. 45 // Deprecated 46 func GetShortName(name string) string { 47 return strings.SplitN(name, ".", 2)[0] 48 } 49 50 // Deprecated: it was added to ensure backwards compatibility and should 51 // be removed when we remove the go/v3 plugin 52 // IsLegacyLayout returns true when is possible to identify that the project 53 // was scaffolded with the previous layout 54 func IsLegacyLayout(config config.Config) bool { 55 for _, pluginKey := range config.GetPluginChain() { 56 if strings.Contains(pluginKey, "go.kubebuilder.io/v3") || strings.Contains(pluginKey, "go.kubebuilder.io/v2") { 57 return true 58 } 59 } 60 return false 61 } 62 63 // Validate ensures a Plugin is valid. 64 func Validate(p Plugin) error { 65 if err := validateName(p.Name()); err != nil { 66 return fmt.Errorf("invalid plugin name %q: %v", p.Name(), err) 67 } 68 if err := p.Version().Validate(); err != nil { 69 return fmt.Errorf("invalid plugin version %q: %v", p.Version(), err) 70 } 71 if len(p.SupportedProjectVersions()) == 0 { 72 return fmt.Errorf("plugin %q must support at least one project version", KeyFor(p)) 73 } 74 for _, projectVersion := range p.SupportedProjectVersions() { 75 if err := projectVersion.Validate(); err != nil { 76 return fmt.Errorf("plugin %q supports an invalid project version %q: %v", KeyFor(p), projectVersion, err) 77 } 78 } 79 return nil 80 } 81 82 // ValidateKey ensures both plugin name and version are valid. 83 func ValidateKey(key string) error { 84 name, version := SplitKey(key) 85 if err := validateName(name); err != nil { 86 return fmt.Errorf("invalid plugin name %q: %v", name, err) 87 } 88 // CLI-set plugins do not have to contain a version. 89 if version != "" { 90 var v Version 91 if err := v.Parse(version); err != nil { 92 return fmt.Errorf("invalid plugin version %q: %v", version, err) 93 } 94 } 95 return nil 96 } 97 98 // validateName ensures name is a valid DNS 1123 subdomain. 99 func validateName(name string) error { 100 if errs := validation.IsDNS1123Subdomain(name); len(errs) != 0 { 101 return fmt.Errorf("invalid plugin name %q: %v", name, errs) 102 } 103 return nil 104 } 105 106 // SupportsVersion checks if a plugin supports a project version. 107 func SupportsVersion(p Plugin, projectVersion config.Version) bool { 108 for _, version := range p.SupportedProjectVersions() { 109 if projectVersion.Compare(version) == 0 { 110 return true 111 } 112 } 113 return false 114 } 115 116 // CommonSupportedProjectVersions returns the projects versions that are supported by all the provided Plugins 117 func CommonSupportedProjectVersions(plugins ...Plugin) []config.Version { 118 // Count how many times each supported project version appears 119 supportedProjectVersionCounter := make(map[config.Version]int) 120 for _, plugin := range plugins { 121 for _, supportedProjectVersion := range plugin.SupportedProjectVersions() { 122 if _, exists := supportedProjectVersionCounter[supportedProjectVersion]; !exists { 123 supportedProjectVersionCounter[supportedProjectVersion] = 1 124 } else { 125 supportedProjectVersionCounter[supportedProjectVersion]++ 126 } 127 } 128 } 129 130 // Check which versions are present the expected number of times 131 supportedProjectVersions := make([]config.Version, 0, len(supportedProjectVersionCounter)) 132 expectedTimes := len(plugins) 133 for supportedProjectVersion, times := range supportedProjectVersionCounter { 134 if times == expectedTimes { 135 supportedProjectVersions = append(supportedProjectVersions, supportedProjectVersion) 136 } 137 } 138 139 // Sort the output to guarantee consistency 140 sort.Slice(supportedProjectVersions, func(i int, j int) bool { 141 return supportedProjectVersions[i].Compare(supportedProjectVersions[j]) == -1 142 }) 143 144 return supportedProjectVersions 145 }