Devops-蓝鲸篇-05-蓝盾插件开发指引

写在前面

开发插件前,先进入插件工作台初始化一个插件,确定插件在平台中的唯一标识

工作台

可以在这里进行新增/发布/下架等管理插件的操作

功能区介绍

在这里插入图片描述

  1. 切换资源类型
  2. 新增插件
  3. 单个插件的管理入口
  4. 升级、下架、删除插件快捷入口
  5. 指引文档和插件 UI 调试工具入口

新增插件

在这里插入图片描述

  1. 标识
  • 插件在平台中的唯一标识,建议取和插件功能相关的可读性好的英文标识
  1. 调试项目
  • 插件发布过程中,可以在调试项目下将插件添加到流水线执行,对插件进行测试,保证插件功能满足预期。
  • 建议新增专用的插件调试项目,避免测试过程中影响到业务。
  1. 开发语言
  • 支持四种语言开发插件:
    • Java(推荐)
    • Python
    • Golang
    • Nodejs

开发插件

初始化好插件之后,可以开始开发插件

插件私有配置

插件级别的敏感信息,如 token、用户名密码、IP、域名等,不建议直接提交到代码库,通过工作台私有配置界面管理

在这里插入图片描述

Golang 插件开发

插件开发框架说明

插件最终打包成一个命令行可执行的命令即可,对开发框架无硬性要求 下边以 demo 插件为例示范

示例插件代码工程的整体结构如下

|- <你的插件标识>
    |- cmd
        |- application
            |- main.go
    |- hello
        |- hello.go

如何开发插件:

参考 plugin-demo-golang

Demo示例研读

plugin-demo-golang clone到本地。
来看下项目结构:

.
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── go.mod
├── go.sum
├── i18n
│   ├── message_en_US.properties
│   └── message_zh_CN.properties
├── main.go
├── task.json
└── translation
    └── translation.go

3 directories, 11 files

咦!和上面说的插件代码工程的整体架构不一样:

|- <你的插件标识>
    |- cmd
        |- application
            |- main.go
    |- hello
        |- hello.go

不过,这不重要,只要插件最终能够打包成一个命令行可执行的命令即可。

这里i18n是实现中英文国际化使用的,

├── i18n
│   ├── message_en_US.properties
│   └── message_zh_CN.properties

看下内容对比:
message_en_US.properties:

input.desc.label=desc
100001=input param [{0}] invitated

message_zh_CN.properties

input.desc.label=描述
100001=输入参数[{0}]非法

那我们就可以猜测,这里是实现输入参数[{0}]非法这句话的中英文,其中[{0}]会使用具体的参数填充。

然后i18ngenerator则是根据properties文件的配置,生成translation代码,如translation.go:

// Code generated by "i18ngenerator"; DO NOT EDIT.

package translation

// Translations
var Translations map[string][][]string = make(map[string][][]string)

func init() {
    Translations["en-US"] = [][]string{
       {
          "100001",
          "input param [{0}] invitated",
       },
       {
          "input.desc.label",
          "desc",
       },
    }
    Translations["zh-CN"] = [][]string{
       {
          "100001",
          "输入参数[{0}]非法",
       },
       {
          "input.desc.label",
          "描述",
       },
    }
}

该文件是由i18ngenerator自动生成的,不要自己改。

main函数内,实现了一个小的输出功能,简单看下源码,然后去进行测试:

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "runtime"
    "time"

    "github.com/ci-plugins/golang-plugin-sdk/api"
    "github.com/ci-plugins/golang-plugin-sdk/log"
    "github.com/ci-plugins/plugin-demo-golang/translation"
)

//go:generate i18ngenerator i18n ./translation/translation.go

type greetingParam struct {
    UserName string `json:"userName"`
    Greeting string `json:"greeting"`
}

func (a *greetingParam) String() string {
    return fmt.Sprintf("userName: %v, greeting: %v", a.UserName, a.Greeting)
}

func main() {
    runtime.GOMAXPROCS(4)
    log.Info("atom-demo-glang starts")
    defer func() {
       if err := recover(); err != nil {
          log.Error("panic: ", err)
          api.FinishBuild(api.StatusError, "panic occurs")
       }
    }()

    api.InitI18n(translation.Translations, api.GetRuntimeLanguage())
    msg, err := api.Localize("input.desc.label")
    if err != nil {
       log.Error(err)
    }
    log.Info(msg)

    helloBuild()
}

func helloBuild() {
    // 获取单个输入参数
    userName := api.GetInputParam("userName")
    log.Info("userName: ", userName)

    // 打屏
    log.Info("\nBuildInfo:")
    log.Info("Project Name:     ", api.GetProjectDisplayName())
    log.Info("Pipeline Id:      ", api.GetPipelineId())
    log.Info("Pipeline Name:    ", api.GetPipelineName())
    log.Info("Pipeline Version: ", api.GetPipelineVersion())
    log.Info("Build Id:         ", api.GetPipelineBuildId())
    log.Info("Build Num:        ", api.GetPipelineBuildNumber())
    log.Info("Start Type:       ", api.GetPipelineStartType())
    log.Info("Start UserId:     ", api.GetPipelineStartUserId())
    log.Info("Start UserName:   ", api.GetPipelineStartUserName())
    log.Info("Start Time:       ", api.GetPipelineStartTimeMills())
    log.Info("Workspace:        ", api.GetWorkspace())

    // 输入参数解析到对象
    paramData := new(greetingParam)
    api.LoadInputParam(paramData)
    log.Info(fmt.Sprintf("\n%v,%v\n", paramData.Greeting, paramData.UserName))

    // 业务逻辑
    log.Info("start build")
    build()
    time.Sleep(2 * time.Second)

    // 输出
    // 字符串输出
    strData := api.NewStringData("test")
    api.AddOutputData("strData_01", strData)

    // 文件归档输出
    artifactData := api.NewArtifactData()
    artifactData.AddArtifact("result.dat")
    api.AddOutputData("artifactData_02", artifactData)

    // 报告输出
    reportData := api.NewReportData("label_01", api.GetWorkspace()+"/report", "report.htm")
    api.AddOutputData("report_01", reportData)

    api.WriteOutput()
    log.Info("build done")
}

func build() {
    log.Info("write result.dat")
    ioutil.WriteFile(api.GetWorkspace()+"/result.dat", []byte("content"), 0644)
    log.Info("write report.htm")
    os.Mkdir(api.GetWorkspace()+"/report", os.ModePerm)
    ioutil.WriteFile(api.GetWorkspace()+"/report/report.htm", []byte("<html><head><title>Report</title></head><body><H1>This is a Report</H1></body></html>"), 0644)
}

在根目录下已经给我们预设了一个task.json文件,后面可以简单修改下这个文件来实现测试:

{
  "atomCode": "goDemo",
  "execution": {
    "language": "golang",
    "packagePath": "goDemo",
    "demands": [
      "chmod +x goDemo"
    ],
    "target": "./goDemo"
  },
  "input": {
    "greeting": {
      "label": "欢迎词",
      "default": "Glad to see you",
      "placeholder": "欢迎词",
      "type": "vuex-input",
      "desc": "欢迎词",
      "required": true,
      "disabled": false,
      "hidden": false,
      "isSensitive": false
    },
    "userName": {
      "label": "姓名",
      "default": "Mr. Huang",
      "placeholder": "姓名",
      "type": "vuex-input",
      "desc": "姓名",
      "required": true,
      "disabled": false,
      "hidden": false,
      "isSensitive": false
    }
  },
  "output": {
    "strData_01": {
      "description": "测试",
      "type": "string",
      "isSensitive": false
    }
  }
}

如何打包发布

1、进入插件代码工程目录下
2、打包
2.1、如果按照正常的demo的目录结构是需要进入cmd/application内执行build命令,因为main在此

cd cmd/application
GO111MODULE=on GOOS=linux GOARCH=amd64 go build -o bin/${executable}

2.2、这次用的demo则直接在根目录下执行build命令,因为main在根目录下

GO111MODULE=on GOOS=linux GOARCH=amd64 go build -o bin/${executable}

这里go build -o bin/ e x e c u t a b l e 会在 b i n 目录下,生成可执行文件,文件名是 {executable}会在bin目录下,生成可执行文件,文件名是 executable会在bin目录下,生成可执行文件,文件名是{executable},即项目名。

也可以自定义一个名字,如GO111MODULE=on GOOS=linux GOARCH=amd64 go build -o bin/kingtest

  1. 在任意位置新建文件夹,命名示例:release_pkg = <你的插件标识>_release
  2. 将步骤 2 生产的执行包拷贝到 <release_pkg> 下
  3. 添加 task.json 文件到 <release_pkg> 下 task.json 见示例,按照插件功能配置。
mkdir kingtest_release
cp bin/kingtest kingtest_release/kingtest
touch kingtest_release/task.json

插件配置规范: 插件配置规范
task.json 示例:

{
  "atomCode": "king-test",                  # atomCode 要与工作台录入的一致
  "execution": {
    "language": "golang",
    "packagePath": "kingtest",              # 发布包中插件安装包的相对路径
    "demands": [
      "echo start run chmod +x kingtest",   # 插件启动前需要执行的安装命令,顺序执行
      "chmod +x kingtest",                  # 插件启动前需要执行的安装命令,顺序执行
      "echo stop run chmod +x kingtest",    # 插件启动前需要执行的安装命令,顺序执行
    ],
    "target": "./kingtest"
  },
  "input": {
    "greeting": {
      "label": "欢迎词",
      "default": "Glad to see you",
      "placeholder": "欢迎词",
      "type": "vuex-input",
      "desc": "欢迎词",
      "required": true,
      "disabled": false,
      "hidden": false,
      "isSensitive": false
    },
    "userName": {
      "label": "姓名",
      "default": "Mr. Huang",
      "placeholder": "姓名",
      "type": "vuex-input",
      "desc": "姓名",
      "required": true,
      "disabled": false,
      "hidden": false,
      "isSensitive": false
    }
  },
  "output": {
    "strData_01": {
      "description": "测试",
      "type": "string",
      "isSensitive": false
    }
  }
}
  1. 在 <release_pkg> 目录下,把所有文件打成 zip 包即可
cd kingtest_release && zip kingtest_release.zip kingtest task.json

zip包结构示例:

|- kingtest_release.zip         # 发布包
   |- kingtest                  # 插件执行包
   |- task.json                 # 插件配置文件

打包完成后,在插件工作台提单发布,即可测试或发布插件

上传一个流水线插件

开发好插件之后,通过研发商店工作台,将插件发布到研发商店,提供给用户添加到流水线中使用。

入口

在工作台列表,点击如下入口发起发布流程:
在这里插入图片描述
首次发布时,入口名为上架
后续更新版本时,入口名为升级

或者在插件发布管理->版本管理界面发起发布流程:
在这里插入图片描述

填写插件相关信息/上传插件发布包

上架/升级插件时,可以修改插件的基本信息,如下所示:
在这里插入图片描述

  1. 适用 Job 类型:
  • 和流水线 Job 类型对应,请按照插件实际适用情况选择
  • 若选错,需新增版本修改
  1. 发布包:
  • task.json 中的 atomCode 需和 新增插件时填写的标识一致,否则上传会失败

测试/发布插件

填写好信息,提交后,进入发布流程,可以测试->重新传包->测试,直至插件满足预期后,手动继续流程将插件发布到研发商店

在这里插入图片描述

  1. 测试:点击后跳转到插件调试项目的流水线服务下,可以将当前插件添加到流水线,验证 UI、功能是否满足预期
  2. 重新传包:当测试发现问题,修复后,重新上传发布包,再次进行测试
  3. 继续:测试 OK,满足预期后,确认提交发布
  4. 取消发布:发布过程中,随时可以终止发布

遇见的几个错误

无权限执行

在测试中遇见一个问题:无权限执行
在这里插入图片描述
execution->demands增加一个命令chmod +x kingtest即可解决

发布进度里重新传包持续报错task.json格式错误

还有个问题,在发布进度里重新传包时,一直报错task.json格式错误,但实际格式是对的!
触发的原因暂不知道,但是确实是一个隐藏的bug。

在这里插入图片描述
直接点击继续,然后走升级插件的方式可以正常使用。

cannot execute binary file: Exec format error

这里的问题是我造成的,最初我在mac环境下编译的可执行文件,命令是:

# mac下执行
go build -o bin/kingtest

但是插件里选择的编译环境是linux。
在这里插入图片描述
解决方式:让插件选择的编译环境和可执行文件的平台统一。

我这里选择重新编译下可执行文件,采用在mac平台交叉编译linux平台可执行文件的方式。

GO111MODULE=on GOOS=linux GOARCH=amd64 go build -o bin/kingtest

也可以新建一个插件,编译环境选择mac。

运行结果

流水线结果:
在这里插入图片描述
输出结果:

[Plugin info]
=====================================================================
Task           : king-test
Description    : bk插件测试
Version        : 1.0.3
Author         : huari
Help           : More Information
=====================================================================
-----
[Input]
input(normal): (欢迎词)greeting=Glad to see you
input(normal): (姓名)userName=Mr. Huang
-----
[Install plugin]
-----
start run chmod +x kingtest
stop run chmod +x kingtest
atom-demo-glang starts
描述
userName:  Mr. Huang

BuildInfo:
Project Name:      GOPS
Pipeline Id:       p-8967ed52b08847c8a5b0140937db0975
Pipeline Name:     king-test
Pipeline Version:  11
Build Id:          b-78947b5c39f34b32bbafb803042d1e22
Build Num:         15
Start Type:        MANUAL
Start UserId:      huari
Start UserName:    huari
Start Time:        1733298107286
Workspace:         /data/devops/workspace

Glad to see you,Mr. Huang
start build
write result.dat
write report.htm
build done
[Output]
1 file match: 
  /data/devops/workspace/result.dat
prepare to upload 7 B
1/1 file(s) finished
output(except): artifactData_02=result.dat
入口文件检测完成
上传自定义产出物成功,共产生了1个文件
output(except): report_01=report.htm
output(normal): strData_01=test
-----
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值