diff --git a/tests/config/operator_install/olsconfig.crd.bedrock_anthropic.yaml b/tests/config/operator_install/olsconfig.crd.bedrock_anthropic.yaml new file mode 100644 index 000000000..981da966d --- /dev/null +++ b/tests/config/operator_install/olsconfig.crd.bedrock_anthropic.yaml @@ -0,0 +1,37 @@ +apiVersion: ols.openshift.io/v1alpha1 +kind: OLSConfig +metadata: + name: cluster + labels: + app.kubernetes.io/created-by: lightspeed-operator + app.kubernetes.io/instance: olsconfig-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: olsconfig + app.kubernetes.io/part-of: lightspeed-operator +spec: + llm: + providers: + - name: bedrock + type: bedrock + url: "https://bedrock-mantle.us-east-1.api.aws" + credentialsSecretRef: + name: llmcreds + models: + - name: anthropic.claude-sonnet-4-6 + ols: + defaultModel: anthropic.claude-sonnet-4-6 + defaultProvider: bedrock + deployment: + replicas: 1 + disableAuth: false + logLevel: DEBUG + queryFilters: + - name: foo_filter + pattern: '\b(?:foo)\b' + replaceWith: "deployment" + - name: bar_filter + pattern: '\b(?:bar)\b' + replaceWith: "openshift" + userDataCollection: + feedbackDisabled: true + transcriptsDisabled: true diff --git a/tests/config/operator_install/olsconfig.crd.bedrock_deepseek.yaml b/tests/config/operator_install/olsconfig.crd.bedrock_deepseek.yaml new file mode 100644 index 000000000..ffb198f43 --- /dev/null +++ b/tests/config/operator_install/olsconfig.crd.bedrock_deepseek.yaml @@ -0,0 +1,37 @@ +apiVersion: ols.openshift.io/v1alpha1 +kind: OLSConfig +metadata: + name: cluster + labels: + app.kubernetes.io/created-by: lightspeed-operator + app.kubernetes.io/instance: olsconfig-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: olsconfig + app.kubernetes.io/part-of: lightspeed-operator +spec: + llm: + providers: + - name: bedrock + type: bedrock + url: "https://bedrock-mantle.us-east-1.api.aws" + credentialsSecretRef: + name: llmcreds + models: + - name: deepseek.v3-v1:0 + ols: + defaultModel: deepseek.v3-v1:0 + defaultProvider: bedrock + deployment: + replicas: 1 + disableAuth: false + logLevel: DEBUG + queryFilters: + - name: foo_filter + pattern: '\b(?:foo)\b' + replaceWith: "deployment" + - name: bar_filter + pattern: '\b(?:bar)\b' + replaceWith: "openshift" + userDataCollection: + feedbackDisabled: true + transcriptsDisabled: true diff --git a/tests/config/operator_install/olsconfig.crd.bedrock_deepseek_tool_calling.yaml b/tests/config/operator_install/olsconfig.crd.bedrock_deepseek_tool_calling.yaml new file mode 100644 index 000000000..65f377df7 --- /dev/null +++ b/tests/config/operator_install/olsconfig.crd.bedrock_deepseek_tool_calling.yaml @@ -0,0 +1,38 @@ +apiVersion: ols.openshift.io/v1alpha1 +kind: OLSConfig +metadata: + name: cluster + labels: + app.kubernetes.io/created-by: lightspeed-operator + app.kubernetes.io/instance: olsconfig-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: olsconfig + app.kubernetes.io/part-of: lightspeed-operator +spec: + llm: + providers: + - name: bedrock + type: bedrock + url: "https://bedrock-mantle.us-east-1.api.aws" + credentialsSecretRef: + name: llmcreds + models: + - name: deepseek.v3-v1:0 + ols: + defaultModel: deepseek.v3-v1:0 + defaultProvider: bedrock + deployment: + replicas: 1 + disableAuth: false + logLevel: DEBUG + queryFilters: + - name: foo_filter + pattern: '\b(?:foo)\b' + replaceWith: "deployment" + - name: bar_filter + pattern: '\b(?:bar)\b' + replaceWith: "openshift" + introspectionEnabled: true + userDataCollection: + feedbackDisabled: true + transcriptsDisabled: true diff --git a/tests/e2e/utils/ols_installer.py b/tests/e2e/utils/ols_installer.py index e50b97ffb..201708409 100644 --- a/tests/e2e/utils/ols_installer.py +++ b/tests/e2e/utils/ols_installer.py @@ -261,6 +261,75 @@ def ensure_azure_entra_id_secret() -> None: ) +_BEDROCK_IAM_ENV_KEYS: tuple[str, ...] = ( + "BEDROCK_AWS_ACCESS_KEY_ID", + "BEDROCK_AWS_SECRET_ACCESS_KEY", +) + +_BEDROCK_ROLE_ENV_KEYS: tuple[str, ...] = ( + "BEDROCK_ROLE_AWS_ACCESS_KEY_ID", + "BEDROCK_ROLE_AWS_SECRET_ACCESS_KEY", + "BEDROCK_ROLE_ARN", +) + + +def ensure_bedrock_iam_secret(creds: str) -> None: + """Create openshift-lightspeed/llmcreds with AWS IAM credentials for Bedrock. + + Bedrock does not use an API token file like other providers. Instead, the + ``PROVIDER_KEY_PATH`` run_suite argument carries a discriminator string + (``"iam"`` or ``"iam_role"``) that selects which set of env vars to read. + + Args: + creds: discriminator passed via ``PROVIDER_KEY_PATH`` — + ``"iam"`` for direct IAM credentials, + ``"iam_role"`` for STS assume-role credentials. + """ + if creds == "iam_role": + env_keys = _BEDROCK_ROLE_ENV_KEYS + elif creds == "iam": + env_keys = _BEDROCK_IAM_ENV_KEYS + else: + raise ValueError(f"Unsupported Bedrock credentials mode: {creds}") + + values = {k: os.getenv(k) for k in env_keys} + missing = [k for k in env_keys if not values[k]] + if missing: + print( + "Skipping Bedrock IAM secret creation (unset or empty): " + f"{', '.join(missing)}. " + "Bedrock provider will not work until these CI/Vault/Prow env vars " + "are set." + ) + return + + oc_args: list[str] = [ + "create", + "secret", + "generic", + "llmcreds", + ] + + if creds == "iam_role": + oc_args.extend( + [ + f"--from-literal=aws_access_key_id={values['BEDROCK_ROLE_AWS_ACCESS_KEY_ID']}", + f"--from-literal=aws_secret_access_key={values['BEDROCK_ROLE_AWS_SECRET_ACCESS_KEY']}", + f"--from-literal=role_arn={values['BEDROCK_ROLE_ARN']}", + ] + ) + else: + oc_args.extend( + [ + f"--from-literal=aws_access_key_id={values['BEDROCK_AWS_ACCESS_KEY_ID']}", + f"--from-literal=aws_secret_access_key={values['BEDROCK_AWS_SECRET_ACCESS_KEY']}", + ] + ) + + print(f"Ensuring Bedrock IAM secret exists (mode={creds})...") + cluster_utils.run_oc(oc_args, ignore_existing_resource=True) + + def create_secrets(provider_name: str, creds: str, provider_size: int) -> None: """Create Kubernetes secrets needed for an LLM provider (API creds). @@ -285,6 +354,9 @@ def create_secrets(provider_name: str, creds: str, provider_size: int) -> None: ) except subprocess.CalledProcessError: print("llmcreds secret does not yet exist. Creating it.") + if provider_name.startswith("bedrock"): + ensure_bedrock_iam_secret(creds) + return if creds == "empty": cluster_utils.run_oc( [ diff --git a/tests/scripts/test-e2e-cluster-periodics.sh b/tests/scripts/test-e2e-cluster-periodics.sh index 7a0797261..949965a62 100755 --- a/tests/scripts/test-e2e-cluster-periodics.sh +++ b/tests/scripts/test-e2e-cluster-periodics.sh @@ -57,6 +57,21 @@ function run_suites() { run_suite "rhaiis_vllm" "not azure_entra_id and not certificates and not (tool_calling and not smoketest and not rag) and not byok1 and not byok2 and not quota_limits and not data_export" "rhaiis_vllm" "$RHAIIS_PROVIDER_KEY_PATH" "meta-llama/Llama-3.1-70B-Instruct" "$OLS_IMAGE" "default" (( rc = rc || $? )) + # Bedrock suites — PROVIDER_KEY_PATH carries a discriminator ("iam" or + # "iam_role") instead of a credential file path; see + # ensure_bedrock_iam_secret() in ols_installer.py. + run_suite "bedrock_anthropic" "not azure_entra_id and not certificates and not (tool_calling and not smoketest and not rag) and not byok1 and not byok2 and not quota_limits and not data_export" "bedrock_anthropic" "iam" "anthropic.claude-sonnet-4-6" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + + run_suite "bedrock_deepseek" "not azure_entra_id and not certificates and not (tool_calling and not smoketest and not rag) and not byok1 and not byok2 and not quota_limits and not data_export" "bedrock_deepseek" "iam" "deepseek.v3-v1:0" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + + run_suite "bedrock_anthropic_iam_role" "smoketest" "bedrock_anthropic" "iam_role" "anthropic.claude-sonnet-4-6" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + + run_suite "bedrock_deepseek_iam_role" "smoketest" "bedrock_deepseek" "iam_role" "deepseek.v3-v1:0" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + # smoke tests for RHOAI VLLM-compatible provider run_suite "rhoai_vllm" "smoketest" "rhoai_vllm" "$OPENAI_PROVIDER_KEY_PATH" "gpt-3.5-turbo" "$OLS_IMAGE" "default" (( rc = rc || $? )) @@ -70,6 +85,8 @@ function run_suites() { # TODO: Reduce execution time. Sequential execution will take more time. Parallel execution will have cluster claim issue. # Run tool calling - Enable tool_calling + run_suite "bedrock_deepseek_tool_calling" "tool_calling" "bedrock_deepseek" "iam" "deepseek.v3-v1:0" "$OLS_IMAGE" "tool_calling" + (( rc = rc || $? )) run_suite "azure_openai_tool_calling" "tool_calling" "azure_openai" "$AZUREOPENAI_PROVIDER_KEY_PATH" "gpt-4.1-mini" "$OLS_IMAGE" "tool_calling" (( rc = rc || $? )) run_suite "openai_tool_calling" "tool_calling" "openai" "$OPENAI_PROVIDER_KEY_PATH" "gpt-4.1-mini" "$OLS_IMAGE" "tool_calling" diff --git a/tests/scripts/test-e2e-cluster.sh b/tests/scripts/test-e2e-cluster.sh index cee7f40d9..91427c521 100755 --- a/tests/scripts/test-e2e-cluster.sh +++ b/tests/scripts/test-e2e-cluster.sh @@ -64,8 +64,25 @@ function run_suites() { run_suite "rhelai_vllm" "smoketest" "rhelai_vllm" "$OPENAI_PROVIDER_KEY_PATH" "gpt-3.5-turbo" "$OLS_IMAGE" "default" (( rc = rc || $? )) + # Bedrock suites — PROVIDER_KEY_PATH carries a discriminator ("iam" or + # "iam_role") instead of a credential file path; see + # ensure_bedrock_iam_secret() in ols_installer.py. + run_suite "bedrock_anthropic" "not azure_entra_id and not certificates and not (tool_calling and not smoketest and not rag) and not byok1 and not byok2 and not quota_limits and not data_export" "bedrock_anthropic" "iam" "anthropic.claude-sonnet-4-6" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + + run_suite "bedrock_deepseek" "not azure_entra_id and not certificates and not (tool_calling and not smoketest and not rag) and not byok1 and not byok2 and not quota_limits and not data_export" "bedrock_deepseek" "iam" "deepseek.v3-v1:0" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + + run_suite "bedrock_anthropic_iam_role" "smoketest" "bedrock_anthropic" "iam_role" "anthropic.claude-sonnet-4-6" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + + run_suite "bedrock_deepseek_iam_role" "smoketest" "bedrock_deepseek" "iam_role" "deepseek.v3-v1:0" "$OLS_IMAGE" "default" + (( rc = rc || $? )) + # TODO: Reduce execution time. Sequential execution will take more time. Parallel execution will have cluster claim issue. # Run tool calling - Enable tool_calling + run_suite "bedrock_deepseek_tool_calling" "tool_calling" "bedrock_deepseek" "iam" "deepseek.v3-v1:0" "$OLS_IMAGE" "tool_calling" + (( rc = rc || $? )) run_suite "azure_openai_tool_calling" "tool_calling" "azure_openai" "$AZUREOPENAI_PROVIDER_KEY_PATH" "gpt-4.1-mini" "$OLS_IMAGE" "tool_calling" (( rc = rc || $? )) run_suite "openai_tool_calling" "tool_calling" "openai" "$OPENAI_PROVIDER_KEY_PATH" "gpt-4.1-mini" "$OLS_IMAGE" "tool_calling"