Add settings API and admission controller

export functions from pkg/api/validation add settings API add settings to pkg/registry add settings api to pkg/master/master.go add admission control plugin for pod preset add new admission control plugin to kube-apiserver add settings to import_known_versions.go add settings to codegen add validation tests add settings to client generation add protobufs generation for settings api update linted packages add settings to testapi add settings install to clientset add start of e2e add pod preset plugin to config-test.sh Signed-off-by: 's avatarJess Frazelle <acidburn@google.com>
parent 2869ff6a
...@@ -191,7 +191,7 @@ fi ...@@ -191,7 +191,7 @@ fi
ENABLE_RESCHEDULER="${KUBE_ENABLE_RESCHEDULER:-true}" ENABLE_RESCHEDULER="${KUBE_ENABLE_RESCHEDULER:-true}"
# If we included ResourceQuota, we should keep it at the end of the list to prevent incrementing quota usage prematurely. # If we included ResourceQuota, we should keep it at the end of the list to prevent incrementing quota usage prematurely.
ADMISSION_CONTROL="${KUBE_ADMISSION_CONTROL:-NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds}" ADMISSION_CONTROL="${KUBE_ADMISSION_CONTROL:-NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds,PodPreset}"
# Optional: if set to true kube-up will automatically check for existing resources and clean them up. # Optional: if set to true kube-up will automatically check for existing resources and clean them up.
KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false} KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false}
......
...@@ -39,6 +39,7 @@ import ( ...@@ -39,6 +39,7 @@ import (
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/lifecycle" _ "k8s.io/kubernetes/plugin/pkg/admission/namespace/lifecycle"
_ "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/resourcequota" _ "k8s.io/kubernetes/plugin/pkg/admission/resourcequota"
_ "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy" _ "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy"
_ "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny" _ "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
......
...@@ -46,6 +46,7 @@ var ( ...@@ -46,6 +46,7 @@ var (
"storage/", "storage/",
"apps/", "apps/",
"policy/", "policy/",
"settings/",
}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/,rbac/\"") }, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/,rbac/\"")
includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.") includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.")
basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group. Default to \"k8s.io/kubernetes/pkg/apis\"") basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group. Default to \"k8s.io/kubernetes/pkg/apis\"")
......
...@@ -85,6 +85,7 @@ func New() *Generator { ...@@ -85,6 +85,7 @@ func New() *Generator {
`k8s.io/kubernetes/federation/apis/federation/v1beta1`, `k8s.io/kubernetes/federation/apis/federation/v1beta1`,
`k8s.io/kubernetes/pkg/apis/certificates/v1beta1`, `k8s.io/kubernetes/pkg/apis/certificates/v1beta1`,
`k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1`, `k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1`,
`k8s.io/kubernetes/pkg/apis/settings/v1alpha1`,
`k8s.io/kubernetes/pkg/apis/storage/v1beta1`, `k8s.io/kubernetes/pkg/apis/storage/v1beta1`,
}, ","), }, ","),
DropEmbeddedFields: "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta", DropEmbeddedFields: "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta",
......
...@@ -79,6 +79,8 @@ pkg/apis/imagepolicy/install ...@@ -79,6 +79,8 @@ pkg/apis/imagepolicy/install
pkg/apis/policy/install pkg/apis/policy/install
pkg/apis/rbac/install pkg/apis/rbac/install
pkg/apis/rbac/v1alpha1 pkg/apis/rbac/v1alpha1
pkg/apis/settings/install
pkg/apis/settings/validation
pkg/apis/storage/install pkg/apis/storage/install
pkg/apis/storage/validation pkg/apis/storage/validation
pkg/bootstrap/api pkg/bootstrap/api
...@@ -103,6 +105,8 @@ pkg/client/informers/informers_generated/externalversions/policy/v1beta1 ...@@ -103,6 +105,8 @@ pkg/client/informers/informers_generated/externalversions/policy/v1beta1
pkg/client/informers/informers_generated/externalversions/rbac pkg/client/informers/informers_generated/externalversions/rbac
pkg/client/informers/informers_generated/externalversions/rbac/v1alpha1 pkg/client/informers/informers_generated/externalversions/rbac/v1alpha1
pkg/client/informers/informers_generated/externalversions/rbac/v1beta1 pkg/client/informers/informers_generated/externalversions/rbac/v1beta1
pkg/client/informers/informers_generated/externalversions/settings
pkg/client/informers/informers_generated/externalversions/settings/v1alpha1
pkg/client/informers/informers_generated/externalversions/storage pkg/client/informers/informers_generated/externalversions/storage
pkg/client/informers/informers_generated/externalversions/storage/v1beta1 pkg/client/informers/informers_generated/externalversions/storage/v1beta1
pkg/client/informers/informers_generated/internalversion pkg/client/informers/informers_generated/internalversion
...@@ -122,6 +126,8 @@ pkg/client/informers/informers_generated/internalversion/policy ...@@ -122,6 +126,8 @@ pkg/client/informers/informers_generated/internalversion/policy
pkg/client/informers/informers_generated/internalversion/policy/internalversion pkg/client/informers/informers_generated/internalversion/policy/internalversion
pkg/client/informers/informers_generated/internalversion/rbac pkg/client/informers/informers_generated/internalversion/rbac
pkg/client/informers/informers_generated/internalversion/rbac/internalversion pkg/client/informers/informers_generated/internalversion/rbac/internalversion
pkg/client/informers/informers_generated/internalversion/settings
pkg/client/informers/informers_generated/internalversion/settings/internalversion
pkg/client/informers/informers_generated/internalversion/storage pkg/client/informers/informers_generated/internalversion/storage
pkg/client/informers/informers_generated/internalversion/storage/internalversion pkg/client/informers/informers_generated/internalversion/storage/internalversion
pkg/client/listers/apps/internalversion pkg/client/listers/apps/internalversion
...@@ -152,6 +158,8 @@ pkg/client/listers/policy/v1beta1 ...@@ -152,6 +158,8 @@ pkg/client/listers/policy/v1beta1
pkg/client/listers/rbac/internalversion pkg/client/listers/rbac/internalversion
pkg/client/listers/rbac/v1alpha1 pkg/client/listers/rbac/v1alpha1
pkg/client/listers/rbac/v1beta1 pkg/client/listers/rbac/v1beta1
pkg/client/listers/settings/internalversion
pkg/client/listers/settings/v1alpha1
pkg/client/listers/storage/internalversion pkg/client/listers/storage/internalversion
pkg/client/listers/storage/v1beta1 pkg/client/listers/storage/v1beta1
pkg/client/metrics pkg/client/metrics
......
...@@ -29,7 +29,7 @@ KUBE_OUTPUT_BINPATH="${KUBE_OUTPUT}/bin" ...@@ -29,7 +29,7 @@ KUBE_OUTPUT_BINPATH="${KUBE_OUTPUT}/bin"
# compression for build container # compression for build container
KUBE_RSYNC_COMPRESS="${KUBE_RSYNC_COMPRESS:-0}" KUBE_RSYNC_COMPRESS="${KUBE_RSYNC_COMPRESS:-0}"
# Set no_proxy for localhost if behind a proxy, otherwise, # Set no_proxy for localhost if behind a proxy, otherwise,
# the connections to localhost in scripts will time out # the connections to localhost in scripts will time out
export no_proxy=127.0.0.1,localhost export no_proxy=127.0.0.1,localhost
...@@ -68,7 +68,8 @@ imagepolicy.k8s.io/v1alpha1 \ ...@@ -68,7 +68,8 @@ imagepolicy.k8s.io/v1alpha1 \
policy/v1beta1 \ policy/v1beta1 \
rbac.authorization.k8s.io/v1beta1 \ rbac.authorization.k8s.io/v1beta1 \
rbac.authorization.k8s.io/v1alpha1 \ rbac.authorization.k8s.io/v1alpha1 \
storage.k8s.io/v1beta1\ settings.k8s.io/v1alpha1 \
storage.k8s.io/v1beta1 \
}" }"
# not all group versions are exposed by the server. This list contains those # not all group versions are exposed by the server. This list contains those
......
...@@ -43,7 +43,7 @@ GV_DIRS=() ...@@ -43,7 +43,7 @@ GV_DIRS=()
for gv in "${GROUP_VERSIONS[@]}"; do for gv in "${GROUP_VERSIONS[@]}"; do
# add items, but strip off any leading apis/ you find to match command expectations # add items, but strip off any leading apis/ you find to match command expectations
api_dir=$(kube::util::group-version-to-pkg-path "${gv}") api_dir=$(kube::util::group-version-to-pkg-path "${gv}")
nopkg_dir=${api_dir#pkg/} nopkg_dir=${api_dir#pkg/}
pkg_dir=${nopkg_dir#apis/} pkg_dir=${nopkg_dir#apis/}
# skip groups that aren't being served, clients for these don't matter # skip groups that aren't being served, clients for these don't matter
......
...@@ -45,6 +45,7 @@ import ( ...@@ -45,6 +45,7 @@ import (
"k8s.io/kubernetes/pkg/apis/imagepolicy" "k8s.io/kubernetes/pkg/apis/imagepolicy"
"k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/settings"
"k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/apis/storage"
_ "k8s.io/kubernetes/federation/apis/federation/install" _ "k8s.io/kubernetes/federation/apis/federation/install"
...@@ -60,6 +61,7 @@ import ( ...@@ -60,6 +61,7 @@ import (
_ "k8s.io/kubernetes/pkg/apis/imagepolicy/install" _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install"
_ "k8s.io/kubernetes/pkg/apis/settings/install"
_ "k8s.io/kubernetes/pkg/apis/storage/install" _ "k8s.io/kubernetes/pkg/apis/storage/install"
) )
...@@ -75,6 +77,7 @@ var ( ...@@ -75,6 +77,7 @@ var (
Federation TestGroup Federation TestGroup
Rbac TestGroup Rbac TestGroup
Certificates TestGroup Certificates TestGroup
Settings TestGroup
Storage TestGroup Storage TestGroup
ImagePolicy TestGroup ImagePolicy TestGroup
...@@ -230,6 +233,15 @@ func init() { ...@@ -230,6 +233,15 @@ func init() {
externalTypes: api.Scheme.KnownTypes(externalGroupVersion), externalTypes: api.Scheme.KnownTypes(externalGroupVersion),
} }
} }
if _, ok := Groups[settings.GroupName]; !ok {
externalGroupVersion := schema.GroupVersion{Group: settings.GroupName, Version: api.Registry.GroupOrDie(settings.GroupName).GroupVersion.Version}
Groups[settings.GroupName] = TestGroup{
externalGroupVersion: externalGroupVersion,
internalGroupVersion: settings.SchemeGroupVersion,
internalTypes: api.Scheme.KnownTypes(settings.SchemeGroupVersion),
externalTypes: api.Scheme.KnownTypes(externalGroupVersion),
}
}
if _, ok := Groups[storage.GroupName]; !ok { if _, ok := Groups[storage.GroupName]; !ok {
externalGroupVersion := schema.GroupVersion{Group: storage.GroupName, Version: api.Registry.GroupOrDie(storage.GroupName).GroupVersion.Version} externalGroupVersion := schema.GroupVersion{Group: storage.GroupName, Version: api.Registry.GroupOrDie(storage.GroupName).GroupVersion.Version}
Groups[storage.GroupName] = TestGroup{ Groups[storage.GroupName] = TestGroup{
...@@ -276,6 +288,7 @@ func init() { ...@@ -276,6 +288,7 @@ func init() {
Extensions = Groups[extensions.GroupName] Extensions = Groups[extensions.GroupName]
Federation = Groups[federation.GroupName] Federation = Groups[federation.GroupName]
Rbac = Groups[rbac.GroupName] Rbac = Groups[rbac.GroupName]
Settings = Groups[settings.GroupName]
Storage = Groups[storage.GroupName] Storage = Groups[storage.GroupName]
ImagePolicy = Groups[imagepolicy.GroupName] ImagePolicy = Groups[imagepolicy.GroupName]
Authorization = Groups[authorization.GroupName] Authorization = Groups[authorization.GroupName]
......
...@@ -319,7 +319,7 @@ func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fld ...@@ -319,7 +319,7 @@ func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fld
return genericvalidation.ValidateNoNewFinalizers(newFinalizers, oldFinalizers, fldPath) return genericvalidation.ValidateNoNewFinalizers(newFinalizers, oldFinalizers, fldPath)
} }
func validateVolumes(volumes []api.Volume, fldPath *field.Path) (sets.String, field.ErrorList) { func ValidateVolumes(volumes []api.Volume, fldPath *field.Path) (sets.String, field.ErrorList) {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
allNames := sets.String{} allNames := sets.String{}
...@@ -1330,7 +1330,7 @@ func validateContainerPorts(ports []api.ContainerPort, fldPath *field.Path) fiel ...@@ -1330,7 +1330,7 @@ func validateContainerPorts(ports []api.ContainerPort, fldPath *field.Path) fiel
return allErrs return allErrs
} }
func validateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList { func ValidateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
for i, ev := range vars { for i, ev := range vars {
...@@ -1422,7 +1422,7 @@ func validateContainerResourceFieldSelector(fs *api.ResourceFieldSelector, expre ...@@ -1422,7 +1422,7 @@ func validateContainerResourceFieldSelector(fs *api.ResourceFieldSelector, expre
return allErrs return allErrs
} }
func validateEnvFrom(vars []api.EnvFromSource, fldPath *field.Path) field.ErrorList { func ValidateEnvFrom(vars []api.EnvFromSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
for i, ev := range vars { for i, ev := range vars {
idxPath := fldPath.Index(i) idxPath := fldPath.Index(i)
...@@ -1523,7 +1523,7 @@ func validateSecretKeySelector(s *api.SecretKeySelector, fldPath *field.Path) fi ...@@ -1523,7 +1523,7 @@ func validateSecretKeySelector(s *api.SecretKeySelector, fldPath *field.Path) fi
return allErrs return allErrs
} }
func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath *field.Path) field.ErrorList { func ValidateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
mountpoints := sets.NewString() mountpoints := sets.NewString()
...@@ -1778,8 +1778,8 @@ func validateContainers(containers []api.Container, volumes sets.String, fldPath ...@@ -1778,8 +1778,8 @@ func validateContainers(containers []api.Container, volumes sets.String, fldPath
allErrs = append(allErrs, validateProbe(ctr.ReadinessProbe, idxPath.Child("readinessProbe"))...) allErrs = append(allErrs, validateProbe(ctr.ReadinessProbe, idxPath.Child("readinessProbe"))...)
allErrs = append(allErrs, validateContainerPorts(ctr.Ports, idxPath.Child("ports"))...) allErrs = append(allErrs, validateContainerPorts(ctr.Ports, idxPath.Child("ports"))...)
allErrs = append(allErrs, validateEnv(ctr.Env, idxPath.Child("env"))...) allErrs = append(allErrs, ValidateEnv(ctr.Env, idxPath.Child("env"))...)
allErrs = append(allErrs, validateVolumeMounts(ctr.VolumeMounts, volumes, idxPath.Child("volumeMounts"))...) allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volumes, idxPath.Child("volumeMounts"))...)
allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, idxPath.Child("imagePullPolicy"))...) allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, idxPath.Child("imagePullPolicy"))...)
allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"))...) allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"))...)
allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, idxPath.Child("securityContext"))...) allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, idxPath.Child("securityContext"))...)
...@@ -1988,7 +1988,7 @@ func ValidatePod(pod *api.Pod) field.ErrorList { ...@@ -1988,7 +1988,7 @@ func ValidatePod(pod *api.Pod) field.ErrorList {
func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList { func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
allVolumes, vErrs := validateVolumes(spec.Volumes, fldPath.Child("volumes")) allVolumes, vErrs := ValidateVolumes(spec.Volumes, fldPath.Child("volumes"))
allErrs = append(allErrs, vErrs...) allErrs = append(allErrs, vErrs...)
allErrs = append(allErrs, validateContainers(spec.Containers, allVolumes, fldPath.Child("containers"))...) allErrs = append(allErrs, validateContainers(spec.Containers, allVolumes, fldPath.Child("containers"))...)
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, allVolumes, fldPath.Child("initContainers"))...) allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, allVolumes, fldPath.Child("initContainers"))...)
......
...@@ -1931,7 +1931,7 @@ func TestValidateVolumes(t *testing.T) { ...@@ -1931,7 +1931,7 @@ func TestValidateVolumes(t *testing.T) {
} }
for i, tc := range testCases { for i, tc := range testCases {
names, errs := validateVolumes([]api.Volume{tc.vol}, field.NewPath("field")) names, errs := ValidateVolumes([]api.Volume{tc.vol}, field.NewPath("field"))
if len(errs) > 0 && tc.errtype == "" { if len(errs) > 0 && tc.errtype == "" {
t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs) t.Errorf("[%d: %q] unexpected error(s): %v", i, tc.name, errs)
} else if len(errs) > 1 { } else if len(errs) > 1 {
...@@ -1957,7 +1957,7 @@ func TestValidateVolumes(t *testing.T) { ...@@ -1957,7 +1957,7 @@ func TestValidateVolumes(t *testing.T) {
{Name: "abc", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, {Name: "abc", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
{Name: "abc", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, {Name: "abc", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
} }
_, errs := validateVolumes(dupsCase, field.NewPath("field")) _, errs := ValidateVolumes(dupsCase, field.NewPath("field"))
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("expected error") t.Errorf("expected error")
} else if len(errs) != 1 { } else if len(errs) != 1 {
...@@ -2121,7 +2121,7 @@ func TestValidateEnv(t *testing.T) { ...@@ -2121,7 +2121,7 @@ func TestValidateEnv(t *testing.T) {
}, },
}, },
} }
if errs := validateEnv(successCase, field.NewPath("field")); len(errs) != 0 { if errs := ValidateEnv(successCase, field.NewPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs) t.Errorf("expected success: %v", errs)
} }
...@@ -2303,7 +2303,7 @@ func TestValidateEnv(t *testing.T) { ...@@ -2303,7 +2303,7 @@ func TestValidateEnv(t *testing.T) {
}, },
} }
for _, tc := range errorCases { for _, tc := range errorCases {
if errs := validateEnv(tc.envs, field.NewPath("field")); len(errs) == 0 { if errs := ValidateEnv(tc.envs, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %s", tc.name) t.Errorf("expected failure for %s", tc.name)
} else { } else {
for i := range errs { for i := range errs {
...@@ -2341,7 +2341,7 @@ func TestValidateEnvFrom(t *testing.T) { ...@@ -2341,7 +2341,7 @@ func TestValidateEnvFrom(t *testing.T) {
}, },
}, },
} }
if errs := validateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 { if errs := ValidateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs) t.Errorf("expected success: %v", errs)
} }
...@@ -2413,7 +2413,7 @@ func TestValidateEnvFrom(t *testing.T) { ...@@ -2413,7 +2413,7 @@ func TestValidateEnvFrom(t *testing.T) {
}, },
} }
for _, tc := range errorCases { for _, tc := range errorCases {
if errs := validateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 { if errs := ValidateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %s", tc.name) t.Errorf("expected failure for %s", tc.name)
} else { } else {
for i := range errs { for i := range errs {
...@@ -2439,7 +2439,7 @@ func TestValidateVolumeMounts(t *testing.T) { ...@@ -2439,7 +2439,7 @@ func TestValidateVolumeMounts(t *testing.T) {
{Name: "abc-123", MountPath: "/bad", SubPath: "..baz"}, {Name: "abc-123", MountPath: "/bad", SubPath: "..baz"},
{Name: "abc", MountPath: "c:/foo/bar"}, {Name: "abc", MountPath: "c:/foo/bar"},
} }
if errs := validateVolumeMounts(successCase, volumes, field.NewPath("field")); len(errs) != 0 { if errs := ValidateVolumeMounts(successCase, volumes, field.NewPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs) t.Errorf("expected success: %v", errs)
} }
...@@ -2454,7 +2454,7 @@ func TestValidateVolumeMounts(t *testing.T) { ...@@ -2454,7 +2454,7 @@ func TestValidateVolumeMounts(t *testing.T) {
"subpath ends in ..": {{Name: "abc", MountPath: "/bar", SubPath: "./.."}}, "subpath ends in ..": {{Name: "abc", MountPath: "/bar", SubPath: "./.."}},
} }
for k, v := range errorCases { for k, v := range errorCases {
if errs := validateVolumeMounts(v, volumes, field.NewPath("field")); len(errs) == 0 { if errs := ValidateVolumeMounts(v, volumes, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %s", k) t.Errorf("expected failure for %s", k)
} }
} }
......
/*
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.
*/
// +k8s:deepcopy-gen=package,register
// +groupName=settings.k8s.io
package settings // import "k8s.io/kubernetes/pkg/apis/settings"
/*
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 install installs the settings API group, making it available as
// an option to all of the API encoding/decoding machinery.
package install
import (
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/settings"
"k8s.io/kubernetes/pkg/apis/settings/v1alpha1"
)
func init() {
Install(api.GroupFactoryRegistry, api.Registry, api.Scheme)
}
// Install registers the API group and adds types to a scheme
func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) {
if err := announced.NewGroupMetaFactory(
&announced.GroupMetaFactoryArgs{
GroupName: settings.GroupName,
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
ImportPrefix: "k8s.io/kubernetes/pkg/apis/settings",
AddInternalObjectsToScheme: settings.AddToScheme,
},
announced.VersionToSchemeFunc{
v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme,
},
).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil {
panic(err)
}
}
/*
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 settings
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// GroupName is the group name use in this package
const GroupName = "settings.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&PodPreset{},
&PodPresetList{},
)
return nil
}
/*
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 settings
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api"
)
// +genclient=true
// PodPreset is a policy resource that defines additional runtime
// requirements for a Pod.
type PodPreset struct {
metav1.TypeMeta
// +optional
metav1.ObjectMeta
// +optional
Spec PodPresetSpec
}
// PodPresetSpec is a description of a pod injection policy.
type PodPresetSpec struct {
// Selector is a label query over a set of resources, in this case pods.
// Required.
Selector metav1.LabelSelector
// Env defines the collection of EnvVar to inject into containers.
// +optional
Env []api.EnvVar
// EnvFrom defines the collection of EnvFromSource to inject into containers.
// +optional
EnvFrom []api.EnvFromSource
// Volumes defines the collection of Volume to inject into the pod.
// +optional
Volumes []api.Volume
// VolumeMounts defines the collection of VolumeMount to inject into containers.
// +optional
VolumeMounts []api.VolumeMount
}
// PodPresetList is a list of PodPreset objects.
type PodPresetList struct {
metav1.TypeMeta
// +optional
metav1.ListMeta
Items []PodPreset
}
/*
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.
*/
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/settings
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +groupName=settings.k8s.io
package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/settings/v1alpha1"
/*
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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "settings.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&PodPreset{},
&PodPresetList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
/*
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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api/v1"
)
// +genclient=true
// PodPreset is a policy resource that defines additional runtime
// requirements for a Pod.
type PodPreset struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// +optional
Spec PodPresetSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
}
// PodPresetSpec is a description of a pod injection policy.
type PodPresetSpec struct {
// Selector is a label query over a set of resources, in this case pods.
// Required.
Selector metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,1,opt,name=selector"`
// Env defines the collection of EnvVar to inject into containers.
// +optional
Env []v1.EnvVar `json:"env,omitempty" protobuf:"bytes,2,rep,name=env"`
// EnvFrom defines the collection of EnvFromSource to inject into containers.
// +optional
EnvFrom []v1.EnvFromSource `json:"envFrom,omitempty" protobuf:"bytes,3,rep,name=envFrom"`
// Volumes defines the collection of Volume to inject into the pod.
// +optional
Volumes []v1.Volume `json:"volumes,omitempty" protobuf:"bytes,4,rep,name=volumes"`
// VolumeMounts defines the collection of VolumeMount to inject into containers.
// +optional
VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty" protobuf:"bytes,5,rep,name=volumeMounts"`
}
// PodPresetList is a list of PodPreset objects.
type PodPresetList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Items is a list of schema objects.
Items []PodPreset `json:"items" protobuf:"bytes,2,rep,name=items"`
}
/*
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 validation
import (
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
apivalidation "k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/settings"
)
// ValidatePodPresetName can be used to check whether the given PodPreset name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidatePodPresetName(name string, prefix bool) []string {
// TODO: Validate that there's name for the suffix inserted by the pods.
// Currently this is just "-index". In the future we may allow a user
// specified list of suffixes and we need to validate the longest one.
return apivalidation.NameIsDNSSubdomain(name, prefix)
}
// ValidatePodPresetSpec tests if required fields in the PodPreset spec are set.
func ValidatePodPresetSpec(spec *settings.PodPresetSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&spec.Selector, fldPath.Child("selector"))...)
if spec.Env == nil && spec.EnvFrom == nil && spec.VolumeMounts == nil && spec.Volumes == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("volumes", "env", "envFrom", "volumeMounts"), "must specify at least one"))
}
volumes, vErrs := apivalidation.ValidateVolumes(spec.Volumes, fldPath.Child("volumes"))
allErrs = append(allErrs, vErrs...)
allErrs = append(allErrs, apivalidation.ValidateEnv(spec.Env, fldPath.Child("env"))...)
allErrs = append(allErrs, apivalidation.ValidateEnvFrom(spec.EnvFrom, fldPath.Child("envFrom"))...)
allErrs = append(allErrs, apivalidation.ValidateVolumeMounts(spec.VolumeMounts, volumes, fldPath.Child("volumeMounts"))...)
return allErrs
}
// ValidatePodPreset validates a PodPreset.
func ValidatePodPreset(pip *settings.PodPreset) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&pip.ObjectMeta, true, ValidatePodPresetName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidatePodPresetSpec(&pip.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidatePodPresetUpdate tests if required fields in the PodPreset are set.
func ValidatePodPresetUpdate(pip, oldPip *settings.PodPreset) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&pip.ObjectMeta, &oldPip.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidatePodPresetSpec(&pip.Spec, field.NewPath("spec"))...)
return allErrs
}
/*
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 validation
import (
"strings"
"testing"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/settings"
)
func TestValidateEmptyPodPreset(t *testing.T) {
emptyPodPreset := &settings.PodPreset{
Spec: settings.PodPresetSpec{},
}
errList := ValidatePodPreset(emptyPodPreset)
if errList == nil {
t.Fatal("empty pod preset should return an error")
}
}
func TestValidateEmptyPodPresetItems(t *testing.T) {
emptyPodPreset := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
Name: "hello",
Namespace: "sample",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
},
}
errList := ValidatePodPreset(emptyPodPreset)
if !strings.Contains(errList.ToAggregate().Error(), "must specify at least one") {
t.Fatal("empty pod preset with label selector should return an error")
}
}
func TestValidatePodPresets(t *testing.T) {
p := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
Name: "hello",
Namespace: "sample",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
Env: []api.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
EnvFrom: []api.EnvFromSource{
{
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
{
Prefix: "pre_",
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
},
},
}
errList := ValidatePodPreset(p)
if errList != nil {
if errList.ToAggregate() != nil {
t.Fatalf("errors: %#v", errList.ToAggregate().Error())
}
}
p = &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
Name: "hello",
Namespace: "sample",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
Env: []api.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
VolumeMounts: []api.VolumeMount{
{Name: "vol", MountPath: "/foo"},
},
EnvFrom: []api.EnvFromSource{
{
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
{
Prefix: "pre_",
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
},
},
}
errList = ValidatePodPreset(p)
if errList != nil {
if errList.ToAggregate() != nil {
t.Fatalf("errors: %#v", errList.ToAggregate().Error())
}
}
}
func TestValidatePodPresetsiVolumeMountError(t *testing.T) {
p := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
Name: "hello",
Namespace: "sample",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
VolumeMounts: []api.VolumeMount{
{Name: "dne", MountPath: "/foo"},
},
Env: []api.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
EnvFrom: []api.EnvFromSource{
{
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
{
Prefix: "pre_",
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
},
},
}
errList := ValidatePodPreset(p)
if !strings.Contains(errList.ToAggregate().Error(), "spec.volumeMounts[0].name: Not found") {
t.Fatal("should have returned error for volume that does not exist")
}
}
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
_ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install"
_ "k8s.io/kubernetes/pkg/apis/settings/install"
_ "k8s.io/kubernetes/pkg/apis/storage/install" _ "k8s.io/kubernetes/pkg/apis/storage/install"
) )
......
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
_ "k8s.io/kubernetes/pkg/apis/imagepolicy/install" _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install"
_ "k8s.io/kubernetes/pkg/apis/settings/install"
_ "k8s.io/kubernetes/pkg/apis/storage/install" _ "k8s.io/kubernetes/pkg/apis/storage/install"
) )
......
...@@ -45,6 +45,7 @@ import ( ...@@ -45,6 +45,7 @@ import (
policyapiv1beta1 "k8s.io/kubernetes/pkg/apis/policy/v1beta1" policyapiv1beta1 "k8s.io/kubernetes/pkg/apis/policy/v1beta1"
rbacapi "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1" rbacapi "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
rbacv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1" rbacv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1"
settingsapi "k8s.io/kubernetes/pkg/apis/settings/v1alpha1"
storageapiv1beta1 "k8s.io/kubernetes/pkg/apis/storage/v1beta1" storageapiv1beta1 "k8s.io/kubernetes/pkg/apis/storage/v1beta1"
corev1client "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1" corev1client "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
...@@ -68,6 +69,7 @@ import ( ...@@ -68,6 +69,7 @@ import (
extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest" extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest"
policyrest "k8s.io/kubernetes/pkg/registry/policy/rest" policyrest "k8s.io/kubernetes/pkg/registry/policy/rest"
rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest" rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest"
settingsrest "k8s.io/kubernetes/pkg/registry/settings/rest"
storagerest "k8s.io/kubernetes/pkg/registry/storage/rest" storagerest "k8s.io/kubernetes/pkg/registry/storage/rest"
) )
...@@ -248,6 +250,7 @@ func (c completedConfig) New() (*Master, error) { ...@@ -248,6 +250,7 @@ func (c completedConfig) New() (*Master, error) {
policyrest.RESTStorageProvider{}, policyrest.RESTStorageProvider{},
rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer}, rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer},
storagerest.RESTStorageProvider{}, storagerest.RESTStorageProvider{},
settingsrest.RESTStorageProvider{},
} }
m.InstallAPIs(c.Config.APIResourceConfigSource, c.Config.GenericConfig.RESTOptionsGetter, restStorageProviders...) m.InstallAPIs(c.Config.APIResourceConfigSource, c.Config.GenericConfig.RESTOptionsGetter, restStorageProviders...)
...@@ -371,6 +374,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { ...@@ -371,6 +374,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
policyapiv1beta1.SchemeGroupVersion, policyapiv1beta1.SchemeGroupVersion,
rbacv1beta1.SchemeGroupVersion, rbacv1beta1.SchemeGroupVersion,
rbacapi.SchemeGroupVersion, rbacapi.SchemeGroupVersion,
settingsapi.SchemeGroupVersion,
storageapiv1beta1.SchemeGroupVersion, storageapiv1beta1.SchemeGroupVersion,
certificatesapiv1beta1.SchemeGroupVersion, certificatesapiv1beta1.SchemeGroupVersion,
authorizationapiv1.SchemeGroupVersion, authorizationapiv1.SchemeGroupVersion,
......
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podpreset // import "k8s.io/kubernetes/pkg/registry/settings/podpreset"
/*
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 podpreset
import (
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/settings"
)
// Registry is an interface for things that know how to store PodPresets.
type Registry interface {
ListPodPresets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*settings.PodPresetList, error)
CreatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error
UpdatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error
GetPodPreset(ctx genericapirequest.Context, ppID string, options *metav1.GetOptions) (*settings.PodPreset, error)
DeletePodPreset(ctx genericapirequest.Context, ppID string) error
WatchPodPresets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListPodPresets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*settings.PodPresetList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*settings.PodPresetList), nil
}
func (s *storage) CreatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error {
_, err := s.Create(ctx, pp)
return err
}
func (s *storage) UpdatePodPreset(ctx genericapirequest.Context, pp *settings.PodPreset) error {
_, _, err := s.Update(ctx, pp.Name, rest.DefaultUpdatedObjectInfo(pp, api.Scheme))
return err
}
func (s *storage) WatchPodPresets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetPodPreset(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*settings.PodPreset, error) {
obj, err := s.Get(ctx, name, options)
if err != nil {
return nil, err
}
return obj.(*settings.PodPreset), nil
}
func (s *storage) DeletePodPreset(ctx genericapirequest.Context, name string) error {
_, _, err := s.Delete(ctx, name, nil)
return err
}
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package storage
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/api"
settingsapi "k8s.io/kubernetes/pkg/apis/settings"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/settings/podpreset"
)
// rest implements a RESTStorage for replication controllers against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against replication controllers.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
Copier: api.Scheme,
NewFunc: func() runtime.Object { return &settingsapi.PodPreset{} },
NewListFunc: func() runtime.Object { return &settingsapi.PodPresetList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*settingsapi.PodPreset).GetName(), nil
},
PredicateFunc: podpreset.Matcher,
QualifiedResource: settingsapi.Resource("podpresets"),
WatchCacheSize: cachesize.GetWatchCacheSizeByResource("podpresets"),
CreateStrategy: podpreset.Strategy,
UpdateStrategy: podpreset.Strategy,
DeleteStrategy: podpreset.Strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: podpreset.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podpreset
import (
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
apistorage "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/settings"
"k8s.io/kubernetes/pkg/apis/settings/validation"
)
// podPresetStrategy implements verification logic for Pod Injection Policies.
type podPresetStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating Pod Injection Policy objects.
var Strategy = podPresetStrategy{api.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns true because all Pod Injection Policies need to be within a namespace.
func (podPresetStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of a Pod Injection Policy before creation.
func (podPresetStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
pip := obj.(*settings.PodPreset)
pip.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (podPresetStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
newPodPreset := obj.(*settings.PodPreset)
oldPodPreset := old.(*settings.PodPreset)
// Update is not allowed
newPodPreset.Spec = oldPodPreset.Spec
}
// Validate validates a new PodPreset.
func (podPresetStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
pip := obj.(*settings.PodPreset)
return validation.ValidatePodPreset(pip)
}
// Canonicalize normalizes the object after validation.
func (podPresetStrategy) Canonicalize(obj runtime.Object) {}
// AllowCreateOnUpdate is false for PodPreset; this means POST is needed to create one.
func (podPresetStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (podPresetStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidatePodPreset(obj.(*settings.PodPreset))
updateErrorList := validation.ValidatePodPresetUpdate(obj.(*settings.PodPreset), old.(*settings.PodPreset))
return append(validationErrorList, updateErrorList...)
}
// AllowUnconditionalUpdate is the default update policy for Pod Injection Policy objects.
func (podPresetStrategy) AllowUnconditionalUpdate() bool {
return true
}
// SelectableFields returns a field set that represents the object.
func SelectableFields(pip *settings.PodPreset) fields.Set {
return generic.ObjectMetaFieldsSet(&pip.ObjectMeta, true)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
pip, ok := obj.(*settings.PodPreset)
if !ok {
return nil, nil, fmt.Errorf("given object is not a PodPreset.")
}
return labels.Set(pip.ObjectMeta.Labels), SelectableFields(pip), nil
}
// Matcher is the filter used by the generic etcd backend to watch events
// from etcd to clients of the apiserver only interested in specific labels/fields.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
/*
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 rest
import (
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/settings"
settingsapiv1alpha1 "k8s.io/kubernetes/pkg/apis/settings/v1alpha1"
podpresetstore "k8s.io/kubernetes/pkg/registry/settings/podpreset/storage"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(settings.GroupName, api.Registry, api.Scheme, api.ParameterCodec, api.Codecs)
if apiResourceConfigSource.AnyResourcesForVersionEnabled(settingsapiv1alpha1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[settingsapiv1alpha1.SchemeGroupVersion.Version] = p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = settingsapiv1alpha1.SchemeGroupVersion
}
return apiGroupInfo, true
}
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
version := settingsapiv1alpha1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("podpresets")) {
podPresetStorage := podpresetstore.NewREST(restOptionsGetter)
storage["podpresets"] = podPresetStorage
}
return storage
}
func (p RESTStorageProvider) GroupName() string {
return settings.GroupName
}
/*
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 admission
import (
"fmt"
"io"
"reflect"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apiserver/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/settings"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
settingslisters "k8s.io/kubernetes/pkg/client/listers/settings/internalversion"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
const (
annotationPrefix = "podpreset.admission.kubernetes.io"
pluginName = "PodPreset"
)
func init() {
admission.RegisterPlugin(pluginName, func(config io.Reader) (admission.Interface, error) {
return NewPlugin(), nil
})
}
// podPresetPlugin is an implementation of admission.Interface.
type podPresetPlugin struct {
*admission.Handler
client internalclientset.Interface
lister settingslisters.PodPresetLister
}
var _ = kubeapiserveradmission.WantsInformerFactory(&podPresetPlugin{})
var _ = kubeapiserveradmission.WantsInternalClientSet(&podPresetPlugin{})
// NewPlugin creates a new pod preset admission plugin.
func NewPlugin() *podPresetPlugin {
return &podPresetPlugin{
Handler: admission.NewHandler(admission.Create, admission.Update),
}
}
func (plugin *podPresetPlugin) Validate() error {
if plugin.client == nil {
return fmt.Errorf("%s requires a client", pluginName)
}
if plugin.lister == nil {
return fmt.Errorf("%s requires a lister", pluginName)
}
return nil
}
func (a *podPresetPlugin) SetInternalClientSet(client internalclientset.Interface) {
a.client = client
}
func (a *podPresetPlugin) SetInformerFactory(f informers.SharedInformerFactory) {
podPresetInformer := f.Settings().InternalVersion().PodPresets()
a.lister = podPresetInformer.Lister()
a.SetReadyFunc(podPresetInformer.Informer().HasSynced)
}
// Admit injects a pod with the specific fields for each pod preset it matches.
func (c *podPresetPlugin) Admit(a admission.Attributes) error {
// Ignore all calls to subresources or resources other than pods.
// Ignore all operations other than CREATE.
if len(a.GetSubresource()) != 0 || a.GetResource().GroupResource() != api.Resource("pods") || a.GetOperation() != admission.Create {
return nil
}
pod, ok := a.GetObject().(*api.Pod)
if !ok {
return errors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
}
list, err := c.lister.PodPresets(pod.GetNamespace()).List(labels.Everything())
if err != nil {
return fmt.Errorf("listing pod presets failed: %v", err)
}
// get the pod presets and iterate over them
for _, pip := range list {
selector, err := metav1.LabelSelectorAsSelector(&pip.Spec.Selector)
if err != nil {
return fmt.Errorf("listing pod presets for namespace:%s failed: %v", pod.GetNamespace(), err)
}
// check if the pod labels match the selector
if !selector.Matches(labels.Set(pod.Labels)) {
continue
}
glog.V(4).Infof("PodPreset %s matches pod %s labels", pip.GetName(), pod.GetName())
// merge in policy for Env
if pip.Spec.Env != nil {
for i, ctr := range pod.Spec.Containers {
r, err := mergeEnv(pip, ctr.Env)
if err != nil {
// add event to pod
c.addEvent(pod, pip, err.Error())
return nil
}
pod.Spec.Containers[i].Env = r
}
}
// merge in policy for EnvFrom
if pip.Spec.EnvFrom != nil {
for i, ctr := range pod.Spec.Containers {
r, err := mergeEnvFrom(pip, ctr.EnvFrom)
if err != nil {
// add event to pod
c.addEvent(pod, pip, err.Error())
return nil
}
pod.Spec.Containers[i].EnvFrom = r
}
}
// merge in policy for VolumeMounts
if pip.Spec.VolumeMounts != nil {
for i, ctr := range pod.Spec.Containers {
r, err := mergeVolumeMounts(pip, ctr.VolumeMounts)
if err != nil {
// add event to pod
c.addEvent(pod, pip, err.Error())
return nil
}
pod.Spec.Containers[i].VolumeMounts = r
}
}
// merge in policy for Volumes
if pip.Spec.Volumes != nil {
r, err := mergeVolumes(pip, pod.Spec.Volumes)
if err != nil {
// add event to pod
c.addEvent(pod, pip, err.Error())
return nil
}
pod.Spec.Volumes = r
}
glog.V(4).Infof("PodPreset %s merged with pod %s successfully", pip.GetName(), pod.GetName())
// add annotation
if pod.ObjectMeta.Annotations == nil {
pod.ObjectMeta.Annotations = map[string]string{}
}
pod.ObjectMeta.Annotations[fmt.Sprintf("%s/%s", annotationPrefix, pip.GetName())] = pip.GetResourceVersion()
}
return nil
}
func mergeEnv(pip *settings.PodPreset, original []api.EnvVar) ([]api.EnvVar, error) {
// if there were no original envvar just return the pip envvar
if original == nil {
return pip.Spec.Env, nil
}
orig := map[string]interface{}{}
for _, v := range original {
orig[v.Name] = v
}
// check for conflicts.
for _, v := range pip.Spec.Env {
found, ok := orig[v.Name]
if !ok {
// if we don't already have it append it and continue
original = append(original, v)
continue
}
// make sure they are identical or throw an error
if !reflect.DeepEqual(found, v) {
return nil, fmt.Errorf("merging env for %s has a conflict on %s: \n%#v\ndoes not match\n%#v\n in container", pip.GetName(), v.Name, v, found)
}
}
return original, nil
}
func mergeEnvFrom(pip *settings.PodPreset, original []api.EnvFromSource) ([]api.EnvFromSource, error) {
// if there were no original envfrom just return the pip envfrom
if original == nil {
return pip.Spec.EnvFrom, nil
}
return append(original, pip.Spec.EnvFrom...), nil
}
func mergeVolumeMounts(pip *settings.PodPreset, original []api.VolumeMount) ([]api.VolumeMount, error) {
// if there were no original volume mount just return the pip volume mount
if original == nil {
return pip.Spec.VolumeMounts, nil
}
// first key by name
orig := map[string]interface{}{}
for _, v := range original {
orig[v.Name] = v
}
// check for conflicts.
for _, v := range pip.Spec.VolumeMounts {
found, ok := orig[v.Name]
if !ok {
// if we don't already have it continue
continue
}
// make sure they are identical or throw an error
if !reflect.DeepEqual(found, v) {
return nil, fmt.Errorf("merging volume mounts for %s has a conflict on %s: \n%#v\ndoes not match\n%#v\n in container", pip.GetName(), v.Name, v, found)
}
}
// key by mount path
orig = map[string]interface{}{}
for _, v := range original {
orig[v.MountPath] = v
}
// check for conflicts.
for _, v := range pip.Spec.VolumeMounts {
found, ok := orig[v.MountPath]
if !ok {
// if we don't already have it append it and continue
original = append(original, v)
continue
}
// make sure they are identical or throw an error
if !reflect.DeepEqual(found, v) {
return nil, fmt.Errorf("merging volume mounts for %s has a conflict on mount path %s: \n%#v\ndoes not match\n%#v\n in container", pip.GetName(), v.MountPath, v, found)
}
}
return original, nil
}
func mergeVolumes(pip *settings.PodPreset, original []api.Volume) ([]api.Volume, error) {
// if there were no original volumes just return the pip volumes
if original == nil {
return pip.Spec.Volumes, nil
}
orig := map[string]api.Volume{}
for _, v := range original {
orig[v.Name] = v
}
// check for conflicts.
for _, v := range pip.Spec.Volumes {
found, ok := orig[v.Name]
if !ok {
// if we don't already have it append it and continue
original = append(original, v)
continue
}
if !reflect.DeepEqual(found, v) {
return nil, fmt.Errorf("merging volumes for %s has a conflict on %s: \n%#v\ndoes not match\n%#v\nin pod spec", pip.GetName(), v.Name, v, found)
}
}
return original, nil
}
func (c *podPresetPlugin) addEvent(pod *api.Pod, pip *settings.PodPreset, message string) {
ref, err := api.GetReference(api.Scheme, pod)
if err != nil {
glog.Errorf("pip %s: get reference for pod %s failed: %v", pip.GetName(), pod.GetName(), err)
return
}
e := &api.Event{
InvolvedObject: *ref,
Message: message,
Source: api.EventSource{
Component: fmt.Sprintf("pip %s", pip.GetName()),
},
Type: "Warning",
}
if _, err := c.client.Core().Events(pod.GetNamespace()).Create(e); err != nil {
glog.Errorf("pip %s: creating pod event failed: %v", pip.GetName(), err)
return
}
}
...@@ -73,6 +73,7 @@ go_library( ...@@ -73,6 +73,7 @@ go_library(
"persistent_volumes-disruptive.go", "persistent_volumes-disruptive.go",
"persistent_volumes-vsphere.go", "persistent_volumes-vsphere.go",
"pod_gc.go", "pod_gc.go",
"podpreset.go",
"pods.go", "pods.go",
"portforward.go", "portforward.go",
"pre_stop.go", "pre_stop.go",
...@@ -118,6 +119,7 @@ go_library( ...@@ -118,6 +119,7 @@ go_library(
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library", "//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/rbac/v1beta1:go_default_library", "//pkg/apis/rbac/v1beta1:go_default_library",
"//pkg/apis/settings/v1alpha1:go_default_library",
"//pkg/apis/storage/util:go_default_library", "//pkg/apis/storage/util:go_default_library",
"//pkg/apis/storage/v1beta1:go_default_library", "//pkg/apis/storage/v1beta1:go_default_library",
"//pkg/apis/storage/v1beta1/util:go_default_library", "//pkg/apis/storage/v1beta1/util:go_default_library",
......
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package e2e
import (
"reflect"
"strconv"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/kubernetes/pkg/api/v1"
settings "k8s.io/kubernetes/pkg/apis/settings/v1alpha1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = framework.KubeDescribe("PodPreset", func() {
f := framework.NewDefaultFramework("podpreset")
var podClient *framework.PodClient
BeforeEach(func() {
// only run on gce for the time being til we find an easier way to update
// the admission controllers used on the others
framework.SkipUnlessProviderIs("gce")
podClient = f.PodClient()
})
// Simplest case: all pods succeed promptly
It("should create a pod preset", func() {
By("Creating a pod preset")
pip := &settings.PodPreset{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: f.Namespace.Name,
},
Spec: settings.PodPresetSpec{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
Volumes: []v1.Volume{{Name: "vol", VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}}},
VolumeMounts: []v1.VolumeMount{
{Name: "vol", MountPath: "/foo"},
},
Env: []v1.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
},
}
_, err := createPodPreset(f.ClientSet, f.Namespace.Name, pip)
Expect(err).NotTo(HaveOccurred())
By("creating the pod")
name := "pod-preset-pod"
value := strconv.Itoa(time.Now().Nanosecond())
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: f.Namespace.Name,
Labels: map[string]string{
"name": "foo",
"time": value,
"security": "S2",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "nginx",
Image: "gcr.io/google_containers/nginx-slim:0.7",
},
},
},
}
By("setting up watch")
selector := labels.SelectorFromSet(labels.Set(map[string]string{"time": value}))
options := metav1.ListOptions{LabelSelector: selector.String()}
pods, err := podClient.List(options)
Expect(err).NotTo(HaveOccurred(), "failed to query for pod")
Expect(len(pods.Items)).To(Equal(0))
options = metav1.ListOptions{
LabelSelector: selector.String(),
ResourceVersion: pods.ListMeta.ResourceVersion,
}
w, err := podClient.Watch(options)
Expect(err).NotTo(HaveOccurred(), "failed to set up watch")
By("submitting the pod to kubernetes")
podClient.Create(pod)
By("verifying the pod is in kubernetes")
selector = labels.SelectorFromSet(labels.Set(map[string]string{"time": value}))
options = metav1.ListOptions{LabelSelector: selector.String()}
pods, err = podClient.List(options)
Expect(err).NotTo(HaveOccurred(), "failed to query for pod")
Expect(len(pods.Items)).To(Equal(1))
By("verifying pod creation was observed")
select {
case event, _ := <-w.ResultChan():
if event.Type != watch.Added {
framework.Failf("Failed to observe pod creation: %v", event)
}
case <-time.After(framework.PodStartTimeout):
Fail("Timeout while waiting for pod creation")
}
// We need to wait for the pod to be running, otherwise the deletion
// may be carried out immediately rather than gracefully.
framework.ExpectNoError(f.WaitForPodRunning(pod.Name))
By("ensuring pod is modified")
// save the running pod
pod, err = podClient.Get(pod.Name, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred(), "failed to GET scheduled pod")
// check the annotation is there
if _, ok := pod.Annotations["podpreset.admission.kubernetes.io/hello"]; !ok {
framework.Failf("Annotation not found in pod annotations: \n%v\n", pod.Annotations)
}
// verify the env is the same
if !reflect.DeepEqual(pip.Spec.Env, pod.Spec.Containers[0].Env) {
framework.Failf("env of pod container does not match the env of the pip: expected %#v, got: %#v", pip.Spec.Env, pod.Spec.Containers[0].Env)
}
})
It("should not modify the pod on conflict", func() {
By("Creating a pod preset")
pip := &settings.PodPreset{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: f.Namespace.Name,
},
Spec: settings.PodPresetSpec{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
Volumes: []v1.Volume{{Name: "vol", VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}}},
VolumeMounts: []v1.VolumeMount{
{Name: "vol", MountPath: "/foo"},
},
Env: []v1.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
},
}
_, err := createPodPreset(f.ClientSet, f.Namespace.Name, pip)
Expect(err).NotTo(HaveOccurred())
By("creating the pod")
name := "pod-preset-pod"
value := strconv.Itoa(time.Now().Nanosecond())
originalPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: f.Namespace.Name,
Labels: map[string]string{
"name": "foo",
"time": value,
"security": "S2",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "nginx",
Image: "gcr.io/google_containers/nginx-slim:0.7",
Env: []v1.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value"}},
},
},
},
}
By("setting up watch")
selector := labels.SelectorFromSet(labels.Set(map[string]string{"time": value}))
options := metav1.ListOptions{LabelSelector: selector.String()}
pods, err := podClient.List(options)
Expect(err).NotTo(HaveOccurred(), "failed to query for pod")
Expect(len(pods.Items)).To(Equal(0))
options = metav1.ListOptions{
LabelSelector: selector.String(),
ResourceVersion: pods.ListMeta.ResourceVersion,
}
w, err := podClient.Watch(options)
Expect(err).NotTo(HaveOccurred(), "failed to set up watch")
By("submitting the pod to kubernetes")
podClient.Create(originalPod)
By("verifying the pod is in kubernetes")
selector = labels.SelectorFromSet(labels.Set(map[string]string{"time": value}))
options = metav1.ListOptions{LabelSelector: selector.String()}
pods, err = podClient.List(options)
Expect(err).NotTo(HaveOccurred(), "failed to query for pod")
Expect(len(pods.Items)).To(Equal(1))
By("verifying pod creation was observed")
select {
case event, _ := <-w.ResultChan():
if event.Type != watch.Added {
framework.Failf("Failed to observe pod creation: %v", event)
}
case <-time.After(framework.PodStartTimeout):
Fail("Timeout while waiting for pod creation")
}
// We need to wait for the pod to be running, otherwise the deletion
// may be carried out immediately rather than gracefully.
framework.ExpectNoError(f.WaitForPodRunning(originalPod.Name))
By("ensuring pod is modified")
// save the running pod
pod, err := podClient.Get(originalPod.Name, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred(), "failed to GET scheduled pod")
// check the annotation is not there
if _, ok := pod.Annotations["podpreset.admission.kubernetes.io/hello"]; ok {
framework.Failf("Annotation found in pod annotations and should not be: \n%v\n", pod.Annotations)
}
// verify the env is the same
if !reflect.DeepEqual(originalPod.Spec.Containers[0].Env, pod.Spec.Containers[0].Env) {
framework.Failf("env of pod container does not match the env of the original pod: expected %#v, got: %#v", originalPod.Spec.Containers[0].Env, pod.Spec.Containers[0].Env)
}
})
})
func getPodPreset(c clientset.Interface, ns, name string) (*settings.PodPreset, error) {
return c.Settings().PodPresets(ns).Get(name, metav1.GetOptions{})
}
func createPodPreset(c clientset.Interface, ns string, job *settings.PodPreset) (*settings.PodPreset, error) {
return c.Settings().PodPresets(ns).Create(job)
}
func updatePodPreset(c clientset.Interface, ns string, job *settings.PodPreset) (*settings.PodPreset, error) {
return c.Settings().PodPresets(ns).Update(job)
}
func deletePodPreset(c clientset.Interface, ns, name string) error {
return c.Settings().PodPresets(ns).Delete(name, nil)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment