diff --git a/cli/server/harness.wire.go b/cli/server/harness.wire.go index cb2d8adc7..8d7184905 100644 --- a/cli/server/harness.wire.go +++ b/cli/server/harness.wire.go @@ -12,7 +12,6 @@ import ( "github.com/harness/gitness/events" "github.com/harness/gitness/gitrpc" - gitrpcevents "github.com/harness/gitness/gitrpc/events" gitrpcserver "github.com/harness/gitness/gitrpc/server" "github.com/harness/gitness/harness/auth/authn" "github.com/harness/gitness/harness/auth/authz" @@ -30,8 +29,10 @@ import ( "github.com/harness/gitness/internal/api/controller/user" controllerwebhook "github.com/harness/gitness/internal/api/controller/webhook" "github.com/harness/gitness/internal/cron" + eventsgit "github.com/harness/gitness/internal/events/git" "github.com/harness/gitness/internal/server" "github.com/harness/gitness/internal/store/database" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/internal/webhook" gitnesstypes "github.com/harness/gitness/types" @@ -47,6 +48,7 @@ func initSystem(ctx context.Context, config *gitnesstypes.Config) (*system, erro database.WireSet, server.WireSet, cron.WireSet, + url.WireSet, space.WireSet, repo.WireSet, pullreq.WireSet, @@ -54,7 +56,7 @@ func initSystem(ctx context.Context, config *gitnesstypes.Config) (*system, erro user.WireSet, service.WireSet, serviceaccount.WireSet, - gitrpcevents.WireSet, + eventsgit.WireSet, gitrpcserver.WireSet, gitrpc.WireSet, types.LoadConfig, diff --git a/cli/server/harness.wire_gen.go b/cli/server/harness.wire_gen.go index eaf2fc974..8a7d26484 100644 --- a/cli/server/harness.wire_gen.go +++ b/cli/server/harness.wire_gen.go @@ -10,7 +10,6 @@ import ( "github.com/harness/gitness/events" "github.com/harness/gitness/gitrpc" - events2 "github.com/harness/gitness/gitrpc/events" server2 "github.com/harness/gitness/gitrpc/server" "github.com/harness/gitness/harness/auth/authn" "github.com/harness/gitness/harness/auth/authz" @@ -28,9 +27,11 @@ import ( "github.com/harness/gitness/internal/api/controller/user" webhook2 "github.com/harness/gitness/internal/api/controller/webhook" "github.com/harness/gitness/internal/cron" + events2 "github.com/harness/gitness/internal/events/git" router2 "github.com/harness/gitness/internal/router" "github.com/harness/gitness/internal/server" "github.com/harness/gitness/internal/store/database" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/internal/webhook" "github.com/harness/gitness/types" ) @@ -80,8 +81,12 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { spaceStore := database.ProvideSpaceStore(db, pathTransformation) repoStore := database.ProvideRepoStore(db, pathTransformation) serviceaccountController := serviceaccount.NewController(serviceAccount, authorizer, principalStore, spaceStore, repoStore, tokenStore) + provider, err := url.ProvideURLProvider(config) + if err != nil { + return nil, err + } checkSpace := check.ProvideSpaceCheck() - spaceController := space.ProvideController(config, checkSpace, authorizer, spaceStore, repoStore, principalStore) + spaceController := space.ProvideController(provider, checkSpace, authorizer, spaceStore, repoStore, principalStore) accountClient, err := client.ProvideAccountClient(serviceJWTProvider, typesConfig) if err != nil { return nil, err @@ -96,7 +101,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { if err != nil { return nil, err } - repoController := repo.ProvideController(config, checkRepo, authorizer, spaceStore, repoStore, principalStore, gitrpcInterface) + repoController := repo.ProvideController(config, provider, checkRepo, authorizer, spaceStore, repoStore, principalStore, gitrpcInterface) pullReqStore := database.ProvidePullReqStore(db) pullReqActivityStore := database.ProvidePullReqActivityStore(db) pullreqController := pullreq.ProvideController(db, authorizer, pullReqStore, pullReqActivityStore, repoStore, principalStore, gitrpcInterface) @@ -116,13 +121,13 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { if err != nil { return nil, err } - webhookServer, err := webhook.ProvideServer(ctx, webhookConfig, readerFactory, webhookStore, webhookExecutionStore, repoStore) + webhookServer, err := webhook.ProvideServer(ctx, webhookConfig, readerFactory, webhookStore, webhookExecutionStore, repoStore, provider, principalStore) if err != nil { return nil, err } webhookController := webhook2.ProvideController(config, db, authorizer, webhookStore, webhookExecutionStore, repoStore, webhookServer) apiHandler := router.ProvideAPIHandler(config, authenticator, accountClient, controller, spaceController, repoController, pullreqController, webhookController) - gitHandler := router.ProvideGitHandler(config, repoStore, authenticator, authorizer, gitrpcInterface) + gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface) webHandler := router2.ProvideWebHandler(config) routerRouter := router2.ProvideRouter(apiHandler, gitHandler, webHandler) serverServer := server.ProvideServer(config, routerRouter) diff --git a/cli/server/standalone.wire.go b/cli/server/standalone.wire.go index d2a83290c..66a810ee8 100644 --- a/cli/server/standalone.wire.go +++ b/cli/server/standalone.wire.go @@ -12,7 +12,6 @@ import ( "github.com/harness/gitness/events" "github.com/harness/gitness/gitrpc" - gitrpcevents "github.com/harness/gitness/gitrpc/events" gitrpcserver "github.com/harness/gitness/gitrpc/server" "github.com/harness/gitness/internal/api/controller/pullreq" "github.com/harness/gitness/internal/api/controller/repo" @@ -24,10 +23,12 @@ import ( "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/bootstrap" "github.com/harness/gitness/internal/cron" + eventsgit "github.com/harness/gitness/internal/events/git" "github.com/harness/gitness/internal/router" "github.com/harness/gitness/internal/server" "github.com/harness/gitness/internal/store" "github.com/harness/gitness/internal/store/database" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/internal/webhook" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" @@ -45,6 +46,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { router.WireSet, server.WireSet, cron.WireSet, + url.WireSet, space.WireSet, repo.WireSet, pullreq.WireSet, @@ -53,7 +55,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { user.WireSet, authn.WireSet, authz.WireSet, - gitrpcevents.WireSet, + eventsgit.WireSet, gitrpcserver.WireSet, gitrpc.WireSet, store.WireSet, diff --git a/cli/server/standalone.wire_gen.go b/cli/server/standalone.wire_gen.go index 87fdf8249..fd38f9d89 100644 --- a/cli/server/standalone.wire_gen.go +++ b/cli/server/standalone.wire_gen.go @@ -10,7 +10,6 @@ import ( "github.com/harness/gitness/events" "github.com/harness/gitness/gitrpc" - events2 "github.com/harness/gitness/gitrpc/events" server2 "github.com/harness/gitness/gitrpc/server" "github.com/harness/gitness/internal/api/controller/pullreq" "github.com/harness/gitness/internal/api/controller/repo" @@ -22,10 +21,12 @@ import ( "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/bootstrap" "github.com/harness/gitness/internal/cron" + events2 "github.com/harness/gitness/internal/events/git" "github.com/harness/gitness/internal/router" "github.com/harness/gitness/internal/server" "github.com/harness/gitness/internal/store" "github.com/harness/gitness/internal/store/database" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/internal/webhook" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" @@ -46,6 +47,10 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { controller := user.NewController(checkUser, authorizer, principalStore, tokenStore) bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller) authenticator := authn.ProvideAuthenticator(principalStore, tokenStore) + provider, err := url.ProvideURLProvider(config) + if err != nil { + return nil, err + } checkRepo := check.ProvideRepoCheck() pathTransformation := store.ProvidePathTransformation() spaceStore := database.ProvideSpaceStore(db, pathTransformation) @@ -55,9 +60,9 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { if err != nil { return nil, err } - repoController := repo.ProvideController(config, checkRepo, authorizer, spaceStore, repoStore, principalStore, gitrpcInterface) + repoController := repo.ProvideController(config, provider, checkRepo, authorizer, spaceStore, repoStore, principalStore, gitrpcInterface) checkSpace := check.ProvideSpaceCheck() - spaceController := space.ProvideController(config, checkSpace, authorizer, spaceStore, repoStore, principalStore) + spaceController := space.ProvideController(provider, checkSpace, authorizer, spaceStore, repoStore, principalStore) pullReqStore := database.ProvidePullReqStore(db) pullReqActivityStore := database.ProvidePullReqActivityStore(db) pullreqController := pullreq.ProvideController(db, authorizer, pullReqStore, pullReqActivityStore, repoStore, principalStore, gitrpcInterface) @@ -77,7 +82,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { if err != nil { return nil, err } - webhookServer, err := webhook.ProvideServer(ctx, webhookConfig, readerFactory, webhookStore, webhookExecutionStore, repoStore) + webhookServer, err := webhook.ProvideServer(ctx, webhookConfig, readerFactory, webhookStore, webhookExecutionStore, repoStore, provider, principalStore) if err != nil { return nil, err } @@ -85,7 +90,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) { serviceAccount := check.ProvideServiceAccountCheck() serviceaccountController := serviceaccount.NewController(serviceAccount, authorizer, principalStore, spaceStore, repoStore, tokenStore) apiHandler := router.ProvideAPIHandler(config, authenticator, repoController, spaceController, pullreqController, webhookController, serviceaccountController, controller) - gitHandler := router.ProvideGitHandler(config, repoStore, authenticator, authorizer, gitrpcInterface) + gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface) webHandler := router.ProvideWebHandler(config) routerRouter := router.ProvideRouter(apiHandler, gitHandler, webHandler) serverServer := server.ProvideServer(config, routerRouter) diff --git a/events/error.go b/events/error.go new file mode 100644 index 000000000..cccb1632e --- /dev/null +++ b/events/error.go @@ -0,0 +1,45 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package events + +import ( + "errors" + "fmt" +) + +var ( + errDiscardEvent = &discardEventError{} +) + +// discardEventError is an error which, if returned by the event handler, +// causes the source event to be discarded despite any erros. +type discardEventError struct { + inner error +} + +func NewDiscardEventError(inner error) error { + return &discardEventError{ + inner: inner, + } +} + +func NewDiscardEventErrorf(format string, args ...interface{}) error { + return &discardEventError{ + inner: fmt.Errorf(format, args...), + } +} + +func (e *discardEventError) Error() string { + return fmt.Sprintf("discarding requested due to: %s", e.inner) +} + +func (e *discardEventError) Unwrap() error { + return e.inner +} + +func (e *discardEventError) Is(target error) bool { + // NOTE: it's an internal event and we only ever check with the singleton instance + return errors.Is(target, errDiscardEvent) +} diff --git a/events/reader.go b/events/reader.go index 04426b1f6..8b19061ea 100644 --- a/events/reader.go +++ b/events/reader.go @@ -198,7 +198,16 @@ func ReaderRegisterEvent[T interface{}](reader *GenericReader, ctx = log.WithContext(ctx) // call provided handler with correctly typed payload - return fn(ctx, &event) + err = fn(ctx, &event) + + // handle discardEventError + if errors.Is(err, errDiscardEvent) { + log.Warn().Err(err).Msgf("discarding event '%s'", event.ID) + return nil + } + + // any other error we return as is + return err }) } diff --git a/internal/api/controller/repo/commit.go b/internal/api/controller/repo/commit.go index 0f44b8070..510ec06cc 100644 --- a/internal/api/controller/repo/commit.go +++ b/internal/api/controller/repo/commit.go @@ -6,6 +6,7 @@ package repo import ( "context" + "fmt" "github.com/harness/gitness/gitrpc" apiauth "github.com/harness/gitness/internal/api/auth" @@ -58,8 +59,13 @@ func (c *Controller) CommitFiles(ctx context.Context, session *auth.Session, } } + writeParams, err := CreateRPCWriteParams(ctx, c.urlProvider, session, repo) + if err != nil { + return CommitFilesResponse{}, fmt.Errorf("failed to create RPC write params: %w", err) + } + commit, err := c.gitRPCClient.CommitFiles(ctx, &gitrpc.CommitFilesParams{ - WriteParams: CreateRPCWriteParams(session, repo), + WriteParams: writeParams, Title: in.Title, Message: in.Message, Branch: in.Branch, diff --git a/internal/api/controller/repo/controller.go b/internal/api/controller/repo/controller.go index c26c4d7c7..47ce724de 100644 --- a/internal/api/controller/repo/controller.go +++ b/internal/api/controller/repo/controller.go @@ -5,17 +5,23 @@ package repo import ( + "context" + "github.com/harness/gitness/gitrpc" + "github.com/harness/gitness/internal/api/request" "github.com/harness/gitness/internal/auth" "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" + + "github.com/rs/zerolog/log" ) type Controller struct { defaultBranch string - gitBaseURL string + urlProvider *url.Provider repoCheck check.Repo authorizer authz.Authorizer spaceStore store.SpaceStore @@ -26,7 +32,7 @@ type Controller struct { func NewController( defaultBranch string, - gitBaseURL string, + urlProvider *url.Provider, repoCheck check.Repo, authorizer authz.Authorizer, spaceStore store.SpaceStore, @@ -36,7 +42,7 @@ func NewController( ) *Controller { return &Controller{ defaultBranch: defaultBranch, - gitBaseURL: gitBaseURL, + urlProvider: urlProvider, repoCheck: repoCheck, authorizer: authorizer, spaceStore: spaceStore, @@ -48,10 +54,25 @@ func NewController( // CreateRPCWriteParams creates base write parameters for gitrpc write operations. // IMPORTANT: session & repo are assumed to be not nil! -func CreateRPCWriteParams(session *auth.Session, repo *types.Repository) gitrpc.WriteParams { +func CreateRPCWriteParams(ctx context.Context, urlProvider *url.Provider, + session *auth.Session, repo *types.Repository) (gitrpc.WriteParams, error) { + requestID, ok := request.RequestIDFrom(ctx) + if !ok { + // best effort retrieving of requestID - log in case we can't find it but don't fail operation. + log.Ctx(ctx).Warn().Msg("operation doesn't have a requestID in the context.") + } + // generate envars (add everything githook CLI needs for execution) - // TODO: envVars := githook.GenerateGitHookEnvironmentVariables(repo, session.Principal) - envVars := map[string]string{} + envVars := map[string]string{"X-Request-Id": requestID} + // envVars, err := githook.GenerateEnvironmentVariables(&githook.Payload{ + // BaseURL: urlProvider.GetAPIBaseURL(), + // RepoID: repo.ID, + // PrincipalID: session.Principal.ID, + // RequestID: requestID, + // }) + // if err != nil { + // return gitrpc.WriteParams{}, fmt.Errorf("failed to generate git hook environment variables: %w", err) + // } return gitrpc.WriteParams{ Actor: gitrpc.Identity{ @@ -60,7 +81,7 @@ func CreateRPCWriteParams(session *auth.Session, repo *types.Repository) gitrpc. }, RepoUID: repo.GitUID, EnvVars: envVars, - } + }, nil } // CreateRPCReadParams creates base read parameters for gitrpc read operations. diff --git a/internal/api/controller/repo/create.go b/internal/api/controller/repo/create.go index dcac6f3e1..f6194c981 100644 --- a/internal/api/controller/repo/create.go +++ b/internal/api/controller/repo/create.go @@ -142,10 +142,7 @@ func (c *Controller) Create(ctx context.Context, session *auth.Session, in *Crea } // populate repo url - repo.GitURL, err = GenerateRepoGitURL(c.gitBaseURL, repo.Path) - if err != nil { - return nil, err - } + repo.GitURL = c.urlProvider.GenerateRepoCloneURL(repo.Path) return repo, nil } diff --git a/internal/api/controller/repo/create_branch.go b/internal/api/controller/repo/create_branch.go index 0a56fb379..d33d232be 100644 --- a/internal/api/controller/repo/create_branch.go +++ b/internal/api/controller/repo/create_branch.go @@ -44,11 +44,16 @@ func (c *Controller) CreateBranch(ctx context.Context, session *auth.Session, err = checkBranchName(in.Name) if err != nil { - return nil, err + return nil, fmt.Errorf("branch name failed check: %w", err) + } + + writeParams, err := CreateRPCWriteParams(ctx, c.urlProvider, session, repo) + if err != nil { + return nil, fmt.Errorf("failed to create RPC write params: %w", err) } rpcOut, err := c.gitRPCClient.CreateBranch(ctx, &gitrpc.CreateBranchParams{ - WriteParams: CreateRPCWriteParams(session, repo), + WriteParams: writeParams, BranchName: in.Name, Target: *in.Target, }) diff --git a/internal/api/controller/repo/delete_branch.go b/internal/api/controller/repo/delete_branch.go index 75677cb8b..344f5ecb5 100644 --- a/internal/api/controller/repo/delete_branch.go +++ b/internal/api/controller/repo/delete_branch.go @@ -6,6 +6,7 @@ package repo import ( "context" + "fmt" "github.com/harness/gitness/gitrpc" apiauth "github.com/harness/gitness/internal/api/auth" @@ -33,8 +34,13 @@ func (c *Controller) DeleteBranch(ctx context.Context, session *auth.Session, re return usererror.ErrDefaultBranchCantBeDeleted } + writeParams, err := CreateRPCWriteParams(ctx, c.urlProvider, session, repo) + if err != nil { + return fmt.Errorf("failed to create RPC write params: %w", err) + } + err = c.gitRPCClient.DeleteBranch(ctx, &gitrpc.DeleteBranchParams{ - WriteParams: CreateRPCWriteParams(session, repo), + WriteParams: writeParams, BranchName: branchName, }) if err != nil { diff --git a/internal/api/controller/repo/find.go b/internal/api/controller/repo/find.go index 8c8be0b34..a7f95b609 100644 --- a/internal/api/controller/repo/find.go +++ b/internal/api/controller/repo/find.go @@ -6,10 +6,6 @@ package repo import ( "context" - "fmt" - "net/url" - "path" - "strings" apiauth "github.com/harness/gitness/internal/api/auth" "github.com/harness/gitness/internal/auth" @@ -28,23 +24,8 @@ func (c *Controller) Find(ctx context.Context, session *auth.Session, repoRef st return nil, err } - repo.GitURL, err = GenerateRepoGitURL(c.gitBaseURL, repo.Path) - if err != nil { - return nil, err - } + // backfil clone url + repo.GitURL = c.urlProvider.GenerateRepoCloneURL(repo.Path) return repo, nil } - -func GenerateRepoGitURL(gitBaseURL string, repoPath string) (string, error) { - repoPath = path.Clean(repoPath) - if !strings.HasSuffix(repoPath, ".git") { - repoPath += ".git" - } - gitURL, err := url.JoinPath(gitBaseURL, repoPath) - if err != nil { - return "", fmt.Errorf("failed to join base url '%s' with path '%s': %w", gitBaseURL, repoPath, err) - } - - return gitURL, nil -} diff --git a/internal/api/controller/repo/update.go b/internal/api/controller/repo/update.go index d4343f429..2cf8749c8 100644 --- a/internal/api/controller/repo/update.go +++ b/internal/api/controller/repo/update.go @@ -61,11 +61,8 @@ func (c *Controller) Update(ctx context.Context, session *auth.Session, return nil, err } - // populate repo url - repo.GitURL, err = GenerateRepoGitURL(c.gitBaseURL, repo.Path) - if err != nil { - return nil, err - } + // backfill repo url + repo.GitURL = c.urlProvider.GenerateRepoCloneURL(repo.Path) return repo, nil } diff --git a/internal/api/controller/repo/wire.go b/internal/api/controller/repo/wire.go index 5ea2760f2..26d47d788 100644 --- a/internal/api/controller/repo/wire.go +++ b/internal/api/controller/repo/wire.go @@ -8,6 +8,7 @@ import ( "github.com/harness/gitness/gitrpc" "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" @@ -19,9 +20,10 @@ var WireSet = wire.NewSet( ProvideController, ) -func ProvideController(config *types.Config, repoCheck check.Repo, authorizer authz.Authorizer, - spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore, +func ProvideController(config *types.Config, urlProvider *url.Provider, + repoCheck check.Repo, authorizer authz.Authorizer, spaceStore store.SpaceStore, + repoStore store.RepoStore, principalStore store.PrincipalStore, rpcClient gitrpc.Interface) *Controller { - return NewController(config.Git.DefaultBranch, config.Git.BaseURL, + return NewController(config.Git.DefaultBranch, urlProvider, repoCheck, authorizer, spaceStore, repoStore, principalStore, rpcClient) } diff --git a/internal/api/controller/space/controller.go b/internal/api/controller/space/controller.go index 23f569d53..41eb10366 100644 --- a/internal/api/controller/space/controller.go +++ b/internal/api/controller/space/controller.go @@ -7,11 +7,12 @@ package space import ( "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/types/check" ) type Controller struct { - gitBaseURL string + urlProvider *url.Provider spaceCheck check.Space authorizer authz.Authorizer spaceStore store.SpaceStore @@ -19,10 +20,11 @@ type Controller struct { principalStore store.PrincipalStore } -func NewController(gitBaseURL string, spaceCheck check.Space, authorizer authz.Authorizer, spaceStore store.SpaceStore, - repoStore store.RepoStore, principalStore store.PrincipalStore) *Controller { +func NewController(urlProvider *url.Provider, spaceCheck check.Space, + authorizer authz.Authorizer, spaceStore store.SpaceStore, repoStore store.RepoStore, + principalStore store.PrincipalStore) *Controller { return &Controller{ - gitBaseURL: gitBaseURL, + urlProvider: urlProvider, spaceCheck: spaceCheck, authorizer: authorizer, spaceStore: spaceStore, diff --git a/internal/api/controller/space/list_repositories.go b/internal/api/controller/space/list_repositories.go index c17cfa2dd..ebce1ef38 100644 --- a/internal/api/controller/space/list_repositories.go +++ b/internal/api/controller/space/list_repositories.go @@ -9,7 +9,6 @@ import ( "fmt" apiauth "github.com/harness/gitness/internal/api/auth" - "github.com/harness/gitness/internal/api/controller/repo" "github.com/harness/gitness/internal/auth" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" @@ -39,11 +38,9 @@ func (c *Controller) ListRepositories(ctx context.Context, session *auth.Session return nil, 0, fmt.Errorf("failed to list child repos: %w", err) } - for _, r := range repos { - r.GitURL, err = repo.GenerateRepoGitURL(c.gitBaseURL, r.Path) - if err != nil { - return nil, 0, err - } + // backfill URLs + for _, repo := range repos { + repo.GitURL = c.urlProvider.GenerateRepoCloneURL(repo.Path) } /* diff --git a/internal/api/controller/space/wire.go b/internal/api/controller/space/wire.go index ba5abb97e..2b634f769 100644 --- a/internal/api/controller/space/wire.go +++ b/internal/api/controller/space/wire.go @@ -7,7 +7,7 @@ package space import ( "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/store" - "github.com/harness/gitness/types" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/types/check" "github.com/google/wire" @@ -18,7 +18,7 @@ var WireSet = wire.NewSet( ProvideController, ) -func ProvideController(config *types.Config, spaceCheck check.Space, authorizer authz.Authorizer, +func ProvideController(urlProvider *url.Provider, spaceCheck check.Space, authorizer authz.Authorizer, spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore) *Controller { - return NewController(config.Git.BaseURL, spaceCheck, authorizer, spaceStore, repoStore, principalStore) + return NewController(urlProvider, spaceCheck, authorizer, spaceStore, repoStore, principalStore) } diff --git a/internal/api/handler/repo/http_git.go b/internal/api/handler/repo/http_git.go index 7efbed4f6..007ffcee8 100644 --- a/internal/api/handler/repo/http_git.go +++ b/internal/api/handler/repo/http_git.go @@ -19,6 +19,7 @@ import ( "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/paths" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/types/enum" "github.com/rs/zerolog/hlog" @@ -88,11 +89,12 @@ func GetInfoRefs(client gitrpc.Interface, repoStore store.RepoStore, authorizer } } -func GetUploadPack(client gitrpc.Interface, repoStore store.RepoStore, authorizer authz.Authorizer) http.HandlerFunc { +func GetUploadPack(client gitrpc.Interface, urlProvider *url.Provider, + repoStore store.RepoStore, authorizer authz.Authorizer) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { const service = "upload-pack" - if err := serviceRPC(w, r, client, repoStore, authorizer, service, false, + if err := serviceRPC(w, r, client, urlProvider, repoStore, authorizer, service, false, enum.PermissionRepoView, true); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -100,10 +102,11 @@ func GetUploadPack(client gitrpc.Interface, repoStore store.RepoStore, authorize } } -func PostReceivePack(client gitrpc.Interface, repoStore store.RepoStore, authorizer authz.Authorizer) http.HandlerFunc { +func PostReceivePack(client gitrpc.Interface, urlProvider *url.Provider, + repoStore store.RepoStore, authorizer authz.Authorizer) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { const service = "receive-pack" - if err := serviceRPC(w, r, client, repoStore, authorizer, service, true, + if err := serviceRPC(w, r, client, urlProvider, repoStore, authorizer, service, true, enum.PermissionRepoEdit, false); err != nil { var authError *GitAuthError if errors.As(err, &authError) { @@ -120,6 +123,7 @@ func serviceRPC( w http.ResponseWriter, r *http.Request, client gitrpc.Interface, + urlProvider *url.Provider, repoStore store.RepoStore, authorizer authz.Authorizer, service string, @@ -180,7 +184,11 @@ func serviceRPC( // setup read/writeparams depending on whether it's a write operation if isWriteOperation { - writeParams := repoctrl.CreateRPCWriteParams(session, repo) + var writeParams gitrpc.WriteParams + writeParams, err = repoctrl.CreateRPCWriteParams(ctx, urlProvider, session, repo) + if err != nil { + return fmt.Errorf("failed to create RPC write params: %w", err) + } params.WriteParams = &writeParams } else { readParams := repoctrl.CreateRPCReadParams(repo) diff --git a/gitrpc/events/branch_created.go b/internal/events/git/branch_created.go similarity index 86% rename from gitrpc/events/branch_created.go rename to internal/events/git/branch_created.go index b0e939b13..7fad1d08a 100644 --- a/gitrpc/events/branch_created.go +++ b/internal/events/git/branch_created.go @@ -15,10 +15,10 @@ import ( const BranchCreatedEvent events.EventType = "branchcreated" type BranchCreatedPayload struct { - RepoUID string `json:"repo_uid"` - BranchName string `json:"branch_name"` - FullRef string `json:"full_ref"` - SHA string `json:"sha"` + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + SHA string `json:"sha"` } func (r *Reporter) BranchCreated(ctx context.Context, payload *BranchCreatedPayload) { diff --git a/gitrpc/events/branch_deleted.go b/internal/events/git/branch_deleted.go similarity index 86% rename from gitrpc/events/branch_deleted.go rename to internal/events/git/branch_deleted.go index cdacf8d46..6b37d41bf 100644 --- a/gitrpc/events/branch_deleted.go +++ b/internal/events/git/branch_deleted.go @@ -15,10 +15,10 @@ import ( const BranchDeletedEvent events.EventType = "branchdeleted" type BranchDeletedPayload struct { - RepoUID string `json:"repo_uid"` - BranchName string `json:"branch_name"` - FullRef string `json:"full_ref"` - SHA string `json:"sha"` + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + SHA string `json:"sha"` } func (r *Reporter) BranchDeleted(ctx context.Context, payload *BranchDeletedPayload) { diff --git a/gitrpc/events/branch_updated.go b/internal/events/git/branch_updated.go similarity index 78% rename from gitrpc/events/branch_updated.go rename to internal/events/git/branch_updated.go index 7f600d72a..983681ede 100644 --- a/gitrpc/events/branch_updated.go +++ b/internal/events/git/branch_updated.go @@ -15,12 +15,12 @@ import ( const BranchUpdatedEvent events.EventType = "branchupdated" type BranchUpdatedPayload struct { - RepoUID string `json:"repo_uid"` - BranchName string `json:"branch_name"` - FullRef string `json:"full_ref"` - OldSHA string `json:"old_sha"` - NewSHA string `json:"new_sha"` - Forced bool `json:"forced"` + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + OldSHA string `json:"old_sha"` + NewSHA string `json:"new_sha"` + // Forced bool `json:"forced"` TODO: data not available yet. } func (r *Reporter) BranchUpdated(ctx context.Context, payload *BranchUpdatedPayload) { diff --git a/gitrpc/events/events.go b/internal/events/git/events.go similarity index 100% rename from gitrpc/events/events.go rename to internal/events/git/events.go diff --git a/gitrpc/events/reader.go b/internal/events/git/reader.go similarity index 100% rename from gitrpc/events/reader.go rename to internal/events/git/reader.go diff --git a/gitrpc/events/reporter.go b/internal/events/git/reporter.go similarity index 100% rename from gitrpc/events/reporter.go rename to internal/events/git/reporter.go diff --git a/gitrpc/events/wire.go b/internal/events/git/wire.go similarity index 80% rename from gitrpc/events/wire.go rename to internal/events/git/wire.go index e60ac4d5c..077161b5c 100644 --- a/gitrpc/events/wire.go +++ b/internal/events/git/wire.go @@ -13,8 +13,13 @@ import ( // WireSet provides a wire set for this package. var WireSet = wire.NewSet( ProvideReaderFactory, + ProvideReporter, ) func ProvideReaderFactory(eventsSystem *events.System) (*events.ReaderFactory[*Reader], error) { return NewReaderFactory(eventsSystem) } + +func ProvideReporter(eventsSystem *events.System) (*Reporter, error) { + return NewReporter(eventsSystem) +} diff --git a/internal/router/git.go b/internal/router/git.go index 6dca8a973..42d6aec90 100644 --- a/internal/router/git.go +++ b/internal/router/git.go @@ -17,6 +17,7 @@ import ( "github.com/harness/gitness/internal/auth/authn" "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/types" "github.com/go-chi/chi" @@ -32,6 +33,7 @@ type GitHandler interface { // NewGitHandler returns a new GitHandler. func NewGitHandler( config *types.Config, + urlProvider *url.Provider, repoStore store.RepoStore, authenticator authn.Authenticator, authorizer authz.Authorizer, @@ -54,9 +56,8 @@ func NewGitHandler( r.Use(middlewareauthn.Attempt(authenticator)) // smart protocol - r.Handle("/git-upload-pack", handlerrepo.GetUploadPack(client, repoStore, authorizer)) - - r.Post("/git-receive-pack", handlerrepo.PostReceivePack(client, repoStore, authorizer)) + r.Handle("/git-upload-pack", handlerrepo.GetUploadPack(client, urlProvider, repoStore, authorizer)) + r.Post("/git-receive-pack", handlerrepo.PostReceivePack(client, urlProvider, repoStore, authorizer)) r.Get("/info/refs", handlerrepo.GetInfoRefs(client, repoStore, authorizer)) // dumb protocol diff --git a/internal/router/wire.go b/internal/router/wire.go index f1eb9f5c8..b83bdbcfa 100644 --- a/internal/router/wire.go +++ b/internal/router/wire.go @@ -15,6 +15,7 @@ import ( "github.com/harness/gitness/internal/auth/authn" "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" "github.com/harness/gitness/types" "github.com/google/wire" @@ -38,12 +39,13 @@ func ProvideRouter( func ProvideGitHandler( config *types.Config, + urlProvider *url.Provider, repoStore store.RepoStore, authenticator authn.Authenticator, authorizer authz.Authorizer, client gitrpc.Interface, ) GitHandler { - return NewGitHandler(config, repoStore, authenticator, authorizer, client) + return NewGitHandler(config, urlProvider, repoStore, authenticator, authorizer, client) } func ProvideAPIHandler( @@ -55,7 +57,8 @@ func ProvideAPIHandler( webhookCtrl *webhook.Controller, saCtrl *serviceaccount.Controller, userCtrl *user.Controller) APIHandler { - return NewAPIHandler(config, authenticator, repoCtrl, spaceCtrl, pullreqCtrl, webhookCtrl, saCtrl, userCtrl) + return NewAPIHandler(config, authenticator, repoCtrl, spaceCtrl, pullreqCtrl, + webhookCtrl, saCtrl, userCtrl) } func ProvideWebHandler(config *types.Config) WebHandler { diff --git a/internal/url/provider.go b/internal/url/provider.go new file mode 100644 index 000000000..1055836b7 --- /dev/null +++ b/internal/url/provider.go @@ -0,0 +1,49 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package url + +import ( + "fmt" + "net/url" + "path" + "strings" +) + +// Provider provides the URLs of the gitness system. +type Provider struct { + // apiURLRaw stores the raw URL the api endpoints are available at. + apiURLRaw string + + // gitURL stores the URL the git endpoints are available at. + // NOTE: we store it as url.URL so we can derive clone URLS without errors. + gitURL *url.URL +} + +func NewProvider(apiURLRaw string, rawGitURL string) (*Provider, error) { + gitURL, err := url.Parse(rawGitURL) + if err != nil { + return nil, fmt.Errorf("provided rawGitURL '%s' is invalid: %w", rawGitURL, err) + } + + return &Provider{ + apiURLRaw: apiURLRaw, + gitURL: gitURL, + }, nil +} + +// GetAPIBaseURL returns the base url of the api server. +func (p *Provider) GetAPIBaseURL() string { + return p.apiURLRaw +} + +// GenerateRepoCloneURL generates the git clone URL for the provided repo path. +func (p *Provider) GenerateRepoCloneURL(repoPath string) string { + repoPath = path.Clean(repoPath) + if !strings.HasSuffix(repoPath, ".git") { + repoPath += ".git" + } + + return p.gitURL.JoinPath(repoPath).String() +} diff --git a/internal/url/wire.go b/internal/url/wire.go new file mode 100644 index 000000000..81d5d739d --- /dev/null +++ b/internal/url/wire.go @@ -0,0 +1,21 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package url + +import ( + "github.com/harness/gitness/types" + + "github.com/google/wire" +) + +// WireSet provides a wire set for this package. +var WireSet = wire.NewSet(ProvideURLProvider) + +func ProvideURLProvider(config *types.Config) (*Provider, error) { + return NewProvider( + config.URL.API, + config.URL.Git, + ) +} diff --git a/internal/webhook/branch.go b/internal/webhook/branch.go index a201b7e8d..bcf6cafff 100644 --- a/internal/webhook/branch.go +++ b/internal/webhook/branch.go @@ -8,82 +8,75 @@ import ( "context" "github.com/harness/gitness/events" - gitevents "github.com/harness/gitness/gitrpc/events" - "github.com/harness/gitness/internal/store" + gitevents "github.com/harness/gitness/internal/events/git" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" ) // BranchBody describes the body of Branch related webhook triggers. +// NOTE: Use a single payload format to make it easier for consumers! // TODO: move in separate package for small import? type BranchBody struct { - Repo RepoMetadata `json:"repo"` - Ref string `json:"ref"` - Before string `json:"before"` - After string `json:"after"` - Forced bool `json:"forced"` + Trigger enum.WebhookTrigger `json:"trigger"` + Repo RepositoryInfo `json:"repo"` + Principal PrincipalInfo `json:"principal"` + Ref string `json:"ref"` + Before string `json:"before"` + After string `json:"after"` + // Forced bool `json:"forced"` TODO: data has to be calculated explicitly } -func getEventHandlerForBranchCreated(server *Server, - repoStore store.RepoStore) func(context.Context, *events.Event[*gitevents.BranchCreatedPayload]) error { - return func(ctx context.Context, event *events.Event[*gitevents.BranchCreatedPayload]) error { - return triggerForEventWithGitUID(ctx, server, repoStore, event.ID, event.Payload.RepoUID, - enum.WebhookTriggerBranchPushed, func(repo *types.Repository) interface{} { - return &BranchBody{ - Repo: RepoMetadata{ - ID: repo.ID, - Path: repo.Path, - UID: repo.UID, - DefaultBranch: repo.DefaultBranch, - GitURL: "", // TODO: GitURL has to be generated - }, - Ref: event.Payload.FullRef, - Before: types.NilSHA, - After: event.Payload.SHA, - } - }) - } +// handleEventBranchCreated handles branch created events +// and triggers branch created webhooks for the source repo. +func (s *Server) handleEventBranchCreated(ctx context.Context, + event *events.Event[*gitevents.BranchCreatedPayload]) error { + return s.triggerWebhooksForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerBranchCreated, + event.ID, event.Payload.RepoID, event.Payload.PrincipalID, + func(repo *types.Repository, principal *types.Principal) interface{} { + return &BranchBody{ + Trigger: enum.WebhookTriggerBranchCreated, + Repo: repositoryInfoFrom(repo, s.urlProvider), + Principal: principalInfoFrom(principal), + Ref: event.Payload.Ref, + Before: types.NilSHA, + After: event.Payload.SHA, + } + }) } -func getEventHandlerForBranchUpdated(server *Server, - repoStore store.RepoStore) func(context.Context, *events.Event[*gitevents.BranchUpdatedPayload]) error { - return func(ctx context.Context, event *events.Event[*gitevents.BranchUpdatedPayload]) error { - return triggerForEventWithGitUID(ctx, server, repoStore, event.ID, event.Payload.RepoUID, - enum.WebhookTriggerBranchPushed, func(repo *types.Repository) interface{} { - return &BranchBody{ - Repo: RepoMetadata{ - ID: repo.ID, - Path: repo.Path, - UID: repo.UID, - DefaultBranch: repo.DefaultBranch, - GitURL: "", // TODO: GitURL has to be generated - }, - Ref: event.Payload.FullRef, - Before: event.Payload.OldSHA, - After: event.Payload.NewSHA, - Forced: event.Payload.Forced, - } - }) - } +// handleEventBranchUpdated handles branch updated events +// and triggers branch updated webhooks for the source repo. +func (s *Server) handleEventBranchUpdated(ctx context.Context, + event *events.Event[*gitevents.BranchUpdatedPayload]) error { + return s.triggerWebhooksForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerBranchUpdated, + event.ID, event.Payload.RepoID, event.Payload.PrincipalID, + func(repo *types.Repository, principal *types.Principal) interface{} { + return &BranchBody{ + Trigger: enum.WebhookTriggerBranchUpdated, + Repo: repositoryInfoFrom(repo, s.urlProvider), + Principal: principalInfoFrom(principal), + Ref: event.Payload.Ref, + Before: event.Payload.OldSHA, + After: event.Payload.NewSHA, + // Forced: true/false, // TODO: data not available yet + } + }) } -func getEventHandlerForBranchDeleted(server *Server, - repoStore store.RepoStore) func(context.Context, *events.Event[*gitevents.BranchDeletedPayload]) error { - return func(ctx context.Context, event *events.Event[*gitevents.BranchDeletedPayload]) error { - return triggerForEventWithGitUID(ctx, server, repoStore, event.ID, event.Payload.RepoUID, - enum.WebhookTriggerBranchDeleted, func(repo *types.Repository) interface{} { - return &BranchBody{ - Repo: RepoMetadata{ - ID: repo.ID, - Path: repo.Path, - UID: repo.UID, - DefaultBranch: repo.DefaultBranch, - GitURL: "", // TODO: GitURL has to be generated - }, - Ref: event.Payload.FullRef, - Before: event.Payload.SHA, - After: types.NilSHA, - } - }) - } +// handleEventBranchDeleted handles branch deleted events +// and triggers branch deleted webhooks for the source repo. +func (s *Server) handleEventBranchDeleted(ctx context.Context, + event *events.Event[*gitevents.BranchDeletedPayload]) error { + return s.triggerWebhooksForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerBranchDeleted, + event.ID, event.Payload.RepoID, event.Payload.PrincipalID, + func(repo *types.Repository, principal *types.Principal) interface{} { + return &BranchBody{ + Trigger: enum.WebhookTriggerBranchDeleted, + Repo: repositoryInfoFrom(repo, s.urlProvider), + Principal: principalInfoFrom(principal), + Ref: event.Payload.Ref, + Before: event.Payload.SHA, + After: types.NilSHA, + } + }) } diff --git a/internal/webhook/common.go b/internal/webhook/common.go new file mode 100644 index 000000000..01225a506 --- /dev/null +++ b/internal/webhook/common.go @@ -0,0 +1,50 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package webhook + +import ( + "github.com/harness/gitness/internal/url" + "github.com/harness/gitness/types" +) + +// RepositoryInfo describes the repo related info for a webhook payload. +// NOTE: don't use types package as we want webhook payload to be independent from API calls. +type RepositoryInfo struct { + ID int64 `json:"id"` + Path string `json:"path"` + UID string `json:"uid"` + DefaultBranch string `json:"default_branch"` + GitURL string `json:"git_url"` +} + +// repositoryInfoFrom gets the RespositoryInfo from a types.Repository. +func repositoryInfoFrom(repo *types.Repository, urlProvider *url.Provider) RepositoryInfo { + return RepositoryInfo{ + ID: repo.ID, + Path: repo.Path, + UID: repo.UID, + DefaultBranch: repo.DefaultBranch, + GitURL: urlProvider.GenerateRepoCloneURL(repo.Path), + } +} + +// PrincipalInfo describes the principal related info for a webhook payload. +// NOTE: don't use types package as we want webhook payload to be independent from API calls. +type PrincipalInfo struct { + ID int64 `json:"id"` + UID string `json:"uid"` + DisplayName string `json:"display_name"` + Email string `json:"email"` +} + +// principalInfoFrom gets the PrincipalInfo from a types.Principal. +func principalInfoFrom(principal *types.Principal) PrincipalInfo { + return PrincipalInfo{ + ID: principal.ID, + UID: principal.UID, + DisplayName: principal.DisplayName, + Email: principal.Email, + } +} diff --git a/internal/webhook/events.go b/internal/webhook/events.go index 1abafefa9..9f1e81388 100644 --- a/internal/webhook/events.go +++ b/internal/webhook/events.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" + "github.com/harness/gitness/events" "github.com/harness/gitness/internal/store" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" @@ -21,32 +22,69 @@ func generateTriggerIDFromEventID(eventID string) string { return fmt.Sprintf("event-%s", eventID) } -func triggerForEventWithGitUID(ctx context.Context, server *Server, repoStore store.RepoStore, eventID string, - repoGitUID string, triggerType enum.WebhookTrigger, createBody func(*types.Repository) interface{}) error { - // TODO: can we avoid this DB call? would need the gitrpc to know the repo id though ... - repo, err := repoStore.FindByGitUID(ctx, repoGitUID) - - // not found error is unrecoverable - most likely a racing condition of repo being deleted by now - if err != nil && errors.Is(err, store.ErrResourceNotFound) { - log.Ctx(ctx).Warn().Err(err). - Msgf("discard event since repo with gitUID '%s' doesn't exist anymore", repoGitUID) - return nil - } - - // all other errors we return and force the event to be reprocessed +// triggerWebhooksForEventWithRepoAndPrincipal triggers all webhooks for the given repo and triggerType +// using the eventID to generate a deterministic triggerID and using the output of bodyFn as payload. +// The method tries to find the repository and principal and provides both to the bodyFn to generate the body. +func (s *Server) triggerWebhooksForEventWithRepoAndPrincipal(ctx context.Context, + triggerType enum.WebhookTrigger, eventID string, repoID int64, principalID int64, + createBodyFn func(*types.Repository, *types.Principal) interface{}) error { + // NOTE: technically we could avoid this call if we send the data via the event (though then events will get big) + repo, err := s.findRepositoryForEvent(ctx, repoID) if err != nil { - return fmt.Errorf("failed to get repo for gitUID '%s': %w", repoGitUID, err) + return err } - body := createBody(repo) - return triggerForEvent(ctx, server, eventID, enum.WebhookParentRepo, repo.ID, triggerType, body) + // NOTE: technically we could avoid this call if we send the data via the event (though then events will get big) + principal, err := s.findPrincipalForEvent(ctx, principalID) + if err != nil { + return err + } + + // create body + body := createBodyFn(repo, principal) + + return s.triggerWebhooksForEvent(ctx, eventID, enum.WebhookParentRepo, repo.ID, triggerType, body) } -func triggerForEvent(ctx context.Context, server *Server, eventID string, +// findRepositoryForEvent finds the repository for the provided repoID. +func (s *Server) findRepositoryForEvent(ctx context.Context, repoID int64) (*types.Repository, error) { + repo, err := s.repoStore.Find(ctx, repoID) + + if err != nil && errors.Is(err, store.ErrResourceNotFound) { + // not found error is unrecoverable - most likely a racing condition of repo being deleted by now + return nil, events.NewDiscardEventErrorf("repo with id '%d' doesn't exist anymore", repoID) + } + if err != nil { + // all other errors we return and force the event to be reprocessed + return nil, fmt.Errorf("failed to get repo for id '%d': %w", repoID, err) + } + + return repo, nil +} + +// findPrincipalForEvent finds the principal for the provided principalID. +func (s *Server) findPrincipalForEvent(ctx context.Context, principalID int64) (*types.Principal, error) { + principal, err := s.principalStore.Find(ctx, principalID) + + if err != nil && errors.Is(err, store.ErrResourceNotFound) { + // this should never happen (as we won't delete principals) - discard event + return nil, events.NewDiscardEventErrorf("principal with id '%d' doesn't exist anymore", principalID) + } + if err != nil { + // all other errors we return and force the event to be reprocessed + return nil, fmt.Errorf("failed to get principal for id '%d': %w", principalID, err) + } + + return principal, nil +} + +// triggerWebhooksForEvent triggers all webhooks for the given parentType/ID and triggerType +// using the eventID to generate a deterministic triggerID and sending the provided body as payload. +func (s *Server) triggerWebhooksForEvent(ctx context.Context, eventID string, parentType enum.WebhookParent, parentID int64, triggerType enum.WebhookTrigger, body interface{}) error { triggerID := generateTriggerIDFromEventID(eventID) - results, err := server.triggerWebhooksFor(ctx, parentType, parentID, triggerID, triggerType, body) + results, err := s.triggerWebhooksFor(ctx, parentType, parentID, triggerID, triggerType, body) // return all errors and force the event to be reprocessed (it's not webhook execution specific!) if err != nil { diff --git a/internal/webhook/repo.go b/internal/webhook/repo.go deleted file mode 100644 index dcefa3914..000000000 --- a/internal/webhook/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2022 Harness Inc. All rights reserved. -// Use of this source code is governed by the Polyform Free Trial License -// that can be found in the LICENSE.md file for this repository. - -package webhook - -// RepoMetadata describes the repo related metadata of webhook payload. -// TODO: move in separate package for small import? -type RepoMetadata struct { - ID int64 `json:"id"` - Path string `json:"path"` - UID string `json:"uid"` - DefaultBranch string `json:"default_branch"` - GitURL string `json:"git_url,omitempty"` // TODO: remove once handled properly. -} diff --git a/internal/webhook/server.go b/internal/webhook/server.go index 84ec6bb19..fd0859e9e 100644 --- a/internal/webhook/server.go +++ b/internal/webhook/server.go @@ -11,8 +11,9 @@ import ( "time" "github.com/harness/gitness/events" - gitevents "github.com/harness/gitness/gitrpc/events" + gitevents "github.com/harness/gitness/internal/events/git" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" ) const ( @@ -32,6 +33,9 @@ type Config struct { type Server struct { webhookStore store.WebhookStore webhookExecutionStore store.WebhookExecutionStore + urlProvider *url.Provider + repoStore store.RepoStore + principalStore store.PrincipalStore readerCanceler *events.ReaderCanceler secureHTTPClient *http.Client @@ -41,16 +45,20 @@ type Server struct { func NewServer(ctx context.Context, config Config, gitReaderFactory *events.ReaderFactory[*gitevents.Reader], webhookStore store.WebhookStore, webhookExecutionStore store.WebhookExecutionStore, - repoStore store.RepoStore) (*Server, error) { + repoStore store.RepoStore, urlProvider *url.Provider, + principalStore store.PrincipalStore) (*Server, error) { server := &Server{ + webhookStore: webhookStore, + webhookExecutionStore: webhookExecutionStore, + repoStore: repoStore, + urlProvider: urlProvider, + principalStore: principalStore, + // set after launching factory readerCanceler: nil, secureHTTPClient: newHTTPClient(config.AllowLoopback, config.AllowPrivateNetwork, false), insecureHTTPClient: newHTTPClient(config.AllowLoopback, config.AllowPrivateNetwork, true), - - webhookStore: webhookStore, - webhookExecutionStore: webhookExecutionStore, } canceler, err := gitReaderFactory.Launch(ctx, eventsReaderGroupName, config.EventReaderName, func(r *gitevents.Reader) error { @@ -60,9 +68,9 @@ func NewServer(ctx context.Context, config Config, _ = r.SetProcessingTimeout(processingTimeout) // register events - _ = r.RegisterBranchCreated(getEventHandlerForBranchCreated(server, repoStore)) - _ = r.RegisterBranchDeleted(getEventHandlerForBranchDeleted(server, repoStore)) - _ = r.RegisterBranchUpdated(getEventHandlerForBranchUpdated(server, repoStore)) + _ = r.RegisterBranchCreated(server.handleEventBranchCreated) + _ = r.RegisterBranchUpdated(server.handleEventBranchUpdated) + _ = r.RegisterBranchDeleted(server.handleEventBranchDeleted) return nil }) diff --git a/internal/webhook/wire.go b/internal/webhook/wire.go index 560bd2c93..32336e37b 100644 --- a/internal/webhook/wire.go +++ b/internal/webhook/wire.go @@ -8,8 +8,9 @@ import ( "context" "github.com/harness/gitness/events" - gitevents "github.com/harness/gitness/gitrpc/events" + gitevents "github.com/harness/gitness/internal/events/git" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/internal/url" "github.com/google/wire" ) @@ -22,6 +23,8 @@ var WireSet = wire.NewSet( func ProvideServer(ctx context.Context, config Config, gitReaderFactory *events.ReaderFactory[*gitevents.Reader], webhookStore store.WebhookStore, webhookExecutionStore store.WebhookExecutionStore, - repoStore store.RepoStore) (*Server, error) { - return NewServer(ctx, config, gitReaderFactory, webhookStore, webhookExecutionStore, repoStore) + repoStore store.RepoStore, urlProvider *url.Provider, + principalStore store.PrincipalStore) (*Server, error) { + return NewServer(ctx, config, gitReaderFactory, webhookStore, webhookExecutionStore, + repoStore, urlProvider, principalStore) } diff --git a/types/config.go b/types/config.go index 81bec6f3e..c5b3a305a 100644 --- a/types/config.go +++ b/types/config.go @@ -16,9 +16,14 @@ type Config struct { Debug bool `envconfig:"GITNESS_DEBUG"` Trace bool `envconfig:"GITNESS_TRACE"` + // URL defines the URLs via which the different parts of the service are reachable by. + URL struct { + Git string `envconfig:"GITNESS_URL_GIT" default:"http://localhost:3000"` + API string `envconfig:"GITNESS_URL_API" default:"http://localhost:3000"` + } + // Git defines the git configuration parameters Git struct { - BaseURL string `envconfig:"GITNESS_GIT_BASE_URL" default:"http://localhost:3000"` // clone url Root string `envconfig:"GITNESS_GIT_ROOT"` TmpDir string `envconfig:"GITNESS_GIT_TMP_DIR"` // directory for temporary data (repo clone) ServerHookPath string `envconfig:"GITNESS_GIT_SERVER_HOOK_PATH"` // path to binary used as git server hook diff --git a/types/enum/webhook.go b/types/enum/webhook.go index 4eed771b8..7d474c46e 100644 --- a/types/enum/webhook.go +++ b/types/enum/webhook.go @@ -51,15 +51,18 @@ func GetAllWebhookExecutionResults() []WebhookExecutionResult { type WebhookTrigger string const ( - // WebhookTriggerBranchPushed gets triggered when a branch gets pushed (created or updated). - WebhookTriggerBranchPushed WebhookTrigger = "branch_pushed" + // WebhookTriggerBranchCreated gets triggered when a branch gets created. + WebhookTriggerBranchCreated WebhookTrigger = "branch_created" + // WebhookTriggerBranchUpdated gets triggered when a branch gets updated. + WebhookTriggerBranchUpdated WebhookTrigger = "branch_updated" // WebhookTriggerBranchDeleted gets triggered when a branch gets deleted. WebhookTriggerBranchDeleted WebhookTrigger = "branch_deleted" ) func GetAllWebhookTriggers() []WebhookTrigger { return []WebhookTrigger{ - WebhookTriggerBranchPushed, + WebhookTriggerBranchCreated, + WebhookTriggerBranchUpdated, WebhookTriggerBranchDeleted, } }