Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
147 changes: 76 additions & 71 deletions api/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1728,103 +1728,104 @@ func CancelBuild(c *gin.Context) {
"user": u.GetName(),
}).Infof("canceling build %s", entry)

// TODO: add support for removing builds from the queue
//
// check to see if build is not running
if !strings.EqualFold(b.GetStatus(), constants.StatusRunning) {
retErr := fmt.Errorf("found build %s but its status was %s", entry, b.GetStatus())
switch b.GetStatus() {
case constants.StatusRunning:
// retrieve the worker info
w, err := database.FromContext(c).GetWorkerForHostname(b.GetHost())
if err != nil {
retErr := fmt.Errorf("unable to get worker for build %s: %w", entry, err)
util.HandleError(c, http.StatusNotFound, retErr)

util.HandleError(c, http.StatusBadRequest, retErr)
return
}

return
}
for _, executor := range e {
// check each executor on the worker running the build to see if it's running the build we want to cancel
if strings.EqualFold(executor.Repo.GetFullName(), r.GetFullName()) && *executor.GetBuild().Number == b.GetNumber() {
// prepare the request to the worker
client := http.DefaultClient
client.Timeout = 30 * time.Second

// retrieve the worker info
w, err := database.FromContext(c).GetWorkerForHostname(b.GetHost())
if err != nil {
retErr := fmt.Errorf("unable to get worker for build %s: %w", entry, err)
util.HandleError(c, http.StatusNotFound, retErr)
// set the API endpoint path we send the request to
u := fmt.Sprintf("%s/api/v1/executors/%d/build/cancel", w.GetAddress(), executor.GetID())

return
}
req, err := http.NewRequestWithContext(context.Background(), "DELETE", u, nil)
if err != nil {
retErr := fmt.Errorf("unable to form a request to %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)

for _, executor := range e {
// check each executor on the worker running the build to see if it's running the build we want to cancel
if strings.EqualFold(executor.Repo.GetFullName(), r.GetFullName()) && *executor.GetBuild().Number == b.GetNumber() {
// prepare the request to the worker
client := http.DefaultClient
client.Timeout = 30 * time.Second
return
}

// set the API endpoint path we send the request to
u := fmt.Sprintf("%s/api/v1/executors/%d/build/cancel", w.GetAddress(), executor.GetID())
tm := c.MustGet("token-manager").(*token.Manager)

req, err := http.NewRequestWithContext(context.Background(), "DELETE", u, nil)
if err != nil {
retErr := fmt.Errorf("unable to form a request to %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)
// set mint token options
mto := &token.MintTokenOpts{
Hostname: "vela-server",
TokenType: constants.WorkerAuthTokenType,
TokenDuration: time.Minute * 1,
}

return
}
// mint token
tkn, err := tm.MintToken(mto)
if err != nil {
retErr := fmt.Errorf("unable to generate auth token: %w", err)
util.HandleError(c, http.StatusInternalServerError, retErr)

tm := c.MustGet("token-manager").(*token.Manager)
return
}

// set mint token options
mto := &token.MintTokenOpts{
Hostname: "vela-server",
TokenType: constants.WorkerAuthTokenType,
TokenDuration: time.Minute * 1,
}
// add the token to authenticate to the worker
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn))

// mint token
tkn, err := tm.MintToken(mto)
if err != nil {
retErr := fmt.Errorf("unable to generate auth token: %w", err)
util.HandleError(c, http.StatusInternalServerError, retErr)
// perform the request to the worker
resp, err := client.Do(req)
if err != nil {
retErr := fmt.Errorf("unable to connect to %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)

return
}
return
}
defer resp.Body.Close()

// add the token to authenticate to the worker
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn))
// Read Response Body
respBody, err := io.ReadAll(resp.Body)
if err != nil {
retErr := fmt.Errorf("unable to read response from %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)

// perform the request to the worker
resp, err := client.Do(req)
if err != nil {
retErr := fmt.Errorf("unable to connect to %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)
return
}

return
}
defer resp.Body.Close()

// Read Response Body
respBody, err := io.ReadAll(resp.Body)
if err != nil {
retErr := fmt.Errorf("unable to read response from %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)
err = json.Unmarshal(respBody, b)
if err != nil {
retErr := fmt.Errorf("unable to parse response from %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)

return
}
return
}

err = json.Unmarshal(respBody, b)
if err != nil {
retErr := fmt.Errorf("unable to parse response from %s: %w", u, err)
util.HandleError(c, http.StatusBadRequest, retErr)
c.JSON(resp.StatusCode, b)

return
}
}
case constants.StatusPending:
break

c.JSON(resp.StatusCode, b)
default:
retErr := fmt.Errorf("found build %s but its status was %s", entry, b.GetStatus())

return
}
util.HandleError(c, http.StatusBadRequest, retErr)

return
}

// build has been abandoned
// update the status in the build table
b.SetStatus(constants.StatusCanceled)

err = database.FromContext(c).UpdateBuild(b)
err := database.FromContext(c).UpdateBuild(b)
if err != nil {
retErr := fmt.Errorf("unable to update status for build %s: %w", entry, err)
util.HandleError(c, http.StatusInternalServerError, retErr)
Expand Down Expand Up @@ -1956,6 +1957,10 @@ func CancelBuild(c *gin.Context) {
// description: Bad request
// schema:
// "$ref": "#/definitions/Error"
// '409':
// description: Conflict (requested build token for build not in pending state)
// schema:
// "$ref": "#/definitions/Error"
// '500':
// description: Unable to generate build token
// schema:
Expand All @@ -1979,10 +1984,10 @@ func GetBuildToken(c *gin.Context) {
"user": cl.Subject,
}).Infof("generating build token for build %s/%d", r.GetFullName(), b.GetNumber())

// if build is not in a pending state, then a build token should not be needed - bad request
// if build is not in a pending state, then a build token should not be needed - conflict
if !strings.EqualFold(b.GetStatus(), constants.StatusPending) {
retErr := fmt.Errorf("unable to mint build token: build is not in pending state")
util.HandleError(c, http.StatusBadRequest, retErr)
util.HandleError(c, http.StatusConflict, retErr)

return
}
Expand Down
9 changes: 9 additions & 0 deletions router/middleware/executors/executors.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ func Establish() gin.HandlerFunc {
return func(c *gin.Context) {
e := new([]library.Executor)
b := build.Retrieve(c)

// if build has no host, we cannot establish executors
if len(b.GetHost()) == 0 {
ToContext(c, *e)
c.Next()

return
}

// retrieve the worker
w, err := database.FromContext(c).GetWorkerForHostname(b.GetHost())
if err != nil {
Expand Down