├── .gitignore
├── Makefile
├── README.md
├── build
├── build.sh
├── cmd
├── migrate
│ └── main.go
└── server
│ └── main.go
├── config
├── config-prod.toml
└── config.toml
├── docs
├── docs.go
├── swagger.json
└── swagger.yaml
├── go-ops.service
├── go.mod
├── go.sum
├── imgs
├── vm1.png
└── vm2.png
├── internal
├── api
│ ├── message
│ │ └── README.md
│ ├── multi_cloud
│ │ ├── README.md
│ │ ├── account.go
│ │ ├── instance.go
│ │ └── template.go
│ ├── nginx
│ │ └── README.md
│ └── scheduler
│ │ └── dag.go
├── app
│ └── server.go
├── cron
│ └── index.go
├── db
│ ├── db.go
│ └── pagination.go
├── models
│ ├── base.go
│ ├── multi_cloud
│ │ ├── README.md
│ │ ├── account.go
│ │ ├── instance.go
│ │ └── template.go
│ └── scheduler
│ │ ├── dag.go
│ │ └── task_instance.go
├── redis
│ └── index.go
├── router
│ ├── middleware
│ │ ├── auth.go
│ │ ├── cors.go
│ │ ├── life_cycle.go
│ │ └── rid.go
│ └── router.go
└── service
│ └── scheduler
│ └── serializer.go
├── pkg
├── multi_cloud_sdk
│ ├── README.md
│ ├── ali.go
│ ├── aws.go
│ ├── core.go
│ ├── gmap.go
│ ├── hw.go
│ └── ten.go
├── request
│ └── request.go
├── tools
│ ├── convert.go
│ ├── hash.go
│ ├── msg.go
│ └── print.go
└── 三方调用
└── scripts
├── cron
└── cron_sync_instance.go
└── 业务脚本
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/go,vim,linux,macos,emacs,pycharm,windows,sublimetext
3 | # Edit at https://www.gitignore.io/?templates=go,vim,linux,macos,emacs,pycharm,windows,sublimetext
4 |
5 | ### Emacs ###
6 | # -*- mode: gitignore; -*-
7 | *~
8 | \#*\#
9 | /.emacs.desktop
10 | /.emacs.desktop.lock
11 | *.elc
12 | auto-save-list
13 | tramp
14 | .\#*
15 |
16 | # Org-mode
17 | .org-id-locations
18 | *_archive
19 |
20 | # flymake-mode
21 | *_flymake.*
22 |
23 | # eshell files
24 | /eshell/history
25 | /eshell/lastdir
26 |
27 | # elpa packages
28 | /elpa/
29 |
30 | # reftex files
31 | *.rel
32 |
33 | # AUCTeX auto folder
34 | /auto/
35 |
36 | # cask packages
37 | .cask/
38 | dist/
39 |
40 | # Flycheck
41 | flycheck_*.el
42 |
43 | # server auth directory
44 | /server/
45 |
46 | # projectiles files
47 | .projectile
48 |
49 | # directory configuration
50 | .dir-locals.el
51 |
52 | # network security
53 | /network-security.data
54 |
55 |
56 | ### Go ###
57 | # Binaries for programs and plugins
58 | *.exe
59 | *.exe~
60 | *.dll
61 | *.so
62 | *.dylib
63 |
64 | # Test binary, built with `go test -c`
65 | *.test
66 |
67 | # Output of the go coverage tool, specifically when used with LiteIDE
68 | *.out
69 |
70 | # Dependency directories (remove the comment below to include it)
71 | # vendor/
72 |
73 | ### Go Patch ###
74 | /vendor/
75 | /Godeps/
76 |
77 | ### Linux ###
78 |
79 | # temporary files which can be created if a process still has a handle open of a deleted file
80 | .fuse_hidden*
81 |
82 | # KDE directory preferences
83 | .directory
84 |
85 | # Linux trash folder which might appear on any partition or disk
86 | .Trash-*
87 |
88 | # .nfs files are created when an open file is removed but is still being accessed
89 | .nfs*
90 |
91 | ### macOS ###
92 | # General
93 | .DS_Store
94 | .AppleDouble
95 | .LSOverride
96 |
97 | # Icon must end with two \r
98 | Icon
99 |
100 | # Thumbnails
101 | ._*
102 |
103 | # Files that might appear in the root of a volume
104 | .DocumentRevisions-V100
105 | .fseventsd
106 | .Spotlight-V100
107 | .TemporaryItems
108 | .Trashes
109 | .VolumeIcon.icns
110 | .com.apple.timemachine.donotpresent
111 |
112 | # Directories potentially created on remote AFP share
113 | .AppleDB
114 | .AppleDesktop
115 | Network Trash Folder
116 | Temporary Items
117 | .apdisk
118 |
119 | ### PyCharm ###
120 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
121 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
122 |
123 | # User-specific stuff
124 | .idea/**/workspace.xml
125 | .idea/**/tasks.xml
126 | .idea/**/usage.statistics.xml
127 | .idea/**/dictionaries
128 | .idea/**/shelf
129 |
130 | # Generated files
131 | .idea/**/contentModel.xml
132 |
133 | # Sensitive or high-churn files
134 | .idea/**/dataSources/
135 | .idea/**/dataSources.ids
136 | .idea/**/dataSources.local.xml
137 | .idea/**/sqlDataSources.xml
138 | .idea/**/dynamic.xml
139 | .idea/**/uiDesigner.xml
140 | .idea/**/dbnavigator.xml
141 |
142 | # Gradle
143 | .idea/**/gradle.xml
144 | .idea/**/libraries
145 |
146 | # Gradle and Maven with auto-import
147 | # When using Gradle or Maven with auto-import, you should exclude module files,
148 | # since they will be recreated, and may cause churn. Uncomment if using
149 | # auto-import.
150 | # .idea/modules.xml
151 | # .idea/*.iml
152 | # .idea/modules
153 | # *.iml
154 | # *.ipr
155 |
156 | # CMake
157 | cmake-build-*/
158 |
159 | # Mongo Explorer plugin
160 | .idea/**/mongoSettings.xml
161 |
162 | # File-based project format
163 | *.iws
164 |
165 | # IntelliJ
166 | out/
167 |
168 | # mpeltonen/sbt-idea plugin
169 | .idea_modules/
170 |
171 | # JIRA plugin
172 | atlassian-ide-plugin.xml
173 |
174 | # Cursive Clojure plugin
175 | .idea/replstate.xml
176 |
177 | # Crashlytics plugin (for Android Studio and IntelliJ)
178 | com_crashlytics_export_strings.xml
179 | crashlytics.properties
180 | crashlytics-build.properties
181 | fabric.properties
182 |
183 | # Editor-based Rest Client
184 | .idea/httpRequests
185 |
186 | # Android studio 3.1+ serialized cache file
187 | .idea/caches/build_file_checksums.ser
188 |
189 | ### PyCharm Patch ###
190 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
191 |
192 | # *.iml
193 | # modules.xml
194 | # .idea/misc.xml
195 | # *.ipr
196 |
197 | # Sonarlint plugin
198 | .idea/**/sonarlint/
199 |
200 | # SonarQube Plugin
201 | .idea/**/sonarIssues.xml
202 |
203 | # Markdown Navigator plugin
204 | .idea/**/markdown-navigator.xml
205 | .idea/**/markdown-navigator/
206 |
207 | ### SublimeText ###
208 | # Cache files for Sublime Text
209 | *.tmlanguage.cache
210 | *.tmPreferences.cache
211 | *.stTheme.cache
212 |
213 | # Workspace files are user-specific
214 | *.sublime-workspace
215 |
216 | # Project files should be checked into the repository, unless a significant
217 | # proportion of contributors will probably not be using Sublime Text
218 | # *.sublime-project
219 |
220 | # SFTP configuration file
221 | sftp-config.json
222 |
223 | # Package control specific files
224 | Package Control.last-run
225 | Package Control.ca-list
226 | Package Control.ca-bundle
227 | Package Control.system-ca-bundle
228 | Package Control.cache/
229 | Package Control.ca-certs/
230 | Package Control.merged-ca-bundle
231 | Package Control.user-ca-bundle
232 | oscrypto-ca-bundle.crt
233 | bh_unicode_properties.cache
234 |
235 | # Sublime-github package stores a github token in this file
236 | # https://packagecontrol.io/packages/sublime-github
237 | GitHub.sublime-settings
238 |
239 | ### Vim ###
240 | # Swap
241 | [._]*.s[a-v][a-z]
242 | [._]*.sw[a-p]
243 | [._]s[a-rt-v][a-z]
244 | [._]ss[a-gi-z]
245 | [._]sw[a-p]
246 |
247 | # Session
248 | Session.vim
249 | Sessionx.vim
250 |
251 | # Temporary
252 | .netrwhist
253 |
254 | # Auto-generated tag files
255 | tags
256 |
257 | # Persistent undo
258 | [._]*.un~
259 |
260 | # Coc configuration directory
261 | .vim
262 |
263 | ### Windows ###
264 | # Windows thumbnail cache files
265 | Thumbs.db
266 | Thumbs.db:encryptable
267 | ehthumbs.db
268 | ehthumbs_vista.db
269 |
270 | # Dump file
271 | *.stackdump
272 |
273 | # Folder config file
274 | [Dd]esktop.ini
275 |
276 | # Recycle Bin used on file shares
277 | $RECYCLE.BIN/
278 |
279 | # Windows Installer files
280 | *.cab
281 | *.msi
282 | *.msix
283 | *.msm
284 | *.msp
285 |
286 | # Windows shortcuts
287 | *.lnk
288 |
289 | # End of https://www.gitignore.io/api/go,vim,linux,macos,emacs,pycharm,windows,sublimetext
290 |
291 |
292 | .idea
293 | __pycache__
294 | .venv
295 | logs/*
296 | celerybeat-schedule.db
297 | celerybeat-schedule
298 | celerybeat.pid
299 | ./pkg/
300 | ./tools/*
301 | ./tests/*
302 | data/*
303 | tests/*
304 | .idea/workspace.xml
305 | bin/*
306 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | run:
2 | @go run cmd/server/main.go --config=config/config.toml
3 |
4 | run-prod:
5 | @go run cmd/server/main.go --config=config/config-prod.toml
6 |
7 | migrate:
8 | @go run cmd/migrate/main.go --config=config/config.toml
9 |
10 | migrate-prod:
11 | @go run cmd/migrate/main.go --config=config/config-prod.toml
12 |
13 | swagger:
14 | @swag init -g cmd/server/main.go
15 |
16 | build-server-linux:
17 | @CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/lightning-go cmd/server/main.go
18 |
19 | build-windows:
20 | @CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/lightning-go cmd/server/main.go
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lightning-go
2 |
3 | ## 环境依赖
4 |
5 | ### 版本依赖
6 |
7 | - Go 1.14+
8 | - MySQL 5.7+
9 | - Redis 3.2+
10 |
11 |
12 | ## 模块介绍
13 | - [x] 多云云主机生命周期管理
14 | - [x] 阿里云
15 | - [x] 腾讯云
16 | - [ ] 华为云
17 | - [ ] 亚马逊
18 | - [ ] 青云
19 | - [ ] 消息中心
20 | - [ ] 邮件
21 | - [ ] 短信
22 | - [ ] 阿里云短信
23 | - [ ] 腾讯云短信
24 | - [ ] 电话
25 | - [ ] 阿里云语音电话
26 | - [ ] 腾讯云语音电话
27 | - [ ] 社交工具
28 | - [ ] 钉钉
29 | - [ ] 微信
30 | - [X] 任务调度
31 | - [X] 触发Dag任务
32 | - [X] 查询Dag详情
33 | - [ ] 查看任务日志
34 | - [ ] 定时任务
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | ## 项目目录结构
43 |
44 | ```bash
45 | ├── README.md
46 | ├── build.sh
47 | ├── cmd # 项目入口
48 | │ ├── migrate # 创建表
49 | │ └── server # 启动服务
50 | ├── config # 配置文件
51 | │ ├── config.toml # 配置文件
52 | ├── go-ops.service # 服务systemd文件
53 | ├── go.mod
54 | ├── go.sum
55 | ├── internal # 内部依赖
56 | │ ├── app # 应用
57 | │ ├── cron # 定时任务
58 | │ ├── db # 数据库
59 | │ └── http # http
60 | ├── logs # 日志目录
61 | ├── pkg
62 | ├── scripts
63 | └── test # 测试用例
64 | ```
65 |
66 | ## 部署
67 |
68 | - 克隆代码
69 | ```bash
70 | $ git clone git@github.com:zhengyansheng/lightning-go.git
71 | ```
72 |
73 | ```bash
74 | $ cd lightning-go
75 | $ go mod init lightning-go
76 |
77 | ```
78 |
79 | - 同步数据库
80 | ```bash
81 | # make migrate
82 | ```
83 |
84 | - 启动服务
85 | ```bash
86 | # make run
87 | ```
88 |
89 | - 生成 api docs
90 | ```bash
91 | # make swagger
92 | ```
93 |
94 | ## 定时任务
95 |
96 | - 同步云主机元数据
97 | - 同步新增
98 | - 变更
99 | - 通知异常
100 |
101 | ```bash
102 | $ go run scripts/cron/cron_sync_instance.go
103 | ```
104 |
--------------------------------------------------------------------------------
/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -xe
3 | LDFLAGS="-s -w"
4 | rm -rf output
5 | mkdir -p output
6 | TMP='/tmp'
7 | VERSION='1.0.0'
8 |
9 | function buildProduct() {
10 | echo "build go-ops beging"
11 | git pull
12 | go build -ldflags "$LDFLAGS" -o go-ops cmd/server/main.go
13 | mv kjcloud output
14 | systemctl restart go-ops
15 | }
16 |
17 | function _help() {
18 | echo "Welcome to go-ops build system"
19 | }
20 | function main() {
21 |
22 | _help
23 | select ch in "go-ops";
24 | do
25 | case $ch in
26 | "go-ops" ) buildProduct
27 | break;;
28 |
29 | *) echo "please choose a number"
30 | break;;
31 | esac
32 | done
33 | }
34 | main
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -xe
3 | LDFLAGS="-s -w"
4 | rm -rf output
5 | mkdir -p output
6 | TMP='/tmp'
7 | VERSION='1.0.0'
8 |
9 | function buildProduct() {
10 | echo "build go-ops beging"
11 | git pull
12 | go build -ldflags "$LDFLAGS" -o go-ops cmd/server/main.go
13 | mv kjcloud output
14 | systemctl restart go-ops
15 | }
16 |
17 | function _help() {
18 | echo "Welcome to go-ops build system"
19 | }
20 | function main() {
21 |
22 | _help
23 | select ch in "go-ops";
24 | do
25 | case $ch in
26 | "go-ops" ) buildProduct
27 | break;;
28 |
29 | *) echo "please choose a number"
30 | break;;
31 | esac
32 | done
33 | }
34 | main
--------------------------------------------------------------------------------
/cmd/migrate/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/douyu/jupiter"
7 | "github.com/douyu/jupiter/pkg/conf"
8 | _ "github.com/go-sql-driver/mysql"
9 | "github.com/jinzhu/gorm"
10 |
11 | "lightning-go/internal/db"
12 | "lightning-go/internal/models/multi_cloud"
13 | )
14 |
15 | const RECREATE = true
16 |
17 | // Admin indicates the user is a system administrator.
18 | type InstallAPP struct {
19 | jupiter.Application
20 | }
21 |
22 | func main() {
23 | eng := &InstallAPP{}
24 | _ = eng.Startup(
25 | eng.initApp,
26 | )
27 | initDB()
28 | db.Init()
29 |
30 | }
31 |
32 | func initDB() error {
33 | var dbName = "chaos"
34 | gormdb, err := gorm.Open(
35 | "mysql",
36 | conf.GetString("jupiter.mysql.master.dsn"),
37 | )
38 |
39 | if err != nil {
40 | return err
41 | }
42 | if RECREATE {
43 | var result []struct {
44 | Sqlstr string
45 | }
46 | gormdb.Debug().Raw("SELECT concat('DROP TABLE IF EXISTS `', table_name, '`;') as sqlstr FROM information_schema.tables WHERE table_schema = '" + dbName + "'").Scan(&result)
47 | for _, v := range result {
48 | fmt.Println(`sql drop:`, v.Sqlstr)
49 |
50 | gormdb.Exec(v.Sqlstr)
51 | }
52 | }
53 |
54 | defer func() {
55 | _ = gormdb.Close()
56 | }()
57 |
58 | models := []interface{}{
59 | &multi_cloud.CloudTemplate{},
60 | &multi_cloud.Account{},
61 | &multi_cloud.InstanceLifeCycle{},
62 | }
63 | if RECREATE {
64 | gormdb.DropTableIfExists(models...)
65 | }
66 | //
67 |
68 | // 删除原来的表
69 | gormdb.SingularTable(true)
70 | gormdb.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(models...)
71 | fmt.Println("create table ok")
72 | return nil
73 |
74 | }
75 |
76 | func (eng *InstallAPP) initApp() error {
77 | return nil
78 | }
79 |
--------------------------------------------------------------------------------
/cmd/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/douyu/jupiter/pkg/xlog"
5 | "lightning-go/internal/app"
6 | )
7 |
8 | // @title lightning-go api
9 | // @version 1.0
10 | // @description lightning-go api
11 | // @termsOfService https://github.com/zhengyansheng/lightning-go
12 | // @license.name MIT
13 | // @license.url https://github.com/zhengyansheng/lightning-go
14 |
15 | func main() {
16 |
17 | eng := app.NewEngine()
18 | if err := eng.Run(); err != nil {
19 | xlog.Error("service could not run", xlog.FieldErr(err))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/config/config-prod.toml:
--------------------------------------------------------------------------------
1 | [jupiter.server.http]
2 | host = "127.0.0.1"
3 | port = 9900
4 |
5 | [jupiter.logger.default]
6 | enableConsole = true
7 | level = "debug"
8 | debug = true
9 | MaxAge = 2
10 | MaxBackup = 10
11 | Dir = "./logs/"
12 | Async = false
13 |
14 | [jupiter.mysql.master]
15 | connMaxLifetime = "300s"
16 | debug = true
17 | dsn = "root:123456@tcp(127.0.0.1:3306)/lightning-go?charset=utf8&parseTime=True&loc=Local&readTimeout=5s&timeout=5s&writeTimeout=3s"
18 | level = "panic"
19 | maxIdleConns = 50
20 | maxOpenConns = 100
21 |
22 | [jupiter.mysql.airflow]
23 | connMaxLifetime = "300s"
24 | debug = true
25 | dsn = "root:123456@tcp(127.0.0.1:3306)/airflow?charset=utf8&parseTime=True&loc=Local&readTimeout=5s&timeout=5s&writeTimeout=3s"
26 | level = "panic"
27 | maxIdleConns = 50
28 | maxOpenConns = 100
29 |
30 |
31 | [jupiter.cron.chaos]
32 | withSeconds = true
33 | immediatelyRun = false
34 | concurrentDelay = -1
35 |
36 |
37 | [jupiter.server.governor]
38 | enable = false
39 | host = "0.0.0.0"
40 | port = 9800
41 |
42 |
43 | [go-ops.hosts]
44 | auth = "http://121.4.224.236:8000/api/v1/has-perm"
45 |
46 | [go-ops.scheduler]
47 | airflowUrl = "http://121.4.224.236:8888"
48 | airflowUserName = "admin"
49 | airflowUserPassword = "zhengyansheng"
50 |
--------------------------------------------------------------------------------
/config/config.toml:
--------------------------------------------------------------------------------
1 | [jupiter.server.http]
2 | host = "127.0.0.1"
3 | port = 9900
4 |
5 | [jupiter.logger.default]
6 | enableConsole = true
7 | level = "debug"
8 | debug = true
9 | MaxAge = 2
10 | MaxBackup = 10
11 | Dir = "./log/"
12 | Async = false
13 |
14 | [jupiter.mysql.master]
15 | connMaxLifetime = "300s"
16 | debug = true
17 | dsn = "root:12345678@tcp(192.168.3.53:3306)/lightning-go?charset=utf8&parseTime=True&loc=Local&readTimeout=5s&timeout=5s&writeTimeout=3s"
18 | level = "panic"
19 | maxIdleConns = 50
20 | maxOpenConns = 100
21 |
22 | [jupiter.mysql.airflow]
23 | connMaxLifetime = "300s"
24 | debug = true
25 | dsn = "zhengyansheng:zhengyansheng@2021@tcp(www.aiops724.com:3306)/airflow?charset=utf8&parseTime=True&loc=Local&readTimeout=5s&timeout=5s&writeTimeout=3s"
26 | level = "panic"
27 | maxIdleConns = 50
28 | maxOpenConns = 100
29 |
30 |
31 | [jupiter.cron.chaos]
32 | withSeconds = true
33 | immediatelyRun = false
34 | concurrentDelay = -1
35 |
36 |
37 | [jupiter.server.governor]
38 | enable = false
39 | host = "0.0.0.0"
40 | port = 9800
41 |
42 |
43 | [go-ops.hosts]
44 | auth = "http://121.4.224.236:8000/api/v1/has-perm"
45 |
46 | [go-ops.scheduler]
47 | airflowUrl = "http://121.4.224.236:8888"
48 | airflowUserName = "admin"
49 | airflowUserPassword = "zhengyansheng"
50 |
--------------------------------------------------------------------------------
/docs/docs.go:
--------------------------------------------------------------------------------
1 | // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
2 | // This file was generated by swaggo/swag
3 |
4 | package docs
5 |
6 | import (
7 | "bytes"
8 | "encoding/json"
9 | "strings"
10 |
11 | "github.com/alecthomas/template"
12 | "github.com/swaggo/swag"
13 | )
14 |
15 | var doc = `{
16 | "schemes": {{ marshal .Schemes }},
17 | "swagger": "2.0",
18 | "info": {
19 | "description": "{{.Description}}",
20 | "title": "{{.Title}}",
21 | "termsOfService": "https://github.com/zhengyansheng/lightning-go",
22 | "contact": {},
23 | "license": {
24 | "name": "MIT",
25 | "url": "https://github.com/zhengyansheng/lightning-go"
26 | },
27 | "version": "{{.Version}}"
28 | },
29 | "host": "{{.Host}}",
30 | "basePath": "{{.BasePath}}",
31 | "paths": {}
32 | }`
33 |
34 | type swaggerInfo struct {
35 | Version string
36 | Host string
37 | BasePath string
38 | Schemes []string
39 | Title string
40 | Description string
41 | }
42 |
43 | // SwaggerInfo holds exported Swagger Info so clients can modify it
44 | var SwaggerInfo = swaggerInfo{
45 | Version: "1.0",
46 | Host: "",
47 | BasePath: "",
48 | Schemes: []string{},
49 | Title: "lightning-go api",
50 | Description: "lightning-go api",
51 | }
52 |
53 | type s struct{}
54 |
55 | func (s *s) ReadDoc() string {
56 | sInfo := SwaggerInfo
57 | sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
58 |
59 | t, err := template.New("swagger_info").Funcs(template.FuncMap{
60 | "marshal": func(v interface{}) string {
61 | a, _ := json.Marshal(v)
62 | return string(a)
63 | },
64 | }).Parse(doc)
65 | if err != nil {
66 | return doc
67 | }
68 |
69 | var tpl bytes.Buffer
70 | if err := t.Execute(&tpl, sInfo); err != nil {
71 | return doc
72 | }
73 |
74 | return tpl.String()
75 | }
76 |
77 | func init() {
78 | swag.Register(swag.Name, &s{})
79 | }
80 |
--------------------------------------------------------------------------------
/docs/swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "description": "lightning-go api",
5 | "title": "lightning-go api",
6 | "termsOfService": "https://github.com/zhengyansheng/lightning-go",
7 | "contact": {},
8 | "license": {
9 | "name": "MIT",
10 | "url": "https://github.com/zhengyansheng/lightning-go"
11 | },
12 | "version": "1.0"
13 | },
14 | "paths": {}
15 | }
--------------------------------------------------------------------------------
/docs/swagger.yaml:
--------------------------------------------------------------------------------
1 | info:
2 | contact: {}
3 | description: lightning-go api
4 | license:
5 | name: MIT
6 | url: https://github.com/zhengyansheng/lightning-go
7 | termsOfService: https://github.com/zhengyansheng/lightning-go
8 | title: lightning-go api
9 | version: "1.0"
10 | paths: {}
11 | swagger: "2.0"
12 |
--------------------------------------------------------------------------------
/go-ops.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description= Go-OPS Service.
3 | ConditionFileIsExecutable=/data/go-ops/output/kjcloud
4 |
5 | [Service]
6 | StartLimitInterval=5
7 | StartLimitBurst=10
8 | ExecStart=/data/go-ops/output/go-ops --config=/data/go-ops/config/server.toml
9 | StandardOutput=file:/data/go-ops/log/tapi.log
10 |
11 | Restart=always
12 | RestartSec=120
13 | EnvironmentFile=-/etc/sysconfig/TPApi
14 |
15 | [Install]
16 | WantedBy=multi-user.target
17 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module lightning-go
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
7 | github.com/alibabacloud-go/darabonba-openapi v0.1.5
8 | github.com/alibabacloud-go/ecs-20140526/v2 v2.0.2
9 | github.com/alibabacloud-go/tea v1.1.15
10 | github.com/douyu/jupiter v0.2.9
11 | github.com/fatih/structs v1.1.0
12 | github.com/gin-gonic/gin v1.6.3
13 | github.com/go-openapi/spec v0.20.3 // indirect
14 | github.com/go-openapi/swag v0.19.15 // indirect
15 | github.com/go-resty/resty/v2 v2.2.0
16 | github.com/go-sql-driver/mysql v1.5.0
17 | github.com/jinzhu/gorm v1.9.12
18 | github.com/mailru/easyjson v0.7.7 // indirect
19 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
20 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
21 | github.com/swaggo/gin-swagger v1.2.0
22 | github.com/swaggo/swag v1.7.0
23 | github.com/tencentcloud/tencentcloud-sdk-go v1.0.128
24 | github.com/tidwall/gjson v1.2.1
25 | golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c // indirect
26 | golang.org/x/sys v0.0.0-20210402192133-700132347e07 // indirect
27 | golang.org/x/text v0.3.6 // indirect
28 | golang.org/x/tools v0.1.0 // indirect
29 | )
30 |
--------------------------------------------------------------------------------
/imgs/vm1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengyansheng/lightning-go/4f66fb5cf55e4c989392a550f14df926b4be93d5/imgs/vm1.png
--------------------------------------------------------------------------------
/imgs/vm2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengyansheng/lightning-go/4f66fb5cf55e4c989392a550f14df926b4be93d5/imgs/vm2.png
--------------------------------------------------------------------------------
/internal/api/message/README.md:
--------------------------------------------------------------------------------
1 | # 消息中心
2 |
3 | > 消息通知:
4 | > 为其它API提供最底层的统一消息网关,比如 工单,监控,任务异常等
5 |
6 | ## 需求
7 |
8 | - 发送渠道类型
9 | - 发送内容及相关信息保存数据库,方便页面展示分析
10 | - API接口设计可统一,通过类型来区分不同的发送渠道,尽量让接口用起来更简单
11 | - 扩展: 发送内容,服务端支持模版格式选择
12 |
13 | ### 发送消息
14 | ```text
15 | POST /api/v1/message
16 | Header
17 | Content-Type: application/json
18 | Authorization: JWT xxxxx
19 |
20 | {
21 | "channel": "email/ short_letter/ phone/ ding/ wechat"
22 | "data": {
23 |
24 | }
25 | }
26 | ```
27 |
28 | 注意
29 | 1. 如果需要用到第三方ak/sk,需要本地建表存储。
30 |
31 | ### 查询消息
32 | ```text
33 | GET /api/v1/message
34 | Header
35 | Content-Type: application/json
36 | Authorization: JWT xxxxx
37 |
38 | {
39 | "code": 0,
40 | "message": nil,
41 | "data": []
42 | }
43 | ```
44 |
--------------------------------------------------------------------------------
/internal/api/multi_cloud/README.md:
--------------------------------------------------------------------------------
1 | # 多云管理
2 |
3 | ## 模版
4 |
5 | - 环境节点能够基本操作
6 | - 创建/删除模版,交付机器。
7 | - 非环境节点只能查看模版
8 | - 多展示AppKey字段用来区分
9 |
10 |
11 | ## 交付
12 |
13 | - 触发 Airflow Dag
14 |
15 | ```json
16 | {
17 | "data": {
18 | "account": "ali.lightning",
19 | "pay_type": "PostPaid",
20 | "region_id": "cn-beijing",
21 | "zone_id": "cn-beijing-c",
22 | "instance_type": "ecs.sn1.medium",
23 | "image_id": "centos_7_8_x64_20G_alibase_20200914.vhd",
24 | "vpc_id": "vpc-2ze80et76jwcsuc2asobq",
25 | "subnet_id": "vsw-2zelzctt7aougw3bhz5ev",
26 | "disks": [{
27 | "disk_type": "system",
28 | "disk_storage_type": "cloud",
29 | "disk_storage_size": 100
30 | }],
31 | "security_group_ids": ["sg-2zeb9n0qzygmjwn7hwae"],
32 | "hostname": "lightning-fe2.ops.prod.ali"
33 | }
34 | }
35 | ```
--------------------------------------------------------------------------------
/internal/api/multi_cloud/account.go:
--------------------------------------------------------------------------------
1 | package multi_cloud
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "lightning-go/internal/models/multi_cloud"
7 | "lightning-go/pkg/tools"
8 |
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | func CreateAccountView(c *gin.Context) {
13 | // Validate field
14 | var s multi_cloud.Account
15 | if err := c.ShouldBindJSON(&s); err != nil {
16 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
17 | return
18 | }
19 | // Create
20 | err := s.Create()
21 | if err != nil {
22 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
23 | return
24 | }
25 | // Response
26 | tools.JSONOk(c, "Create ok.")
27 | }
28 |
29 | func ListAccountView(c *gin.Context) {
30 | var s multi_cloud.Account
31 | // List
32 | accountsInfo, err := s.List()
33 | if err != nil {
34 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
35 | return
36 | }
37 | // Response
38 | tools.JSONOk(c, accountsInfo)
39 | }
40 |
41 | func DeleteAccountView(c *gin.Context) {
42 | var s multi_cloud.Account
43 | pk := c.Param("id")
44 | pkUint, _ := tools.StringToUint(pk)
45 | s.ID = pkUint
46 |
47 | // Delete
48 | err := s.Delete()
49 | if err != nil {
50 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
51 | return
52 | }
53 | // Response
54 | tools.JSONOk(c, "Delete ok.")
55 | }
56 |
57 | func UpdateAccountView(c *gin.Context) {
58 | var s multi_cloud.Account
59 | pk := c.Param("id")
60 | pkUint, _ := tools.StringToUint(pk)
61 | s.ID = pkUint
62 |
63 | // Update
64 | bytes, err := ioutil.ReadAll(c.Request.Body)
65 | if err != nil {
66 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
67 | return
68 | }
69 | data := make(map[string]interface{})
70 | err = json.Unmarshal(bytes, &data)
71 | if err != nil {
72 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
73 | return
74 | }
75 | err = s.Update(data)
76 | if err != nil {
77 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
78 | return
79 | }
80 | // Response
81 | tools.JSONOk(c, "", "Update ok.")
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/internal/api/multi_cloud/instance.go:
--------------------------------------------------------------------------------
1 | package multi_cloud
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "lightning-go/internal/models/multi_cloud"
7 | "lightning-go/pkg/multi_cloud_sdk"
8 | "lightning-go/pkg/tools"
9 | "reflect"
10 | )
11 |
12 | func CreateInstanceView(c *gin.Context) {
13 | // Validate field
14 | s := struct {
15 | Account string `json:"account" binding:"required"`
16 | PayType string `json:"pay_type" binding:"required"`
17 | RegionId string `json:"region_id" binding:"required"`
18 | ZoneId string `json:"zone_id" binding:"required"`
19 | InstanceType string `json:"instance_type" binding:"required"`
20 | ImageId string `json:"image_id" binding:"required"`
21 | VpcId string `json:"vpc_id" binding:"required"`
22 | SubnetId string `json:"subnet_id" binding:"required"`
23 | SecurityGroupIds []string `json:"security_group_ids" binding:"required"`
24 | Hostname string `json:"hostname" binding:"required"`
25 | DryRun bool `json:"dry_run" binding:"omitempty"`
26 | }{}
27 | if err := c.ShouldBindJSON(&s); err != nil {
28 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("ShouldBindJSON %v", err.Error()))
29 | return
30 | }
31 | // Factory CreateInstance
32 | tools.PrettyPrint(s)
33 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
34 | if err != nil {
35 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("NewFactoryByAccount %v", err.Error()))
36 | return
37 | }
38 | response, err := clt.CreateInstance(s.PayType, s.Hostname, s.InstanceType, s.ZoneId, s.ImageId, s.VpcId, s.SubnetId, s.SecurityGroupIds, s.DryRun)
39 | if err != nil {
40 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("CreateInstance %v", err.Error()))
41 | return
42 | }
43 | // Response
44 | tools.JSONOk(c, response)
45 | }
46 |
47 | func StartInstanceView(c *gin.Context) {
48 | // Validate field
49 | s := struct {
50 | Account string `json:"account" binding:"required"`
51 | RegionId string `json:"region_id" binding:"required"`
52 | InstanceId string `json:"instance_id" binding:"required"`
53 | }{}
54 | if err := c.ShouldBindJSON(&s); err != nil {
55 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
56 | return
57 | }
58 | // Factory StartInstance
59 | tools.PrettyPrint(s)
60 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
61 | if err != nil {
62 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
63 | return
64 | }
65 | response, err := clt.StartInstance(s.InstanceId)
66 | if err != nil {
67 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("StartInstance %v", err.Error()))
68 | return
69 | }
70 | // Response
71 | tools.JSONOk(c, response)
72 | }
73 |
74 | func StopInstanceView(c *gin.Context) {
75 | // Validate field
76 | s := struct {
77 | Account string `json:"account" binding:"required"`
78 | RegionId string `json:"region_id" binding:"required"`
79 | InstanceId string `json:"instance_id" binding:"required"`
80 | }{}
81 | if err := c.ShouldBindJSON(&s); err != nil {
82 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
83 | return
84 | }
85 | // Factory StopInstance
86 | tools.PrettyPrint(s)
87 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
88 | if err != nil {
89 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
90 | return
91 | }
92 | response, err := clt.StopInstance(s.InstanceId)
93 | if err != nil {
94 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("StopInstance %v", err.Error()))
95 | return
96 | }
97 | // Response
98 | tools.JSONOk(c, response)
99 | }
100 |
101 | func RebootInstanceView(c *gin.Context) {
102 | // Validate field
103 | s := struct {
104 | Account string `json:"account" binding:"required"`
105 | RegionId string `json:"region_id" binding:"required"`
106 | InstanceId string `json:"instance_id" binding:"required"`
107 | ForceStop bool `json:"force_stop"`
108 | }{}
109 | if err := c.ShouldBindJSON(&s); err != nil {
110 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
111 | return
112 | }
113 | // Factory RebootInstance
114 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
115 | if err != nil {
116 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
117 | return
118 | }
119 | response, err := clt.RebootInstance(s.InstanceId, s.ForceStop)
120 | if err != nil {
121 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("RebootInstance %v", err.Error()))
122 | return
123 | }
124 | // Response
125 | tools.JSONOk(c, response)
126 | }
127 |
128 | func ListInstancesView(c *gin.Context) {
129 | // Validate field
130 | s := struct {
131 | Account string `form:"account" binding:"required"`
132 | RegionId string `form:"region_id" binding:"required"`
133 | }{}
134 | if err := c.ShouldBind(&s); err != nil {
135 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
136 | return
137 | }
138 | // Factory ListInstances
139 | tools.PrettyPrint(s)
140 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
141 | if err != nil {
142 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
143 | return
144 | }
145 | response, err := clt.ListInstances()
146 | if err != nil {
147 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("ListInstances %v", err.Error()))
148 | return
149 | }
150 | // Response
151 | tools.JSONOk(c, response)
152 | }
153 |
154 | func InstanceDetailView(c *gin.Context) {
155 | // Validate field
156 | s := struct {
157 | Account string `form:"account" binding:"required"`
158 | RegionId string `form:"region_id" binding:"required"`
159 | }{}
160 | if err := c.ShouldBind(&s); err != nil {
161 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
162 | return
163 | }
164 | // Factory ListInstance
165 | tools.PrettyPrint(s)
166 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
167 | if err != nil {
168 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
169 | return
170 | }
171 | response, err := clt.ListInstance(c.Param("instance_id"))
172 | if err != nil {
173 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("ListInstance %v", err.Error()))
174 | return
175 | }
176 | // Response
177 | tools.JSONOk(c, response)
178 | }
179 |
180 | func ListRegionView(c *gin.Context) {
181 | // Validate field
182 | s := struct {
183 | Account string `form:"account" binding:"required"`
184 | RegionId string `form:"region_id" binding:"required"`
185 | }{}
186 | if err := c.ShouldBind(&s); err != nil {
187 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
188 | return
189 | }
190 | // Factory ListRegions
191 | tools.PrettyPrint(s)
192 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
193 | if err != nil {
194 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
195 | return
196 | }
197 | response, err := clt.ListRegions()
198 | if err != nil {
199 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("ListRegion %v", err.Error()))
200 | return
201 | }
202 | // Response
203 | tools.JSONOk(c, response)
204 | }
205 |
206 | func DestroyInstanceView(c *gin.Context) {
207 | // Validate field
208 | s := struct {
209 | Account string `json:"account" binding:"required"`
210 | RegionId string `json:"region_id" binding:"required"`
211 | InstanceId string `json:"instance_id" binding:"required"`
212 | ForceStop bool `json:"force_stop"`
213 | }{}
214 | if err := c.ShouldBindJSON(&s); err != nil {
215 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
216 | return
217 | }
218 | // Factory DestroyInstance
219 | tools.PrettyPrint(s)
220 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
221 | if err != nil {
222 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
223 | return
224 | }
225 | response, err := clt.DestroyInstance(s.InstanceId, s.ForceStop)
226 | if err != nil {
227 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("DestroyInstance %v", err.Error()))
228 | return
229 | }
230 | // Response
231 | tools.JSONOk(c, response)
232 | }
233 |
234 | func ModifyInstanceNameView(c *gin.Context) {
235 | // Validate field
236 | s := struct {
237 | Account string `json:"account" binding:"required"`
238 | RegionId string `json:"region_id" binding:"required"`
239 | InstanceId string `json:"instance_id" binding:"required"`
240 | InstanceName string `json:"instance_name" binding:"required"`
241 | }{}
242 | if err := c.ShouldBindJSON(&s); err != nil {
243 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
244 | return
245 | }
246 | // Factory ModifyInstanceName
247 | tools.PrettyPrint(s)
248 | clt, err := multi_cloud_sdk.NewFactoryByAccount(s.Account, s.RegionId)
249 | if err != nil {
250 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
251 | return
252 | }
253 | response, err := clt.ModifyInstanceName(s.InstanceId, s.InstanceName)
254 | if err != nil {
255 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("ModifyInstanceName %v", err.Error()))
256 | return
257 | }
258 | // Response
259 | tools.JSONOk(c, response)
260 | }
261 |
262 | func LifeCyclelView(c *gin.Context) {
263 | // Validate field
264 | var cycleArr = [](map[string]interface{}){}
265 | var cycle = multi_cloud.InstanceLifeCycle{}
266 | cycle.InstanceId = c.Param("instance_id")
267 |
268 | // List
269 | response, err := cycle.GetByInstanceId()
270 | if err != nil {
271 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
272 | return
273 | }
274 |
275 | // Response
276 | for _, v := range response {
277 | t := make(map[string]interface{})
278 | fmt.Println(v.CreatedAt, reflect.TypeOf(v.CreatedAt))
279 | formatDateTime := v.CreatedAt.Format("2006-01-02 15:04:05")
280 | t[formatDateTime] = v.Uri
281 | cycleArr = append(cycleArr, t)
282 | }
283 | tools.JSONOk(c, cycleArr)
284 | }
285 |
--------------------------------------------------------------------------------
/internal/api/multi_cloud/template.go:
--------------------------------------------------------------------------------
1 | package multi_cloud
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/gin-gonic/gin/binding"
6 | "lightning-go/internal/models/multi_cloud"
7 | "lightning-go/pkg/tools"
8 | )
9 |
10 | func CreateTemplateView(c *gin.Context) {
11 | var (
12 | err error
13 | template multi_cloud.CloudTemplate
14 | )
15 | // 参数验证
16 | err = c.ShouldBindWith(&template, binding.JSON)
17 | if err != nil {
18 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
19 | return
20 | }
21 | // 格式化打印验证的数据
22 | tools.PrettyPrint(template)
23 |
24 | // 保存
25 | err = template.Create()
26 | if err != nil {
27 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
28 | return
29 | }
30 |
31 | // 返回
32 | tools.JSONOk(c, "Created successfully")
33 | }
34 |
35 | func CetTemplateByAppKeyView(c *gin.Context) {
36 | var (
37 | err error
38 | template multi_cloud.CloudTemplate
39 | templates []multi_cloud.CloudTemplate
40 | )
41 | // 参数验证
42 | appKey, ok := c.GetQuery("app_key")
43 | if !ok {
44 | tools.JSONFailed(c, tools.MSG_ERR, "app_key is required.")
45 | return
46 | }
47 |
48 | // 保存
49 |
50 | template.AppKey = appKey
51 |
52 | if v, ok := c.GetQuery("account"); ok {
53 | template.Account = v
54 | }
55 |
56 | if v, ok := c.GetQuery("region_id"); ok {
57 | template.RegionId = v
58 | }
59 |
60 | // 格式化打印验证的数据
61 | tools.PrettyPrint(template)
62 |
63 | templates, err = template.ListByAppKey()
64 | if err != nil {
65 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
66 | return
67 | }
68 |
69 | // 返回
70 | tools.JSONOk(c, templates)
71 | }
72 |
73 | func DeleteTemplateView(c *gin.Context) {
74 | var template multi_cloud.CloudTemplate
75 |
76 | pk := c.Param("id")
77 | pkUint, _ := tools.StringToUint(pk)
78 | template.ID = pkUint
79 | err := template.Delete()
80 | if err != nil {
81 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
82 | return
83 | }
84 |
85 | // 返回
86 | tools.JSONOk(c, "Delete ok.")
87 | }
88 |
--------------------------------------------------------------------------------
/internal/api/nginx/README.md:
--------------------------------------------------------------------------------
1 | # 流量调度
2 | > nginx管理
3 |
4 | ## 核心功能
5 |
6 | - nginx server
7 | - nginx location
8 | - nginx upstream
9 |
10 | ## etcd
11 |
12 |
13 | ```text
14 | /corp///ip
15 | ```
16 |
--------------------------------------------------------------------------------
/internal/api/scheduler/dag.go:
--------------------------------------------------------------------------------
1 | package scheduler
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "lightning-go/internal/models/scheduler"
7 | "lightning-go/internal/service/scheduler"
8 | "lightning-go/pkg/tools"
9 |
10 | "strings"
11 |
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | // 触发Dag
16 | func TriggerDagRun(c *gin.Context) {
17 | // 接收所有参数 不做验证
18 | var dagRun service.DagRunDataSerializer
19 | dagRun.DagName = c.Param("dagName") // /:id
20 | bytes, err := ioutil.ReadAll(c.Request.Body)
21 | if err != nil {
22 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
23 | return
24 | }
25 | paramData, err := tools.ByteToJson(bytes)
26 | if err != nil {
27 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
28 | return
29 | }
30 | dagRun.Data = paramData
31 | msg, err := dagRun.Trigger()
32 | if err != nil {
33 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
34 | return
35 | }
36 | tools.JSONOk(c, msg)
37 | }
38 |
39 | // 触发Dag
40 | func TriggerDagRunV2(c *gin.Context) {
41 | // Validate
42 | var dagRun service.DagRunDataSerializer
43 | if err := c.ShouldBindJSON(&dagRun); err != nil {
44 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
45 | return
46 | }
47 | dagRun.DagName = c.Param("dagName") // /:id
48 | tools.PrettyPrint(dagRun)
49 | msg, err := dagRun.Trigger()
50 | if err != nil {
51 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
52 | return
53 | }
54 | tools.JSONOk(c, msg)
55 | }
56 |
57 | // 查询Dag
58 | // /api/v1/task-scheduler/dag
59 | // /api/v1/task-scheduler/dag?dag_id=delivery_machine&execution_date=2021-04-02 20:12:09.063224
60 | func ListDagRun(c *gin.Context) {
61 | var (
62 | dagRun scheduler.DagRun
63 | taskInstance scheduler.TaskInstance
64 | )
65 | // replace
66 | // https://blog.csdn.net/Yvken_Zh/article/details/104861765
67 | c.Request.URL.RawQuery = strings.ReplaceAll(c.Request.URL.RawQuery, "+", "%2b")
68 |
69 | // Get params
70 | dagId := c.Query("dag_id") // ?dag_id=xxx
71 | executionDate := c.Query("execution_date")
72 | fmt.Printf("dagId: %v, executionDate: %v\n", dagId, executionDate)
73 | if dagId != "" && executionDate != "" {
74 | // execute sql
75 | taskInstance.DagId = dagId
76 | taskInstance.ExecutionDate = executionDate
77 | dagRuns, err := taskInstance.ListByDagAndExecDate()
78 | if err != nil {
79 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
80 | return
81 | }
82 | // Response
83 | tools.JSONOk(c, dagRuns)
84 | } else {
85 | // execute sql
86 | dagRuns, err := dagRun.List()
87 | if err != nil {
88 | tools.JSONFailed(c, tools.MSG_ERR, err.Error())
89 | return
90 | }
91 | // Response
92 | tools.JSONOk(c, dagRuns)
93 | }
94 | return
95 | }
96 |
97 | func ListTaskLog(c *gin.Context) {
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/internal/app/server.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "fmt"
5 | "lightning-go/internal/db"
6 | "lightning-go/internal/router"
7 |
8 | "github.com/douyu/jupiter"
9 | "github.com/douyu/jupiter/pkg/server/xgin"
10 | "github.com/douyu/jupiter/pkg/util/xcolor"
11 | "github.com/douyu/jupiter/pkg/util/xgo"
12 | "github.com/douyu/jupiter/pkg/worker/xcron"
13 | "github.com/douyu/jupiter/pkg/xlog"
14 | )
15 |
16 | type Engine struct {
17 | jupiter.Application
18 | }
19 |
20 | func NewEngine() *Engine {
21 | eng := &Engine{}
22 | eng.HideBanner = true
23 | if err := eng.Startup(
24 | xgo.ParallelWithError(
25 | eng.printBanner,
26 | eng.serveHTTP,
27 | eng.initApp,
28 | ),
29 | ); err != nil {
30 | xlog.Panic("startup engine", xlog.Any("err", err))
31 | }
32 |
33 | return eng
34 | }
35 |
36 | func (eng *Engine) printBanner() error {
37 |
38 | const banner = `
39 | ______ ____ ____ ____ _____
40 | / ____// __ \ / __ \ / __ \/ ___/
41 | / / __ / / / // / / // /_/ /\__ \
42 | / /_/ // /_/ // /_/ // ____/___/ /
43 | \____/ \____/ \____//_/ /____/
44 |
45 | Welcome to LIGHTNING-OPS API, starting application ...
46 | `
47 |
48 | fmt.Println(xcolor.Green(banner))
49 | return nil
50 | }
51 | func (eng *Engine) initApp() error {
52 |
53 | db.Init()
54 | eng.initCron()
55 | eng.printBanner()
56 | //redis.InitRedis()
57 | return nil
58 | }
59 |
60 | func (eng *Engine) initCron() error {
61 | cron := xcron.StdConfig("chaos").Build()
62 | eng.Schedule(cron)
63 | return nil
64 | }
65 |
66 | //http服务
67 | func (eng *Engine) serveHTTP() error {
68 | server := xgin.StdConfig("http").Build()
69 | router.InitRouters(server)
70 | return eng.Serve(server)
71 | }
72 |
--------------------------------------------------------------------------------
/internal/cron/index.go:
--------------------------------------------------------------------------------
1 | package cron
2 |
3 | import (
4 | "github.com/douyu/jupiter/pkg/worker/xcron"
5 | )
6 |
7 | func InitShedule(cron *xcron.Cron) {
8 | //add cron task here
9 |
10 | // ex
11 | //if !conf.GetBool("kjcloud.disable_iso") {
12 | // cron.Schedule(xcron.Every(time.Minute*5), xcron.FuncJob(ResetInstallStatus))
13 | //}
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/internal/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "github.com/douyu/jupiter/pkg/store/gorm"
5 | )
6 |
7 | var (
8 | DB *gorm.DB
9 | Airflow *gorm.DB
10 | )
11 |
12 | func Init() {
13 |
14 | DB = gorm.StdConfig("master").Build()
15 | Airflow = gorm.StdConfig("airflow").Build()
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/internal/db/pagination.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import "github.com/douyu/jupiter/pkg/store/gorm"
4 |
5 | // 单表分页
6 | type PaginationQ struct {
7 | Ok bool `json:"ok"`
8 | Limit int `form:"size" json:"size"`
9 | Offset int `form:"page" json:"page"`
10 | Data interface{} `json:"data" comment:"muster be a pointer of slice gorm.Model"` // save pagination list
11 | Total int `json:"total"`
12 | }
13 |
14 | func CRUD(p *PaginationQ, queryTx *gorm.DB, list interface{}) (int, error) {
15 | if p.Limit < 1 {
16 | p.Limit = 10
17 | }
18 | if p.Offset < 1 {
19 | p.Offset = 1
20 | }
21 |
22 | var total int
23 | err := queryTx.Count(&total).Error
24 | if err != nil {
25 | return 0, err
26 | }
27 | offset := p.Limit * (p.Offset - 1)
28 | err = queryTx.Limit(p.Limit).Offset(offset).Find(list).Error
29 | if err != nil {
30 | return 0, err
31 | }
32 | return total, err
33 | }
34 |
--------------------------------------------------------------------------------
/internal/models/base.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "bytes"
5 | "database/sql/driver"
6 | "errors"
7 | "fmt"
8 | "lightning-go/pkg/tools"
9 | "time"
10 | )
11 |
12 | type JSON []byte
13 |
14 | func (j JSON) Value() (driver.Value, error) {
15 | if j.IsNull() {
16 | return nil, nil
17 | }
18 | return string(j), nil
19 | }
20 | func (j *JSON) Scan(value interface{}) error {
21 | if value == nil {
22 | *j = nil
23 | return nil
24 | }
25 | s, ok := value.([]byte)
26 | if !ok {
27 | errors.New("Invalid Scan Source ")
28 | }
29 | *j = append((*j)[0:0], s...)
30 | return nil
31 | }
32 | func (m JSON) MarshalJSON() ([]byte, error) {
33 | if m == nil {
34 | return []byte("null"), nil
35 | }
36 | //fmt.Println("---> MarshalJSON", tools.CompressStr(string(m)))
37 | m = []byte(tools.CompressStr(string(m)))
38 | return []byte(tools.CompressStr(string(m))), nil
39 | //return m, nil
40 | }
41 | func (m *JSON) UnmarshalJSON(data []byte) error {
42 | if m == nil {
43 | return errors.New("null point exception")
44 | }
45 | *m = append((*m)[0:0], data...)
46 | fmt.Println("---> UnmarshalJSON")
47 | return nil
48 | }
49 | func (j JSON) IsNull() bool {
50 | return len(j) == 0 || string(j) == "null"
51 | }
52 | func (j JSON) Equals(j1 JSON) bool {
53 | return bytes.Equal([]byte(j), []byte(j1))
54 | }
55 |
56 | // 重写MarshalJSON实现models json返回的时间格式
57 | type JSONTime struct {
58 | time.Time
59 | }
60 |
61 | func (t JSONTime) MarshalJSON() ([]byte, error) {
62 | formatted := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05"))
63 | return []byte(formatted), nil
64 | }
65 |
66 | func (t JSONTime) Value() (driver.Value, error) {
67 | var zeroTime time.Time
68 | if t.Time.UnixNano() == zeroTime.UnixNano() {
69 | return nil, nil
70 | }
71 | return t.Time, nil
72 | }
73 |
74 | func (t *JSONTime) Scan(v interface{}) error {
75 | value, ok := v.(time.Time)
76 | if ok {
77 | *t = JSONTime{Time: value}
78 | return nil
79 | }
80 | return fmt.Errorf("无法转换 %v 的时间格式", v)
81 | }
82 |
--------------------------------------------------------------------------------
/internal/models/multi_cloud/README.md:
--------------------------------------------------------------------------------
1 | ```json5
2 | {
3 | "app_key": "com.pdd.ops.monkey.monkey-cache",
4 | "account": "ali.corp3",
5 | "pay_type": "post_pay",
6 | "region_id": "cn-beijing",
7 | "zone_id": "cn-beijing-a",
8 | "instance_type": "c3.4xlarge",
9 | "hostname": "monkey.monkey-api1.ops.prod",
10 | "is_public": false,
11 | "is_eip": true,
12 | "image_id": "i-xxxxx",
13 | "vpc_id": "vpc-xxx",
14 | "subnet_id": "aaa",
15 | "disks": [{"disk_type":"system", "disk_storage_type":"cloud", "disk_storage_size":100}],
16 | "security_group_ids": ["s1", "s2"]
17 | }
18 | ```
--------------------------------------------------------------------------------
/internal/models/multi_cloud/account.go:
--------------------------------------------------------------------------------
1 | package multi_cloud
2 |
3 | import "lightning-go/internal/db"
4 |
5 | // 创建云主机配置参数
6 | type Account struct {
7 | ID uint `gorm:"primary_key" json:"id"`
8 | EnName string `json:"en_name" binding:"required"` // 英文名
9 | CnName string `json:"cn_name" binding:"required"` // 中文名
10 | Platform string `json:"platform" binding:"required"` // 平台; aws/ali/ten/hw
11 | AccessKeyId string `json:"access_key_id" binding:"required"` // 密钥
12 | SecretKeyId string `json:"secret_key_id" binding:"required"` // 密钥
13 | RootId string `json:"root_id" binding:"omitempty"` // 主帐号ID
14 | }
15 |
16 | func (s *Account) TableName() string {
17 | return "cloud_account"
18 | }
19 |
20 | func (s *Account) Create() (err error) {
21 | err = db.DB.Debug().Table(s.TableName()).Create(s).Error
22 | return
23 | }
24 |
25 | func (s *Account) Delete() (err error) {
26 | err = db.DB.Debug().Table(s.TableName()).Where("ID = ?", s.ID).Delete(&s).Error
27 | return
28 | }
29 |
30 | func (s *Account) Update(data map[string]interface{}) (err error) {
31 | err = db.DB.Debug().Table(s.TableName()).Where("ID = ?", s.ID).Update(data).Error
32 | return
33 | }
34 |
35 | func (s *Account) List() (accounts []Account, err error) {
36 | err = db.DB.Debug().Table(s.TableName()).Select([]string{"id", "en_name", "cn_name", "platform", "access_key_id", "root_id"}).Find(&accounts).Error
37 | return
38 | }
39 |
40 | func (s *Account) GetByAccount() (account Account, err error) {
41 | err = db.DB.Debug().Table(s.TableName()).Where("en_name = ?", s.EnName).Find(&account).Error
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/internal/models/multi_cloud/instance.go:
--------------------------------------------------------------------------------
1 | package multi_cloud
2 |
3 | import (
4 | "lightning-go/internal/db"
5 | "lightning-go/internal/models"
6 | )
7 |
8 | // 记录生命周期
9 | type InstanceLifeCycle struct {
10 | InstanceId string `json:"instance_id"`
11 | Uri string `json:"uri"`
12 | Method string `json:"method"`
13 | Query string `json:"query"`
14 | Body models.JSON `json:"body" gorm:"type:text"`
15 | RemoteIp string `json:"remote_ip"`
16 | CreateUser string `json:"create_user"`
17 | Response models.JSON `json:"response" gorm:"type:text"`
18 | IsSuccess bool `json:"is_success"` // 0 fail || 1 success
19 | CreatedAt models.JSONTime `json:"created_at"`
20 | }
21 |
22 | func (s *InstanceLifeCycle) TableName() string {
23 | return "instance_life_cycle"
24 | }
25 |
26 | func (s *InstanceLifeCycle) Create() (err error) {
27 | err = db.DB.Debug().Table(s.TableName()).Create(s).Error
28 | return
29 | }
30 |
31 | func (s *InstanceLifeCycle) GetByInstanceId() (instances []InstanceLifeCycle, err error) {
32 | err = db.DB.Debug().Table(s.TableName()).Where("instance_id = ?", s.InstanceId).Find(&instances).Order("created_at").Error
33 | return
34 | }
35 |
--------------------------------------------------------------------------------
/internal/models/multi_cloud/template.go:
--------------------------------------------------------------------------------
1 | package multi_cloud
2 |
3 | import (
4 | "lightning-go/internal/db"
5 | "lightning-go/internal/models"
6 |
7 | "github.com/jinzhu/gorm"
8 | )
9 |
10 | // 创建云主机配置参数
11 | type CloudTemplate struct {
12 | gorm.Model
13 | AppKey string `json:"app_key" binding:"required"` // 关联到service_tree服务
14 | Account string `json:"account" binding:"required"` // 帐号
15 | PayType string `json:"pay_type"` // 支付方式 包年和按月
16 | RegionId string `json:"region_id" binding:"required"` // 地域
17 | ZoneId string `json:"zone_id"` // 可用区
18 | InstanceType string `json:"instance_type" binding:"required"` // 机型
19 | IsPublic bool `json:"is_public"` // 公网地址
20 | IsEip bool `json:"is_eip"` // 弹性IP
21 | ImageId string `json:"image_id" binding:"required"` // 镜像ID
22 | VpcId string `json:"vpc_id" binding:"required"` // vpc id
23 | SubnetId string `json:"subnet_id"` // 子网
24 | Disks models.JSON `json:"disks" binding:"required"` // 云盘
25 | SecurityGroupIds models.JSON `json:"security_group_ids" binding:"required"` // 安全组
26 | Count int `json:"-" binding:"omitempty"` // 数量
27 | }
28 |
29 | type Disk struct {
30 | DiskStorageSize int `json:"disk_storage_size"` // 云盘存储大小
31 | DiskStorageType string `json:"disk_storage_type"` // 云盘存储类型 cloud cloud-ssd
32 | DiskType string `json:"disk_type"` // 云盘类型 system | data
33 | }
34 |
35 | func (s *CloudTemplate) TableName() string {
36 | return "cloud_template"
37 | }
38 |
39 | func (s *CloudTemplate) Create() (err error) {
40 | err = db.DB.Debug().Table(s.TableName()).Create(s).Error
41 | return
42 | }
43 |
44 | func (s *CloudTemplate) Get() (template CloudTemplate, err error) {
45 | err = db.DB.Debug().Table(s.TableName()).Where("id = ?", s.ID).Find(&template).Error
46 | return
47 | }
48 |
49 | func (s *CloudTemplate) Delete() (err error) {
50 | err = db.DB.Debug().Table(s.TableName()).Where("id = ?", s.ID).Delete(&s).Error
51 | return
52 | }
53 |
54 | func (s *CloudTemplate) ListByAppKey() (templates []CloudTemplate, err error) {
55 | table := db.DB.Debug().Table(s.TableName())
56 | table = table.Where("app_key = ?", s.AppKey)
57 |
58 | if s.Account != "" {
59 | table = table.Where("account = ?", s.Account)
60 | }
61 | if s.RegionId != "" {
62 | table = table.Where("region_id = ?", s.RegionId)
63 | }
64 |
65 | err = table.Find(&templates).Error
66 | return
67 | }
68 |
--------------------------------------------------------------------------------
/internal/models/scheduler/dag.go:
--------------------------------------------------------------------------------
1 | package scheduler
2 |
3 | import "lightning-go/internal/db"
4 |
5 | // 创建云主机配置参数
6 | type DagRun struct {
7 | ID uint `gorm:"primary_key" json:"id"`
8 | DagId string `json:"dag_id"`
9 | ExecutionDate string `json:"execution_date"`
10 | State string `json:"state"`
11 | RunId string `json:"run_id"`
12 | ExternalTrigger int `json:"external_trigger"`
13 | Conf string `json:"conf"`
14 | StartDate string `json:"start_date"`
15 | EndDate string `json:"end_date"`
16 | RunType string `json:"run_type"`
17 | LastSchedulingDecision string `json:"last_scheduling_decision"`
18 | DagHash string `json:"dag_hash"`
19 | CreatingJobId int `json:"creating_job_id"`
20 | }
21 |
22 | func (s *DagRun) TableName() string {
23 | return "dag_run"
24 | }
25 |
26 | func (s *DagRun) List() (dagRuns []DagRun, err error) {
27 | err = db.Airflow.Debug().Table(s.TableName()).Find(&dagRuns).Error
28 | return
29 | }
30 |
--------------------------------------------------------------------------------
/internal/models/scheduler/task_instance.go:
--------------------------------------------------------------------------------
1 | package scheduler
2 |
3 | import "lightning-go/internal/db"
4 |
5 | // 创建云主机配置参数
6 | type TaskInstance struct {
7 | TaskId string `json:"task_id"`
8 | DagId string `json:"dag_id" binding:"required"`
9 | ExecutionDate string `json:"execution_date" binding:"required"`
10 | StartDate string `json:"start_date"`
11 | EndDate string `json:"end_date"`
12 | Duration float64 `json:"duration"`
13 | State string `json:"state"`
14 | TryNumber int `json:"try_number"`
15 | Hostname string `json:"hostname"`
16 | Unixname string `json:"unixname"`
17 | JobId int `json:"job_id"`
18 | Pool string `json:"pool"`
19 | Queue string `json:"queue"`
20 | PriorityWeight string `json:"priority_weight"`
21 | Operator string `json:"operator"`
22 | QueuedDttm string `json:"queued_dttm"`
23 | Pid int `json:"pid"`
24 | MaxTries int `json:"max_tries"`
25 | ExecutorConfig string `json:"executor_config"`
26 | PoolSlots int `json:"pool_slots"`
27 | QueuedByJobId int `json:"queued_by_job_id"`
28 | ExternalExecutorId string `json:"external_executor_id"`
29 | }
30 |
31 | func (s *TaskInstance) TableName() string {
32 | return "task_instance"
33 | }
34 |
35 | func (s *TaskInstance) ListByDagAndExecDate() (tasInstances []TaskInstance, err error) {
36 | table := db.Airflow.Debug().Table(s.TableName())
37 | err = table.Where("dag_id = ? AND execution_date = ?", s.DagId, s.ExecutionDate).Find(&tasInstances).Error
38 | return
39 | }
40 |
--------------------------------------------------------------------------------
/internal/redis/index.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "github.com/douyu/jupiter/pkg/client/redis"
5 | "github.com/douyu/jupiter/pkg/xlog"
6 | "time"
7 | )
8 |
9 | var RedisStub *redis.Redis
10 |
11 | func InitRedis() error {
12 |
13 | RedisStub = redis.StdRedisStubConfig("kjcloud").Build()
14 | setRes := RedisStub.Set("jupiter-redis", "redisStub", time.Second*5)
15 | xlog.Info("redisStub set string", xlog.Any("res", setRes))
16 |
17 | getRes := RedisStub.Get("jupiter-redis")
18 | xlog.Info("redisStub get string", xlog.Any("res", getRes))
19 | return nil
20 | }
21 |
--------------------------------------------------------------------------------
/internal/router/middleware/auth.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/douyu/jupiter/pkg/conf"
7 | "github.com/gin-gonic/gin"
8 | "github.com/go-resty/resty/v2"
9 |
10 | "lightning-go/pkg/tools"
11 | )
12 |
13 | func JWTAuth() gin.HandlerFunc {
14 | return func(c *gin.Context) {
15 |
16 | authSuccess := tools.JSONResult{}
17 | jwt := c.Request.Header.Get("authorization")
18 | // 处理请求
19 | if len(jwt) < 1 {
20 | tools.JSONFailed(c, 403, "The token is empty")
21 | c.Abort()
22 | return
23 | }
24 |
25 | client := resty.New()
26 | _, err := client.R().
27 | SetHeader("Content-Type", "application/json").
28 | SetHeader("authorization", jwt).
29 | SetBody(map[string]interface{}{
30 | "path": c.Request.URL.Path,
31 | "method": c.Request.Method,
32 | }).
33 | SetResult(&authSuccess).
34 | Post(conf.GetString("go-ops.hosts.auth"))
35 |
36 | if err != nil {
37 | tools.JSONFailed(c, tools.MSG_ERR, fmt.Sprintf("auth err :%v ", err))
38 | c.Abort()
39 | return
40 | }
41 |
42 | if authSuccess.Code != 0 {
43 | tools.JSONFailed(c, authSuccess.Code, authSuccess.Message)
44 | c.Abort()
45 | return
46 | }
47 | c.Next()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/internal/router/middleware/cors.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | // 解决跨域
9 | func Cors() gin.HandlerFunc {
10 | return func(c *gin.Context) {
11 | method := c.Request.Method
12 |
13 | c.Header("Access-Control-Allow-Origin", "*")
14 | c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
15 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
16 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
17 | c.Header("Access-Control-Allow-Credentials", "true")
18 |
19 | if method == "OPTIONS" {
20 | c.AbortWithStatus(http.StatusNoContent)
21 | }
22 | // 处理请求
23 | c.Next()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/router/middleware/life_cycle.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "io/ioutil"
8 | "lightning-go/internal/models/multi_cloud"
9 | "lightning-go/pkg/tools"
10 | "strings"
11 |
12 | "github.com/douyu/jupiter/pkg/xlog"
13 | "github.com/gin-gonic/gin"
14 | "github.com/satori/go.uuid"
15 | )
16 |
17 | /*
18 | 创建
19 | 变更
20 | 开机
21 | 关机
22 | 重启
23 | 升配
24 | 降配
25 | 下线
26 | */
27 |
28 | type responseBodyWriter struct {
29 | gin.ResponseWriter
30 | body *bytes.Buffer
31 | }
32 |
33 | func (r responseBodyWriter) Write(b []byte) (int, error) {
34 | r.body.Write(b)
35 | return r.ResponseWriter.Write(b)
36 | }
37 |
38 | func LifeCycle() gin.HandlerFunc {
39 | return func(c *gin.Context) {
40 |
41 | //1. 生成 Request-Id
42 | requestId := c.Request.Header.Get("X-Request-Id")
43 | if requestId == "" {
44 | id, err := uuid.NewV4()
45 | if err != nil {
46 | xlog.Infof("request ID err :%v", err)
47 | return
48 | }
49 | requestId = fmt.Sprintf("%s", id)
50 | }
51 | c.Set("X-Request-Id", requestId)
52 | c.Writer.Header().Set("X-Request-Id", requestId)
53 |
54 | //2. 记录事件生命周期
55 | //cCp := c.Copy()
56 | rawData, _ := c.GetRawData()
57 | // 重新赋值
58 | c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rawData))
59 |
60 | cycle := multi_cloud.InstanceLifeCycle{
61 | Uri: c.Request.URL.Path,
62 | Method: c.Request.Method,
63 | Query: c.Request.URL.RawQuery,
64 | Body: rawData,
65 | RemoteIp: c.ClientIP(),
66 | }
67 |
68 | headerAuthorization := c.Request.Header.Get("Authorization")
69 | if headerAuthorization != "" {
70 | authArr := strings.Split(headerAuthorization, " ")
71 | if len(authArr) == 2 {
72 | cycle.CreateUser = authArr[1]
73 | }
74 | }
75 |
76 | // https://github.com/gin-gonic/gin/issues/1363
77 | w := &responseBodyWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
78 | c.Writer = w
79 |
80 | c.Next()
81 |
82 | // 4. attach field response
83 | cycle.Response = []byte(w.body.String())
84 |
85 | // 5. attach field IsSuccess
86 | var jsonResult tools.JSONResult
87 | err := json.Unmarshal([]byte(w.body.String()), &jsonResult)
88 | if err == nil {
89 | if jsonResult.Code == tools.MSG_OK {
90 | cycle.IsSuccess = true
91 | if vInstanceInfo, ok := jsonResult.Data.(map[string]interface{}); ok {
92 | cycle.InstanceId = vInstanceInfo["instance_id"].(string)
93 | } else {
94 | bodyInfo, err := tools.StringToMap(rawData)
95 | if err != nil {
96 | xlog.Infof("string to map err :%v", err)
97 | }
98 | if instanceId, ok := bodyInfo["instance_id"].(string); ok {
99 | cycle.InstanceId = instanceId
100 | }
101 | fmt.Println(4)
102 |
103 | }
104 | }
105 | } else {
106 | xlog.Infof("middleware attach instance_id and is_success field err :%v", err)
107 | }
108 |
109 | // 6. save
110 | _ = cycle.Create()
111 |
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/internal/router/middleware/rid.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "fmt"
5 | "github.com/douyu/jupiter/pkg/xlog"
6 | "github.com/gin-gonic/gin"
7 | uuid "github.com/satori/go.uuid"
8 | )
9 |
10 | func SetRId() gin.HandlerFunc {
11 | return func(c *gin.Context) {
12 |
13 | requestId := c.Request.Header.Get("X-Request-Id")
14 | if requestId == "" {
15 | id, err := uuid.NewV4()
16 | if err != nil {
17 | xlog.Infof("requeset ID err :%v", err)
18 | return
19 | }
20 | requestId = fmt.Sprintf("%s", id)
21 | }
22 | c.Set("X-Request-Id", requestId)
23 | c.Writer.Header().Set("X-Request-Id", requestId)
24 | c.Next()
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internal/router/router.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | _ "lightning-go/docs"
5 | "lightning-go/internal/api/multi_cloud"
6 | "lightning-go/internal/api/scheduler"
7 | "lightning-go/internal/router/middleware"
8 |
9 | "github.com/douyu/jupiter/pkg/server/xgin"
10 | "github.com/swaggo/gin-swagger"
11 | "github.com/swaggo/gin-swagger/swaggerFiles"
12 | )
13 |
14 | func InitRouters(server *xgin.Server) {
15 |
16 | // set Cors middleware
17 | //send.Use(middleware.Cors())
18 | //send.Use(cors.Default())
19 |
20 | // set ReQueSetId middleware
21 | //send.Use(middleware.SetRId())
22 |
23 | // set JWTauth middleware
24 | //send.Use(middleware.JWTAuth())
25 |
26 | // api swagger doc
27 | url := ginSwagger.URL("http://127.0.0.1:9900/swagger/doc.json")
28 | server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
29 |
30 | // task scheduler
31 | ts := server.Group("/api/v1/task-scheduler/dag")
32 | {
33 | ts.POST("/:dagName", scheduler.TriggerDagRun)
34 | ts.GET("/", scheduler.ListDagRun)
35 | }
36 |
37 | // multi-cloud router
38 | template := server.Group("/api/v1/multi-cloud/template")
39 | {
40 | template.POST("", multi_cloud.CreateTemplateView)
41 | template.GET("/", multi_cloud.CetTemplateByAppKeyView)
42 | template.DELETE("/:id", multi_cloud.DeleteTemplateView)
43 | }
44 | instance := server.Group("/api/v1/multi-cloud/instance")
45 | //instance.Use(middleware.LifeCycle()) // 记录生命周期事件
46 | {
47 | instance.POST("/create", middleware.LifeCycle(), multi_cloud.CreateInstanceView)
48 | instance.POST("/start", middleware.LifeCycle(), multi_cloud.StartInstanceView)
49 | instance.POST("/stop", middleware.LifeCycle(), multi_cloud.StopInstanceView)
50 | instance.POST("/reboot", middleware.LifeCycle(), multi_cloud.RebootInstanceView)
51 | instance.POST("/modify_instance_name", middleware.LifeCycle(), multi_cloud.ModifyInstanceNameView)
52 | instance.GET("/", multi_cloud.ListInstancesView)
53 | instance.GET("/:instance_id", multi_cloud.InstanceDetailView)
54 | instance.POST("/destroy", middleware.LifeCycle(), multi_cloud.DestroyInstanceView)
55 | }
56 | cycle := server.Group("/api/v1/multi-cloud/life_cycle")
57 | {
58 | cycle.GET("/:instance_id", multi_cloud.LifeCyclelView)
59 | }
60 | region := server.Group("/api/v1/multi-cloud/regions")
61 | {
62 | region.GET("/", multi_cloud.ListRegionView)
63 | }
64 | account := server.Group("/api/v1/multi-cloud/account")
65 | {
66 | account.POST("", multi_cloud.CreateAccountView)
67 | account.GET("/", multi_cloud.ListAccountView)
68 | account.DELETE("/:id", multi_cloud.DeleteAccountView)
69 | account.PUT("/:id", multi_cloud.UpdateAccountView)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/internal/service/scheduler/serializer.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "lightning-go/pkg/request"
7 | "lightning-go/pkg/tools"
8 | "time"
9 |
10 | "github.com/douyu/jupiter/pkg/conf"
11 | "github.com/tidwall/gjson"
12 | )
13 |
14 | type CreateInstance struct {
15 | Data map[string]interface{} `json:"data" binding:"required"`
16 | }
17 |
18 | // 验证结构体
19 | type DagRunDataSerializer struct {
20 | DagName string `json:"dag_name"`
21 | Data map[string]interface{} `json:"data" binding:"required"`
22 | }
23 |
24 | type DagRunDataV2Serializer struct {
25 | CreateInstance //匿名字段
26 | }
27 |
28 | type DagRunResponse struct {
29 | DagRunId string `json:"dag_run_id"`
30 | StartDate string `json:"start_date"`
31 | EndDate string `json:"end_date"`
32 | ExecutionDate string `json:"execution_date"`
33 | ExternalTrigger string `json:"external_trigger"`
34 | State string `json:"state"`
35 | }
36 |
37 | /*
38 |
39 | {
40 | "dags": [
41 | {
42 | "dag_id": "cron_demo",
43 | "description": "A cron demo test",
44 | "file_token": "Ii9yb290L2FpcmZsb3cvZGFncy9jcm9uX2RlbW8ucHki.ZEWrY1Ti5R8nGaywU-AcFmj6ixE",
45 | "fileloc": "/root/airflow/dags/cron_demo.py",
46 | "is_paused": true,
47 | "is_subdag": false,
48 | "owners": [
49 | "zhengshuai"
50 | ],
51 | "root_dag_id": null,
52 | "schedule_interval": {
53 | "__type": "CronExpression",
54 | "value": "@daily"
55 | },
56 | "tags": []
57 | },
58 | {
59 | "dag_id": "delivery_machine",
60 | "description": "交付机器",
61 | "file_token": "Ii9yb290L2FpcmZsb3cvZGFncy9kZWxpdmVyeV9tYWNoaW5lLnB5Ig.BI52o4f3FdqjUptIKk-4U1Lcc_Y",
62 | "fileloc": "/root/airflow/dags/delivery_machine.py",
63 | "is_paused": false,
64 | "is_subdag": false,
65 | "owners": [
66 | "zhengshuai"
67 | ],
68 | "root_dag_id": null,
69 | "schedule_interval": null,
70 | "tags": []
71 | },
72 | {
73 | "dag_id": "example_bash_operator",
74 | "description": null,
75 | "file_token": ".eJw9yjEOgCAMAMC_sEsHE79DilYgIm1aovJ7Jx0vOQfK3AGL7pXvQO2CWiLI6Jnb7Bew0mkSXA9MZN8DevCUSmHDZD8iWg4spNhZvQz3AmOfI1c.JFGlbq1mSnq865555vKdAlpQONo",
76 | "fileloc": "/root/airflow_env/lib/python3.6/site-packages/airflow/example_dags/example_bash_operator.py",
77 | "is_paused": true,
78 | "is_subdag": false,
79 | "owners": [
80 | "airflow"
81 | ],
82 | "root_dag_id": null,
83 | "schedule_interval": {
84 | "__type": "CronExpression",
85 | "value": "0 0 * * *"
86 | },
87 | "tags": [
88 | {
89 | "name": "example"
90 | },
91 | {
92 | "name": "example2"
93 | }
94 | ]
95 | },
96 | */
97 |
98 | type DagDetailResponse struct {
99 | DagId string `json:"dag_id"`
100 | Description string `json:"description"`
101 | FileToken string `json:"file_token"`
102 | IsPaused bool `json:"is_paused"`
103 | IsSubdag bool `json:"is_subdag"`
104 | RootDagId string `json:"root_dag_id"`
105 | Fileloc string `json:"fileloc"`
106 | Owners []string `json:"owners"`
107 | Tags []map[string]string `json:"tags"`
108 | ScheduleInterval interface{} `json:"schedule_interval"`
109 | }
110 |
111 | type DagListResponse struct {
112 | Dags []DagDetailResponse `json:"dags"`
113 | }
114 |
115 | func GetAirflowHeader() map[string]string {
116 | return map[string]string{
117 | "Content-Type": "application/json",
118 | "Authorization": fmt.Sprintf("Basic %s", tools.Base64Encode(fmt.Sprintf("%s:%s",
119 | conf.GetString("go-ops.scheduler.airflowUserName"),
120 | conf.GetString("go-ops.scheduler.airflowUserPassword"),
121 | ))),
122 | }
123 | }
124 |
125 | func (s DagRunDataSerializer) Trigger() (interface{}, error) {
126 | var (
127 | timeout = time.Duration(time.Second * 10)
128 | body = make(map[string]interface{})
129 | dagResponse DagRunResponse
130 | )
131 |
132 | dagUrl := fmt.Sprintf("%s/api/v1/dags/%s/dagRuns",
133 | conf.GetString("go-ops.scheduler.airflowUrl"),
134 | s.DagName,
135 | )
136 | header := map[string]string{
137 | "Content-Type": "application/json",
138 | "Authorization": fmt.Sprintf("Basic %s", tools.Base64Encode(fmt.Sprintf("%s:%s",
139 | conf.GetString("go-ops.scheduler.airflowUserName"),
140 | conf.GetString("go-ops.scheduler.airflowUserPassword"),
141 | ))),
142 | }
143 |
144 | body["conf"] = s.Data
145 | tools.PrettyPrint(body)
146 | tools.PrettyPrint(dagUrl)
147 | bodyByte, err := tools.JsonToByte(body)
148 | if err != nil {
149 | return "JsonToByte error", err
150 | }
151 | responseByte, err := request.Post(bodyByte, header, dagUrl, timeout)
152 | if err != nil {
153 | return "request.Post err", err
154 | }
155 |
156 | // 反序列化 response
157 | _ = json.Unmarshal(responseByte, &dagResponse)
158 | tools.PrettyPrint(dagResponse)
159 | return dagResponse, nil
160 | }
161 |
162 | type DagMgt struct {
163 | DagName string `json:"dag_name"`
164 | DagRunId string `json:"dag_run_id"`
165 | Limit int `json:"limit"`
166 | }
167 |
168 | func (d DagMgt) ListDag() (DagListResponse, error) {
169 | var (
170 | dagUrl string
171 | dagListResponse DagListResponse
172 | )
173 | d.Limit = 100
174 | dagUrl = fmt.Sprintf("%s/api/v1/dags?limit=%d",
175 | conf.GetString("go-ops.scheduler.airflowUrl"),
176 | d.Limit,
177 | )
178 |
179 | fmt.Printf("dagUrl %s\n", dagUrl)
180 | responseByte, err := request.GetWithHeader(dagUrl, GetAirflowHeader(), time.Duration(time.Second*5))
181 | if err != nil {
182 | return dagListResponse, err
183 | }
184 | // 反序列化 response
185 | _ = json.Unmarshal(responseByte, &dagListResponse)
186 | tools.PrettyPrint(dagListResponse)
187 | return dagListResponse, nil
188 | }
189 |
190 | func (d DagMgt) DetailDag() (DagDetailResponse, error) {
191 | var (
192 | dagUrl string
193 | dagDetailResponse DagDetailResponse
194 | )
195 | dagUrl = fmt.Sprintf("%s/api/v1/dags/%s",
196 | conf.GetString("go-ops.scheduler.airflowUrl"),
197 | d.DagName,
198 | )
199 |
200 | fmt.Printf("dagUrl %s\n", dagUrl)
201 | responseByte, err := request.GetWithHeader(dagUrl, GetAirflowHeader(), time.Duration(time.Second*5))
202 | if err != nil {
203 | return dagDetailResponse, err
204 | }
205 | // 反序列化 response
206 | _ = json.Unmarshal(responseByte, &dagDetailResponse)
207 | tools.PrettyPrint(dagDetailResponse)
208 | return dagDetailResponse, nil
209 | }
210 |
211 | func (d DagMgt) TaskInstance() (map[string]interface{}, error) {
212 | var (
213 | dagUrl string
214 | jsonMap = make(map[string]interface{})
215 | )
216 | d.Limit = 100
217 | dagUrl = fmt.Sprintf("%s/api/v1/dags/%s/dagRuns/%s/taskInstances?limit=%d",
218 | conf.GetString("go-ops.scheduler.airflowUrl"),
219 | d.DagName,
220 | d.DagRunId,
221 | d.Limit,
222 | )
223 |
224 | fmt.Printf("dagUrl %s\n", dagUrl)
225 | responseByte, err := request.GetWithHeader(dagUrl, GetAirflowHeader(), time.Duration(time.Second*5))
226 | if err != nil {
227 | return jsonMap, err
228 | }
229 | // 反序列化 response
230 | fmt.Println(string(responseByte))
231 | result := gjson.Get(string(responseByte), "task_instances")
232 |
233 | err = json.Unmarshal([]byte(result.Raw), &jsonMap)
234 | if err != nil {
235 | return jsonMap, err
236 | }
237 | return jsonMap, nil
238 | }
239 |
--------------------------------------------------------------------------------
/pkg/multi_cloud_sdk/README.md:
--------------------------------------------------------------------------------
1 | # create instance
2 |
3 | ## ali create instance
4 | ```json
5 |
6 | {
7 | "account": "ali.lightning",
8 | "pay_type": "PostPaid",
9 | "region_id": "cn-beijing",
10 | "zone_id": "cn-beijing-c",
11 | "instance_type": "ecs.sn1.medium",
12 | "image_id": "centos_7_8_x64_20G_alibase_20200914.vhd",
13 | "vpc_id": "vpc-2ze80et76jwcsuc2asobq",
14 | "subnet_id": "vsw-2zelzctt7aougw3bhz5ev",
15 | "disks": [{
16 | "disk_type": "system",
17 | "disk_storage_type": "cloud",
18 | "disk_storage_size": 100
19 | }],
20 | "security_group_ids": ["sg-2zeb9n0qzygmjwn7hwae"],
21 | "hostname": "lightning-fe1.ops.prod.ali"
22 | }
23 |
24 | ```
25 |
26 |
27 | ## ten create instance
28 | ```json
29 |
30 | {
31 | "account": "ten.lightning",
32 | "pay_type": "PostPaid",
33 | "region_id": "ap-shanghai",
34 | "zone_id": "ap-shanghai-2",
35 | "instance_type": "S4.MEDIUM4",
36 | "image_id": "img-oikl1tzv",
37 | "vpc_id": "vpc-2qckup44",
38 | "subnet_id": "subnet-r1tor2m1",
39 | "disks": [{
40 | "disk_type": "system",
41 | "disk_storage_type": "LOCAL_SSD",
42 | "disk_storage_size": 100
43 | }],
44 | "security_group_ids": ["sg-qodmlw0w"],
45 | "hostname": "lightning-fe1.ops.prod.ten"
46 | }
47 |
48 | ```
49 |
--------------------------------------------------------------------------------
/pkg/multi_cloud_sdk/ali.go:
--------------------------------------------------------------------------------
1 | package multi_cloud_sdk
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "lightning-go/pkg/tools"
7 | "strings"
8 |
9 | openapi "github.com/alibabacloud-go/darabonba-openapi/client"
10 | ecs "github.com/alibabacloud-go/ecs-20140526/v2/client"
11 | "github.com/alibabacloud-go/tea/tea"
12 | )
13 |
14 | type aliyunClient struct {
15 | regionId string
16 | ecsClt *ecs.Client
17 | }
18 |
19 | func NewAliEcs(accessKeyId, accessKeySecret, regionId string) (*aliyunClient, error) {
20 | openCfg := &openapi.Config{
21 | AccessKeyId: tea.String(accessKeyId),
22 | AccessKeySecret: tea.String(accessKeySecret),
23 | RegionId: tea.String(regionId),
24 | //ConnectTimeout: tea.Int(10),
25 | //ReadTimeout: tea.Int(10),
26 | }
27 | clt, err := ecs.NewClient(openCfg)
28 | if err != nil {
29 | return &aliyunClient{}, err
30 | }
31 | return &aliyunClient{
32 | regionId: regionId,
33 | ecsClt: clt,
34 | }, nil
35 | }
36 |
37 | // create and start
38 | func (ali *aliyunClient) CreateInstance(PayType, hostname, instanceType, zoneId, imageId, vpcId, subnetId string, securityGroupIds []string, dryRun bool) (instanceInfo map[string]string, err error) {
39 | /*
40 | 实例的付费方式。取值范围:
41 | PrePaid:包年包月
42 | PostPaid(默认):按量付费
43 | 选择包年包月时,您必须确认自己的账号支持余额支付或者信用支付,否则将返回InvalidPayMethod的错误提示。
44 | */
45 | instanceInfo = make(map[string]string)
46 | runInstancesRequest := &ecs.RunInstancesRequest{
47 | InstanceChargeType: tea.String(PayType),
48 | InstanceName: tea.String(hostname),
49 | Password: tea.String(password),
50 | InstanceType: tea.String(instanceType),
51 | RegionId: tea.String(ali.regionId),
52 | ZoneId: tea.String(zoneId),
53 | ImageId: tea.String(imageId),
54 | DryRun: tea.Bool(dryRun),
55 | VSwitchId: tea.String(subnetId),
56 | SecurityGroupId: tea.String(securityGroupIds[0]),
57 | }
58 |
59 | if runInstancesRequest.InstanceChargeType == tea.String("PrePaid") {
60 | // 包年包月 特殊设置
61 | runInstancesRequest.PeriodUnit = tea.String("Month")
62 | runInstancesRequest.Period = tea.Int32(1)
63 | runInstancesRequest.AutoRenew = tea.Bool(true)
64 | runInstancesRequest.AutoRenewPeriod = tea.Int32(1)
65 | }
66 | response, err := ali.ecsClt.RunInstances(runInstancesRequest)
67 | if err != nil {
68 | return instanceInfo, err
69 | }
70 | instanceIdSet := response.Body.InstanceIdSets.InstanceIdSet
71 | if len(instanceIdSet) == 0 {
72 | return instanceInfo, errors.New("Request ok, but instance create not success. ")
73 | }
74 | instanceInfo["instance_id"] = tea.StringValue(instanceIdSet[0])
75 | return instanceInfo, nil
76 | }
77 |
78 | // create
79 | func (ali *aliyunClient) CreateInstanceButStop(PayType, hostname, instanceType, zoneId, imageId, vpcId, subnetId string, securityGroupIds []string, dryRun bool) (instanceId string, err error) {
80 | /*
81 | 实例的付费方式。取值范围:
82 | PrePaid:包年包月
83 | PostPaid(默认):按量付费
84 | 选择包年包月时,您必须确认自己的账号支持余额支付或者信用支付,否则将返回InvalidPayMethod的错误提示。
85 | */
86 | request := &ecs.CreateInstanceRequest{
87 | InstanceChargeType: tea.String(PayType),
88 | InstanceName: tea.String(hostname),
89 | Password: tea.String(password),
90 | InstanceType: tea.String(instanceType),
91 | RegionId: tea.String(ali.regionId),
92 | ZoneId: tea.String(zoneId),
93 | ImageId: tea.String(imageId),
94 | DryRun: tea.Bool(dryRun),
95 | VSwitchId: tea.String(subnetId),
96 | SecurityGroupId: tea.String(securityGroupIds[0]),
97 | }
98 |
99 | if request.InstanceChargeType == tea.String("PrePaid") {
100 | // 包年包月 特殊设置
101 | request.PeriodUnit = tea.String("Month")
102 | request.Period = tea.Int32(1)
103 | request.AutoRenew = tea.Bool(true)
104 | request.AutoRenewPeriod = tea.Int32(1)
105 | }
106 | response, err := ali.ecsClt.CreateInstance(request)
107 | if err != nil {
108 | return "Call RunInstances error", err
109 | }
110 |
111 | return tea.StringValue(response.Body.InstanceId), nil
112 | }
113 |
114 | func (ali *aliyunClient) StartInstance(instanceId string) (string, error) {
115 | startInstancesRequest := &ecs.StartInstancesRequest{
116 | RegionId: tea.String(ali.regionId),
117 | BatchOptimization: tea.String("AllTogether"),
118 | InstanceId: []*string{tea.String(instanceId)},
119 | }
120 | response, err := ali.ecsClt.StartInstances(startInstancesRequest)
121 | if err != nil {
122 | return err.Error(), err
123 | }
124 | for _, v := range response.Body.InstanceResponses.InstanceResponse {
125 | if *v.Message == "success" && *v.Code == "200" {
126 | return *response.Body.RequestId, nil
127 | }
128 | }
129 | return "", errors.New("Start fail. ")
130 | }
131 |
132 | func (ali *aliyunClient) StopInstance(instanceId string) (string, error) {
133 | stopInstancesRequest := &ecs.StopInstancesRequest{
134 | RegionId: tea.String(ali.regionId),
135 | BatchOptimization: tea.String("AllTogether"),
136 | InstanceId: []*string{tea.String(instanceId)},
137 | ForceStop: tea.Bool(true),
138 | }
139 | response, err := ali.ecsClt.StopInstances(stopInstancesRequest)
140 | if err != nil {
141 | return err.Error(), err
142 | }
143 |
144 | for _, v := range response.Body.InstanceResponses.InstanceResponse {
145 | if *v.Message == "success" && *v.Code == "200" {
146 | return *response.Body.RequestId, nil
147 | }
148 | }
149 | return "", errors.New("Stop fail. ")
150 | }
151 |
152 | func (ali *aliyunClient) RebootInstance(instanceId string, forceStop bool) (string, error) {
153 | // 如果机器状态是关机,则无法重启;必须先开机 在关机才行
154 | rebootInstanceRequest := &ecs.RebootInstanceRequest{
155 | InstanceId: tea.String(instanceId),
156 | ForceStop: tea.Bool(forceStop),
157 | }
158 | response, err := ali.ecsClt.RebootInstance(rebootInstanceRequest)
159 | if err != nil {
160 | return err.Error(), err
161 | }
162 | requestId := response.Body.RequestId
163 | return *requestId, nil
164 | }
165 |
166 | func (ali *aliyunClient) DestroyInstance(instanceId string, forceStop bool) (string, error) {
167 | deleteInstanceRequest := &ecs.DeleteInstanceRequest{
168 | InstanceId: tea.String(instanceId),
169 | Force: tea.Bool(forceStop),
170 | TerminateSubscription: tea.Bool(true), // 是否强制释放运行中(Running的实例
171 | }
172 | response, err := ali.ecsClt.DeleteInstance(deleteInstanceRequest)
173 | if err != nil {
174 | return err.Error(), err
175 | }
176 | return *response.Body.RequestId, nil
177 | }
178 |
179 | func (ali *aliyunClient) ModifyInstanceName(instanceId string, instanceName string) (string, error) {
180 | modifyInstanceAttributeRequest := &ecs.ModifyInstanceAttributeRequest{
181 | InstanceId: tea.String(instanceId),
182 | HostName: tea.String(instanceName),
183 | InstanceName: tea.String(instanceName),
184 | }
185 | response, err := ali.ecsClt.ModifyInstanceAttribute(modifyInstanceAttributeRequest)
186 | if err != nil {
187 | return err.Error(), err
188 | }
189 | return *response.Body.RequestId, nil
190 | }
191 |
192 | func (ali *aliyunClient) ListInstance(instanceId string) (map[string]interface{}, error) {
193 | var instanceInfo = make(map[string]interface{})
194 | describeInstancesRequest := &ecs.DescribeInstancesRequest{
195 | PageSize: tea.Int32(100),
196 | InstanceIds: tea.String(instanceId),
197 | RegionId: tea.String(ali.regionId),
198 | DryRun: tea.Bool(false),
199 | }
200 | instances, err := ali.ecsClt.DescribeInstances(describeInstancesRequest)
201 | if err != nil {
202 | return instanceInfo, err
203 | }
204 | if len(instances.Body.Instances.Instance) == 0 {
205 | return instanceInfo, errors.New(fmt.Sprintf("%s not found ", instanceId))
206 | }
207 | for _, instance := range instances.Body.Instances.Instance {
208 | instanceInfo, _ = ali.processInstance(instance)
209 |
210 | }
211 | return instanceInfo, nil
212 | }
213 |
214 | func (ali *aliyunClient) ListInstances() ([]map[string]interface{}, error) {
215 | var instanceArray []map[string]interface{}
216 | for pageNumber := 1; pageNumber <= 3; pageNumber++ {
217 | describeInstancesRequest := &ecs.DescribeInstancesRequest{
218 | PageSize: tea.Int32(100),
219 | PageNumber: tea.Int32(int32(pageNumber)),
220 | RegionId: tea.String(ali.regionId),
221 | DryRun: tea.Bool(false),
222 | }
223 | // 复制代码运行请自行打印 API 的返回值
224 | instances, err := ali.ecsClt.DescribeInstances(describeInstancesRequest)
225 | if err != nil {
226 | return instanceArray, err
227 | }
228 | // 如果返回结果集中为空 则退出循环
229 | if len(instances.Body.Instances.Instance) == 0 {
230 | break
231 | }
232 | for _, instance := range instances.Body.Instances.Instance {
233 | info, _ := ali.processInstance(instance)
234 | instanceArray = append(instanceArray, info)
235 |
236 | }
237 | }
238 | return instanceArray, nil
239 | }
240 |
241 | func (ali *aliyunClient) processInstance(instance *ecs.DescribeInstancesResponseBodyInstancesInstance) (map[string]interface{}, error) {
242 | defer func() {
243 | if err := recover(); err != nil {
244 | fmt.Printf("recover %s\n", err)
245 | }
246 | }()
247 |
248 | tools.PrettyPrint(instance)
249 |
250 | var publicIpAddress *string
251 | info := make(map[string]interface{})
252 |
253 | if len(instance.PublicIpAddress.IpAddress) != 0 {
254 | publicIpAddress = instance.PublicIpAddress.IpAddress[0]
255 | }
256 | info = map[string]interface{}{
257 | "type": cloudType,
258 | "platform": "ali",
259 | "instance_charge_type": instance.InstanceChargeType,
260 | "instance_id": instance.InstanceId,
261 | "private_ip": instance.VpcAttributes.PrivateIpAddress.IpAddress[0],
262 | "public_ip": publicIpAddress,
263 | "eip_ip": instance.EipAddress.IpAddress,
264 | //"instance_name": instance.InstanceName,
265 | "hostname": instance.InstanceName,
266 | "image_id": instance.ImageId,
267 | "os_system": instance.OSType,
268 | "os_version": instance.OSName,
269 | "instance_type": instance.InstanceType,
270 | "region_id": instance.RegionId,
271 | "zone_id": instance.ZoneId,
272 | "vpc_id": instance.VpcAttributes.VpcId,
273 | "subnet_id": instance.VpcAttributes.VSwitchId,
274 | "security_group_ids": instance.SecurityGroupIds.SecurityGroupId,
275 | "state": strings.ToLower(*instance.Status),
276 | "mem_total": *instance.Memory / 1024,
277 | "cpu_total": instance.Cpu,
278 | "start_time": tools.ReplaceDateTime(*instance.StartTime),
279 | "create_server_time": tools.ReplaceDateTime(*instance.CreationTime),
280 | "expired_time": tools.ReplaceDateTime(*instance.ExpiredTime),
281 | }
282 | return info, nil
283 | }
284 |
285 | func (ali *aliyunClient) ListRegions() ([]map[string]string, error) {
286 | var regions []map[string]string
287 | describeRegionsRequest := &ecs.DescribeRegionsRequest{
288 | AcceptLanguage: tea.String("zh-CN"),
289 | ResourceType: tea.String("instance"),
290 | }
291 | // 复制代码运行请自行打印 API 的返回值
292 | regionInfo, err := ali.ecsClt.DescribeRegions(describeRegionsRequest)
293 | if err != nil {
294 | return regions, err
295 | }
296 | for _, reg := range regionInfo.Body.Regions.Region {
297 | info := map[string]string{
298 | "region_id": *reg.RegionId,
299 | "local_name": *reg.LocalName,
300 | }
301 | regions = append(regions, info)
302 | }
303 | return regions, nil
304 | }
305 |
--------------------------------------------------------------------------------
/pkg/multi_cloud_sdk/aws.go:
--------------------------------------------------------------------------------
1 | package multi_cloud_sdk
2 |
--------------------------------------------------------------------------------
/pkg/multi_cloud_sdk/core.go:
--------------------------------------------------------------------------------
1 | package multi_cloud_sdk
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "lightning-go/internal/models/multi_cloud"
7 | )
8 |
9 | type client interface {
10 | CreateInstance(PayType, hostname, instanceType, zoneId, imageId, vpcId, subnetId string, securityGroupIds []string, dryRun bool) (map[string]string, error)
11 | StartInstance(string) (string, error)
12 | StopInstance(string) (string, error)
13 | RebootInstance(string, bool) (string, error)
14 | DestroyInstance(string, bool) (string, error)
15 | ModifyInstanceName(string, string) (string, error)
16 | ListInstances() ([]map[string]interface{}, error)
17 | ListInstance(string) (map[string]interface{}, error)
18 | ListRegions() ([]map[string]string, error)
19 | }
20 |
21 | func NewFactory(cloudPlatform, accessKeyId, secretKeyId, regionId string) (clt client, err error) {
22 | switch cloudPlatform {
23 | case "ali":
24 | clt, err = NewAliEcs(accessKeyId, secretKeyId, regionId)
25 | return
26 | case "ten":
27 | clt, err = NewTenCvm(accessKeyId, secretKeyId, regionId)
28 | return
29 | }
30 | return
31 | }
32 |
33 | func NewFactoryByAccount(account, regionId string) (clt client, err error) {
34 | var (
35 | mc = multi_cloud.Account{EnName: account}
36 | )
37 | accessKeyInfo, err := mc.GetByAccount()
38 | if err != nil {
39 | return nil, errors.New(fmt.Sprintf("account: %s not found. ", account))
40 | }
41 | switch accessKeyInfo.Platform {
42 | case "ali":
43 | clt, err = NewAliEcs(accessKeyInfo.AccessKeyId, accessKeyInfo.SecretKeyId, regionId)
44 | return
45 | case "ten":
46 | clt, err = NewTenCvm(accessKeyInfo.AccessKeyId, accessKeyInfo.SecretKeyId, regionId)
47 | return
48 | }
49 | return
50 | }
51 |
--------------------------------------------------------------------------------
/pkg/multi_cloud_sdk/gmap.go:
--------------------------------------------------------------------------------
1 | package multi_cloud_sdk
2 |
3 | var (
4 | password string = "Zys$$2123ju"
5 | cloudType string = "cloud"
6 | )
7 |
--------------------------------------------------------------------------------
/pkg/multi_cloud_sdk/hw.go:
--------------------------------------------------------------------------------
1 | package multi_cloud_sdk
2 |
--------------------------------------------------------------------------------
/pkg/multi_cloud_sdk/ten.go:
--------------------------------------------------------------------------------
1 | package multi_cloud_sdk
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "lightning-go/pkg/tools"
7 | "strings"
8 |
9 | "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
10 |
11 | "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
12 | cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
13 | )
14 |
15 | type tencentcloudClient struct {
16 | regionId string
17 | cvmClt *cvm.Client
18 | }
19 |
20 | func NewTenCvm(accessKeyId, accessKeySecret, regionId string) (*tencentcloudClient, error) {
21 | credential := common.NewCredential(
22 | accessKeyId,
23 | accessKeySecret,
24 | )
25 | cpf := profile.NewClientProfile()
26 | cpf.HttpProfile.Endpoint = "cvm.tencentcloudapi.com"
27 | clt, err := cvm.NewClient(credential, regionId, cpf)
28 | if err != nil {
29 | return &tencentcloudClient{}, errors.New("")
30 | }
31 | return &tencentcloudClient{
32 | regionId: regionId,
33 | cvmClt: clt,
34 | }, nil
35 | }
36 |
37 | func (ten *tencentcloudClient) CreateInstance(instanceChargeType, hostname, instanceType, zoneId, imageId, vpcId, subnetId string, securityGroupIds []string, dryRun bool) (instanceInfo map[string]string, err error) {
38 | //fmt.Println(hostname, instanceType, zoneId, imageId, dryRun)
39 | /*
40 | instanceChargeType 实例付费类型
41 | PREPAID:预付费,即包年包月
42 | POSTPAID_BY_HOUR:按小时后付费
43 | CDHPAID:独享子机(基于专用宿主机创建,宿主机部分的资源不收费)
44 | SPOTPAID:竞价付费
45 | */
46 | instanceInfo = make(map[string]string)
47 | request := cvm.NewRunInstancesRequest()
48 |
49 | if instanceChargeType == "PostPaid" {
50 | request.InstanceChargeType = common.StringPtr("POSTPAID_BY_HOUR")
51 | } else if instanceChargeType == "PrePaid" {
52 | request.InstanceChargeType = common.StringPtr("PREPAID")
53 | request.InstanceChargePrepaid = &cvm.InstanceChargePrepaid{
54 | Period: common.Int64Ptr(1),
55 | RenewFlag: common.StringPtr("NOTIFY_AND_AUTO_RENEW"),
56 | }
57 | } else {
58 | return instanceInfo, errors.New("instanceChargeType only support PostPaid or PrePaid")
59 | }
60 | request.InstanceType = common.StringPtr(instanceType)
61 | request.Placement = &cvm.Placement{
62 | Zone: common.StringPtr(zoneId),
63 | }
64 | request.InstanceCount = common.Int64Ptr(1)
65 | request.InstanceName = common.StringPtr(hostname)
66 | request.LoginSettings = &cvm.LoginSettings{
67 | Password: common.StringPtr(password),
68 | }
69 | request.VirtualPrivateCloud = &cvm.VirtualPrivateCloud{
70 | VpcId: common.StringPtr(vpcId),
71 | SubnetId: common.StringPtr(subnetId),
72 | }
73 | request.HostName = common.StringPtr(hostname)
74 | request.ImageId = common.StringPtr(imageId)
75 | request.SecurityGroupIds = common.StringPtrs(securityGroupIds)
76 | request.SystemDisk = &cvm.SystemDisk{
77 | DiskType: common.StringPtr("CLOUD_PREMIUM"),
78 | DiskSize: common.Int64Ptr(100),
79 | }
80 | //request.DataDisks = []*cvm.DataDisk {
81 | // &cvm.DataDisk {
82 | // DiskType: common.StringPtr("LOCAL_SSD"),
83 | // DiskSize: common.Int64Ptr(100),
84 | // DeleteWithInstance: common.BoolPtr(true),
85 | // },
86 | //}
87 | request.DryRun = common.BoolPtr(dryRun)
88 |
89 | response, err := ten.cvmClt.RunInstances(request)
90 | if err != nil {
91 | return instanceInfo, err
92 | }
93 | fmt.Printf("%s", response.ToJsonString())
94 | fmt.Println(response.Response.InstanceIdSet)
95 |
96 | instanceIdSet := response.Response.InstanceIdSet
97 | //requestId := response.Response.RequestId
98 | if len(instanceIdSet) != 1 {
99 | return instanceInfo, err
100 | } else {
101 | instanceInfo["instance_id"] = common.StringValues(instanceIdSet)[0]
102 | return instanceInfo, nil
103 | }
104 | }
105 |
106 | func (ten *tencentcloudClient) StartInstance(instanceId string) (string, error) {
107 | request := cvm.NewStartInstancesRequest()
108 |
109 | request.InstanceIds = common.StringPtrs([]string{instanceId})
110 |
111 | response, err := ten.cvmClt.StartInstances(request)
112 | if err != nil {
113 | return "An API error", err
114 | }
115 | return *response.Response.RequestId, nil
116 | }
117 |
118 | func (ten *tencentcloudClient) StopInstance(instanceId string) (string, error) {
119 | request := cvm.NewStopInstancesRequest()
120 |
121 | request.InstanceIds = common.StringPtrs([]string{instanceId})
122 | request.ForceStop = common.BoolPtr(true)
123 | //request.StopType = common.StringPtr("HARD")
124 | request.StoppedMode = common.StringPtr("STOP_CHARGING")
125 |
126 | response, err := ten.cvmClt.StopInstances(request)
127 | if err != nil {
128 | return "An API error", err
129 | }
130 | return *response.Response.RequestId, nil
131 | }
132 |
133 | func (ten *tencentcloudClient) RebootInstance(instanceId string, forceStop bool) (string, error) {
134 | request := cvm.NewRebootInstancesRequest()
135 |
136 | request.InstanceIds = common.StringPtrs([]string{instanceId})
137 | //request.ForceReboot = common.BoolPtr(true)
138 | if forceStop {
139 | request.StopType = common.StringPtr("HARD")
140 | } else {
141 | request.StopType = common.StringPtr("SOFT")
142 | }
143 |
144 | response, err := ten.cvmClt.RebootInstances(request)
145 | if err != nil {
146 | return "An API error", err
147 | }
148 | return *response.Response.RequestId, nil
149 | }
150 |
151 | func (ten *tencentcloudClient) DestroyInstance(instanceId string, forceStop bool) (string, error) {
152 | request := cvm.NewTerminateInstancesRequest()
153 |
154 | request.InstanceIds = common.StringPtrs([]string{instanceId})
155 |
156 | response, err := ten.cvmClt.TerminateInstances(request)
157 | if err != nil {
158 | return "An API error", err
159 | }
160 | return *response.Response.RequestId, nil
161 | }
162 |
163 | func (ten *tencentcloudClient) ModifyInstanceName(instanceId string, instanceName string) (string, error) {
164 | request := cvm.NewModifyInstancesAttributeRequest()
165 |
166 | request.InstanceIds = common.StringPtrs([]string{instanceId})
167 | request.InstanceName = common.StringPtr(instanceName)
168 |
169 | response, err := ten.cvmClt.ModifyInstancesAttribute(request)
170 | if err != nil {
171 | return "An API error", err
172 | }
173 | return *response.Response.RequestId, nil
174 | }
175 |
176 | func (ten *tencentcloudClient) ModifyInstanceSecurityGroups(instanceId string, securityGroupIds []string) (string, error) {
177 | request := cvm.NewModifyInstancesAttributeRequest()
178 |
179 | request.InstanceIds = common.StringPtrs([]string{instanceId})
180 | request.SecurityGroups = common.StringPtrs(securityGroupIds)
181 |
182 | response, err := ten.cvmClt.ModifyInstancesAttribute(request)
183 | if err != nil {
184 | return "An API error", err
185 | }
186 | fmt.Printf("%s", response.ToJsonString())
187 | return "", nil
188 | }
189 |
190 | func (ten *tencentcloudClient) ResetInstance() ([]map[string]string, error) {
191 | return nil, nil
192 | }
193 |
194 | func (ten *tencentcloudClient) ListInstance(instanceId string) (map[string]interface{}, error) {
195 | var instanceInfo = make(map[string]interface{})
196 | request := cvm.NewDescribeInstancesRequest()
197 | request.InstanceIds = common.StringPtrs([]string{instanceId})
198 | response, err := ten.cvmClt.DescribeInstances(request)
199 | if err != nil {
200 | return instanceInfo, err
201 | }
202 | // 如果返回结果集中为空 则退出循环
203 | if len(response.Response.InstanceSet) == 0 {
204 | return instanceInfo, errors.New("Response instanceSet count 0 ")
205 | }
206 | info, err := ten.processInstance(response.Response.InstanceSet[0])
207 | if err != nil {
208 | return instanceInfo, err
209 | }
210 | instanceInfo = info
211 | return instanceInfo, nil
212 | }
213 |
214 | func (ten *tencentcloudClient) ListInstances() ([]map[string]interface{}, error) {
215 | var instanceArray []map[string]interface{}
216 | for pageNumber := 0; pageNumber <= 3; pageNumber++ {
217 | request := cvm.NewDescribeInstancesRequest()
218 | request.Offset = common.Int64Ptr(int64(pageNumber) * 100)
219 | request.Limit = common.Int64Ptr(100)
220 | response, err := ten.cvmClt.DescribeInstances(request)
221 | if err != nil {
222 | return instanceArray, err
223 | }
224 | // 如果返回结果集中为空 则退出循环
225 | if len(response.Response.InstanceSet) == 0 {
226 | break
227 | }
228 | for _, instance := range response.Response.InstanceSet {
229 | info, err := ten.processInstance(instance)
230 | if err == nil {
231 | instanceArray = append(instanceArray, info)
232 | }
233 | }
234 | }
235 | tools.PrettyPrint(instanceArray)
236 | return instanceArray, nil
237 | }
238 |
239 | func (ten *tencentcloudClient) processInstance(instance *cvm.Instance) (map[string]interface{}, error) {
240 | var (
241 | publicIP string
242 | expiredTime string
243 | instanceChargeType string
244 | )
245 |
246 | defer func() {
247 | if err := recover(); err != nil {
248 | fmt.Printf("recover %s\n", err)
249 | }
250 | }()
251 | tools.PrettyPrint(instance)
252 | if *instance.InstanceChargeType == "PREPAID" {
253 | instanceChargeType = "PrePaid"
254 | expiredTime = tools.ReplaceDateTime(*instance.ExpiredTime)
255 | } else {
256 | // POSTPAID_BY_HOUR
257 | instanceChargeType = "PostPaid"
258 | expiredTime = "0000-00-00 00:00:00"
259 | }
260 | // system and data disk
261 | disks := []map[string]interface{}{}
262 | tmpSystemDisk := make(map[string]interface{})
263 | tmpSystemDisk["disk_id"] = *instance.SystemDisk.DiskId
264 | tmpSystemDisk["disk_type"] = *instance.SystemDisk.DiskType
265 | tmpSystemDisk["disk_size"] = *instance.SystemDisk.DiskSize
266 | disks = append(disks, tmpSystemDisk)
267 | if len(instance.DataDisks) >= 1 {
268 | for _, disk := range instance.DataDisks {
269 | tmpDataDisk := make(map[string]interface{})
270 | tmpDataDisk["disk_id"] = *disk.DiskId
271 | tmpDataDisk["disk_type"] = *disk.DiskType
272 | tmpDataDisk["disk_size"] = *disk.DiskSize
273 | disks = append(disks, tmpDataDisk)
274 | }
275 | }
276 |
277 | // publicIpx
278 | if len(instance.PublicIpAddresses) == 1 {
279 | publicIP = *instance.PublicIpAddresses[0]
280 | } else {
281 | publicIP = ""
282 | }
283 | return map[string]interface{}{
284 | "type": cloudType,
285 | "platform": "ten",
286 | "instance_charge_type": instanceChargeType,
287 | "instance_id": instance.InstanceId,
288 | "private_ip": instance.PrivateIpAddresses[0],
289 | "public_ip": publicIP,
290 | "eip_ip": "",
291 | "hostname": instance.InstanceName,
292 | "image_id": instance.ImageId,
293 | "os_system": strings.Fields(*instance.OsName)[0],
294 | "os_version": instance.OsName,
295 | "instance_type": instance.InstanceType,
296 | "region_id": ten.regionId,
297 | "zone_id": instance.Placement.Zone,
298 | "vpc_id": instance.VirtualPrivateCloud.VpcId,
299 | "subnet_id": instance.VirtualPrivateCloud.SubnetId,
300 | "security_group_ids": instance.SecurityGroupIds,
301 | "state": strings.ToLower(*instance.InstanceState),
302 | "mem_total": *instance.Memory,
303 | "cpu_total": instance.CPU,
304 | "disks": disks,
305 | "start_time": "0000-00-00 00:00:00",
306 | "create_server_time": tools.ReplaceDateTime(*instance.CreatedTime),
307 | "expired_time": expiredTime,
308 | }, nil
309 | }
310 |
311 | func (ten *tencentcloudClient) ListRegions() (regions []map[string]string, err error) {
312 | request := cvm.NewDescribeRegionsRequest()
313 | response, err := ten.cvmClt.DescribeRegions(request)
314 | if err != nil {
315 | return regions, err
316 | }
317 | for _, v := range response.Response.RegionSet {
318 | m := make(map[string]string)
319 | m["region_id"] = *v.Region
320 | m["local_name"] = *v.RegionName
321 | regions = append(regions, m)
322 | }
323 | return regions, nil
324 | }
325 |
--------------------------------------------------------------------------------
/pkg/request/request.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io/ioutil"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | func Post(body []byte, header map[string]string, url string, timeout time.Duration) ([]byte, error) {
12 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
13 | if err != nil {
14 | return nil, err
15 | }
16 | for key, value := range header {
17 | req.Header.Set(key, value)
18 | }
19 | client := &http.Client{}
20 | if timeout != 0 {
21 | client.Timeout = timeout
22 | }
23 | resp, err := client.Do(req)
24 | if err != nil {
25 | return nil, err
26 | }
27 | defer resp.Body.Close()
28 | response, _ := ioutil.ReadAll(resp.Body)
29 | if resp.StatusCode != http.StatusOK {
30 | return nil, fmt.Errorf("remote server occer error and http code is %d, response: %v", resp.StatusCode, string(response))
31 | }
32 | //response, _ := ioutil.ReadAll(resp.Body)
33 | return response, nil
34 | }
35 |
36 | func Delete(url string, timeout time.Duration) ([]byte, error) {
37 | req, err := http.NewRequest("DELETE", url, nil)
38 | if err != nil {
39 | return nil, err
40 | }
41 | client := &http.Client{}
42 | if timeout != 0 {
43 | client.Timeout = timeout
44 | }
45 | resp, err := client.Do(req)
46 | if err != nil {
47 | return nil, err
48 | }
49 | defer resp.Body.Close()
50 | if resp.StatusCode != http.StatusOK {
51 | return nil, fmt.Errorf("remote server occer error and http code is %d", resp.StatusCode)
52 | }
53 | response, _ := ioutil.ReadAll(resp.Body)
54 | return response, nil
55 | }
56 | func Get(url string, timeout time.Duration) ([]byte, error) {
57 | req, err := http.NewRequest("GET", url, nil)
58 | if err != nil {
59 | return nil, err
60 | }
61 | client := &http.Client{}
62 | if timeout != 0 {
63 | client.Timeout = timeout
64 | }
65 | resp, err := client.Do(req)
66 | if err != nil {
67 | return nil, err
68 | }
69 | defer resp.Body.Close()
70 | if resp.StatusCode != http.StatusOK {
71 | return nil, fmt.Errorf("remote server occer error and http code is %d", resp.StatusCode)
72 | }
73 | response, _ := ioutil.ReadAll(resp.Body)
74 | return response, nil
75 | }
76 |
77 | func GetWithHeader(url string, header map[string]string, timeout time.Duration) ([]byte, error) {
78 | req, err := http.NewRequest("GET", url, nil)
79 | if err != nil {
80 | return nil, err
81 | }
82 | for key, value := range header {
83 | req.Header.Set(key, value)
84 | }
85 | client := &http.Client{}
86 | if timeout != 0 {
87 | client.Timeout = timeout
88 | }
89 | resp, err := client.Do(req)
90 | if err != nil {
91 | return nil, err
92 | }
93 | defer resp.Body.Close()
94 | if resp.StatusCode != http.StatusOK {
95 | return nil, fmt.Errorf("remote server occer error and http code is %d", resp.StatusCode)
96 | }
97 | response, _ := ioutil.ReadAll(resp.Body)
98 | return response, nil
99 | }
100 |
101 | func Put(body []byte, header map[string]string, url string, timeout time.Duration) ([]byte, error) {
102 | req, err := http.NewRequest("PUT", url, bytes.NewBuffer(body))
103 | if err != nil {
104 | return nil, err
105 | }
106 | for key, value := range header {
107 | req.Header.Set(key, value)
108 | }
109 | client := &http.Client{}
110 | if timeout != 0 {
111 | client.Timeout = timeout
112 | }
113 | resp, err := client.Do(req)
114 | if err != nil {
115 | return nil, err
116 | }
117 | defer resp.Body.Close()
118 | response, _ := ioutil.ReadAll(resp.Body)
119 | if resp.StatusCode != http.StatusOK {
120 | return nil, fmt.Errorf("remote server occer error and http code is %d, response: %v", resp.StatusCode, string(response))
121 | }
122 | return response, nil
123 | }
--------------------------------------------------------------------------------
/pkg/tools/convert.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "encoding/json"
5 | "regexp"
6 | "strconv"
7 | "strings"
8 | "time"
9 |
10 | "github.com/fatih/structs"
11 | )
12 |
13 | // string to int
14 | func StringToInt(s string) (int, error) {
15 | return strconv.Atoi(s)
16 | }
17 |
18 | // string to uint
19 | func StringToUint(s string) (v uint, err error) {
20 | var _int int
21 | _int, err = strconv.Atoi(s)
22 | v = uint(_int)
23 | return
24 | }
25 |
26 | func StructToMap(s interface{}) (m map[string]interface{}) {
27 | m = structs.Map(s)
28 | return
29 | }
30 |
31 | func StringToMap(s []byte) (m map[string]interface{}, err error) {
32 | m = make(map[string]interface{})
33 | err = json.Unmarshal(s, &m)
34 | return
35 | }
36 |
37 | func ByteToJson(b []byte) (data map[string]interface{}, err error) {
38 | err = json.Unmarshal(b, &data)
39 | return
40 | }
41 |
42 | func JsonToByte(data map[string]interface{}) (b []byte, err error) {
43 | b, err = json.Marshal(data)
44 | return
45 | }
46 |
47 | func ReplaceDateTime(dTime string) string {
48 | // "2020-11-02T15:38Z"
49 | s := strings.Replace(dTime, "T", " ", -1)
50 | return strings.Trim(strings.Replace(s, "Z", " ", -1), " ")
51 | }
52 |
53 | //利用正则表达式压缩字符串,去除空格或制表符
54 | func CompressStr(str string) string {
55 | if str == "" {
56 | return ""
57 | }
58 | //匹配一个或多个空白符的正则表达式
59 | reg := regexp.MustCompile("\\s+")
60 | return reg.ReplaceAllString(str, "")
61 | }
62 |
63 | func FormatNormalDateTime(dateTime time.Time) string {
64 | return dateTime.Format("2006-01-02 15:04:05")
65 | }
66 |
--------------------------------------------------------------------------------
/pkg/tools/hash.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import "encoding/base64"
4 |
5 | func Base64Encode(s string) string {
6 | return base64.StdEncoding.EncodeToString([]byte(s))
7 | }
8 |
--------------------------------------------------------------------------------
/pkg/tools/msg.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | )
9 |
10 | const (
11 | MSG_ERR = -1
12 | MSG_OK = 0
13 | UNAUTHORIZED_ACCESS = 10000
14 | )
15 |
16 | // JSONResult json
17 | type JSONResult struct {
18 | Code int `json:"code"`
19 | Message string `json:"message"`
20 | Data interface{} `json:"data"`
21 | RequestId interface{} `json:"request_id"`
22 | }
23 |
24 | type JSONResultQ struct {
25 | Code int `json:"code"`
26 | Message string `json:"message"`
27 | Data interface{} `json:"data"`
28 | Total int `json:"total"`
29 | RequestId interface{} `json:"request_id"`
30 | }
31 |
32 | // JSON 渲染
33 | func JSON(c *gin.Context, Code int, message string, data ...interface{}) {
34 | result := new(JSONResult)
35 | result.Code = Code
36 | result.Message = message
37 |
38 | if len(data) > 0 {
39 | result.Data = data[0]
40 | } else {
41 | result.Data = ""
42 | }
43 | c.JSON(http.StatusOK, result)
44 | }
45 |
46 | // Json 失败返回
47 | func JSONFailed(c *gin.Context, Code int, message string) {
48 | result := new(JSONResult)
49 | result.Code = Code
50 | result.Message = message
51 |
52 | c.JSON(http.StatusOK, result)
53 | }
54 |
55 | // Json 成功返回
56 | func JSONOk(c *gin.Context, data ...interface{}) {
57 | var result interface{}
58 | var message string
59 | fmt.Println(data, len(data))
60 | if len(data) > 0 && len(data) <= 1 {
61 | result = data[0]
62 | } else if len(data) > 1 {
63 | v, ok := data[1].(string)
64 | if ok {
65 | message = v
66 | } else {
67 | message = ""
68 | }
69 | } else {
70 | result = ""
71 | }
72 |
73 | c.JSON(http.StatusOK, JSONResult{
74 | Code: MSG_OK,
75 | Message: message,
76 | Data: result,
77 | RequestId: c.Request.Header.Get("X-Request-Id"),
78 | })
79 | }
80 |
81 | func JSONokQ(c *gin.Context, total int, data ...interface{}) {
82 | var result interface{}
83 | if len(data) > 0 {
84 | result = data[0]
85 | } else {
86 | result = ""
87 | }
88 |
89 | c.JSON(http.StatusOK, JSONResultQ{
90 | Code: MSG_OK,
91 | Message: "Ok",
92 | Data: result,
93 | Total: total,
94 | RequestId: c.Request.Header.Get("X-Request-Id"),
95 | })
96 | }
97 |
98 | type Err struct {
99 | Code int `json:"code"`
100 | Message string `json:"message"`
101 | }
102 |
103 | func (e *Err) Error() string {
104 | err, _ := json.Marshal(e)
105 | return string(err)
106 | }
107 |
108 | func New(code int, msg string) *Err {
109 | return &Err{
110 | Code: code,
111 | Message: msg,
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/pkg/tools/print.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | // 格式化打印
9 | func PrettyPrint(v interface{}) {
10 | b, err := json.MarshalIndent(v, "", " ")
11 | if err == nil {
12 | fmt.Println(string(b))
13 | } else {
14 | fmt.Println(b)
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/三方调用:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengyansheng/lightning-go/4f66fb5cf55e4c989392a550f14df926b4be93d5/pkg/三方调用
--------------------------------------------------------------------------------
/scripts/cron/cron_sync_instance.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "lightning-go/pkg/request"
7 | "lightning-go/pkg/tools"
8 | "strings"
9 | "sync"
10 | "time"
11 |
12 | "github.com/tidwall/gjson"
13 | )
14 |
15 | var (
16 | multiCloudHost = "http://go-ops.aiops724.com"
17 | opsHost = "http://ops.aiops724.com"
18 | regionUri = "/api/v1/multi-cloud/regions/"
19 | instanceUri = "/api/v1/multi-cloud/instance/"
20 | cmdbUri = "/api/v1/cmdb/instances/"
21 | cmdbUpdateUri = "/api/v1/cmdb/instances/multi_update/"
22 | accounts = []string{
23 | "ali.lightning",
24 | "ten.lightning",
25 | }
26 | )
27 |
28 | func getCloudPlatRegions(account string) (m []map[string]string, err error) {
29 | var regionId string
30 | accountArray := strings.Split(account, ".")
31 | switch accountArray[0] {
32 | case "ali":
33 | regionId = "cn-beijing"
34 | case "ten":
35 | regionId = "ap-beijing"
36 | case "aws":
37 | regionId = "cn-north-1"
38 | }
39 | url := fmt.Sprintf("%s%s?account=%s®ion_id=%s", multiCloudHost, regionUri, account, regionId)
40 | dataByte, err := request.Get(url, time.Duration(time.Second*10))
41 | if err != nil {
42 | return
43 | }
44 |
45 | if gjson.Get(string(dataByte), "code").Num == 0 {
46 | dataArray := gjson.Get(string(dataByte), "data").Array()
47 | for _, region := range dataArray {
48 | regionMap := make(map[string]string)
49 | regionMap["local_name"] = region.Map()["local_name"].Str
50 | regionMap["region_id"] = region.Map()["region_id"].Str
51 | m = append(m, regionMap)
52 | }
53 | return
54 | }
55 | return
56 | }
57 |
58 | type response struct {
59 | Code int `json:"code"`
60 | Message string `json:"message"`
61 | Data []map[string]interface{} `json:"data"`
62 | }
63 |
64 | func getInstances(account, regionId string) (m []map[string]interface{}, err error) {
65 | url := fmt.Sprintf("%s%s?account=%s®ion_id=%s", multiCloudHost, instanceUri, account, regionId)
66 | dataByte, err := request.Get(url, time.Duration(time.Second*10))
67 | if err != nil {
68 | return
69 | }
70 |
71 | var res response
72 | err = json.Unmarshal(dataByte, &res)
73 | m = res.Data
74 | return
75 | }
76 |
77 | func postCmdb(instances map[string]interface{}) (string, error) {
78 | url := fmt.Sprintf("%s%s", opsHost, cmdbUri)
79 | fmt.Printf("url:%s\n", url)
80 | headers := make(map[string]string)
81 | headers["Content-Type"] = "application/json"
82 | dataByte, err := json.Marshal(instances)
83 | if err != nil {
84 | return "json.Marshal err", err
85 | }
86 | dataByte, err = request.Post(dataByte, headers, url, time.Duration(time.Second*10))
87 | if err != nil {
88 | return string(dataByte), err
89 | }
90 | return "", nil
91 | }
92 |
93 | func postMultiCmdb(instances []map[string]interface{}) (string, error) {
94 | url := fmt.Sprintf("%s%s", opsHost, cmdbUri)
95 | fmt.Printf("url:%s\n", url)
96 | headers := make(map[string]string)
97 | headers["Content-Type"] = "application/json"
98 | dataByte, err := json.Marshal(instances)
99 | if err != nil {
100 | return "json.Marshal err", err
101 | }
102 | dataByte, err = request.Post(dataByte, headers, url, time.Duration(time.Second*10))
103 | if err != nil {
104 | return string(dataByte), err
105 | }
106 | return "", nil
107 | }
108 |
109 | func updateCmdb(instances []map[string]interface{}) (string, error) {
110 | url := fmt.Sprintf("%s%s", opsHost, cmdbUpdateUri)
111 | fmt.Printf("url:%s\n", url)
112 | headers := make(map[string]string)
113 | headers["Content-Type"] = "application/json"
114 | dataByte, err := json.Marshal(instances)
115 | if err != nil {
116 | return "json.Marshal err", err
117 | }
118 | dataByte, err = request.Put(dataByte, headers, url, time.Duration(time.Second*10))
119 | if err != nil {
120 | return string(dataByte), err
121 | }
122 | return "", nil
123 | }
124 |
125 | func main() {
126 | var wg sync.WaitGroup
127 | for _, account := range accounts {
128 | //1. 获取平台下所有的地域
129 | regionMap, err := getCloudPlatRegions(account)
130 | if err != nil {
131 | fmt.Println("getCloudPlatRegions err", err)
132 | return
133 | }
134 |
135 | //2. 获取云主机信息
136 | for _, info := range regionMap {
137 | wg.Add(1)
138 | go func(account, regionId string) {
139 | defer wg.Done()
140 | instances, err := getInstances(account, regionId)
141 | if err != nil {
142 | fmt.Println("getInstances err", err)
143 | return
144 | }
145 | if len(instances) == 0 {
146 | fmt.Printf("account: %s region_id: %s instances not found.\n", account, regionId)
147 | return
148 | }
149 | fmt.Printf("account: %s region_id: %s instances length %d.\n", account, regionId, len(instances))
150 | var dataList []map[string]interface{}
151 | for _, instanceInfo := range instances {
152 | instanceInfo["account"] = account
153 | dataList = append(dataList, instanceInfo)
154 | }
155 | tools.PrettyPrint(dataList)
156 | //3. 同步云主机信息
157 | res, err := postMultiCmdb(dataList)
158 | if err != nil {
159 | fmt.Printf("Post cmdb err: %v, response: %v\n", err, res)
160 | }
161 | //4. 变更云主机信息到cmdb
162 | res, err = updateCmdb(dataList)
163 | if err != nil {
164 | fmt.Printf("Update cmdb err: %v, response: %v\n", err, res)
165 | }
166 | }(account, info["region_id"])
167 | }
168 | }
169 | wg.Wait()
170 | fmt.Println("cron sync instance ok.")
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/scripts/业务脚本:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengyansheng/lightning-go/4f66fb5cf55e4c989392a550f14df926b4be93d5/scripts/业务脚本
--------------------------------------------------------------------------------