Remove selflink setting from apiserver

Kubernetes-commit: 0a674d3ed92ce6384e2e8018ca63cf2925474146
This commit is contained in:
Wojciech Tyczyński 2022-01-13 15:29:21 +01:00 committed by Kubernetes Publisher
parent 9edad29ced
commit 38d7003d33
6 changed files with 5 additions and 356 deletions

View File

@ -1444,65 +1444,6 @@ func TestSelfLinkSkipsEmptyName(t *testing.T) {
}
}
func TestRootSelfLink(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemoveSelfLink, false)()
storage := map[string]rest.Storage{}
simpleStorage := GetWithOptionsRootRESTStorage{
SimpleTypedStorage: &SimpleTypedStorage{
baseType: &genericapitesting.SimpleRoot{}, // a root scoped type
item: &genericapitesting.SimpleRoot{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Other: "foo",
},
},
takesPath: "atAPath",
}
storage["simple"] = &simpleStorage
storage["simple/sub"] = &simpleStorage
handler := handle(storage)
server := httptest.NewServer(handler)
defer server.Close()
testCases := []struct {
url string
selfLink string
}{
{
url: server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo",
selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo",
},
{
url: server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo/sub",
selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo/sub",
},
}
for _, test := range testCases {
resp, err := http.Get(test.url)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusOK, resp)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
t.Logf("Data: %s", string(body))
}
var out genericapitesting.SimpleRoot
if _, err := extractBody(resp, &out); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if out.SelfLink != test.selfLink {
t.Errorf("unexpected self link: %#v", out.SelfLink)
}
}
}
func TestMetadata(t *testing.T) {
simpleStorage := &MetadataRESTStorage{&SimpleRESTStorage{}, []string{"text/plain"}}
h := handle(map[string]rest.Storage{"simple": simpleStorage})
@ -3885,15 +3826,6 @@ type setTestSelfLinker struct {
func (s *setTestSelfLinker) Namespace(runtime.Object) (string, error) { return s.namespace, s.err }
func (s *setTestSelfLinker) Name(runtime.Object) (string, error) { return s.name, s.err }
func (s *setTestSelfLinker) SelfLink(runtime.Object) (string, error) { return "", s.err }
func (s *setTestSelfLinker) SetSelfLink(obj runtime.Object, selfLink string) error {
if e, a := s.expectedSet, selfLink; e != a {
if !s.alternativeSet.Has(a) {
s.t.Errorf("expected '%v', got '%v'", e, a)
}
}
s.called = true
return s.err
}
func TestCreate(t *testing.T) {
storage := SimpleRESTStorage{

View File

@ -19,8 +19,6 @@ package handlers
import (
"fmt"
"net/http"
"net/url"
"strings"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
@ -38,14 +36,6 @@ type ScopeNamer interface {
// ObjectName returns the namespace and name from an object if they exist, or an error if the object
// does not support names.
ObjectName(obj runtime.Object) (namespace, name string, err error)
// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
// does not support selfLinks.
SetSelfLink(obj runtime.Object, url string) error
// GenerateLink creates an encoded URI for a given runtime object that represents the canonical path
// and query.
GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error)
// GenerateListLink creates an encoded URI for a list that represents the canonical path and query.
GenerateListLink(req *http.Request) (uri string, err error)
}
type ContextBasedNaming struct {
@ -59,10 +49,6 @@ type ContextBasedNaming struct {
// ContextBasedNaming implements ScopeNamer
var _ ScopeNamer = ContextBasedNaming{}
func (n ContextBasedNaming) SetSelfLink(obj runtime.Object, url string) error {
return n.SelfLinker.SetSelfLink(obj, url)
}
func (n ContextBasedNaming) Namespace(req *http.Request) (namespace string, err error) {
requestInfo, ok := request.RequestInfoFrom(req.Context())
if !ok {
@ -83,55 +69,6 @@ func (n ContextBasedNaming) Name(req *http.Request) (namespace, name string, err
return requestInfo.Namespace, requestInfo.Name, nil
}
// fastURLPathEncode encodes the provided path as a URL path
func fastURLPathEncode(path string) string {
for _, r := range []byte(path) {
switch {
case r >= '-' && r <= '9', r >= 'A' && r <= 'Z', r >= 'a' && r <= 'z':
// characters within this range do not require escaping
default:
var u url.URL
u.Path = path
return u.EscapedPath()
}
}
return path
}
func (n ContextBasedNaming) GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error) {
namespace, name, err := n.ObjectName(obj)
if err == errEmptyName && len(requestInfo.Name) > 0 {
name = requestInfo.Name
} else if err != nil {
return "", err
}
if len(namespace) == 0 && len(requestInfo.Namespace) > 0 {
namespace = requestInfo.Namespace
}
if n.ClusterScoped {
return n.SelfLinkPathPrefix + url.QueryEscape(name) + n.SelfLinkPathSuffix, nil
}
builder := strings.Builder{}
builder.Grow(len(n.SelfLinkPathPrefix) + len(namespace) + len(requestInfo.Resource) + len(name) + len(n.SelfLinkPathSuffix) + 8)
builder.WriteString(n.SelfLinkPathPrefix)
builder.WriteString(namespace)
builder.WriteByte('/')
builder.WriteString(requestInfo.Resource)
builder.WriteByte('/')
builder.WriteString(name)
builder.WriteString(n.SelfLinkPathSuffix)
return fastURLPathEncode(builder.String()), nil
}
func (n ContextBasedNaming) GenerateListLink(req *http.Request) (uri string, err error) {
if len(req.URL.RawPath) > 0 {
return req.URL.RawPath, nil
}
return fastURLPathEncode(req.URL.Path), nil
}
func (n ContextBasedNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) {
name, err = n.SelfLinker.Name(obj)
if err != nil {

View File

@ -1,179 +0,0 @@
/*
Copyright 2017 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 handlers
import (
"math/rand"
"net/url"
"testing"
fuzz "github.com/google/gofuzz"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/endpoints/request"
)
func TestGenerateLink(t *testing.T) {
testCases := []struct {
name string
requestInfo *request.RequestInfo
obj runtime.Object
expect string
expectErr bool
clusterScoped bool
}{
{
name: "obj has more priority than requestInfo",
requestInfo: &request.RequestInfo{
Name: "should-not-use",
Namespace: "should-not-use",
Resource: "pod",
},
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "should-use", Namespace: "should-use"}},
expect: "/api/v1/should-use/pod/should-use",
expectErr: false,
clusterScoped: false,
},
{
name: "hit errEmptyName",
requestInfo: &request.RequestInfo{
Name: "should-use",
Namespace: "should-use",
Resource: "pod",
},
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "should-not-use"}},
expect: "/api/v1/should-use/pod/should-use",
expectErr: false,
clusterScoped: false,
},
{
name: "use namespace of requestInfo if obj namespace is empty",
requestInfo: &request.RequestInfo{
Name: "should-not-use",
Namespace: "should-use",
Resource: "pod",
},
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "should-use"}},
expect: "/api/v1/should-use/pod/should-use",
expectErr: false,
clusterScoped: false,
},
{
name: "hit error",
requestInfo: &request.RequestInfo{
Name: "",
Namespace: "",
Resource: "pod",
},
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{}},
expect: "name must be provided",
expectErr: true,
clusterScoped: false,
},
{
name: "cluster scoped",
requestInfo: &request.RequestInfo{
Name: "only-name",
Namespace: "should-not-use",
Resource: "pod",
},
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{}},
expect: "/api/v1/only-name",
expectErr: false,
clusterScoped: true,
},
}
for _, test := range testCases {
n := ContextBasedNaming{
SelfLinker: meta.NewAccessor(),
SelfLinkPathPrefix: "/api/v1/",
ClusterScoped: test.clusterScoped,
}
uri, err := n.GenerateLink(test.requestInfo, test.obj)
if uri != test.expect && err.Error() != test.expect {
if test.expectErr {
t.Fatalf("%s: unexpected non-error: %v", test.name, err)
} else {
t.Fatalf("%s: expected: %v, but got: %v", test.name, test.expect, uri)
}
}
}
}
func Test_fastURLPathEncode_fuzz(t *testing.T) {
specialCases := []string{"/", "//", ".", "*", "/abc%"}
for _, test := range specialCases {
got := fastURLPathEncode(test)
u := url.URL{Path: test}
expected := u.EscapedPath()
if got != expected {
t.Errorf("%q did not match %q", got, expected)
}
}
f := fuzz.New().Funcs(
func(s *string, c fuzz.Continue) {
*s = randString(c.Rand)
},
)
for i := 0; i < 2000; i++ {
var test string
f.Fuzz(&test)
got := fastURLPathEncode(test)
u := url.URL{Path: test}
expected := u.EscapedPath()
if got != expected {
t.Errorf("%q did not match %q", got, expected)
}
}
}
// Unicode range fuzzer from github.com/google/gofuzz/fuzz.go
type charRange struct {
first, last rune
}
var unicodeRanges = []charRange{
{0x00, 0x255},
{' ', '~'}, // ASCII characters
{'\u00a0', '\u02af'}, // Multi-byte encoded characters
{'\u4e00', '\u9fff'}, // Common CJK (even longer encodings)
}
// randString makes a random string up to 20 characters long. The returned string
// may include a variety of (valid) UTF-8 encodings.
func randString(r *rand.Rand) string {
n := r.Intn(20)
runes := make([]rune, n)
for i := range runes {
runes[i] = unicodeRanges[r.Intn(len(unicodeRanges))].choose(r)
}
return string(runes)
}
// choose returns a random unicode character from the given range, using the
// given randomness source.
func (r *charRange) choose(rand *rand.Rand) rune {
count := int64(r.last - r.first)
return r.first + rune(rand.Int63n(count))
}

View File

@ -77,11 +77,6 @@ type mockNamer struct{}
func (*mockNamer) Namespace(_ *http.Request) (string, error) { return "", nil }
func (*mockNamer) Name(_ *http.Request) (string, string, error) { return "", "", nil }
func (*mockNamer) ObjectName(_ runtime.Object) (string, string, error) { return "", "", nil }
func (*mockNamer) SetSelfLink(_ runtime.Object, _ string) error { return nil }
func (*mockNamer) GenerateLink(_ *request.RequestInfo, _ runtime.Object) (string, error) {
return "", nil
}
func (*mockNamer) GenerateListLink(_ *http.Request) (string, error) { return "", nil }
func TestCacheableObject(t *testing.T) {
pomGVK := metav1.SchemeGroupVersion.WithKind("PartialObjectMetadata")

View File

@ -49,7 +49,6 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/warning"
"k8s.io/klog/v2"
)
const (
@ -260,18 +259,6 @@ func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime
return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v (%s)", objGVK.Kind, baseErr, summary))
}
// setSelfLink sets the self link of an object (or the child items in a list) to the base URL of the request
// plus the path and query generated by the provided linkFunc
func setSelfLink(obj runtime.Object, requestInfo *request.RequestInfo, namer ScopeNamer) error {
// TODO: SelfLink generation should return a full URL?
uri, err := namer.GenerateLink(requestInfo, obj)
if err != nil {
return nil
}
return namer.SetSelfLink(obj, uri)
}
func hasUID(obj runtime.Object) (bool, error) {
if obj == nil {
return false, nil
@ -387,29 +374,22 @@ func setObjectSelfLink(ctx context.Context, obj runtime.Object, req *http.Reques
// check via reflection, but as we move away from reflection we require that you not only carry Items but
// ListMeta into order to be identified as a list.
if !meta.IsListType(obj) {
requestInfo, ok := request.RequestInfoFrom(ctx)
_, ok := request.RequestInfoFrom(ctx)
if !ok {
return fmt.Errorf("missing requestInfo")
}
return setSelfLink(obj, requestInfo, namer)
return nil
}
uri, err := namer.GenerateListLink(req)
if err != nil {
return err
}
if err := namer.SetSelfLink(obj, uri); err != nil {
klog.V(4).InfoS("Unable to set self link on object", "error", err)
}
requestInfo, ok := request.RequestInfoFrom(ctx)
_, ok := request.RequestInfoFrom(ctx)
if !ok {
return fmt.Errorf("missing requestInfo")
}
count := 0
err = meta.EachListItem(obj, func(obj runtime.Object) error {
err := meta.EachListItem(obj, func(obj runtime.Object) error {
count++
return setSelfLink(obj, requestInfo, namer)
return nil
})
if count == 0 {

View File

@ -300,22 +300,6 @@ func (p *testNamer) ObjectName(obj runtime.Object) (namespace, name string, err
return p.namespace, p.name, nil
}
// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
// does not support selfLinks.
func (p *testNamer) SetSelfLink(obj runtime.Object, url string) error {
return errors.New("not implemented")
}
// GenerateLink creates a path and query for a given runtime object that represents the canonical path.
func (p *testNamer) GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error) {
return "", errors.New("not implemented")
}
// GenerateListLink creates a path and query for a list that represents the canonical path.
func (p *testNamer) GenerateListLink(req *http.Request) (uri string, err error) {
return "", errors.New("not implemented")
}
type patchTestCase struct {
name string