Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
7ddd66d
[06/24] Add gke_code_executor.py
syangx39 Jun 24, 2025
e8635b9
[06/24] Add gke_code_executor.py
syangx39 Jun 24, 2025
3dd917b
[06/24] Add gke_code_executor.py
syangx39 Jun 24, 2025
bdbda05
[06/24] Add gke_code_executor.py
syangx39 Jun 24, 2025
45fbf28
Merge branch 'google:main' into main
syangx39 Jul 10, 2025
c011798
[07/10] Add deployemnt_rbac.yaml into samples folder
syangx39 Jul 10, 2025
e54728e
[07/10] Add deployemnt_rbac.yaml into samples folder
syangx39 Jul 10, 2025
c6a7374
[07/10] Add deployemnt_rbac.yaml into samples folder
syangx39 Jul 10, 2025
b5d6ce4
[07/10] Make cpu/memory requests configurable
syangx39 Jul 10, 2025
3d839f9
[07/10] Add annotation
syangx39 Jul 11, 2025
1546600
Merge branch 'google:main' into main
syangx39 Jul 11, 2025
ae43fad
[07/10] Raise error in _get_pod_logs()
syangx39 Jul 11, 2025
764c3b0
[07/10] Add owner_reference
syangx39 Jul 11, 2025
cb14ea8
[07/10] Add owner_reference
syangx39 Jul 11, 2025
63deea5
Merge branch 'google:main' into main
syangx39 Jul 11, 2025
253ffdf
Merge branch 'main' into main
hangfei Jul 16, 2025
5533fbf
Merge branch 'main' into main
hangfei Jul 22, 2025
937aa76
Merge branch 'google:main' into main
syangx39 Jul 25, 2025
bb6c865
[07/25] add the sample
syangx39 Jul 25, 2025
2242a36
[07/25] modify pyproject.toml to add gke-specific dependency
syangx39 Jul 25, 2025
819d4e6
Merge branch 'main' into main
hangfei Jul 28, 2025
3e88da2
Merge branch 'main' into main
hangfei Jul 30, 2025
add4d76
Merge branch 'google:main' into main
syangx39 Aug 15, 2025
45bdd7c
[08/15] rename cpu_request
syangx39 Aug 15, 2025
d0b9397
[08/15] rename cpu_request
syangx39 Aug 15, 2025
8748117
[08/15] rename cpu_request
syangx39 Aug 15, 2025
5c84bd6
Merge branch 'main' into main
syangx39 Aug 15, 2025
3ed54b4
Merge branch 'main' into main
hangfei Aug 19, 2025
a6f8562
Merge branch 'main' into main
seanzhou1023 Aug 19, 2025
f4ca009
Merge branch 'main' into main
hangfei Aug 19, 2025
c0ab6f1
Merge branch 'main' into main
hangfei Aug 20, 2025
2188681
Merge branch 'google:main' into main
syangx39 Aug 25, 2025
5d851e0
[08/25] Modify ADK
syangx39 Aug 25, 2025
51b94a1
[08/25] Modify ADK
syangx39 Aug 25, 2025
b429edb
[08/25] Modify ADK
syangx39 Aug 25, 2025
80c17ed
[08/25] Modify ADK
syangx39 Aug 25, 2025
6c1dba9
[08/25] Modify ADK
syangx39 Aug 25, 2025
ef8aef9
[08/25] Modify ADK
syangx39 Aug 25, 2025
b4ce8a2
Merge branch 'google:main' into main
syangx39 Aug 27, 2025
50e43aa
Merge branch 'google:main' into main
syangx39 Aug 28, 2025
d1a8d11
[08/28] GKE cli fix
syangx39 Aug 28, 2025
d500ff3
[08/28] GKE cli fix
syangx39 Aug 28, 2025
b434fc9
[08/28] GKE cli fix
syangx39 Aug 28, 2025
09fa7ce
[08/28] GKE cli fix
syangx39 Aug 28, 2025
61eec69
[08/28] GKE cli fix
syangx39 Aug 28, 2025
9598469
[08/28] GKE cli fix
syangx39 Aug 28, 2025
01c5261
[08/28] GKE cli fix
syangx39 Aug 28, 2025
37983f9
[08/28] GKE cli fix
syangx39 Aug 28, 2025
eb3008b
[08/28] GKE cli fix
syangx39 Aug 28, 2025
49b7c22
[08/28] GKE cli fix
syangx39 Aug 28, 2025
4e88f42
[08/28] GKE cli fix
syangx39 Aug 29, 2025
bd0cb7f
Merge branch 'main' into gke_cli_fix
hangfei Sep 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions src/google/adk/cli/cli_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import json
import os
from pathlib import Path
import shutil
import subprocess
from typing import Final
Expand Down Expand Up @@ -43,7 +44,7 @@
# Set up environment variables - End

# Install ADK - Start
RUN pip install google-adk=={adk_version}
{adk_install_instructions}
# Install ADK - End

# Copy agent - Start
Expand Down Expand Up @@ -246,6 +247,10 @@ def to_cloud_run(
)
click.echo('Copying agent source code completed.')

adk_install_instructions = (
f'RUN pip install google-adk=={adk_version}'
)

# create Dockerfile
click.echo('Creating Dockerfile...')
host_option = '--host=0.0.0.0' if adk_version > '0.5.0' else ''
Expand All @@ -268,6 +273,7 @@ def to_cloud_run(
),
trace_to_cloud_option='--trace_to_cloud' if trace_to_cloud else '',
allow_origins_option=allow_origins_option,
adk_install_instructions=adk_install_instructions,
adk_version=adk_version,
host_option=host_option,
a2a_option=a2a_option,
Expand Down Expand Up @@ -622,6 +628,8 @@ def to_gke(
artifact_service_uri: Optional[str] = None,
memory_service_uri: Optional[str] = None,
a2a: bool = False,
editable: bool = False,
service_account_name: Optional[str] = None,
):
"""Deploys an agent to Google Kubernetes Engine(GKE).

Expand All @@ -645,6 +653,7 @@ def to_gke(
session_service_uri: The URI of the session service.
artifact_service_uri: The URI of the artifact service.
memory_service_uri: The URI of the memory service.
service_account_name: The name of the Kubernetes Service Account to use for the deployed agent pod.
"""
click.secho(
'\n🚀 Starting ADK Agent Deployment to GKE...', fg='cyan', bold=True
Expand Down Expand Up @@ -680,6 +689,22 @@ def to_gke(
)
click.secho('✅ Environment prepared.', fg='green')

adk_install_instructions = (
f'RUN pip install "google-adk=={adk_version}"'
)
if editable:
click.echo(' - Preparing local ADK source for editable install...')
# Find the project root to include pyproject.toml
adk_source_path = next(p for p in Path(__file__).resolve().parents if (p / 'pyproject.toml').is_file())
temp_adk_source_dest = Path(temp_folder) / 'adk_local_src'
shutil.copytree(adk_source_path, temp_adk_source_dest)
adk_install_instructions = (
'# Install ADK from local source \n'
'COPY --chown=myuser:myuser adk_local_src/ /app/adk_local_src/\n'
'RUN pip install --editable "/app/adk_local_src/[extensions]"'
)
click.secho('✅ Local ADK source prepared.', fg='green')

allow_origins_option = (
f'--allow_origins={",".join(allow_origins)}' if allow_origins else ''
)
Expand All @@ -703,6 +728,7 @@ def to_gke(
),
trace_to_cloud_option='--trace_to_cloud' if trace_to_cloud else '',
allow_origins_option=allow_origins_option,
adk_install_instructions=adk_install_instructions,
adk_version=adk_version,
host_option=host_option,
a2a_option='--a2a' if a2a else '',
Expand Down Expand Up @@ -742,6 +768,10 @@ def to_gke(

# Create a Kubernetes deployment
click.echo(' - Creating Kubernetes deployment.yaml...')
sa_yaml_block = ''
if service_account_name:
# The newline at the end is important for correct YAML formatting.
sa_yaml_block = f'serviceAccountName: {service_account_name}\n'
deployment_yaml = f"""
apiVersion: apps/v1
kind: Deployment
Expand All @@ -766,6 +796,7 @@ def to_gke(
app.kubernetes.io/instance: {service_name}
app.kubernetes.io/managed-by: adk-cli
spec:
{sa_yaml_block}
containers:
- name: {service_name}
image: {image_name}
Expand Down Expand Up @@ -813,8 +844,8 @@ def to_gke(
result = subprocess.run(
['kubectl', 'apply', '-f', temp_folder],
check=True,
capture_output=True, # <-- Add this
text=True, # <-- Add this
capture_output=True,
text=True,
)

# 2. Print the captured output line by line
Expand Down
7 changes: 7 additions & 0 deletions src/google/adk/cli/cli_tools_click.py
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,11 @@ def cli_deploy_agent_engine(
" of the AGENT source code)."
),
)
@click.option(
"--service-account-name",
default=None,
help="Optional. Name of the K8s Service Account for the pod.",
)
@click.option(
"--port",
type=int,
Expand Down Expand Up @@ -1351,6 +1356,7 @@ def cli_deploy_gke(
cluster_name: str,
service_name: str,
app_name: str,
service_account_name: Optional[str],
temp_folder: str,
port: int,
trace_to_cloud: bool,
Expand All @@ -1377,6 +1383,7 @@ def cli_deploy_gke(
cluster_name=cluster_name,
service_name=service_name,
app_name=app_name,
service_account_name=service_account_name,
temp_folder=temp_folder,
port=port,
trace_to_cloud=trace_to_cloud,
Expand Down
98 changes: 98 additions & 0 deletions tests/unittests/cli/utils/test_cli_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,104 @@ def mock_subprocess_run(*args, **kwargs):
assert f"containerPort: 9090" in yaml_content
assert f"targetPort: 9090" in yaml_content
assert "type: LoadBalancer" in yaml_content
assert "serviceAccountName:" not in yaml_content

# 4. Verify cleanup
assert str(rmtree_recorder.get_last_call_args()[0]) == str(tmp_path)

def test_to_gke_with_service_account(
monkeypatch: pytest.MonkeyPatch,
agent_dir: Callable[[bool, bool], Path],
tmp_path: Path,
) -> None:
"""
Tests that `to_gke` correctly adds the serviceAccountName to the
deployment manifest when the parameter is provided.
"""
src_dir = agent_dir(False, False)
monkeypatch.setattr(subprocess, "run", lambda *a, **k: types.SimpleNamespace(stdout=""))
monkeypatch.setattr(shutil, "rmtree", lambda *a, **k: None)

# Execute with the new service_account_name parameter
cli_deploy.to_gke(
agent_folder=str(src_dir),
project="gke-proj",
region="us-east1",
cluster_name="my-gke-cluster",
service_name="gke-svc",
app_name="agent",
temp_folder=str(tmp_path),
port=9090,
trace_to_cloud=False,
with_ui=False,
log_level="debug",
adk_version="1.2.0",
service_account_name="my-test-sa",
)

deployment_yaml_path = tmp_path / "deployment.yaml"
assert deployment_yaml_path.is_file()
yaml_content = deployment_yaml_path.read_text()

assert "serviceAccountName: my-test-sa" in yaml_content

def test_to_gke_editable_mode(
monkeypatch: pytest.MonkeyPatch,
agent_dir: Callable[[bool, bool], Path],
tmp_path: Path,
) -> None:
"""
Tests that `to_gke` with `editable=True` generates the correct Dockerfile.

Verifies:
1. The local ADK source is copied (mocked).
2. The Dockerfile contains `COPY` and `pip install --editable` commands.
3. The Dockerfile does NOT contain the standard `pip install from pypi`.
"""
src_dir = agent_dir(False, False)
# Mock subprocess and cleanup functions
monkeypatch.setattr(subprocess, "run", lambda *a, **k: types.SimpleNamespace(stdout=""))
monkeypatch.setattr(shutil, "rmtree", lambda *a, **k: None)

# Mock the shutil.copytree to avoid actual file operations for the ADK source
copytree_recorder = _Recorder()
# The first call will be for the agent, the second for the ADK source
original_copytree = shutil.copytree
def mock_copytree(src, dst, **kwargs):
copytree_recorder(src, dst, **kwargs)
# We still need to copy the agent for the test to proceed
if "agent" in str(src):
original_copytree(src, dst, **kwargs)

monkeypatch.setattr(shutil, "copytree", mock_copytree)

# Execute
cli_deploy.to_gke(
agent_folder=str(src_dir),
project="gke-proj",
region="us-east1",
cluster_name="my-gke-cluster",
service_name="gke-svc",
app_name="agent",
temp_folder=str(tmp_path),
port=9090,
trace_to_cloud=False,
with_ui=False,
log_level="debug",
adk_version="1.2.0",
editable=True, # Test the new editable path
)

# 1. Verify that copytree was called for the ADK source
assert len(copytree_recorder.calls) == 2
adk_source_copy_call = copytree_recorder.calls[1][0]
assert str(adk_source_copy_call[1]) == str(tmp_path / "adk_local_src")

# 2. Verify Dockerfile content for editable mode
dockerfile_path = tmp_path / "Dockerfile"
assert dockerfile_path.is_file()
dockerfile_content = dockerfile_path.read_text()

assert "COPY --chown=myuser:myuser adk_local_src/ /app/adk_local_src/" in dockerfile_content
assert 'RUN pip install --editable "/app/adk_local_src/[extensions]"' in dockerfile_content
assert "RUN pip install google-adk==" not in dockerfile_content
Loading