2019-02-21 16:58:25 +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.
2019-05-03 21:48:49 +00:00
package action
2019-02-21 16:58:25 +00:00
import (
"context"
"fmt"
2019-04-30 10:13:51 +00:00
"net/url"
2019-02-21 16:58:25 +00:00
"path"
2019-07-01 09:40:20 +00:00
gitsource "agola.io/agola/internal/gitsources"
2022-02-24 10:18:29 +00:00
"agola.io/agola/internal/services/gateway/common"
2019-07-01 09:40:20 +00:00
"agola.io/agola/internal/services/types"
"agola.io/agola/internal/util"
2019-07-31 13:39:07 +00:00
csapitypes "agola.io/agola/services/configstore/api/types"
cstypes "agola.io/agola/services/configstore/types"
2019-02-21 16:58:25 +00:00
2019-05-23 09:23:14 +00:00
errors "golang.org/x/xerrors"
2019-02-21 16:58:25 +00:00
)
2019-07-31 13:39:07 +00:00
func ( h * ActionHandler ) GetProject ( ctx context . Context , projectRef string ) ( * csapitypes . Project , error ) {
2022-02-21 11:19:55 +00:00
project , _ , err := h . configstoreClient . GetProject ( ctx , projectRef )
2019-05-05 12:27:22 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , err
2019-05-05 12:27:22 +00:00
}
2019-05-03 21:19:23 +00:00
isProjectMember , err := h . IsProjectMember ( ctx , project . OwnerType , project . OwnerID )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to determine ownership: %w" , err )
2019-05-03 21:19:23 +00:00
}
2019-07-31 13:17:54 +00:00
if project . GlobalVisibility == cstypes . VisibilityPublic {
2019-05-03 21:19:23 +00:00
return project , nil
}
if ! isProjectMember {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrForbidden , errors . Errorf ( "user not authorized" ) )
2019-05-03 21:19:23 +00:00
}
2019-05-05 12:27:22 +00:00
return project , nil
}
2019-02-21 16:58:25 +00:00
type CreateProjectRequest struct {
Name string
2019-05-05 15:19:23 +00:00
ParentRef string
2019-07-31 13:17:54 +00:00
Visibility cstypes . Visibility
2019-02-21 16:58:25 +00:00
RemoteSourceName string
2019-04-03 09:07:54 +00:00
RepoPath string
2019-02-21 16:58:25 +00:00
SkipSSHHostKeyCheck bool
2020-01-25 11:33:53 +00:00
PassVarsToForkedPR bool
2019-02-21 16:58:25 +00:00
}
2019-07-31 13:39:07 +00:00
func ( h * ActionHandler ) CreateProject ( ctx context . Context , req * CreateProjectRequest ) ( * csapitypes . Project , error ) {
2022-02-24 10:18:29 +00:00
curUserID := common . CurrentUserID ( ctx )
2019-05-09 13:34:58 +00:00
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , curUserID )
2019-05-03 21:19:23 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get user %q: %w" , curUserID , err ) )
2019-05-03 21:19:23 +00:00
}
parentRef := req . ParentRef
if parentRef == "" {
// create project in current user namespace
parentRef = path . Join ( "user" , user . Name )
}
2022-02-21 11:19:55 +00:00
pg , _ , err := h . configstoreClient . GetProjectGroup ( ctx , parentRef )
2019-05-03 21:19:23 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get project group %q: %w" , parentRef , err ) )
2019-05-03 21:19:23 +00:00
}
isProjectOwner , err := h . IsProjectOwner ( ctx , pg . OwnerType , pg . OwnerID )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to determine ownership: %w" , err )
2019-05-03 21:19:23 +00:00
}
if ! isProjectOwner {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrForbidden , errors . Errorf ( "user not authorized" ) )
2019-05-03 21:19:23 +00:00
}
2019-02-21 16:58:25 +00:00
if ! util . ValidateName ( req . Name ) {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "invalid project name %q" , req . Name ) )
2019-02-21 16:58:25 +00:00
}
2019-05-03 07:55:37 +00:00
if req . RemoteSourceName == "" {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "empty remote source name" ) )
2019-05-03 07:55:37 +00:00
}
if req . RepoPath == "" {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "empty remote repo path" ) )
2019-05-03 07:55:37 +00:00
}
2019-02-21 16:58:25 +00:00
2019-05-03 12:24:18 +00:00
projectPath := path . Join ( pg . Path , req . Name )
2022-02-21 11:19:55 +00:00
if _ , _ , err = h . configstoreClient . GetProject ( ctx , projectPath ) ; err != nil {
if ! util . RemoteErrorIs ( err , util . ErrNotExist ) {
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get project %q: %w" , req . Name , err ) )
2019-05-03 12:24:18 +00:00
}
} else {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "project %q already exists" , projectPath ) )
2019-05-03 12:24:18 +00:00
}
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , req . RemoteSourceName )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get remote source %q: %w" , req . RemoteSourceName , err ) )
2019-02-21 16:58:25 +00:00
}
2019-07-31 13:17:54 +00:00
var la * cstypes . LinkedAccount
2019-02-21 16:58:25 +00:00
for _ , v := range user . LinkedAccounts {
if v . RemoteSourceID == rs . ID {
la = v
break
}
}
if la == nil {
return nil , errors . Errorf ( "user doesn't have a linked account for remote source %q" , rs . Name )
}
2019-06-11 09:08:40 +00:00
gitSource , err := h . GetGitSource ( ctx , rs , user . Name , la )
2019-04-03 09:07:54 +00:00
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to create gitsource client: %w" , err )
2019-04-03 09:07:54 +00:00
}
2019-06-11 09:08:40 +00:00
repo , err := gitSource . GetRepoInfo ( req . RepoPath )
2019-04-03 09:07:54 +00:00
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to get repository info from gitsource: %w" , err )
2019-04-03 09:07:54 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "generating ssh key pairs" )
2019-04-03 09:07:54 +00:00
privateKey , _ , err := util . GenSSHKeyPair ( 4096 )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to generate ssh key pair: %w" , err )
2019-04-03 09:07:54 +00:00
}
2019-07-31 13:17:54 +00:00
p := & cstypes . Project {
2019-03-14 13:36:18 +00:00
Name : req . Name ,
2019-07-31 13:17:54 +00:00
Parent : cstypes . Parent {
Type : cstypes . ConfigTypeProjectGroup ,
2019-05-05 15:19:23 +00:00
ID : parentRef ,
2019-03-14 13:36:18 +00:00
} ,
2019-05-03 10:21:44 +00:00
Visibility : req . Visibility ,
2019-07-31 13:17:54 +00:00
RemoteRepositoryConfigType : cstypes . RemoteRepositoryConfigTypeRemoteSource ,
2019-05-03 10:21:44 +00:00
RemoteSourceID : rs . ID ,
LinkedAccountID : la . ID ,
RepositoryID : repo . ID ,
RepositoryPath : req . RepoPath ,
SkipSSHHostKeyCheck : req . SkipSSHHostKeyCheck ,
SSHPrivateKey : string ( privateKey ) ,
2020-01-25 11:33:53 +00:00
PassVarsToForkedPR : req . PassVarsToForkedPR ,
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "creating project" )
2022-02-21 11:19:55 +00:00
rp , _ , err := h . configstoreClient . CreateProject ( ctx , p )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to create project: %w" , err ) )
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "project %s created, ID: %s" , rp . Name , rp . ID )
2019-06-14 12:57:59 +00:00
if serr := h . setupGitSourceRepo ( ctx , rs , user , la , rp ) ; serr != nil {
var err error
2022-02-21 17:07:58 +00:00
h . log . Err ( err ) . Msgf ( "failed to setup git source repo, trying to cleanup" )
2019-06-14 12:57:59 +00:00
// try to cleanup gitsource configs and remove project
// we'll log but ignore errors
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "deleting project with ID: %q" , rp . ID )
2022-02-21 11:19:55 +00:00
if _ , err := h . configstoreClient . DeleteProject ( ctx , rp . ID ) ; err != nil {
2022-02-21 17:07:58 +00:00
h . log . Err ( err ) . Msgf ( "failed to delete project " )
2019-06-14 12:57:59 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "cleanup git source repo" )
2019-06-14 12:57:59 +00:00
if err := h . cleanupGitSourceRepo ( ctx , rs , user , la , rp ) ; err != nil {
2022-02-21 17:07:58 +00:00
h . log . Err ( err ) . Msgf ( "failed to cleanup git source repo" )
2019-06-14 12:57:59 +00:00
}
return nil , errors . Errorf ( "failed to setup git source repo: %w" , serr )
}
2019-02-21 16:58:25 +00:00
2019-06-14 12:57:59 +00:00
return rp , nil
2019-02-21 16:58:25 +00:00
}
2019-05-12 21:22:18 +00:00
type UpdateProjectRequest struct {
2019-09-24 13:19:17 +00:00
Name * string
ParentRef * string
2020-01-25 11:33:53 +00:00
Visibility * cstypes . Visibility
PassVarsToForkedPR * bool
2019-05-12 21:22:18 +00:00
}
2019-07-31 13:39:07 +00:00
func ( h * ActionHandler ) UpdateProject ( ctx context . Context , projectRef string , req * UpdateProjectRequest ) ( * csapitypes . Project , error ) {
2022-02-21 11:19:55 +00:00
p , _ , err := h . configstoreClient . GetProject ( ctx , projectRef )
2019-05-12 21:22:18 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get project %q: %w" , projectRef , err ) )
2019-05-12 21:22:18 +00:00
}
isProjectOwner , err := h . IsProjectOwner ( ctx , p . OwnerType , p . OwnerID )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to determine ownership: %w" , err )
2019-05-12 21:22:18 +00:00
}
if ! isProjectOwner {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrForbidden , errors . Errorf ( "user not authorized" ) )
2019-05-12 21:22:18 +00:00
}
2019-09-24 13:19:17 +00:00
if req . Name != nil {
p . Name = * req . Name
}
if req . ParentRef != nil {
p . Parent . ID = * req . ParentRef
}
if req . Visibility != nil {
p . Visibility = * req . Visibility
}
2020-01-25 11:33:53 +00:00
if req . PassVarsToForkedPR != nil {
p . PassVarsToForkedPR = * req . PassVarsToForkedPR
}
2019-05-12 21:22:18 +00:00
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "updating project" )
2022-02-21 11:19:55 +00:00
rp , _ , err := h . configstoreClient . UpdateProject ( ctx , p . ID , p . Project )
2019-05-12 21:22:18 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to update project: %w" , err ) )
2019-05-12 21:22:18 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "project %s updated, ID: %s" , p . Name , p . ID )
2019-05-12 21:22:18 +00:00
return rp , nil
}
2019-07-31 13:39:07 +00:00
func ( h * ActionHandler ) ProjectUpdateRepoLinkedAccount ( ctx context . Context , projectRef string ) ( * csapitypes . Project , error ) {
2022-02-24 10:18:29 +00:00
curUserID := common . CurrentUserID ( ctx )
2019-05-09 13:36:47 +00:00
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , curUserID )
2019-05-09 13:36:47 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get user %q: %w" , curUserID , err ) )
2019-05-09 13:36:47 +00:00
}
2022-02-21 11:19:55 +00:00
p , _ , err := h . configstoreClient . GetProject ( ctx , projectRef )
2019-05-09 13:36:47 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get project %q: %w" , projectRef , err ) )
2019-05-09 13:36:47 +00:00
}
isProjectOwner , err := h . IsProjectOwner ( ctx , p . OwnerType , p . OwnerID )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to determine ownership: %w" , err )
2019-05-09 13:36:47 +00:00
}
if ! isProjectOwner {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrForbidden , errors . Errorf ( "user not authorized" ) )
2019-05-09 13:36:47 +00:00
}
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , p . RemoteSourceID )
2019-05-09 13:36:47 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get remote source %q: %w" , p . RemoteSourceID , err ) )
2019-05-09 13:36:47 +00:00
}
2019-07-31 13:17:54 +00:00
var la * cstypes . LinkedAccount
2019-05-09 13:36:47 +00:00
for _ , v := range user . LinkedAccounts {
if v . RemoteSourceID == rs . ID {
la = v
break
}
}
if la == nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user doesn't have a linked account for remote source %q" , rs . Name ) )
2019-05-09 13:36:47 +00:00
}
gitsource , err := h . GetGitSource ( ctx , rs , user . Name , la )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to create gitsource client: %w" , err )
2019-05-09 13:36:47 +00:00
}
// check user has access to the repository
_ , err = gitsource . GetRepoInfo ( p . RepositoryPath )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to get repository info from gitsource: %w" , err )
2019-05-09 13:36:47 +00:00
}
p . LinkedAccountID = la . ID
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "updating project" )
2022-02-21 11:19:55 +00:00
rp , _ , err := h . configstoreClient . UpdateProject ( ctx , p . ID , p . Project )
2019-05-09 13:36:47 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to update project: %w" , err ) )
2019-05-09 13:36:47 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "project %s updated, ID: %s" , p . Name , p . ID )
2019-05-09 13:36:47 +00:00
return rp , nil
}
2019-07-31 13:39:07 +00:00
func ( h * ActionHandler ) setupGitSourceRepo ( ctx context . Context , rs * cstypes . RemoteSource , user * cstypes . User , la * cstypes . LinkedAccount , project * csapitypes . Project ) error {
2019-05-03 21:48:49 +00:00
gitsource , err := h . GetGitSource ( ctx , rs , user . Name , la )
2019-04-03 09:07:54 +00:00
if err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to create gitsource client: %w" , err )
2019-04-03 09:07:54 +00:00
}
2019-02-21 16:58:25 +00:00
2019-04-11 15:11:17 +00:00
pubKey , err := util . ExtractPublicKey ( [ ] byte ( project . SSHPrivateKey ) )
2019-02-21 16:58:25 +00:00
if err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to extract public key: %w" , err )
2019-02-21 16:58:25 +00:00
}
2019-06-14 12:57:59 +00:00
webhookURL , err := h . genWebhookURL ( project )
2019-04-30 10:13:51 +00:00
if err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to generate webhook url: %w" , err )
2019-04-30 10:13:51 +00:00
}
2019-02-21 16:58:25 +00:00
2019-02-28 16:19:53 +00:00
// generate deploy keys and webhooks containing the agola project id so we
// can have multiple projects referencing the same remote repository and this
// will trigger multiple different runs
2019-04-11 15:11:17 +00:00
deployKeyName := fmt . Sprintf ( "agola deploy key - %s" , project . ID )
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "creating/updating deploy key: %s" , deployKeyName )
2019-04-11 15:11:17 +00:00
if err := gitsource . UpdateDeployKey ( project . RepositoryPath , deployKeyName , string ( pubKey ) , true ) ; err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to create deploy key: %w" , err )
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "deleting existing webhooks" )
2019-06-14 12:57:59 +00:00
if err := gitsource . DeleteRepoWebhook ( project . RepositoryPath , webhookURL ) ; err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to delete repository webhook: %w" , err )
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "creating webhook to url: %s" , webhookURL )
2019-06-14 12:57:59 +00:00
if err := gitsource . CreateRepoWebhook ( project . RepositoryPath , webhookURL , project . WebhookSecret ) ; err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to create repository webhook: %w" , err )
2019-02-21 16:58:25 +00:00
}
return nil
}
2019-07-31 13:39:07 +00:00
func ( h * ActionHandler ) cleanupGitSourceRepo ( ctx context . Context , rs * cstypes . RemoteSource , user * cstypes . User , la * cstypes . LinkedAccount , project * csapitypes . Project ) error {
2019-06-14 12:57:59 +00:00
gitsource , err := h . GetGitSource ( ctx , rs , user . Name , la )
if err != nil {
return errors . Errorf ( "failed to create gitsource client: %w" , err )
}
webhookURL , err := h . genWebhookURL ( project )
if err != nil {
return errors . Errorf ( "failed to generate webhook url: %w" , err )
}
// generate deploy keys and webhooks containing the agola project id so we
// can have multiple projects referencing the same remote repository and this
// will trigger multiple different runs
deployKeyName := fmt . Sprintf ( "agola deploy key - %s" , project . ID )
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "deleting deploy key: %s" , deployKeyName )
2019-06-14 12:57:59 +00:00
if err := gitsource . DeleteDeployKey ( project . RepositoryPath , deployKeyName ) ; err != nil {
return errors . Errorf ( "failed to create deploy key: %w" , err )
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "deleting existing webhooks" )
2019-06-14 12:57:59 +00:00
if err := gitsource . DeleteRepoWebhook ( project . RepositoryPath , webhookURL ) ; err != nil {
return errors . Errorf ( "failed to delete repository webhook: %w" , err )
}
return nil
}
2019-07-31 13:39:07 +00:00
func ( h * ActionHandler ) genWebhookURL ( project * csapitypes . Project ) ( string , error ) {
2019-06-14 12:57:59 +00:00
baseWebhookURL := fmt . Sprintf ( "%s/webhooks" , h . apiExposedURL )
webhookURL , err := url . Parse ( baseWebhookURL )
if err != nil {
return "" , errors . Errorf ( "failed to parse base webhook url %q: %w" , baseWebhookURL , err )
}
q := url . Values { }
q . Add ( "projectid" , project . ID )
q . Add ( "agolaid" , h . agolaID )
webhookURL . RawQuery = q . Encode ( )
return webhookURL . String ( ) , nil
}
2019-05-03 21:48:49 +00:00
func ( h * ActionHandler ) ReconfigProject ( ctx context . Context , projectRef string ) error {
2022-02-21 11:19:55 +00:00
p , _ , err := h . configstoreClient . GetProject ( ctx , projectRef )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get project %q: %w" , projectRef , err ) )
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:19:23 +00:00
isProjectOwner , err := h . IsProjectOwner ( ctx , p . OwnerType , p . OwnerID )
if err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to determine ownership: %w" , err )
2019-05-03 21:19:23 +00:00
}
if ! isProjectOwner {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrForbidden , errors . Errorf ( "user not authorized" ) )
2019-05-03 21:19:23 +00:00
}
2019-06-14 13:17:19 +00:00
user , rs , la , err := h . getRemoteRepoAccessData ( ctx , p . LinkedAccountID )
2019-02-21 16:58:25 +00:00
if err != nil {
2019-06-14 13:17:19 +00:00
return errors . Errorf ( "failed to get remote repo access data: %w" , err )
2019-02-21 16:58:25 +00:00
}
2019-04-11 15:11:17 +00:00
// TODO(sgotti) update project repo path if the remote let us query by repository id
2019-06-14 12:57:59 +00:00
return h . setupGitSourceRepo ( ctx , rs , user , la , p )
2019-02-21 16:58:25 +00:00
}
2019-05-05 12:27:22 +00:00
func ( h * ActionHandler ) DeleteProject ( ctx context . Context , projectRef string ) error {
2022-02-21 11:19:55 +00:00
p , _ , err := h . configstoreClient . GetProject ( ctx , projectRef )
2019-05-03 21:19:23 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get project %q: %w" , projectRef , err ) )
2019-05-03 21:19:23 +00:00
}
isProjectOwner , err := h . IsProjectOwner ( ctx , p . OwnerType , p . OwnerID )
if err != nil {
2019-05-23 09:23:14 +00:00
return errors . Errorf ( "failed to determine ownership: %w" , err )
2019-05-03 21:19:23 +00:00
}
if ! isProjectOwner {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrForbidden , errors . Errorf ( "user not authorized" ) )
2019-05-03 21:19:23 +00:00
}
2019-06-14 13:17:19 +00:00
// get data needed for repo cleanup
// we'll log but ignore errors
canDoRepCleanup := true
user , rs , la , err := h . getRemoteRepoAccessData ( ctx , p . LinkedAccountID )
if err != nil {
canDoRepCleanup = false
2022-02-21 17:07:58 +00:00
h . log . Err ( err ) . Msgf ( "failed to get remote repo access data: %+v" , err )
2019-06-14 13:17:19 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "deleting project with ID: %q" , p . ID )
2022-02-21 11:19:55 +00:00
if _ , err = h . configstoreClient . DeleteProject ( ctx , projectRef ) ; err != nil {
return util . NewAPIError ( util . KindFromRemoteError ( err ) , err )
2019-05-05 12:27:22 +00:00
}
2019-06-14 13:17:19 +00:00
// try to cleanup gitsource configs
// we'll log but ignore errors
if canDoRepCleanup {
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "cleanup git source repo" )
2019-06-14 13:17:19 +00:00
if err := h . cleanupGitSourceRepo ( ctx , rs , user , la , p ) ; err != nil {
2022-02-21 17:07:58 +00:00
h . log . Err ( err ) . Msgf ( "failed to cleanup git source repo: %+v" , err )
2019-06-14 13:17:19 +00:00
}
}
2019-05-05 12:27:22 +00:00
return nil
}
2019-06-11 09:08:40 +00:00
func ( h * ActionHandler ) ProjectCreateRun ( ctx context . Context , projectRef , branch , tag , refName , commitSHA string ) error {
2022-02-24 10:18:29 +00:00
curUserID := common . CurrentUserID ( ctx )
2019-06-11 09:08:40 +00:00
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , curUserID )
2019-06-11 09:08:40 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get user %q: %w" , curUserID , err ) )
2019-06-11 09:08:40 +00:00
}
2022-02-21 11:19:55 +00:00
p , _ , err := h . configstoreClient . GetProject ( ctx , projectRef )
2019-06-11 09:08:40 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get project %q: %w" , projectRef , err ) )
2019-06-11 09:08:40 +00:00
}
isProjectOwner , err := h . IsProjectOwner ( ctx , p . OwnerType , p . OwnerID )
if err != nil {
return errors . Errorf ( "failed to determine ownership: %w" , err )
}
if ! isProjectOwner {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrForbidden , errors . Errorf ( "user not authorized" ) )
2019-06-11 09:08:40 +00:00
}
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , p . RemoteSourceID )
2019-06-11 09:08:40 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get remote source %q: %w" , p . RemoteSourceID , err ) )
2019-06-11 09:08:40 +00:00
}
2019-07-31 13:17:54 +00:00
var la * cstypes . LinkedAccount
2019-06-11 09:08:40 +00:00
for _ , v := range user . LinkedAccounts {
if v . RemoteSourceID == rs . ID {
la = v
break
}
}
if la == nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user doesn't have a linked account for remote source %q" , rs . Name ) )
2019-06-11 09:08:40 +00:00
}
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 {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "one of branch, tag or ref is required" ) )
2019-06-11 09:08:40 +00:00
}
if set > 1 {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "only one of branch, tag or ref can be provided" ) )
2019-06-11 09:08:40 +00:00
}
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 {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "failed to get refType for ref %q: %w" , refName , err ) )
2019-06-11 09:08:40 +00:00
}
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 != "" {
2019-08-02 08:03:28 +00:00
refType = types . RunRefTypeTag
2019-06-11 09:08:40 +00:00
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 )
}
2019-06-14 13:17:19 +00:00
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) getRemoteRepoAccessData ( ctx context . Context , linkedAccountID string ) ( * cstypes . User , * cstypes . RemoteSource , * cstypes . LinkedAccount , error ) {
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUserByLinkedAccount ( ctx , linkedAccountID )
2019-06-14 13:17:19 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , nil , nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get user with linked account id %q: %w" , linkedAccountID , err ) )
2019-06-14 13:17:19 +00:00
}
la := user . LinkedAccounts [ linkedAccountID ]
if la == nil {
return nil , nil , nil , errors . Errorf ( "linked account %q in user %q doesn't exist" , linkedAccountID , user . Name )
}
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , la . RemoteSourceID )
2019-06-14 13:17:19 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , nil , nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Errorf ( "failed to get remote source %q: %w" , la . RemoteSourceID , err ) )
2019-06-14 13:17:19 +00:00
}
return user , rs , la , nil
}