diff --git a/api/v1alpha/runtimeclass_types.go b/api/v1alpha/runtimeclass_types.go index 76d34ddb..868b796e 100644 --- a/api/v1alpha/runtimeclass_types.go +++ b/api/v1alpha/runtimeclass_types.go @@ -30,7 +30,7 @@ type RuntimeClassControllerName string // decline. Enumerating the features keeps a class from declaring one that no // provider can interpret. // -// +kubebuilder:validation:Enum=sandboxRuntime;virtualMachineRuntime;configMapVolumes;secretVolumes;diskVolumes;deviceVolumeAttachments;envFrom;imagePullSecrets;containerCapabilities;sandboxSysctls;exec;gracefulTermination +// +kubebuilder:validation:Enum=sandboxRuntime;virtualMachineRuntime;configMapVolumes;secretVolumes;diskVolumes;deviceVolumeAttachments;envFrom;imagePullSecrets;containerCapabilities;sandboxSysctls;exec;execTerminal;gracefulTermination type RuntimeClassFeature string const ( @@ -80,10 +80,13 @@ const ( RuntimeClassFeatureSandboxSysctls RuntimeClassFeature = "sandboxSysctls" // RuntimeClassFeatureExec is the ability to run a command in a running - // instance's container through an InstanceConsoleSession, such as an - // interactive shell. + // instance's container through an InstanceConsoleSession. RuntimeClassFeatureExec RuntimeClassFeature = "exec" + // RuntimeClassFeatureExecTerminal is the ability to give a shell session's + // command an interactive terminal. A class declaring it also declares exec. + RuntimeClassFeatureExecTerminal RuntimeClassFeature = "execTerminal" + // RuntimeClassFeatureGracefulTermination is the ability to signal an // instance's processes when it stops and wait up to the instance's // termination grace period for them to exit. @@ -105,6 +108,7 @@ var runtimeClassFeatureDescriptions = map[RuntimeClassFeature]string{ RuntimeClassFeatureContainerCapabilities: "container capability requests", RuntimeClassFeatureSandboxSysctls: "sandbox sysctls", RuntimeClassFeatureExec: "shell sessions into running instances", + RuntimeClassFeatureExecTerminal: "interactive terminals in shell sessions", RuntimeClassFeatureGracefulTermination: "termination grace periods", } @@ -195,6 +199,7 @@ type RuntimeClassIsolation struct { // +kubebuilder:validation:XValidation:message="supportedSysctls requires the sandboxSysctls feature",rule="!has(self.supportedSysctls) || size(self.supportedSysctls) == 0 || (has(self.features) && 'sandboxSysctls' in self.features)" // +kubebuilder:validation:XValidation:message="the sandboxSysctls feature requires a non-empty supportedSysctls",rule="!has(self.features) || !('sandboxSysctls' in self.features) || (has(self.supportedSysctls) && size(self.supportedSysctls) > 0)" // +kubebuilder:validation:XValidation:message="the sandboxSysctls feature requires the sandboxRuntime feature",rule="!has(self.features) || !('sandboxSysctls' in self.features) || 'sandboxRuntime' in self.features" +// +kubebuilder:validation:XValidation:message="the execTerminal feature requires the exec feature",rule="!has(self.features) || !('execTerminal' in self.features) || 'exec' in self.features" type RuntimeClassCapabilities struct { // The optional parts of the instance API this class serves. Anything absent // is unsupported, so a class that omits a feature rejects requests for it diff --git a/config/base/crd/bases/compute.datumapis.com_runtimeclasses.yaml b/config/base/crd/bases/compute.datumapis.com_runtimeclasses.yaml index 4010e168..ebcf6a93 100644 --- a/config/base/crd/bases/compute.datumapis.com_runtimeclasses.yaml +++ b/config/base/crd/bases/compute.datumapis.com_runtimeclasses.yaml @@ -116,6 +116,7 @@ spec: - containerCapabilities - sandboxSysctls - exec + - execTerminal - gracefulTermination type: string maxItems: 32 @@ -221,6 +222,9 @@ spec: feature rule: '!has(self.features) || !(''sandboxSysctls'' in self.features) || ''sandboxRuntime'' in self.features' + - message: the execTerminal feature requires the exec feature + rule: '!has(self.features) || !(''execTerminal'' in self.features) + || ''exec'' in self.features' controllerName: description: |- The controller that implements this class. A provider watches for classes diff --git a/docs/api/runtimeclasses.md b/docs/api/runtimeclasses.md index 5d85a3c6..d8ec7564 100644 --- a/docs/api/runtimeclasses.md +++ b/docs/api/runtimeclasses.md @@ -104,7 +104,7 @@ Spec is the published contract for this execution tier. What this class can serve, and what it cannot.

- Validations:
  • !has(self.grantableCapabilities) || size(self.grantableCapabilities) == 0 || (has(self.features) && 'containerCapabilities' in self.features): grantableCapabilities requires the containerCapabilities feature
  • !has(self.features) || !('containerCapabilities' in self.features) || (has(self.grantableCapabilities) && size(self.grantableCapabilities) > 0): the containerCapabilities feature requires a non-empty grantableCapabilities
  • !has(self.supportedSysctls) || size(self.supportedSysctls) == 0 || (has(self.features) && 'sandboxSysctls' in self.features): supportedSysctls requires the sandboxSysctls feature
  • !has(self.features) || !('sandboxSysctls' in self.features) || (has(self.supportedSysctls) && size(self.supportedSysctls) > 0): the sandboxSysctls feature requires a non-empty supportedSysctls
  • !has(self.features) || !('sandboxSysctls' in self.features) || 'sandboxRuntime' in self.features: the sandboxSysctls feature requires the sandboxRuntime feature
  • + Validations:
  • !has(self.grantableCapabilities) || size(self.grantableCapabilities) == 0 || (has(self.features) && 'containerCapabilities' in self.features): grantableCapabilities requires the containerCapabilities feature
  • !has(self.features) || !('containerCapabilities' in self.features) || (has(self.grantableCapabilities) && size(self.grantableCapabilities) > 0): the containerCapabilities feature requires a non-empty grantableCapabilities
  • !has(self.supportedSysctls) || size(self.supportedSysctls) == 0 || (has(self.features) && 'sandboxSysctls' in self.features): supportedSysctls requires the sandboxSysctls feature
  • !has(self.features) || !('sandboxSysctls' in self.features) || (has(self.supportedSysctls) && size(self.supportedSysctls) > 0): the sandboxSysctls feature requires a non-empty supportedSysctls
  • !has(self.features) || !('sandboxSysctls' in self.features) || 'sandboxRuntime' in self.features: the sandboxSysctls feature requires the sandboxRuntime feature
  • !has(self.features) || !('execTerminal' in self.features) || 'exec' in self.features: the execTerminal feature requires the exec feature
  • true @@ -249,7 +249,7 @@ statement before committing an image to the tier.
    is unsupported, so a class that omits a feature rejects requests for it rather than serving it by accident.

    - Enum: sandboxRuntime, virtualMachineRuntime, configMapVolumes, secretVolumes, diskVolumes, deviceVolumeAttachments, envFrom, imagePullSecrets, containerCapabilities, sandboxSysctls, exec, gracefulTermination
    + Enum: sandboxRuntime, virtualMachineRuntime, configMapVolumes, secretVolumes, diskVolumes, deviceVolumeAttachments, envFrom, imagePullSecrets, containerCapabilities, sandboxSysctls, exec, execTerminal, gracefulTermination
    false diff --git a/internal/validation/runtimeclass_crd_test.go b/internal/validation/runtimeclass_crd_test.go index 8a262744..e61f6a2d 100644 --- a/internal/validation/runtimeclass_crd_test.go +++ b/internal/validation/runtimeclass_crd_test.go @@ -196,6 +196,21 @@ func TestRuntimeClassCRD(t *testing.T) { "the sandboxSysctls feature requires the sandboxRuntime feature") }) + t.Run("the terminal feature requires exec", func(t *testing.T) { + class := newCatalogEntry("terminal-without-exec") + class.Spec.Capabilities.Features = append(class.Spec.Capabilities.Features, + computev1alpha.RuntimeClassFeatureExecTerminal) + require.ErrorContains(t, c.Create(ctx, class), + "the execTerminal feature requires the exec feature") + }) + + t.Run("a class may declare terminals with exec", func(t *testing.T) { + class := newCatalogEntry("terminal-with-exec") + class.Spec.Capabilities.Features = append(class.Spec.Capabilities.Features, + computev1alpha.RuntimeClassFeatureExec, computev1alpha.RuntimeClassFeatureExecTerminal) + require.NoError(t, c.Create(ctx, class)) + }) + for i, invalid := range []string{ "net..ipv4.forwarding", "net.ipv4.forwarding.", diff --git a/pkg/runtimeclass/capabilities.go b/pkg/runtimeclass/capabilities.go index 2f3ade9e..82b5b5f9 100644 --- a/pkg/runtimeclass/capabilities.go +++ b/pkg/runtimeclass/capabilities.go @@ -42,6 +42,7 @@ const ( FeatureContainerCapabilities = computev1alpha.RuntimeClassFeatureContainerCapabilities FeatureSandboxSysctls = computev1alpha.RuntimeClassFeatureSandboxSysctls FeatureExec = computev1alpha.RuntimeClassFeatureExec + FeatureExecTerminal = computev1alpha.RuntimeClassFeatureExecTerminal FeatureGracefulTermination = computev1alpha.RuntimeClassFeatureGracefulTermination ) diff --git a/pkg/runtimeclass/capabilities_test.go b/pkg/runtimeclass/capabilities_test.go index 966e0e26..d8318d56 100644 --- a/pkg/runtimeclass/capabilities_test.go +++ b/pkg/runtimeclass/capabilities_test.go @@ -69,6 +69,7 @@ func TestFeatureDescriptions(t *testing.T) { FeatureContainerCapabilities, FeatureSandboxSysctls, FeatureExec, + FeatureExecTerminal, FeatureGracefulTermination, }