mirror of
https://github.com/harness/drone.git
synced 2025-05-12 23:20:10 +08:00
[GIT] Add Support for Other Git Clients (#171)
This commit is contained in:
parent
afd86bacb0
commit
a426cdd69b
@ -137,7 +137,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
|||||||
apiHandler := router.ProvideAPIHandler(config, authenticator, accountClient, controller, spaceController, repoController, pullreqController, webhookController, githookController)
|
apiHandler := router.ProvideAPIHandler(config, authenticator, accountClient, controller, spaceController, repoController, pullreqController, webhookController, githookController)
|
||||||
gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface)
|
gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface)
|
||||||
webHandler := router2.ProvideWebHandler(config)
|
webHandler := router2.ProvideWebHandler(config)
|
||||||
routerRouter := router2.ProvideRouter(apiHandler, gitHandler, webHandler)
|
routerRouter := router2.ProvideRouter(config, apiHandler, gitHandler, webHandler)
|
||||||
serverServer := server.ProvideServer(config, routerRouter)
|
serverServer := server.ProvideServer(config, routerRouter)
|
||||||
serverConfig := ProvideGitRPCServerConfig(config)
|
serverConfig := ProvideGitRPCServerConfig(config)
|
||||||
server3, err := server2.ProvideServer(serverConfig)
|
server3, err := server2.ProvideServer(serverConfig)
|
||||||
|
@ -100,7 +100,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
|||||||
apiHandler := router.ProvideAPIHandler(config, authenticator, repoController, spaceController, pullreqController, webhookController, githookController, serviceaccountController, controller)
|
apiHandler := router.ProvideAPIHandler(config, authenticator, repoController, spaceController, pullreqController, webhookController, githookController, serviceaccountController, controller)
|
||||||
gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface)
|
gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface)
|
||||||
webHandler := router.ProvideWebHandler(config)
|
webHandler := router.ProvideWebHandler(config)
|
||||||
routerRouter := router.ProvideRouter(apiHandler, gitHandler, webHandler)
|
routerRouter := router.ProvideRouter(config, apiHandler, gitHandler, webHandler)
|
||||||
serverServer := server.ProvideServer(config, routerRouter)
|
serverServer := server.ProvideServer(config, routerRouter)
|
||||||
serverConfig := ProvideGitRPCServerConfig(config)
|
serverConfig := ProvideGitRPCServerConfig(config)
|
||||||
server3, err := server2.ProvideServer(serverConfig)
|
server3, err := server2.ProvideServer(serverConfig)
|
||||||
|
@ -66,7 +66,7 @@ func CreateRPCWriteParams(ctx context.Context, urlProvider *url.Provider,
|
|||||||
|
|
||||||
// generate envars (add everything githook CLI needs for execution)
|
// generate envars (add everything githook CLI needs for execution)
|
||||||
envVars, err := githook.GenerateEnvironmentVariables(&githook.Payload{
|
envVars, err := githook.GenerateEnvironmentVariables(&githook.Payload{
|
||||||
BaseURL: urlProvider.GetAPIBaseURLInternal(),
|
APIBaseURL: urlProvider.GetAPIBaseURLInternal(),
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
PrincipalID: session.Principal.ID,
|
PrincipalID: session.Principal.ID,
|
||||||
RequestID: requestID,
|
RequestID: requestID,
|
||||||
|
@ -36,7 +36,7 @@ func NewCLI() (*CLI, error) {
|
|||||||
payload: payload,
|
payload: payload,
|
||||||
client: &client{
|
client: &client{
|
||||||
httpClient: http.DefaultClient,
|
httpClient: http.DefaultClient,
|
||||||
baseURL: payload.BaseURL,
|
baseURL: payload.APIBaseURL,
|
||||||
requestPreparation: func(r *http.Request) *http.Request {
|
requestPreparation: func(r *http.Request) *http.Request {
|
||||||
// TODO: reference single constant (together with gitness middleware)
|
// TODO: reference single constant (together with gitness middleware)
|
||||||
r.Header.Add("X-Request-Id", payload.RequestID)
|
r.Header.Add("X-Request-Id", payload.RequestID)
|
||||||
|
@ -53,7 +53,7 @@ func (c *client) PostReceive(ctx context.Context,
|
|||||||
|
|
||||||
// githook executes the requested githook type using the provided input.
|
// githook executes the requested githook type using the provided input.
|
||||||
func (c *client) githook(ctx context.Context, githookType string, in interface{}) (*githook.ServerHookOutput, error) {
|
func (c *client) githook(ctx context.Context, githookType string, in interface{}) (*githook.ServerHookOutput, error) {
|
||||||
uri := fmt.Sprintf("%s/api/v1/internal/git-hooks/%s", c.baseURL, githookType)
|
uri := fmt.Sprintf("%s/v1/internal/git-hooks/%s", c.baseURL, githookType)
|
||||||
bodyBytes, err := json.Marshal(in)
|
bodyBytes, err := json.Marshal(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to serialize input: %w", err)
|
return nil, fmt.Errorf("failed to serialize input: %w", err)
|
||||||
|
@ -21,7 +21,7 @@ const (
|
|||||||
|
|
||||||
// Payload defines the Payload the githook binary is initiated with when executing the git hooks.
|
// Payload defines the Payload the githook binary is initiated with when executing the git hooks.
|
||||||
type Payload struct {
|
type Payload struct {
|
||||||
BaseURL string
|
APIBaseURL string
|
||||||
RepoID int64
|
RepoID int64
|
||||||
PrincipalID int64
|
PrincipalID int64
|
||||||
RequestID string
|
RequestID string
|
||||||
@ -83,7 +83,7 @@ func validatePayload(payload *Payload) error {
|
|||||||
if payload == nil {
|
if payload == nil {
|
||||||
return errors.New("payload is empty")
|
return errors.New("payload is empty")
|
||||||
}
|
}
|
||||||
if payload.BaseURL == "" {
|
if payload.APIBaseURL == "" {
|
||||||
return errors.New("payload doesn't contain a base url")
|
return errors.New("payload doesn't contain a base url")
|
||||||
}
|
}
|
||||||
if payload.PrincipalID <= 0 {
|
if payload.PrincipalID <= 0 {
|
||||||
|
@ -20,13 +20,17 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
APIMount = "/api"
|
APIMount = "/api"
|
||||||
gitUserAgentPrefix = "git/"
|
GitMount = "/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
api APIHandler
|
api APIHandler
|
||||||
git GitHandler
|
git GitHandler
|
||||||
web WebHandler
|
web WebHandler
|
||||||
|
|
||||||
|
// gitHost describes the optional host via which git traffic is identified.
|
||||||
|
// Note: always stored as lowercase.
|
||||||
|
gitHost string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRouter returns a new http.Handler that routes traffic
|
// NewRouter returns a new http.Handler that routes traffic
|
||||||
@ -35,11 +39,14 @@ func NewRouter(
|
|||||||
api APIHandler,
|
api APIHandler,
|
||||||
git GitHandler,
|
git GitHandler,
|
||||||
web WebHandler,
|
web WebHandler,
|
||||||
|
gitHost string,
|
||||||
) *Router {
|
) *Router {
|
||||||
return &Router{
|
return &Router{
|
||||||
api: api,
|
api: api,
|
||||||
git: git,
|
git: git,
|
||||||
web: web,
|
web: web,
|
||||||
|
|
||||||
|
gitHost: strings.ToLower(gitHost),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,15 +64,19 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
* 1. GIT
|
* 1. GIT
|
||||||
*
|
*
|
||||||
* All Git originating traffic starts with "/space1/space2/repo.git".
|
* All Git originating traffic starts with "/space1/space2/repo.git".
|
||||||
* This can collide with other API endpoints and thus has to be checked first.
|
|
||||||
* To avoid any false positives, we use the user-agent header to identify git agents.
|
|
||||||
*/
|
*/
|
||||||
ua := req.Header.Get("user-agent")
|
if r.isGitTraffic(req) {
|
||||||
if strings.HasPrefix(ua, gitUserAgentPrefix) {
|
|
||||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||||
return c.Str("http.handler", "git")
|
return c.Str("http.handler", "git")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// remove matched prefix to simplify API handlers (only if it's there)
|
||||||
|
if err = stripPrefix(GitMount, req); err != nil {
|
||||||
|
hlog.FromRequest(req).Err(err).Msgf("Failed striping of prefix for git request.")
|
||||||
|
render.InternalError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
r.git.ServeHTTP(w, req)
|
r.git.ServeHTTP(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -75,8 +86,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
*
|
*
|
||||||
* All Rest API calls start with "/api/", and thus can be uniquely identified.
|
* All Rest API calls start with "/api/", and thus can be uniquely identified.
|
||||||
*/
|
*/
|
||||||
p := req.URL.Path
|
if r.isAPITraffic(req) {
|
||||||
if strings.HasPrefix(p, APIMount) {
|
|
||||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||||
return c.Str("http.handler", "api")
|
return c.Str("http.handler", "api")
|
||||||
})
|
})
|
||||||
@ -104,7 +114,40 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
r.web.ServeHTTP(w, req)
|
r.web.ServeHTTP(w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stripPrefix removes the prefix from the request path (expected to be there).
|
// stripPrefix removes the prefix from the request path (or noop if it's not there).
|
||||||
func stripPrefix(prefix string, r *http.Request) error {
|
func stripPrefix(prefix string, req *http.Request) error {
|
||||||
return request.ReplacePrefix(r, r.URL.Path[:len(prefix)], "")
|
p := req.URL.Path
|
||||||
|
if !strings.HasPrefix(p, prefix) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return request.ReplacePrefix(req, req.URL.Path[:len(prefix)], "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isGitTraffic returns true iff the request is identified as part of the git http protocol.
|
||||||
|
func (r *Router) isGitTraffic(req *http.Request) bool {
|
||||||
|
// git traffic is always reachable via the git mounting path.
|
||||||
|
p := req.URL.Path
|
||||||
|
if strings.HasPrefix(p, GitMount) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise check if the request came in via the configured git host (if enabled)
|
||||||
|
if len(r.gitHost) > 0 {
|
||||||
|
// cut (optional) port off the host
|
||||||
|
h, _, _ := strings.Cut(req.Host, ":")
|
||||||
|
|
||||||
|
// check if request host matches the configured git host (case insensitive)
|
||||||
|
if r.gitHost == strings.ToLower(h) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise we don't treat it as git traffic
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isAPITraffic returns true iff the request is identified as part of our rest API.
|
||||||
|
func (r *Router) isAPITraffic(req *http.Request) bool {
|
||||||
|
p := req.URL.Path
|
||||||
|
return strings.HasPrefix(p, APIMount)
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,13 @@ var WireSet = wire.NewSet(
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ProvideRouter(
|
func ProvideRouter(
|
||||||
|
config *types.Config,
|
||||||
api APIHandler,
|
api APIHandler,
|
||||||
git GitHandler,
|
git GitHandler,
|
||||||
web WebHandler,
|
web WebHandler,
|
||||||
) *Router {
|
) *Router {
|
||||||
return NewRouter(api, git, web)
|
return NewRouter(api, git, web,
|
||||||
|
config.Server.HTTP.GitHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideGitHandler(
|
func ProvideGitHandler(
|
||||||
|
@ -14,9 +14,12 @@ import (
|
|||||||
// Provider provides the URLs of the gitness system.
|
// Provider provides the URLs of the gitness system.
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
// apiURLRaw stores the raw URL the api endpoints are reachable at publicly.
|
// apiURLRaw stores the raw URL the api endpoints are reachable at publicly.
|
||||||
|
// NOTE: url is guaranteed to not have any trailing '/'.
|
||||||
apiURLRaw string
|
apiURLRaw string
|
||||||
// apiURLInternalRaw stores the raw URL the api endpoints are reachable at internally.
|
|
||||||
// NOTE: no need for internal services to go via public route.
|
// apiURLInternalRaw stores the raw URL the api endpoints are reachable at internally
|
||||||
|
// (no need for internal services to go via public route).
|
||||||
|
// NOTE: url is guaranteed to not have any trailing '/'.
|
||||||
apiURLInternalRaw string
|
apiURLInternalRaw string
|
||||||
|
|
||||||
// gitURL stores the URL the git endpoints are available at.
|
// gitURL stores the URL the git endpoints are available at.
|
||||||
@ -25,6 +28,12 @@ type Provider struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewProvider(apiURLRaw string, apiURLInternalRaw, gitURLRaw string) (*Provider, error) {
|
func NewProvider(apiURLRaw string, apiURLInternalRaw, gitURLRaw string) (*Provider, error) {
|
||||||
|
// remove trailing '/' to make usage easier
|
||||||
|
apiURLRaw = strings.TrimRight(apiURLRaw, "/")
|
||||||
|
apiURLInternalRaw = strings.TrimRight(apiURLInternalRaw, "/")
|
||||||
|
gitURLRaw = strings.TrimRight(gitURLRaw, "/")
|
||||||
|
|
||||||
|
// parse gitURL
|
||||||
gitURL, err := url.Parse(gitURLRaw)
|
gitURL, err := url.Parse(gitURLRaw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("provided gitURLRaw '%s' is invalid: %w", gitURLRaw, err)
|
return nil, fmt.Errorf("provided gitURLRaw '%s' is invalid: %w", gitURLRaw, err)
|
||||||
@ -38,16 +47,19 @@ func NewProvider(apiURLRaw string, apiURLInternalRaw, gitURLRaw string) (*Provid
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIBaseURL returns the publicly reachable base url of the api server.
|
// GetAPIBaseURL returns the publicly reachable base url of the api server.
|
||||||
|
// NOTE: url is guaranteed to not have any trailing '/'.
|
||||||
func (p *Provider) GetAPIBaseURL() string {
|
func (p *Provider) GetAPIBaseURL() string {
|
||||||
return p.apiURLRaw
|
return p.apiURLRaw
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIBaseURLInternal returns the internally reachable base url of the api server.
|
// GetAPIBaseURLInternal returns the internally reachable base url of the api server.
|
||||||
|
// NOTE: url is guaranteed to not have any trailing '/'.
|
||||||
func (p *Provider) GetAPIBaseURLInternal() string {
|
func (p *Provider) GetAPIBaseURLInternal() string {
|
||||||
return p.apiURLInternalRaw
|
return p.apiURLInternalRaw
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRepoCloneURL generates the public git clone URL for the provided repo path.
|
// GenerateRepoCloneURL generates the public git clone URL for the provided repo path.
|
||||||
|
// NOTE: url is guaranteed to not have any trailing '/'.
|
||||||
func (p *Provider) GenerateRepoCloneURL(repoPath string) string {
|
func (p *Provider) GenerateRepoCloneURL(repoPath string) string {
|
||||||
repoPath = path.Clean(repoPath)
|
repoPath = path.Clean(repoPath)
|
||||||
if !strings.HasSuffix(repoPath, ".git") {
|
if !strings.HasSuffix(repoPath, ".git") {
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
illegalRootSpaceNames = []string{"api"}
|
illegalRootSpaceNames = []string{"api", "git"}
|
||||||
|
|
||||||
ErrRootSpaceNameNotAllowed = &ValidationError{
|
ErrRootSpaceNameNotAllowed = &ValidationError{
|
||||||
fmt.Sprintf("The following names are not allowed for a root space: %v", illegalRootSpaceNames),
|
fmt.Sprintf("The following names are not allowed for a root space: %v", illegalRootSpaceNames),
|
||||||
|
@ -18,9 +18,23 @@ type Config struct {
|
|||||||
|
|
||||||
// URL defines the URLs via which the different parts of the service are reachable by.
|
// URL defines the URLs via which the different parts of the service are reachable by.
|
||||||
URL struct {
|
URL struct {
|
||||||
Git string `envconfig:"GITNESS_URL_GIT" default:"http://localhost:3000"`
|
// Git defines the external URL via which the GIT API is reachable.
|
||||||
API string `envconfig:"GITNESS_URL_API" default:"http://localhost:3000"`
|
// NOTE: for routing to work properly, the request path & hostname reaching gitness
|
||||||
APIInternal string `envconfig:"GITNESS_URL_API_INTERNAL" default:"http://localhost:3000"`
|
// have to statisfy at least one of the following two conditions:
|
||||||
|
// - Path ends with `/git`
|
||||||
|
// - Hostname matches Config.Server.HTTP.GitHost
|
||||||
|
// (this could be after proxy path / header rewrite).
|
||||||
|
Git string `envconfig:"GITNESS_URL_GIT" default:"http://localhost:3000/git"`
|
||||||
|
|
||||||
|
// API defines the external URL via which the rest API is reachable.
|
||||||
|
// NOTE: for routing to work properly, the request path reaching gitness has to end with `/api`
|
||||||
|
// (this could be after proxy path rewrite).
|
||||||
|
API string `envconfig:"GITNESS_URL_API" default:"http://localhost:3000/api"`
|
||||||
|
|
||||||
|
// APIInternal defines the internal URL via which the rest API is reachable.
|
||||||
|
// NOTE: for routing to work properly, the request path reaching gitness has to end with `/api`
|
||||||
|
// (this could be after proxy path rewrite).
|
||||||
|
APIInternal string `envconfig:"GITNESS_URL_API_INTERNAL" default:"http://localhost:3000/api"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Git defines the git configuration parameters
|
// Git defines the git configuration parameters
|
||||||
@ -38,6 +52,8 @@ type Config struct {
|
|||||||
Bind string `envconfig:"GITNESS_HTTP_BIND" default:":3000"`
|
Bind string `envconfig:"GITNESS_HTTP_BIND" default:":3000"`
|
||||||
Proto string `envconfig:"GITNESS_HTTP_PROTO"`
|
Proto string `envconfig:"GITNESS_HTTP_PROTO"`
|
||||||
Host string `envconfig:"GITNESS_HTTP_HOST"`
|
Host string `envconfig:"GITNESS_HTTP_HOST"`
|
||||||
|
// GitHost is the host used to identify git traffic (OPTIONAL).
|
||||||
|
GitHost string `envconfig:"GITNESS_HTTP_GIT_HOST" default:"git.localhost"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GRPC defines the grpc configuration parameters
|
// GRPC defines the grpc configuration parameters
|
||||||
|
Loading…
Reference in New Issue
Block a user