#1 Backport for github.com/helm/helm
Merged 3 years ago by eclipseo. Opened 3 years ago by dcavalca.
rpms/ dcavalca/golang-k8s-apiserver deprecation  into  rawhide

@@ -0,0 +1,479 @@ 

+ diff -Naur a/pkg/endpoints/deprecation/deprecation.go b/pkg/endpoints/deprecation/deprecation.go

+ --- a/pkg/endpoints/deprecation/deprecation.go	1969-12-31 16:00:00.000000000 -0800

+ +++ b/pkg/endpoints/deprecation/deprecation.go	2021-05-17 22:36:08.653804042 -0700

+ @@ -0,0 +1,133 @@

+ +/*

+ +Copyright 2020 The Kubernetes Authors.

+ +

+ +Licensed under the Apache License, Version 2.0 (the "License");

+ +you may not use this file except in compliance with the License.

+ +You may obtain a copy of the License at

+ +

+ +    http://www.apache.org/licenses/LICENSE-2.0

+ +

+ +Unless required by applicable law or agreed to in writing, software

+ +distributed under the License is distributed on an "AS IS" BASIS,

+ +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ +See the License for the specific language governing permissions and

+ +limitations under the License.

+ +*/

+ +

+ +package deprecation

+ +

+ +import (

+ +	"fmt"

+ +	"regexp"

+ +	"strconv"

+ +

+ +	"k8s.io/apimachinery/pkg/runtime"

+ +	"k8s.io/apimachinery/pkg/runtime/schema"

+ +	"k8s.io/apimachinery/pkg/version"

+ +)

+ +

+ +type apiLifecycleDeprecated interface {

+ +	APILifecycleDeprecated() (major, minor int)

+ +}

+ +

+ +type apiLifecycleRemoved interface {

+ +	APILifecycleRemoved() (major, minor int)

+ +}

+ +

+ +type apiLifecycleReplacement interface {

+ +	APILifecycleReplacement() schema.GroupVersionKind

+ +}

+ +

+ +// extract all digits at the beginning of the string

+ +var leadingDigits = regexp.MustCompile(`^(\d+)`)

+ +

+ +// MajorMinor parses a numeric major/minor version from the provided version info.

+ +// The minor version drops all characters after the first non-digit character:

+ +//   version.Info{Major:"1", Minor:"2+"} -> 1,2

+ +//   version.Info{Major:"1", Minor:"2.3-build4"} -> 1,2

+ +func MajorMinor(v version.Info) (int, int, error) {

+ +	major, err := strconv.Atoi(v.Major)

+ +	if err != nil {

+ +		return 0, 0, err

+ +	}

+ +	minor, err := strconv.Atoi(leadingDigits.FindString(v.Minor))

+ +	if err != nil {

+ +		return 0, 0, err

+ +	}

+ +	return major, minor, nil

+ +}

+ +

+ +// IsDeprecated returns true if obj implements APILifecycleDeprecated() and returns

+ +// a major/minor version that is non-zero and is <= the specified current major/minor version.

+ +func IsDeprecated(obj runtime.Object, currentMajor, currentMinor int) bool {

+ +	deprecated, isDeprecated := obj.(apiLifecycleDeprecated)

+ +	if !isDeprecated {

+ +		return false

+ +	}

+ +

+ +	deprecatedMajor, deprecatedMinor := deprecated.APILifecycleDeprecated()

+ +	// no deprecation version expressed

+ +	if deprecatedMajor == 0 && deprecatedMinor == 0 {

+ +		return false

+ +	}

+ +	// no current version info available

+ +	if currentMajor == 0 && currentMinor == 0 {

+ +		return true

+ +	}

+ +	// compare deprecation version to current version

+ +	if deprecatedMajor > currentMajor {

+ +		return false

+ +	}

+ +	if deprecatedMajor == currentMajor && deprecatedMinor > currentMinor {

+ +		return false

+ +	}

+ +	return true

+ +}

+ +

+ +// RemovedRelease returns the major/minor version in which the given object is unavailable (in the form "<major>.<minor>")

+ +// if the object implements APILifecycleRemoved() to indicate a non-zero removal version, and returns an empty string otherwise.

+ +func RemovedRelease(obj runtime.Object) string {

+ +	if removed, hasRemovalInfo := obj.(apiLifecycleRemoved); hasRemovalInfo {

+ +		removedMajor, removedMinor := removed.APILifecycleRemoved()

+ +		if removedMajor != 0 || removedMinor != 0 {

+ +			return fmt.Sprintf("%d.%d", removedMajor, removedMinor)

+ +		}

+ +	}

+ +	return ""

+ +}

+ +

+ +// WarningMessage returns a human-readable deprecation warning if the object implements APILifecycleDeprecated()

+ +// to indicate a non-zero deprecated major/minor version and has a populated GetObjectKind().GroupVersionKind().

+ +func WarningMessage(obj runtime.Object) string {

+ +	deprecated, isDeprecated := obj.(apiLifecycleDeprecated)

+ +	if !isDeprecated {

+ +		return ""

+ +	}

+ +

+ +	deprecatedMajor, deprecatedMinor := deprecated.APILifecycleDeprecated()

+ +	if deprecatedMajor == 0 && deprecatedMinor == 0 {

+ +		return ""

+ +	}

+ +

+ +	gvk := obj.GetObjectKind().GroupVersionKind()

+ +	if gvk.Empty() {

+ +		return ""

+ +	}

+ +	deprecationWarning := fmt.Sprintf("%s %s is deprecated in v%d.%d+", gvk.GroupVersion().String(), gvk.Kind, deprecatedMajor, deprecatedMinor)

+ +

+ +	if removed, hasRemovalInfo := obj.(apiLifecycleRemoved); hasRemovalInfo {

+ +		removedMajor, removedMinor := removed.APILifecycleRemoved()

+ +		if removedMajor != 0 || removedMinor != 0 {

+ +			deprecationWarning = deprecationWarning + fmt.Sprintf(", unavailable in v%d.%d+", removedMajor, removedMinor)

+ +		}

+ +	}

+ +

+ +	if replaced, hasReplacement := obj.(apiLifecycleReplacement); hasReplacement {

+ +		replacement := replaced.APILifecycleReplacement()

+ +		if !replacement.Empty() {

+ +			deprecationWarning = deprecationWarning + fmt.Sprintf("; use %s %s", replacement.GroupVersion().String(), replacement.Kind)

+ +		}

+ +	}

+ +

+ +	return deprecationWarning

+ +}

+ diff -Naur a/pkg/endpoints/deprecation/deprecation_test.go b/pkg/endpoints/deprecation/deprecation_test.go

+ --- a/pkg/endpoints/deprecation/deprecation_test.go	1969-12-31 16:00:00.000000000 -0800

+ +++ b/pkg/endpoints/deprecation/deprecation_test.go	2021-05-17 22:36:08.653804042 -0700

+ @@ -0,0 +1,319 @@

+ +/*

+ +Copyright 2020 The Kubernetes Authors.

+ +

+ +Licensed under the Apache License, Version 2.0 (the "License");

+ +you may not use this file except in compliance with the License.

+ +You may obtain a copy of the License at

+ +

+ +    http://www.apache.org/licenses/LICENSE-2.0

+ +

+ +Unless required by applicable law or agreed to in writing, software

+ +distributed under the License is distributed on an "AS IS" BASIS,

+ +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ +See the License for the specific language governing permissions and

+ +limitations under the License.

+ +*/

+ +

+ +package deprecation

+ +

+ +import (

+ +	"testing"

+ +

+ +	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

+ +	"k8s.io/apimachinery/pkg/runtime"

+ +	"k8s.io/apimachinery/pkg/runtime/schema"

+ +	"k8s.io/apimachinery/pkg/version"

+ +)

+ +

+ +func TestMajorMinor(t *testing.T) {

+ +	tests := []struct {

+ +		name        string

+ +		v           version.Info

+ +		expectMajor int

+ +		expectMinor int

+ +		expectErr   bool

+ +	}{

+ +		{

+ +			name:        "empty",

+ +			v:           version.Info{Major: "", Minor: ""},

+ +			expectMajor: 0,

+ +			expectMinor: 0,

+ +			expectErr:   true,

+ +		},

+ +		{

+ +			name:        "non-numeric major",

+ +			v:           version.Info{Major: "A", Minor: "0"},

+ +			expectMajor: 0,

+ +			expectMinor: 0,

+ +			expectErr:   true,

+ +		},

+ +		{

+ +			name:        "non-numeric minor",

+ +			v:           version.Info{Major: "1", Minor: "A"},

+ +			expectMajor: 0,

+ +			expectMinor: 0,

+ +			expectErr:   true,

+ +		},

+ +		{

+ +			name:        "valid",

+ +			v:           version.Info{Major: "1", Minor: "2"},

+ +			expectMajor: 1,

+ +			expectMinor: 2,

+ +			expectErr:   false,

+ +		},

+ +		{

+ +			name:        "valid zero",

+ +			v:           version.Info{Major: "0", Minor: "0"},

+ +			expectMajor: 0,

+ +			expectMinor: 0,

+ +			expectErr:   false,

+ +		},

+ +		{

+ +			name:        "valid zero decimal",

+ +			v:           version.Info{Major: "01", Minor: "02"},

+ +			expectMajor: 1,

+ +			expectMinor: 2,

+ +			expectErr:   false,

+ +		},

+ +		{

+ +			name:        "valid with extra minor",

+ +			v:           version.Info{Major: "1", Minor: "2+"},

+ +			expectMajor: 1,

+ +			expectMinor: 2,

+ +			expectErr:   false,

+ +		},

+ +		{

+ +			name:        "valid with extra minor",

+ +			v:           version.Info{Major: "1", Minor: "2.3"},

+ +			expectMajor: 1,

+ +			expectMinor: 2,

+ +			expectErr:   false,

+ +		},

+ +	}

+ +	for _, tt := range tests {

+ +		t.Run(tt.name, func(t *testing.T) {

+ +			major, minor, err := MajorMinor(tt.v)

+ +			if (err != nil) != tt.expectErr {

+ +				t.Errorf("MajorMinor() error = %v, wantErr %v", err, tt.expectErr)

+ +				return

+ +			}

+ +			if major != tt.expectMajor {

+ +				t.Errorf("MajorMinor() major = %v, want %v", major, tt.expectMajor)

+ +			}

+ +			if minor != tt.expectMinor {

+ +				t.Errorf("MajorMinor() minor = %v, want %v", minor, tt.expectMinor)

+ +			}

+ +		})

+ +	}

+ +}

+ +

+ +func TestIsDeprecated(t *testing.T) {

+ +	tests := []struct {

+ +		name string

+ +

+ +		obj          runtime.Object

+ +		currentMajor int

+ +		currentMinor int

+ +

+ +		want bool

+ +	}{

+ +		{

+ +			name:         "no interface",

+ +			obj:          &fakeObject{},

+ +			currentMajor: 0,

+ +			currentMinor: 0,

+ +			want:         false,

+ +		},

+ +		{

+ +			name:         "interface, zero-value",

+ +			obj:          &fakeDeprecatedObject{},

+ +			currentMajor: 0,

+ +			currentMinor: 0,

+ +			want:         false,

+ +		},

+ +		{

+ +			name:         "interface, non-zero-value, no current value",

+ +			obj:          &fakeDeprecatedObject{major: 10, minor: 20},

+ +			currentMajor: 0,

+ +			currentMinor: 0,

+ +			want:         true,

+ +		},

+ +		{

+ +			name:         "interface, non-zero-value matching major, minor",

+ +			obj:          &fakeDeprecatedObject{major: 10, minor: 20},

+ +			currentMajor: 10,

+ +			currentMinor: 20,

+ +			want:         true,

+ +		},

+ +		{

+ +			name:         "interface, non-zero-value after major, after minor",

+ +			obj:          &fakeDeprecatedObject{major: 10, minor: 20},

+ +			currentMajor: 9,

+ +			currentMinor: 19,

+ +			want:         false,

+ +		},

+ +		{

+ +			name:         "interface, non-zero-value after major, before minor",

+ +			obj:          &fakeDeprecatedObject{major: 10, minor: 20},

+ +			currentMajor: 9,

+ +			currentMinor: 21,

+ +			want:         false,

+ +		},

+ +		{

+ +			name:         "interface, non-zero-value before major, after minor",

+ +			obj:          &fakeDeprecatedObject{major: 10, minor: 20},

+ +			currentMajor: 11,

+ +			currentMinor: 19,

+ +			want:         true,

+ +		},

+ +		{

+ +			name:         "interface, non-zero-value before major, before minor",

+ +			obj:          &fakeDeprecatedObject{major: 10, minor: 20},

+ +			currentMajor: 11,

+ +			currentMinor: 21,

+ +			want:         true,

+ +		},

+ +	}

+ +	for _, tt := range tests {

+ +		t.Run(tt.name, func(t *testing.T) {

+ +			if got := IsDeprecated(tt.obj, tt.currentMajor, tt.currentMinor); got != tt.want {

+ +				t.Errorf("IsDeprecated() = %v, want %v", got, tt.want)

+ +			}

+ +		})

+ +	}

+ +}

+ +

+ +func TestRemovedRelease(t *testing.T) {

+ +	tests := []struct {

+ +		name string

+ +		obj  runtime.Object

+ +		want string

+ +	}{

+ +		{

+ +			name: "no interface",

+ +			obj:  &fakeObject{},

+ +			want: "",

+ +		},

+ +		{

+ +			name: "interface, zero-value",

+ +			obj:  &fakeRemovedObject{removedMajor: 0, removedMinor: 0},

+ +			want: "",

+ +		},

+ +		{

+ +			name: "interface, non-zero major",

+ +			obj:  &fakeRemovedObject{removedMajor: 1, removedMinor: 0},

+ +			want: "1.0",

+ +		},

+ +		{

+ +			name: "interface, non-zero minor",

+ +			obj:  &fakeRemovedObject{removedMajor: 0, removedMinor: 1},

+ +			want: "0.1",

+ +		},

+ +		{

+ +			name: "interface, non-zero",

+ +			obj:  &fakeRemovedObject{removedMajor: 1, removedMinor: 2},

+ +			want: "1.2",

+ +		},

+ +	}

+ +	for _, tt := range tests {

+ +		t.Run(tt.name, func(t *testing.T) {

+ +			if got := RemovedRelease(tt.obj); got != tt.want {

+ +				t.Errorf("RemovedRelease() = %v, want %v", got, tt.want)

+ +			}

+ +		})

+ +	}

+ +}

+ +

+ +func TestWarningMessage(t *testing.T) {

+ +	tests := []struct {

+ +		name string

+ +		obj  runtime.Object

+ +		gvk  schema.GroupVersionKind

+ +		want string

+ +	}{

+ +		{

+ +			name: "no interface, zero-value",

+ +			obj:  &fakeObject{},

+ +			want: "",

+ +		},

+ +		{

+ +			name: "deprecated interface, zero-value",

+ +			obj:  &fakeDeprecatedObject{major: 0, minor: 0},

+ +			gvk:  schema.GroupVersionKind{},

+ +			want: "",

+ +		},

+ +		{

+ +			name: "deprecated interface, non-zero-value",

+ +			obj:  &fakeDeprecatedObject{major: 1, minor: 2},

+ +			gvk:  schema.GroupVersionKind{Group: "mygroup", Version: "v1", Kind: "MyKind"},

+ +			want: "mygroup/v1 MyKind is deprecated in v1.2+",

+ +		},

+ +		{

+ +			name: "removed interface, zero-value removal version",

+ +			obj:  &fakeRemovedObject{major: 1, minor: 2},

+ +			gvk:  schema.GroupVersionKind{Group: "mygroup", Version: "v1", Kind: "MyKind"},

+ +			want: "mygroup/v1 MyKind is deprecated in v1.2+",

+ +		},

+ +		{

+ +			name: "removed interface, non-zero-value removal version",

+ +			obj:  &fakeRemovedObject{major: 1, minor: 2, removedMajor: 3, removedMinor: 4},

+ +			gvk:  schema.GroupVersionKind{Group: "mygroup", Version: "v1", Kind: "MyKind"},

+ +			want: "mygroup/v1 MyKind is deprecated in v1.2+, unavailable in v3.4+",

+ +		},

+ +		{

+ +			name: "replaced interface, zero-value replacement",

+ +			obj:  &fakeReplacedObject{major: 1, minor: 2, removedMajor: 3, removedMinor: 4},

+ +			gvk:  schema.GroupVersionKind{Group: "mygroup", Version: "v1", Kind: "MyKind"},

+ +			want: "mygroup/v1 MyKind is deprecated in v1.2+, unavailable in v3.4+",

+ +		},

+ +		{

+ +			name: "replaced interface, non-zero-value replacement",

+ +			obj:  &fakeReplacedObject{major: 1, minor: 2, removedMajor: 3, removedMinor: 4, replacement: schema.GroupVersionKind{Group: "anothergroup", Version: "v2", Kind: "AnotherKind"}},

+ +			gvk:  schema.GroupVersionKind{Group: "mygroup", Version: "v1", Kind: "MyKind"},

+ +			want: "mygroup/v1 MyKind is deprecated in v1.2+, unavailable in v3.4+; use anothergroup/v2 AnotherKind",

+ +		},

+ +	}

+ +	for _, tt := range tests {

+ +		t.Run(tt.name, func(t *testing.T) {

+ +			tt.obj.GetObjectKind().SetGroupVersionKind(tt.gvk)

+ +			if got := WarningMessage(tt.obj); got != tt.want {

+ +				t.Errorf("WarningMessage() = %v, want %v", got, tt.want)

+ +			}

+ +		})

+ +	}

+ +}

+ +

+ +type fakeObject struct {

+ +	unstructured.Unstructured

+ +}

+ +type fakeDeprecatedObject struct {

+ +	unstructured.Unstructured

+ +	major int

+ +	minor int

+ +}

+ +

+ +func (f *fakeDeprecatedObject) APILifecycleDeprecated() (int, int) { return f.major, f.minor }

+ +

+ +type fakeRemovedObject struct {

+ +	unstructured.Unstructured

+ +	major        int

+ +	minor        int

+ +	removedMajor int

+ +	removedMinor int

+ +}

+ +

+ +func (f *fakeRemovedObject) APILifecycleDeprecated() (int, int) { return f.major, f.minor }

+ +func (f *fakeRemovedObject) APILifecycleRemoved() (int, int)    { return f.removedMajor, f.removedMinor }

+ +

+ +type fakeReplacedObject struct {

+ +	unstructured.Unstructured

+ +	major        int

+ +	minor        int

+ +	replacement  schema.GroupVersionKind

+ +	removedMajor int

+ +	removedMinor int

+ +}

+ +

+ +func (f *fakeReplacedObject) APILifecycleDeprecated() (int, int)               { return f.major, f.minor }

+ +func (f *fakeReplacedObject) APILifecycleRemoved() (int, int)                  { return f.removedMajor, f.removedMinor }

+ +func (f *fakeReplacedObject) APILifecycleReplacement() schema.GroupVersionKind { return f.replacement }

+ diff -Naur a/pkg/endpoints/installer.go b/pkg/endpoints/installer.go

+ --- a/pkg/endpoints/installer.go	2020-09-16 13:23:42.000000000 -0700

+ +++ b/pkg/endpoints/installer.go	2021-05-17 22:36:08.655804062 -0700

+ @@ -33,6 +33,7 @@

+  	"k8s.io/apimachinery/pkg/runtime/schema"

+  	"k8s.io/apimachinery/pkg/types"

+  	"k8s.io/apiserver/pkg/admission"

+ +	"k8s.io/apiserver/pkg/endpoints/deprecation"

+  	"k8s.io/apiserver/pkg/endpoints/discovery"

+  	"k8s.io/apiserver/pkg/endpoints/handlers"

+  	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"

+ @@ -41,6 +42,7 @@

+  	"k8s.io/apiserver/pkg/features"

+  	"k8s.io/apiserver/pkg/registry/rest"

+  	utilfeature "k8s.io/apiserver/pkg/util/feature"

+ +	versioninfo "k8s.io/component-base/version"

+  )

+  

+  const (

file modified
+8 -1
@@ -23,7 +23,7 @@ 

  %global gosupfiles      ${example[@]}

  

  Name:           %{goname}

- Release:        2%{?dist}

+ Release:        3%{?dist}

  Summary:        Library for writing a Kubernetes-style API server

  

  # Upstream license specification: Apache-2.0
@@ -33,6 +33,9 @@ 

  Patch0:         0001-Use-klog-v2.patch

  # Backport for sigs.k8s.io/apiserver-network-proxy

  Patch1:         https://github.com/kubernetes/apiserver/commit/c78dd46c272b441707c6c0d5222802eba8d4edba.patch#/0001-fix-API-change-in-apiserver-network-proxy.patch

+ # Backport for github.com/helm/helm

+ # From https://github.com/kubernetes/apiserver/commit/7add3b408a13119bdb9df97d1946f9a4a1cb36e7

+ Patch2:        0001-apiserver-add-warnings-for-deprecated-APIs.patch

  

  BuildRequires:  golang(bitbucket.org/ww/goautoneg)

  BuildRequires:  golang(github.com/coreos/go-oidc)
@@ -204,6 +207,7 @@ 

  %goprep

  %patch0 -p1

  %patch1 -p1

+ %patch2 -p1

  sed -i "s|github.com/munnerz/goautoneg|bitbucket.org/ww/goautoneg|" $(find . -name "*.go")

  sed -i "s|github.com/googleapis/gnostic/OpenAPIv2|github.com/googleapis/gnostic/openapiv2|" $(find . -name "*.go")

  sed -i "s|k8s.io/klog|k8s.io/klog/v2|" $(find . -name "*.go")
@@ -234,6 +238,9 @@ 

  %gopkgfiles

  

  %changelog

+ * Mon May 17 2021 Davide Cavalca <dcavalca@fedoraproject.org> - 1.18.9-3

+ - Backport for github.com/helm/helm

+ 

  * Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.18.9-2

  - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild

  

I was looking at what it'd take to package Helm, and one missing provides is golang(k8s.io/apiserver/pkg/endpoints/deprecation) (required by https://github.com/helm/helm/blob/v3.5.4/pkg/lint/rules/deprecations.go). This comes from https://github.com/kubernetes/apiserver/commit/7add3b408a13119bdb9df97d1946f9a4a1cb36e7 which was added in 1.19.0, so I'm backporting it here (as updating this package seems non-trivial). With this applied I get:

golang(k8s.io/apiserver/pkg/endpoints/deprecation) = 1.18.9-3.fc35
golang(k8s.io/apiserver/pkg/endpoints/deprecation)(tag=kubernetes-1.18.9) = 1.18.9-3.fc35

in Provides, which should do the trick.

I'd really prefer to update our K8S stack but I really don't have the time now.

Pull-Request has been merged by eclipseo

3 years ago