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  }