Skip to content
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 cmd/dra-example-kubeletplugin/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/status"
"k8s.io/klog/v2"
drapb "k8s.io/kubelet/pkg/apis/dra/v1beta1"
drapb "k8s.io/kubelet/pkg/apis/dra/v1"
registerapi "k8s.io/kubelet/pkg/apis/pluginregistration/v1"

"sigs.k8s.io/dra-example-driver/pkg/consts"
Expand Down
48 changes: 15 additions & 33 deletions cmd/dra-example-webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,16 @@ import (
"github.com/urfave/cli/v2"

admissionv1 "k8s.io/api/admission/v1"
resourceapi "k8s.io/api/resource/v1beta1"
resourceapi "k8s.io/api/resource/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"

configapi "sigs.k8s.io/dra-example-driver/api/example.com/resource/gpu/v1alpha1"
"sigs.k8s.io/dra-example-driver/pkg/consts"
"sigs.k8s.io/dra-example-driver/pkg/flags"
)

var (
resourceClaimResource = metav1.GroupVersionResource{
Group: resourceapi.SchemeGroupVersion.Group,
Version: resourceapi.SchemeGroupVersion.Version,
Resource: "resourceclaims",
}
resourceClaimTemplateResource = metav1.GroupVersionResource{
Group: resourceapi.SchemeGroupVersion.Group,
Version: resourceapi.SchemeGroupVersion.Version,
Resource: "resourceclaimtemplates",
}
)

type Flags struct {
loggingConfig *flags.LoggingConfig

Expand All @@ -60,13 +45,6 @@ type Flags struct {
port int
}

var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)

func init() {
utilruntime.Must(admissionv1.AddToScheme(scheme))
}

func main() {
if err := newApp().Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
Expand Down Expand Up @@ -219,13 +197,10 @@ func admitResourceClaimParameters(ar admissionv1.AdmissionReview) *admissionv1.A
var deviceConfigs []resourceapi.DeviceClaimConfiguration
var specPath string

raw := ar.Request.Object.Raw
deserializer := codecs.UniversalDeserializer()

switch ar.Request.Resource {
case resourceClaimResource:
claim := resourceapi.ResourceClaim{}
if _, _, err := deserializer.Decode(raw, nil, &claim); err != nil {
case resourceClaimResourceV1, resourceClaimResourceV1Beta1, resourceClaimResourceV1Beta2:
claim, err := extractResourceClaim(ar)
if err != nil {
klog.Error(err)
return &admissionv1.AdmissionResponse{
Result: &metav1.Status{
Expand All @@ -236,9 +211,9 @@ func admitResourceClaimParameters(ar admissionv1.AdmissionReview) *admissionv1.A
}
deviceConfigs = claim.Spec.Devices.Config
specPath = "spec"
case resourceClaimTemplateResource:
claimTemplate := resourceapi.ResourceClaimTemplate{}
if _, _, err := deserializer.Decode(raw, nil, &claimTemplate); err != nil {
case resourceClaimTemplateResourceV1, resourceClaimTemplateResourceV1Beta1, resourceClaimTemplateResourceV1Beta2:
claimTemplate, err := extractResourceClaimTemplate(ar)
if err != nil {
klog.Error(err)
return &admissionv1.AdmissionResponse{
Result: &metav1.Status{
Expand All @@ -250,7 +225,14 @@ func admitResourceClaimParameters(ar admissionv1.AdmissionReview) *admissionv1.A
deviceConfigs = claimTemplate.Spec.Spec.Devices.Config
specPath = "spec.spec"
default:
msg := fmt.Sprintf("expected resource to be %s or %s, got %s", resourceClaimResource, resourceClaimTemplateResource, ar.Request.Resource)
msg := fmt.Sprintf(
"expected resource to be one of %v, got %s",
[]metav1.GroupVersionResource{
resourceClaimResourceV1, resourceClaimResourceV1Beta1, resourceClaimResourceV1Beta2,
resourceClaimTemplateResourceV1, resourceClaimTemplateResourceV1Beta1, resourceClaimTemplateResourceV1Beta2,
},
ar.Request.Resource,
)
klog.Error(msg)
return &admissionv1.AdmissionResponse{
Result: &metav1.Status{
Expand Down
160 changes: 99 additions & 61 deletions cmd/dra-example-webhook/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
Expand All @@ -28,7 +29,8 @@ import (
"github.com/stretchr/testify/require"

admissionv1 "k8s.io/api/admission/v1"
resourceapi "k8s.io/api/resource/v1beta1"
resourceapi "k8s.io/api/resource/v1"
resourcev1beta1 "k8s.io/api/resource/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -47,6 +49,40 @@ func TestReadyEndpoint(t *testing.T) {
}

func TestResourceClaimValidatingWebhook(t *testing.T) {
unknownResource := metav1.GroupVersionResource{
Group: "resource.k8s.io",
Version: "v1",
Resource: "unknownresources",
}

validGPUConfig := &configapi.GpuConfig{
Sharing: &configapi.GpuSharing{
Strategy: configapi.TimeSlicingStrategy,
TimeSlicingConfig: &configapi.TimeSlicingConfig{
Interval: configapi.DefaultTimeSlice,
},
},
}

invalidGPUConfigs := []*configapi.GpuConfig{
{
Sharing: &configapi.GpuSharing{
Strategy: configapi.TimeSlicingStrategy,
TimeSlicingConfig: &configapi.TimeSlicingConfig{
Interval: "InvalidInterval",
},
},
},
{
Sharing: &configapi.GpuSharing{
Strategy: configapi.SpacePartitioningStrategy,
SpacePartitioningConfig: &configapi.SpacePartitioningConfig{
PartitionCount: -1,
},
},
},
}

tests := map[string]struct {
admissionReview *admissionv1.AdmissionReview
requestContentType string
Expand All @@ -64,86 +100,72 @@ func TestResourceClaimValidatingWebhook(t *testing.T) {
},
"valid GpuConfig in ResourceClaim": {
admissionReview: admissionReviewWithObject(
resourceClaimWithGpuConfigs(
&configapi.GpuConfig{
Sharing: &configapi.GpuSharing{
Strategy: configapi.TimeSlicingStrategy,
TimeSlicingConfig: &configapi.TimeSlicingConfig{
Interval: configapi.DefaultTimeSlice,
},
},
},
),
resourceClaimResource,
resourceClaimWithGpuConfigs(validGPUConfig),
resourceClaimResourceV1,
),
expectedAllowed: true,
},
"invalid GpuConfigs in ResourceClaim": {
admissionReview: admissionReviewWithObject(
resourceClaimWithGpuConfigs(
&configapi.GpuConfig{
Sharing: &configapi.GpuSharing{
Strategy: configapi.TimeSlicingStrategy,
TimeSlicingConfig: &configapi.TimeSlicingConfig{
Interval: "InvalidInterval",
},
},
},
&configapi.GpuConfig{
Sharing: &configapi.GpuSharing{
Strategy: configapi.SpacePartitioningStrategy,
SpacePartitioningConfig: &configapi.SpacePartitioningConfig{
PartitionCount: -1,
},
},
},
),
resourceClaimResource,
resourceClaimWithGpuConfigs(invalidGPUConfigs...),
resourceClaimResourceV1,
),
expectedAllowed: false,
expectedMessage: "2 configs failed to validate: object at spec.devices.config[0].opaque.parameters is invalid: unknown time-slice interval: InvalidInterval; object at spec.devices.config[1].opaque.parameters is invalid: invalid partition count: -1",
},
"valid GpuConfig in ResourceClaimTemplate": {
admissionReview: admissionReviewWithObject(
resourceClaimTemplateWithGpuConfigs(
&configapi.GpuConfig{
Sharing: &configapi.GpuSharing{
Strategy: configapi.TimeSlicingStrategy,
TimeSlicingConfig: &configapi.TimeSlicingConfig{
Interval: configapi.DefaultTimeSlice,
},
},
},
),
resourceClaimTemplateResource,
resourceClaimTemplateWithGpuConfigs(validGPUConfig),
resourceClaimTemplateResourceV1,
),
expectedAllowed: true,
},
"invalid GpuConfigs in ResourceClaimTemplate": {
admissionReview: admissionReviewWithObject(
resourceClaimTemplateWithGpuConfigs(
&configapi.GpuConfig{
Sharing: &configapi.GpuSharing{
Strategy: configapi.TimeSlicingStrategy,
TimeSlicingConfig: &configapi.TimeSlicingConfig{
Interval: "InvalidInterval",
},
},
},
&configapi.GpuConfig{
Sharing: &configapi.GpuSharing{
Strategy: configapi.SpacePartitioningStrategy,
SpacePartitioningConfig: &configapi.SpacePartitioningConfig{
PartitionCount: -1,
},
},
},
),
resourceClaimTemplateResource,
resourceClaimTemplateWithGpuConfigs(invalidGPUConfigs...),
resourceClaimTemplateResourceV1,
),
expectedAllowed: false,
expectedMessage: "2 configs failed to validate: object at spec.spec.devices.config[0].opaque.parameters is invalid: unknown time-slice interval: InvalidInterval; object at spec.spec.devices.config[1].opaque.parameters is invalid: invalid partition count: -1",
},
"valid GpuConfig in ResourceClaim v1beta1": {
admissionReview: admissionReviewWithObject(
toResourceClaimV1Beta1(resourceClaimWithGpuConfigs(validGPUConfig)),
resourceClaimResourceV1Beta1,
),
expectedAllowed: true,
},
"invalid GpuConfigs in ResourceClaim v1beta1": {
admissionReview: admissionReviewWithObject(
toResourceClaimV1Beta1(resourceClaimWithGpuConfigs(invalidGPUConfigs...)),
resourceClaimResourceV1Beta1,
),
expectedAllowed: false,
expectedMessage: "2 configs failed to validate: object at spec.devices.config[0].opaque.parameters is invalid: unknown time-slice interval: InvalidInterval; object at spec.devices.config[1].opaque.parameters is invalid: invalid partition count: -1",
},
"valid GpuConfig in ResourceClaimTemplate v1beta1": {
admissionReview: admissionReviewWithObject(
toResourceClaimTemplateV1Beta1(resourceClaimTemplateWithGpuConfigs(validGPUConfig)),
resourceClaimTemplateResourceV1Beta1,
),
expectedAllowed: true,
},
"invalid GpuConfigs in ResourceClaimTemplate v1beta1": {
admissionReview: admissionReviewWithObject(
toResourceClaimTemplateV1Beta1(resourceClaimTemplateWithGpuConfigs(invalidGPUConfigs...)),
resourceClaimTemplateResourceV1Beta1,
),
expectedAllowed: false,
expectedMessage: "2 configs failed to validate: object at spec.spec.devices.config[0].opaque.parameters is invalid: unknown time-slice interval: InvalidInterval; object at spec.spec.devices.config[1].opaque.parameters is invalid: invalid partition count: -1",
},
"unknown resource type": {
admissionReview: admissionReviewWithObject(
resourceClaimWithGpuConfigs(validGPUConfig),
unknownResource,
),
expectedAllowed: false,
expectedMessage: "expected resource to be one of [{resource.k8s.io v1 resourceclaims} {resource.k8s.io v1beta1 resourceclaims} {resource.k8s.io v1beta2 resourceclaims} {resource.k8s.io v1 resourceclaimtemplates} {resource.k8s.io v1beta1 resourceclaimtemplates} {resource.k8s.io v1beta2 resourceclaimtemplates}], got {resource.k8s.io v1 unknownresources}",
},
}

s := httptest.NewServer(newMux())
Expand Down Expand Up @@ -238,3 +260,19 @@ func resourceClaimSpecWithGpuConfigs(gpuConfigs ...*configapi.GpuConfig) resourc
}
return resourceClaimSpec
}

func toResourceClaimV1Beta1(v1Claim *resourceapi.ResourceClaim) *resourcev1beta1.ResourceClaim {
v1beta1Claim := &resourcev1beta1.ResourceClaim{}
if err := scheme.Convert(v1Claim, v1beta1Claim, nil); err != nil {
panic(fmt.Sprintf("failed to convert ResourceClaim to v1beta1: %v", err))
}
return v1beta1Claim
}

func toResourceClaimTemplateV1Beta1(v1Template *resourceapi.ResourceClaimTemplate) *resourcev1beta1.ResourceClaimTemplate {
v1beta1Template := &resourcev1beta1.ResourceClaimTemplate{}
if err := scheme.Convert(v1Template, v1beta1Template, nil); err != nil {
panic(fmt.Sprintf("failed to convert ResourceClaimTemplate to v1beta1: %v", err))
}
return v1beta1Template
}
Loading
Loading