diff --git a/go/core/internal/controller/translator/agent/adk_api_translator.go b/go/core/internal/controller/translator/agent/adk_api_translator.go index 1aba032f0..50b680756 100644 --- a/go/core/internal/controller/translator/agent/adk_api_translator.go +++ b/go/core/internal/controller/translator/agent/adk_api_translator.go @@ -128,6 +128,16 @@ var PythonADKFullImageDigest string var GoADKImageDigest string var GoADKFullImageDigest string +// DefaultGoImageConfig is the image config for the Go (ADK) runtime agent. +// Regular agents reference it by tag; sandbox agents pin by digest via +// GoADKImageDigest / GoADKFullImageDigest. +var DefaultGoImageConfig = ImageConfig{ + Registry: "ghcr.io", + Tag: version.Get().Version, + PullPolicy: string(corev1.PullIfNotPresent), + Repository: "kagent-dev/kagent/golang-adk", +} + // DefaultSkillsInitImageConfig is the image config for the skills-init container // that clones skill repositories from Git and pulls OCI skill images. var DefaultSkillsInitImageConfig = ImageConfig{ diff --git a/go/core/internal/controller/translator/agent/deployments.go b/go/core/internal/controller/translator/agent/deployments.go index 6b60a5692..757a1bd0d 100644 --- a/go/core/internal/controller/translator/agent/deployments.go +++ b/go/core/internal/controller/translator/agent/deployments.go @@ -4,7 +4,6 @@ import ( "fmt" "maps" "slices" - "strings" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -92,31 +91,14 @@ func getDefaultNodeSelector(incoming map[string]string) map[string]string { return nodeSelector } -// getRuntimeImageRepository returns the image repository for a given runtime. -// It respects DefaultImageConfig.Repository for the Python runtime, and derives -// the Go runtime repository by replacing the last path segment with "golang-adk". -// This ensures custom repository configurations (e.g., --image-repository flag) work correctly. +// getRuntimeImageRepository returns the image repository for a given runtime: +// DefaultGoImageConfig.Repository for the Go runtime, DefaultImageConfig.Repository +// otherwise. func getRuntimeImageRepository(runtime v1alpha2.DeclarativeRuntime) string { - switch runtime { - case v1alpha2.DeclarativeRuntime_Go: - // Derive Go runtime repository from the default Python repository - // by replacing the last segment (typically "app") with "golang-adk". - // This respects any custom repository configuration. - pythonRepo := DefaultImageConfig.Repository - lastSlash := strings.LastIndex(pythonRepo, "/") - if lastSlash == -1 { - // No slash found, repository is just the image name - return "golang-adk" - } - baseRepo := pythonRepo[:lastSlash] - return baseRepo + "/golang-adk" - case v1alpha2.DeclarativeRuntime_Python: - // Use the configured Python repository as-is - return DefaultImageConfig.Repository - default: - // Default to Python (should never happen due to enum validation) - return DefaultImageConfig.Repository + if runtime == v1alpha2.DeclarativeRuntime_Go { + return DefaultGoImageConfig.Repository } + return DefaultImageConfig.Repository } // validateExtraContainers checks that none of the extra containers use the @@ -143,7 +125,7 @@ func resolvePythonRuntimeImage(registry string, full, pinDigest bool) (string, e digest = PythonADKFullImageDigest imageLabel = "app-full" } - return resolveRuntimeImage(registry, repo, digest, imageLabel, full, pinDigest) + return resolveRuntimeImage(registry, repo, DefaultImageConfig.Tag, digest, imageLabel, full, pinDigest) } func resolveGoRuntimeImage(registry string, full, pinDigest bool) (string, error) { @@ -154,7 +136,7 @@ func resolveGoRuntimeImage(registry string, full, pinDigest bool) (string, error digest = GoADKFullImageDigest imageLabel = "golang-adk-full" } - return resolveRuntimeImage(registry, repo, digest, imageLabel, full, pinDigest) + return resolveRuntimeImage(registry, repo, DefaultGoImageConfig.Tag, digest, imageLabel, full, pinDigest) } // resolveRuntimeImage builds the image reference for a declarative agent runtime. @@ -168,9 +150,8 @@ func resolveGoRuntimeImage(registry string, full, pinDigest bool) (string, error // Sandbox agents require pinDigest: Substrate ActorTemplate validation rejects // image refs without a digest, so those use the link-time (or flag-overridden) // runtime image digests. -func resolveRuntimeImage(registry, repository, digest, imageLabel string, full, pinDigest bool) (string, error) { +func resolveRuntimeImage(registry, repository, tag, digest, imageLabel string, full, pinDigest bool) (string, error) { if !pinDigest { - tag := DefaultImageConfig.Tag if full { tag += "-full" } @@ -210,8 +191,16 @@ func resolveInlineDeployment(agent v1alpha2.AgentObject, mdd *modelDeploymentDat // Determine runtime (defaults to python when spec.declarative.runtime is unset). runtime := v1alpha2.EffectiveDeclarativeRuntime(agent.GetAgentSpec()) - // Get registry - registry := DefaultImageConfig.Registry + // Resolve base registry and pull policy; the Go runtime uses DefaultGoImageConfig. + baseRegistry := DefaultImageConfig.Registry + basePullPolicy := DefaultImageConfig.PullPolicy + if runtime == v1alpha2.DeclarativeRuntime_Go { + baseRegistry = DefaultGoImageConfig.Registry + basePullPolicy = DefaultGoImageConfig.PullPolicy + } + + // Per-agent spec overrides take precedence over all defaults. + registry := baseRegistry if spec.ImageRegistry != "" { registry = spec.ImageRegistry } @@ -236,7 +225,7 @@ func resolveInlineDeployment(agent v1alpha2.AgentObject, mdd *modelDeploymentDat } } - imagePullPolicy := corev1.PullPolicy(DefaultImageConfig.PullPolicy) + imagePullPolicy := corev1.PullPolicy(basePullPolicy) if spec.ImagePullPolicy != "" { imagePullPolicy = corev1.PullPolicy(spec.ImagePullPolicy) } diff --git a/go/core/internal/controller/translator/agent/imageconfig_test.go b/go/core/internal/controller/translator/agent/imageconfig_test.go index b8d3f7aa2..5b89607d6 100644 --- a/go/core/internal/controller/translator/agent/imageconfig_test.go +++ b/go/core/internal/controller/translator/agent/imageconfig_test.go @@ -131,8 +131,13 @@ func TestResolvePythonRuntimeImageWithoutDigest(t *testing.T) { func TestResolveRuntimeImageByTag(t *testing.T) { originalTag := DefaultImageConfig.Tag - t.Cleanup(func() { DefaultImageConfig.Tag = originalTag }) + originalGoTag := DefaultGoImageConfig.Tag + t.Cleanup(func() { + DefaultImageConfig.Tag = originalTag + DefaultGoImageConfig.Tag = originalGoTag + }) DefaultImageConfig.Tag = "v9.9.9" + DefaultGoImageConfig.Tag = "v8.8.8" got, err := resolvePythonRuntimeImage("my-registry.example.com", false, false) require.NoError(t, err) @@ -144,11 +149,11 @@ func TestResolveRuntimeImageByTag(t *testing.T) { got, err = resolveGoRuntimeImage("my-registry.example.com", false, false) require.NoError(t, err) - require.Equal(t, "my-registry.example.com/kagent-dev/kagent/golang-adk:v9.9.9", got) + require.Equal(t, "my-registry.example.com/kagent-dev/kagent/golang-adk:v8.8.8", got) got, err = resolveGoRuntimeImage("my-registry.example.com", true, false) require.NoError(t, err) - require.Equal(t, "my-registry.example.com/kagent-dev/kagent/golang-adk:v9.9.9-full", got) + require.Equal(t, "my-registry.example.com/kagent-dev/kagent/golang-adk:v8.8.8-full", got) } func TestResolveRuntimeImageByTagIgnoresMissingDigest(t *testing.T) { diff --git a/go/core/internal/controller/translator/agent/runtime_test.go b/go/core/internal/controller/translator/agent/runtime_test.go index 78bff6cd3..b64bf45b0 100644 --- a/go/core/internal/controller/translator/agent/runtime_test.go +++ b/go/core/internal/controller/translator/agent/runtime_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" schemev1 "k8s.io/client-go/kubernetes/scheme" @@ -105,7 +106,7 @@ func TestRuntime_GoRuntime(t *testing.T) { require.Len(t, deployment.Spec.Template.Spec.Containers, 1) container := deployment.Spec.Template.Spec.Containers[0] assert.Contains(t, container.Image, "golang-adk", "Image should use golang-adk repository") - assert.Contains(t, container.Image, ":"+translator.DefaultImageConfig.Tag, "Go runtime should reference the golang-adk image by tag") + assert.Contains(t, container.Image, ":"+translator.DefaultGoImageConfig.Tag, "Go runtime should reference the golang-adk image by tag") assert.NotContains(t, container.Image, "@sha256:", "regular agents must not use digest-pinned images") // Verify Go runtime readiness probe timings (fast startup) @@ -179,7 +180,7 @@ func TestRuntime_GoRuntimeWithSkillsUsesFullImageTag(t *testing.T) { require.Len(t, deployment.Spec.Template.Spec.Containers, 1) container := deployment.Spec.Template.Spec.Containers[0] assert.Contains(t, container.Image, "golang-adk", "Image should use golang-adk repository") - assert.Contains(t, container.Image, ":"+translator.DefaultImageConfig.Tag+"-full", "Go runtime with skills should reference the golang-adk full image by tag") + assert.Contains(t, container.Image, ":"+translator.DefaultGoImageConfig.Tag+"-full", "Go runtime with skills should reference the golang-adk full image by tag") assert.NotContains(t, container.Image, "@sha256:", "regular agents must not use digest-pinned images") } @@ -339,70 +340,45 @@ func TestRuntime_DefaultToPython(t *testing.T) { assert.Equal(t, int32(15), container.ReadinessProbe.PeriodSeconds, "Should default to Python's 15s period") } -func TestRuntime_CustomRepositoryPath(t *testing.T) { - withGoRuntimeDigests(t) - ctx := context.Background() - - // Save original DefaultImageConfig.Repository and restore after test - originalRepo := translator.DefaultImageConfig.Repository - defer func() { - translator.DefaultImageConfig.Repository = originalRepo - }() +// withGoImageConfig sets DefaultGoImageConfig for the duration of a test and restores it +// via t.Cleanup. +func withGoImageConfig(t *testing.T, cfg translator.ImageConfig) { + t.Helper() + original := translator.DefaultGoImageConfig + translator.DefaultGoImageConfig = cfg + t.Cleanup(func() { translator.DefaultGoImageConfig = original }) +} - // Set a custom repository path (simulating --image-repository flag) - translator.DefaultImageConfig.Repository = "my-registry.com/custom/app" +// TestRuntime_GoImageConfig_FlatRepository tests that DefaultGoImageConfig.Repository is +// used verbatim, enabling flat-name registry layouts (e.g. "kagent-golang-adk"). +func TestRuntime_GoImageConfig_FlatRepository(t *testing.T) { + withGoImageConfig(t, translator.ImageConfig{Registry: "my-registry.io", Repository: "kagent-golang-adk", Tag: "v0.0.0-test"}) - // Create agent with Go runtime + ctx := context.Background() agent := &v1alpha2.Agent{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-custom-repo-agent", - Namespace: "test", - }, + ObjectMeta: metav1.ObjectMeta{Name: "flat-repo-agent", Namespace: "test"}, Spec: v1alpha2.AgentSpec{ Type: v1alpha2.AgentType_Declarative, Declarative: &v1alpha2.DeclarativeAgentSpec{ Runtime: v1alpha2.DeclarativeRuntime_Go, - SystemMessage: "Test Go agent with custom repo", + SystemMessage: "test", ModelConfig: "test-model", }, }, } - - // Create model config modelConfig := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-model", - Namespace: "test", - }, - Spec: v1alpha2.ModelConfigSpec{ - Provider: "OpenAI", - Model: "gpt-4o", - }, + ObjectMeta: metav1.ObjectMeta{Name: "test-model", Namespace: "test"}, + Spec: v1alpha2.ModelConfigSpec{Provider: "OpenAI", Model: "gpt-4o"}, } - // Set up fake client scheme := schemev1.Scheme - err := v1alpha2.AddToScheme(scheme) - require.NoError(t, err) - - kubeClient := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(agent, modelConfig). - Build() + require.NoError(t, v1alpha2.AddToScheme(scheme)) + kubeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(agent, modelConfig).Build() + translatorInstance := translator.NewAdkApiTranslator(kubeClient, types.NamespacedName{Namespace: "test", Name: "test-model"}, nil, "", nil) - // Create translator - defaultModel := types.NamespacedName{ - Namespace: "test", - Name: "test-model", - } - translatorInstance := translator.NewAdkApiTranslator(kubeClient, defaultModel, nil, "", nil) - - // Translate agent result, err := translator.TranslateAgent(ctx, translatorInstance, agent) require.NoError(t, err) - require.NotNil(t, result) - // Extract deployment from manifest var deployment *appsv1.Deployment for _, obj := range result.Manifest { if dep, ok := obj.(*appsv1.Deployment); ok { @@ -410,73 +386,105 @@ func TestRuntime_CustomRepositoryPath(t *testing.T) { break } } - require.NotNil(t, deployment, "Deployment should be in manifest") + require.NotNil(t, deployment) - // Verify container image uses custom repository base with golang-adk - require.Len(t, deployment.Spec.Template.Spec.Containers, 1) - container := deployment.Spec.Template.Spec.Containers[0] - assert.Contains(t, container.Image, "my-registry.com/custom/golang-adk", "Image should use custom repository with golang-adk") - assert.Contains(t, container.Image, ":"+translator.DefaultImageConfig.Tag, "Go runtime should reference the golang-adk image by tag") - assert.NotContains(t, container.Image, "@sha256:", "regular agents must not use digest-pinned images") + img := deployment.Spec.Template.Spec.Containers[0].Image + assert.Equal(t, "my-registry.io/kagent-golang-adk:v0.0.0-test", img, "Image should use the explicit flat repository verbatim") } -func TestRuntime_CustomRepositoryPath_WithSkillsUsesFullTag(t *testing.T) { - withGoRuntimeDigests(t) - ctx := context.Background() +// TestRuntime_GoImageConfig_ExplicitRegistry tests that DefaultGoImageConfig.Registry is +// applied to the Go runtime while leaving the Python runtime's registry unaffected. +func TestRuntime_GoImageConfig_ExplicitRegistry(t *testing.T) { + withGoImageConfig(t, translator.ImageConfig{Registry: "my.registry.io", Repository: "kagent-dev/kagent/golang-adk", Tag: "v0.0.0-test"}) - originalRepo := translator.DefaultImageConfig.Repository - defer func() { - translator.DefaultImageConfig.Repository = originalRepo - }() - translator.DefaultImageConfig.Repository = "my-registry.com/custom/app" - - agent := &v1alpha2.Agent{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-custom-repo-skills-agent", - Namespace: "test", - }, + ctx := context.Background() + goAgent := &v1alpha2.Agent{ + ObjectMeta: metav1.ObjectMeta{Name: "go-agent", Namespace: "test"}, Spec: v1alpha2.AgentSpec{ Type: v1alpha2.AgentType_Declarative, Declarative: &v1alpha2.DeclarativeAgentSpec{ Runtime: v1alpha2.DeclarativeRuntime_Go, - SystemMessage: "Test Go agent with custom repo and skills", + SystemMessage: "go", ModelConfig: "test-model", }, - Skills: &v1alpha2.SkillForAgent{ - Refs: []string{"example.com/skill:latest"}, + }, + } + pythonAgent := &v1alpha2.Agent{ + ObjectMeta: metav1.ObjectMeta{Name: "python-agent", Namespace: "test"}, + Spec: v1alpha2.AgentSpec{ + Type: v1alpha2.AgentType_Declarative, + Declarative: &v1alpha2.DeclarativeAgentSpec{ + Runtime: v1alpha2.DeclarativeRuntime_Python, + SystemMessage: "python", + ModelConfig: "test-model", }, }, } - modelConfig := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-model", - Namespace: "test", - }, - Spec: v1alpha2.ModelConfigSpec{ - Provider: "OpenAI", - Model: "gpt-4o", - }, + ObjectMeta: metav1.ObjectMeta{Name: "test-model", Namespace: "test"}, + Spec: v1alpha2.ModelConfigSpec{Provider: "OpenAI", Model: "gpt-4o"}, } scheme := schemev1.Scheme - err := v1alpha2.AddToScheme(scheme) + require.NoError(t, v1alpha2.AddToScheme(scheme)) + kubeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(goAgent, pythonAgent, modelConfig).Build() + tn := types.NamespacedName{Namespace: "test", Name: "test-model"} + translatorInstance := translator.NewAdkApiTranslator(kubeClient, tn, nil, "", nil) + + goResult, err := translator.TranslateAgent(ctx, translatorInstance, goAgent) + require.NoError(t, err) + pythonResult, err := translator.TranslateAgent(ctx, translatorInstance, pythonAgent) require.NoError(t, err) - kubeClient := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(agent, modelConfig). - Build() + goImage, pythonImage := "", "" + for _, obj := range goResult.Manifest { + if dep, ok := obj.(*appsv1.Deployment); ok { + goImage = dep.Spec.Template.Spec.Containers[0].Image + break + } + } + for _, obj := range pythonResult.Manifest { + if dep, ok := obj.(*appsv1.Deployment); ok { + pythonImage = dep.Spec.Template.Spec.Containers[0].Image + break + } + } - defaultModel := types.NamespacedName{ - Namespace: "test", - Name: "test-model", + assert.Contains(t, goImage, "my.registry.io/", "Go image should use the explicit Go registry") + assert.NotContains(t, pythonImage, "my.registry.io/", "Python image must not use the Go-specific registry") +} + +// TestRuntime_GoImageConfig_FlatRepository_WithSkillsUsesFullTag verifies that the +// "-full" tag variant is still selected when DefaultGoImageConfig.Repository is set +// explicitly and the agent uses skills (which requires the full image). +func TestRuntime_GoImageConfig_FlatRepository_WithSkillsUsesFullTag(t *testing.T) { + withGoImageConfig(t, translator.ImageConfig{Registry: "my-registry.io", Repository: "kagent-golang-adk", Tag: "v0.0.0-test"}) + + ctx := context.Background() + agent := &v1alpha2.Agent{ + ObjectMeta: metav1.ObjectMeta{Name: "flat-repo-skills-agent", Namespace: "test"}, + Spec: v1alpha2.AgentSpec{ + Type: v1alpha2.AgentType_Declarative, + Declarative: &v1alpha2.DeclarativeAgentSpec{ + Runtime: v1alpha2.DeclarativeRuntime_Go, + SystemMessage: "test", + ModelConfig: "test-model", + }, + Skills: &v1alpha2.SkillForAgent{Refs: []string{"example.com/skill:latest"}}, + }, } - translatorInstance := translator.NewAdkApiTranslator(kubeClient, defaultModel, nil, "", nil) + modelConfig := &v1alpha2.ModelConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "test-model", Namespace: "test"}, + Spec: v1alpha2.ModelConfigSpec{Provider: "OpenAI", Model: "gpt-4o"}, + } + + scheme := schemev1.Scheme + require.NoError(t, v1alpha2.AddToScheme(scheme)) + kubeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(agent, modelConfig).Build() + translatorInstance := translator.NewAdkApiTranslator(kubeClient, types.NamespacedName{Namespace: "test", Name: "test-model"}, nil, "", nil) result, err := translator.TranslateAgent(ctx, translatorInstance, agent) require.NoError(t, err) - require.NotNil(t, result) var deployment *appsv1.Deployment for _, obj := range result.Manifest { @@ -485,11 +493,70 @@ func TestRuntime_CustomRepositoryPath_WithSkillsUsesFullTag(t *testing.T) { break } } - require.NotNil(t, deployment, "Deployment should be in manifest") + require.NotNil(t, deployment) - require.Len(t, deployment.Spec.Template.Spec.Containers, 1) - container := deployment.Spec.Template.Spec.Containers[0] - assert.Contains(t, container.Image, "my-registry.com/custom/golang-adk", "Image should use custom repository with golang-adk") - assert.Contains(t, container.Image, ":"+translator.DefaultImageConfig.Tag+"-full", "Go runtime with skills should reference the golang-adk full image by tag") - assert.NotContains(t, container.Image, "@sha256:", "regular agents must not use digest-pinned images") + img := deployment.Spec.Template.Spec.Containers[0].Image + assert.Equal(t, "my-registry.io/kagent-golang-adk:v0.0.0-test-full", img, "Skills agent should use the full tag variant of the explicit repository") +} + +// TestRuntime_GoImageConfig_ExplicitPullPolicy tests that DefaultGoImageConfig.PullPolicy +// is applied to the Go runtime and does not affect the Python runtime. +func TestRuntime_GoImageConfig_ExplicitPullPolicy(t *testing.T) { + withGoImageConfig(t, translator.ImageConfig{Registry: "my-registry.io", Repository: "kagent-dev/kagent/golang-adk", Tag: "v0.0.0-test", PullPolicy: "Always"}) + + ctx := context.Background() + goAgent := &v1alpha2.Agent{ + ObjectMeta: metav1.ObjectMeta{Name: "go-pullpolicy-agent", Namespace: "test"}, + Spec: v1alpha2.AgentSpec{ + Type: v1alpha2.AgentType_Declarative, + Declarative: &v1alpha2.DeclarativeAgentSpec{ + Runtime: v1alpha2.DeclarativeRuntime_Go, + SystemMessage: "test", + ModelConfig: "test-model", + }, + }, + } + pythonAgent := &v1alpha2.Agent{ + ObjectMeta: metav1.ObjectMeta{Name: "python-pullpolicy-agent", Namespace: "test"}, + Spec: v1alpha2.AgentSpec{ + Type: v1alpha2.AgentType_Declarative, + Declarative: &v1alpha2.DeclarativeAgentSpec{ + Runtime: v1alpha2.DeclarativeRuntime_Python, + SystemMessage: "test", + ModelConfig: "test-model", + }, + }, + } + modelConfig := &v1alpha2.ModelConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "test-model", Namespace: "test"}, + Spec: v1alpha2.ModelConfigSpec{Provider: "OpenAI", Model: "gpt-4o"}, + } + + scheme := schemev1.Scheme + require.NoError(t, v1alpha2.AddToScheme(scheme)) + kubeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(goAgent, pythonAgent, modelConfig).Build() + tn := types.NamespacedName{Namespace: "test", Name: "test-model"} + translatorInstance := translator.NewAdkApiTranslator(kubeClient, tn, nil, "", nil) + + goResult, err := translator.TranslateAgent(ctx, translatorInstance, goAgent) + require.NoError(t, err) + pythonResult, err := translator.TranslateAgent(ctx, translatorInstance, pythonAgent) + require.NoError(t, err) + + goPullPolicy, pythonPullPolicy := corev1.PullPolicy(""), corev1.PullPolicy("") + for _, obj := range goResult.Manifest { + if dep, ok := obj.(*appsv1.Deployment); ok { + goPullPolicy = dep.Spec.Template.Spec.Containers[0].ImagePullPolicy + break + } + } + for _, obj := range pythonResult.Manifest { + if dep, ok := obj.(*appsv1.Deployment); ok { + pythonPullPolicy = dep.Spec.Template.Spec.Containers[0].ImagePullPolicy + break + } + } + + assert.Equal(t, corev1.PullAlways, goPullPolicy, "Go runtime should use the explicit pull policy") + assert.NotEqual(t, corev1.PullAlways, pythonPullPolicy, "Python runtime must not inherit the Go-specific pull policy") } diff --git a/go/core/pkg/app/app.go b/go/core/pkg/app/app.go index 37a8ce3fe..897514bf0 100644 --- a/go/core/pkg/app/app.go +++ b/go/core/pkg/app/app.go @@ -207,6 +207,10 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.StringVar(&agent_translator.DefaultSkillsInitImageConfig.Tag, "skills-init-image-tag", agent_translator.DefaultSkillsInitImageConfig.Tag, "The tag to use for the skills init image.") commandLine.StringVar(&agent_translator.DefaultSkillsInitImageConfig.PullPolicy, "skills-init-image-pull-policy", agent_translator.DefaultSkillsInitImageConfig.PullPolicy, "The pull policy to use for the skills init image.") commandLine.StringVar(&agent_translator.DefaultSkillsInitImageConfig.Repository, "skills-init-image-repository", agent_translator.DefaultSkillsInitImageConfig.Repository, "The repository to use for the skills init image.") + commandLine.StringVar(&agent_translator.DefaultGoImageConfig.Registry, "go-image-registry", agent_translator.DefaultGoImageConfig.Registry, "The registry to use for the Go (ADK) runtime agent image.") + commandLine.StringVar(&agent_translator.DefaultGoImageConfig.Repository, "go-image-repository", agent_translator.DefaultGoImageConfig.Repository, "The repository to use for the Go (ADK) runtime agent image.") + commandLine.StringVar(&agent_translator.DefaultGoImageConfig.Tag, "go-image-tag", agent_translator.DefaultGoImageConfig.Tag, "The tag to use for the Go (ADK) runtime agent image.") + commandLine.StringVar(&agent_translator.DefaultGoImageConfig.PullPolicy, "go-image-pull-policy", agent_translator.DefaultGoImageConfig.PullPolicy, "The pull policy to use for the Go (ADK) runtime agent image.") commandLine.StringVar(&cfg.Substrate.AteAPIEndpoint, "substrate-ate-api-endpoint", "", "gRPC target for Agent Substrate ate-api (e.g. dns:///api.ate-system.svc:443). Enables substrate AgentHarness runtime when set.") commandLine.StringVar(&cfg.Substrate.AteAPITokenFile, "substrate-ate-api-token-file", "", "Path to a Kubernetes projected service account token used as an ate-api bearer token.") @@ -348,6 +352,15 @@ func Start(getExtensionConfig GetExtensionConfig, extraSources []migrations.Sour setupLog.Info("Starting KAgent Controller", "version", Version, "git_commit", GitCommit, "build_date", BuildDate, "config", cfg) + // The Go (ADK) runtime image is configured independently of the agent image. + // Warn operators who mirror only the agent image so a Go-runtime pod does not + // silently pull from the public registry. + if agent_translator.DefaultGoImageConfig.Registry != agent_translator.DefaultImageConfig.Registry { + setupLog.Info("Go (ADK) runtime image registry differs from the agent image registry; set --go-image-registry (GO_IMAGE_REGISTRY) to mirror it", + "goImageRegistry", agent_translator.DefaultGoImageConfig.Registry, + "agentImageRegistry", agent_translator.DefaultImageConfig.Registry) + } + // if the enable-http2 flag is false (the default), http/2 should be disabled // due to its vulnerabilities. More specifically, disabling http/2 will // prevent from being vulnerable to the HTTP/2 Stream Cancellation and diff --git a/helm/kagent/templates/controller-configmap.yaml b/helm/kagent/templates/controller-configmap.yaml index a3b519b6a..110902341 100644 --- a/helm/kagent/templates/controller-configmap.yaml +++ b/helm/kagent/templates/controller-configmap.yaml @@ -23,6 +23,10 @@ data: SKILLS_INIT_IMAGE_REGISTRY: {{ .Values.controller.skillsInitImage.registry | default .Values.registry | quote }} SKILLS_INIT_IMAGE_REPOSITORY: {{ .Values.controller.skillsInitImage.repository | quote }} SKILLS_INIT_IMAGE_TAG: {{ coalesce .Values.controller.skillsInitImage.tag .Values.tag .Chart.Version | quote }} + GO_IMAGE_PULL_POLICY: {{ .Values.controller.goAgentImage.pullPolicy | default .Values.imagePullPolicy | quote }} + GO_IMAGE_REGISTRY: {{ .Values.controller.goAgentImage.registry | default .Values.registry | quote }} + GO_IMAGE_REPOSITORY: {{ .Values.controller.goAgentImage.repository | quote }} + GO_IMAGE_TAG: {{ coalesce .Values.controller.goAgentImage.tag .Values.tag .Chart.Version | quote }} LEADER_ELECT: {{ include "kagent.leaderElectionEnabled" . | quote }} # OpenTelemetry Configuration OTEL_TRACING_ENABLED: {{ .Values.otel.tracing.enabled | quote }} diff --git a/helm/kagent/values.yaml b/helm/kagent/values.yaml index c4617d007..707c6e733 100644 --- a/helm/kagent/values.yaml +++ b/helm/kagent/values.yaml @@ -208,6 +208,12 @@ controller: repository: kagent-dev/kagent/skills-init tag: "" # Will default to global, then Chart version pullPolicy: "" + # -- The image used for the Go (ADK) runtime agent. + goAgentImage: + registry: "" + repository: kagent-dev/kagent/golang-adk + tag: "" # Will default to global, then Chart version + pullPolicy: "" # -- @deprecated Removed in 0.10.0. The A2A SDK now handles SSE buffering and timeouts # internally. These values have no effect and will be removed in a future release. streaming: null