From 3d4418e3516a671c833bab40a7dbd232dce4992b Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Tue, 21 May 2019 11:45:42 -0700 Subject: [PATCH] add rpc/v2 endpoint --- LICENSE | 6 +- cmd/drone-server/inject_server.go | 11 +- cmd/drone-server/wire_gen.go | 3 +- go.mod | 2 +- go.sum | 2 + operator/manager/manager.go | 10 +- operator/manager/rpc/client.go | 4 +- operator/manager/rpc/server.go | 4 +- operator/manager/rpc2/client.go | 7 + operator/manager/rpc2/handler.go | 295 ++++++++++++++++++++++++++++++ operator/manager/rpc2/server.go | 66 +++++++ operator/manager/rpc2/types.go | 27 +++ operator/runner/runner.go | 2 +- scheduler/queue/queue.go | 26 +++ scheduler/queue/queue_test.go | 38 ++++ trigger/trigger.go | 8 +- trigger/trigger_test.go | 2 + 17 files changed, 494 insertions(+), 19 deletions(-) create mode 100644 operator/manager/rpc2/client.go create mode 100644 operator/manager/rpc2/handler.go create mode 100644 operator/manager/rpc2/server.go create mode 100644 operator/manager/rpc2/types.go diff --git a/LICENSE b/LICENSE index b186d8c9..2b883300 100644 --- a/LICENSE +++ b/LICENSE @@ -59,7 +59,7 @@ legal claim.** ----------------------------------------------------------------- -Waiver: Small Business +Waiver: Individual and Small Business Contributor waives the terms of rule 1 for companies meeting all the following criteria, counting all subsidiaries and affiliated @@ -76,13 +76,13 @@ future versions of the software. ----------------------------------------------------------------- -Waiver: Low Volume +Waiver: Low Usage Contributor waives the terms of rule 1 for companies meeting all the following criteria, counting all subsidiaries and affiliated entities as one: -1. less than 15,000 total pipelines executed using this software +1. less than 5,000 total pipelines executed using this software in the immediately preceding, year-long period Contributor will not revoke this waiver, but may change terms for diff --git a/cmd/drone-server/inject_server.go b/cmd/drone-server/inject_server.go index da2fbee7..26e6b026 100644 --- a/cmd/drone-server/inject_server.go +++ b/cmd/drone-server/inject_server.go @@ -23,6 +23,7 @@ import ( "github.com/drone/drone/metric" "github.com/drone/drone/operator/manager" "github.com/drone/drone/operator/manager/rpc" + "github.com/drone/drone/operator/manager/rpc2" "github.com/drone/drone/server" "github.com/google/wire" @@ -38,16 +39,18 @@ var serverSet = wire.NewSet( web.New, provideRouter, provideRPC, + provideRPC2, provideServer, provideServerOptions, ) // provideRouter is a Wire provider function that returns a // router that is serves the provided handlers. -func provideRouter(api api.Server, web web.Server, rpc http.Handler, metrics *metric.Server) *chi.Mux { +func provideRouter(api api.Server, web web.Server, rpc http.Handler, rpcv2 rpc2.Server, metrics *metric.Server) *chi.Mux { r := chi.NewRouter() r.Mount("/metrics", metrics) r.Mount("/api", api.Handler()) + r.Mount("/rpc/v2", rpcv2) r.Mount("/rpc", rpc) r.Mount("/", web.Handler()) return r @@ -59,6 +62,12 @@ func provideRPC(m manager.BuildManager, config config.Config) http.Handler { return rpc.NewServer(m, config.RPC.Secret) } +// provideRPC2 is a Wire provider function that returns an rpc +// handler that exposes the build manager to a remote agent. +func provideRPC2(m manager.BuildManager, config config.Config) rpc2.Server { + return rpc2.NewServer(m, config.RPC.Secret) +} + // provideServer is a Wire provider function that returns an // http server that is configured from the environment. func provideServer(handler *chi.Mux, config config.Config) *server.Server { diff --git a/cmd/drone-server/wire_gen.go b/cmd/drone-server/wire_gen.go index 415edb57..b11d7568 100644 --- a/cmd/drone-server/wire_gen.go +++ b/cmd/drone-server/wire_gen.go @@ -93,8 +93,9 @@ func InitializeApplication(config2 config.Config) (application, error) { options := provideServerOptions(config2) webServer := web.New(admissionService, buildStore, client, hookParser, coreLicense, licenseService, middleware, repositoryStore, session, syncer, triggerer, userStore, userService, webhookSender, options, system) handler := provideRPC(buildManager, config2) + rpc2Server := provideRPC2(buildManager, config2) metricServer := metric.NewServer(session) - mux := provideRouter(server, webServer, handler, metricServer) + mux := provideRouter(server, webServer, handler, rpc2Server, metricServer) serverServer := provideServer(mux, config2) mainApplication := newApplication(cronScheduler, datadog, runner, serverServer, userStore) return mainApplication, nil diff --git a/go.mod b/go.mod index 8cb37320..d8ccaf41 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/drone/drone-go v1.0.5-0.20190427184118-618e4496482e github.com/drone/drone-runtime v1.0.6 github.com/drone/drone-ui v0.0.0-20190423061913-b758d7bee2eb - github.com/drone/drone-yaml v1.0.9-0.20190424150956-115b2ff5f99e + github.com/drone/drone-yaml v1.0.9 github.com/drone/envsubst v1.0.1 github.com/drone/go-license v1.0.2 github.com/drone/go-login v1.0.4-0.20190311170324-2a4df4f242a2 diff --git a/go.sum b/go.sum index 23d737a9..4dc73db6 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,8 @@ github.com/drone/drone-yaml v1.0.8 h1:Jrnd/yC9LbMx/sMkbZcvSCKxWgMWnMaFa6HgjtpA1V github.com/drone/drone-yaml v1.0.8/go.mod h1:1yrotgyD94qoYwgWWm71vAMbcw7Zd3gDersjeT9lYAk= github.com/drone/drone-yaml v1.0.9-0.20190424150956-115b2ff5f99e h1:PvLIeYtS0/EXHolUF3adFiSMWArUi49knDMuclpnPH4= github.com/drone/drone-yaml v1.0.9-0.20190424150956-115b2ff5f99e/go.mod h1:1yrotgyD94qoYwgWWm71vAMbcw7Zd3gDersjeT9lYAk= +github.com/drone/drone-yaml v1.0.9 h1:lVd8l1khAt7Ck+WMAjnRfEj+3jxLGOa8R7FITDh3jbw= +github.com/drone/drone-yaml v1.0.9/go.mod h1:1yrotgyD94qoYwgWWm71vAMbcw7Zd3gDersjeT9lYAk= github.com/drone/envsubst v1.0.1 h1:NOOStingM2sbBwsIUeQkKUz8ShwCUzmqMxWrpXItfPE= github.com/drone/envsubst v1.0.1/go.mod h1:bkZbnc/2vh1M12Ecn7EYScpI4YGYU0etwLJICOWi8Z0= github.com/drone/go-license v1.0.2 h1:7OwndfYk+Lp/cGHkxe4HUn/Ysrrw3WYH2pnd99yrkok= diff --git a/operator/manager/manager.go b/operator/manager/manager.go index eb467da5..42b525b8 100644 --- a/operator/manager/manager.go +++ b/operator/manager/manager.go @@ -50,7 +50,7 @@ type ( Request(ctx context.Context, args *Request) (*core.Stage, error) // Accept accepts the build stage for execution. - Accept(ctx context.Context, stage int64, machine string) error + Accept(ctx context.Context, stage int64, machine string) (*core.Stage, error) // Netrc returns a valid netrc for execution. Netrc(ctx context.Context, repo int64) (*core.Netrc, error) @@ -196,7 +196,7 @@ func (m *Manager) Request(ctx context.Context, args *Request) (*core.Stage, erro // agents to pull the same stage from the queue. The system uses optimistic // locking at the database-level to prevent multiple agents from executing the // same stage. -func (m *Manager) Accept(ctx context.Context, id int64, machine string) error { +func (m *Manager) Accept(ctx context.Context, id int64, machine string) (*core.Stage, error) { logger := logrus.WithFields( logrus.Fields{ "stage-id": id, @@ -209,11 +209,11 @@ func (m *Manager) Accept(ctx context.Context, id int64, machine string) error { if err != nil { logger = logger.WithError(err) logger.Warnln("manager: cannot find stage") - return err + return nil, err } if stage.Machine != "" { logger.Debugln("manager: stage already assigned. abort.") - return db.ErrOptimisticLock + return nil, db.ErrOptimisticLock } stage.Machine = machine @@ -230,7 +230,7 @@ func (m *Manager) Accept(ctx context.Context, id int64, machine string) error { } else { logger.Debugln("manager: stage accepted") } - return err + return stage, err } // Details fetches build details. diff --git a/operator/manager/rpc/client.go b/operator/manager/rpc/client.go index b5bf4ce1..669cfa2c 100644 --- a/operator/manager/rpc/client.go +++ b/operator/manager/rpc/client.go @@ -86,9 +86,9 @@ func (s *Client) Request(ctx context.Context, args *manager.Request) (*core.Stag } // Accept accepts the build stage for execution. -func (s *Client) Accept(ctx context.Context, stage int64, machine string) error { +func (s *Client) Accept(ctx context.Context, stage int64, machine string) (*core.Stage, error) { in := &acceptRequest{Stage: stage, Machine: machine} - return s.send(noContext, "/rpc/v1/accept", in, nil) + return nil, s.send(noContext, "/rpc/v1/accept", in, nil) } // Netrc returns a valid netrc for execution. diff --git a/operator/manager/rpc/server.go b/operator/manager/rpc/server.go index 9693cbf4..d228dcbb 100644 --- a/operator/manager/rpc/server.go +++ b/operator/manager/rpc/server.go @@ -103,12 +103,12 @@ func (s *Server) handleAccept(w http.ResponseWriter, r *http.Request) { writeBadRequest(w, err) return } - err = s.manager.Accept(ctx, in.Stage, in.Machine) + out, err := s.manager.Accept(ctx, in.Stage, in.Machine) if err != nil { writeError(w, err) return } - w.WriteHeader(http.StatusNoContent) + json.NewEncoder(w).Encode(out) } func (s *Server) handleNetrc(w http.ResponseWriter, r *http.Request) { diff --git a/operator/manager/rpc2/client.go b/operator/manager/rpc2/client.go new file mode 100644 index 00000000..cf1892a3 --- /dev/null +++ b/operator/manager/rpc2/client.go @@ -0,0 +1,7 @@ +// Copyright 2019 Drone.IO Inc. All rights reserved. +// Use of this source code is governed by the Drone Non-Commercial License +// that can be found in the LICENSE file. + +// +build !oss + +package rpc2 diff --git a/operator/manager/rpc2/handler.go b/operator/manager/rpc2/handler.go new file mode 100644 index 00000000..041d9904 --- /dev/null +++ b/operator/manager/rpc2/handler.go @@ -0,0 +1,295 @@ +// Copyright 2019 Drone.IO Inc. All rights reserved. +// Use of this source code is governed by the Drone Non-Commercial License +// that can be found in the LICENSE file. + +// +build !oss + +/* + +/rpc/v2/stage POST (request) +/rpc/v2/stage/{stage}?machine= POST (accept, details) +/rpc/v2/stage/{stage} PUT (beforeAll, afterAll) +/rpc/v2/stage/{stage}/steps/{step} PUT (before, after) +/rpc/v2/build/{build}/watch POST (watch) +/rpc/v2/stage/{stage}/logs/batch POST (batch) +/rpc/v2/stage/{stage}/logs/upload POST (upload) + +*/ + +package rpc2 + +import ( + "context" + "encoding/json" + "io" + "net/http" + "strconv" + "time" + + "github.com/go-chi/chi" + + "github.com/drone/drone/core" + "github.com/drone/drone/operator/manager" + "github.com/drone/drone/store/shared/db" +) + +// default http request timeout +var defaultTimeout = time.Second * 30 + +var noContext = context.Background() + +// HandleJoin returns an http.HandlerFunc that makes an +// http.Request to join the cluster. +// +// POST /rpc/v2/nodes/:machine +func HandleJoin() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) // this is a no-op + } +} + +// HandleLeave returns an http.HandlerFunc that makes an +// http.Request to leave the cluster. +// +// DELETE /rpc/v2/nodes/:machine +func HandleLeave() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) // this is a no-op + } +} + +// HandlePing returns an http.HandlerFunc that makes an +// http.Request to ping the server and confirm connectivity. +// +// GET /rpc/v2/ping +func HandlePing() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + } +} + +// HandleRequest returns an http.HandlerFunc that processes an +// http.Request to reqeust a stage from the queue for execution. +// +// POST /rpc/v2/stage +func HandleRequest(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + ctx, cancel := context.WithTimeout(ctx, defaultTimeout) + defer cancel() + + req := new(manager.Request) + err := json.NewDecoder(r.Body).Decode(req) + if err != nil { + writeError(w, err) + return + } + stage, err := m.Request(ctx, req) + if err != nil { + writeError(w, err) + } else { + writeJSON(w, stage) + } + } +} + +// HandleAccept returns an http.HandlerFunc that processes an +// http.Request to accept ownership of the stage. +// +// POST /rpc/v2/stage/{stage}?machine= +func HandleAccept(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + stage, _ := strconv.ParseInt( + chi.URLParam(r, "stage"), 10, 64) + + out, err := m.Accept(noContext, stage, r.FormValue("machine")) + if err != nil { + writeError(w, err) + } else { + writeJSON(w, out) + } + } +} + +// HandleInfo returns an http.HandlerFunc that processes an +// http.Request to get the build details. +// +// POST /rpc/v2/build/{build} +func HandleInfo(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + stage, _ := strconv.ParseInt( + chi.URLParam(r, "stage"), 10, 64) + + res, err := m.Details(noContext, stage) + if err != nil { + writeError(w, err) + return + } + + netrc, err := m.Netrc(noContext, res.Repo.ID) + if err != nil { + writeError(w, err) + return + } + + writeJSON(w, &details{ + Context: res, + Netrc: netrc, + Repo: &repositroy{ + Repository: res.Repo, + Secret: res.Repo.Secret, + }, + }) + } +} + +// HandleUpdateStage returns an http.HandlerFunc that processes +// an http.Request to update a stage. +// +// PUT /rpc/v2/stage/{stage} +func HandleUpdateStage(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + dst := new(core.Stage) + err := json.NewDecoder(r.Body).Decode(dst) + if err != nil { + writeError(w, err) + return + } + + if dst.Status == core.StatusPending || + dst.Status == core.StatusRunning { + err = m.BeforeAll(noContext, dst) + } else { + err = m.AfterAll(noContext, dst) + } + + if err != nil { + writeError(w, err) + } else { + writeJSON(w, dst) + } + } +} + +// HandleUpdateStep returns an http.HandlerFunc that processes +// an http.Request to update a step. +// +// POST /rpc/v2/step/{step} +func HandleUpdateStep(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + dst := new(core.Step) + err := json.NewDecoder(r.Body).Decode(dst) + if err != nil { + writeError(w, err) + return + } + + if dst.Status == core.StatusPending || + dst.Status == core.StatusRunning { + err = m.Before(noContext, dst) + } else { + err = m.After(noContext, dst) + } + + if err != nil { + writeError(w, err) + } else { + writeJSON(w, dst) + } + } +} + +// HandleWatch returns an http.HandlerFunc that accepts a +// blocking http.Request that watches a build for cancellation +// events. +// +// GET /rpc/v2/build/{build}/watch +func HandleWatch(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + ctx, cancel := context.WithTimeout(ctx, defaultTimeout) + defer cancel() + + build, _ := strconv.ParseInt( + chi.URLParamFromCtx(ctx, "build"), 10, 64) + + _, err := m.Watch(ctx, build) + if err != nil { + writeError(w, err) + } else { + writeOK(w) + } + } +} + +// HandleLogBatch returns an http.HandlerFunc that accepts an +// http.Request to submit a stream of logs to the system. +// +// POST /rpc/v2/step/{step}/logs/batch +func HandleLogBatch(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + step, _ := strconv.ParseInt( + chi.URLParam(r, "step"), 10, 64) + + lines := []*core.Line{} + err := json.NewDecoder(r.Body).Decode(&lines) + if err != nil { + writeError(w, err) + return + } + + // TODO(bradrydzewski) modify the write function to + // accept a slice of lines. + for _, line := range lines { + err := m.Write(noContext, step, line) + if err != nil { + writeError(w, err) + return + } + } + + writeOK(w) + } +} + +// HandleLogUpload returns an http.HandlerFunc that accepts an +// http.Request to upload and persist logs for a pipeline stage. +// +// POST /rpc/v2/step/{step}/logs/upload +func HandleLogUpload(m manager.BuildManager) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + step, _ := strconv.ParseInt( + chi.URLParam(r, "step"), 10, 64) + + err := m.Upload(noContext, step, r.Body) + if err != nil { + writeError(w, err) + } else { + writeOK(w) + } + } +} + +// write a 200 Status OK to the response body. +func writeJSON(w http.ResponseWriter, v interface{}) { + json.NewEncoder(w).Encode(v) +} + +// write a 200 Status OK to the response body. +func writeOK(w http.ResponseWriter) { + w.WriteHeader(http.StatusOK) +} + +// write an error message to the response body. +func writeError(w http.ResponseWriter, err error) { + if err == context.DeadlineExceeded { + w.WriteHeader(204) // should retry + } else if err == context.Canceled { + w.WriteHeader(204) // should retry + } else if err == db.ErrOptimisticLock { + w.WriteHeader(409) // should abort + } else { + w.WriteHeader(500) // should fail + } + io.WriteString(w, err.Error()) +} diff --git a/operator/manager/rpc2/server.go b/operator/manager/rpc2/server.go new file mode 100644 index 00000000..2371e7bb --- /dev/null +++ b/operator/manager/rpc2/server.go @@ -0,0 +1,66 @@ +// Copyright 2019 Drone.IO Inc. All rights reserved. +// Use of this source code is governed by the Drone Non-Commercial License +// that can be found in the LICENSE file. + +// +build !oss + +/* + +/stage POST (request) +/stage/{stage}?machine= POST (accept, details) +/stage/{stage} PUT (beforeAll, afterAll) +/stage/{stage}/steps/{step} PUT (before, after) +/build/{build}/watch POST (watch) +/stage/{stage}/logs/batch POST (batch) +/stage/{stage}/logs/upload POST (upload) + +*/ + +package rpc2 + +import ( + "net/http" + + "github.com/drone/drone/operator/manager" + + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" +) + +// Server wraps the chi Router in a custom type for wire +// injection purposes. +type Server http.Handler + +// NewServer returns a new rpc server that enables remote +// interaction with the build controller using the http transport. +func NewServer(manager manager.BuildManager, secret string) Server { + r := chi.NewRouter() + r.Use(middleware.Recoverer) + r.Use(middleware.NoCache) + r.Use(authorization(secret)) + r.Post("/nodes/:machine", HandleJoin()) + r.Delete("/nodes/:machine", HandleLeave()) + r.Post("/ping", HandlePing()) + r.Post("/stage", HandleRequest(manager)) + r.Post("/stage/{stage}", HandleAccept(manager)) + r.Get("/stage/{stage}", HandleInfo(manager)) + r.Put("/stage/{stage}", HandleUpdateStage(manager)) + r.Put("/step/{step}", HandleUpdateStep(manager)) + r.Post("/build/{build}/watch", HandleWatch(manager)) + r.Post("/step/{step}/logs/batch", HandleLogBatch(manager)) + r.Post("/step/{step}/logs/upload", HandleLogUpload(manager)) + return Server(r) +} + +func authorization(token string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if token == r.Header.Get("X-Drone-Token") { + next.ServeHTTP(w, r) + } else { + w.WriteHeader(401) + } + }) + } +} + diff --git a/operator/manager/rpc2/types.go b/operator/manager/rpc2/types.go new file mode 100644 index 00000000..5ec3f497 --- /dev/null +++ b/operator/manager/rpc2/types.go @@ -0,0 +1,27 @@ +// Use of this source code is governed by the Drone Non-Commercial License +// that can be found in the LICENSE file. + +// +build !oss + +package rpc2 + +// Copyright 2019 Drone.IO Inc. All rights reserved. +import ( + "github.com/drone/drone/core" + "github.com/drone/drone/operator/manager" +) + +// details provides the runner with the build details and +// includes all environment data required to execute the build. +type details struct { + *manager.Context + Netrc *core.Netrc `json:"netrc"` + Repo *repositroy `json:"repository"` +} + +// repository wraps a repository object to include the secret +// when the repository is marshaled to json. +type repositroy struct { + *core.Repository + Secret string `json:"secret"` +} diff --git a/operator/runner/runner.go b/operator/runner/runner.go index 1e9a7444..7ddb1444 100644 --- a/operator/runner/runner.go +++ b/operator/runner/runner.go @@ -575,7 +575,7 @@ func (r *Runner) poll(ctx context.Context) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - err = r.Manager.Accept(ctx, p.ID, r.Machine) + _, err = r.Manager.Accept(ctx, p.ID, r.Machine) if err == db.ErrOptimisticLock { return nil } else if err != nil { diff --git a/scheduler/queue/queue.go b/scheduler/queue/queue.go index 6fbf7bb3..bce12f0e 100644 --- a/scheduler/queue/queue.go +++ b/scheduler/queue/queue.go @@ -82,6 +82,8 @@ func (q *queue) Resume(ctx context.Context) error { func (q *queue) Request(ctx context.Context, params core.Filter) (*core.Stage, error) { w := &worker{ + kind: params.Kind, + typ: params.Type, os: params.OS, arch: params.Arch, kernel: params.Kernel, @@ -144,6 +146,11 @@ func (q *queue) signal(ctx context.Context) error { loop: for w := range q.workers { + // the worker must match the resource kind and type + if !matchResource(w.kind, w.typ, item.Kind, item.Type) { + continue + } + // the worker is platform-specific. check to ensure // the queue item matches the worker platform. if w.os != item.OS { @@ -205,6 +212,8 @@ func (q *queue) start() error { } type worker struct { + kind string + typ string os string arch string kernel string @@ -250,3 +259,20 @@ func withinLimits(stage *core.Stage, siblings []*core.Stage) bool { } return count < stage.Limit } + +// matchResource is a helper function that returns +func matchResource(kinda, typea, kindb, typeb string) bool { + if kinda == "" { + kinda = "pipeline" + } + if kindb == "" { + kindb = "pipeline" + } + if typea == "" { + typea = "docker" + } + if typeb == "" { + typeb = "docker" + } + return kinda == kindb && typea == typeb +} diff --git a/scheduler/queue/queue_test.go b/scheduler/queue/queue_test.go index 0d6830a5..2a1de66b 100644 --- a/scheduler/queue/queue_test.go +++ b/scheduler/queue/queue_test.go @@ -149,3 +149,41 @@ func TestWithinLimits(t *testing.T) { } } } + +func TestMatchResource(t *testing.T) { + tests := []struct { + kinda, typea, kindb, typeb string + want bool + }{ + // unspecified in yaml, unspecified by agent + {"", "", "", "", true}, + + // unspecified in yaml, specified by agent + {"pipeline", "docker", "", "", true}, + {"pipeline", "", "", "", true}, + {"", "docker", "", "", true}, + + // specified in yaml, unspecified by agent + {"", "", "pipeline", "docker", true}, + {"", "", "pipeline", "", true}, + {"", "", "", "docker", true}, + + // specified in yaml, specified by agent + {"pipeline", "docker", "pipeline", "docker", true}, + {"pipeline", "exec", "pipeline", "docker", false}, + {"approval", "slack", "pipeline", "docker", false}, + + // misc + {"", "docker", "pipeline", "docker", true}, + {"pipeline", "", "pipeline", "docker", true}, + {"pipeline", "docker", "", "docker", true}, + {"pipeline", "docker", "pipeline", "", true}, + } + + for i, test := range tests { + got, want := matchResource(test.kinda, test.typea, test.kindb, test.typeb), test.want + if got != want { + t.Errorf("Unexpectd results at index %d", i) + } + } +} diff --git a/trigger/trigger.go b/trigger/trigger.go index f2b12b9f..c6b2acab 100644 --- a/trigger/trigger.go +++ b/trigger/trigger.go @@ -333,8 +333,8 @@ func (t *triggerer) Trigger(ctx context.Context, repo *core.Repository, base *co RepoID: repo.ID, Number: i + 1, Name: match.Name, - Kind: "", - Type: "", + Kind: match.Kind, + Type: match.Type, OS: match.Platform.OS, Arch: match.Platform.Arch, Variant: match.Platform.Variant, @@ -348,7 +348,9 @@ func (t *triggerer) Trigger(ctx context.Context, repo *core.Repository, base *co Created: time.Now().Unix(), Updated: time.Now().Unix(), } - + if stage.Kind == "pipeline" && stage.Type == "" { + stage.Type = "docker" + } if stage.OS == "" { stage.OS = "linux" } diff --git a/trigger/trigger_test.go b/trigger/trigger_test.go index a042f2c8..c2502374 100644 --- a/trigger/trigger_test.go +++ b/trigger/trigger_test.go @@ -451,6 +451,8 @@ var ( } dummyStage = &core.Stage{ + Kind: "pipeline", + Type: "docker", RepoID: 1, Name: "default", Number: 1,