Skip to content

Commit

Permalink
Added worker ID field to build (#156)
Browse files Browse the repository at this point in the history
* Added worker ID field to build

* Added setting worker ID on build

* Updated CHANGELOG.md

* Removed excess indirection

* Removed log worker id

* Added missing format

* Update build.go

Co-authored-by: Alexamakans <79503481+Alexamakans@users.noreply.github.com>

Co-authored-by: Alexamakans <79503481+Alexamakans@users.noreply.github.com>
  • Loading branch information
applejag and Alexamakans authored Mar 3, 2022
1 parent ae9081c commit 7353fa2
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 11 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ This project tries to follow [SemVer 2.0.0](https://semver.org/).
- Deprecated trigger configs (YAML: `ci.triggerUrl` &amp; `ci.triggerToken`,
environment variables: `WHARF_CI_TRIGGERURL` &amp; `WHARF_CI_TRIGGERTOKEN`)
in favor of new configuration values that allow specifying up to two different
execution engines: (#134)
execution engines: (#134, #156)

| YAML | Environment variable | Type |
| ------------------ | ------------------------ | --------------------- |
| `ci.engine.id` | `WHARF_CI_ENGINE_ID` | string (max 32 chars) |
| `ci.engine.name` | `WHARF_CI_ENGINE_NAME` | string |
| `ci.engine.api` | `WHARF_CI_ENGINE_API` | string |
| `ci.engine.url` | `WHARF_CI_ENGINE_URL` | string |
| `ci.engine.token` | `WHARF_CI_ENGINE_TOKEN` | string |
| `ci.engine2.id` | `WHARF_CI_ENGINE2_ID` | string (max 32 chars) |
| `ci.engine2.name` | `WHARF_CI_ENGINE2_NAME` | string |
| `ci.engine2.api` | `WHARF_CI_ENGINE2_API` | string |
| `ci.engine2.url` | `WHARF_CI_ENGINE2_URL` | string |
| `ci.engine2.token` | `WHARF_CI_ENGINE2_TOKEN` | string |

Expand All @@ -42,6 +44,9 @@ This project tries to follow [SemVer 2.0.0](https://semver.org/).
See [`api/wharfapi/v5/builds.proto`](./api/wharfapi/v5/builds.proto) for full
documentation of the API. (#147)

- Added build field `workerId` that is automatically populated if the engine
API is `wharf-cmd.v1`. (#156)

- Added granular migrations support. Thanks to this, wharf-api now initializes
much quicker as it can skip applying migrations that are already
applied. (#144)
Expand Down
53 changes: 43 additions & 10 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ var defaultGetBuildsOrderBy = orderby.Column{Name: database.BuildColumns.BuildID
// @param environment query string false "Filter by verbatim build environment."
// @param gitBranch query string false "Filter by verbatim build Git branch."
// @param stage query string false "Filter by verbatim build stage."
// @param workerId query string false "Filter by verbatim worker ID."
// @param isInvalid query bool false "Filter by build's valid/invalid state."
// @param status query []string false "Filter by build status name" enums(Scheduling,Running,Completed,Failed)
// @param statusId query []int false "Filter by build status ID. Cannot be used with `status`." enums(0,1,2,3)
Expand All @@ -177,6 +178,7 @@ func (m buildModule) getBuildListHandler(c *gin.Context) {
Environment *string `form:"environment"`
GitBranch *string `form:"gitBranch"`
Stage *string `form:"stage"`
WorkerID *string `form:"workerId"`

IsInvalid *bool `form:"isInvalid"`

Expand Down Expand Up @@ -209,6 +211,7 @@ func (m buildModule) getBuildListHandler(c *gin.Context) {
GitBranch: where.String(database.BuildFields.GitBranch, params.GitBranch),
IsInvalid: where.Bool(database.BuildFields.IsInvalid, params.IsInvalid),
Stage: where.String(database.BuildFields.Stage, params.Stage),
WorkerID: where.String(database.BuildFields.WorkerID, params.WorkerID),
}, where.NonNilFieldNames()...).
Scopes(
optionalTimeRangeScope(database.BuildColumns.ScheduledOn, params.ScheduledAfter, params.ScheduledBefore),
Expand Down Expand Up @@ -791,7 +794,7 @@ func (m buildModule) startBuildHandler(c *gin.Context, projectID uint, stageName
return
}

_, err = triggerBuild(dbJobParams, engine)
workerID, err := triggerBuild(dbJobParams, engine)
if err != nil {
dbBuild.IsInvalid = true
if saveErr := m.Database.Save(&dbBuild).Error; saveErr != nil {
Expand All @@ -809,6 +812,16 @@ func (m buildModule) startBuildHandler(c *gin.Context, projectID uint, stageName
return
}

if workerID != "" {
dbBuild.WorkerID = workerID
if saveErr := m.Database.Save(&dbBuild).Error; saveErr != nil {
ginutil.WriteDBWriteError(c, err, fmt.Sprintf(
"Failed saving worker ID %q for build on stage %q and branch %q for project with ID %d in database.",
workerID, stageName, branch, projectID))
return
}
}

c.JSON(http.StatusOK, modelconv.DBBuildToResponseBuildReferenceWrapper(dbBuild))
}

Expand Down Expand Up @@ -900,17 +913,37 @@ func triggerBuild(dbJobParams []database.Param, engine CIEngineConfig) (string,
}

defer resp.Body.Close()
var body, err2 = ioutil.ReadAll(resp.Body)
if err2 != nil {
return "", err2
}
switch engine.API {
case CIEngineAPIWharfCMDv1:
if problem.IsHTTPResponse(resp) {
prob, err := problem.ParseHTTPResponse(resp)
if err != nil {
return "", fmt.Errorf("parse response as problem: %w", err)
}
return "", prob
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", fmt.Errorf("non-2xx response: %s", resp.Status)
}
var worker struct {
WorkerID string `json:"workerId"`
}
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&worker); err != nil {
return "", fmt.Errorf("decode wharf-cmd.v1 response: %w", err)
}
return worker.WorkerID, nil

var strBody = string(body)
if resp.StatusCode != 200 && resp.StatusCode != 201 {
return "", fmt.Errorf(strBody)
default:
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return "", fmt.Errorf("non-2xx response: %s: %q", resp.Status, string(body))
}
return "", nil
}

return strBody, err2
}

func getDBJobParams(
Expand Down
52 changes: 52 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
"strings"
"time"

"github.com/iver-wharf/wharf-api/v5/pkg/model/database"
Expand Down Expand Up @@ -120,6 +121,19 @@ type CIEngineConfig struct {
// Added in v5.1.0.
Name string

// API is the type of API for this engine. If set to "wharf-cmd.v1" then the
// wharf-api will have additional integration with this engine, such as
// supporting cancelling builds. Possible values are:
//
// jenkins-generic-webhook-trigger
// wharf-cmd.v1
//
// If no value is supplied, then "jenkins-generic-webhook-trigger" is
// assumed.
//
// Added in v5.1.0.
API CIEngineAPI

// URL is the full URL that wharf-api will send a POST request to
// with all of the build metadata. For example to trigger a Jenkins job via
// the "Generic Webhook Trigger":
Expand All @@ -140,6 +154,19 @@ type CIEngineConfig struct {
Token string
}

// CIEngineAPI is an enum of different engine API values.
type CIEngineAPI string

const (
// CIEngineAPIJenkinsGenericWebhookTrigger means wharf-api will target the
// Jenkins Generic Webhook Trigger plugin:
// https://plugins.jenkins.io/generic-webhook-trigger/
CIEngineAPIJenkinsGenericWebhookTrigger CIEngineAPI = "jenkins-generic-webhook-trigger"
// CIEngineAPIWharfCMDv1 means that wharf-api will target the v1 of the
// wharf-cmd-provisioner API.
CIEngineAPIWharfCMDv1 CIEngineAPI = "wharf-cmd.v1"
)

// HTTPConfig holds settings for the HTTP server.
type HTTPConfig struct {
CORS CORSConfig
Expand Down Expand Up @@ -377,10 +404,12 @@ var DefaultConfig = Config{
Engine: CIEngineConfig{
ID: "primary",
Name: "Primary",
API: CIEngineAPIJenkinsGenericWebhookTrigger,
},
Engine2: CIEngineConfig{
ID: "secondary",
Name: "Secondary",
API: CIEngineAPIJenkinsGenericWebhookTrigger,
},
},
HTTP: HTTPConfig{
Expand Down Expand Up @@ -430,9 +459,32 @@ func loadConfig() (Config, error) {
if err := cfg.validate(); err != nil {
return Config{}, err
}
if cfg.CI.Engine.URL != "" {
cfg.CI.Engine.API, err = parseCIEngineAPI(cfg.CI.Engine.API)
if err != nil {
return Config{}, err
}
}
if cfg.CI.Engine2.URL != "" {
cfg.CI.Engine2.API, err = parseCIEngineAPI(cfg.CI.Engine2.API)
if err != nil {
return Config{}, err
}
}
return cfg, nil
}

func parseCIEngineAPI(api CIEngineAPI) (CIEngineAPI, error) {
switch strings.TrimSpace(strings.ToLower(string(api))) {
case "", string(CIEngineAPIJenkinsGenericWebhookTrigger):
return CIEngineAPIJenkinsGenericWebhookTrigger, nil
case string(CIEngineAPIWharfCMDv1):
return CIEngineAPIWharfCMDv1, nil
default:
return "", fmt.Errorf("invalid CI engine API value: %q", api)
}
}

func (cfg *Config) addBackwardCompatibleConfigs() {
if cfg.CI.TriggerToken != "" {
cfg.CI.Engine.Token = cfg.CI.TriggerToken
Expand Down
3 changes: 3 additions & 0 deletions pkg/model/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ var BuildFields = struct {
GitBranch string
Environment string
Stage string
WorkerID string
IsInvalid string
Params string
TestResultSummaries string
Expand All @@ -262,6 +263,7 @@ var BuildFields = struct {
GitBranch: "GitBranch",
Environment: "Environment",
Stage: "Stage",
WorkerID: "WorkerID",
IsInvalid: "IsInvalid",
Params: "Params",
TestResultSummaries: "TestResultSummaries",
Expand Down Expand Up @@ -318,6 +320,7 @@ type Build struct {
GitBranch string `gorm:"size:300;not null;default:''"`
Environment null.String `gorm:"nullable;size:40" swaggertype:"string"`
Stage string `gorm:"size:40;not null;default:''"`
WorkerID string `gorm:"size:40;not null;default:'';index:build_idx_worker_id"`
Params []BuildParam `gorm:"foreignKey:BuildID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
IsInvalid bool `gorm:"not null;default:false"`
TestResultSummaries []TestResultSummary `gorm:"foreignKey:BuildID"`
Expand Down
1 change: 1 addition & 0 deletions pkg/model/response/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type Build struct {
GitBranch string `json:"gitBranch"`
Environment null.String `json:"environment" swaggertype:"string" extensions:"x-nullable"`
Stage string `json:"stage"`
WorkerID string `json:"workerId" example:"5d6bcf20-81fd-4ad8-a446-735aa8423dfe"`
Params []BuildParam `json:"params"`
IsInvalid bool `json:"isInvalid"`
TestResultSummaries []TestResultSummary `json:"testResultSummaries"`
Expand Down
1 change: 1 addition & 0 deletions pkg/modelconv/buildconv.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func DBBuildToResponse(dbBuild database.Build, engineLookup EngineLookup) respon
GitBranch: dbBuild.GitBranch,
Environment: dbBuild.Environment,
Stage: dbBuild.Stage,
WorkerID: dbBuild.WorkerID,
Params: DBBuildParamsToResponses(dbBuild.Params),
IsInvalid: dbBuild.IsInvalid,
TestResultSummaries: DBTestResultSummariesToResponses(dbBuild.TestResultSummaries),
Expand Down

0 comments on commit 7353fa2

Please sign in to comment.