Vendor: Update k8s version

Signed-off-by: Michał Żyłowski <michal.zylowski@intel.com>
This commit is contained in:
Michał Żyłowski 2017-02-03 14:41:32 +01:00
parent dfa93414c5
commit 52baf68d50
3756 changed files with 113013 additions and 92675 deletions

View file

@ -5,110 +5,12 @@ licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"config.go",
"config_selfclient.go",
"discovery.go",
"doc.go",
"genericapiserver.go",
"healthz.go",
"hooks.go",
"resource_config.go",
"resource_encoding_config.go",
"serve.go",
"storage_factory.go",
],
srcs = ["doc.go"],
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/auth/handlers:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/genericapiserver/api:go_default_library",
"//pkg/genericapiserver/api/filters:go_default_library",
"//pkg/genericapiserver/api/openapi:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/genericapiserver/authenticator:go_default_library",
"//pkg/genericapiserver/authorizer:go_default_library",
"//pkg/genericapiserver/filters:go_default_library",
"//pkg/genericapiserver/mux:go_default_library",
"//pkg/genericapiserver/options:go_default_library",
"//pkg/genericapiserver/routes:go_default_library",
"//pkg/storage/storagebackend:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/version:go_default_library",
"//vendor:github.com/coreos/go-systemd/daemon",
"//vendor:github.com/emicklei/go-restful",
"//vendor:github.com/emicklei/go-restful/swagger",
"//vendor:github.com/go-openapi/spec",
"//vendor:github.com/golang/glog",
"//vendor:github.com/pborman/uuid",
"//vendor:github.com/pkg/errors",
"//vendor:gopkg.in/natefinch/lumberjack.v2",
"//vendor:k8s.io/apimachinery/pkg/apimachinery",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/openapi",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apimachinery/pkg/util/validation",
"//vendor:k8s.io/apiserver/pkg/authentication/authenticator",
"//vendor:k8s.io/apiserver/pkg/authentication/request/union",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/authorization/union",
"//vendor:k8s.io/apiserver/pkg/healthz",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
go_test(
name = "go_default_test",
srcs = [
"genericapiserver_test.go",
"resource_config_test.go",
"serve_test.go",
"server_run_options_test.go",
"storage_factory_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/generated/openapi:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/genericapiserver/options:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//pkg/storage/storagebackend:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/util/config:go_default_library",
"//pkg/version:go_default_library",
"//vendor:github.com/go-openapi/spec",
"//vendor:github.com/stretchr/testify/assert",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apimachinery",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
filegroup(
@ -122,14 +24,11 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/genericapiserver/api:all-srcs",
"//pkg/genericapiserver/authenticator:all-srcs",
"//pkg/genericapiserver/authorizer:all-srcs",
"//pkg/genericapiserver/filters:all-srcs",
"//pkg/genericapiserver/mux:all-srcs",
"//pkg/genericapiserver/openapi:all-srcs",
"//pkg/genericapiserver/options:all-srcs",
"//pkg/genericapiserver/routes:all-srcs",
"//pkg/genericapiserver/endpoints:all-srcs",
"//pkg/genericapiserver/registry:all-srcs",
"//pkg/genericapiserver/server:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -1,42 +0,0 @@
/*
Copyright 2014 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 rest
import (
"k8s.io/apimachinery/pkg/runtime"
)
// ObjectFunc is a function to act on a given object. An error may be returned
// if the hook cannot be completed. An ObjectFunc may transform the provided
// object.
type ObjectFunc func(obj runtime.Object) error
// AllFuncs returns an ObjectFunc that attempts to run all of the provided functions
// in order, returning early if there are any errors.
func AllFuncs(fns ...ObjectFunc) ObjectFunc {
return func(obj runtime.Object) error {
for _, fn := range fns {
if fn == nil {
continue
}
if err := fn(obj); err != nil {
return err
}
}
return nil
}
}

View file

@ -16,7 +16,6 @@ go_library(
],
tags = ["automanaged"],
deps = [
"//pkg/util/cert:go_default_library",
"//plugin/pkg/auth/authenticator/token/webhook:go_default_library",
"//vendor:github.com/go-openapi/spec",
"//vendor:k8s.io/apiserver/pkg/authentication/authenticator",
@ -29,6 +28,7 @@ go_library(
"//vendor:k8s.io/apiserver/pkg/authentication/token/tokenfile",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/client-go/kubernetes/typed/authentication/v1beta1",
"//vendor:k8s.io/client-go/util/cert",
],
)

View file

@ -33,7 +33,7 @@ import (
"k8s.io/apiserver/pkg/authentication/user"
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
"k8s.io/kubernetes/pkg/util/cert"
"k8s.io/client-go/util/cert"
webhooktoken "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/webhook"
)

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
// Package genericapiserver contains code to setup a generic kubernetes-like API server.
// This does not contain any kubernetes API specific code.
// This does not contain any Kubernetes specific API types.
// Note that this is a work in progress. We are pulling out generic code (specifically from
// pkg/master) here.
// We plan to move this package into a separate repo on github once it is done.

View file

@ -8,37 +8,6 @@ load(
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"apiserver.go",
"discovery.go",
"doc.go",
"groupversion.go",
"installer.go",
],
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/genericapiserver/api/handlers:go_default_library",
"//pkg/genericapiserver/api/handlers/responsewriters:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//vendor:github.com/emicklei/go-restful",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/conversion",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
"//vendor:k8s.io/apiserver/pkg/handlers/negotiation",
"//vendor:k8s.io/apiserver/pkg/metrics",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
go_test(
name = "go_default_test",
srcs = [
@ -50,23 +19,23 @@ go_test(
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/filters:go_default_library",
"//pkg/genericapiserver/api/handlers:go_default_library",
"//pkg/genericapiserver/api/handlers/responsewriters:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/genericapiserver/api/testing:go_default_library",
"//pkg/genericapiserver/endpoints/filters:go_default_library",
"//pkg/genericapiserver/endpoints/handlers:go_default_library",
"//pkg/genericapiserver/endpoints/handlers/responsewriters:go_default_library",
"//pkg/genericapiserver/endpoints/testing:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//plugin/pkg/admission/admit:go_default_library",
"//plugin/pkg/admission/deny:go_default_library",
"//vendor:github.com/emicklei/go-restful",
"//vendor:golang.org/x/net/websocket",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/internalversion",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/fields",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
@ -76,7 +45,39 @@ go_test(
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/admission",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)
go_library(
name = "go_default_library",
srcs = [
"apiserver.go",
"discovery.go",
"doc.go",
"groupversion.go",
"installer.go",
],
tags = ["automanaged"],
deps = [
"//pkg/apis/extensions:go_default_library",
"//pkg/genericapiserver/endpoints/handlers:go_default_library",
"//pkg/genericapiserver/endpoints/handlers/responsewriters:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//vendor:github.com/emicklei/go-restful",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/conversion",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
"//vendor:k8s.io/apiserver/pkg/admission",
"//vendor:k8s.io/apiserver/pkg/endpoints/handlers/negotiation",
"//vendor:k8s.io/apiserver/pkg/endpoints/metrics",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)
@ -91,11 +92,10 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/genericapiserver/api/filters:all-srcs",
"//pkg/genericapiserver/api/handlers:all-srcs",
"//pkg/genericapiserver/api/openapi:all-srcs",
"//pkg/genericapiserver/api/rest:all-srcs",
"//pkg/genericapiserver/api/testing:all-srcs",
"//pkg/genericapiserver/endpoints/filters:all-srcs",
"//pkg/genericapiserver/endpoints/handlers:all-srcs",
"//pkg/genericapiserver/endpoints/openapi:all-srcs",
"//pkg/genericapiserver/endpoints/testing:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -8,7 +8,6 @@ reviewers:
- lavalamp
- smarterclayton
- wojtek-t
- bgrant0607
- deads2k
- derekwaynecarr
- caesarxuchao

View file

@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"k8s.io/apiserver/pkg/metrics"
"k8s.io/apiserver/pkg/endpoints/metrics"
)
func init() {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"bytes"
@ -36,24 +36,24 @@ import (
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/api"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/api/v1"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/fields"
genericapifilters "k8s.io/kubernetes/pkg/genericapiserver/api/filters"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
genericapitesting "k8s.io/kubernetes/pkg/genericapiserver/api/testing"
genericapifilters "k8s.io/kubernetes/pkg/genericapiserver/endpoints/filters"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
genericapitesting "k8s.io/kubernetes/pkg/genericapiserver/endpoints/testing"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
"k8s.io/kubernetes/plugin/pkg/admission/admit"
"k8s.io/kubernetes/plugin/pkg/admission/deny"
@ -118,20 +118,11 @@ func newMapper() *meta.DefaultRESTMapper {
}
func addGrouplessTypes() {
type ListOptions struct {
Object runtime.Object
metav1.TypeMeta `json:",inline"`
LabelSelector string `json:"labelSelector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty"`
Watch bool `json:"watch,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(grouplessGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &ListOptions{}, &metav1.ExportOptions{},
&v1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ListOptions{}, &metav1.ExportOptions{},
&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
api.Scheme.AddKnownTypes(grouplessInternalGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &api.ListOptions{}, &metav1.ExportOptions{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
}
@ -146,12 +137,12 @@ func addTestTypes() {
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(testGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &ListOptions{}, &metav1.ExportOptions{},
&v1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&SimpleXGSubresource{})
api.Scheme.AddKnownTypes(testGroupVersion, &v1.Pod{})
api.Scheme.AddKnownTypes(testInternalGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &api.ListOptions{}, &metav1.ExportOptions{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&SimpleXGSubresource{})
api.Scheme.AddKnownTypes(testInternalGroupVersion, &api.Pod{})
@ -164,18 +155,9 @@ func addTestTypes() {
}
func addNewTestTypes() {
type ListOptions struct {
Object runtime.Object
metav1.TypeMeta `json:",inline"`
LabelSelector string `json:"labelSelector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty"`
Watch bool `json:"watch,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(newGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &ListOptions{}, &metav1.ExportOptions{},
&api.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&v1.Pod{},
)
metav1.AddToGroupVersion(api.Scheme, newGroupVersion)
@ -314,7 +296,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission.
}
func TestSimpleSetupRight(t *testing.T) {
s := &genericapitesting.Simple{ObjectMeta: apiv1.ObjectMeta{Name: "aName"}}
s := &genericapitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "aName"}}
wire, err := runtime.Encode(codec, s)
if err != nil {
t.Fatal(err)
@ -356,7 +338,7 @@ type SimpleRESTStorage struct {
stream *SimpleStream
deleted string
deleteOptions *api.DeleteOptions
deleteOptions *metav1.DeleteOptions
actualNamespace string
namespacePresent bool
@ -394,7 +376,7 @@ func (storage *SimpleRESTStorage) Export(ctx request.Context, name string, opts
return obj, storage.errors["export"]
}
func (storage *SimpleRESTStorage) List(ctx request.Context, options *api.ListOptions) (runtime.Object, error) {
func (storage *SimpleRESTStorage) List(ctx request.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
storage.checkContext(ctx)
result := &genericapitesting.SimpleList{
Items: storage.list,
@ -457,7 +439,7 @@ func (storage *SimpleRESTStorage) checkContext(ctx request.Context) {
storage.actualNamespace, storage.namespacePresent = request.NamespaceFrom(ctx)
}
func (storage *SimpleRESTStorage) Delete(ctx request.Context, id string, options *api.DeleteOptions) (runtime.Object, error) {
func (storage *SimpleRESTStorage) Delete(ctx request.Context, id string, options *metav1.DeleteOptions) (runtime.Object, error) {
storage.checkContext(ctx)
storage.deleted = id
storage.deleteOptions = options
@ -467,7 +449,7 @@ func (storage *SimpleRESTStorage) Delete(ctx request.Context, id string, options
var obj runtime.Object = &metav1.Status{Status: metav1.StatusSuccess}
var err error
if storage.injectedFunction != nil {
obj, err = storage.injectedFunction(&genericapitesting.Simple{ObjectMeta: apiv1.ObjectMeta{Name: id}})
obj, err = storage.injectedFunction(&genericapitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: id}})
}
return obj, err
}
@ -510,7 +492,7 @@ func (storage *SimpleRESTStorage) Update(ctx request.Context, name string, objIn
}
// Implement ResourceWatcher.
func (storage *SimpleRESTStorage) Watch(ctx request.Context, options *api.ListOptions) (watch.Interface, error) {
func (storage *SimpleRESTStorage) Watch(ctx request.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
storage.lock.Lock()
defer storage.lock.Unlock()
storage.checkContext(ctx)
@ -1116,7 +1098,7 @@ func TestNonEmptyList(t *testing.T) {
simpleStorage := SimpleRESTStorage{
list: []genericapitesting.Simple{
{
ObjectMeta: apiv1.ObjectMeta{Name: "something", Namespace: "other"},
ObjectMeta: metav1.ObjectMeta{Name: "something", Namespace: "other"},
Other: "foo",
},
},
@ -1167,7 +1149,7 @@ func TestSelfLinkSkipsEmptyName(t *testing.T) {
simpleStorage := SimpleRESTStorage{
list: []genericapitesting.Simple{
{
ObjectMeta: apiv1.ObjectMeta{Namespace: "other"},
ObjectMeta: metav1.ObjectMeta{Namespace: "other"},
Other: "foo",
},
},
@ -1241,7 +1223,7 @@ func TestExport(t *testing.T) {
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
item: genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
ResourceVersion: "1234",
CreationTimestamp: metav1.NewTime(time.Unix(10, 10)),
},
@ -1960,7 +1942,7 @@ func TestDeleteWithOptions(t *testing.T) {
defer server.Close()
grace := int64(300)
item := &api.DeleteOptions{
item := &metav1.DeleteOptions{
GracePeriodSeconds: &grace,
}
body, err := runtime.Encode(codec, item)
@ -2001,7 +1983,7 @@ func TestDeleteWithOptionsQuery(t *testing.T) {
defer server.Close()
grace := int64(300)
item := &api.DeleteOptions{
item := &metav1.DeleteOptions{
GracePeriodSeconds: &grace,
}
@ -2038,7 +2020,7 @@ func TestDeleteWithOptionsQueryAndBody(t *testing.T) {
defer server.Close()
grace := int64(300)
item := &api.DeleteOptions{
item := &metav1.DeleteOptions{
GracePeriodSeconds: &grace,
}
body, err := runtime.Encode(codec, item)
@ -2104,7 +2086,7 @@ func TestLegacyDeleteIgnoresOptions(t *testing.T) {
server := httptest.NewServer(handler)
defer server.Close()
item := api.NewDeleteOptions(300)
item := metav1.NewDeleteOptions(300)
body, err := runtime.Encode(codec, item)
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -2174,7 +2156,7 @@ func TestPatch(t *testing.T) {
storage := map[string]rest.Storage{}
ID := "id"
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
Namespace: "", // update should allow the client to send an empty namespace
UID: "uid",
@ -2187,7 +2169,7 @@ func TestPatch(t *testing.T) {
t: t,
expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + ID,
name: ID,
namespace: api.NamespaceDefault,
namespace: metav1.NamespaceDefault,
}
handler := handleLinker(storage, selfLinker)
server := httptest.NewServer(handler)
@ -2213,7 +2195,7 @@ func TestPatchRequiresMatchingName(t *testing.T) {
storage := map[string]rest.Storage{}
ID := "id"
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
Namespace: "", // update should allow the client to send an empty namespace
UID: "uid",
@ -2247,14 +2229,14 @@ func TestUpdate(t *testing.T) {
t: t,
expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + ID,
name: ID,
namespace: api.NamespaceDefault,
namespace: metav1.NamespaceDefault,
}
handler := handleLinker(storage, selfLinker)
server := httptest.NewServer(handler)
defer server.Close()
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
Namespace: "", // update should allow the client to send an empty namespace
},
@ -2291,9 +2273,9 @@ func TestUpdateInvokesAdmissionControl(t *testing.T) {
defer server.Close()
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
Namespace: api.NamespaceDefault,
Namespace: metav1.NamespaceDefault,
},
Other: "bar",
}
@ -2353,7 +2335,7 @@ func TestUpdateAllowsMissingNamespace(t *testing.T) {
defer server.Close()
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
},
Other: "bar",
@ -2390,7 +2372,7 @@ func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) {
defer server.Close()
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
Namespace: "other", // does not match request
},
@ -2427,7 +2409,7 @@ func TestUpdatePreventsMismatchedNamespace(t *testing.T) {
defer server.Close()
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
Namespace: "other",
},
@ -2462,9 +2444,9 @@ func TestUpdateMissing(t *testing.T) {
defer server.Close()
item := &genericapitesting.Simple{
ObjectMeta: apiv1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: ID,
Namespace: api.NamespaceDefault,
Namespace: metav1.NamespaceDefault,
},
Other: "bar",
}
@ -3083,7 +3065,7 @@ func TestWriteJSONDecodeError(t *testing.T) {
if status.Reason != metav1.StatusReasonUnknown {
t.Errorf("unexpected reason %#v", status)
}
if !strings.Contains(status.Message, "no kind is registered for the type api.UnregisteredAPIObject") {
if !strings.Contains(status.Message, "no kind is registered for the type endpoints.UnregisteredAPIObject") {
t.Errorf("unexpected message %#v", status)
}
}
@ -3211,7 +3193,7 @@ func TestUpdateChecksAPIVersion(t *testing.T) {
defer server.Close()
client := http.Client{}
simple := &genericapitesting.Simple{ObjectMeta: apiv1.ObjectMeta{Name: "bar"}}
simple := &genericapitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}
data, err := runtime.Encode(newCodec, simple)
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -3238,10 +3220,10 @@ func TestUpdateChecksAPIVersion(t *testing.T) {
// SimpleXGSubresource is a cross group subresource, i.e. the subresource does not belong to the
// same group as its parent resource.
type SimpleXGSubresource struct {
metav1.TypeMeta `json:",inline"`
api.ObjectMeta `json:"metadata"`
SubresourceInfo string `json:"subresourceInfo,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
SubresourceInfo string `json:"subresourceInfo,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
func (obj *SimpleXGSubresource) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"bytes"
@ -27,9 +27,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/handlers/negotiation"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
)
// AddApiWebService adds a service to return the supported api versions at the legacy /api.

View file

@ -0,0 +1,18 @@
/*
Copyright 2014 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 endpoints contains the generic code that provides a RESTful Kubernetes-style API service.
package endpoints // import "k8s.io/kubernetes/pkg/genericapiserver/endpoints"

View file

@ -8,35 +8,11 @@ load(
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"authorization.go",
"doc.go",
"impersonation.go",
"requestinfo.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/authentication:go_default_library",
"//pkg/genericapiserver/api/handlers/responsewriters:go_default_library",
"//pkg/serviceaccount:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/pborman/uuid",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/httplog",
"//vendor:k8s.io/apiserver/pkg/request",
],
)
go_test(
name = "go_default_test",
srcs = [
"audit_test.go",
"authentication_test.go",
"authorization_test.go",
"impersonation_test.go",
"requestinfo_test.go",
@ -46,11 +22,40 @@ go_test(
deps = [
"//pkg/apis/authentication:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/genericapiserver/api/handlers/responsewriters:go_default_library",
"//pkg/genericapiserver/endpoints/handlers/responsewriters:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apiserver/pkg/authentication/authenticator",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"authentication.go",
"authorization.go",
"doc.go",
"impersonation.go",
"requestinfo.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/authentication:go_default_library",
"//pkg/genericapiserver/endpoints/handlers/responsewriters:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/pborman/uuid",
"//vendor:github.com/prometheus/client_golang/prometheus",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apiserver/pkg/authentication/authenticator",
"//vendor:k8s.io/apiserver/pkg/authentication/serviceaccount",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/server/httplog",
],
)

View file

@ -30,9 +30,9 @@ import (
"github.com/pborman/uuid"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/request"
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
)
var _ http.ResponseWriter = &auditResponseWriter{}

View file

@ -29,7 +29,7 @@ import (
"testing"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/request"
)
type simpleResponseWriter struct {

View file

@ -0,0 +1,117 @@
/*
Copyright 2014 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 filters
import (
"net/http"
"strings"
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apiserver/pkg/authentication/authenticator"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
)
var (
authenticatedUserCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "authenticated_user_requests",
Help: "Counter of authenticated requests broken out by username.",
},
[]string{"username"},
)
)
func init() {
prometheus.MustRegister(authenticatedUserCounter)
}
// WithAuthentication creates an http handler that tries to authenticate the given request as a user, and then
// stores any such user found onto the provided context for the request. If authentication fails or returns an error
// the failed handler is used. On success, "Authorization" header is removed from the request and handler
// is invoked to serve the request.
func WithAuthentication(handler http.Handler, mapper genericapirequest.RequestContextMapper, auth authenticator.Request, failed http.Handler) http.Handler {
if auth == nil {
glog.Warningf("Authentication is disabled")
return handler
}
return genericapirequest.WithRequestContext(
http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
user, ok, err := auth.AuthenticateRequest(req)
if err != nil || !ok {
if err != nil {
glog.Errorf("Unable to authenticate the request due to an error: %v", err)
}
failed.ServeHTTP(w, req)
return
}
// authorization header is not required anymore in case of a successful authentication.
req.Header.Del("Authorization")
if ctx, ok := mapper.Get(req); ok {
mapper.Update(req, genericapirequest.WithUser(ctx, user))
}
authenticatedUserCounter.WithLabelValues(compressUsername(user.GetName())).Inc()
handler.ServeHTTP(w, req)
}),
mapper,
)
}
func Unauthorized(supportsBasicAuth bool) http.HandlerFunc {
if supportsBasicAuth {
return unauthorizedBasicAuth
}
return unauthorized
}
// unauthorizedBasicAuth serves an unauthorized message to clients.
func unauthorizedBasicAuth(w http.ResponseWriter, req *http.Request) {
w.Header().Set("WWW-Authenticate", `Basic realm="kubernetes-master"`)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
}
// unauthorized serves an unauthorized message to clients.
func unauthorized(w http.ResponseWriter, req *http.Request) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
}
// compressUsername maps all possible usernames onto a small set of categories
// of usernames. This is done both to limit the cardinality of the
// authorized_user_requests metric, and to avoid pushing actual usernames in the
// metric.
func compressUsername(username string) string {
switch {
// Known internal identities.
case username == "admin" ||
username == "client" ||
username == "kube_proxy" ||
username == "kubelet" ||
username == "system:serviceaccount:kube-system:default":
return username
// Probably an email address.
case strings.Contains(username, "@"):
return "email_id"
// Anything else (custom service accounts, custom external identities, etc.)
default:
return "other"
}
}

View file

@ -0,0 +1,126 @@
/*
Copyright 2014 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 filters
import (
"errors"
"net/http"
"net/http/httptest"
"testing"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
)
func TestAuthenticateRequest(t *testing.T) {
success := make(chan struct{})
contextMapper := genericapirequest.NewRequestContextMapper()
auth := WithAuthentication(
http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
ctx, ok := contextMapper.Get(req)
if ctx == nil || !ok {
t.Errorf("no context stored on contextMapper: %#v", contextMapper)
}
user, ok := genericapirequest.UserFrom(ctx)
if user == nil || !ok {
t.Errorf("no user stored in context: %#v", ctx)
}
if req.Header.Get("Authorization") != "" {
t.Errorf("Authorization header should be removed from request on success: %#v", req)
}
close(success)
}),
contextMapper,
authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
if req.Header.Get("Authorization") == "Something" {
return &user.DefaultInfo{Name: "user"}, true, nil
}
return nil, false, errors.New("Authorization header is missing.")
}),
http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
t.Errorf("unexpected call to failed")
}),
)
auth.ServeHTTP(httptest.NewRecorder(), &http.Request{Header: map[string][]string{"Authorization": {"Something"}}})
<-success
empty, err := genericapirequest.IsEmpty(contextMapper)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !empty {
t.Fatalf("contextMapper should have no stored requests: %v", contextMapper)
}
}
func TestAuthenticateRequestFailed(t *testing.T) {
failed := make(chan struct{})
contextMapper := genericapirequest.NewRequestContextMapper()
auth := WithAuthentication(
http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
t.Errorf("unexpected call to handler")
}),
contextMapper,
authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
return nil, false, nil
}),
http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
close(failed)
}),
)
auth.ServeHTTP(httptest.NewRecorder(), &http.Request{})
<-failed
empty, err := genericapirequest.IsEmpty(contextMapper)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !empty {
t.Fatalf("contextMapper should have no stored requests: %v", contextMapper)
}
}
func TestAuthenticateRequestError(t *testing.T) {
failed := make(chan struct{})
contextMapper := genericapirequest.NewRequestContextMapper()
auth := WithAuthentication(
http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
t.Errorf("unexpected call to handler")
}),
contextMapper,
authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
return nil, false, errors.New("failure")
}),
http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
close(failed)
}),
)
auth.ServeHTTP(httptest.NewRecorder(), &http.Request{})
<-failed
empty, err := genericapirequest.IsEmpty(contextMapper)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !empty {
t.Fatalf("contextMapper should have no stored requests: %v", contextMapper)
}
}

View file

@ -23,8 +23,8 @@ import (
"github.com/golang/glog"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
)
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.

View file

@ -24,9 +24,9 @@ import (
"testing"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
)
func TestGetAuthorizerAttributes(t *testing.T) {

View file

@ -15,5 +15,7 @@ limitations under the License.
*/
// Package filters contains all the http handler chain filters which
// _are_ api related.
package filters // import "k8s.io/kubernetes/pkg/genericapiserver/api/filters"
// _are_ api related, i.e. which are prerequisite for the API services
// to work (in contrast to the filters in the server package which are
// not part of the API contract).
package filters // import "k8s.io/kubernetes/pkg/genericapiserver/endpoints/filters"

View file

@ -24,14 +24,14 @@ import (
"github.com/golang/glog"
"k8s.io/apiserver/pkg/authentication/serviceaccount"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/httplog"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/httplog"
"k8s.io/kubernetes/pkg/api"
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
)
// WithImpersonation is a filter that will inspect and check requests that attempt to change the user.Info for their requests

View file

@ -26,7 +26,7 @@ import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/request"
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
)

View file

@ -21,8 +21,8 @@ import (
"fmt"
"net/http"
"k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
)
// WithRequestInfo attaches a RequestInfo to the context.

View file

@ -18,7 +18,7 @@ package filters
import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/request"
)
func newTestRequestInfoResolver() *request.RequestInfoFactory {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"fmt"
@ -29,10 +29,10 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
)
// APIGroupVersion is a helper for exposing rest.Storage objects as http.Handlers via go-restful
@ -49,10 +49,14 @@ type APIGroupVersion struct {
GroupVersion schema.GroupVersion
// OptionsExternalVersion controls the Kubernetes APIVersion used for common objects in the apiserver
// schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may
// schema like api.Status, api.DeleteOptions, and metav1.ListOptions. Other implementors may
// define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. If
// empty, defaults to GroupVersion.
OptionsExternalVersion *schema.GroupVersion
// MetaGroupVersion defaults to "meta.k8s.io/v1" and is the scheme group version used to decode
// common API implementations like ListOptions. Future changes will allow this to vary by group
// version (for when the inevitable meta/v2 group emerges).
MetaGroupVersion *schema.GroupVersion
Mapper meta.RESTMapper

View file

@ -17,7 +17,7 @@ go_test(
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/util/strategicpatch:go_default_library",
"//vendor:github.com/emicklei/go-restful",
"//vendor:github.com/evanphx/json-patch",
@ -27,7 +27,7 @@ go_test(
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/diff",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)
@ -36,17 +36,16 @@ go_library(
srcs = [
"discovery.go",
"doc.go",
"patch.go",
"proxy.go",
"rest.go",
"watch.go",
],
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/handlers/responsewriters:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/genericapiserver/endpoints/handlers/responsewriters:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/util:go_default_library",
"//pkg/util/httpstream:go_default_library",
"//pkg/util/proxy:go_default_library",
@ -57,17 +56,21 @@ go_library(
"//vendor:golang.org/x/net/websocket",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/internalversion",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/fields",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer/streaming",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/handlers/negotiation",
"//vendor:k8s.io/apiserver/pkg/httplog",
"//vendor:k8s.io/apiserver/pkg/metrics",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/admission",
"//vendor:k8s.io/apiserver/pkg/endpoints/handlers/negotiation",
"//vendor:k8s.io/apiserver/pkg/endpoints/metrics",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/server/httplog",
"//vendor:k8s.io/apiserver/pkg/util/wsstream",
],
)
@ -83,7 +86,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/genericapiserver/api/handlers/responsewriters:all-srcs",
"//pkg/genericapiserver/endpoints/handlers/responsewriters:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -22,7 +22,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
"github.com/emicklei/go-restful"
)

View file

@ -15,4 +15,4 @@ limitations under the License.
*/
// Package handlers contains HTTP handlers to implement the apiserver APIs.
package handlers // import "k8s.io/kubernetes/pkg/genericapiserver/api/handlers"
package handlers // import "k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers"

View file

@ -0,0 +1,131 @@
/*
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 (
"encoding/json"
"fmt"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/strategicpatch"
"github.com/evanphx/json-patch"
)
// patchObjectJSON patches the <originalObject> with <patchJS> and stores
// the result in <objToUpdate>.
// Currently it also returns the original and patched objects serialized to
// JSONs (this may not be needed once we can apply patches at the
// map[string]interface{} level).
func patchObjectJSON(
patchType types.PatchType,
codec runtime.Codec,
originalObject runtime.Object,
patchJS []byte,
objToUpdate runtime.Object,
versionedObj runtime.Object,
) (originalObjJS []byte, patchedObjJS []byte, retErr error) {
js, err := runtime.Encode(codec, originalObject)
if err != nil {
return nil, nil, err
}
originalObjJS = js
switch patchType {
case types.JSONPatchType:
patchObj, err := jsonpatch.DecodePatch(patchJS)
if err != nil {
return nil, nil, err
}
if patchedObjJS, err = patchObj.Apply(originalObjJS); err != nil {
return nil, nil, err
}
case types.MergePatchType:
if patchedObjJS, err = jsonpatch.MergePatch(originalObjJS, patchJS); err != nil {
return nil, nil, err
}
case types.StrategicMergePatchType:
if patchedObjJS, err = strategicpatch.StrategicMergePatch(originalObjJS, patchJS, versionedObj); err != nil {
return nil, nil, err
}
default:
// only here as a safety net - go-restful filters content-type
return nil, nil, fmt.Errorf("unknown Content-Type header for patch: %v", patchType)
}
if err := runtime.DecodeInto(codec, patchedObjJS, objToUpdate); err != nil {
return nil, nil, err
}
return
}
// strategicPatchObject applies a strategic merge patch of <patchJS> to
// <originalObject> and stores the result in <objToUpdate>.
// It additionally returns the map[string]interface{} representation of the
// <originalObject> and <patchJS>.
func strategicPatchObject(
codec runtime.Codec,
originalObject runtime.Object,
patchJS []byte,
objToUpdate runtime.Object,
versionedObj runtime.Object,
) (originalObjMap map[string]interface{}, patchMap map[string]interface{}, retErr error) {
// TODO: This should be one-step conversion that doesn't require
// json marshaling and unmarshaling once #39017 is fixed.
data, err := runtime.Encode(codec, originalObject)
if err != nil {
return nil, nil, err
}
originalObjMap = make(map[string]interface{})
if err := json.Unmarshal(data, &originalObjMap); err != nil {
return nil, nil, err
}
patchMap = make(map[string]interface{})
if err := json.Unmarshal(patchJS, &patchMap); err != nil {
return nil, nil, err
}
if err := applyPatchToObject(codec, originalObjMap, patchMap, objToUpdate, versionedObj); err != nil {
return nil, nil, err
}
return
}
// applyPatchToObject applies a strategic merge patch of <patchMap> to
// <originalMap> and stores the result in <objToUpdate>, though it operates
// on versioned map[string]interface{} representations.
func applyPatchToObject(
codec runtime.Codec,
originalMap map[string]interface{},
patchMap map[string]interface{},
objToUpdate runtime.Object,
versionedObj runtime.Object,
) error {
patchedObjMap, err := strategicpatch.StrategicMergeMapPatch(originalMap, patchMap, versionedObj)
if err != nil {
return err
}
// TODO: This should be one-step conversion that doesn't require
// json marshaling and unmarshaling once #39017 is fixed.
data, err := json.Marshal(patchedObjMap)
if err != nil {
return err
}
return runtime.DecodeInto(codec, data, objToUpdate)
}

View file

@ -31,12 +31,12 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/httplog"
"k8s.io/apiserver/pkg/metrics"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/metrics"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/httplog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
"k8s.io/kubernetes/pkg/util/httpstream"
proxyutil "k8s.io/kubernetes/pkg/util/proxy"

View file

@ -31,14 +31,14 @@ go_library(
],
tags = ["automanaged"],
deps = [
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/storage:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/handlers/negotiation",
"//vendor:k8s.io/apiserver/pkg/endpoints/handlers/negotiation",
"//vendor:k8s.io/apiserver/pkg/util/flushwriter",
"//vendor:k8s.io/apiserver/pkg/util/wsstream",
],

View file

@ -15,4 +15,4 @@ limitations under the License.
*/
// Package responsewriters containers helpers to write responses in HTTP handlers.
package responsewriters // import "k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
package responsewriters // import "k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"

View file

@ -52,7 +52,7 @@ func apiStatus(err error) *metav1.Status {
status := http.StatusInternalServerError
switch {
//TODO: replace me with NewConflictErr
case storage.IsTestFailed(err):
case storage.IsConflict(err):
status = http.StatusConflict
}
// Log errors that were not converted to an error status

View file

@ -26,10 +26,10 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/util/flushwriter"
"k8s.io/apiserver/pkg/util/wsstream"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
)
// WriteObject renders a returned runtime.Object to the response as a stream or an encoded object. If the object

View file

@ -29,22 +29,22 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/handlers/negotiation"
"k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers/responsewriters"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/strategicpatch"
"github.com/emicklei/go-restful"
"github.com/evanphx/json-patch"
"github.com/golang/glog"
)
@ -87,6 +87,8 @@ type RequestScope struct {
Resource schema.GroupVersionResource
Kind schema.GroupVersionKind
Subresource string
MetaGroupVersion schema.GroupVersion
}
func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Request) {
@ -264,8 +266,8 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
ctx := scope.ContextFunc(req)
ctx = request.WithNamespace(ctx, namespace)
opts := api.ListOptions{}
if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &opts); err != nil {
opts := metainternalversion.ListOptions{}
if err := metainternalversion.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.MetaGroupVersion, &opts); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
@ -302,6 +304,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
}
if (opts.Watch || forceWatch) && rw != nil {
glog.Infof("Started to log from %v for %v", ctx, req.Request.URL.RequestURI())
watcher, err := rw.Watch(ctx, &opts)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
@ -488,7 +491,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
if idx := strings.Index(contentType, ";"); idx > 0 {
contentType = contentType[:idx]
}
patchType := api.PatchType(contentType)
patchType := types.PatchType(contentType)
patchJS, err := readBody(req.Request)
if err != nil {
@ -542,7 +545,7 @@ func patchResource(
versionedObj runtime.Object,
patcher rest.Patcher,
name string,
patchType api.PatchType,
patchType types.PatchType,
patchJS []byte,
namer ScopeNamer,
copier runtime.ObjectCopier,
@ -555,6 +558,8 @@ func patchResource(
var (
originalObjJS []byte
originalPatchedObjJS []byte
originalObjMap map[string]interface{}
originalPatchMap map[string]interface{}
lastConflictErr error
)
@ -569,25 +574,30 @@ func patchResource(
}
switch {
case len(originalObjJS) == 0 || len(originalPatchedObjJS) == 0:
case originalObjJS == nil && originalObjMap == nil:
// first time through,
// 1. apply the patch
// 2. save the originalJS and patchedJS to detect whether there were conflicting changes on retries
if js, err := runtime.Encode(codec, currentObject); err != nil {
return nil, err
} else {
originalObjJS = js
}
if js, err := getPatchedJS(patchType, originalObjJS, patchJS, versionedObj); err != nil {
return nil, err
} else {
originalPatchedObjJS = js
}
// 2. save the original and patched to detect whether there were conflicting changes on retries
objToUpdate := patcher.New()
if err := runtime.DecodeInto(codec, originalPatchedObjJS, objToUpdate); err != nil {
return nil, err
// For performance reasons, in case of strategicpatch, we avoid json
// marshaling and unmarshaling and operate just on map[string]interface{}.
// In case of other patch types, we still have to operate on JSON
// representations.
switch patchType {
case types.JSONPatchType, types.MergePatchType:
originalJS, patchedJS, err := patchObjectJSON(patchType, codec, currentObject, patchJS, objToUpdate, versionedObj)
if err != nil {
return nil, err
}
originalObjJS, originalPatchedObjJS = originalJS, patchedJS
case types.StrategicMergePatchType:
originalMap, patchMap, err := strategicPatchObject(codec, currentObject, patchJS, objToUpdate, versionedObj)
if err != nil {
return nil, err
}
originalObjMap, originalPatchMap = originalMap, patchMap
}
if err := checkName(objToUpdate, name, namespace, namer); err != nil {
return nil, err
@ -602,33 +612,62 @@ func patchResource(
// 2. build a strategic merge patch from originalJS and the currentJS
// 3. ensure no conflicts between the two patches
// 4. apply the #1 patch to the currentJS object
currentObjectJS, err := runtime.Encode(codec, currentObject)
// TODO: This should be one-step conversion that doesn't require
// json marshaling and unmarshaling once #39017 is fixed.
data, err := runtime.Encode(codec, currentObject)
if err != nil {
return nil, err
}
currentPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, currentObjectJS, versionedObj)
if err != nil {
return nil, err
}
originalPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, originalPatchedObjJS, versionedObj)
if err != nil {
currentObjMap := make(map[string]interface{})
if err := json.Unmarshal(data, &currentObjMap); err != nil {
return nil, err
}
diff1 := make(map[string]interface{})
if err := json.Unmarshal(originalPatch, &diff1); err != nil {
return nil, err
var currentPatchMap map[string]interface{}
if originalObjMap != nil {
var err error
currentPatchMap, err = strategicpatch.CreateTwoWayMergeMapPatch(originalObjMap, currentObjMap, versionedObj)
if err != nil {
return nil, err
}
} else {
if originalPatchMap == nil {
// Compute original patch, if we already didn't do this in previous retries.
originalPatch, err := strategicpatch.CreateTwoWayMergePatch(originalObjJS, originalPatchedObjJS, versionedObj)
if err != nil {
return nil, err
}
originalPatchMap = make(map[string]interface{})
if err := json.Unmarshal(originalPatch, &originalPatchMap); err != nil {
return nil, err
}
}
// Compute current patch.
currentObjJS, err := runtime.Encode(codec, currentObject)
if err != nil {
return nil, err
}
currentPatch, err := strategicpatch.CreateTwoWayMergePatch(originalObjJS, currentObjJS, versionedObj)
if err != nil {
return nil, err
}
currentPatchMap = make(map[string]interface{})
if err := json.Unmarshal(currentPatch, &currentPatchMap); err != nil {
return nil, err
}
}
diff2 := make(map[string]interface{})
if err := json.Unmarshal(currentPatch, &diff2); err != nil {
return nil, err
}
hasConflicts, err := strategicpatch.HasConflicts(diff1, diff2)
hasConflicts, err := strategicpatch.HasConflicts(originalPatchMap, currentPatchMap)
if err != nil {
return nil, err
}
if hasConflicts {
glog.V(4).Infof("patchResource failed for resource %s, because there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n", name, diff1, diff2)
if glog.V(4) {
diff1, _ := json.Marshal(currentPatchMap)
diff2, _ := json.Marshal(originalPatchMap)
glog.Infof("patchResource failed for resource %s, because there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n", name, diff1, diff2)
}
// Return the last conflict error we got if we have one
if lastConflictErr != nil {
return nil, lastConflictErr
@ -637,14 +676,11 @@ func patchResource(
return nil, errors.NewConflict(resource.GroupResource(), name, nil)
}
newlyPatchedObjJS, err := getPatchedJS(api.StrategicMergePatchType, currentObjectJS, originalPatch, versionedObj)
if err != nil {
return nil, err
}
objToUpdate := patcher.New()
if err := runtime.DecodeInto(codec, newlyPatchedObjJS, objToUpdate); err != nil {
if err := applyPatchToObject(codec, currentObjMap, originalPatchMap, objToUpdate, versionedObj); err != nil {
return nil, err
}
return objToUpdate, nil
}
}
@ -774,7 +810,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
ctx := scope.ContextFunc(req)
ctx = request.WithNamespace(ctx, namespace)
options := &api.DeleteOptions{}
options := &metav1.DeleteOptions{}
if allowsOptions {
body, err := readBody(req.Request)
if err != nil {
@ -878,8 +914,8 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
}
}
listOptions := api.ListOptions{}
if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &listOptions); err != nil {
listOptions := metainternalversion.ListOptions{}
if err := metainternalversion.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.MetaGroupVersion, &listOptions); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
@ -898,7 +934,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
}
}
options := &api.DeleteOptions{}
options := &metav1.DeleteOptions{}
if checkBody {
body, err := readBody(req.Request)
if err != nil {
@ -1078,24 +1114,6 @@ func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer)
return count, err
}
func getPatchedJS(patchType api.PatchType, originalJS, patchJS []byte, obj runtime.Object) ([]byte, error) {
switch patchType {
case api.JSONPatchType:
patchObj, err := jsonpatch.DecodePatch(patchJS)
if err != nil {
return nil, err
}
return patchObj.Apply(originalJS)
case api.MergePatchType:
return jsonpatch.MergePatch(originalJS, patchJS)
case api.StrategicMergePatchType:
return strategicpatch.StrategicMergePatchData(originalJS, patchJS, obj)
default:
// only here as a safety net - go-restful filters content-type
return nil, fmt.Errorf("unknown Content-Type header for patch: %v", patchType)
}
}
func summarizeData(data []byte, maxLength int) string {
switch {
case len(data) == 0:

View file

@ -32,11 +32,11 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apiserver/pkg/request"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
"k8s.io/kubernetes/pkg/util/strategicpatch"
)
@ -55,16 +55,27 @@ type TestPatchSubType struct {
func (obj *testPatchType) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func TestPatchAnonymousField(t *testing.T) {
originalJS := `{"kind":"testPatchType","theField":"my-value"}`
patch := `{"theField": "changed!"}`
expectedJS := `{"kind":"testPatchType","theField":"changed!"}`
testGV := schema.GroupVersion{Group: "", Version: "v"}
api.Scheme.AddKnownTypes(testGV, &testPatchType{})
codec := api.Codecs.LegacyCodec(testGV)
actualBytes, err := getPatchedJS(api.StrategicMergePatchType, []byte(originalJS), []byte(patch), &testPatchType{})
original := &testPatchType{
TypeMeta: metav1.TypeMeta{Kind: "testPatchType", APIVersion: "v"},
TestPatchSubType: TestPatchSubType{StringField: "my-value"},
}
patch := `{"theField": "changed!"}`
expected := &testPatchType{
TypeMeta: metav1.TypeMeta{Kind: "testPatchType", APIVersion: "v"},
TestPatchSubType: TestPatchSubType{StringField: "changed!"},
}
actual := &testPatchType{}
_, _, err := strategicPatchObject(codec, original, []byte(patch), actual, &testPatchType{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(actualBytes) != expectedJS {
t.Errorf("expected %v, got %v", expectedJS, string(actualBytes))
if !api.Semantic.DeepEqual(actual, expected) {
t.Errorf("expected %#v, got %#v", expected, actual)
}
}
@ -191,9 +202,9 @@ func (tc *patchTestCase) Run(t *testing.T) {
resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
versionedObj := &v1.Pod{}
for _, patchType := range []api.PatchType{api.JSONPatchType, api.MergePatchType, api.StrategicMergePatchType} {
for _, patchType := range []types.PatchType{types.JSONPatchType, types.MergePatchType, types.StrategicMergePatchType} {
// TODO SUPPORT THIS!
if patchType == api.JSONPatchType {
if patchType == types.JSONPatchType {
continue
}
t.Logf("Working with patchType %v", patchType)
@ -211,17 +222,17 @@ func (tc *patchTestCase) Run(t *testing.T) {
patch := []byte{}
switch patchType {
case api.JSONPatchType:
case types.JSONPatchType:
continue
case api.StrategicMergePatchType:
patch, err = strategicpatch.CreateStrategicMergePatch(originalObjJS, changedJS, versionedObj)
case types.StrategicMergePatchType:
patch, err = strategicpatch.CreateTwoWayMergePatch(originalObjJS, changedJS, versionedObj)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
return
}
case api.MergePatchType:
case types.MergePatchType:
patch, err = jsonpatch.CreateMergePatch(originalObjJS, changedJS)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
@ -460,7 +471,7 @@ func TestHasUID(t *testing.T) {
{obj: &api.Pod{}, hasUID: false},
{obj: nil, hasUID: false},
{obj: runtime.Object(nil), hasUID: false},
{obj: &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("A")}}, hasUID: true},
{obj: &api.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("A")}}, hasUID: true},
}
for i, tc := range testcases {
actual, err := hasUID(tc.obj)

View file

@ -29,8 +29,8 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/handlers/negotiation"
"k8s.io/apiserver/pkg/httplog"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/server/httplog"
"k8s.io/apiserver/pkg/util/wsstream"
"github.com/emicklei/go-restful"

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"bytes"
@ -34,13 +34,13 @@ import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/handlers/negotiation"
"k8s.io/apiserver/pkg/metrics"
"k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/metrics"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
"github.com/emicklei/go-restful"
)
@ -521,6 +521,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Resource: a.group.GroupVersion.WithResource(resource),
Subresource: subresource,
Kind: fqKindToRegister,
MetaGroupVersion: metav1.SchemeGroupVersion,
}
if a.group.MetaGroupVersion != nil {
reqScope.MetaGroupVersion = *a.group.MetaGroupVersion
}
for _, action := range actions {
versionedObject := storageMeta.ProducesObject(action.Verb)
@ -638,7 +643,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
route := ws.PATCH(action.Path).To(handler).
Doc(doc).
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
Consumes(string(api.JSONPatchType), string(api.MergePatchType), string(api.StrategicMergePatchType)).
Consumes(string(types.JSONPatchType), string(types.MergePatchType), string(types.StrategicMergePatchType)).
Operation("patch"+namespaced+kind+strings.Title(subresource)+operationSuffix).
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
Returns(http.StatusOK, "OK", versionedObject).

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"bytes"
@ -43,7 +43,7 @@ func TestScopeNamingGenerateLink(t *testing.T) {
true,
}
service := &api.Service{
ObjectMeta: api.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "other",
},
@ -82,8 +82,6 @@ func TestIsVowel(t *testing.T) {
}
func TestGetArticleForNoun(t *testing.T) {
type args struct {
}
tests := []struct {
noun string
padding string

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"bytes"
@ -36,7 +36,7 @@ import (
"golang.org/x/net/websocket"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
)
func TestProxyRequestContentLengthAndTransferEncoding(t *testing.T) {

View file

@ -15,7 +15,6 @@ go_library(
],
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//vendor:github.com/ugorji/go/codec",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",

View file

@ -26,8 +26,7 @@ import (
"fmt"
codec1978 "github.com/ugorji/go/codec"
pkg1_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkg3_types "k8s.io/apimachinery/pkg/types"
pkg2_v1 "k8s.io/kubernetes/pkg/api/v1"
pkg2_types "k8s.io/apimachinery/pkg/types"
"reflect"
"runtime"
time "time"
@ -64,10 +63,9 @@ func init() {
}
if false { // reference the types, but skip this branch at build/run time
var v0 pkg1_v1.TypeMeta
var v1 pkg3_types.UID
var v2 pkg2_v1.ObjectMeta
var v3 time.Time
_, _, _, _ = v0, v1, v2, v3
var v1 pkg2_types.UID
var v2 time.Time
_, _, _ = v0, v1, v2
}
}
@ -158,13 +156,25 @@ func (x *Simple) CodecEncodeSelf(e *codec1978.Encoder) {
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
yy10 := &x.ObjectMeta
yy10.CodecEncodeSelf(e)
yym11 := z.EncBinary()
_ = yym11
if false {
} else if z.HasExtensions() && z.EncExt(yy10) {
} else {
z.EncFallback(yy10)
}
} else {
z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("metadata"))
z.EncSendContainerState(codecSelfer_containerMapValue1234)
yy12 := &x.ObjectMeta
yy12.CodecEncodeSelf(e)
yym13 := z.EncBinary()
_ = yym13
if false {
} else if z.HasExtensions() && z.EncExt(yy12) {
} else {
z.EncFallback(yy12)
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
@ -311,33 +321,39 @@ func (x *Simple) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
}
case "metadata":
if r.TryDecodeAsNil() {
x.ObjectMeta = pkg2_v1.ObjectMeta{}
x.ObjectMeta = pkg1_v1.ObjectMeta{}
} else {
yyv8 := &x.ObjectMeta
yyv8.CodecDecodeSelf(d)
yym9 := z.DecBinary()
_ = yym9
if false {
} else if z.HasExtensions() && z.DecExt(yyv8) {
} else {
z.DecFallback(yyv8, false)
}
}
case "other":
if r.TryDecodeAsNil() {
x.Other = ""
} else {
yyv9 := &x.Other
yym10 := z.DecBinary()
_ = yym10
yyv10 := &x.Other
yym11 := z.DecBinary()
_ = yym11
if false {
} else {
*((*string)(yyv9)) = r.DecodeString()
*((*string)(yyv10)) = r.DecodeString()
}
}
case "labels":
if r.TryDecodeAsNil() {
x.Labels = nil
} else {
yyv11 := &x.Labels
yym12 := z.DecBinary()
_ = yym12
yyv12 := &x.Labels
yym13 := z.DecBinary()
_ = yym13
if false {
} else {
z.F.DecMapStringStringX(yyv11, false, d)
z.F.DecMapStringStringX(yyv12, false, d)
}
}
default:
@ -351,16 +367,16 @@ func (x *Simple) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
var yyj13 int
var yyb13 bool
var yyhl13 bool = l >= 0
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
var yyj14 int
var yyb14 bool
var yyhl14 bool = l >= 0
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -368,21 +384,21 @@ func (x *Simple) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Kind = ""
} else {
yyv14 := &x.Kind
yym15 := z.DecBinary()
_ = yym15
yyv15 := &x.Kind
yym16 := z.DecBinary()
_ = yym16
if false {
} else {
*((*string)(yyv14)) = r.DecodeString()
*((*string)(yyv15)) = r.DecodeString()
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -390,38 +406,44 @@ func (x *Simple) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.APIVersion = ""
} else {
yyv16 := &x.APIVersion
yym17 := z.DecBinary()
_ = yym17
yyv17 := &x.APIVersion
yym18 := z.DecBinary()
_ = yym18
if false {
} else {
*((*string)(yyv16)) = r.DecodeString()
*((*string)(yyv17)) = r.DecodeString()
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.ObjectMeta = pkg2_v1.ObjectMeta{}
x.ObjectMeta = pkg1_v1.ObjectMeta{}
} else {
yyv18 := &x.ObjectMeta
yyv18.CodecDecodeSelf(d)
yyv19 := &x.ObjectMeta
yym20 := z.DecBinary()
_ = yym20
if false {
} else if z.HasExtensions() && z.DecExt(yyv19) {
} else {
z.DecFallback(yyv19, false)
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -429,21 +451,21 @@ func (x *Simple) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Other = ""
} else {
yyv19 := &x.Other
yym20 := z.DecBinary()
_ = yym20
yyv21 := &x.Other
yym22 := z.DecBinary()
_ = yym22
if false {
} else {
*((*string)(yyv19)) = r.DecodeString()
*((*string)(yyv21)) = r.DecodeString()
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -451,26 +473,26 @@ func (x *Simple) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Labels = nil
} else {
yyv21 := &x.Labels
yym22 := z.DecBinary()
_ = yym22
yyv23 := &x.Labels
yym24 := z.DecBinary()
_ = yym24
if false {
} else {
z.F.DecMapStringStringX(yyv21, false, d)
z.F.DecMapStringStringX(yyv23, false, d)
}
}
for {
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
z.DecStructFieldNotFound(yyj13-1, "")
z.DecStructFieldNotFound(yyj14-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}
@ -562,13 +584,25 @@ func (x *SimpleRoot) CodecEncodeSelf(e *codec1978.Encoder) {
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
yy10 := &x.ObjectMeta
yy10.CodecEncodeSelf(e)
yym11 := z.EncBinary()
_ = yym11
if false {
} else if z.HasExtensions() && z.EncExt(yy10) {
} else {
z.EncFallback(yy10)
}
} else {
z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("metadata"))
z.EncSendContainerState(codecSelfer_containerMapValue1234)
yy12 := &x.ObjectMeta
yy12.CodecEncodeSelf(e)
yym13 := z.EncBinary()
_ = yym13
if false {
} else if z.HasExtensions() && z.EncExt(yy12) {
} else {
z.EncFallback(yy12)
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
@ -715,33 +749,39 @@ func (x *SimpleRoot) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
}
case "metadata":
if r.TryDecodeAsNil() {
x.ObjectMeta = pkg2_v1.ObjectMeta{}
x.ObjectMeta = pkg1_v1.ObjectMeta{}
} else {
yyv8 := &x.ObjectMeta
yyv8.CodecDecodeSelf(d)
yym9 := z.DecBinary()
_ = yym9
if false {
} else if z.HasExtensions() && z.DecExt(yyv8) {
} else {
z.DecFallback(yyv8, false)
}
}
case "other":
if r.TryDecodeAsNil() {
x.Other = ""
} else {
yyv9 := &x.Other
yym10 := z.DecBinary()
_ = yym10
yyv10 := &x.Other
yym11 := z.DecBinary()
_ = yym11
if false {
} else {
*((*string)(yyv9)) = r.DecodeString()
*((*string)(yyv10)) = r.DecodeString()
}
}
case "labels":
if r.TryDecodeAsNil() {
x.Labels = nil
} else {
yyv11 := &x.Labels
yym12 := z.DecBinary()
_ = yym12
yyv12 := &x.Labels
yym13 := z.DecBinary()
_ = yym13
if false {
} else {
z.F.DecMapStringStringX(yyv11, false, d)
z.F.DecMapStringStringX(yyv12, false, d)
}
}
default:
@ -755,16 +795,16 @@ func (x *SimpleRoot) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
var yyj13 int
var yyb13 bool
var yyhl13 bool = l >= 0
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
var yyj14 int
var yyb14 bool
var yyhl14 bool = l >= 0
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -772,21 +812,21 @@ func (x *SimpleRoot) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Kind = ""
} else {
yyv14 := &x.Kind
yym15 := z.DecBinary()
_ = yym15
yyv15 := &x.Kind
yym16 := z.DecBinary()
_ = yym16
if false {
} else {
*((*string)(yyv14)) = r.DecodeString()
*((*string)(yyv15)) = r.DecodeString()
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -794,38 +834,44 @@ func (x *SimpleRoot) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.APIVersion = ""
} else {
yyv16 := &x.APIVersion
yym17 := z.DecBinary()
_ = yym17
yyv17 := &x.APIVersion
yym18 := z.DecBinary()
_ = yym18
if false {
} else {
*((*string)(yyv16)) = r.DecodeString()
*((*string)(yyv17)) = r.DecodeString()
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.ObjectMeta = pkg2_v1.ObjectMeta{}
x.ObjectMeta = pkg1_v1.ObjectMeta{}
} else {
yyv18 := &x.ObjectMeta
yyv18.CodecDecodeSelf(d)
yyv19 := &x.ObjectMeta
yym20 := z.DecBinary()
_ = yym20
if false {
} else if z.HasExtensions() && z.DecExt(yyv19) {
} else {
z.DecFallback(yyv19, false)
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -833,21 +879,21 @@ func (x *SimpleRoot) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Other = ""
} else {
yyv19 := &x.Other
yym20 := z.DecBinary()
_ = yym20
yyv21 := &x.Other
yym22 := z.DecBinary()
_ = yym22
if false {
} else {
*((*string)(yyv19)) = r.DecodeString()
*((*string)(yyv21)) = r.DecodeString()
}
}
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -855,26 +901,26 @@ func (x *SimpleRoot) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Labels = nil
} else {
yyv21 := &x.Labels
yym22 := z.DecBinary()
_ = yym22
yyv23 := &x.Labels
yym24 := z.DecBinary()
_ = yym24
if false {
} else {
z.F.DecMapStringStringX(yyv21, false, d)
z.F.DecMapStringStringX(yyv23, false, d)
}
}
for {
yyj13++
if yyhl13 {
yyb13 = yyj13 > l
yyj14++
if yyhl14 {
yyb14 = yyj14 > l
} else {
yyb13 = r.CheckBreak()
yyb14 = r.CheckBreak()
}
if yyb13 {
if yyb14 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
z.DecStructFieldNotFound(yyj13-1, "")
z.DecStructFieldNotFound(yyj14-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}

View file

@ -19,12 +19,11 @@ package testing
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
)
type Simple struct {
metav1.TypeMeta `json:",inline"`
apiv1.ObjectMeta `json:"metadata"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
// +optional
Other string `json:"other,omitempty"`
// +optional
@ -34,8 +33,8 @@ type Simple struct {
func (obj *Simple) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
type SimpleRoot struct {
metav1.TypeMeta `json:",inline"`
apiv1.ObjectMeta `json:"metadata"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
// +optional
Other string `json:"other,omitempty"`
// +optional

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package endpoints
import (
"encoding/json"
@ -31,6 +31,7 @@ import (
"golang.org/x/net/websocket"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
@ -38,11 +39,9 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/kubernetes/pkg/api"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/genericapiserver/api/handlers"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
apitesting "k8s.io/kubernetes/pkg/genericapiserver/api/testing"
"k8s.io/kubernetes/pkg/genericapiserver/endpoints/handlers"
apitesting "k8s.io/kubernetes/pkg/genericapiserver/endpoints/testing"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
)
// watchJSON defines the expected JSON wire equivalent of watch.Event
@ -68,18 +67,18 @@ var watchTestTable = []struct {
t watch.EventType
obj runtime.Object
}{
{watch.Added, &apitesting.Simple{ObjectMeta: apiv1.ObjectMeta{Name: "foo"}}},
{watch.Modified, &apitesting.Simple{ObjectMeta: apiv1.ObjectMeta{Name: "bar"}}},
{watch.Deleted, &apitesting.Simple{ObjectMeta: apiv1.ObjectMeta{Name: "bar"}}},
{watch.Added, &apitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}},
{watch.Modified, &apitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}},
{watch.Deleted, &apitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}},
}
var podWatchTestTable = []struct {
t watch.EventType
obj runtime.Object
}{
{watch.Added, roundTripOrDie(codec, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}})},
{watch.Modified, roundTripOrDie(codec, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}})},
{watch.Deleted, roundTripOrDie(codec, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}})},
{watch.Added, roundTripOrDie(codec, &api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})},
{watch.Modified, roundTripOrDie(codec, &api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}})},
{watch.Deleted, roundTripOrDie(codec, &api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}})},
}
func TestWatchWebsocket(t *testing.T) {
@ -422,28 +421,28 @@ func TestWatchParamParsing(t *testing.T) {
resourceVersion: "1234",
labelSelector: "",
fieldSelector: "",
namespace: api.NamespaceAll,
namespace: metav1.NamespaceAll,
}, {
path: rootPath,
rawQuery: "resourceVersion=314159&fieldSelector=Host%3D&labelSelector=name%3Dfoo",
resourceVersion: "314159",
labelSelector: "name=foo",
fieldSelector: "Host=",
namespace: api.NamespaceAll,
namespace: metav1.NamespaceAll,
}, {
path: rootPath,
rawQuery: "fieldSelector=id%3dfoo&resourceVersion=1492",
resourceVersion: "1492",
labelSelector: "",
fieldSelector: "id=foo",
namespace: api.NamespaceAll,
namespace: metav1.NamespaceAll,
}, {
path: rootPath,
rawQuery: "",
resourceVersion: "",
labelSelector: "",
fieldSelector: "",
namespace: api.NamespaceAll,
namespace: metav1.NamespaceAll,
},
{
path: namespacedPath,

View file

@ -1,143 +0,0 @@
/*
Copyright 2016 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 common
import (
"github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
)
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
type OpenAPIDefinition struct {
Schema spec.Schema
Dependencies []string
}
// OpenAPIDefinitions is collection of all definitions.
type OpenAPIDefinitions map[string]OpenAPIDefinition
// OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface,
// the definition returned by it will be used, otherwise the auto-generated definitions will be used. See
// GetOpenAPITypeFormat for more information about trade-offs of using this interface or GetOpenAPITypeFormat method when
// possible.
type OpenAPIDefinitionGetter interface {
OpenAPIDefinition() *OpenAPIDefinition
}
// Config is set of configuration for openAPI spec generation.
type Config struct {
// List of supported protocols such as https, http, etc.
ProtocolList []string
// Info is general information about the API.
Info *spec.Info
// DefaultResponse will be used if an operation does not have any responses listed. It
// will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
DefaultResponse *spec.Response
// CommonResponses will be added as a response to all operation specs. This is a good place to add common
// responses such as authorization failed.
CommonResponses map[int]spec.Response
// List of webservice's path prefixes to ignore
IgnorePrefixes []string
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
// or any of the models will result in spec generation failure.
Definitions *OpenAPIDefinitions
// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
GetOperationIDAndTags func(servePath string, r *restful.Route) (string, []string, error)
// SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config
// is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses.
SecurityDefinitions *spec.SecurityDefinitions
// DefaultSecurity for all operations. This will pass as spec.SwaggerProps.Security to OpenAPI.
// For most cases, this will be list of acceptable definitions in SecurityDefinitions.
DefaultSecurity []map[string][]string
}
// This function is a reference for converting go (or any custom type) to a simple open API type,format pair. There are
// two ways to customize spec for a type. If you add it here, a type will be converted to a simple type and the type
// comment (the comment that is added before type definition) will be lost. The spec will still have the property
// comment. The second way is to implement OpenAPIDefinitionGetter interface. That function can customize the spec (so
// the spec does not need to be simple type,format) or can even return a simple type,format (e.g. IntOrString). For simple
// type formats, the benefit of adding OpenAPIDefinitionGetter interface is to keep both type and property documentation.
// Example:
// type Sample struct {
// ...
// // port of the server
// port IntOrString
// ...
// }
// // IntOrString documentation...
// type IntOrString { ... }
//
// Adding IntOrString to this function:
// "port" : {
// format: "string",
// type: "int-or-string",
// Description: "port of the server"
// }
//
// Implement OpenAPIDefinitionGetter for IntOrString:
//
// "port" : {
// $Ref: "#/definitions/IntOrString"
// Description: "port of the server"
// }
// ...
// definitions:
// {
// "IntOrString": {
// format: "string",
// type: "int-or-string",
// Description: "IntOrString documentation..." // new
// }
// }
//
func GetOpenAPITypeFormat(typeName string) (string, string) {
schemaTypeFormatMap := map[string][]string{
"uint": {"integer", "int32"},
"uint8": {"integer", "byte"},
"uint16": {"integer", "int32"},
"uint32": {"integer", "int64"},
"uint64": {"integer", "int64"},
"int": {"integer", "int32"},
"int8": {"integer", "byte"},
"int16": {"integer", "int32"},
"int32": {"integer", "int32"},
"int64": {"integer", "int64"},
"byte": {"integer", "byte"},
"float64": {"number", "double"},
"float32": {"number", "float"},
"bool": {"boolean", ""},
"time.Time": {"string", "date-time"},
"string": {"string", ""},
"integer": {"integer", ""},
"number": {"number", ""},
"boolean": {"boolean", ""},
"[]byte": {"string", "byte"}, // base64 encoded characters
}
mapped, ok := schemaTypeFormatMap[typeName]
if !ok {
return "", ""
}
return mapped[0], mapped[1]
}

View file

@ -9,15 +9,8 @@ load(
go_library(
name = "go_default_library",
srcs = [
"common.go",
"doc.go",
],
srcs = ["doc.go"],
tags = ["automanaged"],
deps = [
"//vendor:github.com/emicklei/go-restful",
"//vendor:github.com/go-openapi/spec",
],
)
filegroup(
@ -29,6 +22,10 @@ filegroup(
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
srcs = [
":package-srcs",
"//pkg/genericapiserver/registry/generic:all-srcs",
"//pkg/genericapiserver/registry/rest:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package api contains the code that provides a rest.ful api service.
package api // import "k8s.io/kubernetes/pkg/genericapiserver/api"
// Package registry contains the generic implementation of the storage and system logic.
package registry // import "k8s.io/kubernetes/pkg/genericapiserver/registry"

View file

@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"matcher.go",
"options.go",
"storage_decorator.go",
],
tags = ["automanaged"],
deps = [
"//pkg/storage:go_default_library",
"//pkg/storage/storagebackend:go_default_library",
"//pkg/storage/storagebackend/factory:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/fields",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/genericapiserver/registry/generic/registry:all-srcs",
"//pkg/genericapiserver/registry/generic/rest:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,35 @@
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- deads2k
- yujuhong
- derekwaynecarr
- caesarxuchao
- mikedanese
- liggitt
- nikhiljindal
- bprashanth
- gmarek
- davidopp
- saad-ali
- janetkuo
- pwittrock
- roberthbailey
- ncdc
- eparis
- timothysc
- jlowdermilk
- piosz
- dims
- hongchaodeng
- krousey
- markturansky
- fgrzadkowski
- xiang90
- resouer
- mqliang
- feihujiang
- sdminonne
- goltermann

View file

@ -0,0 +1,19 @@
/*
Copyright 2014 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 generic provides a generic object store interface and a
// generic label/field matching type.
package generic // import "k8s.io/kubernetes/pkg/genericapiserver/registry/generic"

View file

@ -0,0 +1,52 @@
/*
Copyright 2014 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 generic
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
)
// ObjectMetaFieldsSet returns a fields that represent the ObjectMeta.
func ObjectMetaFieldsSet(objectMeta *metav1.ObjectMeta, hasNamespaceField bool) fields.Set {
if !hasNamespaceField {
return fields.Set{
"metadata.name": objectMeta.Name,
}
}
return fields.Set{
"metadata.name": objectMeta.Name,
"metadata.namespace": objectMeta.Namespace,
}
}
// AdObjectMetaField add fields that represent the ObjectMeta to source.
func AddObjectMetaFieldsSet(source fields.Set, objectMeta *metav1.ObjectMeta, hasNamespaceField bool) fields.Set {
source["metadata.name"] = objectMeta.Name
if hasNamespaceField {
source["metadata.namespace"] = objectMeta.Namespace
}
return source
}
// MergeFieldsSets merges a fields'set from fragment into the source.
func MergeFieldsSets(source fields.Set, fragment fields.Set) fields.Set {
for k, value := range fragment {
source[k] = value
}
return source
}

View file

@ -0,0 +1,49 @@
/*
Copyright 2016 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 generic
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/storage"
"k8s.io/kubernetes/pkg/storage/storagebackend"
)
// RESTOptions is set of configuration options to generic registries.
type RESTOptions struct {
StorageConfig *storagebackend.Config
Decorator StorageDecorator
EnableGarbageCollection bool
DeleteCollectionWorkers int
ResourcePrefix string
}
// Implement RESTOptionsGetter so that RESTOptions can directly be used when available (i.e. tests)
func (opts RESTOptions) GetRESTOptions(schema.GroupResource) (RESTOptions, error) {
return opts, nil
}
type RESTOptionsGetter interface {
GetRESTOptions(resource schema.GroupResource) (RESTOptions, error)
}
// StoreOptions is set of configuration options used to complete generic registries.
type StoreOptions struct {
RESTOptions RESTOptionsGetter
TriggerFunc storage.TriggerPublisherFunc
AttrFunc storage.AttrFunc
}

View file

@ -0,0 +1,94 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"decorated_watcher.go",
"doc.go",
"storage_factory.go",
"store.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/errors/storage:go_default_library",
"//pkg/api/validation/path:go_default_library",
"//pkg/genericapiserver/registry/generic:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/registry/cachesize:go_default_library",
"//pkg/storage:go_default_library",
"//pkg/storage/etcd:go_default_library",
"//pkg/storage/storagebackend:go_default_library",
"//pkg/storage/storagebackend/factory:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:golang.org/x/net/context",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/internalversion",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/fields",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)
go_test(
name = "go_default_test",
srcs = [
"decorated_watcher_test.go",
"store_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/genericapiserver/registry/generic:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/registry/core/pod:go_default_library",
"//pkg/storage:go_default_library",
"//pkg/storage/etcd:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//pkg/storage/storagebackend/factory:go_default_library",
"//pkg/storage/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/internalversion",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/fields",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/selection",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,97 @@
/*
Copyright 2016 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 registry
import (
"net/http"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
)
type decoratedWatcher struct {
w watch.Interface
decorator ObjectFunc
cancel context.CancelFunc
resultCh chan watch.Event
}
func newDecoratedWatcher(w watch.Interface, decorator ObjectFunc) *decoratedWatcher {
ctx, cancel := context.WithCancel(context.Background())
d := &decoratedWatcher{
w: w,
decorator: decorator,
cancel: cancel,
resultCh: make(chan watch.Event),
}
go d.run(ctx)
return d
}
func (d *decoratedWatcher) run(ctx context.Context) {
var recv, send watch.Event
for {
select {
case recv = <-d.w.ResultChan():
switch recv.Type {
case watch.Added, watch.Modified, watch.Deleted:
err := d.decorator(recv.Object)
if err != nil {
send = makeStatusErrorEvent(err)
break
}
send = recv
case watch.Error:
send = recv
}
select {
case d.resultCh <- send:
if send.Type == watch.Error {
d.cancel()
}
case <-ctx.Done():
}
case <-ctx.Done():
d.w.Stop()
close(d.resultCh)
return
}
}
}
func (d *decoratedWatcher) Stop() {
d.cancel()
}
func (d *decoratedWatcher) ResultChan() <-chan watch.Event {
return d.resultCh
}
func makeStatusErrorEvent(err error) watch.Event {
status := &metav1.Status{
Status: metav1.StatusFailure,
Message: err.Error(),
Code: http.StatusInternalServerError,
Reason: metav1.StatusReasonInternalError,
}
return watch.Event{
Type: watch.Error,
Object: status,
}
}

View file

@ -0,0 +1,75 @@
/*
Copyright 2016 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 registry
import (
"fmt"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/kubernetes/pkg/api"
)
func TestDecoratedWatcher(t *testing.T) {
w := watch.NewFake()
decorator := func(obj runtime.Object) error {
pod := obj.(*api.Pod)
pod.Annotations = map[string]string{"decorated": "true"}
return nil
}
dw := newDecoratedWatcher(w, decorator)
defer dw.Stop()
go w.Add(&api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
select {
case e := <-dw.ResultChan():
pod, ok := e.Object.(*api.Pod)
if !ok {
t.Errorf("Should received object of type *api.Pod, get type (%T)", e.Object)
return
}
if pod.Annotations["decorated"] != "true" {
t.Errorf("pod.Annotations[\"decorated\"], want=%s, get=%s", "true", pod.Labels["decorated"])
}
case <-time.After(wait.ForeverTestTimeout):
t.Errorf("timeout after %v", wait.ForeverTestTimeout)
}
}
func TestDecoratedWatcherError(t *testing.T) {
w := watch.NewFake()
expErr := fmt.Errorf("expected error")
decorator := func(obj runtime.Object) error {
return expErr
}
dw := newDecoratedWatcher(w, decorator)
defer dw.Stop()
go w.Add(&api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
select {
case e := <-dw.ResultChan():
if e.Type != watch.Error {
t.Errorf("event type want=%v, get=%v", watch.Error, e.Type)
}
case <-time.After(wait.ForeverTestTimeout):
t.Errorf("timeout after %v", wait.ForeverTestTimeout)
}
}

View file

@ -0,0 +1,19 @@
/*
Copyright 2014 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 etcd has a generic implementation of a registry that
// stores things in etcd.
package registry // import "k8s.io/kubernetes/pkg/genericapiserver/registry/generic/registry"

View file

@ -0,0 +1,63 @@
/*
Copyright 2015 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 registry
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/genericapiserver/registry/generic"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/storage/storagebackend"
"k8s.io/kubernetes/pkg/storage/storagebackend/factory"
)
var _ generic.StorageDecorator = StorageWithCacher
// Creates a cacher based given storageConfig.
func StorageWithCacher(
storageConfig *storagebackend.Config,
capacity int,
objectType runtime.Object,
resourcePrefix string,
keyFunc func(obj runtime.Object) (string, error),
newListFunc func() runtime.Object,
getAttrsFunc storage.AttrFunc,
triggerFunc storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) {
s, d := generic.NewRawStorage(storageConfig)
// TODO: we would change this later to make storage always have cacher and hide low level KV layer inside.
// Currently it has two layers of same storage interface -- cacher and low level kv.
cacherConfig := storage.CacherConfig{
CacheCapacity: capacity,
Storage: s,
Versioner: etcdstorage.APIObjectVersioner{},
Type: objectType,
ResourcePrefix: resourcePrefix,
KeyFunc: keyFunc,
NewListFunc: newListFunc,
GetAttrsFunc: getAttrsFunc,
TriggerPublisherFunc: triggerFunc,
Codec: storageConfig.Codec,
}
cacher := storage.NewCacherFromConfig(cacherConfig)
destroyFunc := func() {
cacher.Stop()
d()
}
return cacher, destroyFunc
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"proxy.go",
"response_checker.go",
"streamer.go",
],
tags = ["automanaged"],
deps = [
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/util/config:go_default_library",
"//pkg/util/httpstream:go_default_library",
"//pkg/util/proxy:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/mxk/go-flowrate/flowrate",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
],
)
go_test(
name = "go_default_test",
srcs = [
"proxy_test.go",
"response_checker_test.go",
"streamer_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/util/config:go_default_library",
"//pkg/util/httpstream:go_default_library",
"//pkg/util/proxy:go_default_library",
"//vendor:github.com/stretchr/testify/assert",
"//vendor:github.com/stretchr/testify/require",
"//vendor:golang.org/x/net/websocket",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/util/net",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,19 @@
/*
Copyright 2014 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 rest has generic implementations of resources used for
// REST responses
package rest // import "k8s.io/kubernetes/pkg/genericapiserver/registry/generic/rest"

View file

@ -0,0 +1,371 @@
/*
Copyright 2014 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 rest
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"sync"
"time"
"k8s.io/apimachinery/pkg/api/errors"
utilnet "k8s.io/apimachinery/pkg/util/net"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
utilconfig "k8s.io/kubernetes/pkg/util/config"
"k8s.io/kubernetes/pkg/util/httpstream"
"k8s.io/kubernetes/pkg/util/proxy"
"github.com/golang/glog"
"github.com/mxk/go-flowrate/flowrate"
)
// UpgradeAwareProxyHandler is a handler for proxy requests that may require an upgrade
type UpgradeAwareProxyHandler struct {
UpgradeRequired bool
Location *url.URL
// Transport provides an optional round tripper to use to proxy. If nil, the default proxy transport is used
Transport http.RoundTripper
// WrapTransport indicates whether the provided Transport should be wrapped with default proxy transport behavior (URL rewriting, X-Forwarded-* header setting)
WrapTransport bool
// InterceptRedirects determines whether the proxy should sniff backend responses for redirects,
// following them as necessary.
InterceptRedirects bool
FlushInterval time.Duration
MaxBytesPerSec int64
Responder ErrorResponder
}
const defaultFlushInterval = 200 * time.Millisecond
// ErrorResponder abstracts error reporting to the proxy handler to remove the need to hardcode a particular
// error format.
type ErrorResponder interface {
Error(err error)
}
// NewUpgradeAwareProxyHandler creates a new proxy handler with a default flush interval. Responder is required for returning
// errors to the caller.
func NewUpgradeAwareProxyHandler(location *url.URL, transport http.RoundTripper, wrapTransport, upgradeRequired bool, responder ErrorResponder) *UpgradeAwareProxyHandler {
return &UpgradeAwareProxyHandler{
Location: location,
Transport: transport,
WrapTransport: wrapTransport,
UpgradeRequired: upgradeRequired,
FlushInterval: defaultFlushInterval,
Responder: responder,
}
}
// ServeHTTP handles the proxy request
func (h *UpgradeAwareProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if len(h.Location.Scheme) == 0 {
h.Location.Scheme = "http"
}
if h.tryUpgrade(w, req) {
return
}
if h.UpgradeRequired {
h.Responder.Error(errors.NewBadRequest("Upgrade request required"))
return
}
loc := *h.Location
loc.RawQuery = req.URL.RawQuery
// If original request URL ended in '/', append a '/' at the end of the
// of the proxy URL
if !strings.HasSuffix(loc.Path, "/") && strings.HasSuffix(req.URL.Path, "/") {
loc.Path += "/"
}
// From pkg/genericapiserver/endpoints/handlers/proxy.go#ServeHTTP:
// Redirect requests with an empty path to a location that ends with a '/'
// This is essentially a hack for http://issue.k8s.io/4958.
// Note: Keep this code after tryUpgrade to not break that flow.
if len(loc.Path) == 0 {
var queryPart string
if len(req.URL.RawQuery) > 0 {
queryPart = "?" + req.URL.RawQuery
}
w.Header().Set("Location", req.URL.Path+"/"+queryPart)
w.WriteHeader(http.StatusMovedPermanently)
return
}
if h.Transport == nil || h.WrapTransport {
h.Transport = h.defaultProxyTransport(req.URL, h.Transport)
}
newReq, err := http.NewRequest(req.Method, loc.String(), req.Body)
if err != nil {
h.Responder.Error(err)
return
}
newReq.Header = req.Header
newReq.ContentLength = req.ContentLength
// Copy the TransferEncoding is for future-proofing. Currently Go only supports "chunked" and
// it can determine the TransferEncoding based on ContentLength and the Body.
newReq.TransferEncoding = req.TransferEncoding
proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: h.Location.Scheme, Host: h.Location.Host})
proxy.Transport = h.Transport
proxy.FlushInterval = h.FlushInterval
proxy.ServeHTTP(w, newReq)
}
// tryUpgrade returns true if the request was handled.
func (h *UpgradeAwareProxyHandler) tryUpgrade(w http.ResponseWriter, req *http.Request) bool {
if !httpstream.IsUpgradeRequest(req) {
return false
}
var (
backendConn net.Conn
rawResponse []byte
err error
)
if h.InterceptRedirects && utilconfig.DefaultFeatureGate.StreamingProxyRedirects() {
backendConn, rawResponse, err = h.connectBackendWithRedirects(req)
} else {
backendConn, err = h.connectBackend(req.Method, h.Location, req.Header, req.Body)
}
if err != nil {
h.Responder.Error(err)
return true
}
defer backendConn.Close()
// Once the connection is hijacked, the ErrorResponder will no longer work, so
// hijacking should be the last step in the upgrade.
requestHijacker, ok := w.(http.Hijacker)
if !ok {
h.Responder.Error(fmt.Errorf("request connection cannot be hijacked: %T", w))
return true
}
requestHijackedConn, _, err := requestHijacker.Hijack()
if err != nil {
h.Responder.Error(fmt.Errorf("error hijacking request connection: %v", err))
return true
}
defer requestHijackedConn.Close()
// Forward raw response bytes back to client.
if len(rawResponse) > 0 {
if _, err = requestHijackedConn.Write(rawResponse); err != nil {
utilruntime.HandleError(fmt.Errorf("Error proxying response from backend to client: %v", err))
}
}
// Proxy the connection.
wg := &sync.WaitGroup{}
wg.Add(2)
go func() {
var writer io.WriteCloser
if h.MaxBytesPerSec > 0 {
writer = flowrate.NewWriter(backendConn, h.MaxBytesPerSec)
} else {
writer = backendConn
}
_, err := io.Copy(writer, requestHijackedConn)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
glog.Errorf("Error proxying data from client to backend: %v", err)
}
wg.Done()
}()
go func() {
var reader io.ReadCloser
if h.MaxBytesPerSec > 0 {
reader = flowrate.NewReader(backendConn, h.MaxBytesPerSec)
} else {
reader = backendConn
}
_, err := io.Copy(requestHijackedConn, reader)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
glog.Errorf("Error proxying data from backend to client: %v", err)
}
wg.Done()
}()
wg.Wait()
return true
}
// connectBackend dials the backend at location and forwards a copy of the client request.
func (h *UpgradeAwareProxyHandler) connectBackend(method string, location *url.URL, header http.Header, body io.Reader) (conn net.Conn, err error) {
defer func() {
if err != nil && conn != nil {
conn.Close()
conn = nil
}
}()
beReq, err := http.NewRequest(method, location.String(), body)
if err != nil {
return nil, err
}
beReq.Header = header
conn, err = proxy.DialURL(location, h.Transport)
if err != nil {
return conn, fmt.Errorf("error dialing backend: %v", err)
}
if err = beReq.Write(conn); err != nil {
return conn, fmt.Errorf("error sending request: %v", err)
}
return conn, err
}
// connectBackendWithRedirects dials the backend and forwards a copy of the client request. If the
// client responds with a redirect, it is followed. The raw response bytes are returned, and should
// be forwarded back to the client.
func (h *UpgradeAwareProxyHandler) connectBackendWithRedirects(req *http.Request) (net.Conn, []byte, error) {
const (
maxRedirects = 10
maxResponseSize = 4096
)
var (
initialReq = req
rawResponse = bytes.NewBuffer(make([]byte, 0, 256))
location = h.Location
intermediateConn net.Conn
err error
)
defer func() {
if intermediateConn != nil {
intermediateConn.Close()
}
}()
redirectLoop:
for redirects := 0; ; redirects++ {
if redirects > maxRedirects {
return nil, nil, fmt.Errorf("too many redirects (%d)", redirects)
}
if redirects == 0 {
intermediateConn, err = h.connectBackend(req.Method, location, req.Header, req.Body)
} else {
// Redirected requests switch to "GET" according to the HTTP spec:
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3
intermediateConn, err = h.connectBackend("GET", location, initialReq.Header, nil)
}
if err != nil {
return nil, nil, err
}
// Peek at the backend response.
rawResponse.Reset()
respReader := bufio.NewReader(io.TeeReader(
io.LimitReader(intermediateConn, maxResponseSize), // Don't read more than maxResponseSize bytes.
rawResponse)) // Save the raw response.
resp, err := http.ReadResponse(respReader, req)
if err != nil {
// Unable to read the backend response; let the client handle it.
glog.Warningf("Error reading backend response: %v", err)
break redirectLoop
}
resp.Body.Close() // Unused.
switch resp.StatusCode {
case http.StatusFound:
// Redirect, continue.
default:
// Don't redirect.
break redirectLoop
}
// Reset the connection.
intermediateConn.Close()
intermediateConn = nil
// Prepare to follow the redirect.
redirectStr := resp.Header.Get("Location")
if redirectStr == "" {
return nil, nil, fmt.Errorf("%d response missing Location header", resp.StatusCode)
}
location, err = h.Location.Parse(redirectStr)
if err != nil {
return nil, nil, fmt.Errorf("malformed Location header: %v", err)
}
}
backendConn := intermediateConn
intermediateConn = nil // Don't close the connection when we return it.
return backendConn, rawResponse.Bytes(), nil
}
func (h *UpgradeAwareProxyHandler) defaultProxyTransport(url *url.URL, internalTransport http.RoundTripper) http.RoundTripper {
scheme := url.Scheme
host := url.Host
suffix := h.Location.Path
if strings.HasSuffix(url.Path, "/") && !strings.HasSuffix(suffix, "/") {
suffix += "/"
}
pathPrepend := strings.TrimSuffix(url.Path, suffix)
rewritingTransport := &proxy.Transport{
Scheme: scheme,
Host: host,
PathPrepend: pathPrepend,
RoundTripper: internalTransport,
}
return &corsRemovingTransport{
RoundTripper: rewritingTransport,
}
}
// corsRemovingTransport is a wrapper for an internal transport. It removes CORS headers
// from the internal response.
// Implements pkg/util/net.RoundTripperWrapper
type corsRemovingTransport struct {
http.RoundTripper
}
var _ = utilnet.RoundTripperWrapper(&corsRemovingTransport{})
func (rt *corsRemovingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := rt.RoundTripper.RoundTrip(req)
if err != nil {
return nil, err
}
removeCORSHeaders(resp)
return resp, nil
}
func (rt *corsRemovingTransport) WrappedRoundTripper() http.RoundTripper {
return rt.RoundTripper
}
// removeCORSHeaders strip CORS headers sent from the backend
// This should be called on all responses before returning
func removeCORSHeaders(resp *http.Response) {
resp.Header.Del("Access-Control-Allow-Credentials")
resp.Header.Del("Access-Control-Allow-Headers")
resp.Header.Del("Access-Control-Allow-Methods")
resp.Header.Del("Access-Control-Allow-Origin")
}

View file

@ -0,0 +1,816 @@
/*
Copyright 2014 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 rest
import (
"bytes"
"compress/gzip"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"reflect"
"strconv"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/websocket"
utilnet "k8s.io/apimachinery/pkg/util/net"
utilconfig "k8s.io/kubernetes/pkg/util/config"
"k8s.io/kubernetes/pkg/util/httpstream"
"k8s.io/kubernetes/pkg/util/proxy"
)
const fakeStatusCode = 567
type fakeResponder struct {
t *testing.T
called bool
err error
// called chan error
w http.ResponseWriter
}
func (r *fakeResponder) Error(err error) {
if r.called {
r.t.Errorf("Error responder called again!\nprevious error: %v\nnew error: %v", r.err, err)
}
if r.w != nil {
r.w.WriteHeader(fakeStatusCode)
_, writeErr := r.w.Write([]byte(err.Error()))
assert.NoError(r.t, writeErr)
} else {
r.t.Logf("No ResponseWriter set")
}
r.called = true
r.err = err
}
type fakeConn struct {
err error // The error to return when io is performed over the connection.
}
func (f *fakeConn) Read([]byte) (int, error) { return 0, f.err }
func (f *fakeConn) Write([]byte) (int, error) { return 0, f.err }
func (f *fakeConn) Close() error { return nil }
func (fakeConn) LocalAddr() net.Addr { return nil }
func (fakeConn) RemoteAddr() net.Addr { return nil }
func (fakeConn) SetDeadline(t time.Time) error { return nil }
func (fakeConn) SetReadDeadline(t time.Time) error { return nil }
func (fakeConn) SetWriteDeadline(t time.Time) error { return nil }
type SimpleBackendHandler struct {
requestURL url.URL
requestHeader http.Header
requestBody []byte
requestMethod string
responseBody string
responseHeader map[string]string
t *testing.T
}
func (s *SimpleBackendHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.requestURL = *req.URL
s.requestHeader = req.Header
s.requestMethod = req.Method
var err error
s.requestBody, err = ioutil.ReadAll(req.Body)
if err != nil {
s.t.Errorf("Unexpected error: %v", err)
return
}
if s.responseHeader != nil {
for k, v := range s.responseHeader {
w.Header().Add(k, v)
}
}
w.Write([]byte(s.responseBody))
}
func validateParameters(t *testing.T, name string, actual url.Values, expected map[string]string) {
for k, v := range expected {
actualValue, ok := actual[k]
if !ok {
t.Errorf("%s: Expected parameter %s not received", name, k)
continue
}
if actualValue[0] != v {
t.Errorf("%s: Parameter %s values don't match. Actual: %#v, Expected: %s",
name, k, actualValue, v)
}
}
}
func validateHeaders(t *testing.T, name string, actual http.Header, expected map[string]string, notExpected []string) {
for k, v := range expected {
actualValue, ok := actual[k]
if !ok {
t.Errorf("%s: Expected header %s not received", name, k)
continue
}
if actualValue[0] != v {
t.Errorf("%s: Header %s values don't match. Actual: %s, Expected: %s",
name, k, actualValue, v)
}
}
if notExpected == nil {
return
}
for _, h := range notExpected {
if _, present := actual[h]; present {
t.Errorf("%s: unexpected header: %s", name, h)
}
}
}
func TestServeHTTP(t *testing.T) {
tests := []struct {
name string
method string
requestPath string
expectedPath string
requestBody string
requestParams map[string]string
requestHeader map[string]string
responseHeader map[string]string
expectedRespHeader map[string]string
notExpectedRespHeader []string
upgradeRequired bool
expectError func(err error) bool
}{
{
name: "root path, simple get",
method: "GET",
requestPath: "/",
expectedPath: "/",
},
{
name: "no upgrade header sent",
method: "GET",
requestPath: "/",
upgradeRequired: true,
expectError: func(err error) bool {
return err != nil && strings.Contains(err.Error(), "Upgrade request required")
},
},
{
name: "simple path, get",
method: "GET",
requestPath: "/path/to/test",
expectedPath: "/path/to/test",
},
{
name: "request params",
method: "POST",
requestPath: "/some/path/",
expectedPath: "/some/path/",
requestParams: map[string]string{"param1": "value/1", "param2": "value%2"},
requestBody: "test request body",
},
{
name: "request headers",
method: "PUT",
requestPath: "/some/path",
expectedPath: "/some/path",
requestHeader: map[string]string{"Header1": "value1", "Header2": "value2"},
},
{
name: "empty path - slash should be added",
method: "GET",
requestPath: "",
expectedPath: "/",
},
{
name: "remove CORS headers",
method: "GET",
requestPath: "/some/path",
expectedPath: "/some/path",
responseHeader: map[string]string{
"Header1": "value1",
"Access-Control-Allow-Origin": "some.server",
"Access-Control-Allow-Methods": "GET"},
expectedRespHeader: map[string]string{
"Header1": "value1",
},
notExpectedRespHeader: []string{
"Access-Control-Allow-Origin",
"Access-Control-Allow-Methods",
},
},
}
for i, test := range tests {
func() {
backendResponse := "<html><head></head><body><a href=\"/test/path\">Hello</a></body></html>"
backendResponseHeader := test.responseHeader
// Test a simple header if not specified in the test
if backendResponseHeader == nil && test.expectedRespHeader == nil {
backendResponseHeader = map[string]string{"Content-Type": "text/html"}
test.expectedRespHeader = map[string]string{"Content-Type": "text/html"}
}
backendHandler := &SimpleBackendHandler{
responseBody: backendResponse,
responseHeader: backendResponseHeader,
}
backendServer := httptest.NewServer(backendHandler)
defer backendServer.Close()
responder := &fakeResponder{t: t}
backendURL, _ := url.Parse(backendServer.URL)
backendURL.Path = test.requestPath
proxyHandler := &UpgradeAwareProxyHandler{
Location: backendURL,
Responder: responder,
UpgradeRequired: test.upgradeRequired,
}
proxyServer := httptest.NewServer(proxyHandler)
defer proxyServer.Close()
proxyURL, _ := url.Parse(proxyServer.URL)
proxyURL.Path = test.requestPath
paramValues := url.Values{}
for k, v := range test.requestParams {
paramValues[k] = []string{v}
}
proxyURL.RawQuery = paramValues.Encode()
var requestBody io.Reader
if test.requestBody != "" {
requestBody = bytes.NewBufferString(test.requestBody)
}
req, err := http.NewRequest(test.method, proxyURL.String(), requestBody)
if test.requestHeader != nil {
header := http.Header{}
for k, v := range test.requestHeader {
header.Add(k, v)
}
req.Header = header
}
if err != nil {
t.Errorf("Error creating client request: %v", err)
}
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
t.Errorf("Error from proxy request: %v", err)
}
if test.expectError != nil {
if !responder.called {
t.Errorf("%d: responder was not invoked", i)
return
}
if !test.expectError(responder.err) {
t.Errorf("%d: unexpected error: %v", i, responder.err)
}
return
}
// Validate backend request
// Method
if backendHandler.requestMethod != test.method {
t.Errorf("Unexpected request method: %s. Expected: %s",
backendHandler.requestMethod, test.method)
}
// Body
if string(backendHandler.requestBody) != test.requestBody {
t.Errorf("Unexpected request body: %s. Expected: %s",
string(backendHandler.requestBody), test.requestBody)
}
// Path
if backendHandler.requestURL.Path != test.expectedPath {
t.Errorf("Unexpected request path: %s", backendHandler.requestURL.Path)
}
// Parameters
validateParameters(t, test.name, backendHandler.requestURL.Query(), test.requestParams)
// Headers
validateHeaders(t, test.name+" backend request", backendHandler.requestHeader,
test.requestHeader, nil)
// Validate proxy response
// Response Headers
validateHeaders(t, test.name+" backend headers", res.Header, test.expectedRespHeader, test.notExpectedRespHeader)
// Validate Body
responseBody, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Errorf("Unexpected error reading response body: %v", err)
}
if rb := string(responseBody); rb != backendResponse {
t.Errorf("Did not get expected response body: %s. Expected: %s", rb, backendResponse)
}
// Error
if responder.called {
t.Errorf("Unexpected proxy handler error: %v", responder.err)
}
}()
}
}
func TestProxyUpgrade(t *testing.T) {
localhostPool := x509.NewCertPool()
if !localhostPool.AppendCertsFromPEM(localhostCert) {
t.Errorf("error setting up localhostCert pool")
}
testcases := map[string]struct {
ServerFunc func(http.Handler) *httptest.Server
ProxyTransport http.RoundTripper
}{
"http": {
ServerFunc: httptest.NewServer,
ProxyTransport: nil,
},
"https (invalid hostname + InsecureSkipVerify)": {
ServerFunc: func(h http.Handler) *httptest.Server {
cert, err := tls.X509KeyPair(exampleCert, exampleKey)
if err != nil {
t.Errorf("https (invalid hostname): proxy_test: %v", err)
}
ts := httptest.NewUnstartedServer(h)
ts.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
}
ts.StartTLS()
return ts
},
ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}),
},
"https (valid hostname + RootCAs)": {
ServerFunc: func(h http.Handler) *httptest.Server {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Errorf("https (valid hostname): proxy_test: %v", err)
}
ts := httptest.NewUnstartedServer(h)
ts.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
}
ts.StartTLS()
return ts
},
ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{TLSClientConfig: &tls.Config{RootCAs: localhostPool}}),
},
"https (valid hostname + RootCAs + custom dialer)": {
ServerFunc: func(h http.Handler) *httptest.Server {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Errorf("https (valid hostname): proxy_test: %v", err)
}
ts := httptest.NewUnstartedServer(h)
ts.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
}
ts.StartTLS()
return ts
},
ProxyTransport: utilnet.SetTransportDefaults(&http.Transport{Dial: net.Dial, TLSClientConfig: &tls.Config{RootCAs: localhostPool}}),
},
}
// Enable StreamingProxyRedirects for test.
utilconfig.DefaultFeatureGate.Set("StreamingProxyRedirects=true")
for k, tc := range testcases {
for _, redirect := range []bool{false, true} {
tcName := k
backendPath := "/hello"
if redirect {
tcName += " with redirect"
backendPath = "/redirect"
}
func() { // Cleanup after each test case.
backend := http.NewServeMux()
backend.Handle("/hello", websocket.Handler(func(ws *websocket.Conn) {
defer ws.Close()
body := make([]byte, 5)
ws.Read(body)
ws.Write([]byte("hello " + string(body)))
}))
backend.Handle("/redirect", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/hello", http.StatusFound)
}))
backendServer := tc.ServerFunc(backend)
defer backendServer.Close()
serverURL, _ := url.Parse(backendServer.URL)
serverURL.Path = backendPath
proxyHandler := &UpgradeAwareProxyHandler{
Location: serverURL,
Transport: tc.ProxyTransport,
InterceptRedirects: redirect,
}
proxy := httptest.NewServer(proxyHandler)
defer proxy.Close()
ws, err := websocket.Dial("ws://"+proxy.Listener.Addr().String()+"/some/path", "", "http://127.0.0.1/")
if err != nil {
t.Fatalf("%s: websocket dial err: %s", tcName, err)
}
defer ws.Close()
if _, err := ws.Write([]byte("world")); err != nil {
t.Fatalf("%s: write err: %s", tcName, err)
}
response := make([]byte, 20)
n, err := ws.Read(response)
if err != nil {
t.Fatalf("%s: read err: %s", tcName, err)
}
if e, a := "hello world", string(response[0:n]); e != a {
t.Fatalf("%s: expected '%#v', got '%#v'", tcName, e, a)
}
}()
}
}
}
func TestProxyUpgradeErrorResponse(t *testing.T) {
var (
responder *fakeResponder
expectedErr = errors.New("EXPECTED")
)
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
transport := http.DefaultTransport.(*http.Transport)
transport.Dial = func(network, addr string) (net.Conn, error) {
return &fakeConn{err: expectedErr}, nil
}
responder = &fakeResponder{t: t, w: w}
proxyHandler := &UpgradeAwareProxyHandler{
Location: &url.URL{
Host: "fake-backend",
},
UpgradeRequired: true,
Responder: responder,
Transport: transport,
}
proxyHandler.ServeHTTP(w, r)
}))
defer proxy.Close()
// Send request to proxy server.
req, err := http.NewRequest("POST", "http://"+proxy.Listener.Addr().String()+"/some/path", nil)
require.NoError(t, err)
req.Header.Set(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
// Expect error response.
assert.True(t, responder.called)
assert.Equal(t, fakeStatusCode, resp.StatusCode)
msg, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
assert.Contains(t, string(msg), expectedErr.Error())
}
func TestDefaultProxyTransport(t *testing.T) {
tests := []struct {
name,
url,
location,
expectedScheme,
expectedHost,
expectedPathPrepend string
}{
{
name: "simple path",
url: "http://test.server:8080/a/test/location",
location: "http://localhost/location",
expectedScheme: "http",
expectedHost: "test.server:8080",
expectedPathPrepend: "/a/test",
},
{
name: "empty path",
url: "http://test.server:8080/a/test/",
location: "http://localhost",
expectedScheme: "http",
expectedHost: "test.server:8080",
expectedPathPrepend: "/a/test",
},
{
name: "location ending in slash",
url: "http://test.server:8080/a/test/",
location: "http://localhost/",
expectedScheme: "http",
expectedHost: "test.server:8080",
expectedPathPrepend: "/a/test",
},
}
for _, test := range tests {
locURL, _ := url.Parse(test.location)
URL, _ := url.Parse(test.url)
h := UpgradeAwareProxyHandler{
Location: locURL,
}
result := h.defaultProxyTransport(URL, nil)
transport := result.(*corsRemovingTransport).RoundTripper.(*proxy.Transport)
if transport.Scheme != test.expectedScheme {
t.Errorf("%s: unexpected scheme. Actual: %s, Expected: %s", test.name, transport.Scheme, test.expectedScheme)
}
if transport.Host != test.expectedHost {
t.Errorf("%s: unexpected host. Actual: %s, Expected: %s", test.name, transport.Host, test.expectedHost)
}
if transport.PathPrepend != test.expectedPathPrepend {
t.Errorf("%s: unexpected path prepend. Actual: %s, Expected: %s", test.name, transport.PathPrepend, test.expectedPathPrepend)
}
}
}
func TestProxyRequestContentLengthAndTransferEncoding(t *testing.T) {
chunk := func(data []byte) []byte {
out := &bytes.Buffer{}
chunker := httputil.NewChunkedWriter(out)
for _, b := range data {
if _, err := chunker.Write([]byte{b}); err != nil {
panic(err)
}
}
chunker.Close()
out.Write([]byte("\r\n"))
return out.Bytes()
}
zip := func(data []byte) []byte {
out := &bytes.Buffer{}
zipper := gzip.NewWriter(out)
if _, err := zipper.Write(data); err != nil {
panic(err)
}
zipper.Close()
return out.Bytes()
}
sampleData := []byte("abcde")
table := map[string]struct {
reqHeaders http.Header
reqBody []byte
expectedHeaders http.Header
expectedBody []byte
}{
"content-length": {
reqHeaders: http.Header{
"Content-Length": []string{"5"},
},
reqBody: sampleData,
expectedHeaders: http.Header{
"Content-Length": []string{"5"},
"Content-Encoding": nil, // none set
"Transfer-Encoding": nil, // none set
},
expectedBody: sampleData,
},
"content-length + identity transfer-encoding": {
reqHeaders: http.Header{
"Content-Length": []string{"5"},
"Transfer-Encoding": []string{"identity"},
},
reqBody: sampleData,
expectedHeaders: http.Header{
"Content-Length": []string{"5"},
"Content-Encoding": nil, // none set
"Transfer-Encoding": nil, // gets removed
},
expectedBody: sampleData,
},
"content-length + gzip content-encoding": {
reqHeaders: http.Header{
"Content-Length": []string{strconv.Itoa(len(zip(sampleData)))},
"Content-Encoding": []string{"gzip"},
},
reqBody: zip(sampleData),
expectedHeaders: http.Header{
"Content-Length": []string{strconv.Itoa(len(zip(sampleData)))},
"Content-Encoding": []string{"gzip"},
"Transfer-Encoding": nil, // none set
},
expectedBody: zip(sampleData),
},
"chunked transfer-encoding": {
reqHeaders: http.Header{
"Transfer-Encoding": []string{"chunked"},
},
reqBody: chunk(sampleData),
expectedHeaders: http.Header{
"Content-Length": nil, // none set
"Content-Encoding": nil, // none set
"Transfer-Encoding": nil, // Transfer-Encoding gets removed
},
expectedBody: sampleData, // sample data is unchunked
},
"chunked transfer-encoding + gzip content-encoding": {
reqHeaders: http.Header{
"Content-Encoding": []string{"gzip"},
"Transfer-Encoding": []string{"chunked"},
},
reqBody: chunk(zip(sampleData)),
expectedHeaders: http.Header{
"Content-Length": nil, // none set
"Content-Encoding": []string{"gzip"},
"Transfer-Encoding": nil, // gets removed
},
expectedBody: zip(sampleData), // sample data is unchunked, but content-encoding is preserved
},
// "Transfer-Encoding: gzip" is not supported by go
// See http/transfer.go#fixTransferEncoding (https://golang.org/src/net/http/transfer.go#L427)
// Once it is supported, this test case should succeed
//
// "gzip+chunked transfer-encoding": {
// reqHeaders: http.Header{
// "Transfer-Encoding": []string{"chunked,gzip"},
// },
// reqBody: chunk(zip(sampleData)),
//
// expectedHeaders: http.Header{
// "Content-Length": nil, // no content-length headers
// "Transfer-Encoding": nil, // Transfer-Encoding gets removed
// },
// expectedBody: sampleData,
// },
}
successfulResponse := "backend passed tests"
for k, item := range table {
// Start the downstream server
downstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// Verify headers
for header, v := range item.expectedHeaders {
if !reflect.DeepEqual(v, req.Header[header]) {
t.Errorf("%s: Expected headers for %s to be %v, got %v", k, header, v, req.Header[header])
}
}
// Read body
body, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Errorf("%s: unexpected error %v", k, err)
}
req.Body.Close()
// Verify length
if req.ContentLength > 0 && req.ContentLength != int64(len(body)) {
t.Errorf("%s: ContentLength was %d, len(data) was %d", k, req.ContentLength, len(body))
}
// Verify content
if !bytes.Equal(item.expectedBody, body) {
t.Errorf("%s: Expected %q, got %q", k, string(item.expectedBody), string(body))
}
// Write successful response
w.Write([]byte(successfulResponse))
}))
defer downstreamServer.Close()
responder := &fakeResponder{t: t}
backendURL, _ := url.Parse(downstreamServer.URL)
proxyHandler := &UpgradeAwareProxyHandler{
Location: backendURL,
Responder: responder,
UpgradeRequired: false,
}
proxyServer := httptest.NewServer(proxyHandler)
defer proxyServer.Close()
// Dial the proxy server
conn, err := net.Dial(proxyServer.Listener.Addr().Network(), proxyServer.Listener.Addr().String())
if err != nil {
t.Errorf("unexpected error %v", err)
continue
}
defer conn.Close()
// Add standard http 1.1 headers
if item.reqHeaders == nil {
item.reqHeaders = http.Header{}
}
item.reqHeaders.Add("Connection", "close")
item.reqHeaders.Add("Host", proxyServer.Listener.Addr().String())
// Write the request headers
if _, err := fmt.Fprint(conn, "POST / HTTP/1.1\r\n"); err != nil {
t.Fatalf("%s unexpected error %v", k, err)
}
for header, values := range item.reqHeaders {
for _, value := range values {
if _, err := fmt.Fprintf(conn, "%s: %s\r\n", header, value); err != nil {
t.Fatalf("%s: unexpected error %v", k, err)
}
}
}
// Header separator
if _, err := fmt.Fprint(conn, "\r\n"); err != nil {
t.Fatalf("%s: unexpected error %v", k, err)
}
// Body
if _, err := conn.Write(item.reqBody); err != nil {
t.Fatalf("%s: unexpected error %v", k, err)
}
// Read response
response, err := ioutil.ReadAll(conn)
if err != nil {
t.Errorf("%s: unexpected error %v", k, err)
continue
}
if !strings.HasSuffix(string(response), successfulResponse) {
t.Errorf("%s: Did not get successful response: %s", k, string(response))
continue
}
}
}
// exampleCert was generated from crypto/tls/generate_cert.go with the following command:
// go run generate_cert.go --rsa-bits 512 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var exampleCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBcjCCAR6gAwIBAgIQBOUTYowZaENkZi0faI9DgTALBgkqhkiG9w0BAQswEjEQ
MA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2MDAw
MFowEjEQMA4GA1UEChMHQWNtZSBDbzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCZ
xfR3sgeHBraGFfF/24tTn4PRVAHOf2UOOxSQRs+aYjNqimFqf/SRIblQgeXdBJDR
gVK5F1Js2zwlehw0bHxRAgMBAAGjUDBOMA4GA1UdDwEB/wQEAwIApDATBgNVHSUE
DDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBYGA1UdEQQPMA2CC2V4YW1w
bGUuY29tMAsGCSqGSIb3DQEBCwNBAI/mfBB8dm33IpUl+acSyWfL6gX5Wc0FFyVj
dKeesE1XBuPX1My/rzU6Oy/YwX7LOL4FaeNUS6bbL4axSLPKYSs=
-----END CERTIFICATE-----`)
var exampleKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAJnF9HeyB4cGtoYV8X/bi1Ofg9FUAc5/ZQ47FJBGz5piM2qKYWp/
9JEhuVCB5d0EkNGBUrkXUmzbPCV6HDRsfFECAwEAAQJBAJLH9yPuButniACTn5L5
IJQw1mWQt6zBw9eCo41YWkA0866EgjC53aPZaRjXMp0uNJGdIsys2V5rCOOLWN2C
ODECIQDICHsi8QQQ9wpuJy8X5l8MAfxHL+DIqI84wQTeVM91FQIhAMTME8A18/7h
1Ad6drdnxAkuC0tX6Sx0LDozrmen+HFNAiAlcEDrt0RVkIcpOrg7tuhPLQf0oudl
Zvb3Xlj069awSQIgcT15E/43w2+RASifzVNhQ2MCTr1sSA8lL+xzK+REmnUCIBhQ
j4139pf8Re1J50zBxS/JlQfgDQi9sO9pYeiHIxNs
-----END RSA PRIVATE KEY-----`)
// localhostCert was generated from crypto/tls/generate_cert.go with the following command:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
-----END CERTIFICATE-----`)
// localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
-----END RSA PRIVATE KEY-----`)

View file

@ -0,0 +1,71 @@
/*
Copyright 2014 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 rest
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// Check the http error status from a location URL.
// And convert an error into a structured API object.
// Finally ensure we close the body before returning the error
type HttpResponseChecker interface {
Check(resp *http.Response) error
}
// Max length read from the response body of a location which returns error status
const (
maxReadLength = 50000
)
// A generic http response checker to transform the error.
type GenericHttpResponseChecker struct {
QualifiedResource schema.GroupResource
Name string
}
func (checker GenericHttpResponseChecker) Check(resp *http.Response) error {
if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent {
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(io.LimitReader(resp.Body, maxReadLength))
if err != nil {
return errors.NewInternalError(err)
}
bodyText := string(bodyBytes)
switch {
case resp.StatusCode == http.StatusInternalServerError:
return errors.NewInternalError(fmt.Errorf("%s", bodyText))
case resp.StatusCode == http.StatusBadRequest:
return errors.NewBadRequest(bodyText)
case resp.StatusCode == http.StatusNotFound:
return errors.NewGenericServerResponse(resp.StatusCode, "", checker.QualifiedResource, checker.Name, bodyText, 0, false)
}
return errors.NewGenericServerResponse(resp.StatusCode, "", checker.QualifiedResource, checker.Name, bodyText, 0, false)
}
return nil
}
func NewGenericHttpResponseChecker(qualifiedResource schema.GroupResource, name string) GenericHttpResponseChecker {
return GenericHttpResponseChecker{QualifiedResource: qualifiedResource, Name: name}
}

View file

@ -0,0 +1,95 @@
/*
Copyright 2014 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 rest
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/kubernetes/pkg/api"
)
func TestGenericHttpResponseChecker(t *testing.T) {
responseChecker := NewGenericHttpResponseChecker(api.Resource("pods"), "foo")
tests := []struct {
resp *http.Response
expectError bool
expected error
name string
}{
{
resp: &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("Success")),
StatusCode: http.StatusOK,
},
expectError: false,
name: "ok",
},
{
resp: &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("Invalid request.")),
StatusCode: http.StatusBadRequest,
},
expectError: true,
expected: errors.NewBadRequest("Invalid request."),
name: "bad request",
},
{
resp: &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("Pod does not exist.")),
StatusCode: http.StatusInternalServerError,
},
expectError: true,
expected: errors.NewInternalError(fmt.Errorf("%s", "Pod does not exist.")),
name: "internal server error",
},
}
for _, test := range tests {
err := responseChecker.Check(test.resp)
if test.expectError && err == nil {
t.Error("unexpected non-error")
}
if !test.expectError && err != nil {
t.Errorf("unexpected error: %v", err)
}
if test.expectError && !reflect.DeepEqual(err, test.expected) {
t.Errorf("expected: %s, saw: %s", test.expected, err)
}
}
}
func TestGenericHttpResponseCheckerLimitReader(t *testing.T) {
responseChecker := NewGenericHttpResponseChecker(api.Resource("pods"), "foo")
excessedString := strings.Repeat("a", (maxReadLength + 10000))
resp := &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(excessedString)),
StatusCode: http.StatusBadRequest,
}
err := responseChecker.Check(resp)
if err == nil {
t.Error("unexpected non-error")
}
if len(err.Error()) != maxReadLength {
t.Errorf("expected lenth of error message: %d, saw: %d", maxReadLength, len(err.Error()))
}
}

View file

@ -0,0 +1,79 @@
/*
Copyright 2014 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 rest
import (
"io"
"net/http"
"net/url"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
)
// LocationStreamer is a resource that streams the contents of a particular
// location URL
type LocationStreamer struct {
Location *url.URL
Transport http.RoundTripper
ContentType string
Flush bool
ResponseChecker HttpResponseChecker
}
// a LocationStreamer must implement a rest.ResourceStreamer
var _ rest.ResourceStreamer = &LocationStreamer{}
func (obj *LocationStreamer) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
// InputStream returns a stream with the contents of the URL location. If no location is provided,
// a null stream is returned.
func (s *LocationStreamer) InputStream(apiVersion, acceptHeader string) (stream io.ReadCloser, flush bool, contentType string, err error) {
if s.Location == nil {
// If no location was provided, return a null stream
return nil, false, "", nil
}
transport := s.Transport
if transport == nil {
transport = http.DefaultTransport
}
client := &http.Client{Transport: transport}
resp, err := client.Get(s.Location.String())
if err != nil {
return nil, false, "", err
}
if s.ResponseChecker != nil {
if err = s.ResponseChecker.Check(resp); err != nil {
return nil, false, "", err
}
}
contentType = s.ContentType
if len(contentType) == 0 {
contentType = resp.Header.Get("Content-Type")
if len(contentType) > 0 {
contentType = strings.TrimSpace(strings.SplitN(contentType, ";", 2)[0])
}
}
flush = s.Flush
stream = resp.Body
return
}

View file

@ -0,0 +1,148 @@
/*
Copyright 2014 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 rest
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/kubernetes/pkg/api"
)
func TestInputStreamReader(t *testing.T) {
resultString := "Test output"
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte(resultString))
}))
defer s.Close()
u, err := url.Parse(s.URL)
if err != nil {
t.Errorf("Error parsing server URL: %v", err)
return
}
streamer := &LocationStreamer{
Location: u,
}
readCloser, _, _, err := streamer.InputStream("", "")
if err != nil {
t.Errorf("Unexpected error when getting stream: %v", err)
return
}
defer readCloser.Close()
result, err := ioutil.ReadAll(readCloser)
if string(result) != resultString {
t.Errorf("Stream content does not match. Got: %s. Expected: %s.", string(result), resultString)
}
}
func TestInputStreamNullLocation(t *testing.T) {
streamer := &LocationStreamer{
Location: nil,
}
readCloser, _, _, err := streamer.InputStream("", "")
if err != nil {
t.Errorf("Unexpected error when getting stream with null location: %v", err)
}
if readCloser != nil {
t.Errorf("Expected stream to be nil. Got: %#v", readCloser)
}
}
type testTransport struct {
body string
err error
}
func (tt *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
r := bufio.NewReader(bytes.NewBufferString(tt.body))
return http.ReadResponse(r, req)
}
func fakeTransport(mime, message string) http.RoundTripper {
content := fmt.Sprintf("HTTP/1.1 200 OK\nContent-Type: %s\n\n%s", mime, message)
return &testTransport{body: content}
}
func TestInputStreamContentType(t *testing.T) {
location, _ := url.Parse("http://www.example.com")
streamer := &LocationStreamer{
Location: location,
Transport: fakeTransport("application/json", "hello world"),
}
readCloser, _, contentType, err := streamer.InputStream("", "")
if err != nil {
t.Errorf("Unexpected error when getting stream: %v", err)
return
}
defer readCloser.Close()
if contentType != "application/json" {
t.Errorf("Unexpected content type. Got: %s. Expected: application/json", contentType)
}
}
func TestInputStreamTransport(t *testing.T) {
message := "hello world"
location, _ := url.Parse("http://www.example.com")
streamer := &LocationStreamer{
Location: location,
Transport: fakeTransport("text/plain", message),
}
readCloser, _, _, err := streamer.InputStream("", "")
if err != nil {
t.Errorf("Unexpected error when getting stream: %v", err)
return
}
defer readCloser.Close()
result, err := ioutil.ReadAll(readCloser)
if string(result) != message {
t.Errorf("Stream content does not match. Got: %s. Expected: %s.", string(result), message)
}
}
func fakeInternalServerErrorTransport(mime, message string) http.RoundTripper {
content := fmt.Sprintf("HTTP/1.1 500 \"Internal Server Error\"\nContent-Type: %s\n\n%s", mime, message)
return &testTransport{body: content}
}
func TestInputStreamInternalServerErrorTransport(t *testing.T) {
message := "Pod is in PodPending"
location, _ := url.Parse("http://www.example.com")
streamer := &LocationStreamer{
Location: location,
Transport: fakeInternalServerErrorTransport("text/plain", message),
ResponseChecker: NewGenericHttpResponseChecker(api.Resource(""), ""),
}
expectedError := errors.NewInternalError(fmt.Errorf("%s", message))
_, _, _, err := streamer.InputStream("", "")
if err == nil {
t.Errorf("unexpected non-error")
return
}
if !reflect.DeepEqual(err, expectedError) {
t.Errorf("StreamInternalServerError does not match. Got: %s. Expected: %s.", err, expectedError)
}
}

View file

@ -0,0 +1,62 @@
/*
Copyright 2015 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 generic
import (
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
"k8s.io/kubernetes/pkg/storage/storagebackend"
"k8s.io/kubernetes/pkg/storage/storagebackend/factory"
)
// StorageDecorator is a function signature for producing a storage.Interface
// and an associated DestroyFunc from given parameters.
type StorageDecorator func(
config *storagebackend.Config,
capacity int,
objectType runtime.Object,
resourcePrefix string,
keyFunc func(obj runtime.Object) (string, error),
newListFunc func() runtime.Object,
getAttrsFunc storage.AttrFunc,
trigger storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc)
// UndecoratedStorage returns the given a new storage from the given config
// without any decoration.
func UndecoratedStorage(
config *storagebackend.Config,
capacity int,
objectType runtime.Object,
resourcePrefix string,
keyFunc func(obj runtime.Object) (string, error),
newListFunc func() runtime.Object,
getAttrsFunc storage.AttrFunc,
trigger storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) {
return NewRawStorage(config)
}
// NewRawStorage creates the low level kv storage. This is a work-around for current
// two layer of same storage interface.
// TODO: Once cacher is enabled on all registries (event registry is special), we will remove this method.
func NewRawStorage(config *storagebackend.Config) (storage.Interface, factory.DestroyFunc) {
s, d, err := factory.Create(*config)
if err != nil {
glog.Fatalf("Unable to create storage backend: config (%v), err (%v)", config, err)
}
return s, d
}

View file

@ -17,7 +17,6 @@ go_library(
"export.go",
"meta.go",
"rest.go",
"types.go",
"update.go",
],
tags = ["automanaged"],
@ -28,12 +27,13 @@ go_library(
"//pkg/util/uuid:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/internalversion",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/storage/names",
],
)
@ -49,7 +49,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/genericapiserver/api/rest/resttest:all-srcs",
"//pkg/genericapiserver/registry/rest/resttest:all-srcs",
],
tags = ["automanaged"],
)
@ -62,6 +62,7 @@ go_test(
deps = [
"//pkg/api:go_default_library",
"//pkg/util/uuid:go_default_library",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)

View file

@ -18,12 +18,12 @@ package rest
import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation/genericvalidation"
"k8s.io/kubernetes/pkg/api/validation/path"
)
@ -33,7 +33,7 @@ import (
// API conventions.
type RESTCreateStrategy interface {
runtime.ObjectTyper
// The name generate is used when the standard GenerateName field is set.
// The name generator is used when the standard GenerateName field is set.
// The NameGenerator will be invoked prior to validation.
names.NameGenerator
@ -44,11 +44,16 @@ type RESTCreateStrategy interface {
// sort order-insensitive list fields, etc. This should not remove fields
// whose presence would be considered a validation error.
PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object)
// Validate is invoked after default fields in the object have been filled in before
// the object is persisted. This method should not mutate the object.
// Validate returns an ErrorList with validation errors or nil. Validate
// is invoked after default fields in the object have been filled in
// before the object is persisted. This method should not mutate the
// object.
Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList
// Canonicalize is invoked after validation has succeeded but before the
// object has been persisted. This method may mutate the object.
// Canonicalize allows an object to be mutated into a canonical form. This
// ensures that code that operates on these objects can rely on the common
// form for things like comparison. Canonicalize is invoked after
// validation has succeeded but before the object has been persisted.
// This method may mutate the object.
Canonicalize(obj runtime.Object)
}
@ -66,7 +71,7 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx genericapirequest.Context, ob
return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
}
} else {
objectMeta.Namespace = api.NamespaceNone
objectMeta.Namespace = metav1.NamespaceNone
}
objectMeta.DeletionTimestamp = nil
objectMeta.DeletionGracePeriodSeconds = nil
@ -115,8 +120,8 @@ func CheckGeneratedNameError(strategy RESTCreateStrategy, err error, obj runtime
}
// objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error.
func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.ObjectMeta, schema.GroupVersionKind, error) {
objectMeta, err := api.ObjectMetaFor(obj)
func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*metav1.ObjectMeta, schema.GroupVersionKind, error) {
objectMeta, err := metav1.ObjectMetaFor(obj)
if err != nil {
return nil, schema.GroupVersionKind{}, errors.NewInternalError(err)
}

View file

@ -24,8 +24,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
)
// RESTDeleteStrategy defines deletion behavior on an object that follows Kubernetes
@ -53,7 +52,7 @@ type GarbageCollectionDeleteStrategy interface {
type RESTGracefulDeleteStrategy interface {
// CheckGracefulDelete should return true if the object can be gracefully deleted and set
// any default values on the DeleteOptions.
CheckGracefulDelete(ctx genericapirequest.Context, obj runtime.Object, options *api.DeleteOptions) bool
CheckGracefulDelete(ctx genericapirequest.Context, obj runtime.Object, options *metav1.DeleteOptions) bool
}
// BeforeDelete tests whether the object can be gracefully deleted. If graceful is set the object
@ -62,7 +61,7 @@ type RESTGracefulDeleteStrategy interface {
// condition cannot be checked or the gracePeriodSeconds is invalid. The options argument may be updated with
// default values if graceful is true. Second place where we set deletionTimestamp is pkg/registry/generic/registry/store.go
// this function is responsible for setting deletionTimestamp during gracefulDeletion, other one for cascading deletions.
func BeforeDelete(strategy RESTDeleteStrategy, ctx genericapirequest.Context, obj runtime.Object, options *api.DeleteOptions) (graceful, gracefulPending bool, err error) {
func BeforeDelete(strategy RESTDeleteStrategy, ctx genericapirequest.Context, obj runtime.Object, options *metav1.DeleteOptions) (graceful, gracefulPending bool, err error) {
objectMeta, gvk, kerr := objectMetaAndKind(strategy, obj)
if kerr != nil {
return false, false, kerr

View file

@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package rest defines common logic around changes to Kubernetes resources.
package rest // import "k8s.io/kubernetes/pkg/genericapiserver/api/rest"
// Package rest defines common logic around changes to Kubernetes-style resources.
package rest // import "k8s.io/kubernetes/pkg/genericapiserver/registry/rest"

View file

@ -18,10 +18,14 @@ package rest
import (
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
)
// RESTExportStrategy is the interface that defines how to export a Kubernetes object
// RESTExportStrategy is the interface that defines how to export a Kubernetes
// object. An exported object is stripped of non-user-settable fields and
// optionally, the identifying information related to the object's identity in
// the cluster so that it can be loaded into a different namespace or entirely
// different cluster without conflict.
type RESTExportStrategy interface {
// Export strips fields that can not be set by the user. If 'exact' is false
// fields specific to the cluster are also stripped

View file

@ -18,13 +18,12 @@ package rest
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/api"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/util/uuid"
)
// FillObjectMetaSystemFields populates fields that are managed by the system on ObjectMeta.
func FillObjectMetaSystemFields(ctx genericapirequest.Context, meta *api.ObjectMeta) {
func FillObjectMetaSystemFields(ctx genericapirequest.Context, meta *metav1.ObjectMeta) {
meta.CreationTimestamp = metav1.Now()
// allows admission controllers to assign a UID earlier in the request processing
// to support tracking resources pending creation.
@ -36,9 +35,12 @@ func FillObjectMetaSystemFields(ctx genericapirequest.Context, meta *api.ObjectM
meta.SelfLink = ""
}
// ValidNamespace returns false if the namespace on the context differs from the resource. If the resource has no namespace, it is set to the value in the context.
// TODO(sttts): move into pkg/genericapiserver/api
func ValidNamespace(ctx genericapirequest.Context, resource *api.ObjectMeta) bool {
// ValidNamespace returns false if the namespace on the context differs from
// the resource. If the resource has no namespace, it is set to the value in
// the context.
//
// TODO(sttts): move into pkg/genericapiserver/endpoints
func ValidNamespace(ctx genericapirequest.Context, resource *metav1.ObjectMeta) bool {
ns, ok := genericapirequest.NamespaceFrom(ctx)
if len(resource.Namespace) == 0 {
resource.Namespace = ns

View file

@ -19,7 +19,8 @@ package rest
import (
"testing"
genericapirequest "k8s.io/apiserver/pkg/request"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/uuid"
)
@ -27,7 +28,7 @@ import (
// TestFillObjectMetaSystemFields validates that system populated fields are set on an object
func TestFillObjectMetaSystemFields(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
resource := api.ObjectMeta{}
resource := metav1.ObjectMeta{}
FillObjectMetaSystemFields(ctx, &resource)
if resource.CreationTimestamp.Time.IsZero() {
t.Errorf("resource.CreationTimestamp is zero")
@ -37,7 +38,7 @@ func TestFillObjectMetaSystemFields(t *testing.T) {
// verify we can inject a UID
uid := uuid.NewUUID()
ctx = genericapirequest.WithUID(ctx, uid)
resource = api.ObjectMeta{}
resource = metav1.ObjectMeta{}
FillObjectMetaSystemFields(ctx, &resource)
if resource.UID != uid {
t.Errorf("resource.UID expected: %v, actual: %v", uid, resource.UID)
@ -47,7 +48,7 @@ func TestFillObjectMetaSystemFields(t *testing.T) {
// TestHasObjectMetaSystemFieldValues validates that true is returned if and only if all fields are populated
func TestHasObjectMetaSystemFieldValues(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
resource := api.ObjectMeta{}
resource := metav1.ObjectMeta{}
if api.HasObjectMetaSystemFieldValues(&resource) {
t.Errorf("the resource does not have all fields yet populated, but incorrectly reports it does")
}
@ -68,7 +69,7 @@ func TestValidNamespace(t *testing.T) {
if namespace != resource.Namespace {
t.Fatalf("expected resource to have the default namespace assigned during validation")
}
resource = api.ReplicationController{ObjectMeta: api.ObjectMeta{Namespace: "other"}}
resource = api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Namespace: "other"}}
if ValidNamespace(ctx, &resource.ObjectMeta) {
t.Fatalf("Expected error that resource and context errors do not match because resource has different namespace")
}

View file

@ -21,11 +21,12 @@ import (
"net/http"
"net/url"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/api"
)
@ -71,7 +72,7 @@ type Lister interface {
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
NewList() runtime.Object
// List selects resources in the storage which match to the selector. 'options' can be nil.
List(ctx genericapirequest.Context, options *api.ListOptions) (runtime.Object, error)
List(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (runtime.Object, error)
}
// Exporter is an object that knows how to strip a RESTful resource for export
@ -131,7 +132,7 @@ type GracefulDeleter interface {
// returned error value err when the specified resource is not found.
// Delete *may* return the object that was deleted, or a status object indicating additional
// information about deletion.
Delete(ctx genericapirequest.Context, name string, options *api.DeleteOptions) (runtime.Object, error)
Delete(ctx genericapirequest.Context, name string, options *metav1.DeleteOptions) (runtime.Object, error)
}
// GracefulDeleteAdapter adapts the Deleter interface to GracefulDeleter
@ -140,7 +141,7 @@ type GracefulDeleteAdapter struct {
}
// Delete implements RESTGracefulDeleter in terms of Deleter
func (w GracefulDeleteAdapter) Delete(ctx genericapirequest.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
func (w GracefulDeleteAdapter) Delete(ctx genericapirequest.Context, name string, options *metav1.DeleteOptions) (runtime.Object, error) {
return w.Deleter.Delete(ctx, name)
}
@ -152,7 +153,7 @@ type CollectionDeleter interface {
// them or return an invalid request error.
// DeleteCollection may not be atomic - i.e. it may delete some objects and still
// return an error after it. On success, returns a list of deleted objects.
DeleteCollection(ctx genericapirequest.Context, options *api.DeleteOptions, listOptions *api.ListOptions) (runtime.Object, error)
DeleteCollection(ctx genericapirequest.Context, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error)
}
// Creater is an object that can create an instance of a RESTful object.
@ -225,7 +226,7 @@ type Watcher interface {
// are supported; an error should be returned if 'field' tries to select on a field that
// isn't supported. 'resourceVersion' allows for continuing/starting a watch at a
// particular version.
Watch(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error)
Watch(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
}
// StandardStorage is an interface covering the common verbs. Provided for testing whether a

View file

@ -14,16 +14,17 @@ go_library(
deps = [
"//pkg/api:go_default_library",
"//pkg/api/validation/path:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/genericapiserver/api/rest:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/internalversion",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/conversion",
"//vendor:k8s.io/apimachinery/pkg/fields",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)

View file

@ -24,17 +24,18 @@ import (
"time"
"k8s.io/apimachinery/pkg/api/errors"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
genericapirequest "k8s.io/apiserver/pkg/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation/path"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
"k8s.io/kubernetes/pkg/genericapiserver/registry/rest"
)
type Tester struct {
@ -90,7 +91,7 @@ func (t *Tester) ReturnDeletedObject() *Tester {
// Returns NamespaceNone for cluster-scoped objects.
func (t *Tester) TestNamespace() string {
if t.clusterScope {
return api.NamespaceNone
return metav1.NamespaceNone
}
return "test"
}
@ -104,8 +105,8 @@ func (t *Tester) TestContext() genericapirequest.Context {
return genericapirequest.WithNamespace(genericapirequest.NewContext(), t.TestNamespace())
}
func (t *Tester) getObjectMetaOrFail(obj runtime.Object) *api.ObjectMeta {
meta, err := api.ObjectMetaFor(obj)
func (t *Tester) getObjectMetaOrFail(obj runtime.Object) *metav1.ObjectMeta {
meta, err := metav1.ObjectMetaFor(obj)
if err != nil {
t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, obj)
}
@ -116,7 +117,7 @@ func (t *Tester) setObjectMeta(obj runtime.Object, name string) {
meta := t.getObjectMetaOrFail(obj)
meta.Name = name
if t.clusterScope {
meta.Namespace = api.NamespaceNone
meta.Namespace = metav1.NamespaceNone
} else {
meta.Namespace = genericapirequest.NamespaceValue(t.TestContext())
}
@ -169,7 +170,7 @@ func (t *Tester) TestUpdate(valid runtime.Object, createFn CreateFunc, getFn Get
t.testUpdateFailsOnVersionTooOld(copyOrDie(valid), createFn, getFn)
t.testUpdateOnNotFound(copyOrDie(valid))
if !t.clusterScope {
t.testUpdateRejectsMismatchedNamespace(copyOrDie(valid), createFn)
t.testUpdateRejectsMismatchedNamespace(copyOrDie(valid), createFn, getFn)
}
t.testUpdateInvokesValidation(copyOrDie(valid), createFn, invalidUpdateFn...)
t.testUpdateWithWrongUID(copyOrDie(valid), createFn, getFn)
@ -225,7 +226,7 @@ func (t *Tester) TestWatch(
// Creation tests.
func (t *Tester) delete(ctx genericapirequest.Context, obj runtime.Object) error {
objectMeta, err := api.ObjectMetaFor(obj)
objectMeta, err := metav1.ObjectMetaFor(obj)
if err != nil {
return err
}
@ -293,7 +294,7 @@ func (t *Tester) testCreateDiscardsObjectNamespace(valid runtime.Object) {
}
defer t.delete(t.TestContext(), created)
createdObjectMeta := t.getObjectMetaOrFail(created)
if createdObjectMeta.Namespace != api.NamespaceNone {
if createdObjectMeta.Namespace != metav1.NamespaceNone {
t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace)
}
}
@ -342,7 +343,7 @@ func (t *Tester) testCreateIgnoresContextNamespace(valid runtime.Object) {
}
defer t.delete(ctx, created)
createdObjectMeta := t.getObjectMetaOrFail(created)
if createdObjectMeta.Namespace != api.NamespaceNone {
if createdObjectMeta.Namespace != metav1.NamespaceNone {
t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace)
}
}
@ -361,7 +362,7 @@ func (t *Tester) testCreateIgnoresMismatchedNamespace(valid runtime.Object) {
}
defer t.delete(ctx, created)
createdObjectMeta := t.getObjectMetaOrFail(created)
if createdObjectMeta.Namespace != api.NamespaceNone {
if createdObjectMeta.Namespace != metav1.NamespaceNone {
t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace)
}
}
@ -686,7 +687,7 @@ func (t *Tester) testUpdateOnNotFound(obj runtime.Object) {
}
}
func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, createFn CreateFunc) {
func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, createFn CreateFunc, getFn GetFunc) {
ctx := t.TestContext()
foo := copyOrDie(obj)
@ -695,11 +696,16 @@ func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, create
t.Errorf("unexpected error: %v", err)
}
objectMeta := t.getObjectMetaOrFail(obj)
storedFoo, err := getFn(ctx, foo)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
objectMeta := t.getObjectMetaOrFail(storedFoo)
objectMeta.Name = t.namer(1)
objectMeta.Namespace = "not-default"
obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), "foo1", rest.DefaultUpdatedObjectInfo(obj, api.Scheme))
obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), "foo1", rest.DefaultUpdatedObjectInfo(storedFoo, api.Scheme))
if obj != nil || updated {
t.Errorf("expected nil object and not updated")
}
@ -757,7 +763,7 @@ func (t *Tester) testDeleteNoGraceful(obj runtime.Object, createFn CreateFunc, g
t.Errorf("unexpected error: %v", err)
}
objectMeta := t.getObjectMetaOrFail(foo)
obj, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10))
obj, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(10))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -797,12 +803,12 @@ func (t *Tester) testDeleteWithUID(obj runtime.Object, createFn CreateFunc, getF
if err := createFn(ctx, foo); err != nil {
t.Errorf("unexpected error: %v", err)
}
obj, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewPreconditionDeleteOptions("UID1111"))
obj, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewPreconditionDeleteOptions("UID1111"))
if err == nil || !errors.IsConflict(err) {
t.Errorf("unexpected error: %v", err)
}
obj, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewPreconditionDeleteOptions("UID0000"))
obj, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewPreconditionDeleteOptions("UID0000"))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -834,7 +840,7 @@ func (t *Tester) testDeleteGracefulHasDefault(obj runtime.Object, createFn Creat
}
objectMeta := t.getObjectMetaOrFail(foo)
generation := objectMeta.Generation
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &metav1.DeleteOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -865,7 +871,7 @@ func (t *Tester) testDeleteGracefulWithValue(obj runtime.Object, createFn Create
}
objectMeta := t.getObjectMetaOrFail(foo)
generation := objectMeta.Generation
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(expectedGrace+2))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -896,7 +902,7 @@ func (t *Tester) testDeleteGracefulExtend(obj runtime.Object, createFn CreateFun
}
objectMeta := t.getObjectMetaOrFail(foo)
generation := objectMeta.Generation
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(expectedGrace))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -905,7 +911,7 @@ func (t *Tester) testDeleteGracefulExtend(obj runtime.Object, createFn CreateFun
}
// second delete duration is ignored
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(expectedGrace+2))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -932,7 +938,7 @@ func (t *Tester) testDeleteGracefulImmediate(obj runtime.Object, createFn Create
}
objectMeta := t.getObjectMetaOrFail(foo)
generation := objectMeta.Generation
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(expectedGrace))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -941,7 +947,7 @@ func (t *Tester) testDeleteGracefulImmediate(obj runtime.Object, createFn Create
}
// second delete is immediate, resource is deleted
out, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(0))
out, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(0))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -991,7 +997,7 @@ func (t *Tester) testDeleteGracefulShorten(obj runtime.Object, createFn CreateFu
bigGrace = 2 * expectedGrace
}
objectMeta := t.getObjectMetaOrFail(foo)
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(bigGrace))
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(bigGrace))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -1003,7 +1009,7 @@ func (t *Tester) testDeleteGracefulShorten(obj runtime.Object, createFn CreateFu
deletionTimestamp := *objectMeta.DeletionTimestamp
// second delete duration is ignored
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, metav1.NewDeleteOptions(expectedGrace))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -1191,7 +1197,7 @@ func (t *Tester) testListMatchLabels(obj runtime.Object, assignFn AssignFunc) {
filtered := []runtime.Object{objs[1]}
selector := labels.SelectorFromSet(labels.Set(testLabels))
options := &api.ListOptions{LabelSelector: selector}
options := &metainternalversion.ListOptions{LabelSelector: selector}
listObj, err := t.storage.(rest.Lister).List(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -1233,7 +1239,7 @@ func (t *Tester) testWatchFields(obj runtime.Object, emitFn EmitFunc, fieldsPass
for _, field := range fieldsPass {
for _, action := range actions {
options := &api.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v, %v", err, action)
@ -1257,7 +1263,7 @@ func (t *Tester) testWatchFields(obj runtime.Object, emitFn EmitFunc, fieldsPass
for _, field := range fieldsFail {
for _, action := range actions {
options := &api.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -1282,7 +1288,7 @@ func (t *Tester) testWatchLabels(obj runtime.Object, emitFn EmitFunc, labelsPass
for _, label := range labelsPass {
for _, action := range actions {
options := &api.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -1305,7 +1311,7 @@ func (t *Tester) testWatchLabels(obj runtime.Object, emitFn EmitFunc, labelsPass
for _, label := range labelsFail {
for _, action := range actions {
options := &api.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)

View file

@ -21,9 +21,10 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation/genericvalidation"
)
@ -47,8 +48,11 @@ type RESTUpdateStrategy interface {
// filled in before the object is persisted. This method should not mutate
// the object.
ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList
// Canonicalize is invoked after validation has succeeded but before the
// object has been persisted. This method may mutate the object.
// Canonicalize allows an object to be mutated into a canonical form. This
// ensures that code that operates on these objects can rely on the common
// form for things like comparison. Canonicalize is invoked after
// validation has succeeded but before the object has been persisted.
// This method may mutate the object.
Canonicalize(obj runtime.Object)
// AllowUnconditionalUpdate returns true if the object can be updated
// unconditionally (irrespective of the latest resource version), when
@ -59,11 +63,11 @@ type RESTUpdateStrategy interface {
// TODO: add other common fields that require global validation.
func validateCommonFields(obj, old runtime.Object) (field.ErrorList, error) {
allErrs := field.ErrorList{}
objectMeta, err := api.ObjectMetaFor(obj)
objectMeta, err := metav1.ObjectMetaFor(obj)
if err != nil {
return nil, fmt.Errorf("failed to get new object metadata: %v", err)
}
oldObjectMeta, err := api.ObjectMetaFor(old)
oldObjectMeta, err := metav1.ObjectMetaFor(old)
if err != nil {
return nil, fmt.Errorf("failed to get old object metadata: %v", err)
}
@ -85,10 +89,10 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx genericapirequest.Context, ob
return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
}
} else {
objectMeta.Namespace = api.NamespaceNone
objectMeta.Namespace = metav1.NamespaceNone
}
// Ensure requests cannot update generation
oldMeta, err := api.ObjectMetaFor(old)
oldMeta, err := metav1.ObjectMetaFor(old)
if err != nil {
return err
}

View file

@ -0,0 +1,131 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"genericapiserver_test.go",
"resource_config_test.go",
"serve_test.go",
"server_run_options_test.go",
"storage_factory_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/generated/openapi:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/genericapiserver/server/options:go_default_library",
"//pkg/storage/etcd/testing:go_default_library",
"//pkg/storage/storagebackend:go_default_library",
"//pkg/util/config:go_default_library",
"//vendor:github.com/go-openapi/spec",
"//vendor:github.com/stretchr/testify/assert",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/apimachinery",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apimachinery/pkg/version",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/client-go/util/cert",
],
)
go_library(
name = "go_default_library",
srcs = [
"config.go",
"config_selfclient.go",
"discovery.go",
"doc.go",
"genericapiserver.go",
"healthz.go",
"hooks.go",
"resource_config.go",
"resource_encoding_config.go",
"serve.go",
"storage_factory.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/genericapiserver/authenticator:go_default_library",
"//pkg/genericapiserver/authorizer:go_default_library",
"//pkg/genericapiserver/endpoints:go_default_library",
"//pkg/genericapiserver/endpoints/filters:go_default_library",
"//pkg/genericapiserver/endpoints/openapi:go_default_library",
"//pkg/genericapiserver/registry/rest:go_default_library",
"//pkg/genericapiserver/server/filters:go_default_library",
"//pkg/genericapiserver/server/mux:go_default_library",
"//pkg/genericapiserver/server/options:go_default_library",
"//pkg/genericapiserver/server/routes:go_default_library",
"//pkg/storage/storagebackend:go_default_library",
"//vendor:github.com/coreos/go-systemd/daemon",
"//vendor:github.com/emicklei/go-restful",
"//vendor:github.com/emicklei/go-restful/swagger",
"//vendor:github.com/go-openapi/spec",
"//vendor:github.com/golang/glog",
"//vendor:github.com/pborman/uuid",
"//vendor:github.com/pkg/errors",
"//vendor:gopkg.in/natefinch/lumberjack.v2",
"//vendor:k8s.io/apimachinery/pkg/apimachinery",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/openapi",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"//vendor:k8s.io/apimachinery/pkg/util/net",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apimachinery/pkg/util/validation",
"//vendor:k8s.io/apimachinery/pkg/version",
"//vendor:k8s.io/apiserver/pkg/admission",
"//vendor:k8s.io/apiserver/pkg/authentication/authenticator",
"//vendor:k8s.io/apiserver/pkg/authentication/request/union",
"//vendor:k8s.io/apiserver/pkg/authentication/user",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/authorization/union",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/server/healthz",
"//vendor:k8s.io/client-go/rest",
"//vendor:k8s.io/client-go/util/cert",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/genericapiserver/server/filters:all-srcs",
"//pkg/genericapiserver/server/mux:all-srcs",
"//pkg/genericapiserver/server/openapi:all-srcs",
"//pkg/genericapiserver/server/options:all-srcs",
"//pkg/genericapiserver/server/routes:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package genericapiserver
package server
import (
"crypto/tls"
@ -40,27 +40,26 @@ import (
openapicommon "k8s.io/apimachinery/pkg/openapi"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/version"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/authenticator"
authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
authorizerunion "k8s.io/apiserver/pkg/authorization/union"
"k8s.io/apiserver/pkg/healthz"
apirequest "k8s.io/apiserver/pkg/request"
"k8s.io/kubernetes/pkg/admission"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/healthz"
restclient "k8s.io/client-go/rest"
certutil "k8s.io/client-go/util/cert"
"k8s.io/kubernetes/pkg/api"
authhandlers "k8s.io/kubernetes/pkg/auth/handlers"
"k8s.io/kubernetes/pkg/client/restclient"
genericapifilters "k8s.io/kubernetes/pkg/genericapiserver/api/filters"
apiopenapi "k8s.io/kubernetes/pkg/genericapiserver/api/openapi"
genericauthenticator "k8s.io/kubernetes/pkg/genericapiserver/authenticator"
genericauthorizer "k8s.io/kubernetes/pkg/genericapiserver/authorizer"
genericfilters "k8s.io/kubernetes/pkg/genericapiserver/filters"
"k8s.io/kubernetes/pkg/genericapiserver/mux"
"k8s.io/kubernetes/pkg/genericapiserver/options"
"k8s.io/kubernetes/pkg/genericapiserver/routes"
certutil "k8s.io/kubernetes/pkg/util/cert"
"k8s.io/kubernetes/pkg/version"
genericapifilters "k8s.io/kubernetes/pkg/genericapiserver/endpoints/filters"
apiopenapi "k8s.io/kubernetes/pkg/genericapiserver/endpoints/openapi"
genericfilters "k8s.io/kubernetes/pkg/genericapiserver/server/filters"
"k8s.io/kubernetes/pkg/genericapiserver/server/mux"
"k8s.io/kubernetes/pkg/genericapiserver/server/options"
"k8s.io/kubernetes/pkg/genericapiserver/server/routes"
)
const (
@ -573,7 +572,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) (secure, insec
handler = genericapifilters.WithAuthorization(handler, c.RequestContextMapper, c.Authorizer)
handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
handler = audit(handler) // before impersonation to read original user
handler = authhandlers.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, authhandlers.Unauthorized(c.SupportsBasicAuth))
handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.SupportsBasicAuth))
return handler
}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package genericapiserver
package server
import (
"bytes"
@ -24,7 +24,7 @@ import (
"fmt"
"net"
"k8s.io/kubernetes/pkg/client/restclient"
restclient "k8s.io/client-go/rest"
"github.com/golang/glog"
)

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package genericapiserver
package server
import (
"net"

View file

@ -1,5 +1,5 @@
/*
Copyright 2016 The Kubernetes Authors.
Copyright 2015 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.
@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// package common holds shared codes and types between open API code generator and spec generator.
package common
// Package server contains the plumbing to create kubernetes-like API server command.
package server // import "k8s.io/kubernetes/pkg/genericapiserver/server"

View file

@ -20,14 +20,14 @@ go_library(
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/util:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apiserver/pkg/httplog",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/server/httplog",
],
)
@ -41,10 +41,11 @@ go_test(
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/genericapiserver/api/filters:go_default_library",
"//pkg/genericapiserver/endpoints/filters:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apiserver/pkg/request",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
],
)

View file

@ -16,4 +16,4 @@ limitations under the License.
// Package filters contains all the http handler chain filters which
// are not api related.
package filters // import "k8s.io/kubernetes/pkg/genericapiserver/filters"
package filters // import "k8s.io/kubernetes/pkg/genericapiserver/server/filters"

View file

@ -20,7 +20,7 @@ import (
"net/http"
"k8s.io/apimachinery/pkg/util/sets"
apirequest "k8s.io/apiserver/pkg/request"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
)
// LongRunningRequestCheck is a predicate which is true for long-running http requests.

View file

@ -22,9 +22,9 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/httplog"
apirequest "k8s.io/apiserver/pkg/request"
genericapirequest "k8s.io/apiserver/pkg/request"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/httplog"
"github.com/golang/glog"
)

Some files were not shown because too many files have changed in this diff Show more