Skip to content

Commit b3e5e6d

Browse files
committed
feat: cli funcionality to deploy an Agent to a running GKE cluster
feat: cli funcionality to deploy an Agent to a running GKE cluster feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint test not working locally feat: cli funcionality to deploy an Agent to a running GKE cluster feat: cli funcionality to deploy an Agent to a running GKE cluster feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint test not working locally updated test based on merge conflict feat: cli funcionality to deploy an Agent to a running GKE cluster feat: cli funcionality to deploy an Agent to a running GKE cluster feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint test not working locally feat: cli funcionality to deploy an Agent to a running GKE cluster feat: cli funcionality to deploy an Agent to a running GKE cluster feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint feat: cli funcionality to deploy an Agent to a running GKE cluster added tests for tools click adn cli deploy revert some changes on cli tools click added test for tools click fixed lint issues removed eval storage uri scope for now addressed comments on allow origins and nits formatting improved logging and updated tests added label to deployment so that we can keep track in keywords pipeline added labels to pod spec fixing conflicts when rebasing solving conflicts and log_level updated test pylint test not working locally updated test based on merge conflict fixed cli deploy test
1 parent 206a132 commit b3e5e6d

File tree

4 files changed

+1299
-355
lines changed

4 files changed

+1299
-355
lines changed

src/google/adk/cli/cli_deploy.py

Lines changed: 231 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,11 @@ def to_cloud_run(
153153
app_name: The name of the app, by default, it's basename of `agent_folder`.
154154
temp_folder: The temp folder for the generated Cloud Run source files.
155155
port: The port of the ADK api server.
156-
allow_origins: The list of allowed origins for the ADK api server.
157156
trace_to_cloud: Whether to enable Cloud Trace.
158157
with_ui: Whether to deploy with UI.
159158
verbosity: The verbosity level of the CLI.
160159
adk_version: The ADK version to use in Cloud Run.
160+
allow_origins: The list of allowed origins for the ADK api server.
161161
session_service_uri: The URI of the session service.
162162
artifact_service_uri: The URI of the artifact service.
163163
memory_service_uri: The URI of the memory service.
@@ -182,7 +182,7 @@ def to_cloud_run(
182182
if os.path.exists(requirements_txt_path)
183183
else ''
184184
)
185-
click.echo('Copying agent source code complete.')
185+
click.echo('Copying agent source code completed.')
186186

187187
# create Dockerfile
188188
click.echo('Creating Dockerfile...')
@@ -425,7 +425,7 @@ def to_agent_engine(
425425
'async_stream': ['async_stream_query'],
426426
'stream': ['stream_query', 'streaming_agent_run_with_events'],
427427
},
428-
sys_paths=[temp_folder[1:]],
428+
sys_paths=[temp_folder],
429429
)
430430
agent_config = dict(
431431
agent_engine=agent_engine,
@@ -443,3 +443,231 @@ def to_agent_engine(
443443
finally:
444444
click.echo(f'Cleaning up the temp folder: {temp_folder}')
445445
shutil.rmtree(temp_folder)
446+
447+
448+
def to_gke(
449+
*,
450+
agent_folder: str,
451+
project: Optional[str],
452+
region: Optional[str],
453+
cluster_name: str,
454+
service_name: str,
455+
app_name: str,
456+
temp_folder: str,
457+
port: int,
458+
trace_to_cloud: bool,
459+
with_ui: bool,
460+
log_level: str,
461+
verbosity: str,
462+
adk_version: str,
463+
allow_origins: Optional[list[str]] = None,
464+
session_service_uri: Optional[str] = None,
465+
artifact_service_uri: Optional[str] = None,
466+
memory_service_uri: Optional[str] = None,
467+
a2a: bool = False,
468+
):
469+
"""Deploys an agent to Google Kubernetes Engine(GKE).
470+
471+
Args:
472+
agent_folder: The folder (absolute path) containing the agent source code.
473+
project: Google Cloud project id.
474+
region: Google Cloud region.
475+
cluster_name: The name of the GKE cluster.
476+
service_name: The service name in GKE.
477+
app_name: The name of the app, by default, it's basename of `agent_folder`.
478+
temp_folder: The local directory to use as a temporary workspace for preparing deployment artifacts. The tool populates this folder with a copy of the agent's source code and auto-generates necessary files like a Dockerfile and deployment.yaml.
479+
port: The port of the ADK api server.
480+
trace_to_cloud: Whether to enable Cloud Trace.
481+
with_ui: Whether to deploy with UI.
482+
verbosity: The verbosity level of the CLI.
483+
adk_version: The ADK version to use in GKE.
484+
allow_origins: The list of allowed origins for the ADK api server.
485+
session_service_uri: The URI of the session service.
486+
artifact_service_uri: The URI of the artifact service.
487+
memory_service_uri: The URI of the memory service.
488+
"""
489+
click.secho(
490+
'\n🚀 Starting ADK Agent Deployment to GKE...', fg='cyan', bold=True
491+
)
492+
click.echo('--------------------------------------------------')
493+
# Resolve project early to show the user which one is being used
494+
project = _resolve_project(project)
495+
click.echo(f' Project: {project}')
496+
click.echo(f' Region: {region}')
497+
click.echo(f' Cluster: {cluster_name}')
498+
click.echo('--------------------------------------------------\n')
499+
500+
app_name = app_name or os.path.basename(agent_folder)
501+
502+
click.secho('STEP 1: Preparing build environment...', bold=True)
503+
click.echo(f' - Using temporary directory: {temp_folder}')
504+
505+
# remove temp_folder if exists
506+
if os.path.exists(temp_folder):
507+
click.echo(' - Removing existing temporary directory...')
508+
shutil.rmtree(temp_folder)
509+
510+
try:
511+
# copy agent source code
512+
click.echo(' - Copying agent source code...')
513+
agent_src_path = os.path.join(temp_folder, 'agents', app_name)
514+
shutil.copytree(agent_folder, agent_src_path)
515+
requirements_txt_path = os.path.join(agent_src_path, 'requirements.txt')
516+
install_agent_deps = (
517+
f'RUN pip install -r "/app/agents/{app_name}/requirements.txt"'
518+
if os.path.exists(requirements_txt_path)
519+
else ''
520+
)
521+
click.secho('✅ Environment prepared.', fg='green')
522+
523+
allow_origins_option = (
524+
f'--allow_origins={",".join(allow_origins)}' if allow_origins else ''
525+
)
526+
527+
# create Dockerfile
528+
click.secho('\nSTEP 2: Generating deployment files...', bold=True)
529+
click.echo(' - Creating Dockerfile...')
530+
host_option = '--host=0.0.0.0' if adk_version > '0.5.0' else ''
531+
dockerfile_content = _DOCKERFILE_TEMPLATE.format(
532+
gcp_project_id=project,
533+
gcp_region=region,
534+
app_name=app_name,
535+
port=port,
536+
command='web' if with_ui else 'api_server',
537+
install_agent_deps=install_agent_deps,
538+
service_option=_get_service_option_by_adk_version(
539+
adk_version,
540+
session_service_uri,
541+
artifact_service_uri,
542+
memory_service_uri,
543+
),
544+
trace_to_cloud_option='--trace_to_cloud' if trace_to_cloud else '',
545+
allow_origins_option=allow_origins_option,
546+
adk_version=adk_version,
547+
host_option=host_option,
548+
a2a_option='--a2a' if a2a else '',
549+
)
550+
dockerfile_path = os.path.join(temp_folder, 'Dockerfile')
551+
os.makedirs(temp_folder, exist_ok=True)
552+
with open(dockerfile_path, 'w', encoding='utf-8') as f:
553+
f.write(
554+
dockerfile_content,
555+
)
556+
click.secho(f'✅ Dockerfile generated: {dockerfile_path}', fg='green')
557+
558+
# Build and push the Docker image
559+
click.secho(
560+
'\nSTEP 3: Building container image with Cloud Build...', bold=True
561+
)
562+
click.echo(
563+
' (This may take a few minutes. Raw logs from gcloud will be shown'
564+
' below.)'
565+
)
566+
project = _resolve_project(project)
567+
image_name = f'gcr.io/{project}/{service_name}'
568+
subprocess.run(
569+
[
570+
'gcloud',
571+
'builds',
572+
'submit',
573+
'--tag',
574+
image_name,
575+
'--verbosity',
576+
log_level.lower() if log_level else verbosity,
577+
temp_folder,
578+
],
579+
check=True,
580+
)
581+
click.secho('✅ Container image built and pushed successfully.', fg='green')
582+
583+
# Create a Kubernetes deployment
584+
click.echo(' - Creating Kubernetes deployment.yaml...')
585+
deployment_yaml = f"""
586+
apiVersion: apps/v1
587+
kind: Deployment
588+
metadata:
589+
name: {service_name}
590+
labels:
591+
app.kubernetes.io/name: adk-agent
592+
app.kubernetes.io/version: {adk_version}
593+
app.kubernetes.io/instance: {service_name}
594+
app.kubernetes.io/managed-by: adk-cli
595+
spec:
596+
replicas: 1
597+
selector:
598+
matchLabels:
599+
app: {service_name}
600+
template:
601+
metadata:
602+
labels:
603+
app: {service_name}
604+
app.kubernetes.io/name: adk-agent
605+
app.kubernetes.io/version: {adk_version}
606+
app.kubernetes.io/instance: {service_name}
607+
app.kubernetes.io/managed-by: adk-cli
608+
spec:
609+
containers:
610+
- name: {service_name}
611+
image: {image_name}
612+
ports:
613+
- containerPort: {port}
614+
---
615+
apiVersion: v1
616+
kind: Service
617+
metadata:
618+
name: {service_name}
619+
spec:
620+
type: LoadBalancer
621+
selector:
622+
app: {service_name}
623+
ports:
624+
- port: 80
625+
targetPort: {port}
626+
"""
627+
deployment_yaml_path = os.path.join(temp_folder, 'deployment.yaml')
628+
with open(deployment_yaml_path, 'w', encoding='utf-8') as f:
629+
f.write(deployment_yaml)
630+
click.secho(
631+
f'✅ Kubernetes deployment manifest generated: {deployment_yaml_path}',
632+
fg='green',
633+
)
634+
635+
# Apply the deployment
636+
click.secho('\nSTEP 4: Applying deployment to GKE cluster...', bold=True)
637+
click.echo(' - Getting cluster credentials...')
638+
subprocess.run(
639+
[
640+
'gcloud',
641+
'container',
642+
'clusters',
643+
'get-credentials',
644+
cluster_name,
645+
'--region',
646+
region,
647+
'--project',
648+
project,
649+
],
650+
check=True,
651+
)
652+
click.echo(' - Applying Kubernetes manifest...')
653+
result = subprocess.run(
654+
['kubectl', 'apply', '-f', temp_folder],
655+
check=True,
656+
capture_output=True, # <-- Add this
657+
text=True, # <-- Add this
658+
)
659+
660+
# 2. Print the captured output line by line
661+
click.secho(
662+
' - The following resources were applied to the cluster:', fg='green'
663+
)
664+
for line in result.stdout.strip().split('\n'):
665+
click.echo(f' - {line}')
666+
667+
finally:
668+
click.secho('\nSTEP 5: Cleaning up...', bold=True)
669+
click.echo(f' - Removing temporary directory: {temp_folder}')
670+
shutil.rmtree(temp_folder)
671+
click.secho(
672+
'\n🎉 Deployment to GKE finished successfully!', fg='cyan', bold=True
673+
)

0 commit comments

Comments
 (0)