@@ -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 ('\n STEP 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+ '\n STEP 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 ('\n STEP 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 ('\n STEP 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