feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

36
scripts/build_fe.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="${SCRIPT_DIR}/.."
FRONTEND_DIR="${ROOT_DIR}/frontend"
set -ex
source "${SCRIPT_DIR}/setup_fe.sh"
pushd "${FRONTEND_DIR}"
echo "正在构建前端..."
BUILD_BRANCH=opencoze-local rush rebuild -o @coze-studio/app --verbose
popd
# 复制构建产物到后端静态目录
echo -e "${YELLOW}正在复制构建产物到后端静态目录...${NC}"
BACKEND_STATIC_DIR="${SCRIPT_DIR}/../backend/static"
BIN_STATIC_DIR="${SCRIPT_DIR}/../bin/resources/static"
FRONTEND_DIST_DIR="${FRONTEND_DIR}/apps/coze-studio/dist"
rm -rf "${BACKEND_STATIC_DIR}"
rm -rf "${BIN_STATIC_DIR}"
mkdir -p "${BACKEND_STATIC_DIR}"
mkdir -p "${BIN_STATIC_DIR}"
# 清空目标目录并复制新的构建产物
rm -rf "${BACKEND_STATIC_DIR}"/*
cp -r "${FRONTEND_DIST_DIR}"/* "${BACKEND_STATIC_DIR}/"
cp -r "${FRONTEND_DIST_DIR}"/* "${BIN_STATIC_DIR}/"
echo -e "${GREEN}构建产物复制完成!${NC}"
echo -e "${GREEN}前端文件已复制到: \n ${BACKEND_STATIC_DIR} \n ${BIN_STATIC_DIR} ${NC}"

View File

@@ -0,0 +1,60 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCKER_DIR="$(cd "$SCRIPT_DIR/../../docker" && pwd)"
BACKEND_DIR="$(cd "$SCRIPT_DIR/../../backend" && pwd)"
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
NC='\033[0m'
cd "$DOCKER_DIR/atlas"
source "$DOCKER_DIR/.env"
echo "ATLAS_URL: $ATLAS_URL"
# Check if ATLAS_URL is set
if [ -z "$ATLAS_URL" ]; then
echo -e "${RED}Error: ATLAS_URL is not set. Please set the ATLAS_URL environment variable.${NC}"
exit 1
fi
# check if atlas is installed
OS=$(uname -s)
if command -v atlas &>/dev/null; then
echo -e "${GREEN}Atlas is installed.${NC}"
else
if [ "$OS" = "Darwin" ]; then
# macOS prompt
echo -e "${RED}Atlas is not installed. Please execute the following command to install:${NC}"
echo -e "${RED}brew install ariga/tap/atlas${NC}"
exit 1
else
# Linux prompt
echo -e "${RED}Atlas is not installed. Please execute the following command to install:${NC}"
echo -e "${RED}curl -sSf https://atlasgo.sh | sh -s -- --community${NC}"
exit 1
fi
fi
cd "$DOCKER_DIR/atlas"
atlas schema apply -u $ATLAS_URL --to file://opencoze_latest_schema.hcl --exclude "atlas_schema_revisions,table_*" --auto-approve
echo -e "${GREEN}✅ apply mysql schema successfully${NC}"
# if [ "$OS" = "Darwin" ]; then
# atlas schema apply -u $ATLAS_URL --to file://opencoze_latest_schema.hcl --auto-approve --exclude "table_*"
# echo -e "${GREEN}✅ apply mysql schema successfully${NC}"
# elif [ "$OS" = "Linux" ]; then
# atlas migrate apply \
# --url "$ATLAS_URL" \
# --dir "file://migrations" \
# --revisions-schema opencoze \
# --baseline "20250703095335"
# echo -e "${GREEN}✅ migrate mysql successfully${NC}"
# elif [ "$OS" = "Windows" ]; then
# echo -e "${RED}Windows is not supported. Please install Atlas manually.${NC}"
# exit 1
# fi

View File

@@ -0,0 +1,45 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="$(cd "$SCRIPT_DIR/../../" && pwd)"
ATLAS_DIR="$BASE_DIR/docker/atlas"
DOCKER_DIR="$(cd "$SCRIPT_DIR/../../docker" && pwd)"
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
NC='\033[0m'
# Check if ATLAS_URL is set
if [ -z "$ATLAS_URL" ]; then
echo -e "${RED}Error: ATLAS_URL is not set. Please set the ATLAS_URL environment variable.${NC}"
exit 1
fi
source "$DOCKER_DIR/.env"
echo "ATLAS_URL: $ATLAS_URL"
# check if atlas is installed
OS=$(uname -s)
if command -v atlas &>/dev/null; then
echo -e "${GREEN}Atlas is installed.${NC}"
else
if [ "$OS" = "Darwin" ]; then
# macOS prompt
echo -e "${RED}Atlas is not installed. Please execute the following command to install:${NC}"
echo -e "${RED}brew install ariga/tap/atlas${NC}"
exit 1
else
# Linux prompt
echo -e "${RED}Atlas is not installed. Please execute the following command to install:${NC}"
echo -e "${RED}curl -sSf https://atlasgo.sh | sh -s -- --community${NC}"
exit 1
fi
fi
cd $ATLAS_DIR
atlas migrate diff update --env local --to $ATLAS_URL
atlas schema inspect -u $ATLAS_URL --exclude "atlas_schema_revisions,table_*" >opencoze_latest_schema.hcl

81
scripts/setup/python.sh Executable file
View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
SETUP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_DIR="$(dirname "$SETUP_DIR")"
BASE_DIR="$(dirname "$SCRIPT_DIR")"
BACKEND_DIR="$BASE_DIR/backend"
BIN_DIR="$BASE_DIR/bin"
VENV_DIR="$BIN_DIR/.venv"
echo "Checking for Python virtual environment under $BIN_DIR"
if [ ! -f "$VENV_DIR/bin/activate" ]; then
echo "Virtual environment not found or incomplete. Re-creating..."
rm -rf "$VENV_DIR"
python3 -m venv "$VENV_DIR"
if [ $? -ne 0 ]; then
echo "Failed to create virtual environment - aborting startup"
exit 1
fi
echo "Virtual environment created successfully!"
else
echo "Virtual environment already exists. Skipping creation."
fi
echo "Installing required Python packages"
source "$VENV_DIR/bin/activate"
pip install --upgrade pip
# If you want to use other third-party libraries, you can install them here.
pip install urllib3==1.26.16
REQUESTS_ASYNC_REPO_URL="https://gitcode.com/gh_mirrors/re/requests-async.git"
REQUESTS_ASYNC_DIR="$BIN_DIR/requests-async"
if [ ! -d "$REQUESTS_ASYNC_DIR/.git" ]; then
echo "Cloning requests-async repository..."
rm -rf "$REQUESTS_ASYNC_DIR"
git clone "$REQUESTS_ASYNC_REPO_URL" "$REQUESTS_ASYNC_DIR"
if [ $? -ne 0 ]; then
echo "Failed to clone requests-async repository - aborting startup"
deactivate
exit 1
fi
else
echo "requests-async repository already exists."
fi
pip install pillow==11.2.1 pdfplumber==0.11.7 python-docx==1.2.0 numpy==2.3.1 "$REQUESTS_ASYNC_DIR"
if [ $? -ne 0 ]; then
echo "Failed to install Python packages - aborting startup"
deactivate
exit 1
fi
echo "Python packages installed successfully!"
deactivate
PARSER_SCRIPT_ROOT="$BACKEND_DIR/infra/impl/document/parser/builtin"
PDF_PARSER="$PARSER_SCRIPT_ROOT/parse_pdf.py"
DOCX_PARSER="$PARSER_SCRIPT_ROOT/parse_docx.py"
if [ -f "$PDF_PARSER" ]; then
cp "$PDF_PARSER" "$BIN_DIR/parse_pdf.py"
else
echo "$PDF_PARSER file not found"
exit 1
fi
if [ -f "$DOCX_PARSER" ]; then
cp "$DOCX_PARSER" "$BIN_DIR/parse_docx.py"
else
echo "$DOCX_PARSER file not found"
exit 1
fi

71
scripts/setup/server.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="$(cd "$SCRIPT_DIR/../../" && pwd)"
BACKEND_DIR="$BASE_DIR/backend"
BIN_DIR="$BASE_DIR/bin"
CONFIG_DIR="$BIN_DIR/resources/conf"
RESOURCES_DIR="$BIN_DIR/resources/"
DOCKER_DIR="$BASE_DIR/docker"
echo "🧹 Checking for goimports availability..."
if command -v goimports >/dev/null 2>&1; then
echo "🧹 Formatting Go files with goimports..."
find "$BACKEND_DIR" \
-path "$BACKEND_DIR/api/model" -prune -o \
-path "$BACKEND_DIR/api/router" -prune -o \
-path "*/dal/query*" -prune -o \
-path "*/mock/*" -prune -o \
-path "*_mock.go" -prune -o \
-path "*/dal/model*" -prune -o \
-name "*.go" -exec goimports -w -local "github.com/coze-dev/coze-studio" {} \;
else
echo "⚠️ goimports not found, skipping Go file formatting."
fi
echo "🛠 Building Go project..."
rm -rf "$BIN_DIR/opencoze"
cd $BACKEND_DIR &&
go build -ldflags="-s -w" -o "$BIN_DIR/opencoze" main.go
# 添加构建失败检查
if [ $? -ne 0 ]; then
echo "❌ Go build failed - aborting startup"
exit 1
fi
echo "✅ Build completed successfully!"
echo "📑 Copying environment file..."
if [ -f "$DOCKER_DIR/.env" ]; then
cp "$DOCKER_DIR/.env" "$BIN_DIR/.env"
else
echo "❌ .env file not found in $DOCKER_DIR"
exit 1
fi
if [ -f "$DOCKER_DIR/cert.pem" ]; then
cp "$DOCKER_DIR/cert.pem" "$BIN_DIR/cert.pem"
fi
if [ -f "$DOCKER_DIR/key.pem" ]; then
cp "$DOCKER_DIR/key.pem" "$BIN_DIR/key.pem"
fi
echo "📑 Cleaning configuration files..."
rm -rf "$CONFIG_DIR"
mkdir -p "$CONFIG_DIR"
echo "📑 Copying plugin configuration files..."
cp -r "$BACKEND_DIR/conf" "$RESOURCES_DIR"
cp -r "$BACKEND_DIR/static" "$RESOURCES_DIR"
for arg in "$@"; do
if [[ "$arg" == "-start" ]]; then
echo "🚀 Starting Go service..."
cd $BIN_DIR && ./opencoze "$@"
exit 0
fi
done

53
scripts/setup_fe.sh Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FRONTEND_DIR="${1:-${SCRIPT_DIR}/../frontend}"
set -ex
# 设置颜色变量
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
pushd "${FRONTEND_DIR}"
echo "正在进入前端目录: ${FRONTEND_DIR}"
# 检查 Node.js 是否安装
echo -e "正在检查 Node.js 是否已安装..."
if ! command -v node &> /dev/null; then
echo -e "${RED}错误: 未检测到 Node.js${NC}"
echo -e "${YELLOW}请安装 Node.js 后再继续。推荐使用 nvm 进行安装:${NC}"
echo -e " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash"
echo -e " nvm install --lts"
exit 1
else
NODE_VERSION=$(node -v)
echo -e "${GREEN}Node.js 已安装: ${NODE_VERSION}${NC}"
fi
# 检查 Rush 是否安装
echo -e "正在检查 Rush 是否已安装..."
if ! command -v rush &> /dev/null; then
echo -e "${YELLOW}未检测到 Rush正在为您安装...${NC}"
npm i -g @microsoft/rush
else
RUSH_VERSION=$(rush version)
echo -e "${GREEN}Rush 已安装: ${RUSH_VERSION}${NC}"
fi
echo -e "${GREEN}环境检查完成!${NC}"
echo -e "${YELLOW}开始安装依赖...${NC}"
rush update
echo -e "${GREEN}依赖安装完成!${NC}"
# echo -e "${NC}"
# echo -e "${GREEN}构建完成!${NC}"
popd

27
scripts/start_fe.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FRONTEND_DIR="${1:-${SCRIPT_DIR}/../frontend}"
pushd "${FRONTEND_DIR}/apps/coze-studio"
echo -e "Entering frontend build output directory: ${FRONTEND_DIR}/apps/coze-studio"
# Check if dist directory exists and is not empty
if [ ! -d "dist" ] || [ -z "$(ls -A dist 2>/dev/null)" ]; then
echo -e "dist directory does not exist or is empty, initializing environment..."
bash ${SCRIPT_DIR}/setup_fe.sh
else
echo "dist directory exists and is not empty, skipping environment initialization"
fi
popd
echo -e "Starting backend service..."
make web
echo -e "Starting frontend service..."
pushd "${FRONTEND_DIR}/apps/coze-studio"
WEB_SERVER_PORT=8888 npm run dev
popd

View File

@@ -0,0 +1,43 @@
export VE_AK=""
export VE_SK=""
export VE_REGION=""
export VE_PROJECT_NAME="default"
export VE_VPC_NAME="opencoze-vpc"
# MySQL
export VE_MYSQL_DB_NAME="opencoze"
export VE_MYSQL_USER_NAME="coze"
export VE_MYSQL_USER_PASSWORD="Opencoze123"
export VE_MYSQL_PORT="3306"
export VE_MYSQL_DB_ENGINE_VERSION="MySQL_8_0"
export VE_MYSQL_INSTANCE_CLASS="vedb.mysql.g2.medium"
# The intermediate variable is empty by default; no input is required.
# ZoneID
export VE_ZONE_ID=""
# network
export VE_VPC_ID=""
export VE_SUBNET_ID=""
export VE_ACL_ID=""
export VE_SAFE_GROUP_ID=""
# mysql
export VE_MYSQL_INSTANCE_ID=""
export VE_MYSQL_WHITELIST_ID=""
# redis
export VE_REDIS_INSTANCE_ID=""
export VE_REDIS_ALLOWLIST_ID=""
# rocketmq
export VE_ROCKETMQ_ALLOWLIST_ID=""
export VE_ROCKETMQ_INSTANCE_ID=""
# elasticsearch
export VE_ES_INSTANCE_ID=""
# ECS
export VE_ECS_INSTANCE_ID=""

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"errors"
"fmt"
"log"
"os"
"time"
"github.com/volcengine/volcengine-go-sdk/service/escloud"
"github.com/volcengine/volcengine-go-sdk/volcengine"
)
func CreateESInstance(vpcID, subnetID, zoneID, ts string) (string, error) {
if os.Getenv("VE_ES_INSTANCE_ID") != "" {
return os.Getenv("VE_ES_INSTANCE_ID"), nil
}
svc := escloud.New(sess)
reqNetworkSpecs := &escloud.NetworkSpecForCreateInstanceInOneStepInput{
Bandwidth: volcengine.Int32(10),
IsOpen: volcengine.Bool(true),
SpecName: volcengine.String("es.eip.bgp_fixed_bandwidth"),
Type: volcengine.String("Kibana"),
}
reqExtraPerformance := &escloud.ExtraPerformanceForCreateInstanceInOneStepInput{
Throughput: volcengine.Int32(0),
}
reqNodeSpecsAssigns := &escloud.NodeSpecsAssignForCreateInstanceInOneStepInput{
ExtraPerformance: reqExtraPerformance,
Number: volcengine.Int32(1),
ResourceSpecName: volcengine.String("kibana.x2.small"),
StorageSize: volcengine.Int32(0),
Type: volcengine.String("Kibana"),
}
reqNodeSpecsAssigns1 := &escloud.NodeSpecsAssignForCreateInstanceInOneStepInput{
ExtraPerformance: reqExtraPerformance,
Number: volcengine.Int32(1),
ResourceSpecName: volcengine.String("es.x2.medium"),
StorageSize: volcengine.Int32(30),
StorageSpecName: volcengine.String("es.volume.essd.pl0"),
Type: volcengine.String("Hot"),
}
reqSubnet := &escloud.SubnetForCreateInstanceInOneStepInput{
SubnetId: volcengine.String(subnetID),
}
reqVPC := &escloud.VPCForCreateInstanceInOneStepInput{
VpcId: volcengine.String(vpcID),
}
name := "opencoze-es-" + ts
reqInstanceConfiguration := &escloud.InstanceConfigurationForCreateInstanceInOneStepInput{
AdminPassword: volcengine.String(password),
ChargeType: volcengine.String("PostPaid"),
EnableHttps: volcengine.Bool(false),
EnablePureMaster: volcengine.Bool(false),
InstanceName: volcengine.String(name),
NetworkSpecs: []*escloud.NetworkSpecForCreateInstanceInOneStepInput{reqNetworkSpecs},
NodeSpecsAssigns: []*escloud.NodeSpecsAssignForCreateInstanceInOneStepInput{reqNodeSpecsAssigns, reqNodeSpecsAssigns1},
ProjectName: volcengine.String(projectName),
RegionId: volcengine.String(region),
Subnet: reqSubnet,
VPC: reqVPC,
Version: volcengine.String("V7_10"),
ZoneId: volcengine.String(zoneID),
DeletionProtection: volcengine.Bool(false),
}
reqTags := &escloud.TagForCreateInstanceInOneStepInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
createInstanceInOneStepInput := &escloud.CreateInstanceInOneStepInput{
InstanceConfiguration: reqInstanceConfiguration,
Tags: []*escloud.TagForCreateInstanceInOneStepInput{reqTags},
}
resp, err := svc.CreateInstanceInOneStep(createInstanceInOneStepInput)
if err != nil {
return "", err
}
if resp.InstanceId == nil {
return "", errors.New("InstanceId is empty")
}
return *resp.InstanceId, nil
}
func GetESConnectAddress(instanceID string) (string, error) {
svc := escloud.New(sess)
describeInstanceInput := &escloud.DescribeInstanceInput{
InstanceId: volcengine.String(instanceID),
}
for {
resp, err := svc.DescribeInstance(describeInstanceInput)
if resp.InstanceInfo != nil && resp.InstanceInfo.Status != nil && *resp.InstanceInfo.Status != "Running" {
fmt.Printf("[Elasticsearch] instance(%s) is %s, waiting for it to become ready... \n", instanceID, *resp.InstanceInfo.Status)
time.Sleep(retryTime)
continue
}
if err != nil {
log.Printf("[Elasticsearch] will retry get es instance = %s failed, err= %s\n", instanceID, err.Error())
time.Sleep(retryTime)
continue
}
if resp.InstanceInfo.ESPrivateEndpoint == nil {
log.Printf("[Elasticsearch] DescribeInstanceDetail resp.InstanceInfo.ESPrivateEndpoint is empty, will retry")
time.Sleep(retryTime)
continue
}
return *resp.InstanceInfo.ESPrivateEndpoint, nil
}
}

66
scripts/volcengine/env.go Normal file
View File

@@ -0,0 +1,66 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"fmt"
"os"
"strings"
)
func updateEnvVarInFile(key, newValue string) {
oldValue := os.Getenv(key)
if len(oldValue) > 0 {
return
}
filePath := ".env"
input, err := os.ReadFile(filePath)
if err != nil {
panic(fmt.Errorf("can not read file %s: %w", ".env", err))
}
lines := strings.Split(string(input), "\n")
found := false
for i, line := range lines {
trimmedLine := strings.TrimSpace(line)
var prefix string
if strings.HasPrefix(trimmedLine, "export ") {
prefix = "export "
trimmedLine = strings.TrimPrefix(trimmedLine, "export ")
}
parts := strings.SplitN(trimmedLine, "=", 2)
if len(parts) == 2 && parts[0] == key {
lines[i] = fmt.Sprintf(`%s%s="%s"`, prefix, key, newValue)
found = true
break
}
}
if !found {
panic(fmt.Errorf("can not find var %s in file %s", key, filePath))
}
output := strings.Join(lines, "\n")
err = os.WriteFile(filePath, []byte(output), 0o644)
if err != nil {
panic(fmt.Errorf("can not write file %s: %w", filePath, err))
}
}

77
scripts/volcengine/esc.go Normal file
View File

@@ -0,0 +1,77 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"errors"
"os"
"github.com/volcengine/volcengine-go-sdk/service/ecs"
"github.com/volcengine/volcengine-go-sdk/volcengine"
)
func CreateECSInstance(zoneID, sgID, subnetID, ts string) (string, error) {
if os.Getenv("VE_ECS_INSTANCE_ID") != "" {
return os.Getenv("VE_ECS_INSTANCE_ID"), nil
}
svc := ecs.New(sess)
reqEipAddress := &ecs.EipAddressForRunInstancesInput{
BandwidthMbps: volcengine.Int32(10),
ChargeType: volcengine.String("PayByBandwidth"),
ISP: volcengine.String("BGP"),
ReleaseWithInstance: volcengine.Bool(true),
}
reqNetworkInterfaces := &ecs.NetworkInterfaceForRunInstancesInput{
SecurityGroupIds: volcengine.StringSlice([]string{sgID}),
SubnetId: volcengine.String(subnetID),
}
reqTags := &ecs.TagForRunInstancesInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
reqVolumes := &ecs.VolumeForRunInstancesInput{
Size: volcengine.Int32(100),
}
name := "opencoze-ecs-" + ts
runInstancesInput := &ecs.RunInstancesInput{
DryRun: volcengine.Bool(false),
EipAddress: reqEipAddress,
Hostname: volcengine.String("opencoze-server"),
ImageId: volcengine.String("image-yd6lmt386vgqef1r7xpu"),
InstanceChargeType: volcengine.String("PostPaid"),
InstanceName: volcengine.String(name),
InstanceTypeId: volcengine.String("ecs.c4il.4xlarge"),
NetworkInterfaces: []*ecs.NetworkInterfaceForRunInstancesInput{reqNetworkInterfaces},
Password: volcengine.String(password),
ProjectName: volcengine.String(projectName),
Tags: []*ecs.TagForRunInstancesInput{reqTags},
Volumes: []*ecs.VolumeForRunInstancesInput{reqVolumes},
ZoneId: volcengine.String(zoneID),
}
resp, err := svc.RunInstances(runInstancesInput)
if err != nil {
return "", err
}
if len(resp.InstanceIds) == 0 {
return "", errors.New("[ECS] InstanceIds is empty")
}
return *resp.InstanceIds[0], nil
}

15
scripts/volcengine/go.mod Normal file
View File

@@ -0,0 +1,15 @@
module github.com/coze-dev/coze-studio/scripts/volcengine
go 1.24.1
require (
github.com/joho/godotenv v1.5.1
github.com/volcengine/volcengine-go-sdk v1.1.18
)
require (
github.com/google/uuid v1.3.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/volcengine/volc-sdk-golang v1.0.23 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
)

95
scripts/volcengine/go.sum Normal file
View File

@@ -0,0 +1,95 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/volcengine/volc-sdk-golang v1.0.23 h1:anOslb2Qp6ywnsbyq9jqR0ljuO63kg9PY+4OehIk5R8=
github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU=
github.com/volcengine/volcengine-go-sdk v1.1.18 h1:tVcp1m6R8fgwZFb/MaltrpZY7b2+vYNbmO1MHlDSXIs=
github.com/volcengine/volcengine-go-sdk v1.1.18/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

386
scripts/volcengine/main.go Normal file
View File

@@ -0,0 +1,386 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/joho/godotenv"
"github.com/volcengine/volcengine-go-sdk/volcengine"
"github.com/volcengine/volcengine-go-sdk/volcengine/credentials"
"github.com/volcengine/volcengine-go-sdk/volcengine/session"
)
var (
ak string
sk string
region string
sess *session.Session
projectName string
vpcName string
)
const (
password = "Opencoze123"
)
// MySQL
var (
mysqlDBName string
mysqlUserName string
mysqlUserPassword string
mysqlPort string
mysqlDBEngineVersion string
mysqlInstanceClass string
)
const retryTime = 10 * time.Second
func main() {
if err := loadEnv(); err != nil {
panic("loadEnv failed, err=" + err.Error())
}
if err := initServer(); err != nil {
panic("initServer failed, err=" + err.Error())
}
ts := time.Now().Format("0102-150405")
// -----------------------------------------------------------------
// ------------------------- Setup Network -------------------------
// -----------------------------------------------------------------
// 1. get zoneID
zoneID, err := GetChosenZoneID()
if err != nil {
panic("[Zone] GetZoneID failed, err=" + err.Error())
}
fmt.Println("[Zone] Chosen ZoneID:", zoneID)
updateEnvVarInFile("VE_ZONE_ID", zoneID)
// 2.1 create vpc
vpcID, err := CreatePrivateNetwork(vpcName, ts, zoneID)
if err != nil {
panic("CreatePrivateNetwork failed, err=" + err.Error())
}
fmt.Println("[VPC] Created successfully vpcID:", vpcID)
updateEnvVarInFile("VE_VPC_ID", vpcID)
CheckVpcStatus(vpcID)
// 2.2 create subnet
subnetID, err := CreateSubnet(vpcName, ts, vpcID, zoneID)
if err != nil {
panic("CreateSubnet failed, err=" + err.Error())
}
fmt.Println("[Subnet] Created successfully subnetID:", subnetID)
updateEnvVarInFile("VE_SUBNET_ID", subnetID)
CheckVpcStatus(vpcID)
// 2.3 create network acl
aclID, err := CreateNetworkACL(vpcName, ts, vpcID)
if err != nil {
panic("CreateNetworkACL failed, err=" + err.Error())
}
fmt.Println("[ACL] Created successfully aclID:", aclID)
updateEnvVarInFile("VE_ACL_ID", aclID)
CheckACLStatus(aclID)
// 2.4 attach subnet to acl
if err = AttachSubnetToACL(aclID, subnetID); err != nil {
panic("AttachSubnetToACL failed, err=" + err.Error())
}
fmt.Println("[ACL] Attached successfully subnetID:", subnetID)
// 2.5 create safe group
sgID, err := CreateSafeGroup(ts, vpcID)
if err != nil {
panic("CreateSafeGroup failed, err=" + err.Error())
}
fmt.Println("[SafeGroup] Created successfully safe group ID:", sgID)
updateEnvVarInFile("VE_SAFE_GROUP_ID", sgID)
CheckSafeGroupStatus(sgID)
// 2.6 create rule
if err = CreateSafeGroupRule(sgID); err != nil {
panic("CreateSafeGroupRule failed, err=" + err.Error())
}
fmt.Println("[SafeGroup] Created safe group rule successfully")
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// ----------------- Create All Instances First---------------------
// -----------------------------------------------------------------
// 3.1 create mysql instance
mysqlInstanceID, err := CreateMySQLInstance(vpcID, subnetID, zoneID, ts)
if err != nil {
panic("CreateMySQLInstance failed, err=" + err.Error())
}
fmt.Println("[MySQL] Created mysqlInstanceID:", mysqlInstanceID)
updateEnvVarInFile("VE_MYSQL_INSTANCE_ID", mysqlInstanceID)
// 4.1 create redis white list
allowListID, err := CreateRedisAllowList(ts)
if err != nil {
panic("CreateRedisAllowList failed, err=" + err.Error())
}
fmt.Printf("[Redis] Created successfully AllowListID : %s\n", allowListID)
updateEnvVarInFile("VE_REDIS_ALLOWLIST_ID", allowListID)
// 4.2 create redis instance with allow list id
redisInstanceID, err := CreateRedisInstance(zoneID, allowListID, vpcID, subnetID, ts)
if err != nil {
panic("CreateRedisInstance failed, err=" + err.Error())
}
fmt.Printf("[Redis] Created successfully RedisInstanceID : %s\n", redisInstanceID)
updateEnvVarInFile("VE_REDIS_INSTANCE_ID", redisInstanceID)
// 5.1 create rocketmq allow list
rmqAllowListID, err := CreateRocketMQAllowList(ts)
if err != nil {
panic("CreateRocketMQAllowList failed, err=" + err.Error())
}
fmt.Printf("[RocketMQ] Created successfully AllowListID : %s\n", rmqAllowListID)
updateEnvVarInFile("VE_ROCKETMQ_ALLOWLIST_ID", rmqAllowListID)
// 6.1 create elasticsearch instance
esInstanceID, err := CreateESInstance(vpcID, subnetID, zoneID, ts)
if err != nil {
panic("CreateESInstance failed, err=" + err.Error())
}
fmt.Printf("[Elasticsearch] Created successfully instanceID : %s\n", esInstanceID)
updateEnvVarInFile("VE_ES_INSTANCE_ID", esInstanceID)
// ecsInstanceID, err := CreateECSInstance(zoneID, sgID, subnetID, ts)
// if err != nil {
// panic("CreateECSInstance failed, err=" + err.Error())
// }
// fmt.Printf("[ECS] Created successfully instanceID : %s\n", ecsInstanceID)
// updateEnvVarInFile("VE_ECS_INSTANCE_ID", ecsInstanceID)
// -----------------------------------------------------------------
// -------- Waiting for the instance to become available -----------
// -----------------------------------------------------------------
fmt.Println("-----------------------------------------------------------------")
fmt.Println("Waiting for the instance to become available")
// ----------------- MySQL -------------------
// 3.2 get mysql connect host and port
mysqlHost, mysqlPort, err := GetMySQLConnectAddress(mysqlInstanceID)
if err != nil {
panic("GetMySQLConnectAddress failed, err=" + err.Error())
}
fmt.Printf("[MySQL] MySQL connect address: %s:%s\n", mysqlHost, mysqlPort)
// 3.3 Create DB
if err = CreateDB(mysqlInstanceID); err != nil {
panic("CreateDB failed, err=" + err.Error())
}
fmt.Printf("[MySQL] Created successfully db : %s\n", mysqlDBName)
// 3.4 Create mysql whitelist
whitelistID, err := CreateMySQLWhiteList(mysqlInstanceID, ts)
if err != nil {
panic("CreateMySQLWhiteList failed, err=" + err.Error())
}
fmt.Printf("[MySQL] Created successfully AllowListID : %s \n", whitelistID)
updateEnvVarInFile("VE_MYSQL_WHITELIST_ID", whitelistID)
// 3.5 Associate whitelist to instance
if err = AssociateMySQLWhiteList(mysqlInstanceID, whitelistID); err != nil {
panic("AssociateMySQLWhiteList failed, err=" + err.Error())
}
fmt.Printf("[MySQL] Associated AllowListID(%s) to MySQLInstanceID(%s)\n", whitelistID, mysqlInstanceID)
// ----------------- Redis -------------------
// 4.3 get redis connection string
redisConn, err := GetRedisConnectionString(redisInstanceID)
if err != nil {
panic("GetRedisConnectionString failed, err=" + err.Error())
}
fmt.Printf("[Redis] Redis connection: %s\n", redisConn)
// ----------------- RocketMQ -------------------
// 5.2 create rocketmq instance
rmqInstanceID, err := CreateRocketMQInstance(vpcID, subnetID, zoneID, ts, rmqAllowListID)
if err != nil {
panic("CreateRocketMQInstance failed, err=" + err.Error())
}
fmt.Printf("[RocketMQ] Created successfully instanceID : %s\n", rmqInstanceID)
updateEnvVarInFile("VE_ROCKETMQ_INSTANCE_ID", rmqInstanceID)
// 5.3 get rocketmq connect address
rmqConnectAddress, err := GetRocketMQConnectAddress(rmqInstanceID)
if err != nil {
panic("GetRocketMQConnectAddress failed, err=" + err.Error())
}
fmt.Printf("[RocketMQ] connect address: %s\n", rmqConnectAddress)
rmqAK, rmqSK, err := CreateRocketMQAccessKey(rmqInstanceID)
if err != nil {
panic("CreateRocketMQAccessKey failed, err=" + err.Error())
}
fmt.Printf("[RocketMQ] access key: %s, secret key: %s\n", rmqAK, rmqSK)
if err = CreateRocketMQTopic(rmqAK, rmqInstanceID); err != nil {
panic("CreateRocketMQTopic failed, err=" + err.Error())
}
fmt.Printf("[RocketMQ] topic created successfully\n")
if err = CreateRocketMQGroup(rmqInstanceID); err != nil {
panic("CreateRocketMQGroup failed, err=" + err.Error())
}
fmt.Printf("[RocketMQ] group created successfully\n")
// ----------------- Elasticsearch -------------------
// 5.2 get elasticsearch connect address
esConnectAddress, err := GetESConnectAddress(esInstanceID)
if err != nil {
panic("GetESConnectAddress failed, err=" + err.Error())
}
fmt.Printf("[Elasticsearch] connect address: %s\n", esConnectAddress)
// -----------------------------------------------------------------
// ------------------------ Output Env -----------------------------
// -----------------------------------------------------------------
fmt.Println("-----------------------------------------------------------------")
fmt.Println("Output Env :")
fmt.Printf("# MySQL\n")
fmt.Printf("export MYSQL_DATABASE=%s\n", mysqlDBName)
fmt.Printf("export MYSQL_USER=%s\n", mysqlUserName)
fmt.Printf("export MYSQL_PASSWORD=%s\n", mysqlUserPassword)
fmt.Printf("export MYSQL_HOST=%s\n", mysqlHost)
fmt.Printf("export MYSQL_PORT=%s\n", mysqlPort)
fmt.Println("")
fmt.Printf("# Redis\n")
fmt.Printf("export REDIS_AOF_ENABLED=no\n")
fmt.Printf("export REDIS_IO_THREADS=4\n")
fmt.Printf("export ALLOW_EMPTY_PASSWORD=yes\n")
fmt.Printf("export REDIS_ADDR=%s\n", redisConn)
fmt.Println("")
fmt.Printf("# Elasticsearch\n")
fmt.Printf("export ES_VERSION=v7\n")
fmt.Printf("export ES_ADDR=%s\n", esConnectAddress)
fmt.Printf("export ES_USERNAME=admin\n")
fmt.Printf("export ES_PASSWORD=%s\n", password)
fmt.Println("")
fmt.Printf("# RocketMQ\n")
fmt.Printf("export RMQ_NAME_SERVER=%s\n", rmqConnectAddress)
fmt.Printf("export RMQ_ACCESS_KEY=%s\n", rmqAK)
fmt.Printf("export RMQ_SECRET_KEY=%s\n", rmqSK)
fmt.Println("")
}
func initServer() error {
config := volcengine.NewConfig().WithRegion(region).
WithCredentials(credentials.NewStaticCredentials(ak, sk, ""))
var err error
sess, err = session.NewSession(config)
if err != nil {
return err
}
return nil
}
func loadEnv() error {
err := godotenv.Load()
if err != nil {
return err
}
ak = os.Getenv("VE_AK")
sk = os.Getenv("VE_SK")
region = os.Getenv("VE_REGION")
projectName = os.Getenv("VE_PROJECT_NAME")
vpcName = os.Getenv("VE_VPC_NAME")
// MySQL
mysqlDBName = os.Getenv("VE_MYSQL_DB_NAME")
mysqlUserName = os.Getenv("VE_MYSQL_USER_NAME")
mysqlUserPassword = os.Getenv("VE_MYSQL_USER_PASSWORD")
mysqlPort = os.Getenv("VE_MYSQL_PORT")
mysqlDBEngineVersion = os.Getenv("VE_MYSQL_DB_ENGINE_VERSION")
mysqlInstanceClass = os.Getenv("VE_MYSQL_INSTANCE_CLASS")
if ak == "" {
return errors.New("VE_AK is empty")
}
if sk == "" {
return errors.New("VE_SK is empty")
}
if region == "" {
return errors.New("VE_REGION is empty")
}
if projectName == "" {
return errors.New("VE_PROJECT_NAME is empty")
}
if vpcName == "" {
return errors.New("VE_VPC_NAME is empty")
}
// MySQL
if mysqlDBName == "" {
return errors.New("VE_MYSQL_INSTANCE_NAME is empty")
}
if mysqlUserName == "" {
return errors.New("VE_MYSQL_USER_NAME is empty")
}
if mysqlUserPassword == "" {
return errors.New("VE_MYSQL_USER_PASSWORD is empty")
}
if mysqlPort == "" {
return errors.New("VE_MYSQL_PORT is empty")
}
if mysqlDBEngineVersion == "" {
return errors.New("VE_MYSQL_DB_ENGINE_VERSION is empty")
}
if mysqlInstanceClass == "" {
return errors.New("VE_MYSQL_INSTANCE_CLASS is empty")
}
return nil
}
func s(p *string) string {
if p == nil {
return ""
}
return *p
}

174
scripts/volcengine/mysql.go Normal file
View File

@@ -0,0 +1,174 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/volcengine/volcengine-go-sdk/service/vedbm"
"github.com/volcengine/volcengine-go-sdk/volcengine"
)
func CreateMySQLInstance(vpcID, subnetID, zoneID, ts string) (string, error) {
if os.Getenv("VE_MYSQL_INSTANCE_ID") != "" {
return os.Getenv("VE_MYSQL_INSTANCE_ID"), nil
}
svc := vedbm.New(sess)
reqTags := &vedbm.TagForCreateDBInstanceInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
createDBInstanceInput := &vedbm.CreateDBInstanceInput{
ChargeType: volcengine.String("PostPaid"),
DBEngineVersion: volcengine.String(mysqlDBEngineVersion),
DBTimeZone: volcengine.String("UTC +08:00"),
InstanceName: volcengine.String(mysqlDBName + "-" + ts),
LowerCaseTableNames: volcengine.String("1"),
NodeNumber: volcengine.Int32(2),
NodeSpec: volcengine.String(mysqlInstanceClass),
Number: volcengine.Int32(1),
ProjectName: volcengine.String(projectName),
StorageChargeType: volcengine.String("PostPaid"),
SubnetId: volcengine.String(subnetID),
SuperAccountName: volcengine.String(mysqlUserName),
SuperAccountPassword: volcengine.String(mysqlUserPassword),
Tags: []*vedbm.TagForCreateDBInstanceInput{reqTags},
VpcId: volcengine.String(vpcID),
ZoneIds: volcengine.String(zoneID),
}
resp, err := svc.CreateDBInstance(createDBInstanceInput)
if err != nil {
return "", err
}
if resp.InstanceId == nil {
return "", errors.New("CreateDBInstance resp.InstanceId is nil")
}
return *resp.InstanceId, nil
}
func GetMySQLConnectAddress(mysqlInstanceID string) (string, string, error) {
svc := vedbm.New(sess)
describeDBInstanceDetailInput := &vedbm.DescribeDBInstanceDetailInput{
InstanceId: volcengine.String(mysqlInstanceID),
}
for {
resp, err := svc.DescribeDBInstanceDetail(describeDBInstanceDetailInput)
if resp.InstanceDetail != nil && resp.InstanceDetail.InstanceStatus != nil && *resp.InstanceDetail.InstanceStatus != "Running" {
fmt.Printf("[MySQL] instance(%s) is %s, waiting for it to become ready... \n", mysqlInstanceID, *resp.InstanceDetail.InstanceStatus)
time.Sleep(retryTime)
continue
}
if resp.Metadata != nil && resp.Metadata.Error != nil &&
resp.Metadata.Error.Code == "OperationDenied.UnsupportedOperation" {
fmt.Printf("[MySQL] instance[%s] is creating, waiting for it to become ready... \n", mysqlInstanceID)
time.Sleep(retryTime)
continue
}
if err != nil {
fmt.Printf("[MySQL] failed, err: %s \n", err)
time.Sleep(retryTime)
continue
}
if len(resp.Endpoints) == 0 {
fmt.Printf("[MySQL] endpoints is empty, will try it later \n")
time.Sleep(retryTime)
continue
}
if len(resp.Endpoints[0].Addresses) == 0 {
fmt.Printf("[MySQL] endpoints[0].Addresses is nil, will try it later \n")
time.Sleep(retryTime)
continue
}
if resp.Endpoints[0].Addresses[0].Domain == nil || resp.Endpoints[0].Addresses[0].Port == nil {
fmt.Printf("[MySQL] endpoints[0].Addresses[0].Domain or endpoints[0].Addresses[0].Port is nil, will try it later \n")
time.Sleep(retryTime)
continue
}
return *resp.Endpoints[0].Addresses[0].Domain, *resp.Endpoints[0].Addresses[0].Port, nil
}
}
func CreateDB(mysqlInstanceID string) error {
svc := vedbm.New(sess)
createDatabaseInput := &vedbm.CreateDatabaseInput{
CharacterSetName: volcengine.String("utf8mb4"),
DBName: volcengine.String(mysqlDBName),
InstanceId: volcengine.String(mysqlInstanceID),
}
resp, err := svc.CreateDatabase(createDatabaseInput)
if resp.Metadata != nil && resp.Metadata.Error != nil &&
resp.Metadata.Error.Code == "InvalidDatabaseName.Duplicate" {
return nil
}
if err != nil {
return fmt.Errorf("[MySQL] CreateDB failed, err: %w", err)
}
return nil
}
func CreateMySQLWhiteList(mysqlInstanceID, ts string) (string, error) {
svc := vedbm.New(sess)
createAllowListInput := &vedbm.CreateAllowListInput{
AllowList: volcengine.String("172.16.0.0/12"),
AllowListName: volcengine.String("opencoze-mysql-" + ts),
ProjectName: volcengine.String(projectName),
}
// 复制代码运行示例请自行打印API返回值。
resp, err := svc.CreateAllowList(createAllowListInput)
if err != nil {
return "", err
}
if resp.AllowListId == nil {
return "", errors.New("[MySQL] CreateAllowList resp.AllowListId is nil")
}
return *resp.AllowListId, nil
}
func AssociateMySQLWhiteList(mysqlInstanceID, whitelistID string) error {
svc := vedbm.New(sess)
associateAllowListInput := &vedbm.AssociateAllowListInput{
AllowListIds: volcengine.StringSlice([]string{whitelistID}),
InstanceIds: volcengine.StringSlice([]string{mysqlInstanceID}),
}
// 复制代码运行示例请自行打印API返回值。
_, err := svc.AssociateAllowList(associateAllowListInput)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,339 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/volcengine/volcengine-go-sdk/service/vpc"
"github.com/volcengine/volcengine-go-sdk/volcengine"
)
func CreatePrivateNetwork(vName, ts, zoneID string) (string, error) {
if os.Getenv("VE_VPC_ID") != "" {
return os.Getenv("VE_VPC_ID"), nil
}
svc := vpc.New(sess)
reqTags := &vpc.TagForCreateVpcInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
newVpcName := vName + ts
createVpcInput := &vpc.CreateVpcInput{
CidrBlock: volcengine.String("172.16.0.0/12"),
ClientToken: volcengine.String(newVpcName),
ProjectName: volcengine.String(projectName),
Tags: []*vpc.TagForCreateVpcInput{reqTags},
VpcName: volcengine.String(newVpcName),
}
resp, err := svc.CreateVpc(createVpcInput)
if err != nil {
return "", err
}
if resp.VpcId == nil {
return "", errors.New("[VPC] VpcId is empty")
}
return *resp.VpcId, nil
}
func CheckVpcStatus(vpcID string) {
svc := vpc.New(sess)
describeVpcAttributesInput := &vpc.DescribeVpcAttributesInput{
VpcId: volcengine.String(vpcID),
}
for {
resp, err := svc.DescribeVpcAttributes(describeVpcAttributesInput)
if resp.Status != nil && *resp.Status != "Available" {
fmt.Printf("[VPC] VPC(%s) is %s, waiting for it to become ready... \n", vpcID, *resp.Status)
time.Sleep(retryTime)
continue
}
if err != nil {
fmt.Printf("[VPC] get vpc id = %s failed, err= %s will retry\n", vpcID, err.Error())
time.Sleep(retryTime)
continue
}
if resp.Status == nil {
fmt.Printf("[VPC] get vpc id = %s failed, status is nil will retry\n", vpcID)
time.Sleep(retryTime)
continue
}
if *resp.Status == "Available" {
break
}
}
}
func CreateSubnet(vName, ts, vpcID, zoneID string) (string, error) {
if os.Getenv("VE_SUBNET_ID") != "" {
return os.Getenv("VE_SUBNET_ID"), nil
}
reqSubTags := &vpc.TagForCreateSubnetInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
newSubnetName := vName + "-subnet-" + ts
createSubnetInput := &vpc.CreateSubnetInput{
CidrBlock: volcengine.String("172.16.0.0/24"),
ClientToken: volcengine.String(newSubnetName),
SubnetName: volcengine.String(newSubnetName),
Tags: []*vpc.TagForCreateSubnetInput{reqSubTags},
VpcId: volcengine.String(vpcID),
ZoneId: volcengine.String(zoneID),
}
svc := vpc.New(sess)
resp, err := svc.CreateSubnet(createSubnetInput)
if err != nil {
return "", err
}
if resp.SubnetId == nil {
return "", errors.New("[Subnet] SubnetId is empty")
}
return *resp.SubnetId, nil
}
func CreateNetworkACL(vName, ts, vpcID string) (string, error) {
if os.Getenv("VE_ACL_ID") != "" {
return os.Getenv("VE_ACL_ID"), nil
}
svc := vpc.New(sess)
reqTags := &vpc.TagForCreateNetworkAclInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
newACLName := vName + "-acl-" + ts
createNetworkAclInput := &vpc.CreateNetworkAclInput{
NetworkAclName: volcengine.String(newACLName),
ProjectName: volcengine.String(projectName),
Tags: []*vpc.TagForCreateNetworkAclInput{reqTags},
VpcId: volcengine.String(vpcID),
}
resp, err := svc.CreateNetworkAcl(createNetworkAclInput)
if err != nil {
return "", err
}
if resp.NetworkAclId == nil {
return "", errors.New("NetworkAclId is empty")
}
return *resp.NetworkAclId, nil
}
func CheckACLStatus(aclID string) {
svc := vpc.New(sess)
describeNetworkAclAttributesInput := &vpc.DescribeNetworkAclAttributesInput{
NetworkAclId: volcengine.String(aclID),
}
for {
resp, err := svc.DescribeNetworkAclAttributes(describeNetworkAclAttributesInput)
if resp.NetworkAclAttribute != nil && resp.NetworkAclAttribute.Status != nil && *resp.NetworkAclAttribute.Status != "Available" {
fmt.Printf("[ACL] ACL(%s) is %s, waiting for it to become ready... \n", aclID, *resp.NetworkAclAttribute.Status)
time.Sleep(retryTime)
continue
}
if err != nil {
fmt.Printf("[ACL] will retry get acl = %s failed, err= %s\n", aclID, err.Error())
time.Sleep(retryTime)
continue
}
if resp.NetworkAclAttribute == nil || resp.NetworkAclAttribute.Status == nil {
fmt.Printf("[ACL] ACL(%s) Status is nil, will retry\n", aclID)
time.Sleep(retryTime)
continue
}
if *resp.NetworkAclAttribute.Status == "Available" {
break
}
}
}
func AttachSubnetToACL(aclID, subnetID string) error {
svc := vpc.New(sess)
reqResource := &vpc.ResourceForAssociateNetworkAclInput{
ResourceId: volcengine.String(subnetID),
}
associateNetworkAclInput := &vpc.AssociateNetworkAclInput{
NetworkAclId: volcengine.String(aclID),
Resource: []*vpc.ResourceForAssociateNetworkAclInput{reqResource},
}
resp, err := svc.AssociateNetworkAcl(associateNetworkAclInput)
if resp.Metadata != nil && resp.Metadata.Error != nil &&
resp.Metadata.Error.Code == "InvalidResource.Associated" {
return nil
}
if err != nil {
return err
}
return nil
}
func CreateSafeGroup(ts, vpcID string) (string, error) {
if os.Getenv("VE_SAFE_GROUP_ID") != "" {
return os.Getenv("VE_SAFE_GROUP_ID"), nil
}
svc := vpc.New(sess)
reqTags := &vpc.TagForCreateSecurityGroupInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
createSecurityGroupInput := &vpc.CreateSecurityGroupInput{
ProjectName: volcengine.String(projectName),
SecurityGroupName: volcengine.String("opencoze-sg-" + ts),
Tags: []*vpc.TagForCreateSecurityGroupInput{reqTags},
VpcId: volcengine.String(vpcID),
}
resp, err := svc.CreateSecurityGroup(createSecurityGroupInput)
if err != nil {
return "", err
}
if resp.SecurityGroupId == nil {
return "", errors.New("SecurityGroupId is empty")
}
return *resp.SecurityGroupId, nil
}
func CheckSafeGroupStatus(sgID string) {
for {
svc := vpc.New(sess)
describeSecurityGroupAttributesInput := &vpc.DescribeSecurityGroupAttributesInput{
SecurityGroupId: volcengine.String(sgID),
}
// 复制代码运行示例请自行打印API返回值。
resp, err := svc.DescribeSecurityGroupAttributes(describeSecurityGroupAttributesInput)
if err != nil {
fmt.Printf("[SafeGroup] will retry get safe group = %s failed, err= %s\n", sgID, err.Error())
time.Sleep(retryTime)
continue
}
if resp.Status == nil || *resp.Status != "Available" {
fmt.Printf("[SafeGroup] safe group(%s) is %s, waiting for it to become ready... \n", sgID, *resp.Status)
time.Sleep(retryTime)
continue
}
break
}
}
func CreateSafeGroupRule(sgID string) error {
svc := vpc.New(sess)
type Rule struct {
PortStart int64
PortEnd int64
Protocol string
}
defaultRules := []Rule{
{
PortStart: 8000,
PortEnd: 10000,
Protocol: "tcp",
},
{
PortStart: 80,
PortEnd: 80,
Protocol: "tcp",
},
{
PortStart: 22,
PortEnd: 22,
Protocol: "tcp",
},
{
PortStart: 443,
PortEnd: 443,
Protocol: "tcp",
},
{
PortStart: 3389,
PortEnd: 3389,
Protocol: "tcp",
},
{
PortStart: -1,
PortEnd: -1,
Protocol: "ALL",
},
}
for _, rule := range defaultRules {
authorizeSecurityGroupIngressInput := &vpc.AuthorizeSecurityGroupIngressInput{
CidrIp: volcengine.String("0.0.0.0/0"),
PortEnd: volcengine.Int64(rule.PortEnd),
PortStart: volcengine.Int64(rule.PortStart),
Protocol: volcengine.String(rule.Protocol),
SecurityGroupId: volcengine.String(sgID),
}
if rule.PortStart == -1 {
authorizeSecurityGroupIngressInput.SourceGroupId = &sgID
authorizeSecurityGroupIngressInput.CidrIp = nil
}
resp, err := svc.AuthorizeSecurityGroupIngress(authorizeSecurityGroupIngressInput)
if resp != nil && resp.Metadata != nil && resp.Metadata.Error != nil &&
resp.Metadata.Error.Code == "InvalidSecurityRule.Conflict" {
continue
}
if err != nil {
return err
}
}
return nil
}

133
scripts/volcengine/redis.go Normal file
View File

@@ -0,0 +1,133 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/volcengine/volcengine-go-sdk/service/redis"
"github.com/volcengine/volcengine-go-sdk/volcengine"
)
func CreateRedisAllowList(ts string) (string, error) {
svc := redis.New(sess)
name := "opencoze-redis" + ts
createAllowListInput := &redis.CreateAllowListInput{
AllowList: volcengine.String("172.16.0.0/12"),
AllowListName: volcengine.String(name),
ProjectName: volcengine.String(projectName),
}
resp, err := svc.CreateAllowList(createAllowListInput)
if err != nil {
return "", err
}
if resp.AllowListId == nil {
return "", errors.New("CreateAllowList resp.AllowListId is nil")
}
return *resp.AllowListId, nil
}
func CreateRedisInstance(zoneID, allowListID, vpcID, subnetID, ts string) (string, error) {
instanceID := os.Getenv("VE_REDIS_INSTANCE_ID")
if instanceID != "" {
return instanceID, nil
}
svc := redis.New(sess)
reqConfigureNodes := &redis.ConfigureNodeForCreateDBInstanceInput{
AZ: volcengine.String(zoneID),
}
reqTags := &redis.TagForCreateDBInstanceInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
name := "opencoze-redis-" + ts
createDBInstanceInput := &redis.CreateDBInstanceInput{
AllowListIds: volcengine.StringSlice([]string{allowListID}),
ChargeType: volcengine.String("PostPaid"),
NoAuthMode: volcengine.String("open"),
ConfigureNodes: []*redis.ConfigureNodeForCreateDBInstanceInput{reqConfigureNodes},
EngineVersion: volcengine.String("7.0"),
InstanceName: volcengine.String(name),
MultiAZ: volcengine.String("disabled"),
NodeNumber: volcengine.Int32(2),
ProjectName: volcengine.String(projectName),
RegionId: volcengine.String(region),
ShardCapacity: volcengine.Int64(256),
ShardNumber: volcengine.Int32(1),
ShardedCluster: volcengine.Int32(0),
SubnetId: volcengine.String(subnetID),
Tags: []*redis.TagForCreateDBInstanceInput{reqTags},
VpcId: volcengine.String(vpcID),
}
resp, err := svc.CreateDBInstance(createDBInstanceInput)
if err != nil {
return "", err
}
if resp.InstanceId == nil {
return "", errors.New("[Redis] CreateDBInstance resp.InstanceId is nil")
}
return *resp.InstanceId, nil
}
func GetRedisConnectionString(instanceID string) (string, error) {
svc := redis.New(sess)
describeDBInstanceDetailInput := &redis.DescribeDBInstanceDetailInput{
InstanceId: volcengine.String(instanceID),
}
for {
resp, err := svc.DescribeDBInstanceDetail(describeDBInstanceDetailInput)
if err != nil {
fmt.Printf("[Redis] failed, err: %s \n", err)
time.Sleep(retryTime)
continue
}
if resp.Status == nil || *resp.Status != "Running" {
fmt.Printf("[Redis] instance(%s) is %s, waiting for it to become ready... \n", instanceID, *resp.Status)
time.Sleep(retryTime)
continue
}
if len(resp.VisitAddrs) == 0 {
fmt.Printf("[Redis] instance(%s) is creating, waiting for it to become ready... \n", instanceID)
time.Sleep(retryTime)
continue
}
if resp.VisitAddrs[0].Address == nil || resp.VisitAddrs[0].Port == nil {
fmt.Printf("[Redis] VisitAddrs[0].Address or VisitAddrs[0].Port is nil, will try it later \n")
time.Sleep(retryTime)
continue
}
return fmt.Sprintf("%s:%s", *resp.VisitAddrs[0].Address, *resp.VisitAddrs[0].Port), nil
}
}

View File

@@ -0,0 +1,307 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/volcengine/volcengine-go-sdk/service/rocketmq"
"github.com/volcengine/volcengine-go-sdk/volcengine"
)
func CreateRocketMQAllowList(ts string) (string, error) {
if os.Getenv("VE_ROCKETMQ_ALLOWLIST_ID") != "" {
return os.Getenv("VE_ROCKETMQ_ALLOWLIST_ID"), nil
}
svc := rocketmq.New(sess)
name := fmt.Sprintf("opencoze-rmq-%s", ts)
createAllowListInput := &rocketmq.CreateAllowListInput{
AllowList: volcengine.String("172.16.0.0/12"),
AllowListName: volcengine.String(name),
AllowListType: volcengine.String("IPv4"),
}
resp, err := svc.CreateAllowList(createAllowListInput)
if err != nil {
return "", err
}
if resp.AllowListId == nil {
return "", errors.New("CreateAllowList resp.AllowListId is nil")
}
return *resp.AllowListId, nil
}
func CreateRocketMQInstance(vpcID, subnetID, zoneID, ts, allowListID string) (string, error) {
if os.Getenv("VE_ROCKETMQ_INSTANCE_ID") != "" {
return os.Getenv("VE_ROCKETMQ_INSTANCE_ID"), nil
}
svc := rocketmq.New(sess)
reqBindTags := &rocketmq.BindTagForCreateInstanceInput{
Key: volcengine.String("opencoze"),
Value: volcengine.String("1"),
}
reqChargeInfo := &rocketmq.ChargeInfoForCreateInstanceInput{
ChargeType: volcengine.String("PostPaid"),
}
createInstanceInput := &rocketmq.CreateInstanceInput{
AllowListIds: volcengine.StringSlice([]string{allowListID}),
BindTags: []*rocketmq.BindTagForCreateInstanceInput{reqBindTags},
ChargeInfo: reqChargeInfo,
ComputeSpec: volcengine.String("rocketmq.n1.x2.micro"),
FileReservedTime: volcengine.Int32(72),
IPVersionType: volcengine.String("IPv4"),
InstanceName: volcengine.String(fmt.Sprintf("opencoze-rmq-%s", ts)),
NetworkTypes: volcengine.String("PrivateNetwork"),
ProjectName: volcengine.String(projectName),
StorageSpace: volcengine.Int32(300),
SubnetId: volcengine.String(subnetID),
Version: volcengine.String("4.8"),
VpcId: volcengine.String(vpcID),
ZoneId: volcengine.String(zoneID),
}
resp, err := svc.CreateInstance(createInstanceInput)
if err != nil {
return "", err
}
if resp.InstanceId == nil {
return "", errors.New("[RocketMQ] CreateInstance resp.InstanceId is nil")
}
return *resp.InstanceId, nil
}
func GetRocketMQConnectAddress(instanceID string) (string, error) {
svc := rocketmq.New(sess)
describeInstanceDetailInput := &rocketmq.DescribeInstanceDetailInput{
InstanceId: volcengine.String(instanceID),
}
for {
resp, err := svc.DescribeInstanceDetail(describeInstanceDetailInput)
if err != nil {
return "", err
}
if resp.BasicInfo == nil || resp.BasicInfo.InstanceStatus == nil || *resp.BasicInfo.InstanceStatus != "Running" {
fmt.Printf("[RocketMQ] instance(%s) is %s, waiting for it to become ready... \n", instanceID, *resp.BasicInfo.InstanceStatus)
time.Sleep(retryTime)
continue
}
if len(resp.ConnectionInfo) == 0 || resp.ConnectionInfo[0].InternalEndpoint == nil {
fmt.Println("[RocketMQ] DescribeInstanceDetail resp.ConnectionInfo is empty, will retry")
time.Sleep(retryTime)
continue
}
if *resp.ConnectionInfo[0].InternalEndpoint == "" {
fmt.Printf("[RocketMQ] instance(%s) is creating, waiting for it to become ready... \n", instanceID)
time.Sleep(retryTime)
continue
}
return *resp.ConnectionInfo[0].InternalEndpoint, nil
}
}
func getRocketMQAccessKey(instanceID string) (string, error) {
describeAccessKeysInput := &rocketmq.DescribeAccessKeysInput{
InstanceId: volcengine.String(instanceID),
PageNumber: volcengine.Int32(1),
PageSize: volcengine.Int32(100),
}
svc := rocketmq.New(sess)
resp, err := svc.DescribeAccessKeys(describeAccessKeysInput)
if err != nil {
return "", err
}
ak := ""
for _, info := range resp.AccessKeysInfo {
if info.AccessKey == nil || info.AllAuthority == nil {
continue
}
if *info.AllAuthority == "ALL" {
return *info.AccessKey, nil
}
ak = *info.AccessKey
}
return ak, nil
}
func CreateRocketMQAccessKey(instanceID string) (string, string, error) {
ak, err := getRocketMQAccessKey(instanceID)
if err != nil {
return "", "", err
}
svc := rocketmq.New(sess)
if ak == "" {
createAccessKeyInput := &rocketmq.CreateAccessKeyInput{
AllAuthority: volcengine.String("ALL"),
InstanceId: volcengine.String(instanceID),
}
// 复制代码运行示例请自行打印API返回值。
_, err = svc.CreateAccessKey(createAccessKeyInput)
if err != nil {
return "", "", err
}
ak, err = getRocketMQAccessKey(instanceID)
if err != nil {
return "", "", err
}
}
if ak == "" {
return "", "", errors.New("[RocketMQ] CreateRocketMQAccessKey ak is empty")
}
describeSecretKeyInput := &rocketmq.DescribeSecretKeyInput{
AccessKey: volcengine.String(ak),
InstanceId: volcengine.String(instanceID),
}
resp, err := svc.DescribeSecretKey(describeSecretKeyInput)
if err != nil {
return "", "", err
}
if resp.SecretKey == nil {
return "", "", errors.New("[RocketMQ] CreateRocketMQAccessKey resp.SecretKey is nil")
}
return ak, *resp.SecretKey, nil
}
func CreateRocketMQTopic(ak, instanceID string) error {
svc := rocketmq.New(sess)
describeTopicsInput := &rocketmq.DescribeTopicsInput{
InstanceId: volcengine.String(instanceID),
PageNumber: volcengine.Int32(1),
PageSize: volcengine.Int32(100),
}
topicNeedCreate := map[string]bool{
"opencoze_search_resource": true,
"opencoze_search_app": true,
"opencoze_knowledge": true,
}
resp, err := svc.DescribeTopics(describeTopicsInput)
if err != nil {
return err
}
for _, info := range resp.TopicsInfo {
if info.TopicName == nil {
continue
}
if topicNeedCreate[*info.TopicName] {
topicNeedCreate[*info.TopicName] = false
}
}
reqAccessPolicies := &rocketmq.AccessPolicyForCreateTopicInput{
AccessKey: volcengine.String(ak),
Authority: volcengine.String("ALL"),
}
for topicName, needCreate := range topicNeedCreate {
if !needCreate {
fmt.Printf("[RocketMQ] topic %s already exists, skip\n", topicName)
continue
}
createTopicInput := &rocketmq.CreateTopicInput{
AccessPolicies: []*rocketmq.AccessPolicyForCreateTopicInput{reqAccessPolicies},
InstanceId: volcengine.String(instanceID),
MessageType: volcengine.Int32(0),
QueueNumber: volcengine.Int32(2),
TopicName: volcengine.String(topicName),
}
// 复制代码运行示例请自行打印API返回值。
_, err = svc.CreateTopic(createTopicInput)
if err != nil {
return err
}
fmt.Printf("[RocketMQ] topic %s created\n", topicName)
}
return nil
}
func CreateRocketMQGroup(instanceID string) error {
groupNeedCreate := map[string]bool{
"cg_search_resource": true,
"cg_search_app": true,
"cg_knowledge": true,
}
svc := rocketmq.New(sess)
describeGroupsInput := &rocketmq.DescribeGroupsInput{
InstanceId: volcengine.String(instanceID),
PageNumber: volcengine.Int32(1),
PageSize: volcengine.Int32(100),
}
resp, err := svc.DescribeGroups(describeGroupsInput)
if err != nil {
return err
}
for _, info := range resp.GroupsInfo {
if info.GroupId == nil {
continue
}
if groupNeedCreate[*info.GroupId] {
groupNeedCreate[*info.GroupId] = false
}
}
for groupName, needCreate := range groupNeedCreate {
if !needCreate {
fmt.Printf("[RocketMQ] group %s already exists, skip\n", groupName)
continue
}
createGroupInput := &rocketmq.CreateGroupInput{
GroupId: volcengine.String(groupName),
GroupType: volcengine.String("TCP"),
InstanceId: volcengine.String(instanceID),
}
_, err = svc.CreateGroup(createGroupInput)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2025 coze-dev Authors
*
* 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.
*/
package main
import (
"fmt"
"os"
"text/tabwriter"
"github.com/volcengine/volcengine-go-sdk/service/rocketmq"
)
func GetChosenZoneID() (string, error) {
zoneID := os.Getenv("VE_ZONE_ID")
if zoneID != "" {
return zoneID, nil
}
resp, err := listZoneInfos()
if err != nil {
return "", err
}
return chooseZone(resp), nil
}
func chooseZone(resp *rocketmq.DescribeAvailabilityZonesOutput) string {
fmt.Printf("Please choose a zone id in list: ")
var zoneId string
fmt.Scanln(&zoneId)
// zoneId := "cn-beijing-a"
var found bool
for _, zone := range resp.Zones {
if zoneId == s(zone.ZoneId) {
found = true
break
}
}
if !found {
fmt.Printf("Zone id %s does not exist, please choose again\n", zoneId)
return chooseZone(resp)
}
return zoneId
}
func listZoneInfos() (*rocketmq.DescribeAvailabilityZonesOutput, error) {
svc := rocketmq.New(sess)
resp, err := svc.DescribeAvailabilityZones(&rocketmq.DescribeAvailabilityZonesInput{})
if err != nil {
return nil, err
}
fmt.Printf("AvailabilityZones:\n")
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
fmt.Fprintln(w, "Zone\tZoneID\tStatus\tDescription")
for _, zone := range resp.Zones {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", s(zone.ZoneName), s(zone.ZoneId), s(zone.ZoneStatus), s(zone.Description))
}
w.Flush()
return resp, nil
}