From 15d0632c57f2f3b063e21d428f617162eb1a97b4 Mon Sep 17 00:00:00 2001 From: Ambition <918632536@qq.com> Date: Tue, 18 Jun 2024 21:31:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=93=9D=E9=B2=B8=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bcs-services/bcs-bscp/Makefile | 8 + .../bcs-bscp/cmd/api-server/cmd/migrate.go | 9 +- bcs-services/bcs-bscp/pkg/iam/apigw/apigw.go | 568 ++++++++++++++++++ .../bcs-bscp/pkg/iam/apigw/sync_docs.go | 149 +++++ bcs-services/bcs-bscp/pkg/version/version.go | 13 +- 5 files changed, 742 insertions(+), 5 deletions(-) create mode 100644 bcs-services/bcs-bscp/pkg/iam/apigw/apigw.go create mode 100644 bcs-services/bcs-bscp/pkg/iam/apigw/sync_docs.go diff --git a/bcs-services/bcs-bscp/Makefile b/bcs-services/bcs-bscp/Makefile index ee638eb06d..bd9342d6c8 100644 --- a/bcs-services/bcs-bscp/Makefile +++ b/bcs-services/bcs-bscp/Makefile @@ -1,3 +1,10 @@ +# init the build information +ifdef HASTAG + GITTAG=${HASTAG} +else + GITTAG=$(shell git describe --always) +endif + # version PRO_DIR = $(shell pwd) BUILDTIME = $(shell date +%Y-%m-%dT%T%z) @@ -17,6 +24,7 @@ ifeq ("$(VERSION)", "") export OUTPUT_DIR = ${PRO_DIR}/build/bk-bscp export LDVersionFLAG = "-X github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/version.BUILDTIME=${BUILDTIME} \ -X github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/version.GITHASH=${GITHASH} \ + -X github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/version.GITTAG=${GITTAG} \ -X github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/version.DEBUG=${DEBUG}" else export OUTPUT_DIR = ${PRO_DIR}/build/bk-bscp-${VERSION} diff --git a/bcs-services/bcs-bscp/cmd/api-server/cmd/migrate.go b/bcs-services/bcs-bscp/cmd/api-server/cmd/migrate.go index 17003c169a..7d10656528 100644 --- a/bcs-services/bcs-bscp/cmd/api-server/cmd/migrate.go +++ b/bcs-services/bcs-bscp/cmd/api-server/cmd/migrate.go @@ -15,11 +15,14 @@ package cmd import ( "fmt" + "time" "github.com/spf13/cobra" "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/cc" + "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/iam/apigw" "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/logs" + "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/version" ) var migrateCmd = &cobra.Command{ @@ -42,7 +45,11 @@ var migrateInitApigatewayCmd = &cobra.Command{ logs.InitLogger(cc.ApiServer().Log.Logs()) - fmt.Println("Need to be implemented") + if err := apigw.ReleaseSwagger(cc.ApiServer(), "zh", + fmt.Sprintf("%s+%s", version.GITTAG, time.Now().Format("20060102150405"))); err != nil { + fmt.Println(err) + return + } }, } diff --git a/bcs-services/bcs-bscp/pkg/iam/apigw/apigw.go b/bcs-services/bcs-bscp/pkg/iam/apigw/apigw.go new file mode 100644 index 0000000000..47cb189a64 --- /dev/null +++ b/bcs-services/bcs-bscp/pkg/iam/apigw/apigw.go @@ -0,0 +1,568 @@ +/* + * Tencent is pleased to support the open source community by making Blueking Container Service available. + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package apigw document sync gateway +package apigw + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/cc" + "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/rest/client" +) + +var ( + endpoint = "http://bkapi.sit.bktencent.com/api/bk-apigateway/prod/api/v1/apis/" + syncApi = "%s/%s/sync/" + syncStage = "%s/%s/stages/sync/" + syncResources = "%s/%s/resources/sync/" + importResourceDocsBySwagger = "%s/%s/resource-docs/import/by-swagger/" + createResourceVersion = "%s/%s/resource_versions/" + getLatestResourceVersion = "%s/%s/resource_versions/latest/" + release = "%s/%s/resource_versions/release/" + applyPermissions = "%s/%s/permissions/apply/" + getApigwPublicKey = "%s/%s/public_key/" +) + +// ApiGw document sync gateway interface +type ApiGw interface { + // SyncApi 同步网关,如果网关不存在,创建网关,如果网关已存在,更新网关 + SyncApi(gwName string, req *SyncApiReq) (*SyncApiResp, error) + // SyncStage 同步网关环境,如果环境不存在,创建环境,如果已存在,则更新 + SyncStage(gwName string, req *SyncStageReq) (*SyncStageResp, error) + // SyncResources 同步资源 + SyncResources(gwName string, req *SyncResourcesReq) (*SyncResourcesResp, error) + // ImportResourceDocsBySwagger 根据 swagger 描述文件,导入资源文档 + ImportResourceDocsBySwagger(gwName string, req *ImportResourceDocsBySwaggerReq) ( + *ImportResourceDocsBySwaggerResp, error) + // CreateResourceVersion 创建资源版本 + CreateResourceVersion(gwName string, req *CreateResourceVersionReq) (*CreateResourceVersionResp, error) + // GetLatestResourceVersion 获取网关最新的资源版本 + GetLatestResourceVersion(gwName string) (*GetLatestResourceVersionResp, error) + // Release 发布版本 + Release(gwName string, req *ReleaseReq) (*ReleaseResp, error) + // ApplyPermissions 申请网关API访问权限 + ApplyPermissions(gwName string, req *ApplyPermissionsReq) (*ApplyPermissionsResp, error) + // GetApigwPublicKey 获取网关公钥 + GetApigwPublicKey(gwName string) (*GetApigwPublicKeyResp, error) +} + +// NewApiGw 初始化网关 +func NewApiGw(opt cc.ApiServerSetting) (ApiGw, error) { + + c, err := client.NewClient(nil) + if err != nil { + return nil, err + } + return &apiGw{ + client: c, + opt: opt, + }, nil +} + +type apiGw struct { + client *http.Client + opt cc.ApiServerSetting +} + +// SyncApi 同步网关,如果网关不存在,创建网关,如果网关已存在,更新网关 +func (a *apiGw) SyncApi(gwName string, req *SyncApiReq) (*SyncApiResp, error) { + url := fmt.Sprintf(syncApi, endpoint, gwName) + + jsonData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("sync api serialization JSON failed: %s", err.Error()) + } + + request, err := a.newRequest("POST", url, jsonData) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(SyncApiResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + return result, nil +} + +// SyncStage 同步网关环境,如果环境不存在,创建环境,如果已存在,则更新 +func (a *apiGw) SyncStage(gwName string, req *SyncStageReq) (*SyncStageResp, error) { + url := fmt.Sprintf(syncStage, endpoint, gwName) + + jsonData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("sync stage serialization JSON failed: %s", err.Error()) + } + + request, err := a.newRequest("POST", url, jsonData) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(SyncStageResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +// SyncResources 同步资源 +func (a *apiGw) SyncResources(gwName string, req *SyncResourcesReq) (*SyncResourcesResp, error) { + + url := fmt.Sprintf(syncResources, endpoint, gwName) + + jsonData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("sync resources serialization JSON failed: %s", err.Error()) + } + + request, err := a.newRequest("POST", url, jsonData) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(SyncResourcesResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +// ImportResourceDocsBySwagger 根据 swagger 描述文件,导入资源文档 +func (a *apiGw) ImportResourceDocsBySwagger(gwName string, req *ImportResourceDocsBySwaggerReq) ( + *ImportResourceDocsBySwaggerResp, error) { + url := fmt.Sprintf(importResourceDocsBySwagger, endpoint, gwName) + + jsonData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("import resource docs by swagger serialization JSON failed: %s", err.Error()) + } + + request, err := a.newRequest("POST", url, jsonData) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(ImportResourceDocsBySwaggerResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +// CreateResourceVersion implements ApiGw. +func (a *apiGw) CreateResourceVersion(gwName string, req *CreateResourceVersionReq) ( + *CreateResourceVersionResp, error) { + + url := fmt.Sprintf(createResourceVersion, endpoint, gwName) + + jsonData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("create resource version serialization JSON failed: %s", err.Error()) + } + + request, err := a.newRequest("POST", url, jsonData) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(CreateResourceVersionResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +// GetLatestResourceVersion implements ApiGw. +func (a *apiGw) GetLatestResourceVersion(gwName string) (*GetLatestResourceVersionResp, error) { + + url := fmt.Sprintf(getLatestResourceVersion, endpoint, gwName) + + request, err := a.newRequest("GET", url, nil) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(GetLatestResourceVersionResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +// Release 发布版本 +func (a *apiGw) Release(gwName string, req *ReleaseReq) (*ReleaseResp, error) { + + url := fmt.Sprintf(release, endpoint, gwName) + + jsonData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("release serialization JSON failed: %s", err.Error()) + } + + request, err := a.newRequest("POST", url, jsonData) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(ReleaseResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +// ApplyPermissions implements ApiGw. +func (a *apiGw) ApplyPermissions(gwName string, req *ApplyPermissionsReq) (*ApplyPermissionsResp, error) { + url := fmt.Sprintf(applyPermissions, endpoint, gwName) + + jsonData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("apply permissions serialization JSON failed: %s", err.Error()) + } + + request, err := a.newRequest("POST", url, jsonData) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(ApplyPermissionsResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +// GetApigwPublicKey implements ApiGw. +func (a *apiGw) GetApigwPublicKey(gwName string) (*GetApigwPublicKeyResp, error) { + url := fmt.Sprintf(getApigwPublicKey, endpoint, gwName) + + request, err := a.newRequest("GET", url, nil) + if err != nil { + return nil, err + } + + resp, err := a.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + result := new(GetApigwPublicKeyResp) + + // 将响应体解析为结构体 + if err = json.NewDecoder(resp.Body).Decode(result); err != nil { + return nil, err + } + + return result, nil +} + +func (a *apiGw) newRequest(method, url string, body []byte) (*http.Request, error) { + + var req *http.Request + var err error + + if len(body) > 0 { + req, err = http.NewRequest(method, url, bytes.NewBuffer(body)) + } else { + req, err = http.NewRequest(method, url, nil) + } + + if err != nil { + return nil, err + } + + // 设置请求头 + req.Header.Set("X-Bkapi-Authorization", fmt.Sprintf(`{"bk_app_code": "%s", "bk_app_secret": "%s"}`, + a.opt.Esb.AppCode, a.opt.Esb.AppSecret)) + req.Header.Set("Content-Type", "application/json") + + return req, nil +} + +// ImportResourceDocsBySwaggerReq 输入参数 +type ImportResourceDocsBySwaggerReq struct { + // Language 文档语言,可选值:zh 表示中文,en 表示英文 + Language string `json:"language"` + // Swagger 描述文件的内容 + Swagger string `json:"swagger"` +} + +// ImportResourceDocsBySwaggerResp 输出参数 +type ImportResourceDocsBySwaggerResp struct { + Code int `json:"code"` + Data interface{} `json:"data"` + Result bool `json:"result"` + Message string `json:"message"` +} + +// SyncApiReq 输入参数 +type SyncApiReq struct { + // Description 网关描述 + Description string `json:"description"` + // Maintainers 网关管理员 + Maintainers []string `json:"maintainers"` + // IsPublic 网关是否公开 + IsPublic bool `json:"is_public"` +} + +// SyncApiResp 输出参数 +type SyncApiResp struct { + Data SyncData `json:"data"` + Code int `json:"code"` + Result bool `json:"result"` + Message string `json:"message"` +} + +// SyncData xxx +type SyncData struct { + ID int `json:"id"` + Name string `json:"name"` +} + +// SyncStageReq 输入参数 +type SyncStageReq struct { + Name string `json:"name"` + Description string `json:"description"` + Vars map[string]string `json:"vars"` + ProxyHttp ProxyHttp `json:"proxy_http"` +} + +// ProxyHttp xxx +type ProxyHttp struct { + Timeout int `json:"timeout"` + Upstreams Upstreams `json:"upstreams"` + TransformHeaders `json:"transform_headers"` +} + +// Upstreams xxx +type Upstreams struct { + Loadbalance string `json:"loadbalance"` + Hosts []Host `json:"hosts"` +} + +// Host xx +type Host struct { + Host string `json:"host"` + Weight int `json:"weight"` +} + +// TransformHeaders xxx +type TransformHeaders struct { + Set map[string]string `json:"set"` + Delete []string `json:"delete"` +} + +// SyncResourcesReq 输入参数 +type SyncResourcesReq struct { + Content string `json:"content"` + Delete bool `json:"delete"` +} + +// SyncStageResp 输出参数 +type SyncStageResp struct { + Data SyncData `json:"data"` + Code int `json:"code"` + Result bool `json:"result"` + Message string `json:"message"` +} + +// SyncResourcesResp 输出参数 +type SyncResourcesResp struct { + Code int `json:"code"` + Message string `json:"message"` + Data SyncResourcesData `json:"data"` +} + +// SyncResourcesData xxx +type SyncResourcesData struct { + Added []Added `json:"added"` + Updated []Updated `json:"updated"` + Deleted []Deleted `json:"deleted"` +} + +// Added xxx +type Added struct { + ID int `json:"id"` +} + +// Updated xxx +type Updated struct { + ID int `json:"id"` +} + +// Deleted xxx +type Deleted struct { + ID int `json:"id"` +} + +// ReleaseReq 输入参数 +type ReleaseReq struct { + Version string `json:"version"` + StageNames []string `json:"stage_names"` + Comment string `json:"comment"` +} + +// ReleaseResp 输出参数 +type ReleaseResp struct { + Code int `json:"code"` + Message string `json:"message"` + Data ReleaseData `json:"data"` +} + +// ReleaseData xxx +type ReleaseData struct { + Version string `json:"version"` + StageNames []string `json:"stage_names"` +} + +// CreateResourceVersionReq 输入参数 +type CreateResourceVersionReq struct { + Version string `json:"version"` + Title string `json:"title"` + Comment string `json:"comment"` +} + +// CreateResourceVersionResp 输出参数 +type CreateResourceVersionResp struct { + Code int `json:"code"` + Message string `json:"message"` + Data CreateResourceVersionData `json:"data"` +} + +// CreateResourceVersionData xxx +type CreateResourceVersionData struct { + ID int `json:"id"` + Version string `json:"version"` + Title string `json:"title"` +} + +// ApplyPermissionsReq 输入参数 +type ApplyPermissionsReq struct { + TargetAppCode string `json:"target_app_code"` + ExpireDays int `json:"expire_days"` + GrantDimension string `json:"grant_dimension"` + Reason string `json:"reason"` +} + +// ApplyPermissionsResp 输出参数 +type ApplyPermissionsResp struct { + Code int `json:"code"` + Message string `json:"message"` + Data ApplyPermissionData `json:"data"` +} + +// ApplyPermissionData xxx +type ApplyPermissionData struct { + RecordID int `json:"record_id"` +} + +// GetApigwPublicKeyResp 输出参数 +type GetApigwPublicKeyResp struct { + Code int `json:"code"` + Message string `json:"message"` + Data GetApigwPublicKeyData `json:"data"` +} + +// GetApigwPublicKeyData xxx +type GetApigwPublicKeyData struct { + Issuer string `json:"issuer"` + PublicKey string `json:"public_key"` +} + +// GetLatestResourceVersionResp 输出参数 +type GetLatestResourceVersionResp struct { + Code int `json:"code"` + Message string `json:"message"` + Data GetLatestResourceVersionData `json:"data"` +} + +// GetLatestResourceVersionData xxx +type GetLatestResourceVersionData struct { + Version string `json:"version"` +} diff --git a/bcs-services/bcs-bscp/pkg/iam/apigw/sync_docs.go b/bcs-services/bcs-bscp/pkg/iam/apigw/sync_docs.go new file mode 100644 index 0000000000..3942e2b099 --- /dev/null +++ b/bcs-services/bcs-bscp/pkg/iam/apigw/sync_docs.go @@ -0,0 +1,149 @@ +/* + * Tencent is pleased to support the open source community by making Blueking Container Service available. + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package apigw document sync gateway +package apigw + +import ( + "fmt" + + "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/docs" + "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/cc" +) + +const ( + name = "bk-bscp-test" + env = "prod" + description = "bk-bscp-test 网关描述" + host = "http://bscp-api.sit.bktencent.com" +) + +// ReleaseSwagger 导入swagge 文档 +func ReleaseSwagger(opt cc.ApiServerSetting, language, version string) error { // nolint + // 获取需要导入的文档 + swaggerData, err := docs.Assets.ReadFile("swagger/bkapigw.swagger.json") + if err != nil { + return fmt.Errorf("reads and returns the content of the named file failed, err: %s", err.Error()) + } + // 初始化网关 + gw, err := NewApiGw(opt) + if err != nil { + return fmt.Errorf("init api gateway failed, err: %s", err.Error()) + } + + // 创建或者更新网关 + syncApiResp, err := gw.SyncApi(name, &SyncApiReq{ + Description: description, + Maintainers: []string{"admin"}, + IsPublic: true, + }) + if err != nil { + return fmt.Errorf("create or update gateway failed, err: %s", err.Error()) + } + if syncApiResp.Code != 0 && syncApiResp.Data.ID == 0 { + return fmt.Errorf("create or update gateway failed, err: %s", syncApiResp.Message) + } + + // 同步环境 + syncStageResp, err := gw.SyncStage(syncApiResp.Data.Name, &SyncStageReq{ + Name: env, + Vars: map[string]string{}, + ProxyHttp: ProxyHttp{ + Timeout: 30, + Upstreams: Upstreams{ + Loadbalance: "roundrobin", + Hosts: []Host{{ + Host: host, + Weight: 100, + }}, + }, + TransformHeaders: TransformHeaders{ + Set: map[string]string{}, + Delete: []string{}, + }, + }, + }) + if err != nil { + return fmt.Errorf("sync stage failed, err: %s", err.Error()) + } + if syncStageResp.Code != 0 && syncStageResp.Data.ID == 0 { + return fmt.Errorf("sync stage failed, err: %s", syncStageResp.Message) + } + + // 同步资源 + syncResourcesResp, err := gw.SyncResources(syncApiResp.Data.Name, &SyncResourcesReq{ + Content: string(swaggerData), + Delete: false, + }) + if err != nil { + return fmt.Errorf("sync resource failed, err: %s", err.Error()) + } + if syncResourcesResp.Code != 0 { + return fmt.Errorf("sync resource failed, err: %s", syncResourcesResp.Message) + } + + // 导入swagger文档 + importResp, err := gw.ImportResourceDocsBySwagger(syncApiResp.Data.Name, &ImportResourceDocsBySwaggerReq{ + Language: language, + Swagger: string(swaggerData), + }) + + if err != nil { + return fmt.Errorf("import resource docs by swagger failed, err: %s", err.Error()) + } + if importResp.Code != 0 { + return fmt.Errorf("import resource docs by swagger failed, err: %s", importResp.Message) + } + + // 获取资源最新版本 + lrvResp, err := gw.GetLatestResourceVersion(syncApiResp.Data.Name) + if err != nil { + return fmt.Errorf("get latest resource version failed, err: %s", err.Error()) + } + if lrvResp.Code != 0 { + return fmt.Errorf("get latest resource version failed, err: %s", lrvResp.Message) + } + + // 如果版本为空或者自定义版本和当前版本不一致时创建版本 + if version == "" || version != lrvResp.Data.Version { + // 创建资源版本 + createResourceVersionResp, cErr := gw.CreateResourceVersion(syncApiResp.Data.Name, &CreateResourceVersionReq{ + Version: version, + Title: fmt.Sprintf("%s 版本", version), + Comment: "正式环境", + }) + if cErr != nil { + return fmt.Errorf("create resource version failed, err: %s", cErr.Error()) + } + if createResourceVersionResp.Code != 0 && createResourceVersionResp.Data.ID == 0 { + return fmt.Errorf("create resource version failed, err: %s", createResourceVersionResp.Message) + } + version = createResourceVersionResp.Data.Version + } + + // 发布版本 + releaseResp, err := gw.Release(syncApiResp.Data.Name, &ReleaseReq{ + Version: version, + StageNames: []string{env}, + Comment: fmt.Sprintf("发布 %s 版本", version), + }) + if err != nil { + return fmt.Errorf("release failed, err: %s", err.Error()) + } + if releaseResp.Code != 0 { + return fmt.Errorf("release failed, err: %s", releaseResp.Message) + } + + fmt.Println("swagger sync successful") + + return nil +} diff --git a/bcs-services/bcs-bscp/pkg/version/version.go b/bcs-services/bcs-bscp/pkg/version/version.go index 9fe33d75fa..8088c1504d 100644 --- a/bcs-services/bcs-bscp/pkg/version/version.go +++ b/bcs-services/bcs-bscp/pkg/version/version.go @@ -54,6 +54,9 @@ var ( // GITHASH git hash for release. GITHASH = "unknown" + // GITTAG xxx + GITTAG = "1.0.0" + // DEBUG if enable debug. DEBUG = "false" @@ -87,11 +90,11 @@ func FormatVersion(prefix string, format Format) string { if prefix != "" { prefix += " " } - rawFormat := fmt.Sprintf("%sVersion : %s\nBuildTime: %s\nGitHash : %s\nGoVersion: %s\nClientType: %s", - prefix, VERSION, BUILDTIME, GITHASH, GoVersion, CLIENTTYPE) - jsonFormat := fmt.Sprintf(`%s{"Version": "%s", "BuildTime": "%s", "GitHash": "%s", "GoVersion": "%s", + rawFormat := fmt.Sprintf("%sVersion :%s\nBuildTime :%s\nGitHash :%s\nGITTAG :%s\nGoVersion :%s\nClientType:%s", + prefix, VERSION, BUILDTIME, GITHASH, GITTAG, GoVersion, CLIENTTYPE) + jsonFormat := fmt.Sprintf(`%s{"Version": "%s", "BuildTime": "%s", "GitHash": "%s", "GITHASH": "%s", "GoVersion": "%s", "ClientType": "%s"}`, - prefix, VERSION, BUILDTIME, GITHASH, GoVersion, CLIENTTYPE) + prefix, VERSION, BUILDTIME, GITHASH, GITTAG, GoVersion, CLIENTTYPE) switch format { case Row: @@ -114,6 +117,7 @@ func Version() *SysVersion { return &SysVersion{ Version: VERSION, Hash: GITHASH, + GITTAG: GITTAG, Time: BUILDTIME, GoVersion: GoVersion, ClientType: CLIENTTYPE, @@ -153,6 +157,7 @@ func parseVersion(v string) ([3]uint32, error) { type SysVersion struct { Version string `json:"version"` Hash string `json:"hash"` + GITTAG string `json:"git_tag"` Time string `json:"time"` GoVersion string `json:"go_version"` ClientType string `json:"client_type"`