mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 03:33:56 +00:00
dra testing: add tests for structured parameters
The test driver now supports a ConfigMap (as before) and the named resources structured parameter model. It doesn't have any instance attributes.
This commit is contained in:
parent
3de376ecf6
commit
5e40afca06
@ -24,6 +24,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ import (
|
|||||||
|
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/selection"
|
"k8s.io/apimachinery/pkg/selection"
|
||||||
@ -125,6 +127,12 @@ type Driver struct {
|
|||||||
Name string
|
Name string
|
||||||
Nodes map[string]*app.ExamplePlugin
|
Nodes map[string]*app.ExamplePlugin
|
||||||
|
|
||||||
|
parameterMode parameterMode
|
||||||
|
parameterAPIGroup string
|
||||||
|
parameterAPIVersion string
|
||||||
|
claimParameterAPIKind string
|
||||||
|
classParameterAPIKind string
|
||||||
|
|
||||||
NodeV1alpha2, NodeV1alpha3 bool
|
NodeV1alpha2, NodeV1alpha3 bool
|
||||||
|
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
@ -132,6 +140,14 @@ type Driver struct {
|
|||||||
callCounts map[MethodInstance]int64
|
callCounts map[MethodInstance]int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type parameterMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
parameterModeConfigMap parameterMode = "configmap" // ConfigMap parameters, control plane controller.
|
||||||
|
parameterModeStructured parameterMode = "structured" // No ConfigMaps, directly create and reference in-tree parameter objects.
|
||||||
|
parameterModeTranslated parameterMode = "translated" // Reference ConfigMaps in claim and class, generate in-tree parameter objects.
|
||||||
|
)
|
||||||
|
|
||||||
func (d *Driver) SetUp(nodes *Nodes, resources app.Resources) {
|
func (d *Driver) SetUp(nodes *Nodes, resources app.Resources) {
|
||||||
ginkgo.By(fmt.Sprintf("deploying driver on nodes %v", nodes.NodeNames))
|
ginkgo.By(fmt.Sprintf("deploying driver on nodes %v", nodes.NodeNames))
|
||||||
d.Nodes = map[string]*app.ExamplePlugin{}
|
d.Nodes = map[string]*app.ExamplePlugin{}
|
||||||
@ -147,19 +163,40 @@ func (d *Driver) SetUp(nodes *Nodes, resources app.Resources) {
|
|||||||
d.ctx = ctx
|
d.ctx = ctx
|
||||||
d.cleanup = append(d.cleanup, cancel)
|
d.cleanup = append(d.cleanup, cancel)
|
||||||
|
|
||||||
// The controller is easy: we simply connect to the API server.
|
switch d.parameterMode {
|
||||||
d.Controller = app.NewController(d.f.ClientSet, resources)
|
case "", parameterModeConfigMap:
|
||||||
d.wg.Add(1)
|
// The controller is easy: we simply connect to the API server.
|
||||||
go func() {
|
d.Controller = app.NewController(d.f.ClientSet, resources)
|
||||||
defer d.wg.Done()
|
d.wg.Add(1)
|
||||||
d.Controller.Run(d.ctx, 5 /* workers */)
|
go func() {
|
||||||
}()
|
defer d.wg.Done()
|
||||||
|
d.Controller.Run(d.ctx, 5 /* workers */)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
manifests := []string{
|
manifests := []string{
|
||||||
// The code below matches the content of this manifest (ports,
|
// The code below matches the content of this manifest (ports,
|
||||||
// container names, etc.).
|
// container names, etc.).
|
||||||
"test/e2e/testing-manifests/dra/dra-test-driver-proxy.yaml",
|
"test/e2e/testing-manifests/dra/dra-test-driver-proxy.yaml",
|
||||||
}
|
}
|
||||||
|
if d.parameterMode == "" {
|
||||||
|
d.parameterMode = parameterModeConfigMap
|
||||||
|
}
|
||||||
|
switch d.parameterMode {
|
||||||
|
case parameterModeConfigMap, parameterModeTranslated:
|
||||||
|
d.parameterAPIGroup = ""
|
||||||
|
d.parameterAPIVersion = "v1"
|
||||||
|
d.claimParameterAPIKind = "ConfigMap"
|
||||||
|
d.classParameterAPIKind = "ConfigMap"
|
||||||
|
case parameterModeStructured:
|
||||||
|
d.parameterAPIGroup = "resource.k8s.io"
|
||||||
|
d.parameterAPIVersion = "v1alpha2"
|
||||||
|
d.claimParameterAPIKind = "ResourceClaimParameters"
|
||||||
|
d.classParameterAPIKind = "ResourceClassParameters"
|
||||||
|
default:
|
||||||
|
framework.Failf("unknown test driver parameter mode: %s", d.parameterMode)
|
||||||
|
}
|
||||||
|
|
||||||
instanceKey := "app.kubernetes.io/instance"
|
instanceKey := "app.kubernetes.io/instance"
|
||||||
rsName := ""
|
rsName := ""
|
||||||
draAddr := path.Join(framework.TestContext.KubeletRootDir, "plugins", d.Name+".sock")
|
draAddr := path.Join(framework.TestContext.KubeletRootDir, "plugins", d.Name+".sock")
|
||||||
@ -192,6 +229,10 @@ func (d *Driver) SetUp(nodes *Nodes, resources app.Resources) {
|
|||||||
item.Spec.Template.Spec.Volumes[2].HostPath.Path = path.Join(framework.TestContext.KubeletRootDir, "plugins_registry")
|
item.Spec.Template.Spec.Volumes[2].HostPath.Path = path.Join(framework.TestContext.KubeletRootDir, "plugins_registry")
|
||||||
item.Spec.Template.Spec.Containers[0].Args = append(item.Spec.Template.Spec.Containers[0].Args, "--endpoint=/plugins_registry/"+d.Name+"-reg.sock")
|
item.Spec.Template.Spec.Containers[0].Args = append(item.Spec.Template.Spec.Containers[0].Args, "--endpoint=/plugins_registry/"+d.Name+"-reg.sock")
|
||||||
item.Spec.Template.Spec.Containers[1].Args = append(item.Spec.Template.Spec.Containers[1].Args, "--endpoint=/dra/"+d.Name+".sock")
|
item.Spec.Template.Spec.Containers[1].Args = append(item.Spec.Template.Spec.Containers[1].Args, "--endpoint=/dra/"+d.Name+".sock")
|
||||||
|
case *apiextensionsv1.CustomResourceDefinition:
|
||||||
|
item.Name = strings.ReplaceAll(item.Name, "dra.e2e.example.com", d.parameterAPIGroup)
|
||||||
|
item.Spec.Group = d.parameterAPIGroup
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, manifests...)
|
}, manifests...)
|
||||||
|
@ -18,9 +18,11 @@ package dra
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
@ -32,6 +34,7 @@ import (
|
|||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/dynamic-resource-allocation/controller"
|
"k8s.io/dynamic-resource-allocation/controller"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
@ -42,6 +45,7 @@ import (
|
|||||||
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
||||||
admissionapi "k8s.io/pod-security-admission/api"
|
admissionapi "k8s.io/pod-security-admission/api"
|
||||||
utilpointer "k8s.io/utils/pointer"
|
utilpointer "k8s.io/utils/pointer"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -142,8 +146,8 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
|
|||||||
pod.Spec.NodeName = nodes.NodeNames[0]
|
pod.Spec.NodeName = nodes.NodeNames[0]
|
||||||
b.create(ctx, pod)
|
b.create(ctx, pod)
|
||||||
|
|
||||||
gomega.Consistently(func() error {
|
gomega.Consistently(ctx, func(ctx context.Context) error {
|
||||||
testPod, err := b.f.ClientSet.CoreV1().Pods(pod.Namespace).Get(context.TODO(), pod.Name, metav1.GetOptions{})
|
testPod, err := b.f.ClientSet.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("expected the test pod %s to exist: %w", pod.Name, err)
|
return fmt.Errorf("expected the test pod %s to exist: %w", pod.Name, err)
|
||||||
}
|
}
|
||||||
@ -191,25 +195,188 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.Context("driver", func() {
|
driverTest := func(parameterMode parameterMode) {
|
||||||
nodes := NewNodes(f, 1, 1)
|
nodes := NewNodes(f, 1, 1)
|
||||||
driver := NewDriver(f, nodes, networkResources) // All tests get their own driver instance.
|
maxAllocations := 1
|
||||||
|
numPods := 10
|
||||||
|
generateResources := func() app.Resources {
|
||||||
|
resources := perNode(maxAllocations, nodes)()
|
||||||
|
resources.Shareable = true
|
||||||
|
return resources
|
||||||
|
}
|
||||||
|
driver := NewDriver(f, nodes, generateResources) // All tests get their own driver instance.
|
||||||
|
driver.parameterMode = parameterMode
|
||||||
b := newBuilder(f, driver)
|
b := newBuilder(f, driver)
|
||||||
// We need the parameters name *before* creating it.
|
// We need the parameters name *before* creating it.
|
||||||
b.parametersCounter = 1
|
b.parametersCounter = 1
|
||||||
b.classParametersName = b.parametersName()
|
b.classParametersName = b.parametersName()
|
||||||
|
|
||||||
|
expectedEnv := []string{"user_a", "b", "user_request_foo", "bar", "admin_x", "y"}
|
||||||
|
genParameters := func() []klog.KMetadata {
|
||||||
|
var objects []klog.KMetadata
|
||||||
|
switch parameterMode {
|
||||||
|
case parameterModeConfigMap:
|
||||||
|
objects = append(objects,
|
||||||
|
b.parameters("x", "y"),
|
||||||
|
b.parameters("a", "b", "request_foo", "bar"),
|
||||||
|
)
|
||||||
|
case parameterModeTranslated:
|
||||||
|
objects = append(objects,
|
||||||
|
b.parameters("x", "y"),
|
||||||
|
b.classParameters(b.parametersName(), "x", "y"),
|
||||||
|
b.parameters("a", "b", "request_foo", "bar"),
|
||||||
|
b.claimParameters(b.parametersName(), []string{"a", "b"}, []string{"request_foo", "bar"}),
|
||||||
|
)
|
||||||
|
// The parameters object is not the last one but the second-last.
|
||||||
|
b.parametersCounter--
|
||||||
|
case parameterModeStructured:
|
||||||
|
objects = append(objects,
|
||||||
|
b.classParameters("", "x", "y"),
|
||||||
|
b.claimParameters("", []string{"a", "b"}, []string{"request_foo", "bar"}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return objects
|
||||||
|
}
|
||||||
|
|
||||||
ginkgo.It("supports claim and class parameters", func(ctx context.Context) {
|
ginkgo.It("supports claim and class parameters", func(ctx context.Context) {
|
||||||
classParameters := b.parameters("x", "y")
|
objects := genParameters()
|
||||||
claimParameters := b.parameters()
|
|
||||||
|
// TODO: replace with publishing NodeResourceSlice through kubelet
|
||||||
|
if parameterMode == parameterModeTranslated || parameterMode == parameterModeStructured {
|
||||||
|
objects = append(objects, b.nodeResourceSlice(nodes.NodeNames[0], maxAllocations))
|
||||||
|
}
|
||||||
|
|
||||||
pod, template := b.podInline(resourcev1alpha2.AllocationModeWaitForFirstConsumer)
|
pod, template := b.podInline(resourcev1alpha2.AllocationModeWaitForFirstConsumer)
|
||||||
|
objects = append(objects, pod, template)
|
||||||
|
|
||||||
b.create(ctx, classParameters, claimParameters, pod, template)
|
b.create(ctx, objects...)
|
||||||
|
|
||||||
b.testPod(ctx, f.ClientSet, pod, "user_a", "b", "admin_x", "y")
|
b.testPod(ctx, f.ClientSet, pod, expectedEnv...)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ginkgo.It("supports reusing resources", func(ctx context.Context) {
|
||||||
|
objects := genParameters()
|
||||||
|
pods := make([]*v1.Pod, numPods)
|
||||||
|
for i := 0; i < numPods; i++ {
|
||||||
|
pod, template := b.podInline(resourcev1alpha2.AllocationModeWaitForFirstConsumer)
|
||||||
|
pods[i] = pod
|
||||||
|
objects = append(objects, pod, template)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace with publishing NodeResourceSlice through kubelet
|
||||||
|
if parameterMode == parameterModeTranslated || parameterMode == parameterModeStructured {
|
||||||
|
objects = append(objects, b.nodeResourceSlice(nodes.NodeNames[0], maxAllocations))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.create(ctx, objects...)
|
||||||
|
|
||||||
|
// We don't know the order. All that matters is that all of them get scheduled eventually.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(numPods)
|
||||||
|
for i := 0; i < numPods; i++ {
|
||||||
|
pod := pods[i]
|
||||||
|
go func() {
|
||||||
|
defer ginkgo.GinkgoRecover()
|
||||||
|
defer wg.Done()
|
||||||
|
b.testPod(ctx, f.ClientSet, pod, expectedEnv...)
|
||||||
|
err := f.ClientSet.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{})
|
||||||
|
framework.ExpectNoError(err, "delete pod")
|
||||||
|
framework.ExpectNoError(e2epod.WaitForPodNotFoundInNamespace(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStartSlow))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("supports sharing a claim concurrently", func(ctx context.Context) {
|
||||||
|
objects := genParameters()
|
||||||
|
objects = append(objects, b.externalClaim(resourcev1alpha2.AllocationModeWaitForFirstConsumer))
|
||||||
|
|
||||||
|
pods := make([]*v1.Pod, numPods)
|
||||||
|
for i := 0; i < numPods; i++ {
|
||||||
|
pod := b.podExternal()
|
||||||
|
pods[i] = pod
|
||||||
|
objects = append(objects, pod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace with publishing NodeResourceSlice through kubelet
|
||||||
|
if parameterMode == parameterModeTranslated || parameterMode == parameterModeStructured {
|
||||||
|
objects = append(objects, b.nodeResourceSlice(nodes.NodeNames[0], maxAllocations))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.create(ctx, objects...)
|
||||||
|
|
||||||
|
// We don't know the order. All that matters is that all of them get scheduled eventually.
|
||||||
|
f.Timeouts.PodStartSlow *= time.Duration(numPods)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(numPods)
|
||||||
|
for i := 0; i < numPods; i++ {
|
||||||
|
pod := pods[i]
|
||||||
|
go func() {
|
||||||
|
defer ginkgo.GinkgoRecover()
|
||||||
|
defer wg.Done()
|
||||||
|
b.testPod(ctx, f.ClientSet, pod, expectedEnv...)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("supports sharing a claim sequentially", func(ctx context.Context) {
|
||||||
|
objects := genParameters()
|
||||||
|
|
||||||
|
// Change from "shareable" to "not shareable", if possible.
|
||||||
|
switch parameterMode {
|
||||||
|
case parameterModeConfigMap:
|
||||||
|
ginkgo.Skip("cannot change the driver's controller behavior on-the-fly")
|
||||||
|
case parameterModeTranslated, parameterModeStructured:
|
||||||
|
objects[len(objects)-1].(*resourcev1alpha2.ResourceClaimParameters).Shareable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
objects = append(objects, b.externalClaim(resourcev1alpha2.AllocationModeWaitForFirstConsumer))
|
||||||
|
|
||||||
|
pods := make([]*v1.Pod, numPods)
|
||||||
|
for i := 0; i < numPods; i++ {
|
||||||
|
pod := b.podExternal()
|
||||||
|
pods[i] = pod
|
||||||
|
objects = append(objects, pod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace with publishing NodeResourceSlice through kubelet
|
||||||
|
if parameterMode == parameterModeTranslated || parameterMode == parameterModeStructured {
|
||||||
|
objects = append(objects, b.nodeResourceSlice(nodes.NodeNames[0], maxAllocations))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.create(ctx, objects...)
|
||||||
|
|
||||||
|
// We don't know the order. All that matters is that all of them get scheduled eventually.
|
||||||
|
f.Timeouts.PodStartSlow *= time.Duration(numPods)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(numPods)
|
||||||
|
for i := 0; i < numPods; i++ {
|
||||||
|
pod := pods[i]
|
||||||
|
go func() {
|
||||||
|
defer ginkgo.GinkgoRecover()
|
||||||
|
defer wg.Done()
|
||||||
|
b.testPod(ctx, f.ClientSet, pod, expectedEnv...)
|
||||||
|
// We need to delete each running pod, otherwise the others cannot use the claim.
|
||||||
|
err := f.ClientSet.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{})
|
||||||
|
framework.ExpectNoError(err, "delete pod")
|
||||||
|
framework.ExpectNoError(e2epod.WaitForPodNotFoundInNamespace(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStartSlow))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ginkgo.Context("driver", func() {
|
||||||
|
ginkgo.Context("with ConfigMap parameters", func() { driverTest(parameterModeConfigMap) })
|
||||||
|
ginkgo.Context("with translated parameters", func() { driverTest(parameterModeTranslated) })
|
||||||
|
ginkgo.Context("with structured parameters", func() { driverTest(parameterModeStructured) })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO: move most of the test below into `testDriver` so that they get
|
||||||
|
// executed with different parameters. Not done yet because it'll be easier
|
||||||
|
// once publishing NodeResourceSlices works.
|
||||||
|
|
||||||
ginkgo.Context("cluster", func() {
|
ginkgo.Context("cluster", func() {
|
||||||
nodes := NewNodes(f, 1, 1)
|
nodes := NewNodes(f, 1, 1)
|
||||||
driver := NewDriver(f, nodes, networkResources)
|
driver := NewDriver(f, nodes, networkResources)
|
||||||
@ -942,12 +1109,14 @@ func (b *builder) class() *resourcev1alpha2.ResourceClass {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: b.className(),
|
Name: b.className(),
|
||||||
},
|
},
|
||||||
DriverName: b.driver.Name,
|
DriverName: b.driver.Name,
|
||||||
SuitableNodes: b.nodeSelector(),
|
SuitableNodes: b.nodeSelector(),
|
||||||
|
StructuredParameters: ptr.To(b.driver.parameterMode != parameterModeConfigMap),
|
||||||
}
|
}
|
||||||
if b.classParametersName != "" {
|
if b.classParametersName != "" {
|
||||||
class.ParametersRef = &resourcev1alpha2.ResourceClassParametersReference{
|
class.ParametersRef = &resourcev1alpha2.ResourceClassParametersReference{
|
||||||
Kind: "ConfigMap",
|
APIGroup: b.driver.parameterAPIGroup,
|
||||||
|
Kind: b.driver.classParameterAPIKind,
|
||||||
Name: b.classParametersName,
|
Name: b.classParametersName,
|
||||||
Namespace: b.f.Namespace.Name,
|
Namespace: b.f.Namespace.Name,
|
||||||
}
|
}
|
||||||
@ -988,8 +1157,9 @@ func (b *builder) externalClaim(allocationMode resourcev1alpha2.AllocationMode)
|
|||||||
Spec: resourcev1alpha2.ResourceClaimSpec{
|
Spec: resourcev1alpha2.ResourceClaimSpec{
|
||||||
ResourceClassName: b.className(),
|
ResourceClassName: b.className(),
|
||||||
ParametersRef: &resourcev1alpha2.ResourceClaimParametersReference{
|
ParametersRef: &resourcev1alpha2.ResourceClaimParametersReference{
|
||||||
Kind: "ConfigMap",
|
APIGroup: b.driver.parameterAPIGroup,
|
||||||
Name: b.parametersName(),
|
Kind: b.driver.claimParameterAPIKind,
|
||||||
|
Name: b.parametersName(),
|
||||||
},
|
},
|
||||||
AllocationMode: allocationMode,
|
AllocationMode: allocationMode,
|
||||||
},
|
},
|
||||||
@ -1005,20 +1175,15 @@ func (b *builder) parametersName() string {
|
|||||||
// parametersEnv returns the default env variables.
|
// parametersEnv returns the default env variables.
|
||||||
func (b *builder) parametersEnv() map[string]string {
|
func (b *builder) parametersEnv() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"a": "b",
|
"a": "b",
|
||||||
|
"request_foo": "bar",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parameters returns a config map with the default env variables.
|
// parameters returns a config map with the default env variables.
|
||||||
func (b *builder) parameters(kv ...string) *v1.ConfigMap {
|
func (b *builder) parameters(kv ...string) *v1.ConfigMap {
|
||||||
|
data := b.parameterData(kv...)
|
||||||
b.parametersCounter++
|
b.parametersCounter++
|
||||||
data := map[string]string{}
|
|
||||||
for i := 0; i < len(kv); i += 2 {
|
|
||||||
data[kv[i]] = kv[i+1]
|
|
||||||
}
|
|
||||||
if len(data) == 0 {
|
|
||||||
data = b.parametersEnv()
|
|
||||||
}
|
|
||||||
return &v1.ConfigMap{
|
return &v1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: b.f.Namespace.Name,
|
Namespace: b.f.Namespace.Name,
|
||||||
@ -1028,6 +1193,116 @@ func (b *builder) parameters(kv ...string) *v1.ConfigMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *builder) classParameters(generatedFrom string, kv ...string) *resourcev1alpha2.ResourceClassParameters {
|
||||||
|
raw := b.rawParameterData(kv...)
|
||||||
|
b.parametersCounter++
|
||||||
|
parameters := &resourcev1alpha2.ResourceClassParameters{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: b.f.Namespace.Name,
|
||||||
|
Name: b.parametersName(),
|
||||||
|
},
|
||||||
|
|
||||||
|
VendorParameters: []resourcev1alpha2.VendorParameters{
|
||||||
|
{DriverName: b.driver.Name, Parameters: runtime.RawExtension{Raw: raw}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if generatedFrom != "" {
|
||||||
|
parameters.GeneratedFrom = &resourcev1alpha2.ResourceClassParametersReference{
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
Namespace: b.f.Namespace.Name,
|
||||||
|
Name: generatedFrom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) claimParameters(generatedFrom string, claimKV, requestKV []string) *resourcev1alpha2.ResourceClaimParameters {
|
||||||
|
b.parametersCounter++
|
||||||
|
parameters := &resourcev1alpha2.ResourceClaimParameters{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: b.f.Namespace.Name,
|
||||||
|
Name: b.parametersName(),
|
||||||
|
},
|
||||||
|
|
||||||
|
Shareable: true,
|
||||||
|
|
||||||
|
// Without any request, nothing gets allocated and vendor
|
||||||
|
// parameters are also not passed down because they get
|
||||||
|
// attached to the allocation result.
|
||||||
|
// TODO: is that the semantic we want?
|
||||||
|
DriverRequests: []resourcev1alpha2.DriverRequests{
|
||||||
|
{
|
||||||
|
DriverName: b.driver.Name,
|
||||||
|
VendorParameters: runtime.RawExtension{Raw: b.rawParameterData(claimKV...)},
|
||||||
|
Requests: []resourcev1alpha2.ResourceRequest{
|
||||||
|
{
|
||||||
|
VendorParameters: runtime.RawExtension{Raw: b.rawParameterData(requestKV...)},
|
||||||
|
ResourceRequestModel: resourcev1alpha2.ResourceRequestModel{
|
||||||
|
NamedResources: &resourcev1alpha2.NamedResourcesRequest{
|
||||||
|
Selector: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if generatedFrom != "" {
|
||||||
|
parameters.GeneratedFrom = &resourcev1alpha2.ResourceClaimParametersReference{
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
Name: generatedFrom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) parameterData(kv ...string) map[string]string {
|
||||||
|
data := map[string]string{}
|
||||||
|
for i := 0; i < len(kv); i += 2 {
|
||||||
|
data[kv[i]] = kv[i+1]
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
data = b.parametersEnv()
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) rawParameterData(kv ...string) []byte {
|
||||||
|
data := b.parameterData(kv...)
|
||||||
|
raw, err := json.Marshal(data)
|
||||||
|
framework.ExpectNoError(err, "JSON encoding of parameter data")
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) nodeResourceSlice(nodeName string, capacity int) *resourcev1alpha2.NodeResourceSlice {
|
||||||
|
slice := &resourcev1alpha2.NodeResourceSlice{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: b.driver.Name + "-" + nodeName,
|
||||||
|
},
|
||||||
|
|
||||||
|
NodeName: nodeName,
|
||||||
|
DriverName: b.driver.Name,
|
||||||
|
|
||||||
|
NodeResourceModel: resourcev1alpha2.NodeResourceModel{
|
||||||
|
NamedResources: &resourcev1alpha2.NamedResourcesResources{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < capacity; i++ {
|
||||||
|
slice.NodeResourceModel.NamedResources.Instances = append(slice.NodeResourceModel.NamedResources.Instances,
|
||||||
|
resourcev1alpha2.NamedResourcesInstance{
|
||||||
|
Name: fmt.Sprintf("instance-%d", i),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
// makePod returns a simple pod with no resource claims.
|
// makePod returns a simple pod with no resource claims.
|
||||||
// The pod prints its env and waits.
|
// The pod prints its env and waits.
|
||||||
func (b *builder) pod() *v1.Pod {
|
func (b *builder) pod() *v1.Pod {
|
||||||
@ -1078,8 +1353,9 @@ func (b *builder) podInline(allocationMode resourcev1alpha2.AllocationMode) (*v1
|
|||||||
Spec: resourcev1alpha2.ResourceClaimSpec{
|
Spec: resourcev1alpha2.ResourceClaimSpec{
|
||||||
ResourceClassName: b.className(),
|
ResourceClassName: b.className(),
|
||||||
ParametersRef: &resourcev1alpha2.ResourceClaimParametersReference{
|
ParametersRef: &resourcev1alpha2.ResourceClaimParametersReference{
|
||||||
Kind: "ConfigMap",
|
APIGroup: b.driver.parameterAPIGroup,
|
||||||
Name: b.parametersName(),
|
Kind: b.driver.claimParameterAPIKind,
|
||||||
|
Name: b.parametersName(),
|
||||||
},
|
},
|
||||||
AllocationMode: allocationMode,
|
AllocationMode: allocationMode,
|
||||||
},
|
},
|
||||||
@ -1134,14 +1410,28 @@ func (b *builder) create(ctx context.Context, objs ...klog.KMetadata) []klog.KMe
|
|||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *resourcev1alpha2.ResourceClass:
|
case *resourcev1alpha2.ResourceClass:
|
||||||
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClasses().Create(ctx, obj, metav1.CreateOptions{})
|
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClasses().Create(ctx, obj, metav1.CreateOptions{})
|
||||||
|
ginkgo.DeferCleanup(func(ctx context.Context) {
|
||||||
|
err := b.f.ClientSet.ResourceV1alpha2().ResourceClasses().Delete(ctx, createdObj.GetName(), metav1.DeleteOptions{})
|
||||||
|
framework.ExpectNoError(err, "delete resource class")
|
||||||
|
})
|
||||||
case *v1.Pod:
|
case *v1.Pod:
|
||||||
createdObj, err = b.f.ClientSet.CoreV1().Pods(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
createdObj, err = b.f.ClientSet.CoreV1().Pods(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
||||||
case *v1.ConfigMap:
|
case *v1.ConfigMap:
|
||||||
_, err = b.f.ClientSet.CoreV1().ConfigMaps(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
createdObj, err = b.f.ClientSet.CoreV1().ConfigMaps(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
||||||
case *resourcev1alpha2.ResourceClaim:
|
case *resourcev1alpha2.ResourceClaim:
|
||||||
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClaims(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClaims(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
||||||
case *resourcev1alpha2.ResourceClaimTemplate:
|
case *resourcev1alpha2.ResourceClaimTemplate:
|
||||||
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClaimTemplates(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClaimTemplates(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
||||||
|
case *resourcev1alpha2.ResourceClassParameters:
|
||||||
|
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClassParameters(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
||||||
|
case *resourcev1alpha2.ResourceClaimParameters:
|
||||||
|
createdObj, err = b.f.ClientSet.ResourceV1alpha2().ResourceClaimParameters(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{})
|
||||||
|
case *resourcev1alpha2.NodeResourceSlice:
|
||||||
|
createdObj, err = b.f.ClientSet.ResourceV1alpha2().NodeResourceSlices().Create(ctx, obj, metav1.CreateOptions{})
|
||||||
|
ginkgo.DeferCleanup(func(ctx context.Context) {
|
||||||
|
err := b.f.ClientSet.ResourceV1alpha2().NodeResourceSlices().Delete(ctx, createdObj.GetName(), metav1.DeleteOptions{})
|
||||||
|
framework.ExpectNoError(err, "delete node resource slice")
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
framework.Fail(fmt.Sprintf("internal error, unsupported type %T", obj), 1)
|
framework.Fail(fmt.Sprintf("internal error, unsupported type %T", obj), 1)
|
||||||
}
|
}
|
||||||
@ -1190,9 +1480,6 @@ func (b *builder) setUp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) tearDown(ctx context.Context) {
|
func (b *builder) tearDown(ctx context.Context) {
|
||||||
err := b.f.ClientSet.ResourceV1alpha2().ResourceClasses().Delete(ctx, b.className(), metav1.DeleteOptions{})
|
|
||||||
framework.ExpectNoError(err, "delete resource class")
|
|
||||||
|
|
||||||
// Before we allow the namespace and all objects in it do be deleted by
|
// Before we allow the namespace and all objects in it do be deleted by
|
||||||
// the framework, we must ensure that test pods and the claims that
|
// the framework, we must ensure that test pods and the claims that
|
||||||
// they use are deleted. Otherwise the driver might get deleted first,
|
// they use are deleted. Otherwise the driver might get deleted first,
|
||||||
|
@ -182,6 +182,11 @@ func (ex *ExamplePlugin) NodePrepareResource(ctx context.Context, req *drapbv1al
|
|||||||
if err := extractParameters(handle.VendorClaimParameters, &p.EnvVars, "user"); err != nil {
|
if err := extractParameters(handle.VendorClaimParameters, &p.EnvVars, "user"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, result := range handle.Results {
|
||||||
|
if err := extractParameters(result.VendorRequestParameters, &p.EnvVars, "user"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// Huh?
|
// Huh?
|
||||||
return nil, fmt.Errorf("invalid length of NodePrepareResourceRequest.StructuredResourceHandle: %d", len(req.StructuredResourceHandle))
|
return nil, fmt.Errorf("invalid length of NodePrepareResourceRequest.StructuredResourceHandle: %d", len(req.StructuredResourceHandle))
|
||||||
|
Loading…
Reference in New Issue
Block a user