diff --git a/veadk/cli/services/vefaas/vefaas.py b/veadk/cli/services/vefaas/vefaas.py index dd2839a9..a1a62a27 100644 --- a/veadk/cli/services/vefaas/vefaas.py +++ b/veadk/cli/services/vefaas/vefaas.py @@ -59,36 +59,20 @@ def __init__(self, access_key: str, secret_key: str, region: str = "cn-beijing") self.template_id = "6874f3360bdbc40008ecf8c7" - def _create_function(self, function_name: str, path: str): - # 1. Read envs - envs = [] - for key, value in veadk.config.veadk_environments.items(): - envs.append(EnvForCreateFunctionInput(key=key, value=value)) - logger.info( - f"Fetch {len(envs)} environment variables.", - ) + def _upload_and_mount_code(self, function_id: str, path: str): + """Upload code to VeFaaS temp bucket and mount to function instance. - # 2. Create function - res = self.client.create_function( - volcenginesdkvefaas.CreateFunctionRequest( - command="./run.sh", - name=function_name, - description="Created by VeADK (Volcengine Agent Development Kit)", - tags=[TagForCreateFunctionInput(key="provider", value="veadk")], - runtime="native-python3.10/v1", - request_timeout=1800, - envs=envs, - ) - ) - function_id = res.id - - # 3. Get a temp bucket to store code + Args: + function_id (str): Target function ID. + path (str): Local project path. + """ + # Get zipped code data code_zip_data, code_zip_size, error = zip_and_encode_folder(path) logger.info( f"Zipped project size: {code_zip_size / 1024 / 1024:.2f} MB", ) - # 4. Upload code to VeFaaS temp bucket + # Upload code to VeFaaS temp bucket req = volcenginesdkvefaas.GetCodeUploadAddressRequest( function_id=function_id, content_length=code_zip_size ) @@ -103,7 +87,7 @@ def _create_function(self, function_name: str, path: str): error_message = f"Upload failed to {upload_url} with status code {response.status_code}: {response.text}" raise ValueError(error_message) - # 5. Mount the TOS bucket to function instance + # Mount the TOS bucket to function instance res = signed_request( ak=self.ak, sk=self.sk, @@ -111,6 +95,34 @@ def _create_function(self, function_name: str, path: str): body={"FunctionId": function_id}, ) + return res + + def _create_function(self, function_name: str, path: str): + # Read envs + envs = [] + for key, value in veadk.config.veadk_environments.items(): + envs.append(EnvForCreateFunctionInput(key=key, value=value)) + logger.info( + f"Fetch {len(envs)} environment variables.", + ) + + # Create function + res = self.client.create_function( + volcenginesdkvefaas.CreateFunctionRequest( + command="./run.sh", + name=function_name, + description="Created by VeADK (Volcengine Agent Development Kit)", + tags=[TagForCreateFunctionInput(key="provider", value="veadk")], + runtime="native-python3.10/v1", + request_timeout=1800, + envs=envs, + ) + ) + function_id = res.id + + # Upload and mount code using extracted method + self._upload_and_mount_code(function_id, path) + return function_name, function_id def _create_application( @@ -202,6 +214,77 @@ def _list_application(self): ) return response["Result"]["Items"] + def _update_function_code( + self, + application_name: str, # application name + path: str, + ) -> tuple[str, str, str]: + """Update existing application function code while preserving URL. + + Args: + application_name (str): Application name to update. + path (str): Local project path. + + Returns: + tuple[str, str, str]: URL, app_id, function_id + """ + # Naming check + if "_" in application_name: + raise ValueError("Function or Application name cannot contain '_'.") + + # Find existing application + app_id = self.find_app_id_by_name(application_name) + if not app_id: + raise ValueError( + f"Application '{application_name}' not found. Use deploy() for new applications." + ) + + # Get application status and extract function info + status, full_response = self._get_application_status(app_id) + if status == "deploy_fail": + raise ValueError( + f"Cannot update failed application. Current status: {status}" + ) + + # Extract function name from application config + cloud_resource = full_response["Result"]["CloudResource"] + cloud_resource = json.loads(cloud_resource) + function_name = cloud_resource["framework"]["function"]["Name"] + # existing_url = cloud_resource["framework"]["url"]["system_url"] + function_id = cloud_resource["framework"]["function"]["Id"] + if not function_id: + raise ValueError(f"Function '{function_name}' not found for update") + + logger.info( + f"Start to update VeFaaS function {function_name} with path {path}." + ) + + # Upload and mount code using extracted method + self._upload_and_mount_code(function_id, path) + + # Use update_function client method to apply changes + self.client.update_function( + volcenginesdkvefaas.UpdateFunctionRequest( + id=function_id, + request_timeout=1800, # Keep same timeout as deploy + ) + ) + + logger.info(f"Function updated successfully: {function_id}") + + logger.info(f"VeFaaS function {function_name} with ID {function_id} updated.") + + # Release the application to apply changes + url = self._release_application(app_id) + + logger.info(f"VeFaaS application {application_name} with ID {app_id} released.") + + logger.info( + f"VeFaaS application {application_name} with ID {app_id} updated on {url}." + ) + + return url, app_id, function_id + def get_application_details(self, app_id: str = None, app_name: str = None): if not app_id and not app_name: raise ValueError("app_id and app_name cannot be both empty.") diff --git a/veadk/cloud/cloud_agent_engine.py b/veadk/cloud/cloud_agent_engine.py index 7b85f8ab..17c48f8d 100644 --- a/veadk/cloud/cloud_agent_engine.py +++ b/veadk/cloud/cloud_agent_engine.py @@ -167,3 +167,39 @@ def remove(self, app_name: str): else: app_id = self._vefaas_service.find_app_id_by_name(app_name) self._vefaas_service.delete(app_id) + + def update_function_code( + self, + application_name: str, + path: str, + ) -> CloudApp: + """Update existing agent project code while keeping the same URL. + + Args: + application_name (str): Existing application name to update. + path (str): Local agent project path. + + Returns: + CloudApp: Updated cloud app with same endpoint. + """ + # convert `path` to absolute path + path = str(Path(path).resolve()) + self._prepare(path, application_name) + + try: + vefaas_application_url, app_id, function_id = ( + self._vefaas_service._update_function_code( + application_name=application_name, + path=path, + ) + ) + + return CloudApp( + vefaas_application_name=application_name, + vefaas_endpoint=vefaas_application_url, + vefaas_application_id=app_id, + ) + except Exception as e: + raise ValueError( + f"Failed to update agent project on Volcengine FaaS platform. Error: {e}" + )