Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 4 additions & 1 deletion deploy/crds/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ Copy from a cluster with CNV operator installed
Copy from a cluster with Ansible Automation Platform operator installed

== serving CRDs
Copy from a cluster with Serverless operator installed
Copy from a cluster with Serverless operator installed

== claws CRD
Copy from the claw-operator repo (config/crd/bases/claw.sandbox.redhat.com_claws.yaml)
40 changes: 40 additions & 0 deletions deploy/crds/claw.sandbox.redhat.com_claws.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
kind: CustomResourceDefinition
apiVersion: apiextensions.k8s.io/v1
metadata:
name: claws.claw.sandbox.redhat.com
spec:
group: claw.sandbox.redhat.com
names:
plural: claws
singular: claw
kind: Claw
listKind: ClawList
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
description: Claw is the Schema for the claws API
type: object
properties:
apiVersion:
type: string
kind:
type: string
metadata:
type: object
spec:
description: Spec defines the desired state of Claw
type: object
properties:
idle:
description: Scale down replicas to put Claw into an idle mode
type: boolean
status:
description: Status defines the observed state of Claw
type: object
x-kubernetes-preserve-unknown-fields: true
subresources:
status: {}
60 changes: 59 additions & 1 deletion test/e2e/parallel/user_workloads_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,21 @@ func TestIdlerAndPriorityClass(t *testing.T) {
podsToIdle := prepareWorkloads(t, await.Member1(), idler.Name, wait.WithSandboxPriorityClass())
podsNoise := prepareWorkloads(t, await.Member1(), idlerNoise.Name, wait.WithSandboxPriorityClass())

// Create a Claw workload only in the dev namespace (the one being idled).
// Not added to prepareWorkloads to avoid exceeding the ClusterResourceQuota pod limit
// when workloads are created in multiple namespaces for the same user.
clawDeployment := createClaw(t, memberAwait, "test-idler-claw", idler.Name)
podsToIdle, err := memberAwait.WaitForPods(t, idler.Name, len(podsToIdle)+int(*clawDeployment.Spec.Replicas),
wait.PodRunning(), wait.WithPodLabel("idler", "idler"), wait.WithSandboxPriorityClass())
require.NoError(t, err)

Comment on lines +41 to +48
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a detail - could have been done as part of prepareWorkloads in the same way as it's done for other workload types to verify that it ignores the "noise" namespace for claw workloads

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved: 5d735f3

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to revert it due to the pod quota issue if we create it in all namespaces in multi-namespace tiers. We alerready create too many resources in the test namespaces.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should adjust that (or refactor the test so it uses more accounts) because this would block us from adding more workloads to the test suite

// Create more noise pods in non-user namespace
memberAwait.CreateNamespace(t, "workloads-noise")
externalNsPodsNoise := prepareWorkloads(t, await.Member1(), "workloads-noise", wait.WithOriginalPriorityClass())

// Set a short timeout for one of the idler to trigger pod idling
// The idler is currently updating its status since it's already been idling the pods. So we need to keep trying to update.
idler, err := wait.For(t, memberAwait.Awaitility, &toolchainv1alpha1.Idler{}).
idler, err = wait.For(t, memberAwait.Awaitility, &toolchainv1alpha1.Idler{}).
Update(idler.Name, memberAwait.Namespace, func(i *toolchainv1alpha1.Idler) {
i.Spec.TimeoutSeconds = 5
})
Expand Down Expand Up @@ -101,6 +109,10 @@ func TestIdlerAndPriorityClass(t *testing.T) {
_, err = memberAwait.WaitForAAP(t, "test-idler-aap", idler.Name, clnt.Resource(aapRes), true)
require.NoError(t, err)

// Wait for the Claw resource to be idled (spec.idle: true)
_, err = memberAwait.WaitForClaw(t, "test-idler-claw", idler.Name, clnt.Resource(clawRes), true)
require.NoError(t, err)

// Wait for the InferenceService to be deleted - the expected action to idle
// the workload is by deleting the InferenceService that is old enough.
// The pods are idled as well, which is verified in the previous step - after some time,
Expand Down Expand Up @@ -208,6 +220,7 @@ func createDeployment(t *testing.T, memberAwait *wait.MemberAwaitility, namespac
}

var aapRes = schema.GroupVersionResource{Group: "aap.ansible.com", Version: "v1alpha1", Resource: "ansibleautomationplatforms"}
var clawRes = schema.GroupVersionResource{Group: "claw.sandbox.redhat.com", Version: "v1alpha1", Resource: "claws"}
var servingRuntimeRes = schema.GroupVersionResource{Group: "serving.kserve.io", Version: "v1alpha1", Resource: "servingruntimes"}
var inferenceServiceRes = schema.GroupVersionResource{Group: "serving.kserve.io", Version: "v1beta1", Resource: "inferenceservices"}
var dataVolumeRes = schema.GroupVersionResource{Group: "cdi.kubevirt.io", Version: "v1beta1", Resource: "datavolumes"}
Expand Down Expand Up @@ -244,6 +257,51 @@ func createAAP(t *testing.T, memberAwait *wait.MemberAwaitility, name, namespace
return deployment
}

// createClaw creates an instance of claws.claw.sandbox.redhat.com with one deployment owned by this instance
// returns the underlying deployment
func createClaw(t *testing.T, memberAwait *wait.MemberAwaitility, name, namespace string) *appsv1.Deployment {
clnt, err := dynamic.NewForConfig(memberAwait.RestConfig)
require.NoError(t, err)

claw := clawResource(name)
createdClaw, err := clnt.Resource(clawRes).Namespace(namespace).Create(context.TODO(), claw, metav1.CreateOptions{})
require.NoError(t, err)

replicas := int32(2)
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{MatchLabels: selectorLabels(name)},
Replicas: &replicas,
Template: podTemplateSpec(name),
},
}
err = controllerutil.SetOwnerReference(createdClaw, deployment, scheme.Scheme)
require.NoError(t, err)
err = memberAwait.Create(t, deployment)
require.NoError(t, err)

return deployment
}

func clawResource(name string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "claw.sandbox.redhat.com/v1alpha1",
"kind": "Claw",
"metadata": map[string]interface{}{
"name": name,
},
"spec": map[string]interface{}{
"idle": false,
},
},
}
}

func createReplicaSet(t *testing.T, memberAwait *wait.MemberAwaitility, namespace string) *appsv1.ReplicaSet {
// Standalone ReplicaSet
replicas := int32(2)
Expand Down
27 changes: 26 additions & 1 deletion testsupport/wait/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -1525,7 +1525,7 @@ func (a *MemberAwaitility) WaitForAAP(t *testing.T, name, namespace string, aapR
var aap *unstructured.Unstructured
err := wait.PollUntilContextTimeout(context.TODO(), a.RetryInterval, a.Timeout, true, func(ctx context.Context) (bool, error) {
var err error
aap, err = aapRes.Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{})
aap, err = aapRes.Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return false, nil
Expand All @@ -1541,6 +1541,31 @@ func (a *MemberAwaitility) WaitForAAP(t *testing.T, name, namespace string, aapR
return aap, err
}

// WaitForClaw waits for the Claw resource to reach the expected idle state (spec.idle)
func (a *MemberAwaitility) WaitForClaw(t *testing.T, name, namespace string, clawRes dynamic.NamespaceableResourceInterface, expectedIdled bool) (*unstructured.Unstructured, error) {
t.Logf("waiting for Claw '%s' in namespace '%s'", name, namespace)
var claw *unstructured.Unstructured
err := wait.PollUntilContextTimeout(context.TODO(), a.RetryInterval, a.Timeout, true, func(ctx context.Context) (bool, error) {
var err error
claw, err = clawRes.Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return false, nil
}
return false, err
}
idled, found, err := unstructured.NestedBool(claw.UnstructuredContent(), "spec", "idle")
if err != nil {
return true, err
}
if !found {
return false, nil
}
return expectedIdled == idled, nil
Comment thread
alexeykazakov marked this conversation as resolved.
})
return claw, err
}

// WaitUntilInferenceServiceDeleted waits for the InferenceService resource to be deleted (idled)
func (a *MemberAwaitility) WaitUntilInferenceServiceDeleted(t *testing.T, name, namespace string, inferenceServiceRes dynamic.NamespaceableResourceInterface) error {
t.Logf("waiting for InferenceService '%s' to be deleted in namespace '%s'", name, namespace)
Expand Down
Loading