From d0a18b6a6ce894e11326a0d83f6059274b12301c Mon Sep 17 00:00:00 2001 From: Hans Knecht Date: Tue, 24 Mar 2026 17:06:16 +0100 Subject: [PATCH 1/2] feat: add bedrock credential type for AWS Bedrock authentication Add a new `bedrock` credential type that injects AWS environment variables (CLAUDE_CODE_USE_BEDROCK, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION) from a referenced Secret, with optional support for AWS_SESSION_TOKEN and ANTHROPIC_BEDROCK_BASE_URL. Refactor credential injection into a centralized credentialEnvVars() function so that adding future providers (e.g. Vertex) requires only a new case block. Co-Authored-By: Claude Opus 4.6 --- api/v1alpha1/task_types.go | 4 +- examples/09-bedrock-credentials/README.md | 72 +++++++++++++++++ examples/09-bedrock-credentials/secret.yaml | 14 ++++ examples/09-bedrock-credentials/task.yaml | 11 +++ internal/cli/config.go | 10 +++ internal/cli/run.go | 84 +++++++++++++++++++- internal/controller/job_builder.go | 78 ++++++++++++------- internal/controller/job_builder_test.go | 86 +++++++++++++++++++++ internal/manifests/install-crd.yaml | 2 + 9 files changed, 329 insertions(+), 32 deletions(-) create mode 100644 examples/09-bedrock-credentials/README.md create mode 100644 examples/09-bedrock-credentials/secret.yaml create mode 100644 examples/09-bedrock-credentials/task.yaml diff --git a/api/v1alpha1/task_types.go b/api/v1alpha1/task_types.go index dfce1d83..2e572898 100644 --- a/api/v1alpha1/task_types.go +++ b/api/v1alpha1/task_types.go @@ -13,6 +13,8 @@ const ( CredentialTypeAPIKey CredentialType = "api-key" // CredentialTypeOAuth uses OAuth for authentication. CredentialTypeOAuth CredentialType = "oauth" + // CredentialTypeBedrock uses AWS credentials for Bedrock authentication. + CredentialTypeBedrock CredentialType = "bedrock" ) // TaskPhase represents the current phase of a Task. @@ -40,7 +42,7 @@ type SecretReference struct { // Credentials defines how to authenticate with the AI agent. type Credentials struct { // Type specifies the credential type (api-key or oauth). - // +kubebuilder:validation:Enum=api-key;oauth + // +kubebuilder:validation:Enum=api-key;oauth;bedrock Type CredentialType `json:"type"` // SecretRef references the Secret containing credentials. diff --git a/examples/09-bedrock-credentials/README.md b/examples/09-bedrock-credentials/README.md new file mode 100644 index 00000000..e6aec0a9 --- /dev/null +++ b/examples/09-bedrock-credentials/README.md @@ -0,0 +1,72 @@ +# Bedrock Credentials + +This example demonstrates running a Claude Code task using AWS Bedrock instead of the Anthropic API directly. + +## Prerequisites + +- AWS account with Bedrock access enabled for Claude models +- AWS IAM credentials with `bedrock:InvokeModel` permissions + +## Setup + +1. Create the Secret with your AWS credentials: + + ```bash + kubectl create secret generic bedrock-credentials \ + --from-literal=AWS_ACCESS_KEY_ID= \ + --from-literal=AWS_SECRET_ACCESS_KEY= \ + --from-literal=AWS_REGION=us-east-1 + ``` + +2. Create the Task: + + ```bash + kubectl apply -f task.yaml + ``` + +## Using the CLI + +You can also use `kelos run` with a config file: + +```yaml +# ~/.kelos/config.yaml +bedrock: + accessKeyID: AKIAIOSFODNN7EXAMPLE + secretAccessKey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY + region: us-east-1 +``` + +```bash +kelos run -p "Fix the bug" +``` + +Or with a pre-created secret: + +```bash +kelos run -p "Fix the bug" --credential-type bedrock --secret bedrock-credentials +``` + +## Optional Fields + +- `AWS_SESSION_TOKEN`: Required when using temporary credentials (e.g. from STS AssumeRole) +- `ANTHROPIC_BEDROCK_BASE_URL`: Custom Bedrock endpoint URL + +## IAM Roles for Service Accounts (IRSA) + +On EKS, you can use IRSA instead of static credentials. In that case, use `podOverrides.env` to set only the required environment variables: + +```yaml +spec: + credentials: + type: api-key + secretRef: + name: dummy-secret # Required by schema; not used by Bedrock + podOverrides: + env: + - name: CLAUDE_CODE_USE_BEDROCK + value: "1" + - name: AWS_REGION + value: us-east-1 +``` + +Note: First-class IRSA support (making `secretRef` optional for bedrock) is planned for a future release. diff --git a/examples/09-bedrock-credentials/secret.yaml b/examples/09-bedrock-credentials/secret.yaml new file mode 100644 index 00000000..020b0abb --- /dev/null +++ b/examples/09-bedrock-credentials/secret.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Secret +metadata: + name: bedrock-credentials +type: Opaque +stringData: + # TODO: Replace with your AWS credentials + AWS_ACCESS_KEY_ID: "AKIAIOSFODNN7EXAMPLE" + AWS_SECRET_ACCESS_KEY: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + AWS_REGION: "us-east-1" + # Optional: uncomment if using temporary credentials (e.g. STS AssumeRole) + # AWS_SESSION_TOKEN: "your-session-token" + # Optional: uncomment to use a custom Bedrock endpoint + # ANTHROPIC_BEDROCK_BASE_URL: "https://bedrock-runtime.us-east-1.amazonaws.com" diff --git a/examples/09-bedrock-credentials/task.yaml b/examples/09-bedrock-credentials/task.yaml new file mode 100644 index 00000000..6d16aa93 --- /dev/null +++ b/examples/09-bedrock-credentials/task.yaml @@ -0,0 +1,11 @@ +apiVersion: kelos.dev/v1alpha1 +kind: Task +metadata: + name: bedrock-task +spec: + type: claude-code + prompt: "Write a Python script that prints the first 20 Fibonacci numbers." + credentials: + type: bedrock + secretRef: + name: bedrock-credentials diff --git a/internal/cli/config.go b/internal/cli/config.go index 60ca0f62..214471e9 100644 --- a/internal/cli/config.go +++ b/internal/cli/config.go @@ -19,6 +19,16 @@ type Config struct { Namespace string `json:"namespace,omitempty"` Workspace WorkspaceConfig `json:"workspace,omitempty"` AgentConfig string `json:"agentConfig,omitempty"` + Bedrock *BedrockConfig `json:"bedrock,omitempty"` +} + +// BedrockConfig holds AWS credentials for Bedrock authentication. +type BedrockConfig struct { + AccessKeyID string `json:"accessKeyID"` + SecretAccessKey string `json:"secretAccessKey"` + Region string `json:"region"` + SessionToken string `json:"sessionToken,omitempty"` + BaseURL string `json:"baseURL,omitempty"` } // WorkspaceConfig holds workspace-related configuration. diff --git a/internal/cli/run.go b/internal/cli/run.go index c85a7aa3..d0a56be7 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -79,8 +79,18 @@ func newRunCommand(cfg *ClientConfig) *cobra.Command { // Auto-create secret from token if no explicit secret is set. if secret == "" && cfg.Config != nil { - if cfg.Config.OAuthToken != "" && cfg.Config.APIKey != "" { - return fmt.Errorf("config file must specify either oauthToken or apiKey, not both") + sources := 0 + if cfg.Config.OAuthToken != "" { + sources++ + } + if cfg.Config.APIKey != "" { + sources++ + } + if cfg.Config.Bedrock != nil { + sources++ + } + if sources > 1 { + return fmt.Errorf("config file must specify only one of oauthToken, apiKey, or bedrock") } if token := cfg.Config.OAuthToken; token != "" { resolved, err := resolveContent(token) @@ -108,6 +118,17 @@ func newRunCommand(cfg *ClientConfig) *cobra.Command { } secret = "kelos-credentials" credentialType = "api-key" + } else if br := cfg.Config.Bedrock; br != nil { + if br.AccessKeyID == "" || br.SecretAccessKey == "" || br.Region == "" { + return fmt.Errorf("bedrock config requires accessKeyID, secretAccessKey, and region") + } + if !dryRun { + if err := ensureBedrockSecret(cfg, "kelos-credentials", br, yes); err != nil { + return err + } + } + secret = "kelos-credentials" + credentialType = "bedrock" } } @@ -304,7 +325,7 @@ func newRunCommand(cfg *ClientConfig) *cobra.Command { cmd.MarkFlagRequired("prompt") - _ = cmd.RegisterFlagCompletionFunc("credential-type", cobra.FixedCompletions([]string{"api-key", "oauth"}, cobra.ShellCompDirectiveNoFileComp)) + _ = cmd.RegisterFlagCompletionFunc("credential-type", cobra.FixedCompletions([]string{"api-key", "oauth", "bedrock"}, cobra.ShellCompDirectiveNoFileComp)) _ = cmd.RegisterFlagCompletionFunc("type", cobra.FixedCompletions([]string{"claude-code", "codex", "gemini", "opencode", "cursor"}, cobra.ShellCompDirectiveNoFileComp)) return cmd @@ -478,3 +499,60 @@ func ensureCredentialSecret(cfg *ClientConfig, name, key, value string, skipConf } return nil } + +// ensureBedrockSecret creates or updates a Secret with AWS Bedrock credentials. +func ensureBedrockSecret(cfg *ClientConfig, name string, br *BedrockConfig, skipConfirm bool) error { + cs, ns, err := cfg.NewClientset() + if err != nil { + return err + } + + data := map[string]string{ + "AWS_ACCESS_KEY_ID": br.AccessKeyID, + "AWS_SECRET_ACCESS_KEY": br.SecretAccessKey, + "AWS_REGION": br.Region, + } + if br.SessionToken != "" { + data["AWS_SESSION_TOKEN"] = br.SessionToken + } + if br.BaseURL != "" { + data["ANTHROPIC_BEDROCK_BASE_URL"] = br.BaseURL + } + + ctx := context.Background() + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + StringData: data, + } + + existing, err := cs.CoreV1().Secrets(ns).Get(ctx, name, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + if _, err := cs.CoreV1().Secrets(ns).Create(ctx, secret, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("creating Bedrock credentials secret: %w", err) + } + return nil + } + if err != nil { + return fmt.Errorf("checking Bedrock credentials secret: %w", err) + } + + if !skipConfirm { + ok, err := confirmOverride(fmt.Sprintf("secret/%s", name)) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("aborted") + } + } + + existing.Data = nil + existing.StringData = secret.StringData + if _, err := cs.CoreV1().Secrets(ns).Update(ctx, existing, metav1.UpdateOptions{}); err != nil { + return fmt.Errorf("updating Bedrock credentials secret: %w", err) + } + return nil +} diff --git a/internal/controller/job_builder.go b/internal/controller/job_builder.go index 57517c1a..8e967328 100644 --- a/internal/controller/job_builder.go +++ b/internal/controller/job_builder.go @@ -168,6 +168,54 @@ func oauthEnvVar(agentType string) string { } } +// credentialEnvVars returns the environment variables to inject for the given +// credential type, agent type, and secret name. This centralises all +// credential-type-specific logic so that new providers (e.g. Vertex) only +// need to add a case here. +func credentialEnvVars(credType kelosv1alpha1.CredentialType, agentType, secretName string) []corev1.EnvVar { + secretRef := func(key string, optional bool) corev1.EnvVar { + sel := &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, + Key: key, + } + if optional { + sel.Optional = ptr(true) + } + return corev1.EnvVar{ + Name: key, + ValueFrom: &corev1.EnvVarSource{SecretKeyRef: sel}, + } + } + + switch credType { + case kelosv1alpha1.CredentialTypeAPIKey: + keyName := apiKeyEnvVar(agentType) + return []corev1.EnvVar{secretRef(keyName, false)} + + case kelosv1alpha1.CredentialTypeOAuth: + tokenName := oauthEnvVar(agentType) + return []corev1.EnvVar{secretRef(tokenName, false)} + + case kelosv1alpha1.CredentialTypeBedrock: + return []corev1.EnvVar{ + {Name: "CLAUDE_CODE_USE_BEDROCK", Value: "1"}, + secretRef("AWS_ACCESS_KEY_ID", false), + secretRef("AWS_SECRET_ACCESS_KEY", false), + secretRef("AWS_REGION", false), + secretRef("AWS_SESSION_TOKEN", true), + secretRef("ANTHROPIC_BEDROCK_BASE_URL", true), + } + + default: + return nil + } +} + +// ptr returns a pointer to the given value. +func ptr[T any](v T) *T { + return &v +} + func effectiveWorkspaceRemotes(workspace *kelosv1alpha1.WorkspaceSpec) []kelosv1alpha1.GitRemote { if workspace == nil { return nil @@ -224,34 +272,8 @@ func (b *JobBuilder) buildAgentJob(task *kelosv1alpha1.Task, workspace *kelosv1a }) } - switch task.Spec.Credentials.Type { - case kelosv1alpha1.CredentialTypeAPIKey: - keyName := apiKeyEnvVar(task.Spec.Type) - envVars = append(envVars, corev1.EnvVar{ - Name: keyName, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: task.Spec.Credentials.SecretRef.Name, - }, - Key: keyName, - }, - }, - }) - case kelosv1alpha1.CredentialTypeOAuth: - tokenName := oauthEnvVar(task.Spec.Type) - envVars = append(envVars, corev1.EnvVar{ - Name: tokenName, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: task.Spec.Credentials.SecretRef.Name, - }, - Key: tokenName, - }, - }, - }) - } + credEnvVars := credentialEnvVars(task.Spec.Credentials.Type, task.Spec.Type, task.Spec.Credentials.SecretRef.Name) + envVars = append(envVars, credEnvVars...) var workspaceEnvVars []corev1.EnvVar var isEnterprise bool diff --git a/internal/controller/job_builder_test.go b/internal/controller/job_builder_test.go index 6729441d..43b06d29 100644 --- a/internal/controller/job_builder_test.go +++ b/internal/controller/job_builder_test.go @@ -4246,3 +4246,89 @@ func TestBuildJob_UpstreamRepoSpecWithoutRemote(t *testing.T) { t.Error("Expected KELOS_UPSTREAM_REPO env var on main container") } } + +func TestBuildClaudeCodeJob_BedrockCredentials(t *testing.T) { + builder := NewJobBuilder() + task := &kelosv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-bedrock", + Namespace: "default", + }, + Spec: kelosv1alpha1.TaskSpec{ + Type: AgentTypeClaudeCode, + Prompt: "Fix the bug", + Credentials: kelosv1alpha1.Credentials{ + Type: kelosv1alpha1.CredentialTypeBedrock, + SecretRef: kelosv1alpha1.SecretReference{Name: "bedrock-creds"}, + }, + }, + } + + job, err := builder.Build(task, nil, nil, task.Spec.Prompt) + if err != nil { + t.Fatalf("Build() returned error: %v", err) + } + + container := job.Spec.Template.Spec.Containers[0] + + // Collect env vars by name for easier assertions. + envMap := make(map[string]corev1.EnvVar) + for _, env := range container.Env { + envMap[env.Name] = env + } + + // CLAUDE_CODE_USE_BEDROCK should be set as a literal value. + if env, ok := envMap["CLAUDE_CODE_USE_BEDROCK"]; !ok { + t.Error("Expected CLAUDE_CODE_USE_BEDROCK env var") + } else if env.Value != "1" { + t.Errorf("CLAUDE_CODE_USE_BEDROCK = %q, want %q", env.Value, "1") + } + + // Required AWS credentials should reference the secret. + for _, key := range []string{"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"} { + env, ok := envMap[key] + if !ok { + t.Errorf("Expected %s env var", key) + continue + } + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Errorf("Expected %s to reference a secret", key) + continue + } + if env.ValueFrom.SecretKeyRef.Name != "bedrock-creds" { + t.Errorf("%s secret name = %q, want %q", key, env.ValueFrom.SecretKeyRef.Name, "bedrock-creds") + } + if env.ValueFrom.SecretKeyRef.Key != key { + t.Errorf("%s secret key = %q, want %q", key, env.ValueFrom.SecretKeyRef.Key, key) + } + if env.ValueFrom.SecretKeyRef.Optional != nil && *env.ValueFrom.SecretKeyRef.Optional { + t.Errorf("%s should not be optional", key) + } + } + + // Optional AWS credentials should be marked optional. + for _, key := range []string{"AWS_SESSION_TOKEN", "ANTHROPIC_BEDROCK_BASE_URL"} { + env, ok := envMap[key] + if !ok { + t.Errorf("Expected %s env var", key) + continue + } + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Errorf("Expected %s to reference a secret", key) + continue + } + if env.ValueFrom.SecretKeyRef.Optional == nil || !*env.ValueFrom.SecretKeyRef.Optional { + t.Errorf("%s should be optional", key) + } + } + + // ANTHROPIC_API_KEY should NOT be set for bedrock credential type. + if _, ok := envMap["ANTHROPIC_API_KEY"]; ok { + t.Error("ANTHROPIC_API_KEY should not be set for bedrock credential type") + } + + // CLAUDE_CODE_OAUTH_TOKEN should NOT be set. + if _, ok := envMap["CLAUDE_CODE_OAUTH_TOKEN"]; ok { + t.Error("CLAUDE_CODE_OAUTH_TOKEN should not be set for bedrock credential type") + } +} diff --git a/internal/manifests/install-crd.yaml b/internal/manifests/install-crd.yaml index 9cff7acd..1ec0e328 100644 --- a/internal/manifests/install-crd.yaml +++ b/internal/manifests/install-crd.yaml @@ -317,6 +317,7 @@ spec: enum: - api-key - oauth + - bedrock type: string required: - secretRef @@ -805,6 +806,7 @@ spec: enum: - api-key - oauth + - bedrock type: string required: - secretRef From d9c994316c5627f8e09fe25cd116c2450163ad56 Mon Sep 17 00:00:00 2001 From: Hans Knecht Date: Tue, 24 Mar 2026 17:40:36 +0100 Subject: [PATCH 2/2] feat: add first-class IRSA support for bedrock credentials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make Credentials.SecretRef a pointer (*SecretReference) so it can be omitted for bedrock credentials using IAM Roles for Service Accounts. Add Region and ServiceAccountName fields to Credentials for IRSA mode. CEL validation ensures secretRef remains required for api-key and oauth credential types. In IRSA mode, only CLAUDE_CODE_USE_BEDROCK=1 and AWS_REGION are injected — the AWS SDK handles auth via the projected service account token. Co-Authored-By: Claude Opus 4.6 --- api/v1alpha1/task_types.go | 19 +- api/v1alpha1/taskspawner_types.go | 1 + api/v1alpha1/zz_generated.deepcopy.go | 10 +- cmd/kelos-spawner/main_test.go | 16 +- examples/09-bedrock-credentials/README.md | 58 +++-- .../09-bedrock-credentials/task-irsa.yaml | 11 + internal/cli/config.go | 13 +- internal/cli/printer.go | 4 +- internal/cli/printer_test.go | 4 +- internal/cli/run.go | 60 +++-- internal/controller/job_builder.go | 66 ++++-- internal/controller/job_builder_test.go | 224 ++++++++++++------ internal/controller/task_controller_test.go | 4 +- .../taskspawner_deployment_builder_test.go | 4 +- internal/manifests/install-crd.yaml | 47 +++- internal/reporting/watcher_test.go | 4 +- test/e2e/opencode_test.go | 2 +- test/e2e/skills_test.go | 2 +- test/e2e/task_test.go | 16 +- test/e2e/taskspawner_test.go | 8 +- test/integration/cli_test.go | 22 +- test/integration/completion_test.go | 6 +- test/integration/install_test.go | 2 +- test/integration/metrics_test.go | 2 +- test/integration/task_test.go | 86 +++---- test/integration/taskspawner_test.go | 54 ++--- 26 files changed, 473 insertions(+), 272 deletions(-) create mode 100644 examples/09-bedrock-credentials/task-irsa.yaml diff --git a/api/v1alpha1/task_types.go b/api/v1alpha1/task_types.go index 2e572898..90435c7c 100644 --- a/api/v1alpha1/task_types.go +++ b/api/v1alpha1/task_types.go @@ -41,12 +41,26 @@ type SecretReference struct { // Credentials defines how to authenticate with the AI agent. type Credentials struct { - // Type specifies the credential type (api-key or oauth). + // Type specifies the credential type. // +kubebuilder:validation:Enum=api-key;oauth;bedrock Type CredentialType `json:"type"` // SecretRef references the Secret containing credentials. - SecretRef SecretReference `json:"secretRef"` + // Required for api-key and oauth types. Optional for bedrock + // when using IAM Roles for Service Accounts (IRSA). + // +optional + SecretRef *SecretReference `json:"secretRef,omitempty"` + + // Region specifies the cloud provider region (e.g. AWS region for Bedrock). + // Used with bedrock credentials when secretRef is omitted (IRSA mode). + // +optional + Region string `json:"region,omitempty"` + + // ServiceAccountName overrides the pod's service account. + // Use with IAM Roles for Service Accounts (IRSA) on EKS to let + // the pod assume an IAM role without static credentials. + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` } // PodOverrides defines optional overrides for the agent pod. @@ -86,6 +100,7 @@ type TaskSpec struct { // Credentials specifies how to authenticate with the agent. // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self.type == 'bedrock' || has(self.secretRef)",message="secretRef is required for api-key and oauth credential types" Credentials Credentials `json:"credentials"` // Model optionally overrides the default model. diff --git a/api/v1alpha1/taskspawner_types.go b/api/v1alpha1/taskspawner_types.go index d35020e6..479f0cb8 100644 --- a/api/v1alpha1/taskspawner_types.go +++ b/api/v1alpha1/taskspawner_types.go @@ -304,6 +304,7 @@ type TaskTemplate struct { // Credentials specifies how to authenticate with the agent. // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self.type == 'bedrock' || has(self.secretRef)",message="secretRef is required for api-key and oauth credential types" Credentials Credentials `json:"credentials"` // Model optionally overrides the default model. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 69bc880b..83ace025 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -151,7 +151,11 @@ func (in *AgentDefinition) DeepCopy() *AgentDefinition { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Credentials) DeepCopyInto(out *Credentials) { *out = *in - out.SecretRef = in.SecretRef + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretReference) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Credentials. @@ -703,7 +707,7 @@ func (in *TaskSpawnerStatus) DeepCopy() *TaskSpawnerStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TaskSpec) DeepCopyInto(out *TaskSpec) { *out = *in - out.Credentials = in.Credentials + in.Credentials.DeepCopyInto(&out.Credentials) if in.WorkspaceRef != nil { in, out := &in.WorkspaceRef, &out.WorkspaceRef *out = new(WorkspaceReference) @@ -779,7 +783,7 @@ func (in *TaskStatus) DeepCopy() *TaskStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TaskTemplate) DeepCopyInto(out *TaskTemplate) { *out = *in - out.Credentials = in.Credentials + in.Credentials.DeepCopyInto(&out.Credentials) if in.WorkspaceRef != nil { in, out := &in.WorkspaceRef, &out.WorkspaceRef *out = new(WorkspaceReference) diff --git a/cmd/kelos-spawner/main_test.go b/cmd/kelos-spawner/main_test.go index 0cec6029..5b4cb21f 100644 --- a/cmd/kelos-spawner/main_test.go +++ b/cmd/kelos-spawner/main_test.go @@ -77,7 +77,7 @@ func newTaskSpawner(name, namespace string, maxConcurrency *int32) *kelosv1alpha Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "test-ws"}, }, @@ -100,7 +100,7 @@ func newTask(name, namespace, spawnerName string, phase kelosv1alpha1.TaskPhase) Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, Status: kelosv1alpha1.TaskStatus{ @@ -212,7 +212,7 @@ func TestBuildSource_Jira(t *testing.T) { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, }, @@ -1186,7 +1186,7 @@ func newCompletedTask(name, namespace, spawnerName string, phase kelosv1alpha1.T Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, Status: kelosv1alpha1.TaskStatus{ @@ -1520,7 +1520,7 @@ func TestRunCycleWithSource_PropagatesUpstreamRepo(t *testing.T) { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, }, @@ -1563,7 +1563,7 @@ func TestRunCycleWithSource_ExplicitUpstreamRepoTakesPrecedence(t *testing.T) { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, UpstreamRepo: "explicit-org/explicit-repo", }, @@ -1843,7 +1843,7 @@ func TestRunReportingCycle_ReportsForAnnotatedTasks(t *testing.T) { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, Status: kelosv1alpha1.TaskStatus{ @@ -1904,7 +1904,7 @@ func TestRunReportingCycle_SkipsTasksWithoutReporting(t *testing.T) { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, Status: kelosv1alpha1.TaskStatus{ diff --git a/examples/09-bedrock-credentials/README.md b/examples/09-bedrock-credentials/README.md index e6aec0a9..cce3e76f 100644 --- a/examples/09-bedrock-credentials/README.md +++ b/examples/09-bedrock-credentials/README.md @@ -7,7 +7,7 @@ This example demonstrates running a Claude Code task using AWS Bedrock instead o - AWS account with Bedrock access enabled for Claude models - AWS IAM credentials with `bedrock:InvokeModel` permissions -## Setup +## Option 1: Static Credentials (Secret) 1. Create the Secret with your AWS credentials: @@ -24,9 +24,12 @@ This example demonstrates running a Claude Code task using AWS Bedrock instead o kubectl apply -f task.yaml ``` -## Using the CLI +### Optional Secret Keys -You can also use `kelos run` with a config file: +- `AWS_SESSION_TOKEN`: Required when using temporary credentials (e.g. from STS AssumeRole) +- `ANTHROPIC_BEDROCK_BASE_URL`: Custom Bedrock endpoint URL + +### CLI with Static Credentials ```yaml # ~/.kelos/config.yaml @@ -46,27 +49,42 @@ Or with a pre-created secret: kelos run -p "Fix the bug" --credential-type bedrock --secret bedrock-credentials ``` -## Optional Fields +## Option 2: IAM Roles for Service Accounts (IRSA) -- `AWS_SESSION_TOKEN`: Required when using temporary credentials (e.g. from STS AssumeRole) -- `ANTHROPIC_BEDROCK_BASE_URL`: Custom Bedrock endpoint URL +On EKS, you can use IRSA instead of static credentials. The AWS SDK automatically picks up credentials from the projected service account token — no Secret needed. + +### Prerequisites -## IAM Roles for Service Accounts (IRSA) +1. Create an IAM role with `bedrock:InvokeModel` permissions +2. Create a Kubernetes ServiceAccount annotated with the IAM role: -On EKS, you can use IRSA instead of static credentials. In that case, use `podOverrides.env` to set only the required environment variables: + ```bash + kubectl create serviceaccount bedrock-agent-sa + kubectl annotate serviceaccount bedrock-agent-sa \ + eks.amazonaws.com/role-arn=arn:aws:iam::123456789012:role/bedrock-agent-role + ``` + +3. Create the Task with `region` and `serviceAccountName` (no `secretRef`): + + ```bash + kubectl apply -f task-irsa.yaml + ``` + +### CLI with IRSA ```yaml -spec: - credentials: - type: api-key - secretRef: - name: dummy-secret # Required by schema; not used by Bedrock - podOverrides: - env: - - name: CLAUDE_CODE_USE_BEDROCK - value: "1" - - name: AWS_REGION - value: us-east-1 +# ~/.kelos/config.yaml +bedrock: + region: us-east-1 + serviceAccountName: bedrock-agent-sa ``` -Note: First-class IRSA support (making `secretRef` optional for bedrock) is planned for a future release. +```bash +kelos run -p "Fix the bug" +``` + +Or with flags: + +```bash +kelos run -p "Fix the bug" --credential-type bedrock --region us-east-1 --service-account bedrock-agent-sa +``` diff --git a/examples/09-bedrock-credentials/task-irsa.yaml b/examples/09-bedrock-credentials/task-irsa.yaml new file mode 100644 index 00000000..96a21ee1 --- /dev/null +++ b/examples/09-bedrock-credentials/task-irsa.yaml @@ -0,0 +1,11 @@ +apiVersion: kelos.dev/v1alpha1 +kind: Task +metadata: + name: bedrock-irsa-task +spec: + type: claude-code + prompt: "Write a Python script that prints the first 20 Fibonacci numbers." + credentials: + type: bedrock + region: us-east-1 + serviceAccountName: bedrock-agent-sa diff --git a/internal/cli/config.go b/internal/cli/config.go index 214471e9..9e43fe88 100644 --- a/internal/cli/config.go +++ b/internal/cli/config.go @@ -23,12 +23,15 @@ type Config struct { } // BedrockConfig holds AWS credentials for Bedrock authentication. +// For IRSA mode, omit accessKeyID and secretAccessKey and set only region +// and serviceAccountName. type BedrockConfig struct { - AccessKeyID string `json:"accessKeyID"` - SecretAccessKey string `json:"secretAccessKey"` - Region string `json:"region"` - SessionToken string `json:"sessionToken,omitempty"` - BaseURL string `json:"baseURL,omitempty"` + AccessKeyID string `json:"accessKeyID,omitempty"` + SecretAccessKey string `json:"secretAccessKey,omitempty"` + Region string `json:"region"` + SessionToken string `json:"sessionToken,omitempty"` + BaseURL string `json:"baseURL,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` } // WorkspaceConfig holds workspace-related configuration. diff --git a/internal/cli/printer.go b/internal/cli/printer.go index e20c90fd..930e1192 100644 --- a/internal/cli/printer.go +++ b/internal/cli/printer.go @@ -64,7 +64,9 @@ func printTaskDetail(w io.Writer, t *kelosv1alpha1.Task) { printField(w, "Type", t.Spec.Type) printField(w, "Phase", string(t.Status.Phase)) printField(w, "Prompt", t.Spec.Prompt) - printField(w, "Secret", t.Spec.Credentials.SecretRef.Name) + if t.Spec.Credentials.SecretRef != nil { + printField(w, "Secret", t.Spec.Credentials.SecretRef.Name) + } printField(w, "Credential Type", string(t.Spec.Credentials.Type)) if t.Spec.Model != "" { printField(w, "Model", t.Spec.Model) diff --git a/internal/cli/printer_test.go b/internal/cli/printer_test.go index 80330601..e74ec1b6 100644 --- a/internal/cli/printer_test.go +++ b/internal/cli/printer_test.go @@ -739,7 +739,7 @@ func TestPrintTaskDetail(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, Model: "claude-sonnet-4-20250514", Image: "custom-image:latest", @@ -993,7 +993,7 @@ func TestPrintTaskDetailMinimal(t *testing.T) { Prompt: "Do something", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "secret"}, }, }, Status: kelosv1alpha1.TaskStatus{ diff --git a/internal/cli/run.go b/internal/cli/run.go index d0a56be7..b52c25c0 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -50,6 +50,8 @@ func newRunCommand(cfg *ClientConfig) *cobra.Command { agentConfigRef string dependsOn []string branch string + region string + serviceAccount string ) cmd := &cobra.Command{ @@ -119,20 +121,32 @@ func newRunCommand(cfg *ClientConfig) *cobra.Command { secret = "kelos-credentials" credentialType = "api-key" } else if br := cfg.Config.Bedrock; br != nil { - if br.AccessKeyID == "" || br.SecretAccessKey == "" || br.Region == "" { - return fmt.Errorf("bedrock config requires accessKeyID, secretAccessKey, and region") - } - if !dryRun { - if err := ensureBedrockSecret(cfg, "kelos-credentials", br, yes); err != nil { - return err + hasStaticCreds := br.AccessKeyID != "" || br.SecretAccessKey != "" + if hasStaticCreds { + if br.AccessKeyID == "" || br.SecretAccessKey == "" || br.Region == "" { + return fmt.Errorf("bedrock config requires accessKeyID, secretAccessKey, and region when using static credentials") + } + if !dryRun { + if err := ensureBedrockSecret(cfg, "kelos-credentials", br, yes); err != nil { + return err + } + } + secret = "kelos-credentials" + } else { + // IRSA mode — no secret needed, region is set on credentials directly. + if br.Region == "" { + return fmt.Errorf("bedrock config requires region") + } + region = br.Region + if br.ServiceAccountName != "" && serviceAccount == "" { + serviceAccount = br.ServiceAccountName } } - secret = "kelos-credentials" credentialType = "bedrock" } } - if secret == "" { + if secret == "" && credentialType != "bedrock" { return fmt.Errorf("no credentials configured (set oauthToken/apiKey in config file, or use --secret flag)") } @@ -214,22 +228,28 @@ func newRunCommand(cfg *ClientConfig) *cobra.Command { name = "task-" + rand.String(5) } + creds := kelosv1alpha1.Credentials{ + Type: kelosv1alpha1.CredentialType(credentialType), + Region: region, + } + if secret != "" { + creds.SecretRef = &kelosv1alpha1.SecretReference{Name: secret} + } + if serviceAccount != "" { + creds.ServiceAccountName = serviceAccount + } + task := &kelosv1alpha1.Task{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: ns, }, Spec: kelosv1alpha1.TaskSpec{ - Type: agentType, - Prompt: prompt, - Credentials: kelosv1alpha1.Credentials{ - Type: kelosv1alpha1.CredentialType(credentialType), - SecretRef: kelosv1alpha1.SecretReference{ - Name: secret, - }, - }, - Model: model, - Image: image, + Type: agentType, + Prompt: prompt, + Credentials: creds, + Model: model, + Image: image, }, } @@ -309,7 +329,9 @@ func newRunCommand(cfg *ClientConfig) *cobra.Command { cmd.Flags().StringVarP(&prompt, "prompt", "p", "", "task prompt (required)") cmd.Flags().StringVarP(&agentType, "type", "t", "claude-code", "agent type (claude-code, codex, gemini, opencode, cursor)") cmd.Flags().StringVar(&secret, "secret", "", "secret name with credentials (overrides oauthToken/apiKey in config)") - cmd.Flags().StringVar(&credentialType, "credential-type", "api-key", "credential type (api-key, oauth)") + cmd.Flags().StringVar(&credentialType, "credential-type", "api-key", "credential type (api-key, oauth, bedrock)") + cmd.Flags().StringVar(®ion, "region", "", "cloud provider region (e.g. us-east-1 for Bedrock IRSA)") + cmd.Flags().StringVar(&serviceAccount, "service-account", "", "pod service account name (e.g. for IRSA on EKS)") cmd.Flags().StringVar(&model, "model", "", "model override") cmd.Flags().StringVar(&image, "image", "", "custom agent image (must implement agent image interface)") cmd.Flags().StringVar(&name, "name", "", "task name (auto-generated if omitted)") diff --git a/internal/controller/job_builder.go b/internal/controller/job_builder.go index 8e967328..c10c5548 100644 --- a/internal/controller/job_builder.go +++ b/internal/controller/job_builder.go @@ -169,11 +169,15 @@ func oauthEnvVar(agentType string) string { } // credentialEnvVars returns the environment variables to inject for the given -// credential type, agent type, and secret name. This centralises all -// credential-type-specific logic so that new providers (e.g. Vertex) only -// need to add a case here. -func credentialEnvVars(credType kelosv1alpha1.CredentialType, agentType, secretName string) []corev1.EnvVar { - secretRef := func(key string, optional bool) corev1.EnvVar { +// credentials and agent type. This centralises all credential-type-specific +// logic so that new providers (e.g. Vertex) only need to add a case here. +func credentialEnvVars(creds kelosv1alpha1.Credentials, agentType string) []corev1.EnvVar { + secretName := "" + if creds.SecretRef != nil { + secretName = creds.SecretRef.Name + } + + secretEnvRef := func(key string, optional bool) corev1.EnvVar { sel := &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, Key: key, @@ -187,24 +191,37 @@ func credentialEnvVars(credType kelosv1alpha1.CredentialType, agentType, secretN } } - switch credType { + switch creds.Type { case kelosv1alpha1.CredentialTypeAPIKey: keyName := apiKeyEnvVar(agentType) - return []corev1.EnvVar{secretRef(keyName, false)} + return []corev1.EnvVar{secretEnvRef(keyName, false)} case kelosv1alpha1.CredentialTypeOAuth: tokenName := oauthEnvVar(agentType) - return []corev1.EnvVar{secretRef(tokenName, false)} + return []corev1.EnvVar{secretEnvRef(tokenName, false)} case kelosv1alpha1.CredentialTypeBedrock: - return []corev1.EnvVar{ + envs := []corev1.EnvVar{ {Name: "CLAUDE_CODE_USE_BEDROCK", Value: "1"}, - secretRef("AWS_ACCESS_KEY_ID", false), - secretRef("AWS_SECRET_ACCESS_KEY", false), - secretRef("AWS_REGION", false), - secretRef("AWS_SESSION_TOKEN", true), - secretRef("ANTHROPIC_BEDROCK_BASE_URL", true), } + if secretName != "" { + // Static credentials from a Secret. + envs = append(envs, + secretEnvRef("AWS_ACCESS_KEY_ID", false), + secretEnvRef("AWS_SECRET_ACCESS_KEY", false), + secretEnvRef("AWS_REGION", false), + secretEnvRef("AWS_SESSION_TOKEN", true), + secretEnvRef("ANTHROPIC_BEDROCK_BASE_URL", true), + ) + } else if creds.Region != "" { + // IRSA mode — SDK picks up credentials from the projected + // service account token; only the region is needed. + envs = append(envs, corev1.EnvVar{ + Name: "AWS_REGION", + Value: creds.Region, + }) + } + return envs default: return nil @@ -272,7 +289,7 @@ func (b *JobBuilder) buildAgentJob(task *kelosv1alpha1.Task, workspace *kelosv1a }) } - credEnvVars := credentialEnvVars(task.Spec.Credentials.Type, task.Spec.Type, task.Spec.Credentials.SecretRef.Name) + credEnvVars := credentialEnvVars(task.Spec.Credentials, task.Spec.Type) envVars = append(envVars, credEnvVars...) var workspaceEnvVars []corev1.EnvVar @@ -566,6 +583,12 @@ func (b *JobBuilder) buildAgentJob(task *kelosv1alpha1.Task, workspace *kelosv1a } } + // ServiceAccountName from credentials (e.g. IRSA for Bedrock). + var serviceAccountName string + if task.Spec.Credentials.ServiceAccountName != "" { + serviceAccountName = task.Spec.Credentials.ServiceAccountName + } + // Apply PodOverrides before constructing the Job so all overrides // are reflected in the final spec. var activeDeadlineSeconds *int64 @@ -648,12 +671,13 @@ func (b *JobBuilder) buildAgentJob(task *kelosv1alpha1.Task, workspace *kelosv1a }, }, Spec: corev1.PodSpec{ - RestartPolicy: corev1.RestartPolicyNever, - SecurityContext: podSecurityContext, - InitContainers: initContainers, - Volumes: volumes, - Containers: []corev1.Container{mainContainer}, - NodeSelector: nodeSelector, + RestartPolicy: corev1.RestartPolicyNever, + SecurityContext: podSecurityContext, + ServiceAccountName: serviceAccountName, + InitContainers: initContainers, + Volumes: volumes, + Containers: []corev1.Container{mainContainer}, + NodeSelector: nodeSelector, }, }, }, diff --git a/internal/controller/job_builder_test.go b/internal/controller/job_builder_test.go index 43b06d29..7a2e0194 100644 --- a/internal/controller/job_builder_test.go +++ b/internal/controller/job_builder_test.go @@ -25,7 +25,7 @@ func TestBuildClaudeCodeJob_DefaultImage(t *testing.T) { Prompt: "Hello world", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, Model: "claude-sonnet-4-20250514", }, @@ -80,7 +80,7 @@ func TestBuildClaudeCodeJob_CustomImage(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, Model: "my-model", Image: "my-custom-agent:latest", @@ -136,7 +136,7 @@ func TestBuildClaudeCodeJob_NoModel(t *testing.T) { Prompt: "Hello", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -168,7 +168,7 @@ func TestBuildClaudeCodeJob_WorkspaceWithRef(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -235,7 +235,7 @@ func TestBuildClaudeCodeJob_WorkspaceWithInjectedFiles(t *testing.T) { Prompt: "Inject plugin files", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -307,7 +307,7 @@ func TestBuildClaudeCodeJob_WorkspaceWithInjectedFilesInvalidPath(t *testing.T) Prompt: "Inject plugin files", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -343,7 +343,7 @@ func TestBuildClaudeCodeJob_CustomImageWithWorkspace(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, Image: "my-agent:v1", Model: "gpt-4", @@ -425,7 +425,7 @@ func TestBuildClaudeCodeJob_WorkspaceWithSecretRefPersistsCredentialHelper(t *te Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -483,7 +483,7 @@ func TestBuildClaudeCodeJob_EnterpriseWorkspaceSetsGHHostAndEnterpriseToken(t *t Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -562,7 +562,7 @@ func TestBuildClaudeCodeJob_GithubComWorkspaceUsesGHToken(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -615,7 +615,7 @@ func TestBuildCodexJob_DefaultImage(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "openai-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "openai-secret"}, }, Model: "gpt-4.1", }, @@ -699,7 +699,7 @@ func TestBuildCodexJob_CustomImage(t *testing.T) { Prompt: "Refactor the module", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "openai-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "openai-secret"}, }, Image: "my-codex:v2", }, @@ -735,7 +735,7 @@ func TestBuildCodexJob_WithWorkspace(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "openai-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "openai-secret"}, }, Model: "gpt-4.1", }, @@ -810,7 +810,7 @@ func TestBuildCodexJob_OAuthCredentials(t *testing.T) { Prompt: "Review the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "codex-oauth"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "codex-oauth"}, }, }, } @@ -862,7 +862,7 @@ func TestBuildGeminiJob_DefaultImage(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "gemini-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "gemini-secret"}, }, Model: "gemini-2.5-pro", }, @@ -949,7 +949,7 @@ func TestBuildGeminiJob_CustomImage(t *testing.T) { Prompt: "Refactor the module", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "gemini-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "gemini-secret"}, }, Image: "my-gemini:v2", }, @@ -985,7 +985,7 @@ func TestBuildGeminiJob_WithWorkspace(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "gemini-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "gemini-secret"}, }, Model: "gemini-2.5-pro", }, @@ -1063,7 +1063,7 @@ func TestBuildGeminiJob_OAuthCredentials(t *testing.T) { Prompt: "Review the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "gemini-oauth"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "gemini-oauth"}, }, }, } @@ -1115,7 +1115,7 @@ func TestBuildOpenCodeJob_DefaultImage(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "opencode-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "opencode-secret"}, }, Model: "anthropic/claude-sonnet-4-20250514", }, @@ -1205,7 +1205,7 @@ func TestBuildOpenCodeJob_CustomImage(t *testing.T) { Prompt: "Refactor the module", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "opencode-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "opencode-secret"}, }, Image: "my-opencode:v2", }, @@ -1241,7 +1241,7 @@ func TestBuildOpenCodeJob_WithWorkspace(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "opencode-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "opencode-secret"}, }, Model: "anthropic/claude-sonnet-4-20250514", }, @@ -1322,7 +1322,7 @@ func TestBuildOpenCodeJob_OAuthCredentials(t *testing.T) { Prompt: "Review the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "opencode-oauth"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "opencode-oauth"}, }, }, } @@ -1377,7 +1377,7 @@ func TestBuildCursorJob_DefaultImage(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "cursor-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "cursor-secret"}, }, Model: "claude-sonnet-4-20250514", }, @@ -1461,7 +1461,7 @@ func TestBuildCursorJob_CustomImage(t *testing.T) { Prompt: "Refactor the module", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "cursor-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "cursor-secret"}, }, Image: "my-cursor:v2", }, @@ -1495,7 +1495,7 @@ func TestBuildCursorJob_OAuthCredentials(t *testing.T) { Prompt: "Review the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "cursor-oauth"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "cursor-oauth"}, }, }, } @@ -1555,7 +1555,7 @@ func TestBuildClaudeCodeJob_UnsupportedType(t *testing.T) { Prompt: "Hello", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -1580,7 +1580,7 @@ func TestBuildJob_PodOverridesResources(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, PodOverrides: &kelosv1alpha1.PodOverrides{ Resources: &corev1.ResourceRequirements{ @@ -1634,7 +1634,7 @@ func TestBuildJob_PodOverridesActiveDeadlineSeconds(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, PodOverrides: &kelosv1alpha1.PodOverrides{ ActiveDeadlineSeconds: int64Ptr(1800), @@ -1667,7 +1667,7 @@ func TestBuildJob_PodOverridesEnv(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, Model: "claude-sonnet-4-20250514", PodOverrides: &kelosv1alpha1.PodOverrides{ @@ -1718,7 +1718,7 @@ func TestBuildJob_PodOverridesEnvBuiltinPrecedence(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, Model: "claude-sonnet-4-20250514", PodOverrides: &kelosv1alpha1.PodOverrides{ @@ -1765,7 +1765,7 @@ func TestBuildJob_PodOverridesNodeSelector(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, PodOverrides: &kelosv1alpha1.PodOverrides{ NodeSelector: map[string]string{ @@ -1805,7 +1805,7 @@ func TestBuildJob_PodOverridesAllFields(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "openai-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "openai-secret"}, }, PodOverrides: &kelosv1alpha1.PodOverrides{ Resources: &corev1.ResourceRequirements{ @@ -1871,7 +1871,7 @@ func TestBuildJob_NoPodOverrides(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -1911,7 +1911,7 @@ func TestBuildJob_AgentConfigAgentsMD(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -1962,7 +1962,7 @@ func TestBuildJob_AgentConfigPlugins(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2067,7 +2067,7 @@ func TestBuildJob_AgentConfigFull(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2126,7 +2126,7 @@ func TestBuildJob_AgentConfigSkills(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2235,7 +2235,7 @@ func TestBuildJob_AgentConfigSkillsWithPlugins(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2294,7 +2294,7 @@ func TestBuildJob_AgentConfigSkillsEmptySource(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2326,7 +2326,7 @@ func TestBuildJob_AgentConfigWithWorkspace(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2387,7 +2387,7 @@ func TestBuildJob_AgentConfigWithoutWorkspace(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2437,7 +2437,7 @@ func TestBuildJob_AgentConfigCodex(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2506,7 +2506,7 @@ func TestBuildJob_AgentConfigGemini(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2572,7 +2572,7 @@ func TestBuildJob_AgentConfigOpenCode(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2641,7 +2641,7 @@ func TestBuildJob_AgentConfigPluginNamePathTraversal(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2715,7 +2715,7 @@ func TestBuildJob_BranchSetupInitContainer(t *testing.T) { Branch: "feature-x", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2798,7 +2798,7 @@ func TestBuildJob_BranchSetupWithSecretRefUsesCredentialHelper(t *testing.T) { Branch: "feature-y", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2857,7 +2857,7 @@ func TestBuildJob_BranchWithoutWorkspaceNoInitContainer(t *testing.T) { Branch: "feature-z", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2898,7 +2898,7 @@ func TestBuildJob_BranchEnvDoesNotMutateWorkspaceEnvVars(t *testing.T) { Branch: "feature-w", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2952,7 +2952,7 @@ func TestBuildJob_KelosAgentTypeAlwaysSet(t *testing.T) { Prompt: "Hello", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -2991,7 +2991,7 @@ func TestBuildJob_AgentConfigMCPServers(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3098,7 +3098,7 @@ func TestBuildJob_AgentConfigMCPServersWithHTTPHeaders(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3161,7 +3161,7 @@ func TestBuildJob_AgentConfigMCPServersWithPluginsAndAgentsMD(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3230,7 +3230,7 @@ func TestBuildJob_AgentConfigMCPServersCodex(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "openai-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "openai-secret"}, }, }, } @@ -3274,7 +3274,7 @@ func TestBuildJob_AgentConfigMCPServersGemini(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "gemini-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "gemini-secret"}, }, }, } @@ -3318,7 +3318,7 @@ func TestBuildJob_AgentConfigMCPServersEmptyName(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3354,7 +3354,7 @@ func TestBuildJob_AgentConfigMCPServersDuplicateName(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3399,7 +3399,7 @@ func TestBuildJob_AgentConfigMCPServerNamePathTraversal(t *testing.T) { Prompt: "Fix issue", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3431,7 +3431,7 @@ func TestBuildJob_KelosBaseBranchSetWhenWorkspaceRefPresent(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3473,7 +3473,7 @@ func TestBuildJob_KelosBaseBranchAbsentWhenRefEmpty(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3507,7 +3507,7 @@ func TestBuildJob_KelosBaseBranchAbsentWithoutWorkspace(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3537,7 +3537,7 @@ func TestBuildJob_WorkspaceWithOneRemote(t *testing.T) { Prompt: "Work on feature", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3588,7 +3588,7 @@ func TestBuildJob_WorkspaceWithMultipleRemotes(t *testing.T) { Prompt: "Work on feature", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3639,7 +3639,7 @@ func TestBuildJob_WorkspaceWithNoRemotesNoRemoteSetupContainer(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3674,7 +3674,7 @@ func TestBuildJob_RemoteSetupOrderingWithBranchSetup(t *testing.T) { Branch: "feature-x", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3730,7 +3730,7 @@ func TestBuildJob_RemoteSetupQuotesShellMetacharacters(t *testing.T) { Prompt: "Do work", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3777,7 +3777,7 @@ func TestBuildJob_WorkspaceWithUpstreamRemoteInjectsEnv(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3823,7 +3823,7 @@ func TestBuildJob_WorkspaceWithNonUpstreamRemoteNoEnv(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3861,7 +3861,7 @@ func TestBuildJob_WorkspaceWithInvalidUpstreamRemoteNoEnv(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3902,7 +3902,7 @@ func TestBuildJob_TaskSpawnerLabelInjectsEnv(t *testing.T) { Prompt: "Hello", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3939,7 +3939,7 @@ func TestBuildJob_NoTaskSpawnerLabelNoEnv(t *testing.T) { Prompt: "Hello", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -3969,7 +3969,7 @@ func TestBuildJob_PodFailurePolicy(t *testing.T) { Prompt: "Hello", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -4033,7 +4033,7 @@ func TestBuildJob_GHConfigDirNotSetWithoutSecretRef(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -4067,7 +4067,7 @@ func TestBuildJob_CustomImageGetsGHConfigDir(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, Image: "my-custom-agent:latest", }, @@ -4116,7 +4116,7 @@ func TestBuildJob_CredentialHelperClearsInheritedHelpers(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, }, } @@ -4168,7 +4168,7 @@ func TestBuildJob_UpstreamRepoSpecOverridesRemote(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, UpstreamRepo: "override-org/override-repo", }, @@ -4215,7 +4215,7 @@ func TestBuildJob_UpstreamRepoSpecWithoutRemote(t *testing.T) { Prompt: "Fix the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "my-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "my-secret"}, }, UpstreamRepo: "upstream-org/upstream-repo", }, @@ -4259,7 +4259,7 @@ func TestBuildClaudeCodeJob_BedrockCredentials(t *testing.T) { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeBedrock, - SecretRef: kelosv1alpha1.SecretReference{Name: "bedrock-creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "bedrock-creds"}, }, }, } @@ -4332,3 +4332,71 @@ func TestBuildClaudeCodeJob_BedrockCredentials(t *testing.T) { t.Error("CLAUDE_CODE_OAUTH_TOKEN should not be set for bedrock credential type") } } + +func TestBuildClaudeCodeJob_BedrockIRSA(t *testing.T) { + builder := NewJobBuilder() + task := &kelosv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-bedrock-irsa", + Namespace: "default", + }, + Spec: kelosv1alpha1.TaskSpec{ + Type: AgentTypeClaudeCode, + Prompt: "Fix the bug", + Credentials: kelosv1alpha1.Credentials{ + Type: kelosv1alpha1.CredentialTypeBedrock, + Region: "us-west-2", + ServiceAccountName: "bedrock-agent-sa", + }, + }, + } + + job, err := builder.Build(task, nil, nil, task.Spec.Prompt) + if err != nil { + t.Fatalf("Build() returned error: %v", err) + } + + container := job.Spec.Template.Spec.Containers[0] + + // Collect env vars by name. + envMap := make(map[string]corev1.EnvVar) + for _, env := range container.Env { + envMap[env.Name] = env + } + + // CLAUDE_CODE_USE_BEDROCK should be set as a literal value. + if env, ok := envMap["CLAUDE_CODE_USE_BEDROCK"]; !ok { + t.Error("Expected CLAUDE_CODE_USE_BEDROCK env var") + } else if env.Value != "1" { + t.Errorf("CLAUDE_CODE_USE_BEDROCK = %q, want %q", env.Value, "1") + } + + // AWS_REGION should be set as a literal value (not from a secret). + if env, ok := envMap["AWS_REGION"]; !ok { + t.Error("Expected AWS_REGION env var") + } else { + if env.Value != "us-west-2" { + t.Errorf("AWS_REGION = %q, want %q", env.Value, "us-west-2") + } + if env.ValueFrom != nil { + t.Error("AWS_REGION should be a literal value, not a secret reference") + } + } + + // Static AWS credentials should NOT be set in IRSA mode. + for _, key := range []string{"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "ANTHROPIC_BEDROCK_BASE_URL"} { + if _, ok := envMap[key]; ok { + t.Errorf("%s should not be set in IRSA mode (no secretRef)", key) + } + } + + // ANTHROPIC_API_KEY should NOT be set. + if _, ok := envMap["ANTHROPIC_API_KEY"]; ok { + t.Error("ANTHROPIC_API_KEY should not be set for bedrock credential type") + } + + // ServiceAccountName should be set on the pod spec. + if job.Spec.Template.Spec.ServiceAccountName != "bedrock-agent-sa" { + t.Errorf("ServiceAccountName = %q, want %q", job.Spec.Template.Spec.ServiceAccountName, "bedrock-agent-sa") + } +} diff --git a/internal/controller/task_controller_test.go b/internal/controller/task_controller_test.go index d4895345..a1323e9f 100644 --- a/internal/controller/task_controller_test.go +++ b/internal/controller/task_controller_test.go @@ -607,7 +607,7 @@ func TestUpdateStatusRefreshesPodName(t *testing.T) { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "creds", }, }, @@ -663,7 +663,7 @@ func TestUpdateStatusClearsStalePodNameWhenNoLivePodsRemain(t *testing.T) { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "creds", }, }, diff --git a/internal/controller/taskspawner_deployment_builder_test.go b/internal/controller/taskspawner_deployment_builder_test.go index c17ae3eb..ca7e4e73 100644 --- a/internal/controller/taskspawner_deployment_builder_test.go +++ b/internal/controller/taskspawner_deployment_builder_test.go @@ -1483,7 +1483,7 @@ func TestBuildCronJob_BasicSchedule(t *testing.T) { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, }, @@ -1611,7 +1611,7 @@ func TestIsCronBased(t *testing.T) { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, }, diff --git a/internal/manifests/install-crd.yaml b/internal/manifests/install-crd.yaml index 1ec0e328..30b14269 100644 --- a/internal/manifests/install-crd.yaml +++ b/internal/manifests/install-crd.yaml @@ -303,8 +303,16 @@ spec: credentials: description: Credentials specifies how to authenticate with the agent. properties: + region: + description: |- + Region specifies the cloud provider region (e.g. AWS region for Bedrock). + Used with bedrock credentials when secretRef is omitted (IRSA mode). + type: string secretRef: - description: SecretRef references the Secret containing credentials. + description: |- + SecretRef references the Secret containing credentials. + Required for api-key and oauth types. Optional for bedrock + when using IAM Roles for Service Accounts (IRSA). properties: name: description: Name is the name of the secret. @@ -312,17 +320,26 @@ spec: required: - name type: object + serviceAccountName: + description: |- + ServiceAccountName overrides the pod's service account. + Use with IAM Roles for Service Accounts (IRSA) on EKS to let + the pod assume an IAM role without static credentials. + type: string type: - description: Type specifies the credential type (api-key or oauth). + description: Type specifies the credential type. enum: - api-key - oauth - bedrock type: string required: - - secretRef - type type: object + x-kubernetes-validations: + - message: secretRef is required for api-key and oauth credential + types + rule: self.type == 'bedrock' || has(self.secretRef) dependsOn: description: DependsOn lists Task names that must succeed before this Task starts. @@ -791,8 +808,16 @@ spec: description: Credentials specifies how to authenticate with the agent. properties: + region: + description: |- + Region specifies the cloud provider region (e.g. AWS region for Bedrock). + Used with bedrock credentials when secretRef is omitted (IRSA mode). + type: string secretRef: - description: SecretRef references the Secret containing credentials. + description: |- + SecretRef references the Secret containing credentials. + Required for api-key and oauth types. Optional for bedrock + when using IAM Roles for Service Accounts (IRSA). properties: name: description: Name is the name of the secret. @@ -800,18 +825,26 @@ spec: required: - name type: object + serviceAccountName: + description: |- + ServiceAccountName overrides the pod's service account. + Use with IAM Roles for Service Accounts (IRSA) on EKS to let + the pod assume an IAM role without static credentials. + type: string type: - description: Type specifies the credential type (api-key or - oauth). + description: Type specifies the credential type. enum: - api-key - oauth - bedrock type: string required: - - secretRef - type type: object + x-kubernetes-validations: + - message: secretRef is required for api-key and oauth credential + types + rule: self.type == 'bedrock' || has(self.secretRef) dependsOn: description: DependsOn lists Task names that spawned Tasks depend on. diff --git a/internal/reporting/watcher_test.go b/internal/reporting/watcher_test.go index 9605af04..4c6b471f 100644 --- a/internal/reporting/watcher_test.go +++ b/internal/reporting/watcher_test.go @@ -107,7 +107,7 @@ func newTaskWithAnnotations(name, namespace string, phase kelosv1alpha1.TaskPhas Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, Status: kelosv1alpha1.TaskStatus{ @@ -555,7 +555,7 @@ func TestReportTaskStatus_NilAnnotations(t *testing.T) { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "creds"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "creds"}, }, }, Status: kelosv1alpha1.TaskStatus{ diff --git a/test/e2e/opencode_test.go b/test/e2e/opencode_test.go index e8b92d13..05285dd3 100644 --- a/test/e2e/opencode_test.go +++ b/test/e2e/opencode_test.go @@ -31,7 +31,7 @@ var _ = Describe("OpenCode Task", func() { Prompt: "Print 'Hello from OpenCode e2e test' to stdout", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "opencode-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "opencode-credentials"}, }, }, }) diff --git a/test/e2e/skills_test.go b/test/e2e/skills_test.go index 11f09145..0adbf507 100644 --- a/test/e2e/skills_test.go +++ b/test/e2e/skills_test.go @@ -81,7 +81,7 @@ var _ = Describe("Task with skills.sh AgentConfig", func() { Prompt: "Print 'Hello from skills.sh e2e test' to stdout", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "claude-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "claude-credentials"}, }, AgentConfigRef: &kelosv1alpha1.AgentConfigReference{ Name: "skills-ac", diff --git a/test/e2e/task_test.go b/test/e2e/task_test.go index 360d1e5d..920eaece 100644 --- a/test/e2e/task_test.go +++ b/test/e2e/task_test.go @@ -37,7 +37,7 @@ func describeAgentTests(cfg agentTestConfig) { Prompt: "Print 'Hello from Kelos e2e test' to stdout", Credentials: kelosv1alpha1.Credentials{ Type: cfg.CredentialType, - SecretRef: kelosv1alpha1.SecretReference{Name: cfg.SecretName}, + SecretRef: &kelosv1alpha1.SecretReference{Name: cfg.SecretName}, }, }, }) @@ -92,7 +92,7 @@ func describeAgentTests(cfg agentTestConfig) { Prompt: "Create a file called 'test.txt' with the content 'hello' in the current directory and print 'done'", Credentials: kelosv1alpha1.Credentials{ Type: cfg.CredentialType, - SecretRef: kelosv1alpha1.SecretReference{Name: cfg.SecretName}, + SecretRef: &kelosv1alpha1.SecretReference{Name: cfg.SecretName}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "e2e-workspace"}, }, @@ -153,7 +153,7 @@ func describeAgentTests(cfg agentTestConfig) { Prompt: "Print 'hello' to stdout", Credentials: kelosv1alpha1.Credentials{ Type: cfg.CredentialType, - SecretRef: kelosv1alpha1.SecretReference{Name: cfg.SecretName}, + SecretRef: &kelosv1alpha1.SecretReference{Name: cfg.SecretName}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "e2e-outputs-workspace"}, }, @@ -225,7 +225,7 @@ func describeAgentTests(cfg agentTestConfig) { Prompt: "Print 'Task A done' to stdout", Credentials: kelosv1alpha1.Credentials{ Type: cfg.CredentialType, - SecretRef: kelosv1alpha1.SecretReference{Name: cfg.SecretName}, + SecretRef: &kelosv1alpha1.SecretReference{Name: cfg.SecretName}, }, }, }) @@ -242,7 +242,7 @@ func describeAgentTests(cfg agentTestConfig) { DependsOn: []string{"dep-chain-a"}, Credentials: kelosv1alpha1.Credentials{ Type: cfg.CredentialType, - SecretRef: kelosv1alpha1.SecretReference{Name: cfg.SecretName}, + SecretRef: &kelosv1alpha1.SecretReference{Name: cfg.SecretName}, }, }, }) @@ -288,7 +288,7 @@ func describeAgentTests(cfg agentTestConfig) { Prompt: "Print 'Hello' to stdout", Credentials: kelosv1alpha1.Credentials{ Type: cfg.CredentialType, - SecretRef: kelosv1alpha1.SecretReference{Name: cfg.SecretName}, + SecretRef: &kelosv1alpha1.SecretReference{Name: cfg.SecretName}, }, }, }) @@ -336,7 +336,7 @@ var _ = Describe("Task with make available", func() { Prompt: "Run 'make --version' and print the output", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "claude-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "claude-credentials"}, }, }, }) @@ -400,7 +400,7 @@ var _ = Describe("Task with workspace and secretRef", func() { Prompt: "Run 'gh auth status' and print the output", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "claude-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "claude-credentials"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "e2e-github-workspace"}, }, diff --git a/test/e2e/taskspawner_test.go b/test/e2e/taskspawner_test.go index 471b963c..d1e12020 100644 --- a/test/e2e/taskspawner_test.go +++ b/test/e2e/taskspawner_test.go @@ -63,7 +63,7 @@ var _ = Describe("TaskSpawner", func() { }, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "claude-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "claude-credentials"}, }, PromptTemplate: "Fix: {{.Title}}\n{{.Body}}", }, @@ -112,7 +112,7 @@ var _ = Describe("TaskSpawner", func() { }, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "claude-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "claude-credentials"}, }, }, PollInterval: "5m", @@ -174,7 +174,7 @@ var _ = Describe("Cron TaskSpawner", func() { Model: testModel, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "claude-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "claude-credentials"}, }, PromptTemplate: "Cron triggered at {{.Time}} (schedule: {{.Schedule}}). Print 'Hello from cron'", }, @@ -212,7 +212,7 @@ var _ = Describe("Cron TaskSpawner", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{Name: "claude-credentials"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "claude-credentials"}, }, }, PollInterval: "5m", diff --git a/test/integration/cli_test.go b/test/integration/cli_test.go index ed610eca..41d9faed 100644 --- a/test/integration/cli_test.go +++ b/test/integration/cli_test.go @@ -198,7 +198,7 @@ var _ = Describe("CLI Delete All Commands", func() { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -289,7 +289,7 @@ var _ = Describe("CLI Delete All Commands", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -379,7 +379,7 @@ var _ = Describe("CLI Delete TaskSpawner Command", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -427,7 +427,7 @@ var _ = Describe("CLI Delete TaskSpawner Command", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -475,7 +475,7 @@ var _ = Describe("CLI Delete TaskSpawner Command", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -543,7 +543,7 @@ var _ = Describe("CLI Suspend/Resume Commands", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -589,7 +589,7 @@ var _ = Describe("CLI Suspend/Resume Commands", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -635,7 +635,7 @@ var _ = Describe("CLI Suspend/Resume Commands", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -679,7 +679,7 @@ var _ = Describe("CLI Suspend/Resume Commands", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -717,7 +717,7 @@ var _ = Describe("CLI Suspend/Resume Commands", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -770,7 +770,7 @@ var _ = Describe("CLI Suspend/Resume Commands", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, diff --git a/test/integration/completion_test.go b/test/integration/completion_test.go index 99e295a8..05d9c7e4 100644 --- a/test/integration/completion_test.go +++ b/test/integration/completion_test.go @@ -68,7 +68,7 @@ var _ = Describe("Completion", func() { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -106,7 +106,7 @@ var _ = Describe("Completion", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, @@ -143,7 +143,7 @@ var _ = Describe("Completion", func() { Prompt: "test", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "test-secret", }, }, diff --git a/test/integration/install_test.go b/test/integration/install_test.go index 61ad178b..aa32ede3 100644 --- a/test/integration/install_test.go +++ b/test/integration/install_test.go @@ -241,7 +241,7 @@ var _ = Describe("Install/Uninstall", Ordered, func() { Prompt: "test prompt", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "fake-secret"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "fake-secret"}, }, }, } diff --git a/test/integration/metrics_test.go b/test/integration/metrics_test.go index d53755ec..64da1b10 100644 --- a/test/integration/metrics_test.go +++ b/test/integration/metrics_test.go @@ -91,7 +91,7 @@ func createAndCompleteTask(nsName, taskName, spawner, model string) *kelosv1alph Prompt: fmt.Sprintf("Test task %s", taskName), Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, diff --git a/test/integration/task_test.go b/test/integration/task_test.go index ef339816..642996d7 100644 --- a/test/integration/task_test.go +++ b/test/integration/task_test.go @@ -79,7 +79,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Create a hello world program", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -234,7 +234,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Create a hello world program", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-oauth", }, }, @@ -339,7 +339,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Resolve MCP headersFrom", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, AgentConfigRef: &kelosv1alpha1.AgentConfigReference{Name: "mcp-headers-from-config"}, }, @@ -445,7 +445,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Resolve MCP envFrom", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, AgentConfigRef: &kelosv1alpha1.AgentConfigReference{Name: "mcp-env-from-config"}, }, @@ -534,7 +534,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fail on missing MCP secret", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, AgentConfigRef: &kelosv1alpha1.AgentConfigReference{Name: "mcp-missing-secret-config"}, }, @@ -629,7 +629,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Prefer MCP secret values", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, AgentConfigRef: &kelosv1alpha1.AgentConfigReference{Name: "mcp-precedence-config"}, }, @@ -711,7 +711,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -834,7 +834,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Create a PR", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -953,7 +953,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Review the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -1030,7 +1030,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Create a hello world program", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -1112,7 +1112,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Create a hello world program", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -1183,7 +1183,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Create a hello world program", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -1274,7 +1274,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -1386,7 +1386,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -1481,7 +1481,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "codex-api-key", }, }, @@ -1590,7 +1590,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Refactor the module", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "codex-api-key", }, }, @@ -1674,7 +1674,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Review the code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "codex-oauth-secret", }, }, @@ -1739,7 +1739,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "opencode-api-key", }, }, @@ -1835,7 +1835,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -1958,7 +1958,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Fix the bug", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -2048,7 +2048,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Test events", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -2164,7 +2164,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Test failure event", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -2248,7 +2248,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Do something", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2266,7 +2266,7 @@ var _ = Describe("Task Controller", func() { DependsOn: []string{"task-a"}, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2364,7 +2364,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Do something", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2382,7 +2382,7 @@ var _ = Describe("Task Controller", func() { DependsOn: []string{"dep-task-a"}, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2467,7 +2467,7 @@ var _ = Describe("Task Controller", func() { Branch: "feature-1", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2510,7 +2510,7 @@ var _ = Describe("Task Controller", func() { Branch: "feature-1", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2597,7 +2597,7 @@ var _ = Describe("Task Controller", func() { Branch: "feature-x", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{ Name: "test-workspace", @@ -2714,7 +2714,7 @@ var _ = Describe("Task Controller", func() { Branch: "feature-1", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "workspace-a"}, }, @@ -2758,7 +2758,7 @@ var _ = Describe("Task Controller", func() { Branch: "feature-1", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "workspace-b"}, }, @@ -2808,7 +2808,7 @@ var _ = Describe("Task Controller", func() { DependsOn: []string{"cycle-task-b"}, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2826,7 +2826,7 @@ var _ = Describe("Task Controller", func() { DependsOn: []string{"cycle-task-a"}, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2878,7 +2878,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Generate outputs", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2929,7 +2929,7 @@ var _ = Describe("Task Controller", func() { DependsOn: []string{"tmpl-task-a"}, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -2980,7 +2980,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Generate results", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -3035,7 +3035,7 @@ var _ = Describe("Task Controller", func() { DependsOn: []string{"results-task-a"}, Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, }, } @@ -3103,7 +3103,7 @@ var _ = Describe("Task Controller", func() { Branch: "kelos-task-42", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "test-workspace"}, }, @@ -3125,7 +3125,7 @@ var _ = Describe("Task Controller", func() { Branch: "kelos-task-99", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "test-workspace"}, }, @@ -3252,7 +3252,7 @@ var _ = Describe("Task Controller", func() { Branch: "kelos-task-42", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "lock-workspace"}, }, @@ -3298,7 +3298,7 @@ var _ = Describe("Task Controller", func() { Branch: "kelos-task-42", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, + SecretRef: &kelosv1alpha1.SecretReference{Name: "anthropic-api-key"}, }, WorkspaceRef: &kelosv1alpha1.WorkspaceReference{Name: "lock-workspace"}, }, @@ -3372,7 +3372,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Original prompt", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, @@ -3454,7 +3454,7 @@ var _ = Describe("Task Controller", func() { Prompt: "Work on feature", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeAPIKey, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "anthropic-api-key", }, }, diff --git a/test/integration/taskspawner_test.go b/test/integration/taskspawner_test.go index 7489aa85..84eed323 100644 --- a/test/integration/taskspawner_test.go +++ b/test/integration/taskspawner_test.go @@ -66,7 +66,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -199,7 +199,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -265,7 +265,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -335,7 +335,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -428,7 +428,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -485,7 +485,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -578,7 +578,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -658,7 +658,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -801,7 +801,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -924,7 +924,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1005,7 +1005,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1115,7 +1115,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1188,7 +1188,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1309,7 +1309,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1388,7 +1388,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1465,7 +1465,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1547,7 +1547,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1685,7 +1685,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1831,7 +1831,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1921,7 +1921,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -1996,7 +1996,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -2067,7 +2067,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -2144,7 +2144,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -2222,7 +2222,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -2279,7 +2279,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -2341,7 +2341,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, }, @@ -2418,7 +2418,7 @@ var _ = Describe("TaskSpawner Controller", func() { Type: "claude-code", Credentials: kelosv1alpha1.Credentials{ Type: kelosv1alpha1.CredentialTypeOAuth, - SecretRef: kelosv1alpha1.SecretReference{ + SecretRef: &kelosv1alpha1.SecretReference{ Name: "claude-credentials", }, },