gateway: add manual run creation
Add an API and related action to manually create a run from a git branch/tag/ref with optional commitSHA. Currently only branches and tags are supported (no pull requests). If not commitSHA is provided the commit sha referenced by the provided branch/tag/ref is used.
This commit is contained in:
parent
fafcf3a623
commit
98c2f76f5d
|
@ -21,6 +21,7 @@ import (
|
|||
"net/url"
|
||||
"path"
|
||||
|
||||
gitsource "github.com/sorintlab/agola/internal/gitsources"
|
||||
csapi "github.com/sorintlab/agola/internal/services/configstore/api"
|
||||
"github.com/sorintlab/agola/internal/services/types"
|
||||
"github.com/sorintlab/agola/internal/util"
|
||||
|
@ -120,12 +121,12 @@ func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectReq
|
|||
return nil, errors.Errorf("user doesn't have a linked account for remote source %q", rs.Name)
|
||||
}
|
||||
|
||||
gitsource, err := h.GetGitSource(ctx, rs, user.Name, la)
|
||||
gitSource, err := h.GetGitSource(ctx, rs, user.Name, la)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to create gitsource client: %w", err)
|
||||
}
|
||||
|
||||
repo, err := gitsource.GetRepoInfo(req.RepoPath)
|
||||
repo, err := gitSource.GetRepoInfo(req.RepoPath)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to get repository info from gitsource: %w", err)
|
||||
}
|
||||
|
@ -350,3 +351,165 @@ func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) er
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ActionHandler) ProjectCreateRun(ctx context.Context, projectRef, branch, tag, refName, commitSHA string) error {
|
||||
curUserID := h.CurrentUserID(ctx)
|
||||
|
||||
user, resp, err := h.configstoreClient.GetUser(ctx, curUserID)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to get user %q: %w", curUserID, ErrFromRemote(resp, err))
|
||||
}
|
||||
|
||||
p, resp, err := h.configstoreClient.GetProject(ctx, projectRef)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err))
|
||||
}
|
||||
|
||||
isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to determine ownership: %w", err)
|
||||
}
|
||||
if !isProjectOwner {
|
||||
return util.NewErrForbidden(errors.Errorf("user not authorized"))
|
||||
}
|
||||
|
||||
rs, resp, err := h.configstoreClient.GetRemoteSource(ctx, p.RemoteSourceID)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to get remote source %q: %w", p.RemoteSourceID, ErrFromRemote(resp, err))
|
||||
}
|
||||
h.log.Infof("rs: %s", util.Dump(rs))
|
||||
var la *types.LinkedAccount
|
||||
for _, v := range user.LinkedAccounts {
|
||||
if v.RemoteSourceID == rs.ID {
|
||||
la = v
|
||||
break
|
||||
}
|
||||
}
|
||||
h.log.Infof("la: %s", util.Dump(la))
|
||||
if la == nil {
|
||||
return util.NewErrBadRequest(errors.Errorf("user doesn't have a linked account for remote source %q", rs.Name))
|
||||
}
|
||||
|
||||
gitSource, err := h.GetGitSource(ctx, rs, user.Name, la)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to create gitsource client: %w", err)
|
||||
}
|
||||
|
||||
// check user has access to the repository
|
||||
repoInfo, err := gitSource.GetRepoInfo(p.RepositoryPath)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to get repository info from gitsource: %w", err)
|
||||
}
|
||||
|
||||
set := 0
|
||||
if branch != "" {
|
||||
set++
|
||||
}
|
||||
if tag != "" {
|
||||
set++
|
||||
}
|
||||
if refName != "" {
|
||||
set++
|
||||
}
|
||||
if set == 0 {
|
||||
return util.NewErrBadRequest(errors.Errorf("one of branch, tag or ref is required"))
|
||||
}
|
||||
if set > 1 {
|
||||
return util.NewErrBadRequest(errors.Errorf("only one of branch, tag or ref can be provided"))
|
||||
}
|
||||
|
||||
var refType types.RunRefType
|
||||
var message string
|
||||
var branchLink, tagLink string
|
||||
|
||||
var refCommitSHA string
|
||||
if refName == "" {
|
||||
if branch != "" {
|
||||
refName = gitSource.BranchRef(branch)
|
||||
}
|
||||
if tag != "" {
|
||||
refName = gitSource.TagRef(tag)
|
||||
}
|
||||
}
|
||||
|
||||
gitRefType, name, err := gitSource.RefType(refName)
|
||||
if err != nil {
|
||||
return util.NewErrBadRequest(errors.Errorf("failed to get refType for ref %q: %w", refName, err))
|
||||
}
|
||||
ref, err := gitSource.GetRef(p.RepositoryPath, refName)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to get ref information from git source for ref %q: %w", refName, err)
|
||||
}
|
||||
refCommitSHA = ref.CommitSHA
|
||||
switch gitRefType {
|
||||
case gitsource.RefTypeBranch:
|
||||
branch = name
|
||||
case gitsource.RefTypeTag:
|
||||
tag = name
|
||||
// TODO(sgotti) implement manual run creation on a pull request if really needed
|
||||
default:
|
||||
return errors.Errorf("unsupported ref %q for manual run creation", refName)
|
||||
}
|
||||
|
||||
// TODO(sgotti) check that the provided ref contains the provided commitSHA
|
||||
|
||||
// if no commitSHA has been provided use the ref commit sha
|
||||
if commitSHA == "" && refCommitSHA != "" {
|
||||
commitSHA = refCommitSHA
|
||||
}
|
||||
|
||||
commit, err := gitSource.GetCommit(p.RepositoryPath, commitSHA)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to get commit information from git source for commit sha %q: %w", commitSHA, err)
|
||||
}
|
||||
|
||||
// use the commit full sha since the user could have provided a short commit sha
|
||||
commitSHA = commit.SHA
|
||||
|
||||
if branch != "" {
|
||||
refType = types.RunRefTypeBranch
|
||||
message = commit.Message
|
||||
branchLink = gitSource.BranchLink(repoInfo, branch)
|
||||
|
||||
}
|
||||
|
||||
if tag != "" {
|
||||
refType = types.RunRefTypeBranch
|
||||
message = fmt.Sprintf("Tag %s", tag)
|
||||
tagLink = gitSource.TagLink(repoInfo, tag)
|
||||
|
||||
}
|
||||
|
||||
// use remotesource skipSSHHostKeyCheck config and override with project config if set to true there
|
||||
skipSSHHostKeyCheck := rs.SkipSSHHostKeyCheck
|
||||
if p.SkipSSHHostKeyCheck {
|
||||
skipSSHHostKeyCheck = p.SkipSSHHostKeyCheck
|
||||
}
|
||||
|
||||
req := &CreateRunRequest{
|
||||
RunType: types.RunTypeProject,
|
||||
RefType: refType,
|
||||
RunCreationTrigger: types.RunCreationTriggerTypeManual,
|
||||
|
||||
Project: p.Project,
|
||||
RepoPath: p.RepositoryPath,
|
||||
GitSource: gitSource,
|
||||
CommitSHA: commitSHA,
|
||||
Message: message,
|
||||
Branch: branch,
|
||||
Tag: tag,
|
||||
PullRequestID: "",
|
||||
Ref: refName,
|
||||
SSHPrivKey: p.SSHPrivateKey,
|
||||
SSHHostKey: rs.SSHHostKey,
|
||||
SkipSSHHostKeyCheck: skipSSHHostKeyCheck,
|
||||
CloneURL: repoInfo.SSHCloneURL,
|
||||
|
||||
CommitLink: gitSource.CommitLink(repoInfo, commitSHA),
|
||||
BranchLink: branchLink,
|
||||
TagLink: tagLink,
|
||||
PullRequestLink: "",
|
||||
}
|
||||
|
||||
return h.CreateRuns(ctx, req)
|
||||
}
|
||||
|
|
|
@ -430,7 +430,7 @@ func (h *ActionHandler) CreateRuns(ctx context.Context, req *CreateRunRequest) e
|
|||
|
||||
data, filename, err := h.fetchConfigFiles(req.GitSource, req.RepoPath, req.CommitSHA)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to fetch config file: %w", err)
|
||||
return util.NewErrInternal(errors.Errorf("failed to fetch config file: %w", err))
|
||||
}
|
||||
h.log.Debug("data: %s", data)
|
||||
|
||||
|
|
|
@ -260,3 +260,46 @@ func createProjectResponse(r *csapi.Project) *ProjectResponse {
|
|||
|
||||
return res
|
||||
}
|
||||
|
||||
type ProjectCreateRunRequest struct {
|
||||
Branch string `json:"branch,omitempty"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
CommitSHA string `json:"commit_sha,omitempty"`
|
||||
}
|
||||
|
||||
type ProjectCreateRunHandler struct {
|
||||
log *zap.SugaredLogger
|
||||
ah *action.ActionHandler
|
||||
}
|
||||
|
||||
func NewProjectCreateRunHandler(logger *zap.Logger, ah *action.ActionHandler) *ProjectCreateRunHandler {
|
||||
return &ProjectCreateRunHandler{log: logger.Sugar(), ah: ah}
|
||||
}
|
||||
|
||||
func (h *ProjectCreateRunHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
vars := mux.Vars(r)
|
||||
projectRef, err := url.PathUnescape(vars["projectref"])
|
||||
if err != nil {
|
||||
httpError(w, util.NewErrBadRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
var req ProjectCreateRunRequest
|
||||
d := json.NewDecoder(r.Body)
|
||||
if err := d.Decode(&req); err != nil {
|
||||
httpError(w, util.NewErrBadRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
err = h.ah.ProjectCreateRun(ctx, projectRef, req.Branch, req.Tag, req.Ref, req.CommitSHA)
|
||||
if httpError(w, err) {
|
||||
h.log.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := httpResponse(w, http.StatusCreated, nil); err != nil {
|
||||
h.log.Errorf("err: %+v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@ func (g *Gateway) Run(ctx context.Context) error {
|
|||
deleteProjectHandler := api.NewDeleteProjectHandler(logger, g.ah)
|
||||
projectReconfigHandler := api.NewProjectReconfigHandler(logger, g.ah)
|
||||
projectUpdateRepoLinkedAccountHandler := api.NewProjectUpdateRepoLinkedAccountHandler(logger, g.ah)
|
||||
projectCreateRunHandler := api.NewProjectCreateRunHandler(logger, g.ah)
|
||||
|
||||
secretHandler := api.NewSecretHandler(logger, g.ah)
|
||||
createSecretHandler := api.NewCreateSecretHandler(logger, g.ah)
|
||||
|
@ -242,6 +243,7 @@ func (g *Gateway) Run(ctx context.Context) error {
|
|||
apirouter.Handle("/projects/{projectref}", authForcedHandler(deleteProjectHandler)).Methods("DELETE")
|
||||
apirouter.Handle("/projects/{projectref}/reconfig", authForcedHandler(projectReconfigHandler)).Methods("PUT")
|
||||
apirouter.Handle("/projects/{projectref}/updaterepolinkedaccount", authForcedHandler(projectUpdateRepoLinkedAccountHandler)).Methods("PUT")
|
||||
apirouter.Handle("/projects/{projectref}/createrun", authForcedHandler(projectCreateRunHandler)).Methods("PUT")
|
||||
|
||||
apirouter.Handle("/projectgroups/{projectgroupref}/secrets", authForcedHandler(secretHandler)).Methods("GET")
|
||||
apirouter.Handle("/projects/{projectref}/secrets", authForcedHandler(secretHandler)).Methods("GET")
|
||||
|
|
|
@ -33,4 +33,5 @@ type RunCreationTriggerType string
|
|||
|
||||
const (
|
||||
RunCreationTriggerTypeWebhook RunCreationTriggerType = "webhook"
|
||||
RunCreationTriggerTypeManual RunCreationTriggerType = "manual"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue