Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generic ephemeral volume GA #105609

Merged
merged 4 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 8 additions & 12 deletions cmd/kube-controller-manager/app/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,18 +368,15 @@ func startVolumeExpandController(ctx context.Context, controllerContext Controll
}

func startEphemeralVolumeController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
if utilfeature.DefaultFeatureGate.Enabled(features.GenericEphemeralVolume) {
ephemeralController, err := ephemeral.NewController(
controllerContext.ClientBuilder.ClientOrDie("ephemeral-volume-controller"),
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims())
if err != nil {
return nil, true, fmt.Errorf("failed to start ephemeral volume controller: %v", err)
}
go ephemeralController.Run(int(controllerContext.ComponentConfig.EphemeralVolumeController.ConcurrentEphemeralVolumeSyncs), ctx.Done())
return nil, true, nil
ephemeralController, err := ephemeral.NewController(
controllerContext.ClientBuilder.ClientOrDie("ephemeral-volume-controller"),
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims())
if err != nil {
return nil, true, fmt.Errorf("failed to start ephemeral volume controller: %v", err)
}
return nil, false, nil
go ephemeralController.Run(int(controllerContext.ComponentConfig.EphemeralVolumeController.ConcurrentEphemeralVolumeSyncs), ctx.Done())
return nil, true, nil
}

func startEndpointController(ctx context.Context, controllerCtx ControllerContext) (controller.Interface, bool, error) {
Expand Down Expand Up @@ -554,7 +551,6 @@ func startPVCProtectionController(ctx context.Context, controllerContext Control
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.ClientBuilder.ClientOrDie("pvc-protection-controller"),
utilfeature.DefaultFeatureGate.Enabled(features.StorageObjectInUseProtection),
utilfeature.DefaultFeatureGate.Enabled(features.GenericEphemeralVolume),
)
if err != nil {
return nil, true, fmt.Errorf("failed to start the pvc protection controller: %v", err)
Expand Down
24 changes: 0 additions & 24 deletions pkg/api/pod/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ func dropDisabledFields(
dropDisabledProcMountField(podSpec, oldPodSpec)

dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec)
dropDisabledEphemeralVolumeSourceAlphaFields(podSpec, oldPodSpec)

if !utilfeature.DefaultFeatureGate.Enabled(features.NonPreemptingPriority) &&
!podPriorityInUse(oldPodSpec) {
Expand Down Expand Up @@ -605,16 +604,6 @@ func dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec *api.PodSpec) {
}
}

// dropDisabledEphemeralVolumeSourceAlphaFields removes disabled alpha fields from []EphemeralVolumeSource.
// This should be called from PrepareForCreate/PrepareForUpdate for all pod specs resources containing a EphemeralVolumeSource
func dropDisabledEphemeralVolumeSourceAlphaFields(podSpec, oldPodSpec *api.PodSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.GenericEphemeralVolume) && !ephemeralInUse(oldPodSpec) {
for i := range podSpec.Volumes {
podSpec.Volumes[i].Ephemeral = nil
}
}
}

func dropPodAffinityTermNamespaceSelector(terms []api.PodAffinityTerm) {
for i := range terms {
terms[i].NamespaceSelector = nil
Expand Down Expand Up @@ -795,19 +784,6 @@ func csiInUse(podSpec *api.PodSpec) bool {
return false
}

// ephemeralInUse returns true if any pod's spec include inline CSI volumes.
func ephemeralInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
return false
}
for i := range podSpec.Volumes {
if podSpec.Volumes[i].Ephemeral != nil {
return true
}
}
return false
}

// SeccompAnnotationForField takes a pod seccomp profile field and returns the
// converted annotation value
func SeccompAnnotationForField(field *api.SeccompProfile) string {
Expand Down
3 changes: 0 additions & 3 deletions pkg/apis/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,6 @@ type VolumeSource struct {
// A pod can use both types of ephemeral volumes and
// persistent volumes at the same time.
//
// This is a beta feature and only available when the GenericEphemeralVolume
// feature gate is enabled.
//
// +optional
Ephemeral *EphemeralVolumeSource
}
Expand Down
27 changes: 8 additions & 19 deletions pkg/apis/policy/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"regexp"
"strings"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
Expand Down Expand Up @@ -92,34 +92,27 @@ func ValidatePodDisruptionBudgetStatusUpdate(status, oldStatus policy.PodDisrupt
// trailing dashes are allowed.
var ValidatePodSecurityPolicyName = apimachineryvalidation.NameIsDNSSubdomain

// PodSecurityPolicyValidationOptions contains additional parameters for ValidatePodSecurityPolicy.
type PodSecurityPolicyValidationOptions struct {
// AllowEphemeralVolumeType determines whether Ephemeral is a valid entry
// in PodSecurityPolicySpec.Volumes.
AllowEphemeralVolumeType bool
}

// ValidatePodSecurityPolicy validates a PodSecurityPolicy and returns an ErrorList
// with any errors.
func ValidatePodSecurityPolicy(psp *policy.PodSecurityPolicy, opts PodSecurityPolicyValidationOptions) field.ErrorList {
func ValidatePodSecurityPolicy(psp *policy.PodSecurityPolicy) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...)
allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(psp.Annotations, field.NewPath("metadata").Child("annotations"))...)
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, opts, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...)
return allErrs
}

// ValidatePodSecurityPolicySpec validates a PodSecurityPolicySpec and returns an ErrorList
// with any errors.
func ValidatePodSecurityPolicySpec(spec *policy.PodSecurityPolicySpec, opts PodSecurityPolicyValidationOptions, fldPath *field.Path) field.ErrorList {
func ValidatePodSecurityPolicySpec(spec *policy.PodSecurityPolicySpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...)
allErrs = append(allErrs, validatePSPRunAsGroup(fldPath.Child("runAsGroup"), spec.RunAsGroup)...)
allErrs = append(allErrs, validatePSPSELinux(fldPath.Child("seLinux"), &spec.SELinux)...)
allErrs = append(allErrs, validatePSPSupplementalGroup(fldPath.Child("supplementalGroups"), &spec.SupplementalGroups)...)
allErrs = append(allErrs, validatePSPFSGroup(fldPath.Child("fsGroup"), &spec.FSGroup)...)
allErrs = append(allErrs, validatePodSecurityPolicyVolumes(opts, fldPath, spec.Volumes)...)
allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...)
if len(spec.RequiredDropCapabilities) > 0 && hasCap(policy.AllowAllCapabilities, spec.AllowedCapabilities) {
allErrs = append(allErrs, field.Invalid(field.NewPath("requiredDropCapabilities"), spec.RequiredDropCapabilities,
"must be empty when all capabilities are allowed by a wildcard"))
Expand Down Expand Up @@ -327,15 +320,11 @@ func validatePSPSupplementalGroup(fldPath *field.Path, groupOptions *policy.Supp
}

// validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy.
func validatePodSecurityPolicyVolumes(opts PodSecurityPolicyValidationOptions, fldPath *field.Path, volumes []policy.FSType) field.ErrorList {
func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []policy.FSType) field.ErrorList {
allErrs := field.ErrorList{}
allowed := psputil.GetAllFSTypesAsSet()
// add in the * value since that is a pseudo type that is not included by default
allowed.Insert(string(policy.All))
// Ephemeral may or may not be allowed.
if !opts.AllowEphemeralVolumeType {
allowed.Delete(string(policy.Ephemeral))
}
for _, v := range volumes {
if !allowed.Has(string(v)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
Expand Down Expand Up @@ -530,11 +519,11 @@ func validateRuntimeClassStrategy(fldPath *field.Path, rc *policy.RuntimeClassSt
}

// ValidatePodSecurityPolicyUpdate validates a PSP for updates.
func ValidatePodSecurityPolicyUpdate(old *policy.PodSecurityPolicy, new *policy.PodSecurityPolicy, opts PodSecurityPolicyValidationOptions) field.ErrorList {
func ValidatePodSecurityPolicyUpdate(old *policy.PodSecurityPolicy, new *policy.PodSecurityPolicy) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&new.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(new.Annotations, field.NewPath("metadata").Child("annotations"))...)
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, opts, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, field.NewPath("spec"))...)
return allErrs
}

Expand Down
57 changes: 24 additions & 33 deletions pkg/apis/policy/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"time"

"github.com/stretchr/testify/assert"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -590,7 +590,7 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
}

for k, v := range errorCases {
errs := ValidatePodSecurityPolicy(v.psp, PodSecurityPolicyValidationOptions{})
errs := ValidatePodSecurityPolicy(v.psp)
if len(errs) == 0 {
t.Errorf("%s expected errors but got none", k)
continue
Expand All @@ -613,7 +613,7 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
// Should not be able to update to an invalid policy.
for k, v := range errorCases {
v.psp.ResourceVersion = "444" // Required for updates.
errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp, PodSecurityPolicyValidationOptions{})
errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp)
if len(errs) == 0 {
t.Errorf("[%s] expected update errors but got none", k)
continue
Expand Down Expand Up @@ -743,13 +743,13 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
}

for k, v := range successCases {
if errs := ValidatePodSecurityPolicy(v.psp, PodSecurityPolicyValidationOptions{}); len(errs) != 0 {
if errs := ValidatePodSecurityPolicy(v.psp); len(errs) != 0 {
t.Errorf("Expected success for %s, got %v", k, errs)
}

// Should be able to update to a valid PSP.
v.psp.ResourceVersion = "444" // Required for updates.
if errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp, PodSecurityPolicyValidationOptions{}); len(errs) != 0 {
if errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp); len(errs) != 0 {
t.Errorf("Expected success for %s update, got %v", k, errs)
}
}
Expand Down Expand Up @@ -786,7 +786,7 @@ func TestValidatePSPVolumes(t *testing.T) {
for _, strVolume := range volumes.List() {
psp := validPSP()
psp.Spec.Volumes = []policy.FSType{policy.FSType(strVolume)}
errs := ValidatePodSecurityPolicy(psp, PodSecurityPolicyValidationOptions{AllowEphemeralVolumeType: true})
errs := ValidatePodSecurityPolicy(psp)
if len(errs) != 0 {
t.Errorf("%s validation expected no errors but received %v", strVolume, errs)
}
Expand Down Expand Up @@ -1118,34 +1118,25 @@ func TestAllowEphemeralVolumeType(t *testing.T) {
},
}

for _, allowed := range []bool{true, false} {
for _, oldPSPInfo := range pspInfo {
for _, newPSPInfo := range pspInfo {
oldPSP := oldPSPInfo.psp()
newPSP := newPSPInfo.psp()
if newPSP == nil {
continue
}

t.Run(fmt.Sprintf("feature enabled=%v, old PodSecurityPolicySpec %v, new PodSecurityPolicySpec %v", allowed, oldPSPInfo.description, newPSPInfo.description), func(t *testing.T) {
opts := PodSecurityPolicyValidationOptions{
AllowEphemeralVolumeType: allowed,
}
var errs field.ErrorList
expectErrors := newPSPInfo.hasGenericVolume && !allowed
if oldPSP == nil {
errs = ValidatePodSecurityPolicy(newPSP, opts)
} else {
errs = ValidatePodSecurityPolicyUpdate(oldPSP, newPSP, opts)
}
if expectErrors && len(errs) == 0 {
t.Error("expected errors, got none")
}
if !expectErrors && len(errs) > 0 {
t.Errorf("expected no errors, got: %v", errs)
}
})
for _, oldPSPInfo := range pspInfo {
for _, newPSPInfo := range pspInfo {
oldPSP := oldPSPInfo.psp()
newPSP := newPSPInfo.psp()
if newPSP == nil {
continue
}

t.Run(fmt.Sprintf("old PodSecurityPolicySpec %v, new PodSecurityPolicySpec %v", oldPSPInfo.description, newPSPInfo.description), func(t *testing.T) {
var errs field.ErrorList
if oldPSP == nil {
errs = ValidatePodSecurityPolicy(newPSP)
} else {
errs = ValidatePodSecurityPolicyUpdate(oldPSP, newPSP)
}
if len(errs) > 0 {
t.Errorf("expected no errors, got: %v", errs)
}
})
}
}
}
5 changes: 2 additions & 3 deletions pkg/controller/volume/attachdetach/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,9 @@ func CreateVolumeSpec(podVolume v1.Volume, pod *v1.Pod, nodeName types.NodeName,
claimName = pvcSource.ClaimName
readOnly = pvcSource.ReadOnly
}
isEphemeral := false
if ephemeralSource := podVolume.VolumeSource.Ephemeral; ephemeralSource != nil && utilfeature.DefaultFeatureGate.Enabled(features.GenericEphemeralVolume) {
isEphemeral := podVolume.VolumeSource.Ephemeral != nil
if isEphemeral {
claimName = ephemeral.VolumeClaimName(pod, &podVolume)
isEphemeral = true
}
if claimName != "" {
klog.V(10).Infof(
Expand Down
17 changes: 7 additions & 10 deletions pkg/controller/volume/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ import (
"fmt"

v1 "k8s.io/api/core/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/features"
"k8s.io/component-helpers/storage/ephemeral"
)

const (
Expand All @@ -31,9 +30,9 @@ const (
)

// PodPVCIndexFunc creates an index function that returns PVC keys (=
// namespace/name) for given pod. If enabled, this includes the PVCs
// namespace/name) for given pod. This includes the PVCs
// that might be created for generic ephemeral volumes.
func PodPVCIndexFunc(genericEphemeralVolumeFeatureEnabled bool) func(obj interface{}) ([]string, error) {
func PodPVCIndexFunc() func(obj interface{}) ([]string, error) {
return func(obj interface{}) ([]string, error) {
pod, ok := obj.(*v1.Pod)
if !ok {
Expand All @@ -44,9 +43,8 @@ func PodPVCIndexFunc(genericEphemeralVolumeFeatureEnabled bool) func(obj interfa
claimName := ""
if pvcSource := podVolume.VolumeSource.PersistentVolumeClaim; pvcSource != nil {
claimName = pvcSource.ClaimName
}
if ephemeralSource := podVolume.VolumeSource.Ephemeral; genericEphemeralVolumeFeatureEnabled && ephemeralSource != nil {
claimName = pod.Name + "-" + podVolume.Name
} else if podVolume.VolumeSource.Ephemeral != nil {
claimName = ephemeral.VolumeClaimName(pod, &podVolume)
}
if claimName != "" {
keys = append(keys, fmt.Sprintf("%s/%s", pod.Namespace, claimName))
Expand All @@ -56,10 +54,9 @@ func PodPVCIndexFunc(genericEphemeralVolumeFeatureEnabled bool) func(obj interfa
}
}

// AddPodPVCIndexerIfNotPresent adds the PodPVCIndexFunc with the current global setting for GenericEphemeralVolume.
// AddPodPVCIndexerIfNotPresent adds the PodPVCIndexFunc.
func AddPodPVCIndexerIfNotPresent(indexer cache.Indexer) error {
return AddIndexerIfNotPresent(indexer, PodPVCIndex,
PodPVCIndexFunc(utilfeature.DefaultFeatureGate.Enabled(features.GenericEphemeralVolume)))
return AddIndexerIfNotPresent(indexer, PodPVCIndex, PodPVCIndexFunc())
}

// AddIndexerIfNotPresent adds the index function with the name into the cache indexer if not present
Expand Down
Loading