Skip to content

Commit

Permalink
Add pvc support (knative#12458)
Browse files Browse the repository at this point in the history
* add pvc support

* fix header

* fix fmt

* simplify implementation

* fixes

* fix msg

* improve test names

* nits

* more fixes
  • Loading branch information
skonto committed Mar 10, 2022
1 parent af93da7 commit 3a8cb21
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 24 deletions.
12 changes: 11 additions & 1 deletion config/core/configmaps/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ metadata:
app.kubernetes.io/version: devel
serving.knative.dev/release: devel
annotations:
knative.dev/example-checksum: "2897f625"
knative.dev/example-checksum: "d9e300ba"
data:
_example: |-
################################
Expand Down Expand Up @@ -149,3 +149,13 @@ data:
# 1. Enabled: enabling init containers support
# 2. Disabled: disabling init containers support
kubernetes.podspec-init-containers: "disabled"
# Controls whether persistent volume claim support is enabled or not.
# 1. Enabled: enabling persistent volume claim support
# 2. Disabled: disabling persistent volume claim support
kubernetes.podspec-persistent-volume-claim: "disabled"
# Controls whether write access for persistent volumes is enabled or not.
# 1. Enabled: enabling write access for persistent volumes
# 2. Disabled: disabling write access for persistent volumes
kubernetes.podspec-persistent-volume-write: "disabled"
6 changes: 6 additions & 0 deletions pkg/apis/config/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ func defaultFeaturesConfig() *Features {
ContainerSpecAddCapabilities: Disabled,
PodSpecTolerations: Disabled,
PodSpecVolumesEmptyDir: Disabled,
PodSpecPersistentVolumeClaim: Disabled,
PodSpecPersistentVolumeWrite: Disabled,
PodSpecInitContainers: Disabled,
TagHeaderBasedRouting: Disabled,
AutoDetectHTTP2: Disabled,
Expand All @@ -79,6 +81,8 @@ func NewFeaturesConfigFromMap(data map[string]string) (*Features, error) {
asFlag("kubernetes.podspec-tolerations", &nc.PodSpecTolerations),
asFlag("kubernetes.podspec-volumes-emptydir", &nc.PodSpecVolumesEmptyDir),
asFlag("kubernetes.podspec-init-containers", &nc.PodSpecInitContainers),
asFlag("kubernetes.podspec-persistent-volume-claim", &nc.PodSpecPersistentVolumeClaim),
asFlag("kubernetes.podspec-persistent-volume-write", &nc.PodSpecPersistentVolumeWrite),
asFlag("tag-header-based-routing", &nc.TagHeaderBasedRouting),
asFlag("autodetect-http2", &nc.AutoDetectHTTP2)); err != nil {
return nil, err
Expand Down Expand Up @@ -107,6 +111,8 @@ type Features struct {
PodSpecTolerations Flag
PodSpecVolumesEmptyDir Flag
PodSpecInitContainers Flag
PodSpecPersistentVolumeClaim Flag
PodSpecPersistentVolumeWrite Flag
TagHeaderBasedRouting Flag
AutoDetectHTTP2 Flag
}
Expand Down
36 changes: 36 additions & 0 deletions pkg/apis/config/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,42 @@ func TestFeaturesConfiguration(t *testing.T) {
data: map[string]string{
"kubernetes.podspec-volumes-emptydir": "Enabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeClaim: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-claim": "Disabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeClaim: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-claim": "Enabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-write Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeWrite: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-write": "Disabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeWrite: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-write": "Enabled",
},
}, {
name: "kubernetes.podspec-init-containers Disabled",
wantErr: false,
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/serving/fieldmask.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ func VolumeMask(ctx context.Context, in *corev1.Volume) *corev1.Volume {
out.EmptyDir = in.EmptyDir
}

if cfg.Features.PodSpecPersistentVolumeClaim != config.Disabled {
out.PersistentVolumeClaim = in.PersistentVolumeClaim
}

return out
}

Expand All @@ -67,6 +71,10 @@ func VolumeSourceMask(ctx context.Context, in *corev1.VolumeSource) *corev1.Volu
out.EmptyDir = in.EmptyDir
}

if cfg.Features.PodSpecPersistentVolumeClaim != config.Disabled {
out.PersistentVolumeClaim = in.PersistentVolumeClaim
}

// Too many disallowed fields to list

return out
Expand Down
56 changes: 47 additions & 9 deletions pkg/apis/serving/k8s_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,7 @@ var (
func ValidateVolumes(ctx context.Context, vs []corev1.Volume, mountedVolumes sets.String) (map[string]corev1.Volume, *apis.FieldError) {
volumes := make(map[string]corev1.Volume, len(vs))
var errs *apis.FieldError
features := config.FromContextOrDefaults(ctx).Features
for i, volume := range vs {
if volume.EmptyDir != nil && features.PodSpecVolumesEmptyDir != config.Enabled {
errs = errs.Also((&apis.FieldError{Message: fmt.Sprintf("EmptyDir volume support is off, "+
"but found EmptyDir volume %s", volume.Name)}).ViaIndex(i))
}
if _, ok := volumes[volume.Name]; ok {
errs = errs.Also((&apis.FieldError{
Message: fmt.Sprintf("duplicate volume name %q", volume.Name),
Expand All @@ -109,14 +104,36 @@ func ValidateVolumes(ctx context.Context, vs []corev1.Volume, mountedVolumes set
return volumes, errs
}

func validatePersistentVolumeClaims(volume corev1.VolumeSource, features *config.Features) *apis.FieldError {
var errs *apis.FieldError
if volume.PersistentVolumeClaim == nil {
return nil
}
if features.PodSpecPersistentVolumeClaim != config.Enabled {
errs = errs.Also(&apis.FieldError{Message: fmt.Sprintf("Persistent volume claim support is disabled, "+
"but found persistent volume claim %s", volume.PersistentVolumeClaim.ClaimName)})
}
isWriteEnabled := features.PodSpecPersistentVolumeWrite == config.Enabled
if !volume.PersistentVolumeClaim.ReadOnly && !isWriteEnabled {
errs = errs.Also(&apis.FieldError{Message: fmt.Sprintf("Persistent volume write support is disabled, "+
"but found persistent volume claim %s that is not read-only", volume.PersistentVolumeClaim.ClaimName)})
}
return errs
}

func validateVolume(ctx context.Context, volume corev1.Volume) *apis.FieldError {
errs := apis.CheckDisallowedFields(volume, *VolumeMask(ctx, &volume))
features := config.FromContextOrDefaults(ctx).Features
errs := validatePersistentVolumeClaims(volume.VolumeSource, features)
if volume.EmptyDir != nil && features.PodSpecVolumesEmptyDir != config.Enabled {
errs = errs.Also(&apis.FieldError{Message: fmt.Sprintf("EmptyDir volume support is disabled, "+
"but found EmptyDir volume %s", volume.Name)})
}
errs = errs.Also(apis.CheckDisallowedFields(volume, *VolumeMask(ctx, &volume)))
if volume.Name == "" {
errs = apis.ErrMissingField("name")
} else if len(validation.IsDNS1123Label(volume.Name)) != 0 {
errs = apis.ErrInvalidValue(volume.Name, "name")
}

vs := volume.VolumeSource
errs = errs.Also(apis.CheckDisallowedFields(vs, *VolumeSourceMask(ctx, &vs)))
var specified []string
Expand All @@ -142,12 +159,20 @@ func validateVolume(ctx context.Context, volume corev1.Volume) *apis.FieldError
specified = append(specified, "emptyDir")
errs = errs.Also(validateEmptyDirFields(vs.EmptyDir).ViaField("emptyDir"))
}

if vs.PersistentVolumeClaim != nil {
specified = append(specified, "persistentVolumeClaim")
}

if len(specified) == 0 {
fieldPaths := []string{"secret", "configMap", "projected"}
cfg := config.FromContextOrDefaults(ctx)
if cfg.Features.PodSpecVolumesEmptyDir == config.Enabled {
fieldPaths = append(fieldPaths, "emptyDir")
}
if cfg.Features.PodSpecPersistentVolumeClaim == config.Enabled {
fieldPaths = append(fieldPaths, "persistentVolumeClaim")
}
errs = errs.Also(apis.ErrMissingOneOf(fieldPaths...))
} else if len(specified) > 1 {
errs = errs.Also(apis.ErrMultipleOneOf(specified...))
Expand Down Expand Up @@ -597,8 +622,21 @@ func validateVolumeMounts(mounts []corev1.VolumeMount, volumes map[string]corev1
}
seenMountPath.Insert(filepath.Clean(vm.MountPath))

if volumes[vm.Name].EmptyDir == nil && !vm.ReadOnly {
errs = errs.Also(apis.ErrMissingField("readOnly").ViaIndex(i))
shouldCheckReadOnlyVolume := volumes[vm.Name].EmptyDir == nil && volumes[vm.Name].PersistentVolumeClaim == nil
if shouldCheckReadOnlyVolume && !vm.ReadOnly {
errs = errs.Also((&apis.FieldError{
Message: "volume mount should be readOnly for this type of volume",
Paths: []string{"readOnly"},
}).ViaIndex(i))
}

if volumes[vm.Name].PersistentVolumeClaim != nil {
if volumes[vm.Name].PersistentVolumeClaim.ReadOnly && !vm.ReadOnly {
errs = errs.Also((&apis.FieldError{
Message: "volume is readOnly but volume mount is not",
Paths: []string{"readOnly"},
}).ViaIndex(i))
}
}
}
return errs
Expand Down
Loading

0 comments on commit 3a8cb21

Please sign in to comment.