* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "static_function_util.h"
#include "common/utils/struct_transfer.h"
namespace functionsystem::function_agent {
const std::string CONCURRENT_NUM_KEY = "ConcurrentNum";
const std::string CALL_TIMEOUT_KEY = "call_timeout";
const std::string CUSTOM_CONTAINER_RUNTIME_TYPE = "custom image";
const std::string HTTP_RUNTIME_TYPE = "http";
const std::string DEFAULT_DIRECTORY_LIMIT = "512";
const uint32_t INCREMENT_TIMEOUT = 5;
const std::string STORAGE_TYPE_KEY = "storage_type";
const std::string GRACEFUL_SHUTDOWN_TIME_KEY = "GRACEFUL_SHUTDOWN_TIME";
const uint32_t MAX_SHUTDOWN_TIMEOUT = 900;
const std::string INSTANCE_TYPE_NOTE_KEY = "INSTANCE_TYPE_NOTE";
const std::string INSTANCE_LABEL_NOTE_KEY = "INSTANCE_LABEL_NOTE";
const std::string FUNCTION_KEY_NOTE_KEY = "FUNCTION_KEY_NOTE";
const std::string FUNCTION_KEY_SPLIT = "/";
const std::string RESOURCE_SPEC_NOTE_KEY = "RESOURCE_SPEC_NOTE";
const std::string FUNCTION_SIGNATURE_KEY = "FUNCTION_SIGNATURE";
const size_t INSTANCE_ARGS_SIZE = 6;
const size_t LIBRUNTIME_ARGS_INDEX = 0;
const size_t FUNCTION_META_ARGS_INDEX = 1;
const size_t CREATE_PARAM_ARGS_INDEX = 2;
const size_t SCHEDLER_ARGS_INDEX = 3;
const size_t CREATE_EVENT_ARGS_INDEX = 4;
const size_t GLOBAL_CONFIG_ARGS_INDEX = 5;
const size_t PREFIX_EMPTY_SIZE = 16;
const uint16_t HTTP_FUNC_PORT = 8000;
const std::string HTTP_CALL_ROUTE = "invoke";
FunctionLanguageInfo GetLanguageInfo(const FunctionMeta &funcMeta)
{
std::string runtime = funcMeta.funcMetaData.runtime;
std::string executorFormat = "default/0-system-faasExecutor{}/$latest";
if (runtime.find("python3.6") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Python3.6"), resources::LanguageType::Python };
}
if (runtime.find("python3.8") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Python3.8"), resources::LanguageType::Python };
}
if (runtime.find("python3.9") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Python3.9"), resources::LanguageType::Python };
}
if (runtime.find("python3.11") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Python3.11"), resources::LanguageType::Python };
}
if (runtime.find("go") != std::string::npos || runtime.find("http") != std::string::npos
|| runtime.find("custom image") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Go1.x"), resources::LanguageType::Golang };
}
if (runtime.find("java8") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Java8"), resources::LanguageType::Java };
}
if (runtime.find("java11") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Java11"), resources::LanguageType::Java };
}
if (runtime.find("java17") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Java17"), resources::LanguageType::Java };
}
if (runtime.find("java21") != std::string::npos) {
return FunctionLanguageInfo{ fmt::format(executorFormat, "Java21"), resources::LanguageType::Java };
}
return {};
}
std::string LayerToDelegateDownloadInfo(const std::vector<Layer> &layers)
{
nlohmann::json info;
for (const auto &layer : layers) {
nlohmann::json json;
json["appId"] = layer.appID;
json["bucketId"] = layer.bucketID;
json["objectId"] = layer.objectID;
json["hostName"] = layer.hostName;
json["securityToken"] = layer.securityToken;
json["temporaryAccessKey"] = layer.temporaryAccessKey;
json["temporarySecretKey"] = layer.temporarySecretKey;
json["sha256"] = layer.sha256;
json["sha512"] = layer.sha512;
json["storage_type"] = layer.storageType;
json["code_path"] = layer.codePath;
info.push_back(json);
}
return info.dump();
}
void BuildMetaConfig(resources::MetaConfig &config, const FunctionLanguageInfo &info, const FunctionMeta &meta)
{
int recycleTime = 2;
int maxTaskInstanceNum = -1;
int maxConcurrencyCreateNum = 100;
uint32_t threadPoolSize = 0;
uint32_t localThreadPoolSize = 0;
config.set_jobid("");
config.set_recycletime(recycleTime);
config.set_maxtaskinstancenum(maxTaskInstanceNum);
config.set_maxconcurrencycreatenum(maxConcurrencyCreateNum);
config.set_threadpoolsize(threadPoolSize);
config.set_localthreadpoolsize(localThreadPoolSize);
config.set_enablemetrics(true);
config.add_codepaths(meta.extendedMetaData.initializer.handler);
config.add_codepaths(meta.funcMetaData.staticHandler);
auto funcId = config.add_functionids();
funcId->set_language(info.languageType);
funcId->set_functionid(info.functionId);
}
void BuildCreateOption(const std::shared_ptr<messages::ScheduleRequest> &request, const FunctionMeta &meta,
const StaticFunctionConfig &config)
{
const auto createOption = request->mutable_instance()->mutable_createoptions();
(*createOption)[CONCURRENT_NUM_KEY] = std::to_string(meta.instanceMetaData.concurrentNum);
(*createOption)[DELEGATE_DIRECTORY_INFO] = config.delegateDirectoryInfo;
if (meta.instanceMetaData.diskLimit == 0) {
(*createOption)[DELEGATE_DIRECTORY_QUOTA] = DEFAULT_DIRECTORY_LIMIT;
} else {
(*createOption)[DELEGATE_DIRECTORY_QUOTA] = std::to_string(meta.instanceMetaData.diskLimit);
}
(*createOption)[INIT_CALL_TIMEOUT] = std::to_string(meta.extendedMetaData.initializer.timeout + INCREMENT_TIMEOUT);
(*createOption)[CALL_TIMEOUT_KEY] = std::to_string(meta.funcMetaData.timeout);
if (meta.extendedMetaData.customGracefulShutdown.maxShutdownTimeout > 0) {
(*createOption)[GRACEFUL_SHUTDOWN_TIME_KEY] =
std::to_string(meta.extendedMetaData.customGracefulShutdown.maxShutdownTimeout);
request->mutable_instance()->set_gracefulshutdowntime(
meta.extendedMetaData.customGracefulShutdown.maxShutdownTimeout);
} else {
(*createOption)[GRACEFUL_SHUTDOWN_TIME_KEY] = std::to_string(MAX_SHUTDOWN_TIMEOUT);
request->mutable_instance()->set_gracefulshutdowntime(MAX_SHUTDOWN_TIMEOUT);
}
(*createOption)[FUNCTION_KEY_NOTE_KEY] = meta.funcMetaData.tenantId + FUNCTION_KEY_SPLIT + meta.funcMetaData.name
+ FUNCTION_KEY_SPLIT + meta.funcMetaData.version;
nlohmann::json resourceNoteJson;
if (meta.resources.resources().contains(CPU_RESOURCE_NAME)) {
resourceNoteJson["cpu"] =
static_cast<uint64_t>(meta.resources.resources().at(CPU_RESOURCE_NAME).scalar().value());
}
if (meta.resources.resources().contains(MEMORY_RESOURCE_NAME)) {
resourceNoteJson["memory"] =
static_cast<uint64_t>(meta.resources.resources().at(MEMORY_RESOURCE_NAME).scalar().value());
}
resourceNoteJson["invokeLabels"] = config.invokeLabels;
(*createOption)[RESOURCE_SPEC_NOTE_KEY] = resourceNoteJson.dump();
(*createOption)[INSTANCE_TYPE_NOTE_KEY] = config.instanceTypeNote;
(*createOption)[INSTANCE_LABEL_NOTE_KEY] = config.invokeLabels;
(*createOption)[FUNCTION_SIGNATURE_KEY] = config.functionSignature;
(*createOption)[TENANT_ID] = meta.funcMetaData.tenantId;
(*createOption)["lifecycle"] = "detached";
nlohmann::json containerJson;
containerJson[STORAGE_TYPE_KEY] = meta.codeMetaData.storageType;
containerJson["sha512"] = meta.funcMetaData.codeSha512;
containerJson["appId"] = meta.codeMetaData.appId;
containerJson["bucketId"] = meta.codeMetaData.bucketID;
containerJson["objectId"] = meta.codeMetaData.objectID;
if (meta.funcMetaData.runtime != CUSTOM_CONTAINER_RUNTIME_TYPE) {
(*createOption)[DELEGATE_DOWNLOAD] = containerJson.dump();
}
if (!meta.codeMetaData.layers.empty()) {
(*createOption)[DELEGATE_DOWNLOAD] = LayerToDelegateDownloadInfo(meta.codeMetaData.layers);
}
const nlohmann::json encryptJson = { { "secretKey", meta.extendedMetaData.userAgency.secretKey },
{ "accessKey", meta.extendedMetaData.userAgency.accessKey },
{ "authToken", meta.extendedMetaData.userAgency.token },
{ "securityAk", meta.extendedMetaData.userAgency.securityAk },
{ "securitySk", meta.extendedMetaData.userAgency.securitySk },
{ "securityToken", meta.extendedMetaData.userAgency.securityToken },
{ "environment", meta.envMetaData.envInfo },
{ "encrypted_user_data", meta.envMetaData.encryptedUserData },
{ "envKey", meta.envMetaData.envKey },
{ "cryptoAlgorithm", meta.envMetaData.cryptoAlgorithm } };
(*createOption)[DELEGATE_DECRYPT] = encryptJson.dump();
}
void BuildCreateArgs(const std::shared_ptr<messages::ScheduleRequest> &request, const FunctionMeta &meta,
const StaticFunctionConfig &config)
{
auto args = request->mutable_instance()->mutable_args();
std::string prefixStr(PREFIX_EMPTY_SIZE, '\0');
for (size_t i = 0; i < INSTANCE_ARGS_SIZE; i++) {
::common::Arg arg{};
arg.set_type(::common::Arg_ArgType_VALUE);
args->Add(std::move(arg));
}
::resources::MetaData metaData;
metaData.set_invoketype(resources::InvokeType::CreateInstance);
auto functionMeta = metaData.mutable_functionmeta();
functionMeta->set_apitype(resources::ApiType::Faas);
auto functionInfo = GetLanguageInfo(meta);
functionMeta->set_functionid(functionInfo.functionId);
auto metaConfig = metaData.mutable_config();
BuildMetaConfig(*metaConfig, functionInfo, meta);
args->at(LIBRUNTIME_ARGS_INDEX).set_value(metaData.SerializeAsString());
args->at(FUNCTION_META_ARGS_INDEX).set_value(prefixStr + meta.rawJsonStr);
nlohmann::json createParamsData = {};
if (!config.invokeLabels.empty()) {
createParamsData["instanceLabel"] = config.invokeLabels;
}
if (meta.funcMetaData.runtime == HTTP_RUNTIME_TYPE || meta.funcMetaData.runtime == CUSTOM_CONTAINER_RUNTIME_TYPE) {
createParamsData["port"] = HTTP_FUNC_PORT;
createParamsData["callRoute"] = HTTP_CALL_ROUTE;
} else {
if (!meta.extendedMetaData.initializer.handler.empty()) {
createParamsData["userInitEntry"] = meta.extendedMetaData.initializer.handler;
}
createParamsData["userCallEntry"] = meta.funcMetaData.staticHandler;
}
args->at(CREATE_PARAM_ARGS_INDEX).set_value(prefixStr + createParamsData.dump());
args->at(SCHEDLER_ARGS_INDEX).set_value(prefixStr + R"({"schedulerFuncKey":"","schedulerIDList":[]})");
args->at(CREATE_EVENT_ARGS_INDEX).set_value(prefixStr + "{}");
if (meta.funcMetaData.runtime != CUSTOM_CONTAINER_RUNTIME_TYPE) {
args->RemoveLast();
} else {
args->at(GLOBAL_CONFIG_ARGS_INDEX).set_value(prefixStr + "{}");
}
}
void BuildScheduleRequest(const std::shared_ptr<messages::ScheduleRequest> &request, const FunctionMeta &meta,
const StaticFunctionConfig &config)
{
request->set_initrequest("");
request->mutable_instance()->mutable_resources()->CopyFrom(meta.resources);
BuildCreateOption(request, meta, config);
const auto &createOptions = request->instance().createoptions();
if (const auto iter(createOptions.find(INIT_CALL_TIMEOUT)); iter != createOptions.end()) {
uint32_t timeout = 0;
std::stringstream ss(iter->second);
ss >> timeout;
request->mutable_instance()->mutable_scheduleoption()->set_initcalltimeout(timeout);
}
BuildCreateArgs(request, meta, config);
}
}