2019-05-05 22:00:45 +00:00
|
|
|
// Copyright 2019 Sorint.lab
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package action
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-05-06 13:19:29 +00:00
|
|
|
"encoding/json"
|
2019-05-05 22:00:45 +00:00
|
|
|
"net/http"
|
|
|
|
|
2019-05-06 13:19:29 +00:00
|
|
|
"github.com/sorintlab/agola/internal/services/gateway/common"
|
2019-05-05 22:00:45 +00:00
|
|
|
rsapi "github.com/sorintlab/agola/internal/services/runservice/scheduler/api"
|
|
|
|
"github.com/sorintlab/agola/internal/util"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (h *ActionHandler) GetRun(ctx context.Context, runID string) (*rsapi.RunResponse, error) {
|
2019-05-06 13:19:29 +00:00
|
|
|
runResp, resp, err := h.runserviceClient.GetRun(ctx, runID, nil)
|
2019-05-05 22:00:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, ErrFromRemote(resp, err)
|
|
|
|
}
|
2019-05-03 21:19:23 +00:00
|
|
|
canGetRun, err := h.CanGetRun(ctx, runResp.RunConfig.Group)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to determine permissions")
|
|
|
|
}
|
|
|
|
if !canGetRun {
|
|
|
|
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
|
|
|
|
}
|
|
|
|
|
2019-05-05 22:00:45 +00:00
|
|
|
return runResp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type GetRunsRequest struct {
|
|
|
|
PhaseFilter []string
|
2019-05-03 21:19:23 +00:00
|
|
|
Group string
|
2019-05-05 22:00:45 +00:00
|
|
|
LastRun bool
|
|
|
|
ChangeGroups []string
|
|
|
|
StartRunID string
|
|
|
|
Limit int
|
|
|
|
Asc bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *ActionHandler) GetRuns(ctx context.Context, req *GetRunsRequest) (*rsapi.GetRunsResponse, error) {
|
2019-05-03 21:19:23 +00:00
|
|
|
canGetRun, err := h.CanGetRun(ctx, req.Group)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to determine permissions")
|
|
|
|
}
|
|
|
|
if !canGetRun {
|
|
|
|
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
|
|
|
|
}
|
|
|
|
|
|
|
|
groups := []string{req.Group}
|
|
|
|
runsResp, resp, err := h.runserviceClient.GetRuns(ctx, req.PhaseFilter, groups, req.LastRun, req.ChangeGroups, req.StartRunID, req.Limit, req.Asc)
|
2019-05-05 22:00:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, ErrFromRemote(resp, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return runsResp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type GetLogsRequest struct {
|
|
|
|
RunID string
|
|
|
|
TaskID string
|
|
|
|
Setup bool
|
|
|
|
Step int
|
|
|
|
Follow bool
|
|
|
|
Stream bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *ActionHandler) GetLogs(ctx context.Context, req *GetLogsRequest) (*http.Response, error) {
|
2019-05-06 13:19:29 +00:00
|
|
|
runResp, resp, err := h.runserviceClient.GetRun(ctx, req.RunID, nil)
|
2019-05-03 21:19:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, ErrFromRemote(resp, err)
|
|
|
|
}
|
|
|
|
canGetRun, err := h.CanGetRun(ctx, runResp.RunConfig.Group)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to determine permissions")
|
|
|
|
}
|
|
|
|
if !canGetRun {
|
|
|
|
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err = h.runserviceClient.GetLogs(ctx, req.RunID, req.TaskID, req.Setup, req.Step, req.Follow, req.Stream)
|
2019-05-05 22:00:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, ErrFromRemote(resp, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type RunActionType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
RunActionTypeRestart RunActionType = "restart"
|
|
|
|
RunActionTypeStop RunActionType = "stop"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RunActionsRequest struct {
|
|
|
|
RunID string
|
|
|
|
ActionType RunActionType
|
|
|
|
|
|
|
|
// Restart
|
|
|
|
FromStart bool
|
|
|
|
}
|
|
|
|
|
2019-05-07 21:23:58 +00:00
|
|
|
func (h *ActionHandler) RunAction(ctx context.Context, req *RunActionsRequest) (*rsapi.RunResponse, error) {
|
2019-05-06 13:19:29 +00:00
|
|
|
runResp, resp, err := h.runserviceClient.GetRun(ctx, req.RunID, nil)
|
2019-05-03 21:19:23 +00:00
|
|
|
if err != nil {
|
2019-05-07 21:23:58 +00:00
|
|
|
return nil, ErrFromRemote(resp, err)
|
2019-05-03 21:19:23 +00:00
|
|
|
}
|
|
|
|
canGetRun, err := h.CanDoRunActions(ctx, runResp.RunConfig.Group)
|
|
|
|
if err != nil {
|
2019-05-07 21:23:58 +00:00
|
|
|
return nil, errors.Wrapf(err, "failed to determine permissions")
|
2019-05-03 21:19:23 +00:00
|
|
|
}
|
|
|
|
if !canGetRun {
|
2019-05-07 21:23:58 +00:00
|
|
|
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
|
2019-05-03 21:19:23 +00:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:00:45 +00:00
|
|
|
switch req.ActionType {
|
|
|
|
case RunActionTypeRestart:
|
|
|
|
rsreq := &rsapi.RunCreateRequest{
|
|
|
|
RunID: req.RunID,
|
|
|
|
FromStart: req.FromStart,
|
|
|
|
}
|
|
|
|
|
2019-05-07 21:23:58 +00:00
|
|
|
runResp, resp, err = h.runserviceClient.CreateRun(ctx, rsreq)
|
2019-05-05 22:00:45 +00:00
|
|
|
if err != nil {
|
2019-05-07 21:23:58 +00:00
|
|
|
return nil, ErrFromRemote(resp, err)
|
2019-05-05 22:00:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case RunActionTypeStop:
|
|
|
|
rsreq := &rsapi.RunActionsRequest{
|
|
|
|
ActionType: rsapi.RunActionTypeStop,
|
|
|
|
}
|
|
|
|
|
2019-05-07 21:23:58 +00:00
|
|
|
resp, err = h.runserviceClient.RunActions(ctx, req.RunID, rsreq)
|
2019-05-05 22:00:45 +00:00
|
|
|
if err != nil {
|
2019-05-07 21:23:58 +00:00
|
|
|
return nil, ErrFromRemote(resp, err)
|
2019-05-05 22:00:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2019-05-07 21:23:58 +00:00
|
|
|
return nil, util.NewErrBadRequest(errors.Errorf("wrong run action type %q", req.ActionType))
|
2019-05-05 22:00:45 +00:00
|
|
|
}
|
|
|
|
|
2019-05-07 21:23:58 +00:00
|
|
|
return runResp, nil
|
2019-05-05 22:00:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type RunTaskActionType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
RunTaskActionTypeApprove RunTaskActionType = "approve"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RunTaskActionsRequest struct {
|
|
|
|
RunID string
|
|
|
|
TaskID string
|
|
|
|
|
2019-05-06 13:19:29 +00:00
|
|
|
ActionType RunTaskActionType
|
2019-05-05 22:00:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h *ActionHandler) RunTaskAction(ctx context.Context, req *RunTaskActionsRequest) error {
|
2019-05-06 13:19:29 +00:00
|
|
|
runResp, resp, err := h.runserviceClient.GetRun(ctx, req.RunID, nil)
|
2019-05-03 21:19:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return ErrFromRemote(resp, err)
|
|
|
|
}
|
2019-05-06 13:19:29 +00:00
|
|
|
canDoRunAction, err := h.CanDoRunActions(ctx, runResp.RunConfig.Group)
|
2019-05-03 21:19:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to determine permissions")
|
|
|
|
}
|
2019-05-06 13:19:29 +00:00
|
|
|
if !canDoRunAction {
|
2019-05-03 21:19:23 +00:00
|
|
|
return util.NewErrForbidden(errors.Errorf("user not authorized"))
|
|
|
|
}
|
2019-05-06 13:19:29 +00:00
|
|
|
curUserID := h.CurrentUserID(ctx)
|
|
|
|
if curUserID == "" {
|
|
|
|
return util.NewErrBadRequest(errors.Errorf("no logged in user"))
|
|
|
|
}
|
2019-05-03 21:19:23 +00:00
|
|
|
|
2019-05-05 22:00:45 +00:00
|
|
|
switch req.ActionType {
|
|
|
|
case RunTaskActionTypeApprove:
|
2019-05-06 13:19:29 +00:00
|
|
|
rt, ok := runResp.Run.Tasks[req.TaskID]
|
|
|
|
if !ok {
|
|
|
|
return util.NewErrBadRequest(errors.Errorf("run %q doesn't have task %q", req.RunID, req.TaskID))
|
|
|
|
}
|
|
|
|
|
|
|
|
approvers := []string{}
|
|
|
|
annotations := map[string]string{}
|
|
|
|
if rt.Annotations != nil {
|
|
|
|
annotations = rt.Annotations
|
|
|
|
}
|
|
|
|
approversAnnotation, ok := annotations[common.ApproversAnnotation]
|
|
|
|
if ok {
|
|
|
|
if err := json.Unmarshal([]byte(approversAnnotation), &approvers); err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to unmarshal run task approvers annotation")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, approver := range approvers {
|
|
|
|
if approver == curUserID {
|
|
|
|
return util.NewErrBadRequest(errors.Errorf("user %q alredy approved the task", approver))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
approvers = append(approvers, curUserID)
|
|
|
|
|
|
|
|
approversj, err := json.Marshal(approvers)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to marshal run task approvers annotation")
|
|
|
|
}
|
|
|
|
|
|
|
|
annotations[common.ApproversAnnotation] = string(approversj)
|
|
|
|
|
2019-05-05 22:00:45 +00:00
|
|
|
rsreq := &rsapi.RunTaskActionsRequest{
|
2019-05-06 13:19:29 +00:00
|
|
|
ActionType: rsapi.RunTaskActionTypeSetAnnotations,
|
|
|
|
Annotations: annotations,
|
|
|
|
ChangeGroupsUpdateToken: runResp.ChangeGroupsUpdateToken,
|
2019-05-05 22:00:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := h.runserviceClient.RunTaskActions(ctx, req.RunID, req.TaskID, rsreq)
|
|
|
|
if err != nil {
|
|
|
|
return ErrFromRemote(resp, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return util.NewErrBadRequest(errors.Errorf("wrong run task action type %q", req.ActionType))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|