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
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ controllerManager:
# releases.
dgdrDefaultImage: ""
# AISimulate runner image put into v1beta2 DGDR spec.aisimulate.image when unset.
# Empty leaves it unset, and v1beta2 Requests without an image are rejected.
# Empty derives nvcr.io/nvidia/ai-dynamo/dynamo-planner:<operator-version>.
dgdrAISimulateDefaultImage: ""
resources:
limits:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ type ObjectiveSpec struct {
// AISimulateSpec selects the AISimulate execution artifact and supplies native input.
type AISimulateSpec struct {
// Image selects a Dynamo-compatible AISimulate runner. When omitted, admission
// persists the operator's configured image.
// persists the configured override or a version-matched Dynamo planner image.
// +optional
// +kubebuilder:validation:MinLength=1
Image string `json:"image,omitempty"`
Expand Down
2 changes: 1 addition & 1 deletion deploy/operator/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func main() {
"Default DGDR profiler image, put into DGDR spec.image when unset; empty derives dynamo-planner:<operator-version>")
flag.StringVar(&dgdrAISimulateDefaultImage, "dgdr-aisimulate-default-image", "",
"AISimulate runner image, put into v1beta2 DGDR spec.aisimulate.image when unset; "+
"empty leaves the image unset and the Request is rejected")
"empty derives dynamo-planner:<operator-version>")
opts := zap.Options{
Development: true,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func NewDGDRDefaulter(operatorVersion, defaultImage string) *DGDRDefaulter {

// WithV1beta2 sets the defaults of v1beta2 Requests and returns the defaulter.
func (d *DGDRDefaulter) WithV1beta2(options DGDRV1beta2Options) *DGDRDefaulter {
options.AISimulateImage = defaultPlannerImageFor(d.OperatorVersion, options.AISimulateImage)
d.v1beta2 = options
return d
}
Expand Down Expand Up @@ -127,7 +128,7 @@ func (d *DGDRDefaulter) Default(ctx context.Context, obj runtime.Object) error {
// Image derivation remains create-only so updates do not rewrite an
// intentionally cleared image.
if req.Operation == admissionv1.Create && dgdr.Spec.Image == "" {
if img := d.defaultImageFor(); img != "" {
if img := defaultPlannerImageFor(d.OperatorVersion, d.DefaultImage); img != "" {
dgdr.Spec.Image = img
logger.Info("defaulted spec.image from operator version",
"name", dgdr.Name,
Expand All @@ -140,14 +141,13 @@ func (d *DGDRDefaulter) Default(ctx context.Context, obj runtime.Object) error {
return nil
}

// defaultImageFor returns the profiler image for spec.image: DefaultImage when
// set, else the derived image with a canonical semver tag, or an empty string
// when the operator version cannot be parsed.
func (d *DGDRDefaulter) defaultImageFor() string {
if d.DefaultImage != "" {
return d.DefaultImage
// defaultPlannerImageFor returns explicitImage when set, else the planner image with a
// canonical operator-version tag, or an empty string when the version cannot be parsed.
func defaultPlannerImageFor(operatorVersion, explicitImage string) string {
if explicitImage != "" {
return explicitImage
}
version, err := semver.NewVersion(d.OperatorVersion)
version, err := semver.NewVersion(operatorVersion)
if err != nil {
return ""
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

func TestDGDRDefaulter_defaultImageFor(t *testing.T) {
func TestDefaultPlannerImageFor(t *testing.T) {
// Note: the default planner image is only published starting from Dynamo 1.1.0,
// so these tests use 1.1.0 as the earliest known valid version.
tests := []struct {
Expand Down Expand Up @@ -77,10 +77,9 @@ func TestDGDRDefaulter_defaultImageFor(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := NewDGDRDefaulter(tt.operatorVersion, tt.defaultImage)
got := d.defaultImageFor()
got := defaultPlannerImageFor(tt.operatorVersion, tt.defaultImage)
if got != tt.expectedImage {
t.Errorf("defaultImageFor() = %q, want %q", got, tt.expectedImage)
t.Errorf("defaultPlannerImageFor() = %q, want %q", got, tt.expectedImage)
}
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,34 @@ func aisimulateImageOf(t *testing.T, raw []byte) string {
}

func TestDGDRDefaulter_RoutesByRequestedVersion(t *testing.T) {
d := NewDGDRDefaulter("1.1.0", "").WithV1beta2(DGDRV1beta2Options{AISimulateImage: "aisim:1"})
for _, tt := range []struct {
name string
options DGDRV1beta2Options
want string
}{
{
name: "an omitted image is derived from the operator version",
want: "nvcr.io/nvidia/ai-dynamo/dynamo-planner:1.6.0",
},
{
name: "an explicit default image overrides derivation",
options: DGDRV1beta2Options{AISimulateImage: "registry.example/aisimulate:custom"},
want: "registry.example/aisimulate:custom",
},
} {
t.Run(tt.name, func(t *testing.T) {
d := NewDGDRDefaulter("1.6.0", "").WithV1beta2(tt.options)
dgdr := carrierRequest()
if err := d.Default(ctxForRequest(admissionv1.Create, "v1beta2"), dgdr); err != nil {
t.Fatalf("Default() error = %v", err)
}
if got := aisimulateImageOf(t, dgdr.Spec.V1Beta2.Raw); got != tt.want {
t.Fatalf("aisimulate.image = %q, want %q", got, tt.want)
}
})
}

t.Run("a native v1beta2 request is defaulted from its carrier", func(t *testing.T) {
dgdr := carrierRequest()
if err := d.Default(ctxForRequest(admissionv1.Create, "v1beta2"), dgdr); err != nil {
t.Fatalf("Default() error = %v", err)
}
if got := aisimulateImageOf(t, dgdr.Spec.V1Beta2.Raw); got != "aisim:1" {
t.Fatalf("aisimulate.image = %q, want aisim:1", got)
}
})
d := NewDGDRDefaulter("1.6.0", "").WithV1beta2(DGDRV1beta2Options{})

t.Run("RequestKind decides, not the converted Kind", func(t *testing.T) {
dgdr := carrierRequest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func defaultDynamoGraphDeploymentRequestV1beta2(
type dynamoGraphDeploymentRequestV1beta2DefaultOptions struct {
// hardware is the GPU identity and budget discovered from the cluster.
hardware hardwareDefaultsV1beta2
// aisimulateImage is the operator's configured AISimulate image.
// aisimulateImage is the operator's resolved AISimulate image.
aisimulateImage string
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ type HardwareDiscoverer interface {

// DGDRV1beta2Options configures the defaults of v1beta2 Requests.
type DGDRV1beta2Options struct {
// AISimulateImage is the operator's AISimulate runner image, persisted into
// spec.aisimulate.image when the caller omits it. Empty persists nothing, and
// validation then requires the image to be spelled out.
// AISimulateImage overrides the version-matched Dynamo planner image persisted
// into spec.aisimulate.image when the caller omits it.
AISimulateImage string
// Hardware discovers the cluster's GPUs. Nil disables hardware defaulting, and
// validation then requires the hardware to be spelled out.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const (
frontendImage150 = "registry.example/dynamo-frontend:1.5.0"
legacyEPPImage140 = "registry.example/epp-image:1.4.0"
legacySeedUsername = "operatorenv-legacy-seeder"
admissionAISimulateImage = "registry.example/aisimulate:1.2.0"
admissionAISimulateImage = "nvcr.io/nvidia/ai-dynamo/dynamo-planner:1.6.0"
)

func groveProviderOverride(target, value string) *nvidiacomv1beta1.ProviderOverride {
Expand Down Expand Up @@ -97,8 +97,7 @@ func setupAdmissionWebhooks(mgr ctrl.Manager, opts operatorenv.WebhookSetupOptio
OperatorPrincipal: opts.OperatorPrincipal,
Gate: admissionGate,
DGDRV1beta2: webhookdefaulting.DGDRV1beta2Options{
AISimulateImage: admissionAISimulateImage,
Hardware: admissionHardware,
Hardware: admissionHardware,
},
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3242,7 +3242,7 @@ _Appears in:_

| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `image` _string_ | Image selects a Dynamo-compatible AISimulate runner. When omitted, admission<br />persists the operator's configured image. | | MinLength: 1 <br />Optional: \{\} <br /> |
| `image` _string_ | Image selects a Dynamo-compatible AISimulate runner. When omitted, admission<br />persists the configured override or a version-matched Dynamo planner image. | | MinLength: 1 <br />Optional: \{\} <br /> |
| `versionOverride` _string_ | VersionOverride declares the AISimulate compatibility version as three numeric parts<br />(for example 0.13.0), not a semver version: no v prefix, pre-release or build suffix. It is required<br />when the image tag is not a version and wins over a version in the tag. | | MinLength: 1 <br />Pattern: `^(0\|[1-9][0-9]\{0,3\})\.(0\|[1-9][0-9]\{0,3\})\.(0\|[1-9][0-9]\{0,3\})$` <br />Optional: \{\} <br /> |
| `parameters` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#rawextension-runtime-pkg)_ | Parameters contains native AISimulate input. Field names and nesting use<br />the upstream AISimulate JSON contract, including snake_case names. | | Type: object <br />Optional: \{\} <br /> |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4531,7 +4531,7 @@ AISimulateSpec selects the AISimulate execution artifact and supplies native inp
**Appears in:** [DynamoGraphDeploymentRequestNativeSpec](#v1beta2-dynamographdeploymentrequestnativespec), [DynamoGraphDeploymentRequestSpec](#v1beta2-dynamographdeploymentrequestspec), [DynamoGraphDeploymentRunSpec](#dynamographdeploymentrunspec), [NativeSearchInput](#nativesearchinput)

<ParamField path="image" type="string">
Image selects a Dynamo-compatible AISimulate runner. When omitted, admission persists the operator's configured image.
Image selects a Dynamo-compatible AISimulate runner. When omitted, admission persists the configured override or a version-matched Dynamo planner image.
**Validation:** MinLength: 1 Optional: \&#123;\&#125;
</ParamField>

Expand Down
Loading