总控、引擎、插件组件包定义.md

概述

总控、引擎、插件包定义,构建、导入等关键流程实现方案,和接口设计

组件包定义

总控、引擎、插件、Agent 等为大组件,它们的发布时按照下面协议约定构建出的文件称为组件包,导入到总控平台后,会在平台中生成对应可用组件,提供用户使用相应功能。结合平台自身环境,在导入后有不同的用户交互,如流量引擎组件包新导入总控平台时,平台会出现流量引擎安装交互接口,当之前已有可用的流量引擎组件时,平台则会出现流量引擎升级交互接口。

组件包结构

  • 基本信息:

    name:组件包的名称,使用产品定义的组件英文简称。
    version:组件包的当前版本。
    description:组件包的简要描述。
    type:组件包的类型,包括sc(总控组件)、engine(引擎组件)、plugin(插件组件)、agent(代理组件)。

  • 依赖信息:

    dependencies:组件包所依赖的其他组件包列表,包括依赖项的名称、版本范围、类型、兼容版本、不兼容版本和描述。

  • 完整性校验:

    checksum:包含整个压缩包的校验和信息,用于校验包的完整性。

  • 版本更新日志:

    changelog:此版本更新内容。

  • 运行相关内容:

组件包生命周期

构建

构建顺序:

  • 运行相关内容
  • 基本信息、依赖管理、完整性校验

导入

导入时会将组件包元信息录入到平台数据库中,以作安装、升级管理界面查看使用,在平台中生成用户可交互的组件

组件用户操作接口

  • 安装、升级、回退(插件有)、回滚、取消安装、卸载、强制卸载、备份(引擎有)、备份恢复(引擎有)

组件包内容

目录结构

组件包文件格式为 ${组件名}_v${version}.${os_name}-${arch}.tar.gz SC_v1.5.6.linux-x86_64.tar.gz
使用 tar -zxvf 解压后为以下目录接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
SC_v1.5.15.linux-x86_64/
│ ├── nsq
│ │ ├── bin
│ │ ├── hack
│ │ └── systemd
│ ├── redis
│ │ ├── bin
│ │ ├── etc
│ │ └── hack
│ ├── sc
│ │ ├── bin
│ │ ├── certificate
│ │ ├── config
│ │ ├── data
│ │ ├── depends
│ │ ├── dist
│ │ ├── doc
│ │ ├── hack
│ │ ├── log
│ │ ├── systemd
│ │ └── template
│ ├── sc-core
│ │ ├── bin
│ │ ├── config
│ │ └── hack
│ ├── script
│ │ ├── check.sh
│ │ ├── config
│ │ ├── install.sh
│ │ ├── os_info.env
│ │ ├── uninstall.sh
│ │ └── update_ui.sh
│ └── template
│ ├── install.sh
│ ├── leakScan
│ ├── README.md
│ ├── sensitive
│ ├── sqlInject
│ ├── uninstall.sh
│ ├── upgrade.sh
│ ├── waf
│ └── wpd
├── meta.json
├── install.sh
├── uninstall.sh



生命周期管理信息

发布信息

版本更新日志 changelog

格式

1
2
3
4
5
6
7
8
9
10
11
## [1.2.0] - 2024-06-18
### Added
- 新特性 A
- 新特性 B

### Fixed
- Bug X
- Bug Y

### Improved
- 操作体验优化 Z

生命周期说明: 发布时由产品发布此文件,导入时解析此文件,并存入到数据库,安装包、升级包清理时,文件随包一起清理。有必要时,清理无用包信息,可以删除此信息

依赖信息 checksum meta.json

需要最后填充此信息,因为里面包含 checksum 信息,用于包完整性校验

  • sc示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"name": "SC",
// 产品定义的组件英文简称 https://doc.weixin.qq.com/sheet/e3_AIgA6waJABcW3JvHCTFSyGeeGr7PY?scode=ABwA9Qd2ABEu5XCNS9Ab8AhQbAADQ&tab=BB08J2
"version": "1.7.0",
// 发布版本号
"description": "总控组件包",
"type": "sc",
// sc engine plugin agent
"checksum": {
"v1": "1234567890abcdef1234567890abcdef"
},
"dependencies": [
// 依赖管理
{
"name": "SE",
"type": "engine",
"compatible_versions": [">=2.1.0", "<3.0.0"],
"incompatible_versions": [
"2.1.2"
],
"description": "引擎组件包"
}
],
"proto_version": 1, // 协议版本
"changelog": "This is the changelog content. \n- Added new features \n- Fixed bugs \n- Improved performance",
"component": ["sc", "sc-core", "installer"], // 这里就是组件目录名,组件名要和组件目录名一致,也就是在 able.json 中 name 要对上
}



  • 引擎示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"name": "SE", // 产品定义的组件英文简称
"version": "2.1.1", // 发布版本号
"description": "引擎组件包",
"type": "engine", // sc engine plugin agent
"checksum": {
"v1": "1234567890abcdef1234567890abcdef",
},
"comp": {}, // 小组件和目录
"dependencies": [ // 依赖管理
{
"name": "SC",
"type": "sc",
"compatible_versions": ">=1.5.1, <2.0.0",
"incompatible_versions": ["1.6.0", "1.7.0"],
"description": "总控组件包"
},
{
"name": "acl", // 产品定义的组件英文简称
"description": "ACL 插件包",
"type": "plugin",
"incompatible_versions": ["4.1.5"],
}
],
"proto_version": 1,
"changelog": "This is the changelog content. \n- Added new features \n- Fixed bugs \n- Improved performance",
}

  • Agent 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "name": "srfa", // 产品定义的组件英文简称
    "version": "1.6.1", // 发布版本号
    "description": "流量 Agent 组件包",
    "type": "agent", // sc engine plugin agent
    "checksum": {
    "v1": "1234567890abcdef1234567890abcdef",
    },
    "dependencies": [ // 依赖管理
    {
    "name": "srfm", // 镜像引擎
    "type": "engine",
    "compatible_versions": ">=1.5.1, <2.0.0",
    "incompatible_versions": ["1.6.1"],
    "description": "镜像引擎组件包"
    },
    ],
    "proto_version": 1,
    "changelog": "This is the changelog content. \n- Added new features \n- Fixed bugs \n- Improved performance",
    }

  • 插件示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"name": "acl", // 产品定义的组件英文简称
"version": "4.1.3",
"description": "ACL 插件包", // 界面上对于组件包信息描述
"type": "plugin", // sc engine plugin agent
"checksum": {
"v1": "1234567890abcdef1234567890abcdef",
},
"dependencies": [ // 依赖管理
{
"name": "SE",
"type": "engine",
"compatible_versions": ">=2.1.0, <3.0.0",
"incompatible_versions": ["2.1.2"],
"description": "引擎组件包"
},
{
"name": "SC",
"type": "sc",
"compatible_versions": ">=1.5.1, <2.0.0",
"incompatible_versions": ["1.6.0", "1.7.0"],
"description": "总控组件包"
}
],
"proto_version": 1,
"changelog": "This is the changelog content. \n- Added new features \n- Fixed bugs \n- Improved performance",
}

  • 完整性校验机制机制
    1
    2
    3
    # 排除 meta.json 本身递归计算 目录下所有文件的 md5 值,注意,发布时要和导入校验时算法保持一致
    find . -type f ! -name 'meta.json' -exec md5sum {} + | awk '{print $1}' | md5sum

运行时信息

包含 二进制可执行文件,配置文件,内置脚本,等信息

接口定义

组件包集

组件包集定义

组件包集由多个组件包组成,软件交付时,大概率是多个组件包构建为一个组件包集给到用户,才能给用户完整的体验。

结构和构建规则

组件包集文件名格式:20240618-SRBundle-batch.tar.gz,组件包集由多个组件包和一个名为 batch.json 的文件组成。batch.json 文件记录了每个组件包的校验和(checksum)值,用于验证组件包的完整性。组件包集的目录结构如下:

1
2
3
4
5
6
7
8
SRBundle-batch/
├── SC_v1.0.0.tar.gz
├── SE_v2.0.0.tar.gz
├── acl_v3.0.0.tar.gz
├── install.sh
├── uninstall.sh
└── batch.json

batch.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"components": [
{
"name": "SC_v1.0.0.tar.gz",
"checksum": {
"v1": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
},
},
{
"name": "SE_v2.0.0.tar.gz",
"checksum": {
"v1": "abcde1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
},
},
{
"name": "acl_v3.0.0.tar.gz",
"checksum": {
"v1": "7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456"
},
}
],
"proto_version": "1",
}

导入规则

导入组件包集时,系统会首先读取 batch.json 文件并校验每个组件包的完整性。校验通过后,系统会开始导入每个组件包,执行相应的导入逻辑。导入过程如下:

  • 解压组件包集文件
  • 读取并解析 batch.json 文件,校验每个组件包的完整性。
  • 校验通过后,依次导入每个组件包,执行相应的导入逻辑。

问题

升级兼容问题

1.5 总控如何适配新构建的组件包和组件包集

  • 中间版本方案?
    符合现有逻辑,有实现的可行性。决定做,现在开始开发中间版本,至少需要兼容新的导包逻辑,正常展示组件的交互接口

  • 不兼容直接重装
    如果有些存在服务彻底不兼容的情况,那就不做兼容逻辑。

组件包和组件包集信息遗漏

补充信息

  • 发布方信息
  • 发布时间

工具

组件包安装脚本

1
2
3
4
5
# 用户解压组件包
#tar zxvf SC_v1.0.0.tar.gz

./install.sh

组件包集安装脚本

1
2
3
4
# 用户解压组件包集
# tar zxvf 20240618-SRBundle-batch.tar.gz
./install.sh

构建工具(打包平台使用)

生成 meta.json 后续可再开发或内嵌到打包平台中自动完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/bin/bash

# Function to prompt user for input
prompt() {
local var_name=$1
local prompt_text=$2
local default_value=$3
read -p "$prompt_text" input
eval $var_name="'${input:-$default_value}'"
}

# Prompt for required fields
prompt name "输入组件名 component name (required): "
prompt type "输入组件类型 component type (required) [sc/engine/plugin/agent]: "
prompt version "输入组件版本 the component version (required): "
prompt checksum "输出组件 md5 the checksum (MD5) (required): "

# Start building the JSON structure
json_content="{\n"
json_content+=" \"name\": \"$name\",\n"
json_content+=" \"version\": \"$version\",\n"
json_content+=" \"type\": \"$type\",\n"
json_content+=" \"checksum\": {\n"
json_content+=" \"v1\": \"$checksum\"\n"
json_content+=" }"

# Prompt for optional fields
read -p "新增 description? (y/n): " add_description
if [[ $add_description == "y" ]]; then
prompt description "Enter the description: "
json_content+=",\n \"description\": \"$description\""
fi

# Prompt for dependencies
read -p "新增 dependencies? (y/n): " add_dependencies
if [[ $add_dependencies == "y" ]]; then
dependencies="[]"
while : ; do
read -p "Add a dependency? (y/n): " add_dependency
if [[ $add_dependency != "y" ]]; then
break
fi
prompt dep_name "Enter the dependency name: "
prompt dep_type "Enter the dependency type [sc/engine/plugin/agent]: "
prompt dep_compatible_versions "输入 compatible versions: "
prompt dep_incompatible_versions "输入 incompatible versions (使用 , 隔开 如:>=1.5.1, <2.0.0): "
prompt dep_description "输入依赖描述 :"

# Add the dependency to the dependencies array
dependency="{\"name\": \"$dep_name\", \"type\": \"$dep_type\", \"compatibleVersions\": \"$dep_compatible_versions\", \"incompatibleVersions\": [$(echo $dep_incompatible_versions | sed 's/,/","/g' | sed 's/^/"/' | sed 's/$/"/')], \"description\": \"$dep_description\"}"
if [[ $dependencies == "[]" ]]; then
dependencies="[$dependency]"
else
dependencies=$(echo $dependencies | sed 's/]$/,'"${dependency}"']/')
fi
done
json_content+=",\n \"dependencies\": $dependencies"
fi

json_content+=",\n \"protoVersion\": 1\n"

# Prompt for changelog content
echo "请输入 changelog 内容 (结束输入使用 Ctrl+D):"
changelog=$(</dev/stdin)
json_content+=",\n \"changelog\": \"$changelog\"\n"

json_content+="}"

echo -e $json_content > meta.json

echo "meta.json has been created with the provided information."

生成 batch.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash

calculate_checksum() {
local file=$1
md5sum "$file" | awk '{ print $1 }'
}

json_content="{\n"
json_content+=" \"components\": [\n"

# 找到 tar.gz 后缀文件
first=true
for file in *.tar.gz; do
if [[ -f "$file" ]]; then
# Calculate the checksum
checksum=$(calculate_checksum "$file")

if $first; then
first=false
else
json_content+=",\n"
fi

json_content+=" {\n"
json_content+=" \"name\": \"$file\",\n"
json_content+=" \"checksum\": {\n"
json_content+=" \"v1\": \"$checksum\"\n"
json_content+=" }\n"
json_content+=" }"
fi
done

json_content+="\n ],\n"
json_content+=" \"protoVersion\": 1\n"
json_content+="}"

echo -e $json_content > batch.json

echo "batch.json has been created with the checksums of all .tar.gz files in the current directory."

导入接口 golang-kit

解析 meta.json 文件内容,校验包完整性后,再保存基本信息,版本更新日志,依赖关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package main

import (
"archive/tar"
"compress/gzip"
"encoding/json"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"

_ "github.com/go-sql-driver/mysql"
"xorm.io/xorm"
)

// ComponentBall represents the structure of the meta.json file and changelog.md combined
type ComponentBall struct {
ID int64 `xorm:"pk autoincr" json:"id"`
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
Type string `json:"type"`
Checksum string `json:"checksum"`
Dependencies string `json:"dependencies,omitempty"` // Store dependencies as JSON string
ProtoVersion int `json:"protoVersion"`
Changelog string `xorm:"TEXT" json:"changelog"`
DependenciesList []Dependency `json:"dependenciesList" xorm:"-"`
}

// Meta represents the structure of the meta.json file
type Meta struct {
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
Type string `json:"type"`
Checksum Checksum `json:"checksum"`
Dependencies []Dependency `json:"dependencies,omitempty"`
ProtoVersion int `json:"protoVersion"`
Changelog string `json:"changelog"`
}

// Checksum represents the checksum structure
type Checksum struct {
V1 string `json:"v1"`
}

// Dependency represents a dependency in the meta.json file
type Dependency struct {
Name string `json:"name"`
Type string `json:"type"`
CompatibleVersions string `json:"compatibleVersions"`
IncompatibleVersions []string `json:"incompatibleVersions"`
Description string `json:"description"`
}

// validateChecksum validates the checksum of the extracted files
func validateChecksum(extractedDir string, expectedChecksum string) bool {
cmd := exec.Command("bash", "-c", "find . -type f ! -name 'meta.json' -exec md5sum {} + | awk '{print $1}' | md5sum")
cmd.Dir = extractedDir
output, err := cmd.Output()
if err != nil {
log.Printf("Error executing checksum validation command: %v", err)
return false
}

actualChecksumLs := strings.Fields(string(output))
if len(actualChecksumLs) < 2 {
log.Println("Error executing command:", err)
}
actualChecksum := actualChecksumLs[0]
return actualChecksum == expectedChecksum
}

// extractTarGz extracts a tar.gz file to a specified directory
func extractTarGz(gzipStream io.Reader, dst string) error {
uncompressedStream, err := gzip.NewReader(gzipStream)
if err != nil {
return err
}
defer uncompressedStream.Close()

tarReader := tar.NewReader(uncompressedStream)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}

target := filepath.Join(dst, header.Name)
switch header.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil {
return err
}
case tar.TypeReg:
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return err
}
if _, err := io.Copy(f, tarReader); err != nil {
return err
}
f.Close()
}
}
return nil
}

// saveComponent saves the parsed component information to the database
func saveComponent(engine *xorm.Engine, component ComponentBall) error {
session := engine.NewSession()
defer session.Close()

err := session.Begin()
if err != nil {
return err
}

_, err = session.Insert(&component)
if err != nil {
session.Rollback()
return err
}

return session.Commit()
}

func ImportComponent() error {
packageFile := "SC_v1.5.15.linux-x86_64.tar.gz"
extractedDir := "SC_v1.5.15.linux-x86_64"
err := os.MkdirAll(extractedDir, os.ModePerm)
if err != nil {
return err
}

file, err := os.Open(packageFile)
if err != nil {
log.Fatalf("Error opening package file: %v", err)
return err
}
defer file.Close()

err = extractTarGz(file, extractedDir)
if err != nil {
log.Fatalf("Error extracting package file: %v", err)
return err
}

metaFile := filepath.Join(extractedDir, "meta.json")
metaData, err := ioutil.ReadFile(metaFile)
if err != nil {
log.Fatalf("Error reading meta.json: %v", err)
return err
}

// Parse meta.json content
var meta Meta
err = json.Unmarshal(metaData, &meta)
if err != nil {
log.Fatalf("Error parsing meta.json: %v", err)
return err
}

// Validate package integrity
if !validateChecksum(extractedDir, meta.Checksum.V1) {
log.Fatalf("Package integrity validation failed for %s", packageFile)
return err
}

// Convert dependencies to JSON string for storage
dependenciesJSON, err := json.Marshal(meta.Dependencies)
if err != nil {
log.Fatalf("Error marshaling dependencies: %v", err)
return err
}

// Create component struct
component := ComponentBall{
Name: meta.Name,
Version: meta.Version,
Description: meta.Description,
Type: meta.Type,
Checksum: meta.Checksum.V1,
Dependencies: string(dependenciesJSON),
ProtoVersion: meta.ProtoVersion,
Changelog: meta.Changelog,
DependenciesList: meta.Dependencies,
}

// Connect to the database
dbURL := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8&parseTime=True"
engine, err := xorm.NewEngine("mysql", dbURL)
if err != nil {
log.Fatalf("Error connecting to the database: %v", err)
return err
}

// Save component information to the database
err = saveComponent(engine, component)
if err != nil {
log.Fatalf("Error saving component information to the database: %v", err)
return err
}

log.Println("Parsed component information saved to the database successfully.")
return nil
}

func main() {
err := ImportComponent()
if err != nil {
log.Fatalf("Error importing component: %v", err)
}
}



总控、引擎、插件组件包定义.md
https://abrance.github.io/2024/06/18/mdstorage/project/sr/基础设施相关/总控、引擎、插件组件包定义/
Author
xiaoy
Posted on
June 18, 2024
Licensed under