首页 > 其他分享 >三方仓库如何实现Zadig流水线自动触发

三方仓库如何实现Zadig流水线自动触发

时间:2023-05-30 18:31:46浏览次数:42  
标签:三方 zadig string json Zadig workflowTask 流水线 type name



!! 大家好,我是乔克,一个爱折腾的运维工程,一个睡觉都被自己丑醒的云原生爱好者。

作者:乔克
公众号:运维开发故事
博客:www.jokerbai.com


最近因为公司的产研调整,决定将代码仓库从本地的 Gitlab 迁移到云效的 Codeup,不是 Gitlab 不够好,而是 Codeup 在度量、安全等方面比原生的 Gitlab 要好,再则公司的产研管理也迁移到了云效,也为了统一化管理。

有同学可能会问,都用云效了,为什么不直接用它的 AppStack,还要用 Zadig?

AppStack 还处于发展阶段,还有以下问题不适合我们现阶段的需求:

  1. AppStack 不支持管理私有云 Kubernetes 集群(没有公网入口)。
  2. AppStack 不支持 Helm 类应用,改造工作比较大

再则,我也是 Zadig 开源产品的忠实粉丝~~!

但是,Zadig 对非标准的代码仓库的支持力度有限,比如:

  1. 非标准代码仓库不支持列出仓库列表,需要自己手动填写
  2. 非标准代码仓库创建的流水线原生不支持 Webhook 触发

经过综合考虑,手动填写代码仓库信息以及不支持 Webhook 并不影响整体的使用,只是会影响部分项目的工作效率。

但是,为了最小程度上影响原有的产研节奏,我还是准备自己实现三方仓库的 Webhook 触发 Zadig 流水线。因为本身也不复杂。

整体思路

三方仓库如何实现Zadig流水线自动触发_zadig

image.png

实现不复杂,也就是接收到 webhook 触发动作,解析内容,根据需要触发相应的流水线接口。截至目前(v1.17.0)zadig 的触发流水线接口已经可以正常使用了。

开始搬砖

封装 Zadig API

首先封装一下 Zadig 的 API。为了方便使用,之前的弄了一个 go-zadig 项目(https://github.com/joker-bai/go-zadig),这两天将其做了更新,支持最新的 1.17.0 版本的 API。

主要增加了以下内容:

// 执行工作流
type ExecWorkflowTaskOptions struct {
 WorkflowName string        `json:"workflow_name"`
 ProjectName  string        `json:"project_name"`
 Input        WorkflowInput `json:"input"`
}

type WorkflowInput struct {
 TargetEnv string         `json:"target_env,omitempty"`
 Build     ExecBuildArgs  `json:"build"`
 Deploy    ExecDeployArgs `json:"deploy"`
}

type ExecBuildArgs struct {
 Enabled     bool               `json:"enabled"`
 ServiceList []BuildServiceInfo `json:"service_list"`
}

type BuildServiceInfo struct {
 ServiceModule string           `json:"service_module"`
 ServiceName   string           `json:"service_name"`
 RepoInfo      []RepositoryInfo `json:"repo_info"`
 Inputs        []UserInput      `json:"inputs"`
}

type RepositoryInfo struct {
 CodehostName  string `json:"codehost_name"`
 RepoNamespace string `json:"repo_namespace"`
 RepoName      string `json:"repo_name"`
 Branch        string `json:"branch"`
 PR            int    `json:"pr"`
}

type UserInput struct {
 Key   string `json:"key"`
 Value string `json:"value"`
}

type ExecDeployArgs struct {
 Enabled     bool                `json:"enabled"`
 Source      string              `json:"source"`
 ServiceList []DeployServiceInfo `json:"service_list"`
}

type DeployServiceInfo struct {
 ServiceModule string `json:"service_module"`
 ServiceName   string `json:"service_name"`
 Image         string `json:"image"`
}

type ExecWorkflowTaskResponse struct {
 ProjectName  string `json:"project_name,omitempty"`
 WorkflowName string `json:"workflow_name,omitempty"`
 TaskID       int64  `json:"task_id,omitempty"`
}

func (w *WorkflowService) ExecWorkflowTask(opt *ExecWorkflowTaskOptions, options ...RequestOptionFunc) (*ExecWorkflowTaskResponse, *Response, error) {
 path := "openapi/workflows/product/task"
 req, err := w.client.NewRequest(http.MethodPost, path, opt, options)
 if err != nil {
  return nil, nil, err
 }

 task := new(ExecWorkflowTaskResponse)
 resp, err := w.client.Do(req, &task)
 if err != nil {
  return nil, resp, err
 }

 return task, resp, err
}

这部分是执行标准工作流的接口。自定义工作流之前已经实现了,并没有什么变化。

开发 Http Server

由于 Zadig 原生不支持三方仓库的 Webhook,要实现不外乎两种:

  1. 自己修改 Zadig 源码,实现这部分功能。
  2. 找一个中间商,由它来协调。

修改源码的好处是可以不需要再单独对数据这块做太多处理,直接用现成的。但我选择了后者,主要是因为菜,源码改起来费劲。

!! 我使用的是 Uber 的 fx 框架。其实用什么框架不重要,本身的逻辑就很简单,我只是选了一个用起来比较简单和顺手的。

(1)、定义数据结构

package entity

type ZadigWorkflowTask struct {
 ID            int    `json:"id"`
 ProjectName   string `json:"project_name"`   // 项目名
 ServiceModule string `json:"service_module"` // 服务组件名称
 ServiceName   string `json:"service_name"`   // 服务名
 CodehostName  string `json:"codehost_name"`  // 代码源别名
 RepoNamespace string `json:"repo_namespace"` // 仓库组名
 RepoName      string `json:"repo_name"`      // 仓库名
 WorkflowType  string `json:"workflow_type"`  // 工作流类型: product/custom
 JobName       string `json:"job_name"`       // 任务名 workflow_type为custom生效
 JobType       string `json:"job_type"`       // 任务类型 workflow_type为custom生效
 Registry      string `json:"registry"`       // 镜像仓库  workflow_type为custom生效
}

func (z *ZadigWorkflowTask) TableName() string {
 return "zadig_workflow_task"
}

type ZadigWorkflowName struct {
 ID           int    `json:"id"`
 WorkflowName string `json:"workflow_name"` // workflow名
 Branch       string `json:"branch"`        // 分支
 ProjectName  string `json:"project_name"`  // 项目名
 TargetEnv    string `json:"target_env"`    // 目标环境
}

func (z *ZadigWorkflowName) TableName() string {
 return "zadig_workflow_name"
}

我定义的比较简单,命名也比较随意。主要的字段就是 Zadig API 需要的字段,其他不需要的就没写了。

(2)实现 Zadig 触发标准和非标准流水线

package zadig

import (
 "github.com/joker-bai/go-zadig"
 "joker-bai/go-webhook/config"
)

type Zadig struct {
 client *zadig.Client
}

func NewZadig(cfg *config.Config) *Zadig {
 client, err := zadig.NewClient(cfg.ZadigConfig.Token, zadig.WithBaseURL(cfg.ZadigConfig.URL))
 if err != nil {
  panic(err)
 }
 return &Zadig{client: client}
}

// ExecProductWorkflowTask 执行标准工作流
func (z *Zadig) ExecProductWorkflowTask(workflowName, projectName, targetEnv, serviceModule, serviceName, codehostName, repoNamespace, repoName, branch string) error {
 _, _, err := z.client.Workflow.ExecWorkflowTask(&zadig.ExecWorkflowTaskOptions{
  WorkflowName: workflowName,
  ProjectName:  projectName,
  Input: zadig.WorkflowInput{
   TargetEnv: targetEnv,
   Build: zadig.ExecBuildArgs{
    Enabled: true,
    ServiceList: []zadig.BuildServiceInfo{
     {
      ServiceModule: serviceModule,
      ServiceName:   serviceName,
      RepoInfo: []zadig.RepositoryInfo{
       {
        CodehostName:  codehostName,
        RepoNamespace: repoNamespace,
        RepoName:      repoName,
        Branch:        branch,
       },
      },
     },
    },
   },
   Deploy: zadig.ExecDeployArgs{
    Enabled: true,
    Source:  "zadig",
   },
  },
 })
 return err
}

// ExecCustomWorkflowTask 执行自定义工作流
func (z *Zadig) ExecCustomWorkflowTask(projectName, workflowName, jobName, jobType, registry, serviceModule, serviceName, codehostName, repoNamespace, repoName, branch string) error {
 _, _, err := z.client.CustomWorkflow.CreateCustomWorkflowTask(&zadig.CreateCustomWorkflowTask{
  ProjectName:  projectName,
  WorkflowName: workflowName,
  Inputs: []zadig.CreateCustomWorkflowTaskInput{
   {
    JobName: jobName,
    JobType: jobType,
    Parameters: zadig.CreateCustomWorkflowTaskParameters{
     Register: registry,
     ServiceList: []zadig.ServiceList{
      {
       ServiceModule: serviceModule,
       ServiceName:   serviceName,
       RepoInfo: []zadig.RepoInfo{
        {
         CodehostName:  codehostName,
         RepoNamespace: repoNamespace,
         RepoName:      repoName,
         Branch:        branch,
        },
       },
      },
     },
    },
   },
  },
 })

 return err
}

这里对工作流的 API 进行了取舍,根据实际情况选择需要的字段。

(3)实现 service 方法

package service

import (
 "context"
 "fmt"
 "github.com/sirupsen/logrus"
 "go.uber.org/fx"
 "joker-bai/go-webhook/config"
 "joker-bai/go-webhook/infrastructure/db"
 "joker-bai/go-webhook/internal/domain/entity"
 "joker-bai/go-webhook/pkg/zadig"
)

// 获取并处理Codeup的Webhook

type CodeupWebhookService struct {
 logger *logrus.Logger
 cfg    *config.Config
 db     *db.DataBase
}

var RegCodeupWebhookService = fx.Provide(func(logger *logrus.Logger, cfg *config.Config, db *db.DataBase) *CodeupWebhookService {
 return &CodeupWebhookService{
  logger: logger,
  cfg:    cfg,
  db:     db,
 }
})

// ExecZadigWorkflowTask 触发执行zadig的工作流
func (c *CodeupWebhookService) ExecZadigWorkflowTask(ctx context.Context, repoName, branch string) error {
 client := zadig.NewZadig(c.cfg)

 // 从数据库中获取数据
 dbClient := c.db.Master.WithContext(ctx)
 var workflowTask entity.ZadigWorkflowTask
 workflowTaskRes := dbClient.Model(&workflowTask).Where("repo_name = ?", repoName).First(&workflowTask)
 if workflowTaskRes.Error != nil {
  return workflowTaskRes.Error
 }

 // 从数据库获取项目和工作流信息
 var flowName entity.ZadigWorkflowName
 workflowNameRes := dbClient.Model(&flowName).Where("branch = ? and project_name = ?", repoName, workflowTask.ProjectName).First(&flowName)
 if workflowNameRes.Error != nil {
  return workflowNameRes.Error
 }

 // 判断workflow的类别
 if workflowTask.WorkflowType == "product" {
  return client.ExecProductWorkflowTask(
   flowName.WorkflowName,
   workflowTask.ProjectName,
   flowName.TargetEnv,
   workflowTask.ServiceModule,
   workflowTask.ServiceName,
   workflowTask.CodehostName,
   workflowTask.RepoNamespace,
   workflowTask.RepoName,
   branch,
  )
 } else if workflowTask.WorkflowType == "custom" {
  return client.ExecCustomWorkflowTask(
   workflowTask.ProjectName,
   flowName.WorkflowName,
   workflowTask.JobName,
   workflowTask.JobType,
   workflowTask.Registry,
   workflowTask.ServiceModule,
   workflowTask.ServiceName,
   workflowTask.CodehostName,
   workflowTask.RepoNamespace,
   workflowTask.RepoName,
   branch,
  )
 } else {
  return fmt.Errorf("未匹配workflow类型")
 }
}

这里从数据库获取需要的信息,然后根据不同的工作流类型执行不同的接口。

(4)实现 controller 方法

package application

import (
 "fmt"
 "github.com/gin-gonic/gin"
 "github.com/sirupsen/logrus"
 "go.uber.org/fx"
 "joker-bai/go-webhook/internal/core/base"
 "joker-bai/go-webhook/domain/service"
 "strings"
 "time"
)

var regCodeupWebhookApplication = fx.Provide(NewCodeupWebhookController)

type CodeupWebhookController struct {
 logger  *logrus.Logger
 service *service.CodeupWebhookService
}
type CodeupWebhook struct {
 After             string     `json:"after"`
 AliyunPk          string     `json:"aliyun_pk"`
 Before            string     `json:"before"`
 CheckoutSha       string     `json:"checkout_sha"`
 Commits           []Commits  `json:"commits"`
 ObjectKind        string     `json:"object_kind"`
 ProjectID         int        `json:"project_id"`
 Ref               string     `json:"ref"`
 Repository        Repository `json:"repository"`
 TotalCommitsCount int        `json:"total_commits_count"`
 UserEmail         string     `json:"user_email"`
 UserExternUID     string     `json:"user_extern_uid"`
 UserID            int        `json:"user_id"`
 UserName          string     `json:"user_name"`
}
type Author struct {
 Email string `json:"email"`
 Name  string `json:"name"`
}
type Commits struct {
 Author    Author    `json:"author"`
 ID        string    `json:"id"`
 Message   string    `json:"message"`
 Timestamp time.Time `json:"timestamp"`
 URL       string    `json:"url"`
}
type Repository struct {
 Description         string `json:"description"`
 GitHTTPURL          string `json:"git_http_url"`
 GitSecondaryHTTPURL string `json:"git_secondary_http_url"`
 GitSecondarySSHURL  string `json:"git_secondary_ssh_url"`
 GitSSHURL           string `json:"git_ssh_url"`
 Homepage            string `json:"homepage"`
 Name                string `json:"name"`
 URL                 string `json:"url"`
 VisibilityLevel     int    `json:"visibility_level"`
}

func NewCodeupWebhookController(logger *logrus.Logger, service *service.CodeupWebhookService) *CodeupWebhookController {
 return &CodeupWebhookController{
  logger:  logger,
  service: service,
 }
}

// DoCodeupWebhook 获取Codeup 代码webhook的Body
func (c *CodeupWebhookController) DoCodeupWebhook(ctx *gin.Context) {
 output := base.NewResponse(ctx)
 var param CodeupWebhook
 err := ctx.ShouldBindJSON(¶m)
 if err != nil {
  output.Error(10000, err.Error())
  return
 }

 // 获取repo_name,repo_namespace,branch
 repoName := param.Repository.Name
 branch := strings.Split(param.Ref, "/")[2]

 if err := c.service.ExecZadigWorkflowTask(ctx, repoName, branch); err != nil {
  c.logger.Error(err.Error())
  output.Error(502, "执行workflow失败")
 }

 output.Success(gin.H{
  "data": "ok",
 })
}

这部分就更简单了,从 Webhook 中获取数据,然后调 service 去执行即可。

最后就是增加路由了,这部分就不用展示了。

搬砖结束

搬砖完成过后就是对自己开发的 HTTP Server 进行验证了。

整个思路和开发的工作量都不大,上面的代码还有很多地方需要去调整的,如果有相同需求的可以自己去实现,我仅仅是做了一个 demo。

通过引入中间商的方式来实现自己的需求的优点是比较简单,不需要去看或者兼容其他的代码,只需要实现自己的逻辑,缺点就是数据这一块需要单独去处理,比较麻烦。


最后,求关注。如果你还想看更多优质原创文章,欢迎关注我们的公号「运维开发故事」。


我是 乔克,《运维开发故事》公号团队中的一员,一线运维农民工,云原生实践者,这里不仅有硬核的技术干货,还有我们对技术的思考和感悟,欢迎关注我们的公号,期待和你一起成长!



标签:三方,zadig,string,json,Zadig,workflowTask,流水线,type,name
From: https://blog.51cto.com/u_12970189/6381237

相关文章

  • 企业什么情况下需要找第三方软件测评机构?
    软件测试是软件生产周期中必不可少的重要环节,软件企业在进行测试工作时有两种选项:一种是企业内部自有的测试团队,一种是外包给第三方软件测评机构,那么企业在什么情况下需要找第三方软件测评机构呢?接下来由卓码软件测评小编对该问题作简要解答。1.缺乏软件测试资源在信......
  • 3.4 流水线的通用原理
    流水线化的一个重要特性就是提高了系统的吞吐量,不过会轻微增加延迟。计算流水线在现代逻辑设计中,电路延迟以微微秒或皮秒,也就是10的负12次方秒为单位进行计算。假设将系统执行的计算分为三个阶段,每个阶段需要100ps,然后在每个阶段之间放上流水线寄存器,流水线寄存器的延迟为20ps,这......
  • 仅仅是调用第三方接口那么简单吗?
    最近有个项目需要本地处理之后,然后调用第三方接口,本来开始觉得很简单得事情,不就是调用第三方接口吗?但是却一波三折。初版首先有了下面的第一版的设计。这个设计很简单,也是最容易想到的。主要有下面几步1、本地处理;2、调用第三方接口;3、本地日志打印,包括是否调用成功及失败......
  • 预置第三方apk( 可卸载)
    修改文档:vimdevice/mediateksample/k61v1_64_bsp/device.mk添加属性值:PRODUCT_PACKAGES+=   新建mk文档;内置apk时,  Android.mk基本内容:+LOCAL_PATH:=$(callmy-dir)+include$(CLEAR_VARS)+LOCAL_MODULE:=BaiDu+LOCAL_MODULE_CLASS:=APPS+LOCAL_MODUL......
  • gitee 流水线部署go程序
    1、在gitee上新建gotest项目,为了方便演示,main.go代码简略如下packagemainfuncmain(){println("helloworld")}2、编写makefile文件3、创建流水线,选择go项目4、修改部署规则5、测试运行,看到最后输出了go程序的输出helloworld,说明部署成功......
  • 十、Jenkins流水线集成Sonar
    Jenkins安装在阿里云主机上,SonarQube安装在腾讯云主机上。所使用的地址都是外网ip。一、Jenkins配置Sonar安装SonarQubeScannerforJenkins插件:  我这里已经安装了。 配置SonarQubeServer,在系统管理->ConfigureSystem中:  注意:这里的SonarQube的凭据选择Sec......
  • 第三方接口控制请求时间处理
    问题项目中需要调用第三方接口,若第三方接口在X秒内未返回,则继续后续业务操作代码展示publicclassCheckTimeOutDemo{privatestaticExecutorServicethreadService=newThreadPoolExecutor(0,20,60L,TimeUnit.SECONDS,newSynchronousQueue<>(),r->{......
  • 【习题8】三方库 答案
    【习题8】三方库判断题1.三方组件是开发者在系统能力的基础上进行了一层具体功能的封装,对其能力进行拓展的工具。正确(True)错误(False)2.可以通过ohpmuninstall指令下载指定的三方库正确(True)错误(False)3.lottie使用loadAnimation方法加载动画。正确(Tru......
  • 选择小程序第三方开发框架,你需要知道这些
    在选择小程序第三方开发框架时,我们需要综合考虑开发者技术栈、项目需求和目标平台等因素。Taro是一个多端统一开发框架,适合需要覆盖多个平台的开发者,最终的选择应该基于个人的技术背景和项目需求,同时也要关注框架的稳定性、社区支持和文档资源等方面。只有选择最适合自己的开发工......
  • 八、流水线语法之Directives
    一、environmentenvironment指令指定了一系列键值对,这些键值对将被定义为所有步骤或阶段特定步骤的环境变量,具体取决于环境指令在管道中的位置。该指令支持一个特殊的助手方法credentials(),该方法可用于通过Jenkins环境中的标识符访问预定义的credentials。支持的凭据类型Secret......