Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
test: integration for project specified defaults
Browse files Browse the repository at this point in the history
Signed-off-by: Nick Hale <4175918+njhale@users.noreply.github.com>
  • Loading branch information
njhale committed Feb 15, 2024
1 parent a812196 commit b4ef87e
Show file tree
Hide file tree
Showing 13 changed files with 619 additions and 61 deletions.
157 changes: 122 additions & 35 deletions integration/run/run_test.go

Large diffs are not rendered by default.

54 changes: 36 additions & 18 deletions pkg/apis/internal.admin.acorn.io/v1/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (

internalv1 "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1"
"github.com/acorn-io/z"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func getCurrentClusterComputeClassDefault(ctx context.Context, c client.Client, projectDefault string) (*ClusterComputeClassInstance, error) {
func getCurrentClusterComputeClassDefault(ctx context.Context, c client.Client, projectSpecified string) (*ClusterComputeClassInstance, error) {
var clusterComputeClasses ClusterComputeClassInstanceList
if err := c.List(ctx, &clusterComputeClasses, &client.ListOptions{}); err != nil {
return nil, err
Expand All @@ -20,7 +21,7 @@ func getCurrentClusterComputeClassDefault(ctx context.Context, c client.Client,
return clusterComputeClasses.Items[i].Name < clusterComputeClasses.Items[j].Name
})

var defaultCCC, projectDefaultCCC *ClusterComputeClassInstance
var defaultCCC, projectSpecifiedCCC *ClusterComputeClassInstance
for _, clusterComputeClass := range clusterComputeClasses.Items {
if clusterComputeClass.Default {
if defaultCCC != nil {
Expand All @@ -33,19 +34,19 @@ func getCurrentClusterComputeClassDefault(ctx context.Context, c client.Client,
defaultCCC = z.Pointer(clusterComputeClass)
}

if projectDefault != "" && clusterComputeClass.Name == projectDefault {
projectDefaultCCC = z.Pointer(clusterComputeClass)
if projectSpecified != "" && clusterComputeClass.Name == projectSpecified {
projectSpecifiedCCC = z.Pointer(clusterComputeClass)
}
}

if projectDefaultCCC != nil {
return projectDefaultCCC, nil
if projectSpecifiedCCC != nil {
return projectSpecifiedCCC, nil
}

return defaultCCC, nil
}

func getCurrentProjectComputeClassDefault(ctx context.Context, c client.Client, projectDefault, namespace string) (*ProjectComputeClassInstance, error) {
func getCurrentProjectComputeClassDefault(ctx context.Context, c client.Client, projectSpecified, namespace string) (*ProjectComputeClassInstance, error) {
var projectComputeClasses ProjectComputeClassInstanceList
if err := c.List(ctx, &projectComputeClasses, &client.ListOptions{Namespace: namespace}); err != nil {
return nil, err
Expand All @@ -55,7 +56,7 @@ func getCurrentProjectComputeClassDefault(ctx context.Context, c client.Client,
return projectComputeClasses.Items[i].Name < projectComputeClasses.Items[j].Name
})

var defaultPCC, projectDefaultPCC *ProjectComputeClassInstance
var defaultPCC, projectSpecifiedPCC *ProjectComputeClassInstance
for _, projectComputeClass := range projectComputeClasses.Items {
if projectComputeClass.Default {
if defaultPCC != nil {
Expand All @@ -68,39 +69,56 @@ func getCurrentProjectComputeClassDefault(ctx context.Context, c client.Client,
defaultPCC = z.Pointer(projectComputeClass)
}

if projectDefault != "" && projectComputeClass.Name == projectDefault {
projectDefaultPCC = z.Pointer(projectComputeClass)
if projectSpecified != "" && projectComputeClass.Name == projectSpecified {
projectSpecifiedPCC = z.Pointer(projectComputeClass)
}
}

if projectDefaultPCC != nil {
return projectDefaultPCC, nil
if projectSpecifiedPCC != nil {
return projectSpecifiedPCC, nil
}

return defaultPCC, nil
}

func GetDefaultComputeClass(ctx context.Context, c client.Client, namespace string) (string, error) {
// GetDefaultComputeClassName gets the name of the effective default ComputeClass for a given project namespace.
// The precedence for picking the default ComputeClass is as follows:
// 1. Any ProjectComputeClassInstance (in the project namespace) or ClusterComputeClassInstance that is specified by the
// ProjectInstance's DefaultComputeClass field
// 2. The ProjectComputeClassInstance (in the project namespace) with a Default field set to true
// 3. The ClusterComputeClassInstance with a Default field set to true
//
// If no default ComputeClass is found, an empty string is returned.
func GetDefaultComputeClassName(ctx context.Context, c client.Client, namespace string) (string, error) {
var project internalv1.ProjectInstance
if err := c.Get(ctx, client.ObjectKey{Name: namespace}, &project); err != nil {
return "", fmt.Errorf("failed to get projectinstance to determine default compute class: %w", err)
}
projectDefault := project.Status.DefaultComputeClass
projectSpecified := project.Status.DefaultComputeClass

pcc, err := getCurrentProjectComputeClassDefault(ctx, c, projectDefault, namespace)
var defaultComputeClasses []string
pcc, err := getCurrentProjectComputeClassDefault(ctx, c, projectSpecified, namespace)
if err != nil {
return "", err
}
if pcc != nil {
return pcc.Name, nil
defaultComputeClasses = append(defaultComputeClasses, pcc.Name)
}

ccc, err := getCurrentClusterComputeClassDefault(ctx, c, projectDefault)
ccc, err := getCurrentClusterComputeClassDefault(ctx, c, projectSpecified)
if err != nil {
return "", err
}
if ccc != nil {
return ccc.Name, nil
defaultComputeClasses = append(defaultComputeClasses, ccc.Name)
}

if sets.New(defaultComputeClasses...).Has(projectSpecified) {
return projectSpecified, nil
}

if len(defaultComputeClasses) > 0 {
return defaultComputeClasses[0], nil
}

return "", nil
Expand Down
208 changes: 208 additions & 0 deletions pkg/apis/internal.admin.acorn.io/v1/default_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package v1_test

import (
"context"
"testing"

internalv1 "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1"
internaladminv1 "github.com/acorn-io/runtime/pkg/apis/internal.admin.acorn.io/v1"
"github.com/acorn-io/runtime/pkg/scheme"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestGetDefaultComputeClass(t *testing.T) {
ctx := context.Background()

type args struct {
namespace string
client kclient.Client
}
type expected struct {
computeClassName string
error bool
}
for _, tt := range []struct {
name string
args args
expected expected
}{
{
name: "No defaults",
args: args{
namespace: "pcc-project",
client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(
&internalv1.ProjectInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "pcc-project",
},
},
&internaladminv1.ProjectComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "project-compute-class",
Namespace: "pcc-project",
},
},
&internaladminv1.ClusterComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-compute-class",
},
},
).Build(),
},
},
{
name: "Default cluster compute class",
args: args{
namespace: "pcc-project",
client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(
&internalv1.ProjectInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "pcc-project",
},
},
&internaladminv1.ProjectComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "other-project-compute-class",
Namespace: "pcc-project",
},
},
&internaladminv1.ProjectComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "project-compute-class",
Namespace: "pcc-project",
},
},
&internaladminv1.ClusterComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "other-cluster-compute-class",
},
},
&internaladminv1.ClusterComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-compute-class",
},
Default: true,
},
).Build(),
},
expected: expected{
computeClassName: "cluster-compute-class",
},
},
{
name: "Project compute classes take precedence over cluster compute classes",
args: args{
namespace: "pcc-project",
client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(
&internalv1.ProjectInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "pcc-project",
},
},
&internaladminv1.ProjectComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "other-project-compute-class",
Namespace: "pcc-project",
},
},
&internaladminv1.ProjectComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "project-compute-class",
Namespace: "pcc-project",
},
Default: true,
},
&internaladminv1.ClusterComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "other-cluster-compute-class",
},
},
&internaladminv1.ClusterComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-compute-class",
},
Default: true,
},
).Build(),
},
expected: expected{
computeClassName: "project-compute-class",
},
},
{
name: "Project specified compute class takes precedence over default project compute class",
args: args{
namespace: "pcc-project",
client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(
&internalv1.ProjectInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "pcc-project",
},
Status: internalv1.ProjectInstanceStatus{
DefaultComputeClass: "project-specified-default",
},
},
&internaladminv1.ProjectComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "self-specified-default",
Namespace: "pcc-project",
},
Default: true,
},
&internaladminv1.ClusterComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "project-specified-default",
},
},
).Build(),
},
expected: expected{
computeClassName: "project-specified-default",
},
},
{
name: "Project specified compute class not found",
args: args{
namespace: "pcc-project",
client: fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(
&internalv1.ProjectInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "pcc-project",
},
Spec: internalv1.ProjectInstanceSpec{
DefaultComputeClass: "project-specified-default",
},
},
&internaladminv1.ClusterComputeClassInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "self-specified-default",
},
Default: true,
},
).Build(),
},
expected: expected{
computeClassName: "self-specified-default",
},
},
} {
t.Run(tt.name, func(t *testing.T) {
kc := tt.args.client
if kc == nil {
kc = fake.NewClientBuilder().WithScheme(scheme.Scheme).Build()
}

actualComputeClassName, err := internaladminv1.GetDefaultComputeClassName(ctx, tt.args.client, tt.args.namespace)
if tt.expected.error {
require.Error(t, err)
} else {
require.NoError(t, err)
}

require.Equal(t, tt.expected.computeClassName, actualComputeClassName)
})
}
}
2 changes: 1 addition & 1 deletion pkg/computeclasses/computeclasses.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func GetClassForWorkload(ctx context.Context, c client.Client, computeClasses in
ccName := GetComputeClassNameForWorkload(workload, container, computeClasses)
if ccName == "" {
var err error
ccName, err = internaladminv1.GetDefaultComputeClass(ctx, c, namespace)
ccName, err = internaladminv1.GetDefaultComputeClassName(ctx, c, namespace)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/defaults/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func addDefaultMemory(req router.Request, cfg *apiv1.Config, appInstance *v1.App
if value, ok := appInstance.Spec.ComputeClasses[""]; ok {
defaultCC = value
} else {
defaultCC, err = adminv1.GetDefaultComputeClass(req.Ctx, req.Client, appInstance.Namespace)
defaultCC, err = adminv1.GetDefaultComputeClassName(req.Ctx, req.Client, appInstance.Namespace)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/controller/resolvedofferings/computeclass.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func resolveComputeClasses(req router.Request, cfg *apiv1.Config, appInstance *v
if value, ok := appInstance.Spec.ComputeClasses[""]; ok {
defaultCC = value
} else {
defaultCC, err = adminv1.GetDefaultComputeClass(req.Ctx, req.Client, appInstance.Namespace)
defaultCC, err = adminv1.GetDefaultComputeClassName(req.Ctx, req.Client, appInstance.Namespace)
if err != nil {
return err
}
Expand Down Expand Up @@ -132,7 +132,7 @@ func resolveComputeClassForContainer(req router.Request, appInstance *v1.AppInst
// Next, determine the memory request. This is the order of priority:
// 1. runtime-level overrides from the user (in app.Spec)
// 2. defaults in the acorn image
// 3. defaults from compute class
// 3. defaults from compute class and project status
// 4. global default

var (
Expand Down
4 changes: 4 additions & 0 deletions pkg/controller/resolvedofferings/computeclass_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,7 @@ func TestWithAcornfileMemoryAndSpecOverride(t *testing.T) {
func TestRemovedContainer(t *testing.T) {
tester.DefaultTest(t, scheme.Scheme, "testdata/computeclass/removed-container", Calculate)
}

func TestProjectSpecifiedDefault(t *testing.T) {
tester.DefaultTest(t, scheme.Scheme, "testdata/computeclass/project-specified-default", Calculate)
}
Loading

0 comments on commit b4ef87e

Please sign in to comment.