Skip to content

Commit

Permalink
fix(worker): builtin release action (#5597)
Browse files Browse the repository at this point in the history
Signed-off-by: francois  samin <[email protected]>
  • Loading branch information
fsamin authored Dec 17, 2020
1 parent b1029d9 commit 3b2aa60
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 27 deletions.
18 changes: 15 additions & 3 deletions engine/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ import (
"time"

"github.com/gorilla/mux"
"github.com/stretchr/testify/require"

"github.com/ovh/cds/engine/api/authentication/builtin"
"github.com/ovh/cds/engine/api/authentication/local"
authdrivertest "github.com/ovh/cds/engine/api/authentication/test"
"github.com/ovh/cds/engine/api/bootstrap"
apiTest "github.com/ovh/cds/engine/api/test"
"github.com/ovh/cds/engine/api/workflow"
"github.com/ovh/cds/engine/cache"
"github.com/ovh/cds/engine/service"
"github.com/ovh/cds/engine/test"
"github.com/ovh/cds/sdk"
)

func newTestAPI(t *testing.T, bootstrapFunc ...test.Bootstrapf) (*API, *test.FakeTransaction, *Router) {
bootstrapFunc = append(bootstrapFunc, bootstrap.InitiliazeDB)
db, factory, cache := apiTest.SetupPGWithFactory(t, bootstrapFunc...)
db, factory, store := apiTest.SetupPGWithFactory(t, bootstrapFunc...)
router := newRouter(mux.NewRouter(), "/"+test.GetTestName(t))
var cancel context.CancelFunc
router.Background, cancel = context.WithCancel(context.Background())
Expand All @@ -30,7 +33,7 @@ func newTestAPI(t *testing.T, bootstrapFunc ...test.Bootstrapf) (*API, *test.Fak
Router: router,
DBConnectionFactory: factory,
Config: Configuration{},
Cache: cache,
Cache: store,
}
api.AuthenticationDrivers = make(map[sdk.AuthConsumerType]sdk.AuthDriver)
api.AuthenticationDrivers[sdk.ConsumerLocal] = local.NewDriver(context.TODO(), false, "http://localhost:8080", "")
Expand All @@ -40,7 +43,16 @@ func newTestAPI(t *testing.T, bootstrapFunc ...test.Bootstrapf) (*API, *test.Fak
api.GoRoutines = sdk.NewGoRoutines()

api.InitRouter()
t.Cleanup(func() { cancel() })
t.Cleanup(func() {
// Clean all the pending crafting workflow runs
lockKey := cache.Key("api:workflowRunCraft")
require.NoError(t, store.DeleteAll(lockKey))
ids, _ := workflow.LoadCratingWorkflowRunIDs(api.mustDB())
for _, id := range ids {
require.NoError(t, workflow.UpdateCraftedWorkflowRun(api.mustDB(), id))
}
cancel()
})
return api, db, router
}

Expand Down
2 changes: 1 addition & 1 deletion engine/api/repositories_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ vcs_ssh_key: proj-blabla
w2.WorkflowData.Node.Hooks = append(w2.WorkflowData.Node.Hooks[:index], w2.WorkflowData.Node.Hooks[index+1:]...)

// save the workflow with the repositorywebhook deleted
t.Log("Updating the workflo without the repositorywebhook=====")
t.Log("Updating the workflow without the repositorywebhook=====")
test.NoError(t, workflow.Update(context.TODO(), db, api.Cache, *proj, w2, workflow.UpdateOptions{}))

req, err = http.NewRequest("POST", uri, nil)
Expand Down
6 changes: 3 additions & 3 deletions engine/api/workflow/dao_node_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func LoadNodeRunIDs(db gorp.SqlExecutor, wIDs []int64, status []string) ([]sdk.W
}

//LoadNodeRun load a specific node run on a workflow
func LoadNodeRun(db gorp.SqlExecutor, projectkey, workflowname string, id int64, loadOpts LoadRunOptions) (*sdk.WorkflowNodeRun, error) {
func LoadNodeRun(db gorp.SqlExecutor, projectkey, workflowname string, noderunID int64, loadOpts LoadRunOptions) (*sdk.WorkflowNodeRun, error) {
var rr = NodeRun{}
var testsField string
if loadOpts.WithTests {
Expand All @@ -94,8 +94,8 @@ func LoadNodeRun(db gorp.SqlExecutor, projectkey, workflowname string, id int64,
and workflow.name = $2
and workflow_node_run.id = $3`, nodeRunFields, testsField)

if err := db.SelectOne(&rr, query, projectkey, workflowname, id); err != nil {
return nil, sdk.WrapError(err, "Unable to load workflow_node_run proj=%s, workflow=%s, node=%d", projectkey, workflowname, id)
if err := db.SelectOne(&rr, query, projectkey, workflowname, noderunID); err != nil {
return nil, sdk.WrapError(err, "Unable to load workflow_node_run proj=%s, workflow=%s, noderun_id=%d", projectkey, workflowname, noderunID)
}

r, err := fromDBNodeRun(rr, loadOpts)
Expand Down
28 changes: 14 additions & 14 deletions engine/api/workflow_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,29 @@ func (api *API) releaseApplicationWorkflowHandler() service.Handler {
vars := mux.Vars(r)
key := vars["key"]
name := vars["permWorkflowName"]
nodeRunID, errN := requestVarInt(r, "nodeRunID")
if errN != nil {
return errN
nodeRunID, err := requestVarInt(r, "nodeRunID")
if err != nil {
return err
}

var req sdk.WorkflowNodeRunRelease
if errU := service.UnmarshalBody(r, &req); errU != nil {
return errU
if err := service.UnmarshalBody(r, &req); err != nil {
return err
}

proj, errprod := project.Load(ctx, api.mustDB(), key)
if errprod != nil {
return sdk.WrapError(errprod, "releaseApplicationWorkflowHandler")
proj, err := project.Load(ctx, api.mustDB(), key)
if err != nil {
return err
}
loadOpts := workflow.LoadRunOptions{WithArtifacts: true}
wNodeRun, errWNR := workflow.LoadNodeRun(api.mustDB(), key, name, nodeRunID, loadOpts)
if errWNR != nil {
return sdk.WrapError(errWNR, "releaseApplicationWorkflowHandler")
wNodeRun, err := workflow.LoadNodeRun(api.mustDB(), key, name, nodeRunID, loadOpts)
if err != nil {
return err
}

workflowRun, errWR := workflow.LoadRunByIDAndProjectKey(api.mustDB(), key, wNodeRun.WorkflowRunID, loadOpts)
if errWR != nil {
return sdk.WrapError(errWR, "releaseApplicationWorkflowHandler")
workflowRun, err := workflow.LoadRunByIDAndProjectKey(api.mustDB(), key, wNodeRun.WorkflowRunID, loadOpts)
if err != nil {
return err
}

workflowArtifacts := []sdk.WorkflowNodeRunArtifact{}
Expand Down
145 changes: 145 additions & 0 deletions engine/api/workflow_application_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ovh/cds/engine/api/application"
"github.com/ovh/cds/engine/api/services"
"github.com/ovh/cds/engine/api/test"
"github.com/ovh/cds/engine/api/test/assets"
"github.com/ovh/cds/engine/gorpmapper"
"github.com/ovh/cds/sdk"
)

func Test_releaseApplicationWorkflowHandler(t *testing.T) {
api, db, router := newTestAPI(t)

mockVCSSservice, _ := assets.InsertService(t, db, "Test_releaseApplicationWorkflowHandlerVCS", sdk.TypeVCS)
mockCDNService, _ := assets.InitCDNService(t, db)
defer func() {
_ = services.Delete(db, mockCDNService)
_ = services.Delete(db, mockVCSSservice)
}()

//This is a mock for the repositories service
services.HTTPClient = mock(
func(r *http.Request) (*http.Response, error) {
body := new(bytes.Buffer)
wri := new(http.Response)
enc := json.NewEncoder(body)
wri.Body = ioutil.NopCloser(body)

switch r.URL.String() {
case "/vcs/github/repos/myproj/myapp":
repo := sdk.VCSRepo{
ID: "1",
Name: "bar",
URL: "url",
Fullname: "foo/bar",
HTTPCloneURL: "",
Slug: "",
SSHCloneURL: "",
}
if err := enc.Encode(repo); err != nil {
return writeError(wri, err)
}
case "/vcs/github/repos/myproj/myapp/branches":
bs := []sdk.VCSBranch{}
b := sdk.VCSBranch{
DisplayID: "master",
Default: true,
}
bs = append(bs, b)
b2 := sdk.VCSBranch{
DisplayID: "my-branch",
Default: false,
}
bs = append(bs, b2)
if err := enc.Encode(bs); err != nil {
return writeError(wri, err)
}
case "/vcs/github/repos/myproj/myapp/branches/?branch=master":
b := sdk.VCSBranch{
DisplayID: "master",
Default: true,
}
if err := enc.Encode(b); err != nil {
return writeError(wri, err)
}
case "/vcs/github/repos/myproj/myapp/commits/":
c := sdk.VCSCommit{
URL: "url",
Message: "Msg",
Timestamp: time.Now().Unix(),
Hash: "123",
}
if err := enc.Encode(c); err != nil {
return writeError(wri, err)
}
case "/vcs/github/repos/myproj/myapp/branches/?branch=my-branch":
b := sdk.VCSBranch{
DisplayID: "my-branch",
Default: true,
}
if err := enc.Encode(b); err != nil {
return writeError(wri, err)
}
wri.StatusCode = http.StatusCreated
case "/vcs/github/repos/myproj/myapp/releases":
r := sdk.VCSRelease{
ID: 0,
UploadURL: "upload-url",
}
if err := enc.Encode(r); err != nil {
return writeError(wri, err)
}
wri.StatusCode = http.StatusOK
}
return wri, nil
},
)

f := func(t *testing.T, db gorpmapper.SqlExecutorWithTx, _ *sdk.Pipeline, app *sdk.Application) {
app.VCSServer = "github"
app.RepositoryFullname = "myproj/myapp"
app.RepositoryStrategy = sdk.RepositoryStrategy{
ConnectionType: "https",
}
require.NoError(t, application.Update(db, app))
}

ctx := testRunWorkflow(t, api, router, f)
testGetWorkflowJobAsWorker(t, api, db, router, &ctx)
assert.NotNil(t, ctx.job)

// Register the worker
testRegisterWorker(t, api, db, router, &ctx)
// Register the hatchery
testRegisterHatchery(t, api, db, router, &ctx)

uri := router.GetRoute("POST", api.releaseApplicationWorkflowHandler, map[string]string{
"key": ctx.project.Key,
"permWorkflowName": ctx.workflow.Name,
"number": fmt.Sprintf("%d", ctx.run.Number),
"nodeRunID": fmt.Sprintf("%d", ctx.job.WorkflowNodeRunID),
})
test.NotEmpty(t, uri)
rec := httptest.NewRecorder()
req := assets.NewJWTAuthentifiedRequest(t, ctx.workerToken, "POST", uri, sdk.WorkflowNodeRunRelease{
TagName: "my_tag",
ReleaseTitle: "my_release",
ReleaseContent: "my_content",
})
router.Mux.ServeHTTP(rec, req)
require.Equal(t, 204, rec.Code)
}
8 changes: 7 additions & 1 deletion engine/api/workflow_queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ type testRunWorkflowCtx struct {
model *sdk.Model
}

func testRunWorkflow(t *testing.T, api *API, router *Router) testRunWorkflowCtx {
type testRunWorkflowOptions func(*testing.T, gorpmapper.SqlExecutorWithTx, *sdk.Pipeline, *sdk.Application)

func testRunWorkflow(t *testing.T, api *API, router *Router, optsF ...testRunWorkflowOptions) testRunWorkflowCtx {
db, err := api.mustDB().Begin()
require.NoError(t, err)

Expand Down Expand Up @@ -113,6 +115,10 @@ func testRunWorkflow(t *testing.T, api *API, router *Router) testRunWorkflowCtx
t.Fatal(err)
}

for _, opt := range optsF {
opt(t, db, &pip, app)
}

k := &sdk.ApplicationKey{
Name: "my-app-key",
Type: "pgp",
Expand Down
12 changes: 10 additions & 2 deletions engine/worker/internal/action/builtin_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/ovh/cds/engine/worker/pkg/workerruntime"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/log"
)

func RunRelease(ctx context.Context, wk workerruntime.Runtime, a sdk.Action, secrets []sdk.Variable) (sdk.Result, error) {
Expand Down Expand Up @@ -65,8 +66,15 @@ func RunRelease(ctx context.Context, wk workerruntime.Runtime, a sdk.Action, sec
Artifacts: artSplitted,
}

if err := wk.Client().WorkflowNodeRunRelease(pkey.Value, wName.Value, wRunNumber, jobID, req); err != nil {
return res, fmt.Errorf("Cannot make workflow node run release: %s", err)
jobrun, err := wk.Client().QueueJobInfo(ctx, jobID)
if err != nil {
return res, fmt.Errorf("unable to get job info: %v", err)
}

log.Info(ctx, "RunRelease> jobRunID=%v WorkflowNodeRunID:%v", jobID, jobrun.WorkflowNodeRunID)

if err := wk.Client().WorkflowNodeRunRelease(pkey.Value, wName.Value, wRunNumber, jobrun.WorkflowNodeRunID, req); err != nil {
return res, fmt.Errorf("unable to make workflow node run release: %v", err)
}

return sdk.Result{Status: sdk.StatusSuccess}, nil
Expand Down
11 changes: 9 additions & 2 deletions engine/worker/internal/action/builtin_release_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ func TestRunRelease(t *testing.T) {

wk, ctx := SetupTest(t)

gock.New("http://lolcat.host").Post("/project/projKey/workflows/workflowName/runs/999/nodes/666/release").
gock.New("http://lolcat.host").Get("/queue/workflows/666/infos").
Reply(200).JSON(
sdk.WorkflowNodeJobRun{
WorkflowNodeRunID: 6,
})
gock.New("http://lolcat.host").Post("/project/projKey/workflows/workflowName/runs/999/nodes/6/release").
Reply(200)

var checkRequest gock.ObserverFunc = func(request *http.Request, mock gock.Mock) {
Expand All @@ -30,21 +35,23 @@ func TestRunRelease(t *testing.T) {
if mock != nil {
t.Logf("%s %s - Body: %s", mock.Request().Method, mock.Request().URLStruct.String(), string(bodyContent))
switch mock.Request().URLStruct.String() {
case "http://lolcat.host/queue/workflows/666/coverage":
case "http://lolcat.host/project/projKey/workflows/workflowName/runs/999/nodes/6/release":
var releaseRequest sdk.WorkflowNodeRunRelease
err := json.Unmarshal(bodyContent, &releaseRequest)
assert.NoError(t, err)
require.Equal(t, "1.1.1", releaseRequest.TagName)
require.Equal(t, "My Title", releaseRequest.ReleaseTitle)
require.Equal(t, "My description", releaseRequest.ReleaseContent)
require.Equal(t, []string{"*.deb"}, releaseRequest.Artifacts)
t.Logf("release request: %+v", releaseRequest)
}
}
}
gock.Observe(checkRequest)

gock.InterceptClient(wk.Client().(cdsclient.Raw).HTTPClient())
gock.InterceptClient(wk.Client().(cdsclient.Raw).HTTPSSEClient())

wk.Params = append(wk.Params, []sdk.Parameter{
{
Name: "cds.project",
Expand Down
2 changes: 1 addition & 1 deletion sdk/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ func (s *stack) String() string {
} else {
name = sp[1]
}
ignoredNames := StringSlice{"NewError", "NewErrorFrom", "WithStack", "WrapError", "Append", "NewErrorWithStack"}
ignoredNames := StringSlice{"NewError", "NewErrorFrom", "WithStack", "WrapError", "Append", "NewErrorWithStack", "extractBodyErrorFromResponse", "Stream"}
if !ignoredNames.Contains(name) {
names = append(names, name)
}
Expand Down

0 comments on commit 3b2aa60

Please sign in to comment.