Merge branch 'master' into correct-deprecation-errors
This commit is contained in:
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -2,6 +2,12 @@
|
||||
|
||||
**Is this a request for help?** (If yes, you should use our troubleshooting guide and community support channels, see http://kubernetes.io/docs/troubleshooting/.):
|
||||
|
||||
**Note:** Please file issues for subcomponents under the appropriate repo
|
||||
|
||||
| Component | Repo |
|
||||
| --------- | ------------------------------------------------------------------ |
|
||||
| kubectl | [kubernetes/kubectl](https://github.com/kubernetes/kubectl/issues/new) |
|
||||
| kubeadm | [kubernetes/kubeadm](https://github.com/kubernetes/kubeadm/issues/new) |
|
||||
|
||||
**What keywords did you search in Kubernetes issues before filing this one?** (If you have found any duplicates, you should instead reply there.):
|
||||
|
||||
|
@@ -1203,14 +1203,14 @@ function create-cluster-autoscaler-mig-config() {
|
||||
|
||||
# Each MIG must have at least one node, so the min number of nodes
|
||||
# must be greater or equal to the number of migs.
|
||||
if [[ ${AUTOSCALER_MIN_NODES} < ${NUM_MIGS} ]]; then
|
||||
if [[ ${AUTOSCALER_MIN_NODES} -lt ${NUM_MIGS} ]]; then
|
||||
echo "AUTOSCALER_MIN_NODES must be greater or equal ${NUM_MIGS}"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Each MIG must have at least one node, so the min number of nodes
|
||||
# must be greater or equal to the number of migs.
|
||||
if [[ ${AUTOSCALER_MAX_NODES} < ${NUM_MIGS} ]]; then
|
||||
if [[ ${AUTOSCALER_MAX_NODES} -lt ${NUM_MIGS} ]]; then
|
||||
echo "AUTOSCALER_MAX_NODES must be greater or equal ${NUM_MIGS}"
|
||||
exit 2
|
||||
fi
|
||||
|
@@ -199,6 +199,12 @@ func StartControllers(s *options.CloudControllerManagerServer, kubeconfig *restc
|
||||
client := func(serviceAccountName string) clientset.Interface {
|
||||
return rootClientBuilder.ClientOrDie(serviceAccountName)
|
||||
}
|
||||
|
||||
if cloud != nil {
|
||||
// Initialize the cloud provider with a reference to the clientBuilder
|
||||
cloud.Initialize(clientBuilder)
|
||||
}
|
||||
|
||||
versionedClient := client("shared-informers")
|
||||
sharedInformers := informers.NewSharedInformerFactory(versionedClient, resyncPeriod(s)())
|
||||
|
||||
|
@@ -54,6 +54,7 @@ go_library(
|
||||
"//plugin/pkg/admission/namespace/autoprovision:go_default_library",
|
||||
"//plugin/pkg/admission/namespace/exists:go_default_library",
|
||||
"//plugin/pkg/admission/namespace/lifecycle:go_default_library",
|
||||
"//plugin/pkg/admission/noderestriction:go_default_library",
|
||||
"//plugin/pkg/admission/persistentvolume/label:go_default_library",
|
||||
"//plugin/pkg/admission/podnodeselector:go_default_library",
|
||||
"//plugin/pkg/admission/podpreset:go_default_library",
|
||||
|
@@ -37,6 +37,7 @@ import (
|
||||
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision"
|
||||
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/exists"
|
||||
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/lifecycle"
|
||||
_ "k8s.io/kubernetes/plugin/pkg/admission/noderestriction"
|
||||
_ "k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label"
|
||||
_ "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector"
|
||||
_ "k8s.io/kubernetes/plugin/pkg/admission/podpreset"
|
||||
|
@@ -451,6 +451,11 @@ func StartControllers(controllers map[string]InitFunc, s *options.CMServer, root
|
||||
return fmt.Errorf("cloud provider could not be initialized: %v", err)
|
||||
}
|
||||
|
||||
if cloud != nil {
|
||||
// Initialize the cloud provider with a reference to the clientBuilder
|
||||
cloud.Initialize(clientBuilder)
|
||||
}
|
||||
|
||||
if ctx.IsControllerEnabled(nodeControllerName) {
|
||||
_, clusterCIDR, err := net.ParseCIDR(s.ClusterCIDR)
|
||||
if err != nil {
|
||||
|
@@ -41,6 +41,7 @@ filegroup(
|
||||
":package-srcs",
|
||||
"//cmd/libs/go2idl/client-gen/args:all-srcs",
|
||||
"//cmd/libs/go2idl/client-gen/generators:all-srcs",
|
||||
"//cmd/libs/go2idl/client-gen/path:all-srcs",
|
||||
"//cmd/libs/go2idl/client-gen/test_apis/testgroup:all-srcs",
|
||||
"//cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset:all-srcs",
|
||||
"//cmd/libs/go2idl/client-gen/types:all-srcs",
|
||||
|
@@ -22,6 +22,7 @@ go_library(
|
||||
"//cmd/libs/go2idl/client-gen/args:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/generators/fake:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/generators/scheme:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/path:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/types:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/gengo/args:go_default_library",
|
||||
|
@@ -29,6 +29,7 @@ import (
|
||||
clientgenargs "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/args"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/fake"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/scheme"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/path"
|
||||
clientgentypes "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/types"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@@ -275,7 +276,8 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
||||
|
||||
gvToTypes := map[clientgentypes.GroupVersion][]*types.Type{}
|
||||
for gv, inputDir := range customArgs.GroupVersionToInputPath {
|
||||
p := context.Universe.Package(inputDir)
|
||||
// Package are indexed with the vendor prefix stripped
|
||||
p := context.Universe.Package(path.Vendorless(inputDir))
|
||||
for n, t := range p.Types {
|
||||
// filter out types which are not included in user specified overrides.
|
||||
typesOverride, ok := includedTypesOverrides[gv]
|
||||
|
@@ -19,6 +19,7 @@ go_library(
|
||||
deps = [
|
||||
"//cmd/libs/go2idl/client-gen/args:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/generators/scheme:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/path:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/types:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/gengo/generator:go_default_library",
|
||||
|
@@ -24,6 +24,7 @@ import (
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/path"
|
||||
)
|
||||
|
||||
// genFakeForType produces a file for each top-level type.
|
||||
@@ -99,7 +100,7 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
|
||||
}
|
||||
|
||||
// allow user to define a group name that's different from the one parsed from the directory.
|
||||
p := c.Universe.Package(g.inputPackage)
|
||||
p := c.Universe.Package(path.Vendorless(g.inputPackage))
|
||||
if override := types.ExtractCommentTags("+", p.DocComments)["groupName"]; override != nil {
|
||||
groupName = override[0]
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import (
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/path"
|
||||
)
|
||||
|
||||
// genGroup produces a file for a group client, e.g. ExtensionsClient for the extension group.
|
||||
@@ -76,7 +77,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
|
||||
groupName = ""
|
||||
}
|
||||
// allow user to define a group name that's different from the one parsed from the directory.
|
||||
p := c.Universe.Package(g.inputPackage)
|
||||
p := c.Universe.Package(path.Vendorless(g.inputPackage))
|
||||
if override := types.ExtractCommentTags("+", p.DocComments)["groupName"]; override != nil {
|
||||
groupName = override[0]
|
||||
}
|
||||
@@ -95,7 +96,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
|
||||
"restDefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "DefaultKubernetesUserAgent"}),
|
||||
"restRESTClientInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Interface"}),
|
||||
"restRESTClientFor": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "RESTClientFor"}),
|
||||
"SchemeGroupVersion": c.Universe.Variable(types.Name{Package: g.inputPackage, Name: "SchemeGroupVersion"}),
|
||||
"SchemeGroupVersion": c.Universe.Variable(types.Name{Package: path.Vendorless(g.inputPackage), Name: "SchemeGroupVersion"}),
|
||||
}
|
||||
sw.Do(groupInterfaceTemplate, m)
|
||||
sw.Do(groupClientTemplate, m)
|
||||
|
@@ -12,6 +12,7 @@ go_library(
|
||||
srcs = ["generator_for_scheme.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/libs/go2idl/client-gen/path:go_default_library",
|
||||
"//cmd/libs/go2idl/client-gen/types:go_default_library",
|
||||
"//vendor/k8s.io/gengo/generator:go_default_library",
|
||||
"//vendor/k8s.io/gengo/namer:go_default_library",
|
||||
|
@@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/path"
|
||||
clientgentypes "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/types"
|
||||
)
|
||||
|
||||
@@ -66,10 +67,10 @@ func (g *GenScheme) Imports(c *generator.Context) (imports []string) {
|
||||
packagePath = filepath.Dir(packagePath)
|
||||
}
|
||||
packagePath = filepath.Join(packagePath, "install")
|
||||
imports = append(imports, strings.ToLower(fmt.Sprintf("%s \"%s\"", group.Group.NonEmpty(), packagePath)))
|
||||
imports = append(imports, strings.ToLower(fmt.Sprintf("%s \"%s\"", group.Group.NonEmpty(), path.Vendorless(packagePath))))
|
||||
break
|
||||
} else {
|
||||
imports = append(imports, strings.ToLower(fmt.Sprintf("%s%s \"%s\"", group.Group.NonEmpty(), version.NonEmpty(), packagePath)))
|
||||
imports = append(imports, strings.ToLower(fmt.Sprintf("%s%s \"%s\"", group.Group.NonEmpty(), version.NonEmpty(), path.Vendorless(packagePath))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
cmd/libs/go2idl/client-gen/path/BUILD
Normal file
27
cmd/libs/go2idl/client-gen/path/BUILD
Normal file
@@ -0,0 +1,27 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["path.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
31
cmd/libs/go2idl/client-gen/path/path.go
Normal file
31
cmd/libs/go2idl/client-gen/path/path.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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 path
|
||||
|
||||
import "strings"
|
||||
|
||||
// Vendorless removes the longest match of "*/vendor/" from the front of p.
|
||||
// It is useful if a package locates in vendor/, e.g.,
|
||||
// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1, because gengo
|
||||
// indexes the package with its import path, e.g.,
|
||||
// k8s.io/apimachinery/pkg/apis/meta/v1,
|
||||
func Vendorless(p string) string {
|
||||
if pos := strings.LastIndex(p, "/vendor/"); pos != -1 {
|
||||
return p[pos+len("/vendor/"):]
|
||||
}
|
||||
return p
|
||||
}
|
@@ -52,7 +52,8 @@ function unjoin_clusters() {
|
||||
"${context}" \
|
||||
--federation-system-namespace=${FEDERATION_NAMESPACE} \
|
||||
--context="${FEDERATION_KUBE_CONTEXT}" \
|
||||
--host-cluster-context="${HOST_CLUSTER_CONTEXT}"
|
||||
--host-cluster-context="${HOST_CLUSTER_CONTEXT}" \
|
||||
--v=4
|
||||
done
|
||||
}
|
||||
|
||||
|
@@ -94,7 +94,8 @@ function init() {
|
||||
--apiserver-enable-basic-auth=true \
|
||||
--apiserver-enable-token-auth=true \
|
||||
--apiserver-arg-overrides="--v=4" \
|
||||
--controllermanager-arg-overrides="--v=4"
|
||||
--controllermanager-arg-overrides="--v=4" \
|
||||
--v=4
|
||||
}
|
||||
|
||||
# join_clusters joins the clusters in the local kubeconfig to federation. The clusters
|
||||
@@ -107,7 +108,8 @@ function join_clusters() {
|
||||
"${context}" \
|
||||
--federation-system-namespace=${FEDERATION_NAMESPACE} \
|
||||
--host-cluster-context="${HOST_CLUSTER_CONTEXT}" \
|
||||
--context="${FEDERATION_KUBE_CONTEXT}"
|
||||
--context="${FEDERATION_KUBE_CONTEXT}" \
|
||||
--v=4
|
||||
done
|
||||
}
|
||||
|
||||
|
@@ -122,7 +122,7 @@ func (fu *federatedUpdaterImpl) Update(ops []FederatedOperation) error {
|
||||
fu.recordEvent(op.Obj, eventType, "Deleting", eventArgs...)
|
||||
err = fu.deleteFunction(clientset, op.Obj)
|
||||
// IsNotFound error is fine since that means the object is deleted already.
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
if errors.IsNotFound(err) {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ cmd/kubectl
|
||||
cmd/kubelet
|
||||
cmd/libs/go2idl/client-gen
|
||||
cmd/libs/go2idl/client-gen/generators
|
||||
cmd/libs/go2idl/client-gen/path
|
||||
cmd/libs/go2idl/client-gen/test_apis/testgroup/install
|
||||
cmd/libs/go2idl/conversion-gen
|
||||
cmd/libs/go2idl/deepcopy-gen
|
||||
@@ -86,6 +87,7 @@ pkg/apis/settings/install
|
||||
pkg/apis/settings/validation
|
||||
pkg/apis/storage/install
|
||||
pkg/apis/storage/validation
|
||||
pkg/auth/nodeidentifier
|
||||
pkg/bootstrap/api
|
||||
pkg/client/conditions
|
||||
pkg/client/informers/informers_generated/externalversions
|
||||
|
@@ -25,6 +25,7 @@ DOCKERIZE_KUBELET=${DOCKERIZE_KUBELET:-""}
|
||||
ALLOW_PRIVILEGED=${ALLOW_PRIVILEGED:-""}
|
||||
ALLOW_SECURITY_CONTEXT=${ALLOW_SECURITY_CONTEXT:-""}
|
||||
PSP_ADMISSION=${PSP_ADMISSION:-""}
|
||||
NODE_ADMISSION=${NODE_ADMISSION:-""}
|
||||
RUNTIME_CONFIG=${RUNTIME_CONFIG:-""}
|
||||
KUBELET_AUTHORIZATION_WEBHOOK=${KUBELET_AUTHORIZATION_WEBHOOK:-""}
|
||||
KUBELET_AUTHENTICATION_WEBHOOK=${KUBELET_AUTHENTICATION_WEBHOOK:-""}
|
||||
@@ -388,6 +389,9 @@ function start_apiserver {
|
||||
if [[ -n "${PSP_ADMISSION}" ]]; then
|
||||
security_admission=",PodSecurityPolicy"
|
||||
fi
|
||||
if [[ -n "${NODE_ADMISSION}" ]]; then
|
||||
security_admission=",NodeRestriction"
|
||||
fi
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount${security_admission},ResourceQuota,DefaultStorageClass,DefaultTolerationSeconds
|
||||
|
@@ -85,7 +85,6 @@ cgroups-per-qos
|
||||
chaos-chance
|
||||
cidr-allocator-type
|
||||
clean-start
|
||||
cleanup
|
||||
cleanup-iptables
|
||||
client-ca-file
|
||||
client-certificate
|
||||
@@ -273,7 +272,6 @@ federation-name
|
||||
federation-system-namespace
|
||||
federation-upgrade-target
|
||||
file-check-frequency
|
||||
file_content_in_loop
|
||||
file-suffix
|
||||
flex-volume-plugin-dir
|
||||
forward-services
|
||||
@@ -302,7 +300,6 @@ google-json-key
|
||||
grace-period
|
||||
ha-domain
|
||||
hairpin-mode
|
||||
hard
|
||||
hard-pod-affinity-symmetric-weight
|
||||
healthz-bind-address
|
||||
healthz-port
|
||||
@@ -563,7 +560,6 @@ pv-recycler-minimum-timeout-nfs
|
||||
pv-recycler-pod-template-filepath-hostpath
|
||||
pv-recycler-pod-template-filepath-nfs
|
||||
pv-recycler-timeout-increment-hostpath
|
||||
quiet
|
||||
read-only-port
|
||||
really-crash-for-testing
|
||||
reconcile-cidr
|
||||
@@ -592,13 +588,11 @@ requestheader-username-headers
|
||||
required-contexts
|
||||
require-kubeconfig
|
||||
resolv-conf
|
||||
resource
|
||||
resource-container
|
||||
resource-name
|
||||
resource-quota-sync-period
|
||||
resource-version
|
||||
results-dir
|
||||
retry_time
|
||||
rkt-api-endpoint
|
||||
rkt-path
|
||||
rkt-stage1-image
|
||||
@@ -617,7 +611,6 @@ schedule-pods-here
|
||||
scheduler-config
|
||||
scheduler-name
|
||||
schema-cache-dir
|
||||
scopes
|
||||
scrape-timeout
|
||||
seccomp-profile-root
|
||||
secondary-node-eviction-rate
|
||||
@@ -668,7 +661,6 @@ storage-media-type
|
||||
storage-version
|
||||
storage-versions
|
||||
streaming-connection-idle-timeout
|
||||
subresource
|
||||
suicide-timeout
|
||||
sync-frequency
|
||||
system-cgroups
|
||||
@@ -712,7 +704,6 @@ use-service-account-credentials
|
||||
user-whitelist
|
||||
use-service-account-credentials
|
||||
use-taint-based-evictions
|
||||
verb
|
||||
verify-only
|
||||
versioned-clientset-package
|
||||
viper-config
|
||||
|
@@ -31,6 +31,7 @@ filegroup(
|
||||
"//pkg/apis/settings:all-srcs",
|
||||
"//pkg/apis/storage:all-srcs",
|
||||
"//pkg/auth/authorizer/abac:all-srcs",
|
||||
"//pkg/auth/nodeidentifier:all-srcs",
|
||||
"//pkg/auth/user:all-srcs",
|
||||
"//pkg/bootstrap/api:all-srcs",
|
||||
"//pkg/capabilities:all-srcs",
|
||||
|
@@ -21,11 +21,14 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
// Visitor is called with each object name, and returns true if visiting should continue
|
||||
type Visitor func(name string) (shouldContinue bool)
|
||||
|
||||
// VisitPodSecretNames invokes the visitor function with the name of every secret
|
||||
// referenced by the pod spec. If visitor returns false, visiting is short-circuited.
|
||||
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
|
||||
// Returns true if visiting completed, false if visiting was short-circuited.
|
||||
func VisitPodSecretNames(pod *api.Pod, visitor func(string) bool) bool {
|
||||
func VisitPodSecretNames(pod *api.Pod, visitor Visitor) bool {
|
||||
for _, reference := range pod.Spec.ImagePullSecrets {
|
||||
if !visitor(reference.Name) {
|
||||
return false
|
||||
@@ -86,7 +89,7 @@ func VisitPodSecretNames(pod *api.Pod, visitor func(string) bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func visitContainerSecretNames(container *api.Container, visitor func(string) bool) bool {
|
||||
func visitContainerSecretNames(container *api.Container, visitor Visitor) bool {
|
||||
for _, env := range container.EnvFrom {
|
||||
if env.SecretRef != nil {
|
||||
if !visitor(env.SecretRef.Name) {
|
||||
@@ -104,6 +107,60 @@ func visitContainerSecretNames(container *api.Container, visitor func(string) bo
|
||||
return true
|
||||
}
|
||||
|
||||
// VisitPodConfigmapNames invokes the visitor function with the name of every configmap
|
||||
// referenced by the pod spec. If visitor returns false, visiting is short-circuited.
|
||||
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
|
||||
// Returns true if visiting completed, false if visiting was short-circuited.
|
||||
func VisitPodConfigmapNames(pod *api.Pod, visitor Visitor) bool {
|
||||
for i := range pod.Spec.InitContainers {
|
||||
if !visitContainerConfigmapNames(&pod.Spec.InitContainers[i], visitor) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := range pod.Spec.Containers {
|
||||
if !visitContainerConfigmapNames(&pod.Spec.Containers[i], visitor) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
var source *api.VolumeSource
|
||||
for i := range pod.Spec.Volumes {
|
||||
source = &pod.Spec.Volumes[i].VolumeSource
|
||||
switch {
|
||||
case source.Projected != nil:
|
||||
for j := range source.Projected.Sources {
|
||||
if source.Projected.Sources[j].ConfigMap != nil {
|
||||
if !visitor(source.Projected.Sources[j].ConfigMap.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
case source.ConfigMap != nil:
|
||||
if !visitor(source.ConfigMap.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func visitContainerConfigmapNames(container *api.Container, visitor Visitor) bool {
|
||||
for _, env := range container.EnvFrom {
|
||||
if env.ConfigMapRef != nil {
|
||||
if !visitor(env.ConfigMapRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, envVar := range container.Env {
|
||||
if envVar.ValueFrom != nil && envVar.ValueFrom.ConfigMapKeyRef != nil {
|
||||
if !visitor(envVar.ValueFrom.ConfigMapKeyRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsPodReady returns true if a pod is ready; false otherwise.
|
||||
func IsPodReady(pod *api.Pod) bool {
|
||||
return IsPodReadyConditionTrue(pod.Status)
|
||||
|
@@ -107,11 +107,14 @@ func SetInitContainersStatusesAnnotations(pod *v1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Visitor is called with each object name, and returns true if visiting should continue
|
||||
type Visitor func(name string) (shouldContinue bool)
|
||||
|
||||
// VisitPodSecretNames invokes the visitor function with the name of every secret
|
||||
// referenced by the pod spec. If visitor returns false, visiting is short-circuited.
|
||||
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
|
||||
// Returns true if visiting completed, false if visiting was short-circuited.
|
||||
func VisitPodSecretNames(pod *v1.Pod, visitor func(string) bool) bool {
|
||||
func VisitPodSecretNames(pod *v1.Pod, visitor Visitor) bool {
|
||||
for _, reference := range pod.Spec.ImagePullSecrets {
|
||||
if !visitor(reference.Name) {
|
||||
return false
|
||||
@@ -173,7 +176,7 @@ func VisitPodSecretNames(pod *v1.Pod, visitor func(string) bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func visitContainerSecretNames(container *v1.Container, visitor func(string) bool) bool {
|
||||
func visitContainerSecretNames(container *v1.Container, visitor Visitor) bool {
|
||||
for _, env := range container.EnvFrom {
|
||||
if env.SecretRef != nil {
|
||||
if !visitor(env.SecretRef.Name) {
|
||||
|
40
pkg/auth/nodeidentifier/BUILD
Normal file
40
pkg/auth/nodeidentifier/BUILD
Normal file
@@ -0,0 +1,40 @@
|
||||
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 = [
|
||||
"default.go",
|
||||
"interfaces.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["default_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
64
pkg/auth/nodeidentifier/default.go
Normal file
64
pkg/auth/nodeidentifier/default.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 nodeidentifier
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// NewDefaultNodeIdentifier returns a default NodeIdentifier implementation,
|
||||
// which returns isNode=true if the user groups contain the system:nodes group,
|
||||
// and populates nodeName if isNode is true, and the user name is in the format system:node:<nodeName>
|
||||
func NewDefaultNodeIdentifier() NodeIdentifier {
|
||||
return defaultNodeIdentifier{}
|
||||
}
|
||||
|
||||
// defaultNodeIdentifier implements NodeIdentifier
|
||||
type defaultNodeIdentifier struct{}
|
||||
|
||||
// nodeUserNamePrefix is the prefix for usernames in the form `system:node:<nodeName>`
|
||||
const nodeUserNamePrefix = "system:node:"
|
||||
|
||||
// NodeIdentity returns isNode=true if the user groups contain the system:nodes group,
|
||||
// and populates nodeName if isNode is true, and the user name is in the format system:node:<nodeName>
|
||||
func (defaultNodeIdentifier) NodeIdentity(u user.Info) (string, bool) {
|
||||
// Make sure we're a node, and can parse the node name
|
||||
if u == nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
isNode := false
|
||||
for _, g := range u.GetGroups() {
|
||||
if g == user.NodesGroup {
|
||||
isNode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isNode {
|
||||
return "", false
|
||||
}
|
||||
|
||||
userName := u.GetName()
|
||||
nodeName := ""
|
||||
if strings.HasPrefix(userName, nodeUserNamePrefix) {
|
||||
nodeName = strings.TrimPrefix(userName, nodeUserNamePrefix)
|
||||
}
|
||||
|
||||
return nodeName, isNode
|
||||
}
|
68
pkg/auth/nodeidentifier/default_test.go
Normal file
68
pkg/auth/nodeidentifier/default_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 nodeidentifier
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
func TestDefaultNodeIdentifier_NodeIdentity(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
user user.Info
|
||||
expectNodeName string
|
||||
expectIsNode bool
|
||||
}{
|
||||
{
|
||||
name: "nil user",
|
||||
user: nil,
|
||||
expectNodeName: "",
|
||||
expectIsNode: false,
|
||||
},
|
||||
{
|
||||
name: "node username without group",
|
||||
user: &user.DefaultInfo{Name: "system:node:foo"},
|
||||
expectNodeName: "",
|
||||
expectIsNode: false,
|
||||
},
|
||||
{
|
||||
name: "node group without username",
|
||||
user: &user.DefaultInfo{Name: "foo", Groups: []string{"system:nodes"}},
|
||||
expectNodeName: "",
|
||||
expectIsNode: true,
|
||||
},
|
||||
{
|
||||
name: "node group and username",
|
||||
user: &user.DefaultInfo{Name: "system:node:foo", Groups: []string{"system:nodes"}},
|
||||
expectNodeName: "foo",
|
||||
expectIsNode: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
nodeName, isNode := NewDefaultNodeIdentifier().NodeIdentity(tt.user)
|
||||
if nodeName != tt.expectNodeName {
|
||||
t.Errorf("DefaultNodeIdentifier.NodeIdentity() got = %v, want %v", nodeName, tt.expectNodeName)
|
||||
}
|
||||
if isNode != tt.expectIsNode {
|
||||
t.Errorf("DefaultNodeIdentifier.NodeIdentity() got1 = %v, want %v", isNode, tt.expectIsNode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
30
pkg/auth/nodeidentifier/interfaces.go
Normal file
30
pkg/auth/nodeidentifier/interfaces.go
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 nodeidentifier
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// NodeIdentifier determines node information from a given user
|
||||
type NodeIdentifier interface {
|
||||
// IdentifyNode determines node information from the given user.Info.
|
||||
// nodeName is the name of the Node API object associated with the user.Info,
|
||||
// and may be empty if a specific node cannot be determined.
|
||||
// isNode is true if the user.Info represents an identity issued to a node.
|
||||
NodeIdentity(user.Info) (nodeName string, isNode bool)
|
||||
}
|
@@ -17,6 +17,7 @@ go_library(
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
|
@@ -23,10 +23,13 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
// Interface is an abstract, pluggable interface for cloud providers.
|
||||
type Interface interface {
|
||||
// Initialize provides the cloud with a kubernetes client builder
|
||||
Initialize(clientBuilder controller.ControllerClientBuilder)
|
||||
// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
|
||||
LoadBalancer() (LoadBalancer, bool)
|
||||
// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.
|
||||
|
@@ -30,6 +30,7 @@ go_library(
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/service:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/credentialprovider/aws:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
|
||||
|
@@ -49,6 +49,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1/service"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
@@ -888,6 +889,9 @@ func newAWSCloud(config io.Reader, awsServices Services) (*Cloud, error) {
|
||||
return awsCloud, nil
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (c *Cloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// Clusters returns the list of clusters.
|
||||
func (c *Cloud) Clusters() (cloudprovider.Clusters, bool) {
|
||||
return nil, false
|
||||
|
@@ -29,6 +29,7 @@ go_library(
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/service:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//vendor/github.com/Azure/azure-sdk-for-go/arm/compute:go_default_library",
|
||||
|
@@ -20,8 +20,10 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/compute"
|
||||
@@ -30,7 +32,6 @@ import (
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/ghodss/yaml"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CloudProviderName is the value used for the --cloud-provider flag
|
||||
@@ -179,6 +180,9 @@ func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
||||
return &az, nil
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (az *Cloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
|
||||
func (az *Cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
|
||||
return az, true
|
||||
|
@@ -18,6 +18,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/xanzy/go-cloudstack/cloudstack:go_default_library",
|
||||
"//vendor/gopkg.in/gcfg.v1:go_default_library",
|
||||
|
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/xanzy/go-cloudstack/cloudstack"
|
||||
"gopkg.in/gcfg.v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
// ProviderName is the name of this cloud provider.
|
||||
@@ -81,6 +82,9 @@ func newCSCloud(cfg *CSConfig) (*CSCloud, error) {
|
||||
return &CSCloud{client, cfg.Global.ProjectID, cfg.Global.Zone}, nil
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (cs *CSCloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// LoadBalancer returns an implementation of LoadBalancer for CloudStack.
|
||||
func (cs *CSCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
|
||||
return cs, true
|
||||
|
@@ -17,6 +17,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
const defaultProviderName = "fake"
|
||||
@@ -82,6 +83,9 @@ func (f *FakeCloud) ClearCalls() {
|
||||
f.Calls = []string{}
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (f *FakeCloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
func (f *FakeCloud) ListClusters() ([]string, error) {
|
||||
return f.ClusterList, f.Err
|
||||
}
|
||||
|
@@ -38,6 +38,7 @@ go_library(
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/service:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/util/net/sets:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//vendor/cloud.google.com/go/compute/metadata:go_default_library",
|
||||
|
@@ -31,6 +31,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -233,6 +234,9 @@ func CreateGCECloud(projectID, region, zone string, managedZones []string, netwo
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (gce *GCECloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// LoadBalancer returns an implementation of LoadBalancer for Google Compute Engine.
|
||||
func (gce *GCECloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
|
||||
return gce, true
|
||||
|
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package gce
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -62,7 +61,27 @@ func (gce *GCECloud) NodeAddresses(_ types.NodeName) ([]v1.NodeAddress, error) {
|
||||
// This method will not be called from the node that is requesting this ID.
|
||||
// i.e. metadata service and other local methods cannot be used here
|
||||
func (gce *GCECloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
|
||||
return []v1.NodeAddress{}, errors.New("unimplemented")
|
||||
project, zone, name, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return []v1.NodeAddress{}, err
|
||||
}
|
||||
|
||||
instance, err := gce.service.Instances.Get(project, zone, canonicalizeInstanceName(name)).Do()
|
||||
if err != nil {
|
||||
return []v1.NodeAddress{}, fmt.Errorf("error while querying for providerID %q: %v", providerID, err)
|
||||
}
|
||||
|
||||
if len(instance.NetworkInterfaces) < 1 {
|
||||
return []v1.NodeAddress{}, fmt.Errorf("could not find network interfaces for providerID %q", providerID)
|
||||
}
|
||||
networkInterface := instance.NetworkInterfaces[0]
|
||||
|
||||
nodeAddresses := []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: networkInterface.NetworkIP}}
|
||||
for _, config := range networkInterface.AccessConfigs {
|
||||
nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: config.NatIP})
|
||||
}
|
||||
|
||||
return nodeAddresses, nil
|
||||
}
|
||||
|
||||
// InstanceTypeByProviderID returns the cloudprovider instance type of the node
|
||||
@@ -70,7 +89,15 @@ func (gce *GCECloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddr
|
||||
// node that is requesting this ID. i.e. metadata service and other local
|
||||
// methods cannot be used here
|
||||
func (gce *GCECloud) InstanceTypeByProviderID(providerID string) (string, error) {
|
||||
return "", errors.New("unimplemented")
|
||||
project, zone, name, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
instance, err := gce.getInstanceFromProjectInZoneByName(project, zone, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.Type, nil
|
||||
}
|
||||
|
||||
// ExternalID returns the cloud provider ID of the node with the specified NodeName (deprecated).
|
||||
@@ -339,18 +366,29 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error)
|
||||
func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) {
|
||||
// Avoid changing behaviour when not managing multiple zones
|
||||
for _, zone := range gce.managedZones {
|
||||
name = canonicalizeInstanceName(name)
|
||||
mc := newInstancesMetricContext("get", zone)
|
||||
res, err := gce.service.Instances.Get(gce.projectID, zone, name).Do()
|
||||
mc.Observe(err)
|
||||
instance, err := gce.getInstanceFromProjectInZoneByName(gce.projectID, zone, name)
|
||||
if err != nil {
|
||||
glog.Errorf("getInstanceByName: failed to get instance %s; err: %v", name, err)
|
||||
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
return nil, cloudprovider.InstanceNotFound
|
||||
}
|
||||
|
||||
func (gce *GCECloud) getInstanceFromProjectInZoneByName(project, zone, name string) (*gceInstance, error) {
|
||||
name = canonicalizeInstanceName(name)
|
||||
mc := newInstancesMetricContext("get", zone)
|
||||
res, err := gce.service.Instances.Get(project, zone, name).Do()
|
||||
mc.Observe(err)
|
||||
if err != nil {
|
||||
glog.Errorf("getInstanceFromProjectInZoneByName: failed to get instance %s; err: %v", name, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gceInstance{
|
||||
Zone: lastComponent(res.Zone),
|
||||
Name: res.Name,
|
||||
@@ -358,9 +396,6 @@ func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) {
|
||||
Disks: res.Disks,
|
||||
Type: lastComponent(res.MachineType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, cloudprovider.InstanceNotFound
|
||||
}
|
||||
|
||||
func getInstanceIDViaMetadata() (string, error) {
|
||||
|
@@ -158,3 +158,93 @@ func TestCreateFirewallFails(t *testing.T) {
|
||||
t.Errorf("error expected when creating firewall without any tags found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitProviderID(t *testing.T) {
|
||||
providers := []struct {
|
||||
providerID string
|
||||
|
||||
project string
|
||||
zone string
|
||||
instance string
|
||||
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "project-example-164317",
|
||||
zone: "us-central1-f",
|
||||
instance: "kubernetes-node-fhx1",
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example.164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "project-example.164317",
|
||||
zone: "us-central1-f",
|
||||
instance: "kubernetes-node-fhx1",
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-fkubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + ":/project-example-164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: "aws://project-example-164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-f/kubernetes-node-fhx1/",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example.164317//kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example.164317/kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range providers {
|
||||
project, zone, instance, err := splitProviderID(test.providerID)
|
||||
if (err != nil) != test.fail {
|
||||
t.Errorf("Expected to failt=%t, with pattern %v", test.fail, test)
|
||||
}
|
||||
|
||||
if test.fail {
|
||||
continue
|
||||
}
|
||||
|
||||
if project != test.project {
|
||||
t.Errorf("Expected %v, but got %v", test.project, project)
|
||||
}
|
||||
if zone != test.zone {
|
||||
t.Errorf("Expected %v, but got %v", test.zone, zone)
|
||||
}
|
||||
if instance != test.instance {
|
||||
t.Errorf("Expected %v, but got %v", test.instance, instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,9 @@ limitations under the License.
|
||||
package gce
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -35,6 +37,8 @@ type gceInstance struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
var providerIdRE = regexp.MustCompile(`^` + ProviderName + `://([^/]+)/([^/]+)/([^/]+)$`)
|
||||
|
||||
func getProjectAndZone() (string, string, error) {
|
||||
result, err := metadata.Get("instance/zone")
|
||||
if err != nil {
|
||||
@@ -100,3 +104,14 @@ func isHTTPErrorCode(err error, code int) bool {
|
||||
apiErr, ok := err.(*googleapi.Error)
|
||||
return ok && apiErr.Code == code
|
||||
}
|
||||
|
||||
// splitProviderID splits a provider's id into core components.
|
||||
// A providerID is build out of '${ProviderName}://${project-id}/${zone}/${instance-name}'
|
||||
// See cloudprovider.GetInstanceProviderID.
|
||||
func splitProviderID(providerID string) (project, zone, instance string, err error) {
|
||||
matches := providerIdRE.FindStringSubmatch(providerID)
|
||||
if len(matches) != 4 {
|
||||
return "", "", "", errors.New("error splitting providerID")
|
||||
}
|
||||
return matches[1], matches[2], matches[3], nil
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/mesos/mesos-go/detector:go_default_library",
|
||||
"//vendor/github.com/mesos/mesos-go/detector/zoo:go_default_library",
|
||||
|
@@ -31,6 +31,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -89,6 +90,9 @@ func newMesosCloud(configReader io.Reader) (*MesosCloud, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (c *MesosCloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// Implementation of Instances.CurrentNodeName
|
||||
func (c *MesosCloud) CurrentNodeName(hostname string) (types.NodeName, error) {
|
||||
return types.NodeName(hostname), nil
|
||||
|
@@ -24,6 +24,7 @@ go_library(
|
||||
"//pkg/api/v1/helper:go_default_library",
|
||||
"//pkg/api/v1/service:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
|
@@ -45,6 +45,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
const ProviderName = "openstack"
|
||||
@@ -265,6 +266,9 @@ func newOpenStack(cfg Config) (*OpenStack, error) {
|
||||
return &os, nil
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (os *OpenStack) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// mapNodeNameToServerName maps a k8s NodeName to an OpenStack Server Name
|
||||
// This is a simple string cast.
|
||||
func mapNodeNameToServerName(nodeName types.NodeName) string {
|
||||
|
@@ -15,6 +15,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/gopkg.in/gcfg.v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
|
@@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
const ProviderName = "ovirt"
|
||||
@@ -116,7 +117,10 @@ func newOVirtCloud(config io.Reader) (*OVirtCloud, error) {
|
||||
return &OVirtCloud{VmsRequest: request}, nil
|
||||
}
|
||||
|
||||
func (aws *OVirtCloud) Clusters() (cloudprovider.Clusters, bool) {
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (v *OVirtCloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
func (v *OVirtCloud) Clusters() (cloudprovider.Clusters, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
|
@@ -16,6 +16,7 @@ go_library(
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/helper:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/vmware/photon-controller-go-sdk/photon:go_default_library",
|
||||
"//vendor/gopkg.in/gcfg.v1:go_default_library",
|
||||
|
@@ -41,6 +41,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -292,6 +293,9 @@ func newPCCloud(cfg PCConfig) (*PCCloud, error) {
|
||||
return &pc, nil
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (pc *PCCloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// Instances returns an implementation of Instances for Photon Controller.
|
||||
func (pc *PCCloud) Instances() (cloudprovider.Instances, bool) {
|
||||
return pc, true
|
||||
|
@@ -15,6 +15,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/rackspace/gophercloud:go_default_library",
|
||||
"//vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach:go_default_library",
|
||||
|
@@ -43,6 +43,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -218,6 +219,9 @@ func newRackspace(cfg Config) (*Rackspace, error) {
|
||||
return &os, nil
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (os *Rackspace) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
type Instances struct {
|
||||
compute *gophercloud.ServiceClient
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ go_library(
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/helper:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/vmware/govmomi:go_default_library",
|
||||
"//vendor/github.com/vmware/govmomi/find:go_default_library",
|
||||
|
@@ -50,6 +50,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -229,6 +230,9 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
||||
func (vs *VSphere) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
||||
|
||||
// UUID gets the BIOS UUID via the sys interface. This UUID is known by vsphere
|
||||
func getvmUUID() (string, error) {
|
||||
id, err := ioutil.ReadFile(UUIDPath)
|
||||
|
@@ -146,7 +146,6 @@ func (e *EndpointController) Run(workers int, stopCh <-chan struct{}) {
|
||||
|
||||
go func() {
|
||||
defer utilruntime.HandleCrash()
|
||||
time.Sleep(5 * time.Minute) // give time for our cache to fill
|
||||
e.checkLeftoverEndpoints()
|
||||
}()
|
||||
|
||||
|
@@ -18,6 +18,7 @@ package envvars
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -78,18 +79,21 @@ func makeLinkVariables(service *v1.Service) []v1.EnvVar {
|
||||
if sp.Protocol != "" {
|
||||
protocol = string(sp.Protocol)
|
||||
}
|
||||
|
||||
hostPort := net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(sp.Port)))
|
||||
|
||||
if i == 0 {
|
||||
// Docker special-cases the first port.
|
||||
all = append(all, v1.EnvVar{
|
||||
Name: prefix + "_PORT",
|
||||
Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.Spec.ClusterIP, sp.Port),
|
||||
Value: fmt.Sprintf("%s://%s", strings.ToLower(protocol), hostPort),
|
||||
})
|
||||
}
|
||||
portPrefix := fmt.Sprintf("%s_PORT_%d_%s", prefix, sp.Port, strings.ToUpper(protocol))
|
||||
all = append(all, []v1.EnvVar{
|
||||
{
|
||||
Name: portPrefix,
|
||||
Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.Spec.ClusterIP, sp.Port),
|
||||
Value: fmt.Sprintf("%s://%s", strings.ToLower(protocol), hostPort),
|
||||
},
|
||||
{
|
||||
Name: portPrefix + "_PROTO",
|
||||
|
@@ -79,6 +79,17 @@ func TestFromServices(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "super-ipv6"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
ClusterIP: "2001:DB8::",
|
||||
Ports: []v1.ServicePort{
|
||||
{Name: "u-d-p", Port: 8084, Protocol: "UDP"},
|
||||
{Name: "t-c-p", Port: 8084, Protocol: "TCP"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
vars := envvars.FromServices(sl)
|
||||
expected := []v1.EnvVar{
|
||||
@@ -114,6 +125,19 @@ func TestFromServices(t *testing.T) {
|
||||
{Name: "Q_U_U_X_PORT_8083_TCP_PROTO", Value: "tcp"},
|
||||
{Name: "Q_U_U_X_PORT_8083_TCP_PORT", Value: "8083"},
|
||||
{Name: "Q_U_U_X_PORT_8083_TCP_ADDR", Value: "9.8.7.6"},
|
||||
{Name: "SUPER_IPV6_SERVICE_HOST", Value: "2001:DB8::"},
|
||||
{Name: "SUPER_IPV6_SERVICE_PORT", Value: "8084"},
|
||||
{Name: "SUPER_IPV6_SERVICE_PORT_U_D_P", Value: "8084"},
|
||||
{Name: "SUPER_IPV6_SERVICE_PORT_T_C_P", Value: "8084"},
|
||||
{Name: "SUPER_IPV6_PORT", Value: "udp://[2001:DB8::]:8084"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_UDP", Value: "udp://[2001:DB8::]:8084"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_UDP_PROTO", Value: "udp"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_UDP_PORT", Value: "8084"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_UDP_ADDR", Value: "2001:DB8::"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_TCP", Value: "tcp://[2001:DB8::]:8084"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_TCP_PROTO", Value: "tcp"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_TCP_PORT", Value: "8084"},
|
||||
{Name: "SUPER_IPV6_PORT_8084_TCP_ADDR", Value: "2001:DB8::"},
|
||||
}
|
||||
if len(vars) != len(expected) {
|
||||
t.Errorf("Expected %d env vars, got: %+v", len(expected), vars)
|
||||
|
@@ -101,7 +101,7 @@ func hexCIDR(cidr string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
ip = ip.Mask(ipnet.Mask)
|
||||
hexIP := hex.EncodeToString([]byte(ip.To4()))
|
||||
hexIP := hex.EncodeToString([]byte(ip))
|
||||
hexMask := ipnet.Mask.String()
|
||||
return hexIP + "/" + hexMask, nil
|
||||
}
|
||||
@@ -119,6 +119,9 @@ func asciiCIDR(cidr string) (string, error) {
|
||||
ip := net.IP(ipData)
|
||||
|
||||
maskData, err := hex.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mask := net.IPMask(maskData)
|
||||
size, _ := mask.Size()
|
||||
|
||||
|
@@ -94,19 +94,33 @@ func TestNextClassID(t *testing.T) {
|
||||
|
||||
func TestHexCIDR(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
output string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
input: "1.2.0.0/16",
|
||||
name: "IPv4 masked",
|
||||
input: "1.2.3.4/16",
|
||||
output: "01020000/ffff0000",
|
||||
},
|
||||
{
|
||||
name: "IPv4 host",
|
||||
input: "172.17.0.2/32",
|
||||
output: "ac110002/ffffffff",
|
||||
},
|
||||
{
|
||||
name: "IPv6 masked",
|
||||
input: "2001:dead:beef::cafe/64",
|
||||
output: "2001deadbeef00000000000000000000/ffffffffffffffff0000000000000000",
|
||||
},
|
||||
{
|
||||
name: "IPv6 host",
|
||||
input: "2001::5/128",
|
||||
output: "20010000000000000000000000000005/ffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
{
|
||||
name: "invalid CIDR",
|
||||
input: "foo",
|
||||
expectErr: true,
|
||||
},
|
||||
@@ -115,21 +129,76 @@ func TestHexCIDR(t *testing.T) {
|
||||
output, err := hexCIDR(test.input)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
t.Errorf("case %s: unexpected non-error", test.name)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Errorf("case %s: unexpected error: %v", test.name, err)
|
||||
}
|
||||
if output != test.output {
|
||||
t.Errorf("expected: %s, saw: %s", test.output, output)
|
||||
t.Errorf("case %s: expected: %s, saw: %s",
|
||||
test.name, test.output, output)
|
||||
}
|
||||
input, err := asciiCIDR(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsciiCIDR(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
output string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "IPv4",
|
||||
input: "01020000/ffff0000",
|
||||
output: "1.2.0.0/16",
|
||||
},
|
||||
{
|
||||
name: "IPv4 host",
|
||||
input: "ac110002/ffffffff",
|
||||
output: "172.17.0.2/32",
|
||||
},
|
||||
{
|
||||
name: "IPv6",
|
||||
input: "2001deadbeef00000000000000000000/ffffffffffffffff0000000000000000",
|
||||
output: "2001:dead:beef::/64",
|
||||
},
|
||||
{
|
||||
name: "IPv6 host",
|
||||
input: "20010000000000000000000000000005/ffffffffffffffffffffffffffffffff",
|
||||
output: "2001::5/128",
|
||||
},
|
||||
{
|
||||
name: "invalid CIDR",
|
||||
input: "malformed",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "non-hex IP",
|
||||
input: "nonhex/32",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "non-hex mask",
|
||||
input: "01020000/badmask",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
output, err := asciiCIDR(test.input)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("case %s: unexpected non-error", test.name)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Errorf("case %s: unexpected error: %v", test.name, err)
|
||||
}
|
||||
if input != test.input {
|
||||
t.Errorf("expected: %s, saw: %s", test.input, input)
|
||||
if output != test.output {
|
||||
t.Errorf("case %s: expected: %s, saw: %s",
|
||||
test.name, test.output, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ filegroup(
|
||||
"//plugin/pkg/admission/namespace/autoprovision:all-srcs",
|
||||
"//plugin/pkg/admission/namespace/exists:all-srcs",
|
||||
"//plugin/pkg/admission/namespace/lifecycle:all-srcs",
|
||||
"//plugin/pkg/admission/noderestriction:all-srcs",
|
||||
"//plugin/pkg/admission/persistentvolume/label:all-srcs",
|
||||
"//plugin/pkg/admission/podnodeselector:all-srcs",
|
||||
"//plugin/pkg/admission/podpreset:all-srcs",
|
||||
|
@@ -24,7 +24,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("AlwaysAdmit", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("AlwaysAdmit", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewAlwaysAdmit(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -34,7 +34,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("AlwaysPullImages", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("AlwaysPullImages", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewAlwaysPullImages(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -28,7 +28,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("LimitPodHardAntiAffinityTopology", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("LimitPodHardAntiAffinityTopology", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewInterPodAntiAffinity(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -40,7 +40,12 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("DefaultTolerationSeconds", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("DefaultTolerationSeconds", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewDefaultTolerationSeconds(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -25,7 +25,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("AlwaysDeny", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("AlwaysDeny", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewAlwaysDeny(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -30,13 +30,18 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("DenyEscalatingExec", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("DenyEscalatingExec", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewDenyEscalatingExec(), nil
|
||||
})
|
||||
|
||||
// This is for legacy support of the DenyExecOnPrivileged admission controller. Most
|
||||
// of the time DenyEscalatingExec should be preferred.
|
||||
kubeapiserveradmission.Plugins.Register("DenyExecOnPrivileged", func(config io.Reader) (admission.Interface, error) {
|
||||
plugins.Register("DenyExecOnPrivileged", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewDenyExecOnPrivileged(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -32,7 +32,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("OwnerReferencesPermissionEnforcement", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("OwnerReferencesPermissionEnforcement", func(config io.Reader) (admission.Interface, error) {
|
||||
// the pods/status endpoint is ignored by this plugin since old kubelets
|
||||
// corrupt them. the pod status strategy ensures status updates cannot mutate
|
||||
// ownerRef.
|
||||
|
@@ -50,7 +50,12 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("ImagePolicyWebhook", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("ImagePolicyWebhook", func(config io.Reader) (admission.Interface, error) {
|
||||
newImagePolicyWebhook, err := NewImagePolicyWebhook(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -47,7 +47,12 @@ const (
|
||||
|
||||
// WARNING: this feature is experimental and will definitely change.
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("InitialResources", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("InitialResources", func(config io.Reader) (admission.Interface, error) {
|
||||
// TODO: remove the usage of flags in favor of reading versioned configuration
|
||||
s, err := newDataSource(*source)
|
||||
if err != nil {
|
||||
|
@@ -44,7 +44,12 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("LimitRanger", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("LimitRanger", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewLimitRanger(&DefaultLimitRangerActions{})
|
||||
})
|
||||
}
|
||||
|
@@ -31,7 +31,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("NamespaceAutoProvision", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("NamespaceAutoProvision", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewProvision(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -31,7 +31,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("NamespaceExists", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("NamespaceExists", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewExists(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -51,7 +51,12 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
return NewLifecycle(sets.NewString(metav1.NamespaceDefault, metav1.NamespaceSystem, metav1.NamespacePublic))
|
||||
})
|
||||
}
|
||||
|
54
plugin/pkg/admission/noderestriction/BUILD
Normal file
54
plugin/pkg/admission/noderestriction/BUILD
Normal file
@@ -0,0 +1,54 @@
|
||||
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 = ["admission.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/pod:go_default_library",
|
||||
"//pkg/auth/nodeidentifier:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/kubeapiserver/admission:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["admission_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/auth/nodeidentifier:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
8
plugin/pkg/admission/noderestriction/OWNERS
Normal file
8
plugin/pkg/admission/noderestriction/OWNERS
Normal file
@@ -0,0 +1,8 @@
|
||||
approvers:
|
||||
- deads2k
|
||||
- liggitt
|
||||
- timstclair
|
||||
reviewers:
|
||||
- deads2k
|
||||
- liggitt
|
||||
- timstclair
|
203
plugin/pkg/admission/noderestriction/admission.go
Normal file
203
plugin/pkg/admission/noderestriction/admission.go
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
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 node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
coreinternalversion "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||
)
|
||||
|
||||
const (
|
||||
PluginName = "NodeRestriction"
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
return NewPlugin(nodeidentifier.NewDefaultNodeIdentifier(), false), nil
|
||||
})
|
||||
}
|
||||
|
||||
// NewPlugin creates a new NodeRestriction admission plugin.
|
||||
// This plugin identifies requests from nodes
|
||||
func NewPlugin(nodeIdentifier nodeidentifier.NodeIdentifier, strict bool) *nodePlugin {
|
||||
return &nodePlugin{
|
||||
Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
|
||||
nodeIdentifier: nodeIdentifier,
|
||||
strict: strict,
|
||||
}
|
||||
}
|
||||
|
||||
// nodePlugin holds state for and implements the admission plugin.
|
||||
type nodePlugin struct {
|
||||
*admission.Handler
|
||||
strict bool
|
||||
nodeIdentifier nodeidentifier.NodeIdentifier
|
||||
podsGetter coreinternalversion.PodsGetter
|
||||
}
|
||||
|
||||
var (
|
||||
_ = admission.Interface(&nodePlugin{})
|
||||
_ = kubeapiserveradmission.WantsInternalKubeClientSet(&nodePlugin{})
|
||||
)
|
||||
|
||||
func (p *nodePlugin) SetInternalKubeClientSet(f internalclientset.Interface) {
|
||||
p.podsGetter = f.Core()
|
||||
}
|
||||
|
||||
func (p *nodePlugin) Validate() error {
|
||||
if p.nodeIdentifier == nil {
|
||||
return fmt.Errorf("%s requires a node identifier", PluginName)
|
||||
}
|
||||
if p.podsGetter == nil {
|
||||
return fmt.Errorf("%s requires a pod getter", PluginName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
podResource = api.Resource("pods")
|
||||
nodeResource = api.Resource("nodes")
|
||||
)
|
||||
|
||||
func (c *nodePlugin) Admit(a admission.Attributes) error {
|
||||
nodeName, isNode := c.nodeIdentifier.NodeIdentity(a.GetUserInfo())
|
||||
|
||||
// Our job is just to restrict nodes
|
||||
if !isNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(nodeName) == 0 {
|
||||
if c.strict {
|
||||
// In strict mode, disallow requests from nodes we cannot match to a particular node
|
||||
return admission.NewForbidden(a, fmt.Errorf("could not determine node identity from user"))
|
||||
}
|
||||
// Our job is just to restrict identifiable nodes
|
||||
return nil
|
||||
}
|
||||
|
||||
switch a.GetResource().GroupResource() {
|
||||
case podResource:
|
||||
switch a.GetSubresource() {
|
||||
case "":
|
||||
return c.admitPod(nodeName, a)
|
||||
case "status":
|
||||
return c.admitPodStatus(nodeName, a)
|
||||
default:
|
||||
return admission.NewForbidden(a, fmt.Errorf("unexpected pod subresource %s", a.GetSubresource()))
|
||||
}
|
||||
|
||||
case nodeResource:
|
||||
return c.admitNode(nodeName, a)
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *nodePlugin) admitPod(nodeName string, a admission.Attributes) error {
|
||||
switch a.GetOperation() {
|
||||
case admission.Create:
|
||||
// require a pod object
|
||||
pod, ok := a.GetObject().(*api.Pod)
|
||||
if !ok {
|
||||
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
|
||||
}
|
||||
|
||||
// only allow nodes to create mirror pods
|
||||
if _, isMirrorPod := pod.Annotations[api.MirrorPodAnnotationKey]; !isMirrorPod {
|
||||
return admission.NewForbidden(a, fmt.Errorf("pod does not have %q annotation, node %s can only create mirror pods", api.MirrorPodAnnotationKey, nodeName))
|
||||
}
|
||||
|
||||
// only allow nodes to create a pod bound to itself
|
||||
if pod.Spec.NodeName != nodeName {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %s can only create pods with spec.nodeName set to itself", nodeName))
|
||||
}
|
||||
|
||||
// don't allow a node to create a pod that references any other API objects
|
||||
if pod.Spec.ServiceAccountName != "" {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %s can not create pods that reference a service account", nodeName))
|
||||
}
|
||||
hasSecrets := false
|
||||
podutil.VisitPodSecretNames(pod, func(name string) (shouldContinue bool) { hasSecrets = true; return false })
|
||||
if hasSecrets {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %s can not create pods that reference secrets", nodeName))
|
||||
}
|
||||
hasConfigMaps := false
|
||||
podutil.VisitPodConfigmapNames(pod, func(name string) (shouldContinue bool) { hasConfigMaps = true; return false })
|
||||
if hasConfigMaps {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %s can not create pods that reference configmaps", nodeName))
|
||||
}
|
||||
for _, v := range pod.Spec.Volumes {
|
||||
if v.PersistentVolumeClaim != nil {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %s can not create pods that reference persistentvolumeclaims", nodeName))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case admission.Delete:
|
||||
// get the existing pod
|
||||
existingPod, err := c.podsGetter.Pods(a.GetNamespace()).Get(a.GetName(), v1.GetOptions{ResourceVersion: "0"})
|
||||
if err != nil {
|
||||
return admission.NewForbidden(a, err)
|
||||
}
|
||||
// only allow a node to delete a pod bound to itself
|
||||
if existingPod.Spec.NodeName != nodeName {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %s can only delete pods with spec.nodeName set to itself", nodeName))
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return admission.NewForbidden(a, fmt.Errorf("unexpected operation %s", a.GetOperation()))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *nodePlugin) admitPodStatus(nodeName string, a admission.Attributes) error {
|
||||
switch a.GetOperation() {
|
||||
case admission.Update:
|
||||
// require an existing pod
|
||||
pod, ok := a.GetOldObject().(*api.Pod)
|
||||
if !ok {
|
||||
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
|
||||
}
|
||||
// only allow a node to update status of a pod bound to itself
|
||||
if pod.Spec.NodeName != nodeName {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %s can only update pod status for pods with spec.nodeName set to itself", nodeName))
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return admission.NewForbidden(a, fmt.Errorf("unexpected operation %s", a.GetOperation()))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error {
|
||||
if a.GetName() != nodeName {
|
||||
return admission.NewForbidden(a, fmt.Errorf("cannot modify other nodes"))
|
||||
}
|
||||
return nil
|
||||
}
|
476
plugin/pkg/admission/noderestriction/admission_test.go
Normal file
476
plugin/pkg/admission/noderestriction/admission_test.go
Normal file
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
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 node
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
coreinternalversion "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
)
|
||||
|
||||
func makeTestPod(namespace, name, node string, mirror bool) *api.Pod {
|
||||
pod := &api.Pod{}
|
||||
pod.Namespace = namespace
|
||||
pod.Name = name
|
||||
pod.Spec.NodeName = node
|
||||
if mirror {
|
||||
pod.Annotations = map[string]string{api.MirrorPodAnnotationKey: "true"}
|
||||
}
|
||||
return pod
|
||||
}
|
||||
|
||||
func Test_nodePlugin_Admit(t *testing.T) {
|
||||
var (
|
||||
mynode = &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}}
|
||||
bob = &user.DefaultInfo{Name: "bob"}
|
||||
|
||||
mynodeObj = &api.Node{ObjectMeta: metav1.ObjectMeta{Name: "mynode"}}
|
||||
othernodeObj = &api.Node{ObjectMeta: metav1.ObjectMeta{Name: "othernode"}}
|
||||
|
||||
mymirrorpod = makeTestPod("ns", "mymirrorpod", "mynode", true)
|
||||
othermirrorpod = makeTestPod("ns", "othermirrorpod", "othernode", true)
|
||||
unboundmirrorpod = makeTestPod("ns", "unboundmirrorpod", "", true)
|
||||
mypod = makeTestPod("ns", "mypod", "mynode", false)
|
||||
otherpod = makeTestPod("ns", "otherpod", "othernode", false)
|
||||
unboundpod = makeTestPod("ns", "unboundpod", "", false)
|
||||
|
||||
configmapResource = api.Resource("configmap").WithVersion("v1")
|
||||
configmapKind = api.Kind("ConfigMap").WithVersion("v1")
|
||||
|
||||
podResource = api.Resource("pods").WithVersion("v1")
|
||||
podKind = api.Kind("Pod").WithVersion("v1")
|
||||
|
||||
nodeResource = api.Resource("nodes").WithVersion("v1")
|
||||
nodeKind = api.Kind("Node").WithVersion("v1")
|
||||
|
||||
noExistingPods = fake.NewSimpleClientset().Core()
|
||||
existingPods = fake.NewSimpleClientset(mymirrorpod, othermirrorpod, unboundmirrorpod, mypod, otherpod, unboundpod).Core()
|
||||
)
|
||||
|
||||
sapod := makeTestPod("ns", "mysapod", "mynode", true)
|
||||
sapod.Spec.ServiceAccountName = "foo"
|
||||
|
||||
secretpod := makeTestPod("ns", "mysecretpod", "mynode", true)
|
||||
secretpod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}}}
|
||||
|
||||
configmappod := makeTestPod("ns", "myconfigmappod", "mynode", true)
|
||||
configmappod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{ConfigMap: &api.ConfigMapVolumeSource{LocalObjectReference: api.LocalObjectReference{Name: "foo"}}}}}
|
||||
|
||||
pvcpod := makeTestPod("ns", "mypvcpod", "mynode", true)
|
||||
pvcpod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ClaimName: "foo"}}}}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
strict bool
|
||||
podsGetter coreinternalversion.PodsGetter
|
||||
attributes admission.Attributes
|
||||
err string
|
||||
}{
|
||||
// Mirror pods bound to us
|
||||
{
|
||||
name: "allow creating a mirror pod bound to self",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(mymirrorpod, nil, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "forbid update of mirror pod bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(mymirrorpod, mymirrorpod, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "", admission.Update, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "allow delete of mirror pod bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "", admission.Delete, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "forbid create of mirror pod status bound to self",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(mymirrorpod, nil, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "status", admission.Create, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "allow update of mirror pod status bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(mymirrorpod, mymirrorpod, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "status", admission.Update, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of mirror pod status bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, mymirrorpod.Namespace, mymirrorpod.Name, podResource, "status", admission.Delete, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
|
||||
// Mirror pods bound to another node
|
||||
{
|
||||
name: "forbid creating a mirror pod bound to another",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(othermirrorpod, nil, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid update of mirror pod bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(othermirrorpod, othermirrorpod, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "", admission.Update, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of mirror pod bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "", admission.Delete, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid create of mirror pod status bound to another",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(othermirrorpod, nil, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "status", admission.Create, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid update of mirror pod status bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(othermirrorpod, othermirrorpod, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "status", admission.Update, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of mirror pod status bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, othermirrorpod.Namespace, othermirrorpod.Name, podResource, "status", admission.Delete, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
|
||||
// Mirror pods not bound to any node
|
||||
{
|
||||
name: "forbid creating a mirror pod unbound",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundmirrorpod, nil, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid update of mirror pod unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundmirrorpod, unboundmirrorpod, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "", admission.Update, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of mirror pod unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "", admission.Delete, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid create of mirror pod status unbound",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundmirrorpod, nil, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "status", admission.Create, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid update of mirror pod status unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundmirrorpod, unboundmirrorpod, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "status", admission.Update, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of mirror pod status unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundmirrorpod.Namespace, unboundmirrorpod.Name, podResource, "status", admission.Delete, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
|
||||
// Normal pods bound to us
|
||||
{
|
||||
name: "forbid creating a normal pod bound to self",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(mypod, nil, podKind, mypod.Namespace, mypod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "can only create mirror pods",
|
||||
},
|
||||
{
|
||||
name: "forbid update of normal pod bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(mypod, mypod, podKind, mypod.Namespace, mypod.Name, podResource, "", admission.Update, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "allow delete of normal pod bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, mypod.Namespace, mypod.Name, podResource, "", admission.Delete, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "forbid create of normal pod status bound to self",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(mypod, nil, podKind, mypod.Namespace, mypod.Name, podResource, "status", admission.Create, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "allow update of normal pod status bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(mypod, mypod, podKind, mypod.Namespace, mypod.Name, podResource, "status", admission.Update, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of normal pod status bound to self",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, mypod.Namespace, mypod.Name, podResource, "status", admission.Delete, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
|
||||
// Normal pods bound to another
|
||||
{
|
||||
name: "forbid creating a normal pod bound to another",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(otherpod, nil, podKind, otherpod.Namespace, otherpod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "can only create mirror pods",
|
||||
},
|
||||
{
|
||||
name: "forbid update of normal pod bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(otherpod, otherpod, podKind, otherpod.Namespace, otherpod.Name, podResource, "", admission.Update, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of normal pod bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, otherpod.Namespace, otherpod.Name, podResource, "", admission.Delete, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid create of normal pod status bound to another",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(otherpod, nil, podKind, otherpod.Namespace, otherpod.Name, podResource, "status", admission.Create, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid update of normal pod status bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(otherpod, otherpod, podKind, otherpod.Namespace, otherpod.Name, podResource, "status", admission.Update, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of normal pod status bound to another",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, otherpod.Namespace, otherpod.Name, podResource, "status", admission.Delete, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
|
||||
// Normal pods not bound to any node
|
||||
{
|
||||
name: "forbid creating a normal pod unbound",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "can only create mirror pods",
|
||||
},
|
||||
{
|
||||
name: "forbid update of normal pod unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, unboundpod, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Update, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of normal pod unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Delete, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid create of normal pod status unbound",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Create, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
{
|
||||
name: "forbid update of normal pod status unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, unboundpod, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Update, mynode),
|
||||
err: "spec.nodeName set to itself",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of normal pod status unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Delete, mynode),
|
||||
err: "forbidden: unexpected operation",
|
||||
},
|
||||
|
||||
// Missing pod
|
||||
{
|
||||
name: "forbid delete of unknown pod",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Delete, mynode),
|
||||
err: "not found",
|
||||
},
|
||||
|
||||
// Resource pods
|
||||
{
|
||||
name: "forbid create of pod referencing service account",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(sapod, nil, podKind, sapod.Namespace, sapod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "reference a service account",
|
||||
},
|
||||
{
|
||||
name: "forbid create of pod referencing secret",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(secretpod, nil, podKind, secretpod.Namespace, secretpod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "reference secrets",
|
||||
},
|
||||
{
|
||||
name: "forbid create of pod referencing configmap",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(configmappod, nil, podKind, configmappod.Namespace, configmappod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "reference configmaps",
|
||||
},
|
||||
{
|
||||
name: "forbid create of pod referencing persistentvolumeclaim",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(pvcpod, nil, podKind, pvcpod.Namespace, pvcpod.Name, podResource, "", admission.Create, mynode),
|
||||
err: "reference persistentvolumeclaims",
|
||||
},
|
||||
|
||||
// My node object
|
||||
{
|
||||
name: "allow create of my node",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(mynodeObj, nil, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Create, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow update of my node",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(mynodeObj, mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow delete of my node",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Delete, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow update of my node status",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(mynodeObj, mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "status", admission.Update, mynode),
|
||||
err: "",
|
||||
},
|
||||
|
||||
// Other node object
|
||||
{
|
||||
name: "forbid create of other node",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(othernodeObj, nil, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "", admission.Create, mynode),
|
||||
err: "cannot modify other nodes",
|
||||
},
|
||||
{
|
||||
name: "forbid update of other node",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(othernodeObj, othernodeObj, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "", admission.Update, mynode),
|
||||
err: "cannot modify other nodes",
|
||||
},
|
||||
{
|
||||
name: "forbid delete of other node",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "", admission.Delete, mynode),
|
||||
err: "cannot modify other nodes",
|
||||
},
|
||||
{
|
||||
name: "forbid update of other node status",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(othernodeObj, othernodeObj, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "status", admission.Update, mynode),
|
||||
err: "cannot modify other nodes",
|
||||
},
|
||||
|
||||
// Unrelated objects
|
||||
{
|
||||
name: "allow create of unrelated object",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(&api.ConfigMap{}, nil, configmapKind, "myns", "mycm", configmapResource, "", admission.Create, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow update of unrelated object",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(&api.ConfigMap{}, &api.ConfigMap{}, configmapKind, "myns", "mycm", configmapResource, "", admission.Update, mynode),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow delete of unrelated object",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, configmapKind, "myns", "mycm", configmapResource, "", admission.Delete, mynode),
|
||||
err: "",
|
||||
},
|
||||
|
||||
// Unrelated user
|
||||
{
|
||||
name: "allow unrelated user creating a normal pod unbound",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Create, bob),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow unrelated user update of normal pod unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, unboundpod, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Update, bob),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow unrelated user delete of normal pod unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "", admission.Delete, bob),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow unrelated user create of normal pod status unbound",
|
||||
podsGetter: noExistingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Create, bob),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow unrelated user update of normal pod status unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(unboundpod, unboundpod, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Update, bob),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "allow unrelated user delete of normal pod status unbound",
|
||||
podsGetter: existingPods,
|
||||
attributes: admission.NewAttributesRecord(nil, nil, podKind, unboundpod.Namespace, unboundpod.Name, podResource, "status", admission.Delete, bob),
|
||||
err: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := NewPlugin(nodeidentifier.NewDefaultNodeIdentifier(), tt.strict)
|
||||
c.podsGetter = tt.podsGetter
|
||||
err := c.Admit(tt.attributes)
|
||||
if (err == nil) != (len(tt.err) == 0) {
|
||||
t.Errorf("nodePlugin.Admit() error = %v, expected %v", err, tt.err)
|
||||
return
|
||||
}
|
||||
if len(tt.err) > 0 && !strings.Contains(err.Error(), tt.err) {
|
||||
t.Errorf("nodePlugin.Admit() error = %v, expected %v", err, tt.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -33,7 +33,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("PersistentVolumeLabel", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("PersistentVolumeLabel", func(config io.Reader) (admission.Interface, error) {
|
||||
persistentVolumeLabelAdmission := NewPersistentVolumeLabel()
|
||||
return persistentVolumeLabelAdmission, nil
|
||||
})
|
||||
|
@@ -40,7 +40,12 @@ import (
|
||||
var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"}
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("PodNodeSelector", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("PodNodeSelector", func(config io.Reader) (admission.Interface, error) {
|
||||
// TODO move this to a versioned configuration file format.
|
||||
pluginConfig := readConfig(config)
|
||||
plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig)
|
||||
|
@@ -42,7 +42,12 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register(pluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(pluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
return NewPlugin(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -37,6 +37,11 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
kubeapiserveradmission.Plugins.Register("PodTolerationRestriction", func(config io.Reader) (admission.Interface, error) {
|
||||
pluginConfig, err := loadConfiguration(config)
|
||||
if err != nil {
|
||||
|
@@ -33,7 +33,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("ResourceQuota",
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("ResourceQuota",
|
||||
func(config io.Reader) (admission.Interface, error) {
|
||||
// load the configuration provided (if any)
|
||||
configuration, err := LoadConfiguration(config)
|
||||
|
@@ -45,7 +45,12 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
plugin := NewPlugin(psp.NewSimpleStrategyFactory(), getMatchingPolicies, true)
|
||||
return plugin, nil
|
||||
})
|
||||
|
@@ -27,7 +27,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register("SecurityContextDeny", func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("SecurityContextDeny", func(config io.Reader) (admission.Interface, error) {
|
||||
return NewSecurityContextDeny(), nil
|
||||
})
|
||||
}
|
||||
|
@@ -54,7 +54,12 @@ const DefaultAPITokenMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
const PluginName = "ServiceAccount"
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
serviceAccountAdmission := NewServiceAccount()
|
||||
return serviceAccountAdmission, nil
|
||||
})
|
||||
|
@@ -39,7 +39,12 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
kubeapiserveradmission.Plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
Register(&kubeapiserveradmission.Plugins)
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
plugin := newPlugin()
|
||||
return plugin, nil
|
||||
})
|
||||
|
@@ -103,10 +103,11 @@ func InstrumentRouteFunc(verb, resource string, routeFunc restful.RouteFunction)
|
||||
|
||||
routeFunc(request, response)
|
||||
|
||||
reportedVerb := verb
|
||||
if verb == "LIST" && strings.ToLower(request.QueryParameter("watch")) == "true" {
|
||||
verb = "WATCH"
|
||||
reportedVerb = "WATCH"
|
||||
}
|
||||
Monitor(&verb, &resource, cleanUserAgent(utilnet.GetHTTPClient(request.Request)), rw.Header().Get("Content-Type"), delegate.status, now)
|
||||
Monitor(&reportedVerb, &resource, cleanUserAgent(utilnet.GetHTTPClient(request.Request)), rw.Header().Get("Content-Type"), delegate.status, now)
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
apiVersion: apiregistration.k8s.io/v1alpha1
|
||||
apiVersion: apiregistration.k8s.io/v1beta1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1alpha1.mygroup.example.com
|
||||
|
@@ -1,4 +1,4 @@
|
||||
apiVersion: apiregistration.k8s.io/v1alpha1
|
||||
apiVersion: apiregistration.k8s.io/v1beta1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1alpha1.apiextensions.k8s.io
|
||||
|
@@ -5,6 +5,7 @@ licenses(["notice"])
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
@@ -24,3 +25,11 @@ go_library(
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["helpers_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library"],
|
||||
)
|
||||
|
@@ -18,10 +18,10 @@ package apiextensions
|
||||
|
||||
// SetCRDCondition sets the status condition. It either overwrites the existing one or
|
||||
// creates a new one
|
||||
func SetCRDCondition(customResourceDefinition *CustomResourceDefinition, newCondition CustomResourceDefinitionCondition) {
|
||||
existingCondition := FindCRDCondition(customResourceDefinition, newCondition.Type)
|
||||
func SetCRDCondition(crd *CustomResourceDefinition, newCondition CustomResourceDefinitionCondition) {
|
||||
existingCondition := FindCRDCondition(crd, newCondition.Type)
|
||||
if existingCondition == nil {
|
||||
customResourceDefinition.Status.Conditions = append(customResourceDefinition.Status.Conditions, newCondition)
|
||||
crd.Status.Conditions = append(crd.Status.Conditions, newCondition)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -34,11 +34,22 @@ func SetCRDCondition(customResourceDefinition *CustomResourceDefinition, newCond
|
||||
existingCondition.Message = newCondition.Message
|
||||
}
|
||||
|
||||
// RemoveCRDCondition removes the status condition.
|
||||
func RemoveCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) {
|
||||
newConditions := []CustomResourceDefinitionCondition{}
|
||||
for _, condition := range crd.Status.Conditions {
|
||||
if condition.Type != conditionType {
|
||||
newConditions = append(newConditions, condition)
|
||||
}
|
||||
}
|
||||
crd.Status.Conditions = newConditions
|
||||
}
|
||||
|
||||
// FindCRDCondition returns the condition you're looking for or nil
|
||||
func FindCRDCondition(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) *CustomResourceDefinitionCondition {
|
||||
for i := range customResourceDefinition.Status.Conditions {
|
||||
if customResourceDefinition.Status.Conditions[i].Type == conditionType {
|
||||
return &customResourceDefinition.Status.Conditions[i]
|
||||
func FindCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) *CustomResourceDefinitionCondition {
|
||||
for i := range crd.Status.Conditions {
|
||||
if crd.Status.Conditions[i].Type == conditionType {
|
||||
return &crd.Status.Conditions[i]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +57,18 @@ func FindCRDCondition(customResourceDefinition *CustomResourceDefinition, condit
|
||||
}
|
||||
|
||||
// IsCRDConditionTrue indicates if the condition is present and strictly true
|
||||
func IsCRDConditionTrue(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
|
||||
return IsCRDConditionPresentAndEqual(customResourceDefinition, conditionType, ConditionTrue)
|
||||
func IsCRDConditionTrue(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
|
||||
return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionTrue)
|
||||
}
|
||||
|
||||
// IsCRDConditionFalse indicates if the condition is present and false true
|
||||
func IsCRDConditionFalse(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
|
||||
return IsCRDConditionPresentAndEqual(customResourceDefinition, conditionType, ConditionFalse)
|
||||
func IsCRDConditionFalse(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
|
||||
return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionFalse)
|
||||
}
|
||||
|
||||
// IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the arg
|
||||
func IsCRDConditionPresentAndEqual(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType, status ConditionStatus) bool {
|
||||
for _, condition := range customResourceDefinition.Status.Conditions {
|
||||
func IsCRDConditionPresentAndEqual(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType, status ConditionStatus) bool {
|
||||
for _, condition := range crd.Status.Conditions {
|
||||
if condition.Type == conditionType {
|
||||
return condition.Status == status
|
||||
}
|
||||
@@ -76,3 +87,25 @@ func IsCRDConditionEquivalent(lhs, rhs *CustomResourceDefinitionCondition) bool
|
||||
|
||||
return lhs.Message == rhs.Message && lhs.Reason == rhs.Reason && lhs.Status == rhs.Status && lhs.Type == rhs.Type
|
||||
}
|
||||
|
||||
// CRDHasFinalizer returns true if the finalizer is in the list
|
||||
func CRDHasFinalizer(crd *CustomResourceDefinition, needle string) bool {
|
||||
for _, finalizer := range crd.Finalizers {
|
||||
if finalizer == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CRDRemoveFinalizer removes the finalizer if present
|
||||
func CRDRemoveFinalizer(crd *CustomResourceDefinition, needle string) {
|
||||
newFinalizers := []string{}
|
||||
for _, finalizer := range crd.Finalizers {
|
||||
if finalizer != needle {
|
||||
newFinalizers = append(newFinalizers, finalizer)
|
||||
}
|
||||
}
|
||||
crd.Finalizers = newFinalizers
|
||||
}
|
||||
|
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
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 apiextensions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestCRDHasFinalizer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
crd *CustomResourceDefinition
|
||||
finalizerToCheck string
|
||||
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "missing",
|
||||
crd: &CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it"}},
|
||||
},
|
||||
finalizerToCheck: "it",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "present",
|
||||
crd: &CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it", "it"}},
|
||||
},
|
||||
finalizerToCheck: "it",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
actual := CRDHasFinalizer(tc.crd, tc.finalizerToCheck)
|
||||
if tc.expected != actual {
|
||||
t.Errorf("%v expected %v, got %v", tc.name, tc.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCRDRemoveFinalizer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
crd *CustomResourceDefinition
|
||||
finalizerToCheck string
|
||||
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "missing",
|
||||
crd: &CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it"}},
|
||||
},
|
||||
finalizerToCheck: "it",
|
||||
expected: []string{"not-it"},
|
||||
},
|
||||
{
|
||||
name: "present",
|
||||
crd: &CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it", "it"}},
|
||||
},
|
||||
finalizerToCheck: "it",
|
||||
expected: []string{"not-it"},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
CRDRemoveFinalizer(tc.crd, tc.finalizerToCheck)
|
||||
if !reflect.DeepEqual(tc.expected, tc.crd.Finalizers) {
|
||||
t.Errorf("%v expected %v, got %v", tc.name, tc.expected, tc.crd.Finalizers)
|
||||
}
|
||||
}
|
||||
}
|
@@ -104,6 +104,10 @@ type CustomResourceDefinitionStatus struct {
|
||||
AcceptedNames CustomResourceDefinitionNames
|
||||
}
|
||||
|
||||
// CustomResourceCleanupFinalizer is the name of the finalizer which will delete instances of
|
||||
// a CustomResourceDefinition
|
||||
const CustomResourceCleanupFinalizer = "customresourcecleanup.apiextensions.k8s.io"
|
||||
|
||||
// +genclient=true
|
||||
// +nonNamespaced=true
|
||||
|
||||
|
@@ -104,6 +104,10 @@ type CustomResourceDefinitionStatus struct {
|
||||
AcceptedNames CustomResourceDefinitionNames `json:"acceptedNames" protobuf:"bytes,2,opt,name=acceptedNames"`
|
||||
}
|
||||
|
||||
// CustomResourceCleanupFinalizer is the name of the finalizer which will delete instances of
|
||||
// a CustomResourceDefinition
|
||||
const CustomResourceCleanupFinalizer = "customresourcecleanup.apiextensions.k8s.io"
|
||||
|
||||
// +genclient=true
|
||||
// +nonNamespaced=true
|
||||
|
||||
|
@@ -43,6 +43,7 @@ go_library(
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
"//vendor/k8s.io/client-go/discovery:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/workqueue:go_default_library",
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/apis/apiextensions:go_default_library",
|
||||
@@ -54,6 +55,7 @@ go_library(
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/listers/apiextensions/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/controller/finalizer:go_default_library",
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/controller/status:go_default_library",
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/registry/customresource:go_default_library",
|
||||
"//vendor/k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition:go_default_library",
|
||||
|
@@ -31,12 +31,14 @@ import (
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions"
|
||||
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/install"
|
||||
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1"
|
||||
"k8s.io/kube-apiextensions-server/pkg/client/clientset/internalclientset"
|
||||
internalinformers "k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion"
|
||||
"k8s.io/kube-apiextensions-server/pkg/controller/finalizer"
|
||||
"k8s.io/kube-apiextensions-server/pkg/controller/status"
|
||||
"k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition"
|
||||
|
||||
@@ -157,6 +159,10 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
|
||||
|
||||
customResourceDefinitionController := NewDiscoveryController(customResourceDefinitionInformers.Apiextensions().InternalVersion().CustomResourceDefinitions(), versionDiscoveryHandler, groupDiscoveryHandler)
|
||||
namingController := status.NewNamingConditionController(customResourceDefinitionInformers.Apiextensions().InternalVersion().CustomResourceDefinitions(), customResourceDefinitionClient)
|
||||
finalizingController := finalizer.NewCRDFinalizer(
|
||||
customResourceDefinitionInformers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
|
||||
customResourceDefinitionClient,
|
||||
dynamic.NewDynamicClientPool(s.GenericAPIServer.LoopbackClientConfig))
|
||||
|
||||
s.GenericAPIServer.AddPostStartHook("start-apiextensions-informers", func(context genericapiserver.PostStartHookContext) error {
|
||||
customResourceDefinitionInformers.Start(context.StopCh)
|
||||
@@ -165,6 +171,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
|
||||
s.GenericAPIServer.AddPostStartHook("start-apiextensions-controllers", func(context genericapiserver.PostStartHookContext) error {
|
||||
go customResourceDefinitionController.Run(context.StopCh)
|
||||
go namingController.Run(context.StopCh)
|
||||
go finalizingController.Run(5, context.StopCh)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@@ -101,12 +101,18 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
|
||||
}
|
||||
foundVersion = true
|
||||
|
||||
verbs := metav1.Verbs([]string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"})
|
||||
// if we're terminating we don't allow some verbs
|
||||
if apiextensions.IsCRDConditionTrue(crd, apiextensions.Terminating) {
|
||||
verbs = metav1.Verbs([]string{"delete", "deletecollection", "get", "list", "watch"})
|
||||
}
|
||||
|
||||
apiResourcesForDiscovery = append(apiResourcesForDiscovery, metav1.APIResource{
|
||||
Name: crd.Status.AcceptedNames.Plural,
|
||||
SingularName: crd.Status.AcceptedNames.Singular,
|
||||
Namespaced: crd.Spec.Scope == apiextensions.NamespaceScoped,
|
||||
Kind: crd.Status.AcceptedNames.Kind,
|
||||
Verbs: metav1.Verbs([]string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}),
|
||||
Verbs: verbs,
|
||||
ShortNames: crd.Status.AcceptedNames.ShortNames,
|
||||
})
|
||||
}
|
||||
|
@@ -104,13 +104,11 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if !ok {
|
||||
// programmer error
|
||||
panic("missing context")
|
||||
return
|
||||
}
|
||||
requestInfo, ok := apirequest.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
// programmer error
|
||||
panic("missing requestInfo")
|
||||
return
|
||||
}
|
||||
if !requestInfo.IsResourceRequest {
|
||||
pathParts := splitPath(requestInfo.Path)
|
||||
@@ -153,6 +151,8 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
r.delegate.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
terminating := apiextensions.IsCRDConditionTrue(crd, apiextensions.Terminating)
|
||||
|
||||
crdInfo := r.getServingInfoFor(crd)
|
||||
storage := crdInfo.storage
|
||||
requestScope := crdInfo.requestScope
|
||||
@@ -174,14 +174,26 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
handler(w, req)
|
||||
return
|
||||
case "create":
|
||||
if terminating {
|
||||
http.Error(w, fmt.Sprintf("%v not allowed while CustomResourceDefinition is terminating", requestInfo.Verb), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
handler := handlers.CreateResource(storage, requestScope, discovery.NewUnstructuredObjectTyper(nil), r.admission)
|
||||
handler(w, req)
|
||||
return
|
||||
case "update":
|
||||
if terminating {
|
||||
http.Error(w, fmt.Sprintf("%v not allowed while CustomResourceDefinition is terminating", requestInfo.Verb), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
handler := handlers.UpdateResource(storage, requestScope, discovery.NewUnstructuredObjectTyper(nil), r.admission)
|
||||
handler(w, req)
|
||||
return
|
||||
case "patch":
|
||||
if terminating {
|
||||
http.Error(w, fmt.Sprintf("%v not allowed while CustomResourceDefinition is terminating", requestInfo.Verb), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
handler := handlers.PatchResource(storage, requestScope, r.admission, unstructured.UnstructuredObjectConverter{})
|
||||
handler(w, req)
|
||||
return
|
||||
@@ -190,6 +202,11 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
handler := handlers.DeleteResource(storage, allowsOptions, requestScope, r.admission)
|
||||
handler(w, req)
|
||||
return
|
||||
case "deletecollection":
|
||||
checkBody := true
|
||||
handler := handlers.DeleteCollection(storage, checkBody, requestScope, r.admission)
|
||||
handler(w, req)
|
||||
return
|
||||
|
||||
default:
|
||||
http.Error(w, fmt.Sprintf("unhandled verb %q", requestInfo.Verb), http.StatusMethodNotAllowed)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user