Change-Id: If2ff0d6824e661f2c90c9d0abf436e653e5b4720
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/5275
Tested-by: gerrit-photon <photon-checkins@vmware.com>
Reviewed-by: Bo Gan <ganb@vmware.com>
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 |
-From 6b4efdbb56d5d4a0521fd0612e770b17f1e8aae2 Mon Sep 17 00:00:00 2001 |
|
| 1 |
+From 5db4faec519b8f4c471e59f1083a8b6581d77bd3 Mon Sep 17 00:00:00 2001 |
|
| 2 | 2 |
From: Bo Gan <ganb@vmware.com> |
| 3 | 3 |
Date: Sun, 10 Jun 2018 02:16:47 -0700 |
| 4 |
-Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (d06c534) |
|
| 4 |
+Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (da5bd0d) |
|
| 5 | 5 |
|
| 6 | 6 |
--- |
| 7 | 7 |
api/swagger-spec/apps_v1alpha1.json | 21 + |
| ... | ... |
@@ -22,18 +22,18 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (d06c534) |
| 22 | 22 |
pkg/apis/core/validation/validation.go | 25 + |
| 23 | 23 |
pkg/apis/extensions/types.go | 1 + |
| 24 | 24 |
pkg/cloudprovider/providers/BUILD | 2 + |
| 25 |
- pkg/cloudprovider/providers/cascade/BUILD | 56 +++ |
|
| 25 |
+ pkg/cloudprovider/providers/cascade/BUILD | 56 ++ |
|
| 26 | 26 |
pkg/cloudprovider/providers/cascade/OWNERS | 3 + |
| 27 |
- pkg/cloudprovider/providers/cascade/apitypes.go | 227 +++++++++ |
|
| 28 |
- pkg/cloudprovider/providers/cascade/auth.go | 145 ++++++ |
|
| 29 |
- pkg/cloudprovider/providers/cascade/cascade.go | 218 +++++++++ |
|
| 30 |
- .../providers/cascade/cascade_disks.go | 225 +++++++++ |
|
| 31 |
- .../providers/cascade/cascade_instances.go | 91 ++++ |
|
| 27 |
+ pkg/cloudprovider/providers/cascade/apitypes.go | 227 ++++++ |
|
| 28 |
+ pkg/cloudprovider/providers/cascade/auth.go | 145 ++++ |
|
| 29 |
+ pkg/cloudprovider/providers/cascade/cascade.go | 218 ++++++ |
|
| 30 |
+ .../providers/cascade/cascade_disks.go | 225 ++++++ |
|
| 31 |
+ .../providers/cascade/cascade_instances.go | 91 +++ |
|
| 32 | 32 |
.../providers/cascade/cascade_instances_test.go | 43 ++ |
| 33 |
- .../providers/cascade/cascade_loadbalancer.go | 284 +++++++++++ |
|
| 34 |
- pkg/cloudprovider/providers/cascade/client.go | 394 +++++++++++++++ |
|
| 35 |
- pkg/cloudprovider/providers/cascade/oidcclient.go | 297 ++++++++++++ |
|
| 36 |
- pkg/cloudprovider/providers/cascade/restclient.go | 262 ++++++++++ |
|
| 33 |
+ .../providers/cascade/cascade_loadbalancer.go | 284 ++++++++ |
|
| 34 |
+ pkg/cloudprovider/providers/cascade/client.go | 394 ++++++++++ |
|
| 35 |
+ pkg/cloudprovider/providers/cascade/oidcclient.go | 297 ++++++++ |
|
| 36 |
+ pkg/cloudprovider/providers/cascade/restclient.go | 262 +++++++ |
|
| 37 | 37 |
pkg/cloudprovider/providers/cascade/tests_owed | 5 + |
| 38 | 38 |
pkg/cloudprovider/providers/cascade/utils.go | 25 + |
| 39 | 39 |
pkg/cloudprovider/providers/providers.go | 1 + |
| ... | ... |
@@ -41,16 +41,16 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (d06c534) |
| 41 | 41 |
pkg/security/podsecuritypolicy/util/util.go | 3 + |
| 42 | 42 |
pkg/volume/cascade_disk/BUILD | 43 ++ |
| 43 | 43 |
pkg/volume/cascade_disk/OWNERS | 2 + |
| 44 |
- pkg/volume/cascade_disk/attacher.go | 269 +++++++++++ |
|
| 45 |
- pkg/volume/cascade_disk/cascade_disk.go | 391 +++++++++++++++ |
|
| 46 |
- pkg/volume/cascade_disk/cascade_util.go | 107 ++++ |
|
| 47 |
- .../admission/persistentvolume/label/admission.go | 54 +++ |
|
| 48 |
- plugin/pkg/admission/vke/BUILD | 58 +++ |
|
| 49 |
- plugin/pkg/admission/vke/admission.go | 349 +++++++++++++ |
|
| 50 |
- plugin/pkg/admission/vke/admission_test.go | 538 +++++++++++++++++++++ |
|
| 51 |
- staging/src/k8s.io/api/core/v1/generated.pb.go | 310 +++++++++++- |
|
| 44 |
+ pkg/volume/cascade_disk/attacher.go | 265 +++++++ |
|
| 45 |
+ pkg/volume/cascade_disk/cascade_disk.go | 391 ++++++++++ |
|
| 46 |
+ pkg/volume/cascade_disk/cascade_util.go | 152 ++++ |
|
| 47 |
+ .../admission/persistentvolume/label/admission.go | 54 ++ |
|
| 48 |
+ plugin/pkg/admission/vke/BUILD | 60 ++ |
|
| 49 |
+ plugin/pkg/admission/vke/admission.go | 499 +++++++++++++ |
|
| 50 |
+ plugin/pkg/admission/vke/admission_test.go | 809 +++++++++++++++++++++ |
|
| 51 |
+ staging/src/k8s.io/api/core/v1/generated.pb.go | 310 +++++++- |
|
| 52 | 52 |
staging/src/k8s.io/api/core/v1/types.go | 26 +- |
| 53 |
- 46 files changed, 4653 insertions(+), 29 deletions(-) |
|
| 53 |
+ 46 files changed, 5117 insertions(+), 29 deletions(-) |
|
| 54 | 54 |
create mode 100644 pkg/cloudprovider/providers/cascade/BUILD |
| 55 | 55 |
create mode 100644 pkg/cloudprovider/providers/cascade/OWNERS |
| 56 | 56 |
create mode 100644 pkg/cloudprovider/providers/cascade/apitypes.go |
| ... | ... |
@@ -3145,10 +3145,10 @@ index 0000000..c3a4ed7 |
| 3145 | 3145 |
+- ashokc |
| 3146 | 3146 |
diff --git a/pkg/volume/cascade_disk/attacher.go b/pkg/volume/cascade_disk/attacher.go |
| 3147 | 3147 |
new file mode 100644 |
| 3148 |
-index 0000000..607fcb5 |
|
| 3148 |
+index 0000000..66b5836 |
|
| 3149 | 3149 |
--- /dev/null |
| 3150 | 3150 |
+++ b/pkg/volume/cascade_disk/attacher.go |
| 3151 |
-@@ -0,0 +1,269 @@ |
|
| 3151 |
+@@ -0,0 +1,265 @@ |
|
| 3152 | 3152 |
+package cascade_disk |
| 3153 | 3153 |
+ |
| 3154 | 3154 |
+import ( |
| ... | ... |
@@ -3165,7 +3165,6 @@ index 0000000..607fcb5 |
| 3165 | 3165 |
+ "k8s.io/kubernetes/pkg/volume" |
| 3166 | 3166 |
+ volumeutil "k8s.io/kubernetes/pkg/volume/util" |
| 3167 | 3167 |
+ "k8s.io/kubernetes/pkg/volume/util/volumehelper" |
| 3168 |
-+ "strings" |
|
| 3169 | 3168 |
+) |
| 3170 | 3169 |
+ |
| 3171 | 3170 |
+type cascadeDiskAttacher struct {
|
| ... | ... |
@@ -3207,10 +3206,6 @@ index 0000000..607fcb5 |
| 3207 | 3207 |
+ glog.Errorf("Error attaching volume %q to node %q: %+v", volumeSource.DiskID, nodeName, err)
|
| 3208 | 3208 |
+ return "", err |
| 3209 | 3209 |
+ } |
| 3210 |
-+ |
|
| 3211 |
-+ // Cacsade uses device names of the format /dev/sdX, but newer Linux Kernels mount them under /dev/xvdX |
|
| 3212 |
-+ // (source: AWS console). So we have to rename the first occurrence of sd to xvd. |
|
| 3213 |
-+ devicePath = strings.Replace(devicePath, "sd", "xvd", 1) |
|
| 3214 | 3210 |
+ return devicePath, nil |
| 3215 | 3211 |
+} |
| 3216 | 3212 |
+ |
| ... | ... |
@@ -3273,15 +3268,16 @@ index 0000000..607fcb5 |
| 3273 | 3273 |
+ select {
|
| 3274 | 3274 |
+ case <-ticker.C: |
| 3275 | 3275 |
+ glog.V(4).Infof("Checking disk %s is attached", volumeSource.DiskID)
|
| 3276 |
++ devicePath := getDiskByIdPath(devicePath) |
|
| 3276 | 3277 |
+ checkPath, err := verifyDevicePath(devicePath) |
| 3277 | 3278 |
+ if err != nil {
|
| 3278 | 3279 |
+ // Log error, if any, and continue checking periodically. See issue #11321 |
| 3279 |
-+ glog.Warningf("Cascade attacher: WaitForAttach with devicePath %s Checking PD %s Error verify "+
|
|
| 3280 |
++ glog.Warningf("VKE attacher: WaitForAttach with devicePath %s Checking PD %s Error verify "+
|
|
| 3280 | 3281 |
+ "path", devicePath, volumeSource.DiskID) |
| 3281 | 3282 |
+ } else if checkPath != "" {
|
| 3282 | 3283 |
+ // A device path has successfully been created for the disk |
| 3283 | 3284 |
+ glog.V(4).Infof("Successfully found attached disk %s.", volumeSource.DiskID)
|
| 3284 |
-+ return devicePath, nil |
|
| 3285 |
++ return checkPath, nil |
|
| 3285 | 3286 |
+ } |
| 3286 | 3287 |
+ case <-timer.C: |
| 3287 | 3288 |
+ return "", fmt.Errorf("Could not find attached disk %s. Timeout waiting for mount paths to be "+
|
| ... | ... |
@@ -3818,14 +3814,16 @@ index 0000000..a25f224 |
| 3818 | 3818 |
\ No newline at end of file |
| 3819 | 3819 |
diff --git a/pkg/volume/cascade_disk/cascade_util.go b/pkg/volume/cascade_disk/cascade_util.go |
| 3820 | 3820 |
new file mode 100644 |
| 3821 |
-index 0000000..3dcef3d |
|
| 3821 |
+index 0000000..cbcc115 |
|
| 3822 | 3822 |
--- /dev/null |
| 3823 | 3823 |
+++ b/pkg/volume/cascade_disk/cascade_util.go |
| 3824 |
-@@ -0,0 +1,107 @@ |
|
| 3824 |
+@@ -0,0 +1,152 @@ |
|
| 3825 | 3825 |
+package cascade_disk |
| 3826 | 3826 |
+ |
| 3827 | 3827 |
+import ( |
| 3828 | 3828 |
+ "fmt" |
| 3829 |
++ "os" |
|
| 3830 |
++ "path/filepath" |
|
| 3829 | 3831 |
+ "strings" |
| 3830 | 3832 |
+ "time" |
| 3831 | 3833 |
+ |
| ... | ... |
@@ -3854,6 +3852,18 @@ index 0000000..3dcef3d |
| 3854 | 3854 |
+ return "", nil |
| 3855 | 3855 |
+} |
| 3856 | 3856 |
+ |
| 3857 |
++// Returns path for given VKE disk mount |
|
| 3858 |
++func getDiskByIdPath(devicePath string) string {
|
|
| 3859 |
++ nvmePath, err := findNvmeVolume(devicePath) |
|
| 3860 |
++ if err != nil {
|
|
| 3861 |
++ glog.Warningf("error looking for nvme volume %q: %v", devicePath, err)
|
|
| 3862 |
++ } else if nvmePath != "" {
|
|
| 3863 |
++ devicePath = nvmePath |
|
| 3864 |
++ } |
|
| 3865 |
++ |
|
| 3866 |
++ return devicePath |
|
| 3867 |
++} |
|
| 3868 |
++ |
|
| 3857 | 3869 |
+// CreateVolume creates a Cascade persistent disk. |
| 3858 | 3870 |
+func (util *CascadeDiskUtil) CreateVolume(p *cascadeDiskProvisioner) (diskID string, capacityGB int, fstype string, |
| 3859 | 3871 |
+ err error) {
|
| ... | ... |
@@ -3929,6 +3939,37 @@ index 0000000..3dcef3d |
| 3929 | 3929 |
+ } |
| 3930 | 3930 |
+ return cc, nil |
| 3931 | 3931 |
+} |
| 3932 |
++ |
|
| 3933 |
++// findNvmeVolume looks for the nvme volume with the specified name |
|
| 3934 |
++// It follows the symlink (if it exists) and returns the absolute path to the device |
|
| 3935 |
++func findNvmeVolume(findName string) (device string, err error) {
|
|
| 3936 |
++ stat, err := os.Lstat(findName) |
|
| 3937 |
++ if err != nil {
|
|
| 3938 |
++ if os.IsNotExist(err) {
|
|
| 3939 |
++ glog.V(6).Infof("nvme path not found %q", findName)
|
|
| 3940 |
++ return "", nil |
|
| 3941 |
++ } |
|
| 3942 |
++ return "", fmt.Errorf("error getting stat of %q: %v", findName, err)
|
|
| 3943 |
++ } |
|
| 3944 |
++ |
|
| 3945 |
++ if stat.Mode()&os.ModeSymlink != os.ModeSymlink {
|
|
| 3946 |
++ glog.Warningf("nvme file %q found, but was not a symlink", findName)
|
|
| 3947 |
++ return "", nil |
|
| 3948 |
++ } |
|
| 3949 |
++ |
|
| 3950 |
++ // Find the target, resolving to an absolute path |
|
| 3951 |
++ // For example, /dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_vol0fab1d5e3f72a5e23 -> ../../nvme2n1 |
|
| 3952 |
++ resolved, err := filepath.EvalSymlinks(findName) |
|
| 3953 |
++ if err != nil {
|
|
| 3954 |
++ return "", fmt.Errorf("error reading target of symlink %q: %v", findName, err)
|
|
| 3955 |
++ } |
|
| 3956 |
++ |
|
| 3957 |
++ if !strings.HasPrefix(resolved, "/dev") {
|
|
| 3958 |
++ return "", fmt.Errorf("resolved symlink for %q was unexpected: %q", findName, resolved)
|
|
| 3959 |
++ } |
|
| 3960 |
++ |
|
| 3961 |
++ return resolved, nil |
|
| 3962 |
++} |
|
| 3932 | 3963 |
diff --git a/plugin/pkg/admission/persistentvolume/label/admission.go b/plugin/pkg/admission/persistentvolume/label/admission.go |
| 3933 | 3964 |
index 86e1921..bf2912b 100644 |
| 3934 | 3965 |
--- a/plugin/pkg/admission/persistentvolume/label/admission.go |
| ... | ... |
@@ -4014,10 +4055,10 @@ index 86e1921..bf2912b 100644 |
| 4014 | 4014 |
+} |
| 4015 | 4015 |
diff --git a/plugin/pkg/admission/vke/BUILD b/plugin/pkg/admission/vke/BUILD |
| 4016 | 4016 |
new file mode 100644 |
| 4017 |
-index 0000000..b0a6026 |
|
| 4017 |
+index 0000000..d0bb7c7 |
|
| 4018 | 4018 |
--- /dev/null |
| 4019 | 4019 |
+++ b/plugin/pkg/admission/vke/BUILD |
| 4020 |
-@@ -0,0 +1,58 @@ |
|
| 4020 |
+@@ -0,0 +1,60 @@ |
|
| 4021 | 4021 |
+package(default_visibility = ["//visibility:public"]) |
| 4022 | 4022 |
+ |
| 4023 | 4023 |
+load( |
| ... | ... |
@@ -4032,10 +4073,12 @@ index 0000000..b0a6026 |
| 4032 | 4032 |
+ deps = [ |
| 4033 | 4033 |
+ "//pkg/apis/core:go_default_library", |
| 4034 | 4034 |
+ "//pkg/apis/extensions:go_default_library", |
| 4035 |
++ "//pkg/apis/extensions/v1beta1:go_default_library", |
|
| 4035 | 4036 |
+ "//pkg/apis/rbac:go_default_library", |
| 4036 | 4037 |
+ "//pkg/registry/rbac:go_default_library", |
| 4037 | 4038 |
+ "//pkg/security/podsecuritypolicy:go_default_library", |
| 4038 | 4039 |
+ "//vendor/github.com/golang/glog:go_default_library", |
| 4040 |
++ "//vendor/k8s.io/api/extensions/v1beta1:go_default_library" |
|
| 4039 | 4041 |
+ "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", |
| 4040 | 4042 |
+ "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", |
| 4041 | 4043 |
+ "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", |
| ... | ... |
@@ -4079,18 +4122,20 @@ index 0000000..b0a6026 |
| 4079 | 4079 |
\ No newline at end of file |
| 4080 | 4080 |
diff --git a/plugin/pkg/admission/vke/admission.go b/plugin/pkg/admission/vke/admission.go |
| 4081 | 4081 |
new file mode 100644 |
| 4082 |
-index 0000000..6325ca0 |
|
| 4082 |
+index 0000000..c73fc1b |
|
| 4083 | 4083 |
--- /dev/null |
| 4084 | 4084 |
+++ b/plugin/pkg/admission/vke/admission.go |
| 4085 |
-@@ -0,0 +1,349 @@ |
|
| 4085 |
+@@ -0,0 +1,499 @@ |
|
| 4086 | 4086 |
+package vke |
| 4087 | 4087 |
+ |
| 4088 | 4088 |
+import ( |
| 4089 | 4089 |
+ "fmt" |
| 4090 | 4090 |
+ "io" |
| 4091 |
++ "os" |
|
| 4091 | 4092 |
+ "strings" |
| 4092 | 4093 |
+ |
| 4093 | 4094 |
+ "github.com/golang/glog" |
| 4095 |
++ "k8s.io/api/extensions/v1beta1" |
|
| 4094 | 4096 |
+ apiequality "k8s.io/apimachinery/pkg/api/equality" |
| 4095 | 4097 |
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 4096 | 4098 |
+ "k8s.io/apimachinery/pkg/util/validation/field" |
| ... | ... |
@@ -4098,6 +4143,7 @@ index 0000000..6325ca0 |
| 4098 | 4098 |
+ "k8s.io/apiserver/pkg/admission" |
| 4099 | 4099 |
+ api "k8s.io/kubernetes/pkg/apis/core" |
| 4100 | 4100 |
+ "k8s.io/kubernetes/pkg/apis/extensions" |
| 4101 |
++ policybeta "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" |
|
| 4101 | 4102 |
+ "k8s.io/kubernetes/pkg/apis/rbac" |
| 4102 | 4103 |
+ rbacregistry "k8s.io/kubernetes/pkg/registry/rbac" |
| 4103 | 4104 |
+ "k8s.io/kubernetes/pkg/security/podsecuritypolicy" |
| ... | ... |
@@ -4113,6 +4159,10 @@ index 0000000..6325ca0 |
| 4113 | 4113 |
+ reservedPrefix = "vke" |
| 4114 | 4114 |
+ kubeletGroup = "system:nodes" |
| 4115 | 4115 |
+ kubeProxyGroup = "vke:kube-proxies" |
| 4116 |
++ reservedTolerationKey = "Dedicated" |
|
| 4117 |
++ reservedTolerationValue = "Master" |
|
| 4118 |
++ masterNodePrefix = "master" |
|
| 4119 |
++ etcSslCerts = "/etc/ssl/certs" |
|
| 4116 | 4120 |
+) |
| 4117 | 4121 |
+ |
| 4118 | 4122 |
+// Register registers a plugin. |
| ... | ... |
@@ -4132,7 +4182,8 @@ index 0000000..6325ca0 |
| 4132 | 4132 |
+ |
| 4133 | 4133 |
+// vmwareAdmissionControllerConfig holds config data for VMwareAdmissionController. |
| 4134 | 4134 |
+type vmwareAdmissionControllerConfig struct {
|
| 4135 |
-+ PrivilegedGroup string `yaml:"privilegedGroup"` |
|
| 4135 |
++ PrivilegedGroup string `yaml:"privilegedGroup"` |
|
| 4136 |
++ PodSecurityPolicyFile string `yaml:"podSecurityPolicyFile"` |
|
| 4136 | 4137 |
+} |
| 4137 | 4138 |
+ |
| 4138 | 4139 |
+// AdmissionConfig holds config data for admission controllers. |
| ... | ... |
@@ -4161,7 +4212,7 @@ index 0000000..6325ca0 |
| 4161 | 4161 |
+ case api.Resource("pods"):
|
| 4162 | 4162 |
+ err = validatePods(vac, a) |
| 4163 | 4163 |
+ case api.Resource("nodes"):
|
| 4164 |
-+ err = admission.NewForbidden(a, fmt.Errorf("%s validation failed: cannot modify nodes", PluginName))
|
|
| 4164 |
++ err = validateNodes(a) |
|
| 4165 | 4165 |
+ case rbac.Resource("clusterroles"):
|
| 4166 | 4166 |
+ err = validateClusterRoles(a) |
| 4167 | 4167 |
+ case rbac.Resource("clusterrolebindings"):
|
| ... | ... |
@@ -4192,8 +4243,14 @@ index 0000000..6325ca0 |
| 4192 | 4192 |
+ return nil, err |
| 4193 | 4193 |
+ } |
| 4194 | 4194 |
+ |
| 4195 |
++ // Load PSP from file. If it fails, use default. |
|
| 4196 |
++ psp := getPSPFromFile(config.VMwareAdmissionController.PodSecurityPolicyFile) |
|
| 4197 |
++ if psp == nil {
|
|
| 4198 |
++ psp = getDefaultPSP() |
|
| 4199 |
++ } |
|
| 4200 |
++ |
|
| 4195 | 4201 |
+ return &vmwareAdmissionController{
|
| 4196 |
-+ psp: getDefaultPSP(), |
|
| 4202 |
++ psp: psp, |
|
| 4197 | 4203 |
+ strategyFactory: podsecuritypolicy.NewSimpleStrategyFactory(), |
| 4198 | 4204 |
+ privilegedGroup: config.VMwareAdmissionController.PrivilegedGroup, |
| 4199 | 4205 |
+ }, nil |
| ... | ... |
@@ -4221,6 +4278,14 @@ index 0000000..6325ca0 |
| 4221 | 4221 |
+ "configMap", |
| 4222 | 4222 |
+ "persistentVolumeClaim", |
| 4223 | 4223 |
+ "projected", |
| 4224 |
++ "hostPath", |
|
| 4225 |
++ }, |
|
| 4226 |
++ // We allow /etc/ssl/certs to be mounted in read only mode as a hack to allow Wavefront pods to be deployed. |
|
| 4227 |
++ // TODO(ashokc): Once we have support for users to create pods using privileged mode and host path, remove this. |
|
| 4228 |
++ AllowedHostPaths: []extensions.AllowedHostPath{
|
|
| 4229 |
++ {
|
|
| 4230 |
++ etcSslCerts, |
|
| 4231 |
++ }, |
|
| 4224 | 4232 |
+ }, |
| 4225 | 4233 |
+ FSGroup: extensions.FSGroupStrategyOptions{
|
| 4226 | 4234 |
+ Rule: extensions.FSGroupStrategyRunAsAny, |
| ... | ... |
@@ -4238,6 +4303,39 @@ index 0000000..6325ca0 |
| 4238 | 4238 |
+ } |
| 4239 | 4239 |
+} |
| 4240 | 4240 |
+ |
| 4241 |
++func getPSPFromFile(pspFile string) *extensions.PodSecurityPolicy {
|
|
| 4242 |
++ pspBeta := v1beta1.PodSecurityPolicy{}
|
|
| 4243 |
++ pspExtensions := extensions.PodSecurityPolicy{}
|
|
| 4244 |
++ |
|
| 4245 |
++ if pspFile == "" {
|
|
| 4246 |
++ glog.V(2).Infof("%s: PSP file not specified, using default PSP", PluginName)
|
|
| 4247 |
++ return nil |
|
| 4248 |
++ } |
|
| 4249 |
++ |
|
| 4250 |
++ pspConfig, err := os.Open(pspFile) |
|
| 4251 |
++ if err != nil {
|
|
| 4252 |
++ glog.V(2).Infof("%s: cannot open PSP file, using default PSP: %v", PluginName, err)
|
|
| 4253 |
++ return nil |
|
| 4254 |
++ } |
|
| 4255 |
++ |
|
| 4256 |
++ // We load the PSP that we read from file into pspBeta because this is the struct to which we can decode yaml to. |
|
| 4257 |
++ d := yaml.NewYAMLOrJSONDecoder(pspConfig, 4096) |
|
| 4258 |
++ err = d.Decode(&pspBeta) |
|
| 4259 |
++ if err != nil {
|
|
| 4260 |
++ glog.V(2).Infof("%s: cannot decode PSP file, using default PSP: %v", PluginName, err)
|
|
| 4261 |
++ return nil |
|
| 4262 |
++ } |
|
| 4263 |
++ |
|
| 4264 |
++ // We convert pspBeta object into pspExtensions object because this is the one that pod validation uses. |
|
| 4265 |
++ err = policybeta.Convert_v1beta1_PodSecurityPolicy_To_extensions_PodSecurityPolicy(&pspBeta, &pspExtensions, nil) |
|
| 4266 |
++ if err != nil {
|
|
| 4267 |
++ glog.V(2).Infof("%s: cannot convert v1beta1.PSP to extensions.PSP, using default PSP: %v", PluginName, err)
|
|
| 4268 |
++ return nil |
|
| 4269 |
++ } |
|
| 4270 |
++ |
|
| 4271 |
++ return &pspExtensions |
|
| 4272 |
++} |
|
| 4273 |
++ |
|
| 4241 | 4274 |
+func isPrivilegedUser(vac *vmwareAdmissionController, a admission.Attributes) bool {
|
| 4242 | 4275 |
+ // We need to allow the service accounts inside the privileged namespace to be able to access pods and nodes. |
| 4243 | 4276 |
+ // Node-monitor agent, Photon OS update controller and Cluster autoscaler depend on this. |
| ... | ... |
@@ -4305,6 +4403,22 @@ index 0000000..6325ca0 |
| 4305 | 4305 |
+ return false |
| 4306 | 4306 |
+} |
| 4307 | 4307 |
+ |
| 4308 |
++func validateNodes(a admission.Attributes) error {
|
|
| 4309 |
++ // If the operation is Delete, fail. Deleting a node is not something that is useful to the user. Also, by deleting |
|
| 4310 |
++ // a node, they can potentially make their cluster useless. |
|
| 4311 |
++ if a.GetOperation() == admission.Delete {
|
|
| 4312 |
++ return admission.NewForbidden(a, fmt.Errorf("%s validation failed: cannot delete nodes", PluginName))
|
|
| 4313 |
++ } |
|
| 4314 |
++ |
|
| 4315 |
++ // If the operation is on a master node, fail. We do not want to allow the users to modify labels and taints on the |
|
| 4316 |
++ // master node because it can compromise the security of the cluster. |
|
| 4317 |
++ if strings.HasPrefix(a.GetName(), masterNodePrefix) {
|
|
| 4318 |
++ return admission.NewForbidden(a, fmt.Errorf("%s validation failed: cannot modify master nodes", PluginName))
|
|
| 4319 |
++ } |
|
| 4320 |
++ |
|
| 4321 |
++ return nil |
|
| 4322 |
++} |
|
| 4323 |
++ |
|
| 4308 | 4324 |
+func validateClusterRoles(a admission.Attributes) error {
|
| 4309 | 4325 |
+ // If the name in the request is not empty and has the reserved prefix, then fail. We will hit this during delete |
| 4310 | 4326 |
+ // and update operations on the cluster roles. If it does not have the reserved prefix, allow it. If the name is |
| ... | ... |
@@ -4393,6 +4507,12 @@ index 0000000..6325ca0 |
| 4393 | 4393 |
+ // Validate the pod. |
| 4394 | 4394 |
+ errs = append(errs, provider.ValidatePodSecurityContext(pod, field.NewPath("spec", "securityContext"))...)
|
| 4395 | 4395 |
+ |
| 4396 |
++ // Validate the pod's tolerations. |
|
| 4397 |
++ fieldErr := validatePodToleration(pod) |
|
| 4398 |
++ if fieldErr != nil {
|
|
| 4399 |
++ errs = append(errs, fieldErr) |
|
| 4400 |
++ } |
|
| 4401 |
++ |
|
| 4396 | 4402 |
+ // Validate the initContainers that are part of the pod. |
| 4397 | 4403 |
+ for i := range pod.Spec.InitContainers {
|
| 4398 | 4404 |
+ pod.Spec.InitContainers[i].SecurityContext, _, err = provider.CreateContainerSecurityContext(pod, &pod.Spec.InitContainers[i]) |
| ... | ... |
@@ -4417,6 +4537,12 @@ index 0000000..6325ca0 |
| 4417 | 4417 |
+ field.NewPath("spec", "containers").Index(i).Child("securityContext"))...)
|
| 4418 | 4418 |
+ } |
| 4419 | 4419 |
+ |
| 4420 |
++ // Validate that /etc/ssl/certs if mounted using hostPath volume mount is readOnly. |
|
| 4421 |
++ fieldErr = validateEtcSslCertsHostPath(pod) |
|
| 4422 |
++ if fieldErr != nil {
|
|
| 4423 |
++ errs = append(errs, fieldErr) |
|
| 4424 |
++ } |
|
| 4425 |
++ |
|
| 4420 | 4426 |
+ if len(errs) > 0 {
|
| 4421 | 4427 |
+ return admission.NewForbidden(a, |
| 4422 | 4428 |
+ fmt.Errorf("%s validation failed: %v", PluginName, errs))
|
| ... | ... |
@@ -4425,6 +4551,73 @@ index 0000000..6325ca0 |
| 4425 | 4425 |
+ return nil |
| 4426 | 4426 |
+} |
| 4427 | 4427 |
+ |
| 4428 |
++func validatePodToleration(pod *api.Pod) *field.Error {
|
|
| 4429 |
++ // Master nodes are tainted with "Dedicated=Master:NoSchedule". Only vke-system pods are allowed to tolerate |
|
| 4430 |
++ // this taint and to run on master nodes. A user's pod will be rejected if its spec has toleration for this taint. |
|
| 4431 |
++ for _, t := range pod.Spec.Tolerations {
|
|
| 4432 |
++ reject := false |
|
| 4433 |
++ |
|
| 4434 |
++ if t.Key == reservedTolerationKey && t.Value == reservedTolerationValue {
|
|
| 4435 |
++ // Reject pod that has the reserved toleration "Dedicated=Master" |
|
| 4436 |
++ reject = true |
|
| 4437 |
++ } else if t.Operator == api.TolerationOpExists && (t.Key == reservedTolerationKey || t.Key == "") {
|
|
| 4438 |
++ // Reject pod that has wildcard toleration matching the reserved toleration |
|
| 4439 |
++ reject = true |
|
| 4440 |
++ } |
|
| 4441 |
++ |
|
| 4442 |
++ if reject {
|
|
| 4443 |
++ return field.Invalid(field.NewPath("spec", "toleration"), fmt.Sprintf("%+v", t),
|
|
| 4444 |
++ fmt.Sprintf("%s validation failed: should not tolerate master node taint", PluginName))
|
|
| 4445 |
++ } |
|
| 4446 |
++ } |
|
| 4447 |
++ return nil |
|
| 4448 |
++} |
|
| 4449 |
++ |
|
| 4450 |
++// Validate that /etc/ssl/certs if mounted using hostPath volume mount is readOnly. If not, fail. |
|
| 4451 |
++// This is a hack that is needed to get Wavefront pods to work. |
|
| 4452 |
++// TODO(ashokc): Once we have support for users to create pods using privileged mode and host path, remove this. |
|
| 4453 |
++func validateEtcSslCertsHostPath(pod *api.Pod) *field.Error {
|
|
| 4454 |
++ // Get volumes which mount /etc/ssl/certs and put them in a map. |
|
| 4455 |
++ volumes := map[string]struct{}{}
|
|
| 4456 |
++ for _, vol := range pod.Spec.Volumes {
|
|
| 4457 |
++ if vol.HostPath != nil && strings.HasPrefix(vol.HostPath.Path, etcSslCerts) {
|
|
| 4458 |
++ volumes[vol.Name] = struct{}{}
|
|
| 4459 |
++ } |
|
| 4460 |
++ } |
|
| 4461 |
++ |
|
| 4462 |
++ // For every initContainer, get all volumeMounts and verify if it matches any of the volumes in the volumes map. |
|
| 4463 |
++ // If yes, then check if they are read only. If not, return an error. |
|
| 4464 |
++ err := checkVolumeReadOnly(pod.Spec.InitContainers, volumes, "initContainers") |
|
| 4465 |
++ if err != nil {
|
|
| 4466 |
++ return err |
|
| 4467 |
++ } |
|
| 4468 |
++ |
|
| 4469 |
++ // For every container, get all volumeMounts and verify if it matches any of the volumes in the volumes map. |
|
| 4470 |
++ // If yes, then check if they are read only. If not, return an error. |
|
| 4471 |
++ err = checkVolumeReadOnly(pod.Spec.Containers, volumes, "containers") |
|
| 4472 |
++ if err != nil {
|
|
| 4473 |
++ return err |
|
| 4474 |
++ } |
|
| 4475 |
++ |
|
| 4476 |
++ return nil |
|
| 4477 |
++} |
|
| 4478 |
++ |
|
| 4479 |
++// Checks if the container has a volumeMount belonging to the volumes map. If yes, it has to be read only. If not, |
|
| 4480 |
++// return error. |
|
| 4481 |
++func checkVolumeReadOnly(containers []api.Container, volumes map[string]struct{}, containerType string) *field.Error {
|
|
| 4482 |
++ for i, container := range containers {
|
|
| 4483 |
++ for _, vol := range container.VolumeMounts {
|
|
| 4484 |
++ if _, ok := volumes[vol.Name]; ok {
|
|
| 4485 |
++ if !vol.ReadOnly {
|
|
| 4486 |
++ return field.Invalid(field.NewPath("spec", containerType).Index(i).Child("volumeMounts"),
|
|
| 4487 |
++ fmt.Sprintf("%+v", vol), fmt.Sprintf("%s has to be mount as readOnly", etcSslCerts))
|
|
| 4488 |
++ } |
|
| 4489 |
++ } |
|
| 4490 |
++ } |
|
| 4491 |
++ } |
|
| 4492 |
++ return nil |
|
| 4493 |
++} |
|
| 4494 |
++ |
|
| 4428 | 4495 |
+func checkReservedPrefix(resourceName string, a admission.Attributes) error {
|
| 4429 | 4496 |
+ if strings.HasPrefix(resourceName, reservedPrefix) {
|
| 4430 | 4497 |
+ return admission.NewForbidden(a, |
| ... | ... |
@@ -4434,14 +4627,15 @@ index 0000000..6325ca0 |
| 4434 | 4434 |
+} |
| 4435 | 4435 |
diff --git a/plugin/pkg/admission/vke/admission_test.go b/plugin/pkg/admission/vke/admission_test.go |
| 4436 | 4436 |
new file mode 100644 |
| 4437 |
-index 0000000..596b7d4 |
|
| 4437 |
+index 0000000..52c88cc |
|
| 4438 | 4438 |
--- /dev/null |
| 4439 | 4439 |
+++ b/plugin/pkg/admission/vke/admission_test.go |
| 4440 |
-@@ -0,0 +1,538 @@ |
|
| 4440 |
+@@ -0,0 +1,809 @@ |
|
| 4441 | 4441 |
+package vke |
| 4442 | 4442 |
+ |
| 4443 | 4443 |
+import ( |
| 4444 | 4444 |
+ "fmt" |
| 4445 |
++ "os" |
|
| 4445 | 4446 |
+ "strings" |
| 4446 | 4447 |
+ "testing" |
| 4447 | 4448 |
+ |
| ... | ... |
@@ -4457,6 +4651,38 @@ index 0000000..596b7d4 |
| 4457 | 4457 |
+ defaultConfigFileFormat = ` |
| 4458 | 4458 |
+vmwareAdmissionController: |
| 4459 | 4459 |
+ privilegedGroup: %s |
| 4460 |
++ podSecurityPolicyFile: %s |
|
| 4461 |
++` |
|
| 4462 |
++ pspFileName = "/tmp/psp.yaml" |
|
| 4463 |
++ pspConfigFile = ` |
|
| 4464 |
++apiVersion: extensions/v1beta1 |
|
| 4465 |
++kind: PodSecurityPolicy |
|
| 4466 |
++metadata: |
|
| 4467 |
++ name: vmware-pod-security-policy-restricted |
|
| 4468 |
++spec: |
|
| 4469 |
++ privileged: true |
|
| 4470 |
++ fsGroup: |
|
| 4471 |
++ rule: RunAsAny |
|
| 4472 |
++ runAsUser: |
|
| 4473 |
++ rule: RunAsAny |
|
| 4474 |
++ seLinux: |
|
| 4475 |
++ rule: RunAsAny |
|
| 4476 |
++ supplementalGroups: |
|
| 4477 |
++ rule: RunAsAny |
|
| 4478 |
++ volumes: |
|
| 4479 |
++ - 'emptyDir' |
|
| 4480 |
++ - 'secret' |
|
| 4481 |
++ - 'downwardAPI' |
|
| 4482 |
++ - 'configMap' |
|
| 4483 |
++ - 'persistentVolumeClaim' |
|
| 4484 |
++ - 'projected' |
|
| 4485 |
++ - 'hostPath' |
|
| 4486 |
++ hostPID: false |
|
| 4487 |
++ hostIPC: false |
|
| 4488 |
++ hostNetwork: true |
|
| 4489 |
++ hostPorts: |
|
| 4490 |
++ - min: 1 |
|
| 4491 |
++ max: 65536 |
|
| 4460 | 4492 |
+` |
| 4461 | 4493 |
+) |
| 4462 | 4494 |
+ |
| ... | ... |
@@ -4524,7 +4750,19 @@ index 0000000..596b7d4 |
| 4524 | 4524 |
+ }, |
| 4525 | 4525 |
+ "create pod with HostVolume denied": {
|
| 4526 | 4526 |
+ operation: kadmission.Create, |
| 4527 |
-+ pod: newTestPodBuilder().withHostVolume().build(), |
|
| 4527 |
++ pod: newTestPodBuilder().withHostVolume("/", false).build(),
|
|
| 4528 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4529 |
++ shouldPassValidate: false, |
|
| 4530 |
++ }, |
|
| 4531 |
++ "create pod with HostVolume /etc/ssl/certs in read-only mode allowed": {
|
|
| 4532 |
++ operation: kadmission.Create, |
|
| 4533 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", true).build(),
|
|
| 4534 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4535 |
++ shouldPassValidate: true, |
|
| 4536 |
++ }, |
|
| 4537 |
++ "create pod with HostVolume /etc/ssl/certs in read-write mode denied": {
|
|
| 4538 |
++ operation: kadmission.Create, |
|
| 4539 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", false).build(),
|
|
| 4528 | 4540 |
+ userInfo: newTestUserBuilder().build(), |
| 4529 | 4541 |
+ shouldPassValidate: false, |
| 4530 | 4542 |
+ }, |
| ... | ... |
@@ -4536,7 +4774,7 @@ index 0000000..596b7d4 |
| 4536 | 4536 |
+ }, |
| 4537 | 4537 |
+ "create pod with HostVolume and CascadeDisk denied": {
|
| 4538 | 4538 |
+ operation: kadmission.Create, |
| 4539 |
-+ pod: newTestPodBuilder().withHostVolume().withCascadeDisk().build(), |
|
| 4539 |
++ pod: newTestPodBuilder().withHostVolume("/", false).withCascadeDisk().build(),
|
|
| 4540 | 4540 |
+ userInfo: newTestUserBuilder().build(), |
| 4541 | 4541 |
+ shouldPassValidate: false, |
| 4542 | 4542 |
+ }, |
| ... | ... |
@@ -4548,15 +4786,144 @@ index 0000000..596b7d4 |
| 4548 | 4548 |
+ }, |
| 4549 | 4549 |
+ "delete pod allowed": {
|
| 4550 | 4550 |
+ operation: kadmission.Delete, |
| 4551 |
++ pod: nil, |
|
| 4552 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4553 |
++ shouldPassValidate: true, |
|
| 4554 |
++ }, |
|
| 4555 |
++ } |
|
| 4556 |
++ |
|
| 4557 |
++ for k, v := range tests {
|
|
| 4558 |
++ testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t) |
|
| 4559 |
++ } |
|
| 4560 |
++} |
|
| 4561 |
++ |
|
| 4562 |
++func TestAdmitPrivilegedWithCustomPSP(t *testing.T) {
|
|
| 4563 |
++ tests := map[string]struct {
|
|
| 4564 |
++ operation kadmission.Operation |
|
| 4565 |
++ pod *kapi.Pod |
|
| 4566 |
++ name string |
|
| 4567 |
++ userInfo user.Info |
|
| 4568 |
++ shouldPassValidate bool |
|
| 4569 |
++ }{
|
|
| 4570 |
++ "create pod with Privileged=nil allowed": {
|
|
| 4571 |
++ operation: kadmission.Create, |
|
| 4572 |
++ pod: newTestPodBuilder().build(), |
|
| 4573 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4574 |
++ shouldPassValidate: true, |
|
| 4575 |
++ }, |
|
| 4576 |
++ "create pod with Privileged=false allowed": {
|
|
| 4577 |
++ operation: kadmission.Create, |
|
| 4578 |
++ pod: newTestPodBuilder().withPrivileged(false).build(), |
|
| 4579 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4580 |
++ shouldPassValidate: true, |
|
| 4581 |
++ }, |
|
| 4582 |
++ "create pod with Privileged=true allowed": {
|
|
| 4583 |
++ operation: kadmission.Create, |
|
| 4584 |
++ pod: newTestPodBuilder().withPrivileged(true).build(), |
|
| 4585 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4586 |
++ shouldPassValidate: true, |
|
| 4587 |
++ }, |
|
| 4588 |
++ "create pod with multiple containers, one has Privileged=true allowed": {
|
|
| 4589 |
++ operation: kadmission.Create, |
|
| 4590 |
++ pod: newTestPodBuilder().withPrivileged(true).withInitContainer().withContainer().build(), |
|
| 4591 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4592 |
++ shouldPassValidate: true, |
|
| 4593 |
++ }, |
|
| 4594 |
++ "update pod with Privileged=true allowed": {
|
|
| 4595 |
++ operation: kadmission.Update, |
|
| 4596 |
++ pod: newTestPodBuilder().withPrivileged(true).build(), |
|
| 4597 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4598 |
++ shouldPassValidate: true, |
|
| 4599 |
++ }, |
|
| 4600 |
++ "create pod with HostNetwork=true allowed": {
|
|
| 4601 |
++ operation: kadmission.Create, |
|
| 4602 |
++ pod: newTestPodBuilder().withHostNetwork(true).build(), |
|
| 4603 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4604 |
++ shouldPassValidate: true, |
|
| 4605 |
++ }, |
|
| 4606 |
++ "create pod with HostIPC=true denied": {
|
|
| 4607 |
++ operation: kadmission.Create, |
|
| 4608 |
++ pod: newTestPodBuilder().withHostIPC(true).build(), |
|
| 4609 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4610 |
++ shouldPassValidate: false, |
|
| 4611 |
++ }, |
|
| 4612 |
++ "create pod with HostPID=true denied": {
|
|
| 4613 |
++ operation: kadmission.Create, |
|
| 4614 |
++ pod: newTestPodBuilder().withHostPID(true).build(), |
|
| 4615 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4616 |
++ shouldPassValidate: false, |
|
| 4617 |
++ }, |
|
| 4618 |
++ "create pod with HostPort allowed": {
|
|
| 4619 |
++ operation: kadmission.Create, |
|
| 4620 |
++ pod: newTestPodBuilder().withHostPort().build(), |
|
| 4621 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4622 |
++ shouldPassValidate: true, |
|
| 4623 |
++ }, |
|
| 4624 |
++ "create pod with HostVolume allowed": {
|
|
| 4625 |
++ operation: kadmission.Create, |
|
| 4626 |
++ pod: newTestPodBuilder().withHostVolume("/", false).build(),
|
|
| 4627 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4628 |
++ shouldPassValidate: true, |
|
| 4629 |
++ }, |
|
| 4630 |
++ "create pod with HostVolume /etc/ssl/certs in read-only mode allowed": {
|
|
| 4631 |
++ operation: kadmission.Create, |
|
| 4632 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", true).build(),
|
|
| 4633 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4634 |
++ shouldPassValidate: true, |
|
| 4635 |
++ }, |
|
| 4636 |
++ "create pod with HostVolume /etc/ssl/certs in read-write mode denied": {
|
|
| 4637 |
++ operation: kadmission.Create, |
|
| 4638 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", false).build(),
|
|
| 4639 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4640 |
++ shouldPassValidate: false, |
|
| 4641 |
++ }, |
|
| 4642 |
++ "create pod with CascadeDisk allowed": {
|
|
| 4643 |
++ operation: kadmission.Create, |
|
| 4644 |
++ pod: newTestPodBuilder().withCascadeDisk().build(), |
|
| 4645 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4646 |
++ shouldPassValidate: true, |
|
| 4647 |
++ }, |
|
| 4648 |
++ "create pod with HostVolume and CascadeDisk allowed": {
|
|
| 4649 |
++ operation: kadmission.Create, |
|
| 4650 |
++ pod: newTestPodBuilder().withHostVolume("/", false).withCascadeDisk().build(),
|
|
| 4651 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4652 |
++ shouldPassValidate: true, |
|
| 4653 |
++ }, |
|
| 4654 |
++ "connect pod allowed": {
|
|
| 4655 |
++ operation: kadmission.Connect, |
|
| 4551 | 4656 |
+ pod: newTestPodBuilder().build(), |
| 4552 | 4657 |
+ userInfo: newTestUserBuilder().build(), |
| 4553 | 4658 |
+ shouldPassValidate: true, |
| 4554 | 4659 |
+ }, |
| 4660 |
++ "delete pod allowed": {
|
|
| 4661 |
++ operation: kadmission.Delete, |
|
| 4662 |
++ pod: nil, |
|
| 4663 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4664 |
++ shouldPassValidate: true, |
|
| 4665 |
++ }, |
|
| 4666 |
++ } |
|
| 4667 |
++ |
|
| 4668 |
++ // Setup custom PSP file. |
|
| 4669 |
++ file, err := os.Create(pspFileName) |
|
| 4670 |
++ if err != nil {
|
|
| 4671 |
++ t.Errorf("TestAdmitPrivilegedWithCustomPSP: failed to open custom PSP file %v", err)
|
|
| 4672 |
++ return |
|
| 4673 |
++ } |
|
| 4674 |
++ _, err = file.WriteString(pspConfigFile) |
|
| 4675 |
++ if err != nil {
|
|
| 4676 |
++ t.Errorf("TestAdmitPrivilegedWithCustomPSP: failed to write to custom PSP file %v", err)
|
|
| 4677 |
++ return |
|
| 4555 | 4678 |
+ } |
| 4556 | 4679 |
+ |
| 4557 | 4680 |
+ for k, v := range tests {
|
| 4558 | 4681 |
+ testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t) |
| 4559 | 4682 |
+ } |
| 4683 |
++ |
|
| 4684 |
++ // Delete custom PSP file. |
|
| 4685 |
++ err = os.Remove(pspFileName) |
|
| 4686 |
++ if err != nil {
|
|
| 4687 |
++ t.Errorf("TestAdmitPrivilegedWithCustomPSP: failed to delete custom PSP file %v", err)
|
|
| 4688 |
++ } |
|
| 4560 | 4689 |
+} |
| 4561 | 4690 |
+ |
| 4562 | 4691 |
+func TestPrivilegedNamespace(t *testing.T) {
|
| ... | ... |
@@ -4639,6 +5006,63 @@ index 0000000..596b7d4 |
| 4639 | 4639 |
+ } |
| 4640 | 4640 |
+} |
| 4641 | 4641 |
+ |
| 4642 |
++func TestToleration(t *testing.T) {
|
|
| 4643 |
++ tests := map[string]struct {
|
|
| 4644 |
++ operation kadmission.Operation |
|
| 4645 |
++ pod *kapi.Pod |
|
| 4646 |
++ name string |
|
| 4647 |
++ userInfo user.Info |
|
| 4648 |
++ shouldPassValidate bool |
|
| 4649 |
++ }{
|
|
| 4650 |
++ "allowed: create pod with no toleration": {
|
|
| 4651 |
++ operation: kadmission.Create, |
|
| 4652 |
++ pod: newTestPodBuilder().build(), |
|
| 4653 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4654 |
++ shouldPassValidate: true, |
|
| 4655 |
++ }, |
|
| 4656 |
++ "allowed: create pod with normal toleration key": {
|
|
| 4657 |
++ operation: kadmission.Create, |
|
| 4658 |
++ pod: newTestPodBuilder().withToleration("mykey", reservedTolerationValue, kapi.TolerationOpEqual, kapi.TaintEffectNoSchedule).build(),
|
|
| 4659 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4660 |
++ shouldPassValidate: true, |
|
| 4661 |
++ }, |
|
| 4662 |
++ "allowed: create pod with normal toleration value": {
|
|
| 4663 |
++ operation: kadmission.Create, |
|
| 4664 |
++ pod: newTestPodBuilder().withToleration(reservedTolerationKey, "myval", kapi.TolerationOpEqual, kapi.TaintEffectNoSchedule).build(), |
|
| 4665 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4666 |
++ shouldPassValidate: true, |
|
| 4667 |
++ }, |
|
| 4668 |
++ "denied: create pod with reserved toleration": {
|
|
| 4669 |
++ operation: kadmission.Create, |
|
| 4670 |
++ pod: newTestPodBuilder().withToleration(reservedTolerationKey, reservedTolerationValue, kapi.TolerationOpEqual, kapi.TaintEffectNoSchedule).build(), |
|
| 4671 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4672 |
++ shouldPassValidate: false, |
|
| 4673 |
++ }, |
|
| 4674 |
++ "denied: create pod with wildcard toleration": {
|
|
| 4675 |
++ operation: kadmission.Create, |
|
| 4676 |
++ pod: newTestPodBuilder().withToleration("", "", kapi.TolerationOpExists, "").build(),
|
|
| 4677 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4678 |
++ shouldPassValidate: false, |
|
| 4679 |
++ }, |
|
| 4680 |
++ "denied: create pod with value wildcard toleration": {
|
|
| 4681 |
++ operation: kadmission.Create, |
|
| 4682 |
++ pod: newTestPodBuilder().withToleration(reservedTolerationKey, "", kapi.TolerationOpExists, kapi.TaintEffectNoSchedule).build(), |
|
| 4683 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4684 |
++ shouldPassValidate: false, |
|
| 4685 |
++ }, |
|
| 4686 |
++ "allowed: create pod with value wildcard and normal key": {
|
|
| 4687 |
++ operation: kadmission.Create, |
|
| 4688 |
++ pod: newTestPodBuilder().withToleration("mykey", "", kapi.TolerationOpExists, kapi.TaintEffectNoSchedule).build(),
|
|
| 4689 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4690 |
++ shouldPassValidate: true, |
|
| 4691 |
++ }, |
|
| 4692 |
++ } |
|
| 4693 |
++ |
|
| 4694 |
++ for k, v := range tests {
|
|
| 4695 |
++ testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t) |
|
| 4696 |
++ } |
|
| 4697 |
++} |
|
| 4698 |
++ |
|
| 4642 | 4699 |
+func TestClusterLevelResources(t *testing.T) {
|
| 4643 | 4700 |
+ tests := map[string]struct {
|
| 4644 | 4701 |
+ operation kadmission.Operation |
| ... | ... |
@@ -4747,9 +5171,34 @@ index 0000000..596b7d4 |
| 4747 | 4747 |
+ userInfo: newTestUserBuilder().withName(systemUnsecuredUser).build(), |
| 4748 | 4748 |
+ shouldPassValidate: true, |
| 4749 | 4749 |
+ }, |
| 4750 |
-+ "denied: regular lightwave user update nodes": {
|
|
| 4750 |
++ "allowed: regular lightwave user update worker nodes": {
|
|
| 4751 | 4751 |
+ operation: kadmission.Update, |
| 4752 | 4752 |
+ resource: "nodes", |
| 4753 |
++ name: "worker-guid", |
|
| 4754 |
++ namespace: "", |
|
| 4755 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4756 |
++ shouldPassValidate: true, |
|
| 4757 |
++ }, |
|
| 4758 |
++ "denied: regular lightwave user update master nodes": {
|
|
| 4759 |
++ operation: kadmission.Update, |
|
| 4760 |
++ resource: "nodes", |
|
| 4761 |
++ name: "master-guid", |
|
| 4762 |
++ namespace: "", |
|
| 4763 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4764 |
++ shouldPassValidate: false, |
|
| 4765 |
++ }, |
|
| 4766 |
++ "denied: regular lightwave user delete master nodes": {
|
|
| 4767 |
++ operation: kadmission.Delete, |
|
| 4768 |
++ resource: "nodes", |
|
| 4769 |
++ name: "master-guid", |
|
| 4770 |
++ namespace: "", |
|
| 4771 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4772 |
++ shouldPassValidate: false, |
|
| 4773 |
++ }, |
|
| 4774 |
++ "denied: regular lightwave user delete worker nodes": {
|
|
| 4775 |
++ operation: kadmission.Delete, |
|
| 4776 |
++ resource: "nodes", |
|
| 4777 |
++ name: "worker-guid", |
|
| 4753 | 4778 |
+ namespace: "", |
| 4754 | 4779 |
+ userInfo: newTestUserBuilder().build(), |
| 4755 | 4780 |
+ shouldPassValidate: false, |
| ... | ... |
@@ -4770,15 +5219,20 @@ index 0000000..596b7d4 |
| 4770 | 4770 |
+func testPodValidation(testCaseName string, op kadmission.Operation, pod *kapi.Pod, name string, userInfo user.Info, |
| 4771 | 4771 |
+ shouldPassValidate bool, t *testing.T) {
|
| 4772 | 4772 |
+ |
| 4773 |
-+ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup) |
|
| 4773 |
++ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName) |
|
| 4774 | 4774 |
+ configFile := strings.NewReader(defaultConfigFile) |
| 4775 | 4775 |
+ plugin, err := NewVMwareAdmissionController(configFile) |
| 4776 | 4776 |
+ if err != nil {
|
| 4777 | 4777 |
+ t.Errorf("%s: failed to create admission controller %v", testCaseName, err)
|
| 4778 | 4778 |
+ } |
| 4779 | 4779 |
+ |
| 4780 |
++ namespace := "default" |
|
| 4781 |
++ if pod != nil {
|
|
| 4782 |
++ namespace = pod.Namespace |
|
| 4783 |
++ } |
|
| 4784 |
++ |
|
| 4780 | 4785 |
+ attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"),
|
| 4781 |
-+ pod.Namespace, name, kapi.Resource("pods").WithVersion("version"), "", op, userInfo)
|
|
| 4786 |
++ namespace, name, kapi.Resource("pods").WithVersion("version"), "", op, userInfo)
|
|
| 4782 | 4787 |
+ |
| 4783 | 4788 |
+ err = plugin.Validate(attrs) |
| 4784 | 4789 |
+ if shouldPassValidate && err != nil {
|
| ... | ... |
@@ -4791,7 +5245,7 @@ index 0000000..596b7d4 |
| 4791 | 4791 |
+func testResourceValidation(testCaseName string, op kadmission.Operation, resource string, name string, |
| 4792 | 4792 |
+ namespace string, userInfo user.Info, shouldPassValidate bool, t *testing.T) {
|
| 4793 | 4793 |
+ |
| 4794 |
-+ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup) |
|
| 4794 |
++ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName) |
|
| 4795 | 4795 |
+ configFile := strings.NewReader(defaultConfigFile) |
| 4796 | 4796 |
+ plugin, err := NewVMwareAdmissionController(configFile) |
| 4797 | 4797 |
+ if err != nil {
|
| ... | ... |
@@ -4891,19 +5345,19 @@ index 0000000..596b7d4 |
| 4891 | 4891 |
+ return p |
| 4892 | 4892 |
+} |
| 4893 | 4893 |
+ |
| 4894 |
-+func (p *testPodBuilder) withHostVolume() *testPodBuilder {
|
|
| 4894 |
++func (p *testPodBuilder) withHostVolume(hostPath string, readOnly bool) *testPodBuilder {
|
|
| 4895 | 4895 |
+ volume := kapi.Volume{
|
| 4896 | 4896 |
+ Name: "host", |
| 4897 | 4897 |
+ VolumeSource: kapi.VolumeSource{
|
| 4898 | 4898 |
+ HostPath: &kapi.HostPathVolumeSource{
|
| 4899 |
-+ Path: "/", |
|
| 4899 |
++ Path: hostPath, |
|
| 4900 | 4900 |
+ }, |
| 4901 | 4901 |
+ }, |
| 4902 | 4902 |
+ } |
| 4903 |
-+ device := kapi.VolumeDevice{Name: "host", DevicePath: "/host"}
|
|
| 4903 |
++ volumeMount := kapi.VolumeMount{Name: "host", MountPath: "/data", ReadOnly: readOnly}
|
|
| 4904 | 4904 |
+ |
| 4905 | 4905 |
+ p.pod.Spec.Volumes = append(p.pod.Spec.Volumes, volume) |
| 4906 |
-+ p.pod.Spec.Containers[0].VolumeDevices = append(p.pod.Spec.Containers[0].VolumeDevices, device) |
|
| 4906 |
++ p.pod.Spec.Containers[0].VolumeMounts = append(p.pod.Spec.Containers[0].VolumeMounts, volumeMount) |
|
| 4907 | 4907 |
+ return p |
| 4908 | 4908 |
+} |
| 4909 | 4909 |
+ |
| ... | ... |
@@ -4944,6 +5398,16 @@ index 0000000..596b7d4 |
| 4944 | 4944 |
+ return p |
| 4945 | 4945 |
+} |
| 4946 | 4946 |
+ |
| 4947 |
++func (p *testPodBuilder) withToleration(key, value string, operator kapi.TolerationOperator, effect kapi.TaintEffect) *testPodBuilder {
|
|
| 4948 |
++ p.pod.Spec.Tolerations = append(p.pod.Spec.Tolerations, kapi.Toleration{
|
|
| 4949 |
++ Key: key, |
|
| 4950 |
++ Value: value, |
|
| 4951 |
++ Operator: operator, |
|
| 4952 |
++ Effect: effect, |
|
| 4953 |
++ }) |
|
| 4954 |
++ return p |
|
| 4955 |
++} |
|
| 4956 |
++ |
|
| 4947 | 4957 |
+// testUserBuilder |
| 4948 | 4958 |
+type testUserBuilder struct {
|
| 4949 | 4959 |
+ user *user.DefaultInfo |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 |
-From ba75f0a3934a48bfc8be1908c7eefdff3e9b9eaa Mon Sep 17 00:00:00 2001 |
|
| 1 |
+From 4a1a6d90627ddb29b65450deac8b4a7a528a91bf Mon Sep 17 00:00:00 2001 |
|
| 2 | 2 |
From: Bo Gan <ganb@vmware.com> |
| 3 | 3 |
Date: Sun, 10 Jun 2018 02:13:51 -0700 |
| 4 |
-Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (d06c534) |
|
| 4 |
+Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (da5bd0d) |
|
| 5 | 5 |
|
| 6 | 6 |
--- |
| 7 | 7 |
api/swagger-spec/apps_v1alpha1.json | 21 + |
| ... | ... |
@@ -21,18 +21,18 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (d06c534) |
| 21 | 21 |
pkg/apis/core/validation/validation.go | 29 +- |
| 22 | 22 |
pkg/apis/extensions/types.go | 1 + |
| 23 | 23 |
pkg/cloudprovider/providers/BUILD | 2 + |
| 24 |
- pkg/cloudprovider/providers/cascade/BUILD | 56 +++ |
|
| 24 |
+ pkg/cloudprovider/providers/cascade/BUILD | 56 ++ |
|
| 25 | 25 |
pkg/cloudprovider/providers/cascade/OWNERS | 3 + |
| 26 |
- pkg/cloudprovider/providers/cascade/apitypes.go | 227 +++++++++ |
|
| 27 |
- pkg/cloudprovider/providers/cascade/auth.go | 145 ++++++ |
|
| 28 |
- pkg/cloudprovider/providers/cascade/cascade.go | 214 ++++++++ |
|
| 29 |
- .../providers/cascade/cascade_disks.go | 227 +++++++++ |
|
| 30 |
- .../providers/cascade/cascade_instances.go | 92 ++++ |
|
| 26 |
+ pkg/cloudprovider/providers/cascade/apitypes.go | 227 ++++++ |
|
| 27 |
+ pkg/cloudprovider/providers/cascade/auth.go | 145 ++++ |
|
| 28 |
+ pkg/cloudprovider/providers/cascade/cascade.go | 214 ++++++ |
|
| 29 |
+ .../providers/cascade/cascade_disks.go | 227 ++++++ |
|
| 30 |
+ .../providers/cascade/cascade_instances.go | 92 +++ |
|
| 31 | 31 |
.../providers/cascade/cascade_instances_test.go | 44 ++ |
| 32 |
- .../providers/cascade/cascade_loadbalancer.go | 285 +++++++++++ |
|
| 33 |
- pkg/cloudprovider/providers/cascade/client.go | 394 +++++++++++++++ |
|
| 34 |
- pkg/cloudprovider/providers/cascade/oidcclient.go | 297 ++++++++++++ |
|
| 35 |
- pkg/cloudprovider/providers/cascade/restclient.go | 262 ++++++++++ |
|
| 32 |
+ .../providers/cascade/cascade_loadbalancer.go | 285 ++++++++ |
|
| 33 |
+ pkg/cloudprovider/providers/cascade/client.go | 394 ++++++++++ |
|
| 34 |
+ pkg/cloudprovider/providers/cascade/oidcclient.go | 297 ++++++++ |
|
| 35 |
+ pkg/cloudprovider/providers/cascade/restclient.go | 262 +++++++ |
|
| 36 | 36 |
pkg/cloudprovider/providers/cascade/tests_owed | 5 + |
| 37 | 37 |
pkg/cloudprovider/providers/cascade/utils.go | 25 + |
| 38 | 38 |
pkg/cloudprovider/providers/providers.go | 1 + |
| ... | ... |
@@ -41,16 +41,16 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (d06c534) |
| 41 | 41 |
pkg/security/podsecuritypolicy/util/util.go | 3 + |
| 42 | 42 |
pkg/volume/cascade_disk/BUILD | 43 ++ |
| 43 | 43 |
pkg/volume/cascade_disk/OWNERS | 2 + |
| 44 |
- pkg/volume/cascade_disk/attacher.go | 268 ++++++++++ |
|
| 45 |
- pkg/volume/cascade_disk/cascade_disk.go | 390 +++++++++++++++ |
|
| 46 |
- pkg/volume/cascade_disk/cascade_util.go | 107 ++++ |
|
| 47 |
- .../admission/persistentvolume/label/admission.go | 54 +++ |
|
| 48 |
- plugin/pkg/admission/vke/BUILD | 58 +++ |
|
| 49 |
- plugin/pkg/admission/vke/admission.go | 349 +++++++++++++ |
|
| 50 |
- plugin/pkg/admission/vke/admission_test.go | 538 +++++++++++++++++++++ |
|
| 51 |
- staging/src/k8s.io/api/core/v1/generated.pb.go | 310 +++++++++++- |
|
| 44 |
+ pkg/volume/cascade_disk/attacher.go | 264 +++++++ |
|
| 45 |
+ pkg/volume/cascade_disk/cascade_disk.go | 390 ++++++++++ |
|
| 46 |
+ pkg/volume/cascade_disk/cascade_util.go | 152 ++++ |
|
| 47 |
+ .../admission/persistentvolume/label/admission.go | 54 ++ |
|
| 48 |
+ plugin/pkg/admission/vke/BUILD | 60 ++ |
|
| 49 |
+ plugin/pkg/admission/vke/admission.go | 499 +++++++++++++ |
|
| 50 |
+ plugin/pkg/admission/vke/admission_test.go | 809 +++++++++++++++++++++ |
|
| 51 |
+ staging/src/k8s.io/api/core/v1/generated.pb.go | 310 +++++++- |
|
| 52 | 52 |
staging/src/k8s.io/api/core/v1/types.go | 24 +- |
| 53 |
- 46 files changed, 4655 insertions(+), 29 deletions(-) |
|
| 53 |
+ 46 files changed, 5119 insertions(+), 29 deletions(-) |
|
| 54 | 54 |
create mode 100644 pkg/cloudprovider/providers/cascade/BUILD |
| 55 | 55 |
create mode 100644 pkg/cloudprovider/providers/cascade/OWNERS |
| 56 | 56 |
create mode 100644 pkg/cloudprovider/providers/cascade/apitypes.go |
| ... | ... |
@@ -3173,10 +3173,10 @@ index 0000000..c3a4ed7 |
| 3173 | 3173 |
+- ashokc |
| 3174 | 3174 |
diff --git a/pkg/volume/cascade_disk/attacher.go b/pkg/volume/cascade_disk/attacher.go |
| 3175 | 3175 |
new file mode 100644 |
| 3176 |
-index 0000000..80d8d3a |
|
| 3176 |
+index 0000000..c19c37c |
|
| 3177 | 3177 |
--- /dev/null |
| 3178 | 3178 |
+++ b/pkg/volume/cascade_disk/attacher.go |
| 3179 |
-@@ -0,0 +1,268 @@ |
|
| 3179 |
+@@ -0,0 +1,264 @@ |
|
| 3180 | 3180 |
+package cascade_disk |
| 3181 | 3181 |
+ |
| 3182 | 3182 |
+import ( |
| ... | ... |
@@ -3192,7 +3192,6 @@ index 0000000..80d8d3a |
| 3192 | 3192 |
+ "k8s.io/kubernetes/pkg/util/mount" |
| 3193 | 3193 |
+ "k8s.io/kubernetes/pkg/volume" |
| 3194 | 3194 |
+ volumeutil "k8s.io/kubernetes/pkg/volume/util" |
| 3195 |
-+ "strings" |
|
| 3196 | 3195 |
+) |
| 3197 | 3196 |
+ |
| 3198 | 3197 |
+type cascadeDiskAttacher struct {
|
| ... | ... |
@@ -3234,10 +3233,6 @@ index 0000000..80d8d3a |
| 3234 | 3234 |
+ glog.Errorf("Error attaching volume %q to node %q: %+v", volumeSource.DiskID, nodeName, err)
|
| 3235 | 3235 |
+ return "", err |
| 3236 | 3236 |
+ } |
| 3237 |
-+ |
|
| 3238 |
-+ // Cacsade uses device names of the format /dev/sdX, but newer Linux Kernels mount them under /dev/xvdX |
|
| 3239 |
-+ // (source: AWS console). So we have to rename the first occurrence of sd to xvd. |
|
| 3240 |
-+ devicePath = strings.Replace(devicePath, "sd", "xvd", 1) |
|
| 3241 | 3237 |
+ return devicePath, nil |
| 3242 | 3238 |
+} |
| 3243 | 3239 |
+ |
| ... | ... |
@@ -3300,6 +3295,7 @@ index 0000000..80d8d3a |
| 3300 | 3300 |
+ select {
|
| 3301 | 3301 |
+ case <-ticker.C: |
| 3302 | 3302 |
+ glog.V(4).Infof("Checking disk %s is attached", volumeSource.DiskID)
|
| 3303 |
++ devicePath := getDiskByIdPath(devicePath) |
|
| 3303 | 3304 |
+ checkPath, err := verifyDevicePath(devicePath) |
| 3304 | 3305 |
+ if err != nil {
|
| 3305 | 3306 |
+ // Log error, if any, and continue checking periodically. See issue #11321 |
| ... | ... |
@@ -3308,7 +3304,7 @@ index 0000000..80d8d3a |
| 3308 | 3308 |
+ } else if checkPath != "" {
|
| 3309 | 3309 |
+ // A device path has successfully been created for the disk |
| 3310 | 3310 |
+ glog.V(4).Infof("Successfully found attached disk %s.", volumeSource.DiskID)
|
| 3311 |
-+ return devicePath, nil |
|
| 3311 |
++ return checkPath, nil |
|
| 3312 | 3312 |
+ } |
| 3313 | 3313 |
+ case <-timer.C: |
| 3314 | 3314 |
+ return "", fmt.Errorf("Could not find attached disk %s. Timeout waiting for mount paths to be "+
|
| ... | ... |
@@ -3843,10 +3839,10 @@ index 0000000..3968060 |
| 3843 | 3843 |
+} |
| 3844 | 3844 |
diff --git a/pkg/volume/cascade_disk/cascade_util.go b/pkg/volume/cascade_disk/cascade_util.go |
| 3845 | 3845 |
new file mode 100644 |
| 3846 |
-index 0000000..19ddb7f |
|
| 3846 |
+index 0000000..e08b7d0 |
|
| 3847 | 3847 |
--- /dev/null |
| 3848 | 3848 |
+++ b/pkg/volume/cascade_disk/cascade_util.go |
| 3849 |
-@@ -0,0 +1,107 @@ |
|
| 3849 |
+@@ -0,0 +1,152 @@ |
|
| 3850 | 3850 |
+package cascade_disk |
| 3851 | 3851 |
+ |
| 3852 | 3852 |
+import ( |
| ... | ... |
@@ -3860,6 +3856,8 @@ index 0000000..19ddb7f |
| 3860 | 3860 |
+ "k8s.io/kubernetes/pkg/cloudprovider/providers/cascade" |
| 3861 | 3861 |
+ "k8s.io/kubernetes/pkg/volume" |
| 3862 | 3862 |
+ volumeutil "k8s.io/kubernetes/pkg/volume/util" |
| 3863 |
++ "path/filepath" |
|
| 3864 |
++ "os" |
|
| 3863 | 3865 |
+) |
| 3864 | 3866 |
+ |
| 3865 | 3867 |
+const ( |
| ... | ... |
@@ -3879,6 +3877,18 @@ index 0000000..19ddb7f |
| 3879 | 3879 |
+ return "", nil |
| 3880 | 3880 |
+} |
| 3881 | 3881 |
+ |
| 3882 |
++// Returns path for given VKE disk mount |
|
| 3883 |
++func getDiskByIdPath(devicePath string) string {
|
|
| 3884 |
++ nvmePath, err := findNvmeVolume(devicePath) |
|
| 3885 |
++ if err != nil {
|
|
| 3886 |
++ glog.Warningf("error looking for nvme volume %q: %v", devicePath, err)
|
|
| 3887 |
++ } else if nvmePath != "" {
|
|
| 3888 |
++ devicePath = nvmePath |
|
| 3889 |
++ } |
|
| 3890 |
++ |
|
| 3891 |
++ return devicePath |
|
| 3892 |
++} |
|
| 3893 |
++ |
|
| 3882 | 3894 |
+// CreateVolume creates a Cascade persistent disk. |
| 3883 | 3895 |
+func (util *CascadeDiskUtil) CreateVolume(p *cascadeDiskProvisioner) (diskID string, capacityGB int, fstype string, |
| 3884 | 3896 |
+ err error) {
|
| ... | ... |
@@ -3954,6 +3964,37 @@ index 0000000..19ddb7f |
| 3954 | 3954 |
+ } |
| 3955 | 3955 |
+ return cc, nil |
| 3956 | 3956 |
+} |
| 3957 |
++ |
|
| 3958 |
++// findNvmeVolume looks for the nvme volume with the specified name |
|
| 3959 |
++// It follows the symlink (if it exists) and returns the absolute path to the device |
|
| 3960 |
++func findNvmeVolume(findName string) (device string, err error) {
|
|
| 3961 |
++ stat, err := os.Lstat(findName) |
|
| 3962 |
++ if err != nil {
|
|
| 3963 |
++ if os.IsNotExist(err) {
|
|
| 3964 |
++ glog.V(6).Infof("nvme path not found %q", findName)
|
|
| 3965 |
++ return "", nil |
|
| 3966 |
++ } |
|
| 3967 |
++ return "", fmt.Errorf("error getting stat of %q: %v", findName, err)
|
|
| 3968 |
++ } |
|
| 3969 |
++ |
|
| 3970 |
++ if stat.Mode()&os.ModeSymlink != os.ModeSymlink {
|
|
| 3971 |
++ glog.Warningf("nvme file %q found, but was not a symlink", findName)
|
|
| 3972 |
++ return "", nil |
|
| 3973 |
++ } |
|
| 3974 |
++ |
|
| 3975 |
++ // Find the target, resolving to an absolute path |
|
| 3976 |
++ // For example, /dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_vol0fab1d5e3f72a5e23 -> ../../nvme2n1 |
|
| 3977 |
++ resolved, err := filepath.EvalSymlinks(findName) |
|
| 3978 |
++ if err != nil {
|
|
| 3979 |
++ return "", fmt.Errorf("error reading target of symlink %q: %v", findName, err)
|
|
| 3980 |
++ } |
|
| 3981 |
++ |
|
| 3982 |
++ if !strings.HasPrefix(resolved, "/dev") {
|
|
| 3983 |
++ return "", fmt.Errorf("resolved symlink for %q was unexpected: %q", findName, resolved)
|
|
| 3984 |
++ } |
|
| 3985 |
++ |
|
| 3986 |
++ return resolved, nil |
|
| 3987 |
++} |
|
| 3957 | 3988 |
diff --git a/plugin/pkg/admission/persistentvolume/label/admission.go b/plugin/pkg/admission/persistentvolume/label/admission.go |
| 3958 | 3989 |
index 819adae..3d55589 100644 |
| 3959 | 3990 |
--- a/plugin/pkg/admission/persistentvolume/label/admission.go |
| ... | ... |
@@ -4039,10 +4080,10 @@ index 819adae..3d55589 100644 |
| 4039 | 4039 |
+} |
| 4040 | 4040 |
diff --git a/plugin/pkg/admission/vke/BUILD b/plugin/pkg/admission/vke/BUILD |
| 4041 | 4041 |
new file mode 100644 |
| 4042 |
-index 0000000..b0a6026 |
|
| 4042 |
+index 0000000..2fb36c7 |
|
| 4043 | 4043 |
--- /dev/null |
| 4044 | 4044 |
+++ b/plugin/pkg/admission/vke/BUILD |
| 4045 |
-@@ -0,0 +1,58 @@ |
|
| 4045 |
+@@ -0,0 +1,60 @@ |
|
| 4046 | 4046 |
+package(default_visibility = ["//visibility:public"]) |
| 4047 | 4047 |
+ |
| 4048 | 4048 |
+load( |
| ... | ... |
@@ -4057,10 +4098,12 @@ index 0000000..b0a6026 |
| 4057 | 4057 |
+ deps = [ |
| 4058 | 4058 |
+ "//pkg/apis/core:go_default_library", |
| 4059 | 4059 |
+ "//pkg/apis/extensions:go_default_library", |
| 4060 |
++ "//pkg/apis/policy/v1beta1:go_default_library", |
|
| 4060 | 4061 |
+ "//pkg/apis/rbac:go_default_library", |
| 4061 | 4062 |
+ "//pkg/registry/rbac:go_default_library", |
| 4062 | 4063 |
+ "//pkg/security/podsecuritypolicy:go_default_library", |
| 4063 | 4064 |
+ "//vendor/github.com/golang/glog:go_default_library", |
| 4065 |
++ "//vendor/k8s.io/api/policy/v1beta1:go_default_library" |
|
| 4064 | 4066 |
+ "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", |
| 4065 | 4067 |
+ "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", |
| 4066 | 4068 |
+ "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", |
| ... | ... |
@@ -4104,18 +4147,20 @@ index 0000000..b0a6026 |
| 4104 | 4104 |
\ No newline at end of file |
| 4105 | 4105 |
diff --git a/plugin/pkg/admission/vke/admission.go b/plugin/pkg/admission/vke/admission.go |
| 4106 | 4106 |
new file mode 100644 |
| 4107 |
-index 0000000..e33d4e9 |
|
| 4107 |
+index 0000000..64f6c36 |
|
| 4108 | 4108 |
--- /dev/null |
| 4109 | 4109 |
+++ b/plugin/pkg/admission/vke/admission.go |
| 4110 |
-@@ -0,0 +1,349 @@ |
|
| 4110 |
+@@ -0,0 +1,499 @@ |
|
| 4111 | 4111 |
+package vke |
| 4112 | 4112 |
+ |
| 4113 | 4113 |
+import ( |
| 4114 | 4114 |
+ "fmt" |
| 4115 | 4115 |
+ "io" |
| 4116 |
++ "os" |
|
| 4116 | 4117 |
+ "strings" |
| 4117 | 4118 |
+ |
| 4118 | 4119 |
+ "github.com/golang/glog" |
| 4120 |
++ "k8s.io/api/policy/v1beta1" |
|
| 4119 | 4121 |
+ apiequality "k8s.io/apimachinery/pkg/api/equality" |
| 4120 | 4122 |
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 4121 | 4123 |
+ "k8s.io/apimachinery/pkg/util/validation/field" |
| ... | ... |
@@ -4123,6 +4168,7 @@ index 0000000..e33d4e9 |
| 4123 | 4123 |
+ "k8s.io/apiserver/pkg/admission" |
| 4124 | 4124 |
+ api "k8s.io/kubernetes/pkg/apis/core" |
| 4125 | 4125 |
+ "k8s.io/kubernetes/pkg/apis/extensions" |
| 4126 |
++ policybeta "k8s.io/kubernetes/pkg/apis/policy/v1beta1" |
|
| 4126 | 4127 |
+ "k8s.io/kubernetes/pkg/apis/rbac" |
| 4127 | 4128 |
+ rbacregistry "k8s.io/kubernetes/pkg/registry/rbac" |
| 4128 | 4129 |
+ "k8s.io/kubernetes/pkg/security/podsecuritypolicy" |
| ... | ... |
@@ -4138,6 +4184,10 @@ index 0000000..e33d4e9 |
| 4138 | 4138 |
+ reservedPrefix = "vke" |
| 4139 | 4139 |
+ kubeletGroup = "system:nodes" |
| 4140 | 4140 |
+ kubeProxyGroup = "vke:kube-proxies" |
| 4141 |
++ reservedTolerationKey = "Dedicated" |
|
| 4142 |
++ reservedTolerationValue = "Master" |
|
| 4143 |
++ masterNodePrefix = "master" |
|
| 4144 |
++ etcSslCerts = "/etc/ssl/certs" |
|
| 4141 | 4145 |
+) |
| 4142 | 4146 |
+ |
| 4143 | 4147 |
+// Register registers a plugin. |
| ... | ... |
@@ -4157,7 +4207,8 @@ index 0000000..e33d4e9 |
| 4157 | 4157 |
+ |
| 4158 | 4158 |
+// vmwareAdmissionControllerConfig holds config data for VMwareAdmissionController. |
| 4159 | 4159 |
+type vmwareAdmissionControllerConfig struct {
|
| 4160 |
-+ PrivilegedGroup string `yaml:"privilegedGroup"` |
|
| 4160 |
++ PrivilegedGroup string `yaml:"privilegedGroup"` |
|
| 4161 |
++ PodSecurityPolicyFile string `yaml:"podSecurityPolicyFile"` |
|
| 4161 | 4162 |
+} |
| 4162 | 4163 |
+ |
| 4163 | 4164 |
+// AdmissionConfig holds config data for admission controllers. |
| ... | ... |
@@ -4186,7 +4237,7 @@ index 0000000..e33d4e9 |
| 4186 | 4186 |
+ case api.Resource("pods"):
|
| 4187 | 4187 |
+ err = validatePods(vac, a) |
| 4188 | 4188 |
+ case api.Resource("nodes"):
|
| 4189 |
-+ err = admission.NewForbidden(a, fmt.Errorf("%s validation failed: cannot modify nodes", PluginName))
|
|
| 4189 |
++ err = validateNodes(a) |
|
| 4190 | 4190 |
+ case rbac.Resource("clusterroles"):
|
| 4191 | 4191 |
+ err = validateClusterRoles(a) |
| 4192 | 4192 |
+ case rbac.Resource("clusterrolebindings"):
|
| ... | ... |
@@ -4217,8 +4268,14 @@ index 0000000..e33d4e9 |
| 4217 | 4217 |
+ return nil, err |
| 4218 | 4218 |
+ } |
| 4219 | 4219 |
+ |
| 4220 |
++ // Load PSP from file. If it fails, use default. |
|
| 4221 |
++ psp := getPSPFromFile(config.VMwareAdmissionController.PodSecurityPolicyFile) |
|
| 4222 |
++ if psp == nil {
|
|
| 4223 |
++ psp = getDefaultPSP() |
|
| 4224 |
++ } |
|
| 4225 |
++ |
|
| 4220 | 4226 |
+ return &vmwareAdmissionController{
|
| 4221 |
-+ psp: getDefaultPSP(), |
|
| 4227 |
++ psp: psp, |
|
| 4222 | 4228 |
+ strategyFactory: podsecuritypolicy.NewSimpleStrategyFactory(), |
| 4223 | 4229 |
+ privilegedGroup: config.VMwareAdmissionController.PrivilegedGroup, |
| 4224 | 4230 |
+ }, nil |
| ... | ... |
@@ -4246,6 +4303,14 @@ index 0000000..e33d4e9 |
| 4246 | 4246 |
+ "configMap", |
| 4247 | 4247 |
+ "persistentVolumeClaim", |
| 4248 | 4248 |
+ "projected", |
| 4249 |
++ "hostPath", |
|
| 4250 |
++ }, |
|
| 4251 |
++ // We allow /etc/ssl/certs to be mounted in read only mode as a hack to allow Wavefront pods to be deployed. |
|
| 4252 |
++ // TODO(ashokc): Once we have support for users to create pods using privileged mode and host path, remove this. |
|
| 4253 |
++ AllowedHostPaths: []extensions.AllowedHostPath{
|
|
| 4254 |
++ {
|
|
| 4255 |
++ etcSslCerts, |
|
| 4256 |
++ }, |
|
| 4249 | 4257 |
+ }, |
| 4250 | 4258 |
+ FSGroup: extensions.FSGroupStrategyOptions{
|
| 4251 | 4259 |
+ Rule: extensions.FSGroupStrategyRunAsAny, |
| ... | ... |
@@ -4263,6 +4328,39 @@ index 0000000..e33d4e9 |
| 4263 | 4263 |
+ } |
| 4264 | 4264 |
+} |
| 4265 | 4265 |
+ |
| 4266 |
++func getPSPFromFile(pspFile string) *extensions.PodSecurityPolicy {
|
|
| 4267 |
++ pspBeta := v1beta1.PodSecurityPolicy{}
|
|
| 4268 |
++ pspExtensions := extensions.PodSecurityPolicy{}
|
|
| 4269 |
++ |
|
| 4270 |
++ if pspFile == "" {
|
|
| 4271 |
++ glog.V(2).Infof("%s: PSP file not specified, using default PSP", PluginName)
|
|
| 4272 |
++ return nil |
|
| 4273 |
++ } |
|
| 4274 |
++ |
|
| 4275 |
++ pspConfig, err := os.Open(pspFile) |
|
| 4276 |
++ if err != nil {
|
|
| 4277 |
++ glog.V(2).Infof("%s: cannot open PSP file, using default PSP: %v", PluginName, err)
|
|
| 4278 |
++ return nil |
|
| 4279 |
++ } |
|
| 4280 |
++ |
|
| 4281 |
++ // We load the PSP that we read from file into pspBeta because this is the struct to which we can decode yaml to. |
|
| 4282 |
++ d := yaml.NewYAMLOrJSONDecoder(pspConfig, 4096) |
|
| 4283 |
++ err = d.Decode(&pspBeta) |
|
| 4284 |
++ if err != nil {
|
|
| 4285 |
++ glog.V(2).Infof("%s: cannot decode PSP file, using default PSP: %v", PluginName, err)
|
|
| 4286 |
++ return nil |
|
| 4287 |
++ } |
|
| 4288 |
++ |
|
| 4289 |
++ // We convert pspBeta object into pspExtensions object because this is the one that pod validation uses. |
|
| 4290 |
++ err = policybeta.Convert_v1beta1_PodSecurityPolicy_To_extensions_PodSecurityPolicy(&pspBeta, &pspExtensions, nil) |
|
| 4291 |
++ if err != nil {
|
|
| 4292 |
++ glog.V(2).Infof("%s: cannot convert v1beta1.PSP to extensions.PSP, using default PSP: %v", PluginName, err)
|
|
| 4293 |
++ return nil |
|
| 4294 |
++ } |
|
| 4295 |
++ |
|
| 4296 |
++ return &pspExtensions |
|
| 4297 |
++} |
|
| 4298 |
++ |
|
| 4266 | 4299 |
+func isPrivilegedUser(vac *vmwareAdmissionController, a admission.Attributes) bool {
|
| 4267 | 4300 |
+ // We need to allow the service accounts inside the privileged namespace to be able to access pods and nodes. |
| 4268 | 4301 |
+ // Node-monitor agent, Photon OS update controller and Cluster autoscaler depend on this. |
| ... | ... |
@@ -4330,6 +4428,22 @@ index 0000000..e33d4e9 |
| 4330 | 4330 |
+ return false |
| 4331 | 4331 |
+} |
| 4332 | 4332 |
+ |
| 4333 |
++func validateNodes(a admission.Attributes) error {
|
|
| 4334 |
++ // If the operation is Delete, fail. Deleting a node is not something that is useful to the user. Also, by deleting |
|
| 4335 |
++ // a node, they can potentially make their cluster useless. |
|
| 4336 |
++ if a.GetOperation() == admission.Delete {
|
|
| 4337 |
++ return admission.NewForbidden(a, fmt.Errorf("%s validation failed: cannot delete nodes", PluginName))
|
|
| 4338 |
++ } |
|
| 4339 |
++ |
|
| 4340 |
++ // If the operation is on a master node, fail. We do not want to allow the users to modify labels and taints on the |
|
| 4341 |
++ // master node because it can compromise the security of the cluster. |
|
| 4342 |
++ if strings.HasPrefix(a.GetName(), masterNodePrefix) {
|
|
| 4343 |
++ return admission.NewForbidden(a, fmt.Errorf("%s validation failed: cannot modify master nodes", PluginName))
|
|
| 4344 |
++ } |
|
| 4345 |
++ |
|
| 4346 |
++ return nil |
|
| 4347 |
++} |
|
| 4348 |
++ |
|
| 4333 | 4349 |
+func validateClusterRoles(a admission.Attributes) error {
|
| 4334 | 4350 |
+ // If the name in the request is not empty and has the reserved prefix, then fail. We will hit this during delete |
| 4335 | 4351 |
+ // and update operations on the cluster roles. If it does not have the reserved prefix, allow it. If the name is |
| ... | ... |
@@ -4418,6 +4532,12 @@ index 0000000..e33d4e9 |
| 4418 | 4418 |
+ // Validate the pod. |
| 4419 | 4419 |
+ errs = append(errs, provider.ValidatePod(pod, field.NewPath("spec", "securityContext"))...)
|
| 4420 | 4420 |
+ |
| 4421 |
++ // Validate the pod's tolerations. |
|
| 4422 |
++ fieldErr := validatePodToleration(pod) |
|
| 4423 |
++ if fieldErr != nil {
|
|
| 4424 |
++ errs = append(errs, fieldErr) |
|
| 4425 |
++ } |
|
| 4426 |
++ |
|
| 4421 | 4427 |
+ // Validate the initContainers that are part of the pod. |
| 4422 | 4428 |
+ for i := range pod.Spec.InitContainers {
|
| 4423 | 4429 |
+ err := provider.DefaultContainerSecurityContext(pod, &pod.Spec.InitContainers[i]) |
| ... | ... |
@@ -4442,6 +4562,12 @@ index 0000000..e33d4e9 |
| 4442 | 4442 |
+ field.NewPath("spec", "containers").Index(i).Child("securityContext"))...)
|
| 4443 | 4443 |
+ } |
| 4444 | 4444 |
+ |
| 4445 |
++ // Validate that /etc/ssl/certs if mounted using hostPath volume mount is readOnly. |
|
| 4446 |
++ fieldErr = validateEtcSslCertsHostPath(pod) |
|
| 4447 |
++ if fieldErr != nil {
|
|
| 4448 |
++ errs = append(errs, fieldErr) |
|
| 4449 |
++ } |
|
| 4450 |
++ |
|
| 4445 | 4451 |
+ if len(errs) > 0 {
|
| 4446 | 4452 |
+ return admission.NewForbidden(a, |
| 4447 | 4453 |
+ fmt.Errorf("%s validation failed: %v", PluginName, errs))
|
| ... | ... |
@@ -4450,6 +4576,73 @@ index 0000000..e33d4e9 |
| 4450 | 4450 |
+ return nil |
| 4451 | 4451 |
+} |
| 4452 | 4452 |
+ |
| 4453 |
++func validatePodToleration(pod *api.Pod) *field.Error {
|
|
| 4454 |
++ // Master nodes are tainted with "Dedicated=Master:NoSchedule". Only vke-system pods are allowed to tolerate |
|
| 4455 |
++ // this taint and to run on master nodes. A user's pod will be rejected if its spec has toleration for this taint. |
|
| 4456 |
++ for _, t := range pod.Spec.Tolerations {
|
|
| 4457 |
++ reject := false |
|
| 4458 |
++ |
|
| 4459 |
++ if t.Key == reservedTolerationKey && t.Value == reservedTolerationValue {
|
|
| 4460 |
++ // Reject pod that has the reserved toleration "Dedicated=Master" |
|
| 4461 |
++ reject = true |
|
| 4462 |
++ } else if t.Operator == api.TolerationOpExists && (t.Key == reservedTolerationKey || t.Key == "") {
|
|
| 4463 |
++ // Reject pod that has wildcard toleration matching the reserved toleration |
|
| 4464 |
++ reject = true |
|
| 4465 |
++ } |
|
| 4466 |
++ |
|
| 4467 |
++ if reject {
|
|
| 4468 |
++ return field.Invalid(field.NewPath("spec", "toleration"), fmt.Sprintf("%+v", t),
|
|
| 4469 |
++ fmt.Sprintf("%s validation failed: should not tolerate master node taint", PluginName))
|
|
| 4470 |
++ } |
|
| 4471 |
++ } |
|
| 4472 |
++ return nil |
|
| 4473 |
++} |
|
| 4474 |
++ |
|
| 4475 |
++// Validate that /etc/ssl/certs if mounted using hostPath volume mount is readOnly. If not, fail. |
|
| 4476 |
++// This is a hack that is needed to get Wavefront pods to work. |
|
| 4477 |
++// TODO(ashokc): Once we have support for users to create pods using privileged mode and host path, remove this. |
|
| 4478 |
++func validateEtcSslCertsHostPath(pod *api.Pod) *field.Error {
|
|
| 4479 |
++ // Get volumes which mount /etc/ssl/certs and put them in a map. |
|
| 4480 |
++ volumes := map[string]struct{}{}
|
|
| 4481 |
++ for _, vol := range pod.Spec.Volumes {
|
|
| 4482 |
++ if vol.HostPath != nil && strings.HasPrefix(vol.HostPath.Path, etcSslCerts) {
|
|
| 4483 |
++ volumes[vol.Name] = struct{}{}
|
|
| 4484 |
++ } |
|
| 4485 |
++ } |
|
| 4486 |
++ |
|
| 4487 |
++ // For every initContainer, get all volumeMounts and verify if it matches any of the volumes in the volumes map. |
|
| 4488 |
++ // If yes, then check if they are read only. If not, return an error. |
|
| 4489 |
++ err := checkVolumeReadOnly(pod.Spec.InitContainers, volumes, "initContainers") |
|
| 4490 |
++ if err != nil {
|
|
| 4491 |
++ return err |
|
| 4492 |
++ } |
|
| 4493 |
++ |
|
| 4494 |
++ // For every container, get all volumeMounts and verify if it matches any of the volumes in the volumes map. |
|
| 4495 |
++ // If yes, then check if they are read only. If not, return an error. |
|
| 4496 |
++ err = checkVolumeReadOnly(pod.Spec.Containers, volumes, "containers") |
|
| 4497 |
++ if err != nil {
|
|
| 4498 |
++ return err |
|
| 4499 |
++ } |
|
| 4500 |
++ |
|
| 4501 |
++ return nil |
|
| 4502 |
++} |
|
| 4503 |
++ |
|
| 4504 |
++// Checks if the container has a volumeMount belonging to the volumes map. If yes, it has to be read only. If not, |
|
| 4505 |
++// return error. |
|
| 4506 |
++func checkVolumeReadOnly(containers []api.Container, volumes map[string]struct{}, containerType string) *field.Error {
|
|
| 4507 |
++ for i, container := range containers {
|
|
| 4508 |
++ for _, vol := range container.VolumeMounts {
|
|
| 4509 |
++ if _, ok := volumes[vol.Name]; ok {
|
|
| 4510 |
++ if !vol.ReadOnly {
|
|
| 4511 |
++ return field.Invalid(field.NewPath("spec", containerType).Index(i).Child("volumeMounts"),
|
|
| 4512 |
++ fmt.Sprintf("%+v", vol), fmt.Sprintf("%s has to be mount as readOnly", etcSslCerts))
|
|
| 4513 |
++ } |
|
| 4514 |
++ } |
|
| 4515 |
++ } |
|
| 4516 |
++ } |
|
| 4517 |
++ return nil |
|
| 4518 |
++} |
|
| 4519 |
++ |
|
| 4453 | 4520 |
+func checkReservedPrefix(resourceName string, a admission.Attributes) error {
|
| 4454 | 4521 |
+ if strings.HasPrefix(resourceName, reservedPrefix) {
|
| 4455 | 4522 |
+ return admission.NewForbidden(a, |
| ... | ... |
@@ -4459,14 +4652,15 @@ index 0000000..e33d4e9 |
| 4459 | 4459 |
+} |
| 4460 | 4460 |
diff --git a/plugin/pkg/admission/vke/admission_test.go b/plugin/pkg/admission/vke/admission_test.go |
| 4461 | 4461 |
new file mode 100644 |
| 4462 |
-index 0000000..596b7d4 |
|
| 4462 |
+index 0000000..52c88cc |
|
| 4463 | 4463 |
--- /dev/null |
| 4464 | 4464 |
+++ b/plugin/pkg/admission/vke/admission_test.go |
| 4465 |
-@@ -0,0 +1,538 @@ |
|
| 4465 |
+@@ -0,0 +1,809 @@ |
|
| 4466 | 4466 |
+package vke |
| 4467 | 4467 |
+ |
| 4468 | 4468 |
+import ( |
| 4469 | 4469 |
+ "fmt" |
| 4470 |
++ "os" |
|
| 4470 | 4471 |
+ "strings" |
| 4471 | 4472 |
+ "testing" |
| 4472 | 4473 |
+ |
| ... | ... |
@@ -4482,6 +4676,38 @@ index 0000000..596b7d4 |
| 4482 | 4482 |
+ defaultConfigFileFormat = ` |
| 4483 | 4483 |
+vmwareAdmissionController: |
| 4484 | 4484 |
+ privilegedGroup: %s |
| 4485 |
++ podSecurityPolicyFile: %s |
|
| 4486 |
++` |
|
| 4487 |
++ pspFileName = "/tmp/psp.yaml" |
|
| 4488 |
++ pspConfigFile = ` |
|
| 4489 |
++apiVersion: extensions/v1beta1 |
|
| 4490 |
++kind: PodSecurityPolicy |
|
| 4491 |
++metadata: |
|
| 4492 |
++ name: vmware-pod-security-policy-restricted |
|
| 4493 |
++spec: |
|
| 4494 |
++ privileged: true |
|
| 4495 |
++ fsGroup: |
|
| 4496 |
++ rule: RunAsAny |
|
| 4497 |
++ runAsUser: |
|
| 4498 |
++ rule: RunAsAny |
|
| 4499 |
++ seLinux: |
|
| 4500 |
++ rule: RunAsAny |
|
| 4501 |
++ supplementalGroups: |
|
| 4502 |
++ rule: RunAsAny |
|
| 4503 |
++ volumes: |
|
| 4504 |
++ - 'emptyDir' |
|
| 4505 |
++ - 'secret' |
|
| 4506 |
++ - 'downwardAPI' |
|
| 4507 |
++ - 'configMap' |
|
| 4508 |
++ - 'persistentVolumeClaim' |
|
| 4509 |
++ - 'projected' |
|
| 4510 |
++ - 'hostPath' |
|
| 4511 |
++ hostPID: false |
|
| 4512 |
++ hostIPC: false |
|
| 4513 |
++ hostNetwork: true |
|
| 4514 |
++ hostPorts: |
|
| 4515 |
++ - min: 1 |
|
| 4516 |
++ max: 65536 |
|
| 4485 | 4517 |
+` |
| 4486 | 4518 |
+) |
| 4487 | 4519 |
+ |
| ... | ... |
@@ -4549,7 +4775,19 @@ index 0000000..596b7d4 |
| 4549 | 4549 |
+ }, |
| 4550 | 4550 |
+ "create pod with HostVolume denied": {
|
| 4551 | 4551 |
+ operation: kadmission.Create, |
| 4552 |
-+ pod: newTestPodBuilder().withHostVolume().build(), |
|
| 4552 |
++ pod: newTestPodBuilder().withHostVolume("/", false).build(),
|
|
| 4553 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4554 |
++ shouldPassValidate: false, |
|
| 4555 |
++ }, |
|
| 4556 |
++ "create pod with HostVolume /etc/ssl/certs in read-only mode allowed": {
|
|
| 4557 |
++ operation: kadmission.Create, |
|
| 4558 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", true).build(),
|
|
| 4559 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4560 |
++ shouldPassValidate: true, |
|
| 4561 |
++ }, |
|
| 4562 |
++ "create pod with HostVolume /etc/ssl/certs in read-write mode denied": {
|
|
| 4563 |
++ operation: kadmission.Create, |
|
| 4564 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", false).build(),
|
|
| 4553 | 4565 |
+ userInfo: newTestUserBuilder().build(), |
| 4554 | 4566 |
+ shouldPassValidate: false, |
| 4555 | 4567 |
+ }, |
| ... | ... |
@@ -4561,7 +4799,7 @@ index 0000000..596b7d4 |
| 4561 | 4561 |
+ }, |
| 4562 | 4562 |
+ "create pod with HostVolume and CascadeDisk denied": {
|
| 4563 | 4563 |
+ operation: kadmission.Create, |
| 4564 |
-+ pod: newTestPodBuilder().withHostVolume().withCascadeDisk().build(), |
|
| 4564 |
++ pod: newTestPodBuilder().withHostVolume("/", false).withCascadeDisk().build(),
|
|
| 4565 | 4565 |
+ userInfo: newTestUserBuilder().build(), |
| 4566 | 4566 |
+ shouldPassValidate: false, |
| 4567 | 4567 |
+ }, |
| ... | ... |
@@ -4573,15 +4811,144 @@ index 0000000..596b7d4 |
| 4573 | 4573 |
+ }, |
| 4574 | 4574 |
+ "delete pod allowed": {
|
| 4575 | 4575 |
+ operation: kadmission.Delete, |
| 4576 |
++ pod: nil, |
|
| 4577 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4578 |
++ shouldPassValidate: true, |
|
| 4579 |
++ }, |
|
| 4580 |
++ } |
|
| 4581 |
++ |
|
| 4582 |
++ for k, v := range tests {
|
|
| 4583 |
++ testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t) |
|
| 4584 |
++ } |
|
| 4585 |
++} |
|
| 4586 |
++ |
|
| 4587 |
++func TestAdmitPrivilegedWithCustomPSP(t *testing.T) {
|
|
| 4588 |
++ tests := map[string]struct {
|
|
| 4589 |
++ operation kadmission.Operation |
|
| 4590 |
++ pod *kapi.Pod |
|
| 4591 |
++ name string |
|
| 4592 |
++ userInfo user.Info |
|
| 4593 |
++ shouldPassValidate bool |
|
| 4594 |
++ }{
|
|
| 4595 |
++ "create pod with Privileged=nil allowed": {
|
|
| 4596 |
++ operation: kadmission.Create, |
|
| 4597 |
++ pod: newTestPodBuilder().build(), |
|
| 4598 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4599 |
++ shouldPassValidate: true, |
|
| 4600 |
++ }, |
|
| 4601 |
++ "create pod with Privileged=false allowed": {
|
|
| 4602 |
++ operation: kadmission.Create, |
|
| 4603 |
++ pod: newTestPodBuilder().withPrivileged(false).build(), |
|
| 4604 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4605 |
++ shouldPassValidate: true, |
|
| 4606 |
++ }, |
|
| 4607 |
++ "create pod with Privileged=true allowed": {
|
|
| 4608 |
++ operation: kadmission.Create, |
|
| 4609 |
++ pod: newTestPodBuilder().withPrivileged(true).build(), |
|
| 4610 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4611 |
++ shouldPassValidate: true, |
|
| 4612 |
++ }, |
|
| 4613 |
++ "create pod with multiple containers, one has Privileged=true allowed": {
|
|
| 4614 |
++ operation: kadmission.Create, |
|
| 4615 |
++ pod: newTestPodBuilder().withPrivileged(true).withInitContainer().withContainer().build(), |
|
| 4616 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4617 |
++ shouldPassValidate: true, |
|
| 4618 |
++ }, |
|
| 4619 |
++ "update pod with Privileged=true allowed": {
|
|
| 4620 |
++ operation: kadmission.Update, |
|
| 4621 |
++ pod: newTestPodBuilder().withPrivileged(true).build(), |
|
| 4622 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4623 |
++ shouldPassValidate: true, |
|
| 4624 |
++ }, |
|
| 4625 |
++ "create pod with HostNetwork=true allowed": {
|
|
| 4626 |
++ operation: kadmission.Create, |
|
| 4627 |
++ pod: newTestPodBuilder().withHostNetwork(true).build(), |
|
| 4628 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4629 |
++ shouldPassValidate: true, |
|
| 4630 |
++ }, |
|
| 4631 |
++ "create pod with HostIPC=true denied": {
|
|
| 4632 |
++ operation: kadmission.Create, |
|
| 4633 |
++ pod: newTestPodBuilder().withHostIPC(true).build(), |
|
| 4634 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4635 |
++ shouldPassValidate: false, |
|
| 4636 |
++ }, |
|
| 4637 |
++ "create pod with HostPID=true denied": {
|
|
| 4638 |
++ operation: kadmission.Create, |
|
| 4639 |
++ pod: newTestPodBuilder().withHostPID(true).build(), |
|
| 4640 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4641 |
++ shouldPassValidate: false, |
|
| 4642 |
++ }, |
|
| 4643 |
++ "create pod with HostPort allowed": {
|
|
| 4644 |
++ operation: kadmission.Create, |
|
| 4645 |
++ pod: newTestPodBuilder().withHostPort().build(), |
|
| 4646 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4647 |
++ shouldPassValidate: true, |
|
| 4648 |
++ }, |
|
| 4649 |
++ "create pod with HostVolume allowed": {
|
|
| 4650 |
++ operation: kadmission.Create, |
|
| 4651 |
++ pod: newTestPodBuilder().withHostVolume("/", false).build(),
|
|
| 4652 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4653 |
++ shouldPassValidate: true, |
|
| 4654 |
++ }, |
|
| 4655 |
++ "create pod with HostVolume /etc/ssl/certs in read-only mode allowed": {
|
|
| 4656 |
++ operation: kadmission.Create, |
|
| 4657 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", true).build(),
|
|
| 4658 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4659 |
++ shouldPassValidate: true, |
|
| 4660 |
++ }, |
|
| 4661 |
++ "create pod with HostVolume /etc/ssl/certs in read-write mode denied": {
|
|
| 4662 |
++ operation: kadmission.Create, |
|
| 4663 |
++ pod: newTestPodBuilder().withHostVolume("/etc/ssl/certs", false).build(),
|
|
| 4664 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4665 |
++ shouldPassValidate: false, |
|
| 4666 |
++ }, |
|
| 4667 |
++ "create pod with CascadeDisk allowed": {
|
|
| 4668 |
++ operation: kadmission.Create, |
|
| 4669 |
++ pod: newTestPodBuilder().withCascadeDisk().build(), |
|
| 4670 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4671 |
++ shouldPassValidate: true, |
|
| 4672 |
++ }, |
|
| 4673 |
++ "create pod with HostVolume and CascadeDisk allowed": {
|
|
| 4674 |
++ operation: kadmission.Create, |
|
| 4675 |
++ pod: newTestPodBuilder().withHostVolume("/", false).withCascadeDisk().build(),
|
|
| 4676 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4677 |
++ shouldPassValidate: true, |
|
| 4678 |
++ }, |
|
| 4679 |
++ "connect pod allowed": {
|
|
| 4680 |
++ operation: kadmission.Connect, |
|
| 4576 | 4681 |
+ pod: newTestPodBuilder().build(), |
| 4577 | 4682 |
+ userInfo: newTestUserBuilder().build(), |
| 4578 | 4683 |
+ shouldPassValidate: true, |
| 4579 | 4684 |
+ }, |
| 4685 |
++ "delete pod allowed": {
|
|
| 4686 |
++ operation: kadmission.Delete, |
|
| 4687 |
++ pod: nil, |
|
| 4688 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4689 |
++ shouldPassValidate: true, |
|
| 4690 |
++ }, |
|
| 4691 |
++ } |
|
| 4692 |
++ |
|
| 4693 |
++ // Setup custom PSP file. |
|
| 4694 |
++ file, err := os.Create(pspFileName) |
|
| 4695 |
++ if err != nil {
|
|
| 4696 |
++ t.Errorf("TestAdmitPrivilegedWithCustomPSP: failed to open custom PSP file %v", err)
|
|
| 4697 |
++ return |
|
| 4698 |
++ } |
|
| 4699 |
++ _, err = file.WriteString(pspConfigFile) |
|
| 4700 |
++ if err != nil {
|
|
| 4701 |
++ t.Errorf("TestAdmitPrivilegedWithCustomPSP: failed to write to custom PSP file %v", err)
|
|
| 4702 |
++ return |
|
| 4580 | 4703 |
+ } |
| 4581 | 4704 |
+ |
| 4582 | 4705 |
+ for k, v := range tests {
|
| 4583 | 4706 |
+ testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t) |
| 4584 | 4707 |
+ } |
| 4708 |
++ |
|
| 4709 |
++ // Delete custom PSP file. |
|
| 4710 |
++ err = os.Remove(pspFileName) |
|
| 4711 |
++ if err != nil {
|
|
| 4712 |
++ t.Errorf("TestAdmitPrivilegedWithCustomPSP: failed to delete custom PSP file %v", err)
|
|
| 4713 |
++ } |
|
| 4585 | 4714 |
+} |
| 4586 | 4715 |
+ |
| 4587 | 4716 |
+func TestPrivilegedNamespace(t *testing.T) {
|
| ... | ... |
@@ -4664,6 +5031,63 @@ index 0000000..596b7d4 |
| 4664 | 4664 |
+ } |
| 4665 | 4665 |
+} |
| 4666 | 4666 |
+ |
| 4667 |
++func TestToleration(t *testing.T) {
|
|
| 4668 |
++ tests := map[string]struct {
|
|
| 4669 |
++ operation kadmission.Operation |
|
| 4670 |
++ pod *kapi.Pod |
|
| 4671 |
++ name string |
|
| 4672 |
++ userInfo user.Info |
|
| 4673 |
++ shouldPassValidate bool |
|
| 4674 |
++ }{
|
|
| 4675 |
++ "allowed: create pod with no toleration": {
|
|
| 4676 |
++ operation: kadmission.Create, |
|
| 4677 |
++ pod: newTestPodBuilder().build(), |
|
| 4678 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4679 |
++ shouldPassValidate: true, |
|
| 4680 |
++ }, |
|
| 4681 |
++ "allowed: create pod with normal toleration key": {
|
|
| 4682 |
++ operation: kadmission.Create, |
|
| 4683 |
++ pod: newTestPodBuilder().withToleration("mykey", reservedTolerationValue, kapi.TolerationOpEqual, kapi.TaintEffectNoSchedule).build(),
|
|
| 4684 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4685 |
++ shouldPassValidate: true, |
|
| 4686 |
++ }, |
|
| 4687 |
++ "allowed: create pod with normal toleration value": {
|
|
| 4688 |
++ operation: kadmission.Create, |
|
| 4689 |
++ pod: newTestPodBuilder().withToleration(reservedTolerationKey, "myval", kapi.TolerationOpEqual, kapi.TaintEffectNoSchedule).build(), |
|
| 4690 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4691 |
++ shouldPassValidate: true, |
|
| 4692 |
++ }, |
|
| 4693 |
++ "denied: create pod with reserved toleration": {
|
|
| 4694 |
++ operation: kadmission.Create, |
|
| 4695 |
++ pod: newTestPodBuilder().withToleration(reservedTolerationKey, reservedTolerationValue, kapi.TolerationOpEqual, kapi.TaintEffectNoSchedule).build(), |
|
| 4696 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4697 |
++ shouldPassValidate: false, |
|
| 4698 |
++ }, |
|
| 4699 |
++ "denied: create pod with wildcard toleration": {
|
|
| 4700 |
++ operation: kadmission.Create, |
|
| 4701 |
++ pod: newTestPodBuilder().withToleration("", "", kapi.TolerationOpExists, "").build(),
|
|
| 4702 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4703 |
++ shouldPassValidate: false, |
|
| 4704 |
++ }, |
|
| 4705 |
++ "denied: create pod with value wildcard toleration": {
|
|
| 4706 |
++ operation: kadmission.Create, |
|
| 4707 |
++ pod: newTestPodBuilder().withToleration(reservedTolerationKey, "", kapi.TolerationOpExists, kapi.TaintEffectNoSchedule).build(), |
|
| 4708 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4709 |
++ shouldPassValidate: false, |
|
| 4710 |
++ }, |
|
| 4711 |
++ "allowed: create pod with value wildcard and normal key": {
|
|
| 4712 |
++ operation: kadmission.Create, |
|
| 4713 |
++ pod: newTestPodBuilder().withToleration("mykey", "", kapi.TolerationOpExists, kapi.TaintEffectNoSchedule).build(),
|
|
| 4714 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4715 |
++ shouldPassValidate: true, |
|
| 4716 |
++ }, |
|
| 4717 |
++ } |
|
| 4718 |
++ |
|
| 4719 |
++ for k, v := range tests {
|
|
| 4720 |
++ testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t) |
|
| 4721 |
++ } |
|
| 4722 |
++} |
|
| 4723 |
++ |
|
| 4667 | 4724 |
+func TestClusterLevelResources(t *testing.T) {
|
| 4668 | 4725 |
+ tests := map[string]struct {
|
| 4669 | 4726 |
+ operation kadmission.Operation |
| ... | ... |
@@ -4772,9 +5196,34 @@ index 0000000..596b7d4 |
| 4772 | 4772 |
+ userInfo: newTestUserBuilder().withName(systemUnsecuredUser).build(), |
| 4773 | 4773 |
+ shouldPassValidate: true, |
| 4774 | 4774 |
+ }, |
| 4775 |
-+ "denied: regular lightwave user update nodes": {
|
|
| 4775 |
++ "allowed: regular lightwave user update worker nodes": {
|
|
| 4776 | 4776 |
+ operation: kadmission.Update, |
| 4777 | 4777 |
+ resource: "nodes", |
| 4778 |
++ name: "worker-guid", |
|
| 4779 |
++ namespace: "", |
|
| 4780 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4781 |
++ shouldPassValidate: true, |
|
| 4782 |
++ }, |
|
| 4783 |
++ "denied: regular lightwave user update master nodes": {
|
|
| 4784 |
++ operation: kadmission.Update, |
|
| 4785 |
++ resource: "nodes", |
|
| 4786 |
++ name: "master-guid", |
|
| 4787 |
++ namespace: "", |
|
| 4788 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4789 |
++ shouldPassValidate: false, |
|
| 4790 |
++ }, |
|
| 4791 |
++ "denied: regular lightwave user delete master nodes": {
|
|
| 4792 |
++ operation: kadmission.Delete, |
|
| 4793 |
++ resource: "nodes", |
|
| 4794 |
++ name: "master-guid", |
|
| 4795 |
++ namespace: "", |
|
| 4796 |
++ userInfo: newTestUserBuilder().build(), |
|
| 4797 |
++ shouldPassValidate: false, |
|
| 4798 |
++ }, |
|
| 4799 |
++ "denied: regular lightwave user delete worker nodes": {
|
|
| 4800 |
++ operation: kadmission.Delete, |
|
| 4801 |
++ resource: "nodes", |
|
| 4802 |
++ name: "worker-guid", |
|
| 4778 | 4803 |
+ namespace: "", |
| 4779 | 4804 |
+ userInfo: newTestUserBuilder().build(), |
| 4780 | 4805 |
+ shouldPassValidate: false, |
| ... | ... |
@@ -4795,15 +5244,20 @@ index 0000000..596b7d4 |
| 4795 | 4795 |
+func testPodValidation(testCaseName string, op kadmission.Operation, pod *kapi.Pod, name string, userInfo user.Info, |
| 4796 | 4796 |
+ shouldPassValidate bool, t *testing.T) {
|
| 4797 | 4797 |
+ |
| 4798 |
-+ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup) |
|
| 4798 |
++ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName) |
|
| 4799 | 4799 |
+ configFile := strings.NewReader(defaultConfigFile) |
| 4800 | 4800 |
+ plugin, err := NewVMwareAdmissionController(configFile) |
| 4801 | 4801 |
+ if err != nil {
|
| 4802 | 4802 |
+ t.Errorf("%s: failed to create admission controller %v", testCaseName, err)
|
| 4803 | 4803 |
+ } |
| 4804 | 4804 |
+ |
| 4805 |
++ namespace := "default" |
|
| 4806 |
++ if pod != nil {
|
|
| 4807 |
++ namespace = pod.Namespace |
|
| 4808 |
++ } |
|
| 4809 |
++ |
|
| 4805 | 4810 |
+ attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"),
|
| 4806 |
-+ pod.Namespace, name, kapi.Resource("pods").WithVersion("version"), "", op, userInfo)
|
|
| 4811 |
++ namespace, name, kapi.Resource("pods").WithVersion("version"), "", op, userInfo)
|
|
| 4807 | 4812 |
+ |
| 4808 | 4813 |
+ err = plugin.Validate(attrs) |
| 4809 | 4814 |
+ if shouldPassValidate && err != nil {
|
| ... | ... |
@@ -4816,7 +5270,7 @@ index 0000000..596b7d4 |
| 4816 | 4816 |
+func testResourceValidation(testCaseName string, op kadmission.Operation, resource string, name string, |
| 4817 | 4817 |
+ namespace string, userInfo user.Info, shouldPassValidate bool, t *testing.T) {
|
| 4818 | 4818 |
+ |
| 4819 |
-+ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup) |
|
| 4819 |
++ defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName) |
|
| 4820 | 4820 |
+ configFile := strings.NewReader(defaultConfigFile) |
| 4821 | 4821 |
+ plugin, err := NewVMwareAdmissionController(configFile) |
| 4822 | 4822 |
+ if err != nil {
|
| ... | ... |
@@ -4916,19 +5370,19 @@ index 0000000..596b7d4 |
| 4916 | 4916 |
+ return p |
| 4917 | 4917 |
+} |
| 4918 | 4918 |
+ |
| 4919 |
-+func (p *testPodBuilder) withHostVolume() *testPodBuilder {
|
|
| 4919 |
++func (p *testPodBuilder) withHostVolume(hostPath string, readOnly bool) *testPodBuilder {
|
|
| 4920 | 4920 |
+ volume := kapi.Volume{
|
| 4921 | 4921 |
+ Name: "host", |
| 4922 | 4922 |
+ VolumeSource: kapi.VolumeSource{
|
| 4923 | 4923 |
+ HostPath: &kapi.HostPathVolumeSource{
|
| 4924 |
-+ Path: "/", |
|
| 4924 |
++ Path: hostPath, |
|
| 4925 | 4925 |
+ }, |
| 4926 | 4926 |
+ }, |
| 4927 | 4927 |
+ } |
| 4928 |
-+ device := kapi.VolumeDevice{Name: "host", DevicePath: "/host"}
|
|
| 4928 |
++ volumeMount := kapi.VolumeMount{Name: "host", MountPath: "/data", ReadOnly: readOnly}
|
|
| 4929 | 4929 |
+ |
| 4930 | 4930 |
+ p.pod.Spec.Volumes = append(p.pod.Spec.Volumes, volume) |
| 4931 |
-+ p.pod.Spec.Containers[0].VolumeDevices = append(p.pod.Spec.Containers[0].VolumeDevices, device) |
|
| 4931 |
++ p.pod.Spec.Containers[0].VolumeMounts = append(p.pod.Spec.Containers[0].VolumeMounts, volumeMount) |
|
| 4932 | 4932 |
+ return p |
| 4933 | 4933 |
+} |
| 4934 | 4934 |
+ |
| ... | ... |
@@ -4969,6 +5423,16 @@ index 0000000..596b7d4 |
| 4969 | 4969 |
+ return p |
| 4970 | 4970 |
+} |
| 4971 | 4971 |
+ |
| 4972 |
++func (p *testPodBuilder) withToleration(key, value string, operator kapi.TolerationOperator, effect kapi.TaintEffect) *testPodBuilder {
|
|
| 4973 |
++ p.pod.Spec.Tolerations = append(p.pod.Spec.Tolerations, kapi.Toleration{
|
|
| 4974 |
++ Key: key, |
|
| 4975 |
++ Value: value, |
|
| 4976 |
++ Operator: operator, |
|
| 4977 |
++ Effect: effect, |
|
| 4978 |
++ }) |
|
| 4979 |
++ return p |
|
| 4980 |
++} |
|
| 4981 |
++ |
|
| 4972 | 4982 |
+// testUserBuilder |
| 4973 | 4983 |
+type testUserBuilder struct {
|
| 4974 | 4984 |
+ user *user.DefaultInfo |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
Summary: Kubernetes cluster management |
| 2 | 2 |
Name: kubernetes |
| 3 | 3 |
Version: 1.10.2 |
| 4 |
-Release: 6%{?dist}
|
|
| 4 |
+Release: 7%{?dist}
|
|
| 5 | 5 |
License: ASL 2.0 |
| 6 | 6 |
URL: https://github.com/kubernetes/kubernetes/archive/v%{version}.tar.gz
|
| 7 | 7 |
Source0: kubernetes-%{version}.tar.gz
|
| ... | ... |
@@ -207,6 +207,8 @@ fi |
| 207 | 207 |
/opt/vmware/kubernetes/windows/amd64/kubectl.exe |
| 208 | 208 |
|
| 209 | 209 |
%changelog |
| 210 |
+* Tue Jun 19 2018 Bo Gan <ganb@vmware.com> 1.10.2-7 |
|
| 211 |
+- Update vke patch (da5bd0d) |
|
| 210 | 212 |
* Sat Jun 09 2018 Bo Gan <ganb@vmware.com> 1.10.2-6 |
| 211 | 213 |
- Update vke patch (d06c534) |
| 212 | 214 |
* Fri Jun 08 2018 Bo Gan <ganb@vmware.com> 1.10.2-5 |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
Summary: Kubernetes cluster management |
| 2 | 2 |
Name: kubernetes |
| 3 | 3 |
Version: 1.9.6 |
| 4 |
-Release: 5%{?dist}
|
|
| 4 |
+Release: 6%{?dist}
|
|
| 5 | 5 |
License: ASL 2.0 |
| 6 | 6 |
URL: https://github.com/kubernetes/kubernetes/archive/v%{version}.tar.gz
|
| 7 | 7 |
Source0: kubernetes-v%{version}.tar.gz
|
| ... | ... |
@@ -185,6 +185,8 @@ fi |
| 185 | 185 |
%{_bindir}/pause-amd64
|
| 186 | 186 |
|
| 187 | 187 |
%changelog |
| 188 |
+* Tue Jun 19 2018 Bo Gan <ganb@vmware.com> 1.9.6-6 |
|
| 189 |
+- Update vke patch (da5bd0d) |
|
| 188 | 190 |
* Sat Jun 09 2018 Bo Gan <ganb@vmware.com> 1.9.6-5 |
| 189 | 191 |
- Update vke patch (d06c534) |
| 190 | 192 |
* Fri Jun 08 2018 Bo Gan <ganb@vmware.com> 1.9.6-4 |