Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added worker ID field to build #156

Merged
merged 9 commits into from
Mar 3, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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` & `ci.triggerToken`,
environment variables: `WHARF_CI_TRIGGERURL` & `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 @@ -38,6 +40,9 @@ This project tries to follow [SemVer 2.0.0](https://semver.org/).
- Added query parameter `?engine=ID` to `POST /api/project/{projectId}/build`
to allow specifying which execution engine to use for the new build. (#134)

- 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
55 changes: 44 additions & 11 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,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 @@ -176,6 +177,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 @@ -228,6 +230,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),
StatusID: statusID,
}, where.NonNilFieldNames()...).
Scopes(
Expand Down Expand Up @@ -679,7 +682,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 @@ -697,6 +700,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 @@ -782,17 +795,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
}

var strBody = string(body)
if resp.StatusCode != 200 && resp.StatusCode != 201 {
return "", fmt.Errorf(strBody)
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

default:
if resp.StatusCode != 200 && resp.StatusCode != 201 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return "", fmt.Errorf("non-2xx response: %s: %q", resp.Status, string(body))
applejag marked this conversation as resolved.
Show resolved Hide resolved
}
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 @@ -315,6 +317,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