k8s.io/apiserver@v0.31.1/pkg/endpoints/deprecation/deprecation.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 deprecation
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"strconv"
    23  
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  	"k8s.io/apimachinery/pkg/version"
    27  )
    28  
    29  type apiLifecycleDeprecated interface {
    30  	APILifecycleDeprecated() (major, minor int)
    31  }
    32  
    33  type apiLifecycleRemoved interface {
    34  	APILifecycleRemoved() (major, minor int)
    35  }
    36  
    37  type apiLifecycleReplacement interface {
    38  	APILifecycleReplacement() schema.GroupVersionKind
    39  }
    40  
    41  // extract all digits at the beginning of the string
    42  var leadingDigits = regexp.MustCompile(`^(\d+)`)
    43  
    44  // MajorMinor parses a numeric major/minor version from the provided version info.
    45  // The minor version drops all characters after the first non-digit character:
    46  //
    47  //	version.Info{Major:"1", Minor:"2+"} -> 1,2
    48  //	version.Info{Major:"1", Minor:"2.3-build4"} -> 1,2
    49  func MajorMinor(v version.Info) (int, int, error) {
    50  	major, err := strconv.Atoi(v.Major)
    51  	if err != nil {
    52  		return 0, 0, err
    53  	}
    54  	minor, err := strconv.Atoi(leadingDigits.FindString(v.Minor))
    55  	if err != nil {
    56  		return 0, 0, err
    57  	}
    58  	return major, minor, nil
    59  }
    60  
    61  // IsDeprecated returns true if obj implements APILifecycleDeprecated() and returns
    62  // a major/minor version that is non-zero and is <= the specified current major/minor version.
    63  func IsDeprecated(obj runtime.Object, currentMajor, currentMinor int) bool {
    64  	deprecated, isDeprecated := obj.(apiLifecycleDeprecated)
    65  	if !isDeprecated {
    66  		return false
    67  	}
    68  
    69  	deprecatedMajor, deprecatedMinor := deprecated.APILifecycleDeprecated()
    70  	// no deprecation version expressed
    71  	if deprecatedMajor == 0 && deprecatedMinor == 0 {
    72  		return false
    73  	}
    74  	// no current version info available
    75  	if currentMajor == 0 && currentMinor == 0 {
    76  		return true
    77  	}
    78  	// compare deprecation version to current version
    79  	if deprecatedMajor > currentMajor {
    80  		return false
    81  	}
    82  	if deprecatedMajor == currentMajor && deprecatedMinor > currentMinor {
    83  		return false
    84  	}
    85  	return true
    86  }
    87  
    88  // RemovedRelease returns the major/minor version in which the given object is unavailable (in the form "<major>.<minor>")
    89  // if the object implements APILifecycleRemoved() to indicate a non-zero removal version, and returns an empty string otherwise.
    90  func RemovedRelease(obj runtime.Object) string {
    91  	if removed, hasRemovalInfo := obj.(apiLifecycleRemoved); hasRemovalInfo {
    92  		removedMajor, removedMinor := removed.APILifecycleRemoved()
    93  		if removedMajor != 0 || removedMinor != 0 {
    94  			return fmt.Sprintf("%d.%d", removedMajor, removedMinor)
    95  		}
    96  	}
    97  	return ""
    98  }
    99  
   100  // WarningMessage returns a human-readable deprecation warning if the object implements APILifecycleDeprecated()
   101  // to indicate a non-zero deprecated major/minor version and has a populated GetObjectKind().GroupVersionKind().
   102  func WarningMessage(obj runtime.Object) string {
   103  	deprecated, isDeprecated := obj.(apiLifecycleDeprecated)
   104  	if !isDeprecated {
   105  		return ""
   106  	}
   107  
   108  	deprecatedMajor, deprecatedMinor := deprecated.APILifecycleDeprecated()
   109  	if deprecatedMajor == 0 && deprecatedMinor == 0 {
   110  		return ""
   111  	}
   112  
   113  	gvk := obj.GetObjectKind().GroupVersionKind()
   114  	if gvk.Empty() {
   115  		return ""
   116  	}
   117  	deprecationWarning := fmt.Sprintf("%s %s is deprecated in v%d.%d+", gvk.GroupVersion().String(), gvk.Kind, deprecatedMajor, deprecatedMinor)
   118  
   119  	if removed, hasRemovalInfo := obj.(apiLifecycleRemoved); hasRemovalInfo {
   120  		removedMajor, removedMinor := removed.APILifecycleRemoved()
   121  		if removedMajor != 0 || removedMinor != 0 {
   122  			deprecationWarning = deprecationWarning + fmt.Sprintf(", unavailable in v%d.%d+", removedMajor, removedMinor)
   123  		}
   124  	}
   125  
   126  	if replaced, hasReplacement := obj.(apiLifecycleReplacement); hasReplacement {
   127  		replacement := replaced.APILifecycleReplacement()
   128  		if !replacement.Empty() {
   129  			deprecationWarning = deprecationWarning + fmt.Sprintf("; use %s %s", replacement.GroupVersion().String(), replacement.Kind)
   130  		}
   131  	}
   132  
   133  	return deprecationWarning
   134  }