Merge branch 'master' into correct-deprecation-errors

This commit is contained in:
Alexander Campbell
2017-05-19 08:55:17 -07:00
110 changed files with 2356 additions and 103 deletions

View File

@@ -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/.): **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.): **What keywords did you search in Kubernetes issues before filing this one?** (If you have found any duplicates, you should instead reply there.):

View File

@@ -1203,14 +1203,14 @@ function create-cluster-autoscaler-mig-config() {
# Each MIG must have at least one node, so the min number of nodes # Each MIG must have at least one node, so the min number of nodes
# must be greater or equal to the number of migs. # 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}" echo "AUTOSCALER_MIN_NODES must be greater or equal ${NUM_MIGS}"
exit 2 exit 2
fi fi
# Each MIG must have at least one node, so the min number of nodes # Each MIG must have at least one node, so the min number of nodes
# must be greater or equal to the number of migs. # 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}" echo "AUTOSCALER_MAX_NODES must be greater or equal ${NUM_MIGS}"
exit 2 exit 2
fi fi

View File

@@ -199,6 +199,12 @@ func StartControllers(s *options.CloudControllerManagerServer, kubeconfig *restc
client := func(serviceAccountName string) clientset.Interface { client := func(serviceAccountName string) clientset.Interface {
return rootClientBuilder.ClientOrDie(serviceAccountName) return rootClientBuilder.ClientOrDie(serviceAccountName)
} }
if cloud != nil {
// Initialize the cloud provider with a reference to the clientBuilder
cloud.Initialize(clientBuilder)
}
versionedClient := client("shared-informers") versionedClient := client("shared-informers")
sharedInformers := informers.NewSharedInformerFactory(versionedClient, resyncPeriod(s)()) sharedInformers := informers.NewSharedInformerFactory(versionedClient, resyncPeriod(s)())

View File

@@ -54,6 +54,7 @@ go_library(
"//plugin/pkg/admission/namespace/autoprovision:go_default_library", "//plugin/pkg/admission/namespace/autoprovision:go_default_library",
"//plugin/pkg/admission/namespace/exists:go_default_library", "//plugin/pkg/admission/namespace/exists:go_default_library",
"//plugin/pkg/admission/namespace/lifecycle: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/persistentvolume/label:go_default_library",
"//plugin/pkg/admission/podnodeselector:go_default_library", "//plugin/pkg/admission/podnodeselector:go_default_library",
"//plugin/pkg/admission/podpreset:go_default_library", "//plugin/pkg/admission/podpreset:go_default_library",

View File

@@ -37,6 +37,7 @@ import (
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision" _ "k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision"
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/exists" _ "k8s.io/kubernetes/plugin/pkg/admission/namespace/exists"
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/lifecycle" _ "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/persistentvolume/label"
_ "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector" _ "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector"
_ "k8s.io/kubernetes/plugin/pkg/admission/podpreset" _ "k8s.io/kubernetes/plugin/pkg/admission/podpreset"

View File

@@ -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) 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) { if ctx.IsControllerEnabled(nodeControllerName) {
_, clusterCIDR, err := net.ParseCIDR(s.ClusterCIDR) _, clusterCIDR, err := net.ParseCIDR(s.ClusterCIDR)
if err != nil { if err != nil {

View File

@@ -41,6 +41,7 @@ filegroup(
":package-srcs", ":package-srcs",
"//cmd/libs/go2idl/client-gen/args:all-srcs", "//cmd/libs/go2idl/client-gen/args:all-srcs",
"//cmd/libs/go2idl/client-gen/generators: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/test_apis/testgroup:all-srcs",
"//cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset:all-srcs", "//cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset:all-srcs",
"//cmd/libs/go2idl/client-gen/types:all-srcs", "//cmd/libs/go2idl/client-gen/types:all-srcs",

View File

@@ -22,6 +22,7 @@ go_library(
"//cmd/libs/go2idl/client-gen/args:go_default_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/fake:go_default_library",
"//cmd/libs/go2idl/client-gen/generators/scheme: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", "//cmd/libs/go2idl/client-gen/types:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/gengo/args:go_default_library", "//vendor/k8s.io/gengo/args:go_default_library",

View File

@@ -29,6 +29,7 @@ import (
clientgenargs "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/args" 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/fake"
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/scheme" "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" clientgentypes "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/types"
"github.com/golang/glog" "github.com/golang/glog"
@@ -275,7 +276,8 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
gvToTypes := map[clientgentypes.GroupVersion][]*types.Type{} gvToTypes := map[clientgentypes.GroupVersion][]*types.Type{}
for gv, inputDir := range customArgs.GroupVersionToInputPath { 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 { for n, t := range p.Types {
// filter out types which are not included in user specified overrides. // filter out types which are not included in user specified overrides.
typesOverride, ok := includedTypesOverrides[gv] typesOverride, ok := includedTypesOverrides[gv]

View File

@@ -19,6 +19,7 @@ go_library(
deps = [ deps = [
"//cmd/libs/go2idl/client-gen/args:go_default_library", "//cmd/libs/go2idl/client-gen/args:go_default_library",
"//cmd/libs/go2idl/client-gen/generators/scheme: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", "//cmd/libs/go2idl/client-gen/types:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/gengo/generator:go_default_library", "//vendor/k8s.io/gengo/generator:go_default_library",

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/gengo/generator" "k8s.io/gengo/generator"
"k8s.io/gengo/namer" "k8s.io/gengo/namer"
"k8s.io/gengo/types" "k8s.io/gengo/types"
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/path"
) )
// genFakeForType produces a file for each top-level type. // 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. // 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 { if override := types.ExtractCommentTags("+", p.DocComments)["groupName"]; override != nil {
groupName = override[0] groupName = override[0]
} }

View File

@@ -23,6 +23,7 @@ import (
"k8s.io/gengo/generator" "k8s.io/gengo/generator"
"k8s.io/gengo/namer" "k8s.io/gengo/namer"
"k8s.io/gengo/types" "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. // 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 = "" groupName = ""
} }
// allow user to define a group name that's different from the one parsed from the directory. // 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 { if override := types.ExtractCommentTags("+", p.DocComments)["groupName"]; override != nil {
groupName = override[0] 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"}), "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"}), "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"}), "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(groupInterfaceTemplate, m)
sw.Do(groupClientTemplate, m) sw.Do(groupClientTemplate, m)

View File

@@ -12,6 +12,7 @@ go_library(
srcs = ["generator_for_scheme.go"], srcs = ["generator_for_scheme.go"],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//cmd/libs/go2idl/client-gen/path:go_default_library",
"//cmd/libs/go2idl/client-gen/types:go_default_library", "//cmd/libs/go2idl/client-gen/types:go_default_library",
"//vendor/k8s.io/gengo/generator:go_default_library", "//vendor/k8s.io/gengo/generator:go_default_library",
"//vendor/k8s.io/gengo/namer:go_default_library", "//vendor/k8s.io/gengo/namer:go_default_library",

View File

@@ -26,6 +26,7 @@ import (
"k8s.io/gengo/generator" "k8s.io/gengo/generator"
"k8s.io/gengo/namer" "k8s.io/gengo/namer"
"k8s.io/gengo/types" "k8s.io/gengo/types"
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/path"
clientgentypes "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/types" 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.Dir(packagePath)
} }
packagePath = filepath.Join(packagePath, "install") 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 break
} else { } 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))))
} }
} }
} }

View 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"],
)

View 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
}

View File

@@ -52,7 +52,8 @@ function unjoin_clusters() {
"${context}" \ "${context}" \
--federation-system-namespace=${FEDERATION_NAMESPACE} \ --federation-system-namespace=${FEDERATION_NAMESPACE} \
--context="${FEDERATION_KUBE_CONTEXT}" \ --context="${FEDERATION_KUBE_CONTEXT}" \
--host-cluster-context="${HOST_CLUSTER_CONTEXT}" --host-cluster-context="${HOST_CLUSTER_CONTEXT}" \
--v=4
done done
} }

View File

@@ -94,7 +94,8 @@ function init() {
--apiserver-enable-basic-auth=true \ --apiserver-enable-basic-auth=true \
--apiserver-enable-token-auth=true \ --apiserver-enable-token-auth=true \
--apiserver-arg-overrides="--v=4" \ --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 # join_clusters joins the clusters in the local kubeconfig to federation. The clusters
@@ -107,7 +108,8 @@ function join_clusters() {
"${context}" \ "${context}" \
--federation-system-namespace=${FEDERATION_NAMESPACE} \ --federation-system-namespace=${FEDERATION_NAMESPACE} \
--host-cluster-context="${HOST_CLUSTER_CONTEXT}" \ --host-cluster-context="${HOST_CLUSTER_CONTEXT}" \
--context="${FEDERATION_KUBE_CONTEXT}" --context="${FEDERATION_KUBE_CONTEXT}" \
--v=4
done done
} }

View File

@@ -122,7 +122,7 @@ func (fu *federatedUpdaterImpl) Update(ops []FederatedOperation) error {
fu.recordEvent(op.Obj, eventType, "Deleting", eventArgs...) fu.recordEvent(op.Obj, eventType, "Deleting", eventArgs...)
err = fu.deleteFunction(clientset, op.Obj) err = fu.deleteFunction(clientset, op.Obj)
// IsNotFound error is fine since that means the object is deleted already. // IsNotFound error is fine since that means the object is deleted already.
if err != nil && !errors.IsNotFound(err) { if errors.IsNotFound(err) {
err = nil err = nil
} }
} }

View File

@@ -26,6 +26,7 @@ cmd/kubectl
cmd/kubelet cmd/kubelet
cmd/libs/go2idl/client-gen cmd/libs/go2idl/client-gen
cmd/libs/go2idl/client-gen/generators cmd/libs/go2idl/client-gen/generators
cmd/libs/go2idl/client-gen/path
cmd/libs/go2idl/client-gen/test_apis/testgroup/install cmd/libs/go2idl/client-gen/test_apis/testgroup/install
cmd/libs/go2idl/conversion-gen cmd/libs/go2idl/conversion-gen
cmd/libs/go2idl/deepcopy-gen cmd/libs/go2idl/deepcopy-gen
@@ -86,6 +87,7 @@ pkg/apis/settings/install
pkg/apis/settings/validation pkg/apis/settings/validation
pkg/apis/storage/install pkg/apis/storage/install
pkg/apis/storage/validation pkg/apis/storage/validation
pkg/auth/nodeidentifier
pkg/bootstrap/api pkg/bootstrap/api
pkg/client/conditions pkg/client/conditions
pkg/client/informers/informers_generated/externalversions pkg/client/informers/informers_generated/externalversions

View File

@@ -25,6 +25,7 @@ DOCKERIZE_KUBELET=${DOCKERIZE_KUBELET:-""}
ALLOW_PRIVILEGED=${ALLOW_PRIVILEGED:-""} ALLOW_PRIVILEGED=${ALLOW_PRIVILEGED:-""}
ALLOW_SECURITY_CONTEXT=${ALLOW_SECURITY_CONTEXT:-""} ALLOW_SECURITY_CONTEXT=${ALLOW_SECURITY_CONTEXT:-""}
PSP_ADMISSION=${PSP_ADMISSION:-""} PSP_ADMISSION=${PSP_ADMISSION:-""}
NODE_ADMISSION=${NODE_ADMISSION:-""}
RUNTIME_CONFIG=${RUNTIME_CONFIG:-""} RUNTIME_CONFIG=${RUNTIME_CONFIG:-""}
KUBELET_AUTHORIZATION_WEBHOOK=${KUBELET_AUTHORIZATION_WEBHOOK:-""} KUBELET_AUTHORIZATION_WEBHOOK=${KUBELET_AUTHORIZATION_WEBHOOK:-""}
KUBELET_AUTHENTICATION_WEBHOOK=${KUBELET_AUTHENTICATION_WEBHOOK:-""} KUBELET_AUTHENTICATION_WEBHOOK=${KUBELET_AUTHENTICATION_WEBHOOK:-""}
@@ -388,6 +389,9 @@ function start_apiserver {
if [[ -n "${PSP_ADMISSION}" ]]; then if [[ -n "${PSP_ADMISSION}" ]]; then
security_admission=",PodSecurityPolicy" security_admission=",PodSecurityPolicy"
fi fi
if [[ -n "${NODE_ADMISSION}" ]]; then
security_admission=",NodeRestriction"
fi
# Admission Controllers to invoke prior to persisting objects in cluster # Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount${security_admission},ResourceQuota,DefaultStorageClass,DefaultTolerationSeconds ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount${security_admission},ResourceQuota,DefaultStorageClass,DefaultTolerationSeconds

View File

@@ -85,7 +85,6 @@ cgroups-per-qos
chaos-chance chaos-chance
cidr-allocator-type cidr-allocator-type
clean-start clean-start
cleanup
cleanup-iptables cleanup-iptables
client-ca-file client-ca-file
client-certificate client-certificate
@@ -273,7 +272,6 @@ federation-name
federation-system-namespace federation-system-namespace
federation-upgrade-target federation-upgrade-target
file-check-frequency file-check-frequency
file_content_in_loop
file-suffix file-suffix
flex-volume-plugin-dir flex-volume-plugin-dir
forward-services forward-services
@@ -302,7 +300,6 @@ google-json-key
grace-period grace-period
ha-domain ha-domain
hairpin-mode hairpin-mode
hard
hard-pod-affinity-symmetric-weight hard-pod-affinity-symmetric-weight
healthz-bind-address healthz-bind-address
healthz-port healthz-port
@@ -563,7 +560,6 @@ pv-recycler-minimum-timeout-nfs
pv-recycler-pod-template-filepath-hostpath pv-recycler-pod-template-filepath-hostpath
pv-recycler-pod-template-filepath-nfs pv-recycler-pod-template-filepath-nfs
pv-recycler-timeout-increment-hostpath pv-recycler-timeout-increment-hostpath
quiet
read-only-port read-only-port
really-crash-for-testing really-crash-for-testing
reconcile-cidr reconcile-cidr
@@ -592,13 +588,11 @@ requestheader-username-headers
required-contexts required-contexts
require-kubeconfig require-kubeconfig
resolv-conf resolv-conf
resource
resource-container resource-container
resource-name resource-name
resource-quota-sync-period resource-quota-sync-period
resource-version resource-version
results-dir results-dir
retry_time
rkt-api-endpoint rkt-api-endpoint
rkt-path rkt-path
rkt-stage1-image rkt-stage1-image
@@ -617,7 +611,6 @@ schedule-pods-here
scheduler-config scheduler-config
scheduler-name scheduler-name
schema-cache-dir schema-cache-dir
scopes
scrape-timeout scrape-timeout
seccomp-profile-root seccomp-profile-root
secondary-node-eviction-rate secondary-node-eviction-rate
@@ -668,7 +661,6 @@ storage-media-type
storage-version storage-version
storage-versions storage-versions
streaming-connection-idle-timeout streaming-connection-idle-timeout
subresource
suicide-timeout suicide-timeout
sync-frequency sync-frequency
system-cgroups system-cgroups
@@ -712,7 +704,6 @@ use-service-account-credentials
user-whitelist user-whitelist
use-service-account-credentials use-service-account-credentials
use-taint-based-evictions use-taint-based-evictions
verb
verify-only verify-only
versioned-clientset-package versioned-clientset-package
viper-config viper-config

View File

@@ -31,6 +31,7 @@ filegroup(
"//pkg/apis/settings:all-srcs", "//pkg/apis/settings:all-srcs",
"//pkg/apis/storage:all-srcs", "//pkg/apis/storage:all-srcs",
"//pkg/auth/authorizer/abac:all-srcs", "//pkg/auth/authorizer/abac:all-srcs",
"//pkg/auth/nodeidentifier:all-srcs",
"//pkg/auth/user:all-srcs", "//pkg/auth/user:all-srcs",
"//pkg/bootstrap/api:all-srcs", "//pkg/bootstrap/api:all-srcs",
"//pkg/capabilities:all-srcs", "//pkg/capabilities:all-srcs",

View File

@@ -21,11 +21,14 @@ import (
"k8s.io/kubernetes/pkg/api" "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 // VisitPodSecretNames invokes the visitor function with the name of every secret
// referenced by the pod spec. If visitor returns false, visiting is short-circuited. // referenced by the pod spec. If visitor returns false, visiting is short-circuited.
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited. // 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 { for _, reference := range pod.Spec.ImagePullSecrets {
if !visitor(reference.Name) { if !visitor(reference.Name) {
return false return false
@@ -86,7 +89,7 @@ func VisitPodSecretNames(pod *api.Pod, visitor func(string) bool) bool {
return true 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 { for _, env := range container.EnvFrom {
if env.SecretRef != nil { if env.SecretRef != nil {
if !visitor(env.SecretRef.Name) { if !visitor(env.SecretRef.Name) {
@@ -104,6 +107,60 @@ func visitContainerSecretNames(container *api.Container, visitor func(string) bo
return true 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. // IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady(pod *api.Pod) bool { func IsPodReady(pod *api.Pod) bool {
return IsPodReadyConditionTrue(pod.Status) return IsPodReadyConditionTrue(pod.Status)

View File

@@ -107,11 +107,14 @@ func SetInitContainersStatusesAnnotations(pod *v1.Pod) error {
return nil 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 // VisitPodSecretNames invokes the visitor function with the name of every secret
// referenced by the pod spec. If visitor returns false, visiting is short-circuited. // referenced by the pod spec. If visitor returns false, visiting is short-circuited.
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited. // 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 { for _, reference := range pod.Spec.ImagePullSecrets {
if !visitor(reference.Name) { if !visitor(reference.Name) {
return false return false
@@ -173,7 +176,7 @@ func VisitPodSecretNames(pod *v1.Pod, visitor func(string) bool) bool {
return true 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 { for _, env := range container.EnvFrom {
if env.SecretRef != nil { if env.SecretRef != nil {
if !visitor(env.SecretRef.Name) { if !visitor(env.SecretRef.Name) {

View 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"],
)

View 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
}

View 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)
}
})
}
}

View 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)
}

View File

@@ -17,6 +17,7 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
], ],

View File

@@ -23,10 +23,13 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/controller"
) )
// Interface is an abstract, pluggable interface for cloud providers. // Interface is an abstract, pluggable interface for cloud providers.
type Interface interface { 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 returns a balancer interface. Also returns true if the interface is supported, false otherwise.
LoadBalancer() (LoadBalancer, bool) LoadBalancer() (LoadBalancer, bool)
// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise. // Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.

View File

@@ -30,6 +30,7 @@ go_library(
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/api/v1/service:go_default_library", "//pkg/api/v1/service:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/credentialprovider/aws:go_default_library", "//pkg/credentialprovider/aws:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library", "//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",

View File

@@ -49,6 +49,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/api/v1/service" "k8s.io/kubernetes/pkg/api/v1/service"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
) )
@@ -888,6 +889,9 @@ func newAWSCloud(config io.Reader, awsServices Services) (*Cloud, error) {
return awsCloud, nil 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. // Clusters returns the list of clusters.
func (c *Cloud) Clusters() (cloudprovider.Clusters, bool) { func (c *Cloud) Clusters() (cloudprovider.Clusters, bool) {
return nil, false return nil, false

View File

@@ -29,6 +29,7 @@ go_library(
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/api/v1/service:go_default_library", "//pkg/api/v1/service:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/version:go_default_library", "//pkg/version:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//vendor/github.com/Azure/azure-sdk-for-go/arm/compute:go_default_library", "//vendor/github.com/Azure/azure-sdk-for-go/arm/compute:go_default_library",

View File

@@ -20,8 +20,10 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"time"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/version" "k8s.io/kubernetes/pkg/version"
"github.com/Azure/azure-sdk-for-go/arm/compute" "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"
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"time"
) )
// CloudProviderName is the value used for the --cloud-provider flag // 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 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. // LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
func (az *Cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { func (az *Cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
return az, true return az, true

View File

@@ -18,6 +18,7 @@ go_library(
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/xanzy/go-cloudstack/cloudstack:go_default_library", "//vendor/github.com/xanzy/go-cloudstack/cloudstack:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library", "//vendor/gopkg.in/gcfg.v1:go_default_library",

View File

@@ -24,6 +24,7 @@ import (
"github.com/xanzy/go-cloudstack/cloudstack" "github.com/xanzy/go-cloudstack/cloudstack"
"gopkg.in/gcfg.v1" "gopkg.in/gcfg.v1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
// ProviderName is the name of this cloud provider. // 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 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. // LoadBalancer returns an implementation of LoadBalancer for CloudStack.
func (cs *CSCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { func (cs *CSCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
return cs, true return cs, true

View File

@@ -17,6 +17,7 @@ go_library(
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
], ],
) )

View File

@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
const defaultProviderName = "fake" const defaultProviderName = "fake"
@@ -82,6 +83,9 @@ func (f *FakeCloud) ClearCalls() {
f.Calls = []string{} 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) { func (f *FakeCloud) ListClusters() ([]string, error) {
return f.ClusterList, f.Err return f.ClusterList, f.Err
} }

View File

@@ -38,6 +38,7 @@ go_library(
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/api/v1/service:go_default_library", "//pkg/api/v1/service:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/util/net/sets:go_default_library", "//pkg/util/net/sets:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//vendor/cloud.google.com/go/compute/metadata:go_default_library", "//vendor/cloud.google.com/go/compute/metadata:go_default_library",

View File

@@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/flowcontrol" "k8s.io/client-go/util/flowcontrol"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
"github.com/golang/glog" "github.com/golang/glog"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@@ -233,6 +234,9 @@ func CreateGCECloud(projectID, region, zone string, managedZones []string, netwo
}, nil }, 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. // LoadBalancer returns an implementation of LoadBalancer for Google Compute Engine.
func (gce *GCECloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { func (gce *GCECloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
return gce, true return gce, true

View File

@@ -17,7 +17,6 @@ limitations under the License.
package gce package gce
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "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. // 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 // i.e. metadata service and other local methods cannot be used here
func (gce *GCECloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) { 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 // 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 // node that is requesting this ID. i.e. metadata service and other local
// methods cannot be used here // methods cannot be used here
func (gce *GCECloud) InstanceTypeByProviderID(providerID string) (string, error) { 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). // ExternalID returns the cloud provider ID of the node with the specified NodeName (deprecated).
@@ -339,30 +366,38 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error)
func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) { func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) {
// Avoid changing behaviour when not managing multiple zones // Avoid changing behaviour when not managing multiple zones
for _, zone := range gce.managedZones { for _, zone := range gce.managedZones {
name = canonicalizeInstanceName(name) instance, err := gce.getInstanceFromProjectInZoneByName(gce.projectID, zone, name)
mc := newInstancesMetricContext("get", zone)
res, err := gce.service.Instances.Get(gce.projectID, zone, name).Do()
mc.Observe(err)
if err != nil { if err != nil {
glog.Errorf("getInstanceByName: failed to get instance %s; err: %v", name, err)
if isHTTPErrorCode(err, http.StatusNotFound) { if isHTTPErrorCode(err, http.StatusNotFound) {
continue continue
} }
return nil, err return nil, err
} }
return &gceInstance{ return instance, nil
Zone: lastComponent(res.Zone),
Name: res.Name,
ID: res.Id,
Disks: res.Disks,
Type: lastComponent(res.MachineType),
}, nil
} }
return nil, cloudprovider.InstanceNotFound 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,
ID: res.Id,
Disks: res.Disks,
Type: lastComponent(res.MachineType),
}, nil
}
func getInstanceIDViaMetadata() (string, error) { func getInstanceIDViaMetadata() (string, error) {
result, err := metadata.Get("instance/hostname") result, err := metadata.Get("instance/hostname")
if err != nil { if err != nil {

View File

@@ -158,3 +158,93 @@ func TestCreateFirewallFails(t *testing.T) {
t.Errorf("error expected when creating firewall without any tags found") 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)
}
}
}

View File

@@ -17,7 +17,9 @@ limitations under the License.
package gce package gce
import ( import (
"errors"
"fmt" "fmt"
"regexp"
"strings" "strings"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@@ -35,6 +37,8 @@ type gceInstance struct {
Type string Type string
} }
var providerIdRE = regexp.MustCompile(`^` + ProviderName + `://([^/]+)/([^/]+)/([^/]+)$`)
func getProjectAndZone() (string, string, error) { func getProjectAndZone() (string, string, error) {
result, err := metadata.Get("instance/zone") result, err := metadata.Get("instance/zone")
if err != nil { if err != nil {
@@ -100,3 +104,14 @@ func isHTTPErrorCode(err error, code int) bool {
apiErr, ok := err.(*googleapi.Error) apiErr, ok := err.(*googleapi.Error)
return ok && apiErr.Code == code 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
}

View File

@@ -20,6 +20,7 @@ go_library(
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/github.com/golang/glog: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:go_default_library",
"//vendor/github.com/mesos/mesos-go/detector/zoo:go_default_library", "//vendor/github.com/mesos/mesos-go/detector/zoo:go_default_library",

View File

@@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
const ( 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 // Implementation of Instances.CurrentNodeName
func (c *MesosCloud) CurrentNodeName(hostname string) (types.NodeName, error) { func (c *MesosCloud) CurrentNodeName(hostname string) (types.NodeName, error) {
return types.NodeName(hostname), nil return types.NodeName(hostname), nil

View File

@@ -24,6 +24,7 @@ go_library(
"//pkg/api/v1/helper:go_default_library", "//pkg/api/v1/helper:go_default_library",
"//pkg/api/v1/service:go_default_library", "//pkg/api/v1/service:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/util/exec:go_default_library", "//pkg/util/exec:go_default_library",
"//pkg/util/mount:go_default_library", "//pkg/util/mount:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",

View File

@@ -45,6 +45,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper" v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
const ProviderName = "openstack" const ProviderName = "openstack"
@@ -265,6 +266,9 @@ func newOpenStack(cfg Config) (*OpenStack, error) {
return &os, nil 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 // mapNodeNameToServerName maps a k8s NodeName to an OpenStack Server Name
// This is a simple string cast. // This is a simple string cast.
func mapNodeNameToServerName(nodeName types.NodeName) string { func mapNodeNameToServerName(nodeName types.NodeName) string {

View File

@@ -15,6 +15,7 @@ go_library(
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library", "//vendor/gopkg.in/gcfg.v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
], ],

View File

@@ -34,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
const ProviderName = "ovirt" const ProviderName = "ovirt"
@@ -116,7 +117,10 @@ func newOVirtCloud(config io.Reader) (*OVirtCloud, error) {
return &OVirtCloud{VmsRequest: request}, nil 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 return nil, false
} }

View File

@@ -16,6 +16,7 @@ go_library(
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/api/v1/helper:go_default_library", "//pkg/api/v1/helper:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/vmware/photon-controller-go-sdk/photon:go_default_library", "//vendor/github.com/vmware/photon-controller-go-sdk/photon:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library", "//vendor/gopkg.in/gcfg.v1:go_default_library",

View File

@@ -41,6 +41,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper" v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
const ( const (
@@ -292,6 +293,9 @@ func newPCCloud(cfg PCConfig) (*PCCloud, error) {
return &pc, nil 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. // Instances returns an implementation of Instances for Photon Controller.
func (pc *PCCloud) Instances() (cloudprovider.Instances, bool) { func (pc *PCCloud) Instances() (cloudprovider.Instances, bool) {
return pc, true return pc, true

View File

@@ -15,6 +15,7 @@ go_library(
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/rackspace/gophercloud:go_default_library", "//vendor/github.com/rackspace/gophercloud:go_default_library",
"//vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach:go_default_library", "//vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach:go_default_library",

View File

@@ -43,6 +43,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
const ( const (
@@ -218,6 +219,9 @@ func newRackspace(cfg Config) (*Rackspace, error) {
return &os, nil return &os, nil
} }
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
func (os *Rackspace) Initialize(clientBuilder controller.ControllerClientBuilder) {}
type Instances struct { type Instances struct {
compute *gophercloud.ServiceClient compute *gophercloud.ServiceClient
} }

View File

@@ -19,6 +19,7 @@ go_library(
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/api/v1/helper:go_default_library", "//pkg/api/v1/helper:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/vmware/govmomi:go_default_library", "//vendor/github.com/vmware/govmomi:go_default_library",
"//vendor/github.com/vmware/govmomi/find:go_default_library", "//vendor/github.com/vmware/govmomi/find:go_default_library",

View File

@@ -50,6 +50,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper" v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
) )
const ( 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 // UUID gets the BIOS UUID via the sys interface. This UUID is known by vsphere
func getvmUUID() (string, error) { func getvmUUID() (string, error) {
id, err := ioutil.ReadFile(UUIDPath) id, err := ioutil.ReadFile(UUIDPath)

View File

@@ -146,7 +146,6 @@ func (e *EndpointController) Run(workers int, stopCh <-chan struct{}) {
go func() { go func() {
defer utilruntime.HandleCrash() defer utilruntime.HandleCrash()
time.Sleep(5 * time.Minute) // give time for our cache to fill
e.checkLeftoverEndpoints() e.checkLeftoverEndpoints()
}() }()

View File

@@ -18,6 +18,7 @@ package envvars
import ( import (
"fmt" "fmt"
"net"
"strconv" "strconv"
"strings" "strings"
@@ -78,18 +79,21 @@ func makeLinkVariables(service *v1.Service) []v1.EnvVar {
if sp.Protocol != "" { if sp.Protocol != "" {
protocol = string(sp.Protocol) protocol = string(sp.Protocol)
} }
hostPort := net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(sp.Port)))
if i == 0 { if i == 0 {
// Docker special-cases the first port. // Docker special-cases the first port.
all = append(all, v1.EnvVar{ all = append(all, v1.EnvVar{
Name: prefix + "_PORT", 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)) portPrefix := fmt.Sprintf("%s_PORT_%d_%s", prefix, sp.Port, strings.ToUpper(protocol))
all = append(all, []v1.EnvVar{ all = append(all, []v1.EnvVar{
{ {
Name: portPrefix, 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", Name: portPrefix + "_PROTO",

View File

@@ -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) vars := envvars.FromServices(sl)
expected := []v1.EnvVar{ 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_PROTO", Value: "tcp"},
{Name: "Q_U_U_X_PORT_8083_TCP_PORT", Value: "8083"}, {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: "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) { if len(vars) != len(expected) {
t.Errorf("Expected %d env vars, got: %+v", len(expected), vars) t.Errorf("Expected %d env vars, got: %+v", len(expected), vars)

View File

@@ -101,7 +101,7 @@ func hexCIDR(cidr string) (string, error) {
return "", err return "", err
} }
ip = ip.Mask(ipnet.Mask) ip = ip.Mask(ipnet.Mask)
hexIP := hex.EncodeToString([]byte(ip.To4())) hexIP := hex.EncodeToString([]byte(ip))
hexMask := ipnet.Mask.String() hexMask := ipnet.Mask.String()
return hexIP + "/" + hexMask, nil return hexIP + "/" + hexMask, nil
} }
@@ -119,6 +119,9 @@ func asciiCIDR(cidr string) (string, error) {
ip := net.IP(ipData) ip := net.IP(ipData)
maskData, err := hex.DecodeString(parts[1]) maskData, err := hex.DecodeString(parts[1])
if err != nil {
return "", err
}
mask := net.IPMask(maskData) mask := net.IPMask(maskData)
size, _ := mask.Size() size, _ := mask.Size()

View File

@@ -94,19 +94,33 @@ func TestNextClassID(t *testing.T) {
func TestHexCIDR(t *testing.T) { func TestHexCIDR(t *testing.T) {
tests := []struct { tests := []struct {
name string
input string input string
output string output string
expectErr bool expectErr bool
}{ }{
{ {
input: "1.2.0.0/16", name: "IPv4 masked",
input: "1.2.3.4/16",
output: "01020000/ffff0000", output: "01020000/ffff0000",
}, },
{ {
name: "IPv4 host",
input: "172.17.0.2/32", input: "172.17.0.2/32",
output: "ac110002/ffffffff", 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", input: "foo",
expectErr: true, expectErr: true,
}, },
@@ -115,21 +129,76 @@ func TestHexCIDR(t *testing.T) {
output, err := hexCIDR(test.input) output, err := hexCIDR(test.input)
if test.expectErr { if test.expectErr {
if err == nil { if err == nil {
t.Error("unexpected non-error") t.Errorf("case %s: unexpected non-error", test.name)
} }
} else { } else {
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("case %s: unexpected error: %v", test.name, err)
} }
if output != test.output { 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 { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("case %s: unexpected error: %v", test.name, err)
} }
if input != test.input { if output != test.output {
t.Errorf("expected: %s, saw: %s", test.input, input) t.Errorf("case %s: expected: %s, saw: %s",
test.name, test.output, output)
} }
} }
} }

View File

@@ -27,6 +27,7 @@ filegroup(
"//plugin/pkg/admission/namespace/autoprovision:all-srcs", "//plugin/pkg/admission/namespace/autoprovision:all-srcs",
"//plugin/pkg/admission/namespace/exists:all-srcs", "//plugin/pkg/admission/namespace/exists:all-srcs",
"//plugin/pkg/admission/namespace/lifecycle: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/persistentvolume/label:all-srcs",
"//plugin/pkg/admission/podnodeselector:all-srcs", "//plugin/pkg/admission/podnodeselector:all-srcs",
"//plugin/pkg/admission/podpreset:all-srcs", "//plugin/pkg/admission/podpreset:all-srcs",

View File

@@ -24,7 +24,12 @@ import (
) )
func init() { 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 return NewAlwaysAdmit(), nil
}) })
} }

View File

@@ -34,7 +34,12 @@ import (
) )
func init() { 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 return NewAlwaysPullImages(), nil
}) })
} }

View File

@@ -28,7 +28,12 @@ import (
) )
func init() { 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 return NewInterPodAntiAffinity(), nil
}) })
} }

View File

@@ -40,7 +40,12 @@ var (
) )
func init() { 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 return NewDefaultTolerationSeconds(), nil
}) })
} }

View File

@@ -25,7 +25,12 @@ import (
) )
func init() { 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 return NewAlwaysDeny(), nil
}) })
} }

View File

@@ -30,13 +30,18 @@ import (
) )
func init() { 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 return NewDenyEscalatingExec(), nil
}) })
// This is for legacy support of the DenyExecOnPrivileged admission controller. Most // This is for legacy support of the DenyExecOnPrivileged admission controller. Most
// of the time DenyEscalatingExec should be preferred. // 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 return NewDenyExecOnPrivileged(), nil
}) })
} }

View File

@@ -32,7 +32,12 @@ import (
) )
func init() { 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 // the pods/status endpoint is ignored by this plugin since old kubelets
// corrupt them. the pod status strategy ensures status updates cannot mutate // corrupt them. the pod status strategy ensures status updates cannot mutate
// ownerRef. // ownerRef.

View File

@@ -50,7 +50,12 @@ var (
) )
func init() { 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) newImagePolicyWebhook, err := NewImagePolicyWebhook(config)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -47,7 +47,12 @@ const (
// WARNING: this feature is experimental and will definitely change. // WARNING: this feature is experimental and will definitely change.
func init() { 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 // TODO: remove the usage of flags in favor of reading versioned configuration
s, err := newDataSource(*source) s, err := newDataSource(*source)
if err != nil { if err != nil {

View File

@@ -44,7 +44,12 @@ const (
) )
func init() { 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{}) return NewLimitRanger(&DefaultLimitRangerActions{})
}) })
} }

View File

@@ -31,7 +31,12 @@ import (
) )
func init() { 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 return NewProvision(), nil
}) })
} }

View File

@@ -31,7 +31,12 @@ import (
) )
func init() { 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 return NewExists(), nil
}) })
} }

View File

@@ -51,7 +51,12 @@ const (
) )
func init() { 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)) return NewLifecycle(sets.NewString(metav1.NamespaceDefault, metav1.NamespaceSystem, metav1.NamespacePublic))
}) })
} }

View 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"],
)

View File

@@ -0,0 +1,8 @@
approvers:
- deads2k
- liggitt
- timstclair
reviewers:
- deads2k
- liggitt
- timstclair

View 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
}

View 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)
}
})
}
}

View File

@@ -33,7 +33,12 @@ import (
) )
func init() { 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() persistentVolumeLabelAdmission := NewPersistentVolumeLabel()
return persistentVolumeLabelAdmission, nil return persistentVolumeLabelAdmission, nil
}) })

View File

@@ -40,7 +40,12 @@ import (
var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"} var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"}
func init() { 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. // TODO move this to a versioned configuration file format.
pluginConfig := readConfig(config) pluginConfig := readConfig(config)
plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig) plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig)

View File

@@ -42,7 +42,12 @@ const (
) )
func init() { 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 return NewPlugin(), nil
}) })
} }

View File

@@ -37,6 +37,11 @@ import (
) )
func init() { 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) { kubeapiserveradmission.Plugins.Register("PodTolerationRestriction", func(config io.Reader) (admission.Interface, error) {
pluginConfig, err := loadConfiguration(config) pluginConfig, err := loadConfiguration(config)
if err != nil { if err != nil {

View File

@@ -33,7 +33,12 @@ import (
) )
func init() { 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) { func(config io.Reader) (admission.Interface, error) {
// load the configuration provided (if any) // load the configuration provided (if any)
configuration, err := LoadConfiguration(config) configuration, err := LoadConfiguration(config)

View File

@@ -45,7 +45,12 @@ const (
) )
func init() { 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) plugin := NewPlugin(psp.NewSimpleStrategyFactory(), getMatchingPolicies, true)
return plugin, nil return plugin, nil
}) })

View File

@@ -27,7 +27,12 @@ import (
) )
func init() { 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 return NewSecurityContextDeny(), nil
}) })
} }

View File

@@ -54,7 +54,12 @@ const DefaultAPITokenMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
const PluginName = "ServiceAccount" const PluginName = "ServiceAccount"
func init() { 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() serviceAccountAdmission := NewServiceAccount()
return serviceAccountAdmission, nil return serviceAccountAdmission, nil
}) })

View File

@@ -39,7 +39,12 @@ const (
) )
func init() { 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() plugin := newPlugin()
return plugin, nil return plugin, nil
}) })

View File

@@ -103,10 +103,11 @@ func InstrumentRouteFunc(verb, resource string, routeFunc restful.RouteFunction)
routeFunc(request, response) routeFunc(request, response)
reportedVerb := verb
if verb == "LIST" && strings.ToLower(request.QueryParameter("watch")) == "true" { 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)
}) })
} }

View File

@@ -1,4 +1,4 @@
apiVersion: apiregistration.k8s.io/v1alpha1 apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService kind: APIService
metadata: metadata:
name: v1alpha1.mygroup.example.com name: v1alpha1.mygroup.example.com

View File

@@ -1,4 +1,4 @@
apiVersion: apiregistration.k8s.io/v1alpha1 apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService kind: APIService
metadata: metadata:
name: v1alpha1.apiextensions.k8s.io name: v1alpha1.apiextensions.k8s.io

View File

@@ -5,6 +5,7 @@ licenses(["notice"])
load( load(
"@io_bazel_rules_go//go:def.bzl", "@io_bazel_rules_go//go:def.bzl",
"go_library", "go_library",
"go_test",
) )
go_library( go_library(
@@ -24,3 +25,11 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_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"],
)

View File

@@ -18,10 +18,10 @@ package apiextensions
// SetCRDCondition sets the status condition. It either overwrites the existing one or // SetCRDCondition sets the status condition. It either overwrites the existing one or
// creates a new one // creates a new one
func SetCRDCondition(customResourceDefinition *CustomResourceDefinition, newCondition CustomResourceDefinitionCondition) { func SetCRDCondition(crd *CustomResourceDefinition, newCondition CustomResourceDefinitionCondition) {
existingCondition := FindCRDCondition(customResourceDefinition, newCondition.Type) existingCondition := FindCRDCondition(crd, newCondition.Type)
if existingCondition == nil { if existingCondition == nil {
customResourceDefinition.Status.Conditions = append(customResourceDefinition.Status.Conditions, newCondition) crd.Status.Conditions = append(crd.Status.Conditions, newCondition)
return return
} }
@@ -34,11 +34,22 @@ func SetCRDCondition(customResourceDefinition *CustomResourceDefinition, newCond
existingCondition.Message = newCondition.Message 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 // FindCRDCondition returns the condition you're looking for or nil
func FindCRDCondition(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) *CustomResourceDefinitionCondition { func FindCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) *CustomResourceDefinitionCondition {
for i := range customResourceDefinition.Status.Conditions { for i := range crd.Status.Conditions {
if customResourceDefinition.Status.Conditions[i].Type == conditionType { if crd.Status.Conditions[i].Type == conditionType {
return &customResourceDefinition.Status.Conditions[i] return &crd.Status.Conditions[i]
} }
} }
@@ -46,18 +57,18 @@ func FindCRDCondition(customResourceDefinition *CustomResourceDefinition, condit
} }
// IsCRDConditionTrue indicates if the condition is present and strictly true // IsCRDConditionTrue indicates if the condition is present and strictly true
func IsCRDConditionTrue(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool { func IsCRDConditionTrue(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
return IsCRDConditionPresentAndEqual(customResourceDefinition, conditionType, ConditionTrue) return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionTrue)
} }
// IsCRDConditionFalse indicates if the condition is present and false true // IsCRDConditionFalse indicates if the condition is present and false true
func IsCRDConditionFalse(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool { func IsCRDConditionFalse(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
return IsCRDConditionPresentAndEqual(customResourceDefinition, conditionType, ConditionFalse) return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionFalse)
} }
// IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the arg // IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the arg
func IsCRDConditionPresentAndEqual(customResourceDefinition *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType, status ConditionStatus) bool { func IsCRDConditionPresentAndEqual(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType, status ConditionStatus) bool {
for _, condition := range customResourceDefinition.Status.Conditions { for _, condition := range crd.Status.Conditions {
if condition.Type == conditionType { if condition.Type == conditionType {
return condition.Status == status 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 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
}

View File

@@ -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)
}
}
}

View File

@@ -104,6 +104,10 @@ type CustomResourceDefinitionStatus struct {
AcceptedNames CustomResourceDefinitionNames AcceptedNames CustomResourceDefinitionNames
} }
// CustomResourceCleanupFinalizer is the name of the finalizer which will delete instances of
// a CustomResourceDefinition
const CustomResourceCleanupFinalizer = "customresourcecleanup.apiextensions.k8s.io"
// +genclient=true // +genclient=true
// +nonNamespaced=true // +nonNamespaced=true

View File

@@ -104,6 +104,10 @@ type CustomResourceDefinitionStatus struct {
AcceptedNames CustomResourceDefinitionNames `json:"acceptedNames" protobuf:"bytes,2,opt,name=acceptedNames"` 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 // +genclient=true
// +nonNamespaced=true // +nonNamespaced=true

View File

@@ -43,6 +43,7 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/server:go_default_library", "//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend: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/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/tools/cache:go_default_library",
"//vendor/k8s.io/client-go/util/workqueue: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", "//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: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/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/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/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/customresource:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition:go_default_library", "//vendor/k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition:go_default_library",

View File

@@ -31,12 +31,14 @@ import (
genericregistry "k8s.io/apiserver/pkg/registry/generic" genericregistry "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server" 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"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/install" "k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/install"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1" "k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1"
"k8s.io/kube-apiextensions-server/pkg/client/clientset/internalclientset" "k8s.io/kube-apiextensions-server/pkg/client/clientset/internalclientset"
internalinformers "k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion" 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/controller/status"
"k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition" "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) customResourceDefinitionController := NewDiscoveryController(customResourceDefinitionInformers.Apiextensions().InternalVersion().CustomResourceDefinitions(), versionDiscoveryHandler, groupDiscoveryHandler)
namingController := status.NewNamingConditionController(customResourceDefinitionInformers.Apiextensions().InternalVersion().CustomResourceDefinitions(), customResourceDefinitionClient) 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 { s.GenericAPIServer.AddPostStartHook("start-apiextensions-informers", func(context genericapiserver.PostStartHookContext) error {
customResourceDefinitionInformers.Start(context.StopCh) 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 { s.GenericAPIServer.AddPostStartHook("start-apiextensions-controllers", func(context genericapiserver.PostStartHookContext) error {
go customResourceDefinitionController.Run(context.StopCh) go customResourceDefinitionController.Run(context.StopCh)
go namingController.Run(context.StopCh) go namingController.Run(context.StopCh)
go finalizingController.Run(5, context.StopCh)
return nil return nil
}) })

View File

@@ -101,12 +101,18 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
} }
foundVersion = true 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{ apiResourcesForDiscovery = append(apiResourcesForDiscovery, metav1.APIResource{
Name: crd.Status.AcceptedNames.Plural, Name: crd.Status.AcceptedNames.Plural,
SingularName: crd.Status.AcceptedNames.Singular, SingularName: crd.Status.AcceptedNames.Singular,
Namespaced: crd.Spec.Scope == apiextensions.NamespaceScoped, Namespaced: crd.Spec.Scope == apiextensions.NamespaceScoped,
Kind: crd.Status.AcceptedNames.Kind, Kind: crd.Status.AcceptedNames.Kind,
Verbs: metav1.Verbs([]string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}), Verbs: verbs,
ShortNames: crd.Status.AcceptedNames.ShortNames, ShortNames: crd.Status.AcceptedNames.ShortNames,
}) })
} }

View File

@@ -104,13 +104,11 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if !ok { if !ok {
// programmer error // programmer error
panic("missing context") panic("missing context")
return
} }
requestInfo, ok := apirequest.RequestInfoFrom(ctx) requestInfo, ok := apirequest.RequestInfoFrom(ctx)
if !ok { if !ok {
// programmer error // programmer error
panic("missing requestInfo") panic("missing requestInfo")
return
} }
if !requestInfo.IsResourceRequest { if !requestInfo.IsResourceRequest {
pathParts := splitPath(requestInfo.Path) pathParts := splitPath(requestInfo.Path)
@@ -153,6 +151,8 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.delegate.ServeHTTP(w, req) r.delegate.ServeHTTP(w, req)
} }
terminating := apiextensions.IsCRDConditionTrue(crd, apiextensions.Terminating)
crdInfo := r.getServingInfoFor(crd) crdInfo := r.getServingInfoFor(crd)
storage := crdInfo.storage storage := crdInfo.storage
requestScope := crdInfo.requestScope requestScope := crdInfo.requestScope
@@ -174,14 +174,26 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
handler(w, req) handler(w, req)
return return
case "create": 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 := handlers.CreateResource(storage, requestScope, discovery.NewUnstructuredObjectTyper(nil), r.admission)
handler(w, req) handler(w, req)
return return
case "update": 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 := handlers.UpdateResource(storage, requestScope, discovery.NewUnstructuredObjectTyper(nil), r.admission)
handler(w, req) handler(w, req)
return return
case "patch": 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 := handlers.PatchResource(storage, requestScope, r.admission, unstructured.UnstructuredObjectConverter{})
handler(w, req) handler(w, req)
return return
@@ -190,6 +202,11 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
handler := handlers.DeleteResource(storage, allowsOptions, requestScope, r.admission) handler := handlers.DeleteResource(storage, allowsOptions, requestScope, r.admission)
handler(w, req) handler(w, req)
return return
case "deletecollection":
checkBody := true
handler := handlers.DeleteCollection(storage, checkBody, requestScope, r.admission)
handler(w, req)
return
default: default:
http.Error(w, fmt.Sprintf("unhandled verb %q", requestInfo.Verb), http.StatusMethodNotAllowed) 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