mirror of
https://github.com/harness/drone.git
synced 2025-05-12 23:20:10 +08:00
[Webhook] Expose User-Agent and X-{NAME} via Config, enhance request headers (#289)
This commit is contained in:
parent
c5d92b498b
commit
27777bb2b1
@ -25,6 +25,14 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
// UserAgentIdentity specifies the identity used for the user agent header
|
||||||
|
// IMPORTANT: do not include version.
|
||||||
|
UserAgentIdentity string `envconfig:"GITNESS_WEBHOOK_USER_AGENT_IDENTITY" default:"Gitness"`
|
||||||
|
// HeaderIdentity specifies the identity used for headers in webhook calls (e.g. X-Gitness-Trigger, ...).
|
||||||
|
// NOTE: If no value is provided, the UserAgentIdentity will be used.
|
||||||
|
HeaderIdentity string `envconfig:"GITNESS_WEBHOOK_HEADER_IDENTITY"`
|
||||||
|
// EventReaderName is the name used to read events from stream.
|
||||||
|
// Note: this should be different for every running instance.
|
||||||
EventReaderName string `envconfig:"GITNESS_WEBHOOK_EVENT_READER_NAME"`
|
EventReaderName string `envconfig:"GITNESS_WEBHOOK_EVENT_READER_NAME"`
|
||||||
Concurrency int `envconfig:"GITNESS_WEBHOOK_CONCURRENCY" default:"4"`
|
Concurrency int `envconfig:"GITNESS_WEBHOOK_CONCURRENCY" default:"4"`
|
||||||
MaxRetries int `envconfig:"GITNESS_WEBHOOK_MAX_RETRIES" default:"3"`
|
MaxRetries int `envconfig:"GITNESS_WEBHOOK_MAX_RETRIES" default:"3"`
|
||||||
@ -32,13 +40,16 @@ type Config struct {
|
|||||||
AllowLoopback bool `envconfig:"GITNESS_WEBHOOK_ALLOW_LOOPBACK" default:"false"`
|
AllowLoopback bool `envconfig:"GITNESS_WEBHOOK_ALLOW_LOOPBACK" default:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Prepare() error {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return errors.New("config is required")
|
return errors.New("config is required")
|
||||||
}
|
}
|
||||||
if c.EventReaderName == "" {
|
if c.EventReaderName == "" {
|
||||||
return errors.New("config.EventReaderName is required")
|
return errors.New("config.EventReaderName is required")
|
||||||
}
|
}
|
||||||
|
if c.UserAgentIdentity == "" {
|
||||||
|
return errors.New("config.UserAgentIdentity is required")
|
||||||
|
}
|
||||||
if c.Concurrency < 1 {
|
if c.Concurrency < 1 {
|
||||||
return errors.New("config.Concurrency has to be a positive number")
|
return errors.New("config.Concurrency has to be a positive number")
|
||||||
}
|
}
|
||||||
@ -46,6 +57,11 @@ func (c *Config) Validate() error {
|
|||||||
return errors.New("config.MaxRetries can't be negative")
|
return errors.New("config.MaxRetries can't be negative")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backfill data
|
||||||
|
if c.HeaderIdentity == "" {
|
||||||
|
c.HeaderIdentity = c.UserAgentIdentity
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +77,8 @@ type Service struct {
|
|||||||
|
|
||||||
secureHTTPClient *http.Client
|
secureHTTPClient *http.Client
|
||||||
insecureHTTPClient *http.Client
|
insecureHTTPClient *http.Client
|
||||||
|
|
||||||
|
config Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(ctx context.Context, config Config,
|
func NewService(ctx context.Context, config Config,
|
||||||
@ -69,7 +87,7 @@ func NewService(ctx context.Context, config Config,
|
|||||||
webhookStore store.WebhookStore, webhookExecutionStore store.WebhookExecutionStore,
|
webhookStore store.WebhookStore, webhookExecutionStore store.WebhookExecutionStore,
|
||||||
repoStore store.RepoStore, pullreqStore store.PullReqStore, urlProvider *url.Provider,
|
repoStore store.RepoStore, pullreqStore store.PullReqStore, urlProvider *url.Provider,
|
||||||
principalStore store.PrincipalStore, gitRPCClient gitrpc.Interface) (*Service, error) {
|
principalStore store.PrincipalStore, gitRPCClient gitrpc.Interface) (*Service, error) {
|
||||||
if err := config.Validate(); err != nil {
|
if err := config.Prepare(); err != nil {
|
||||||
return nil, fmt.Errorf("provided config is invalid: %w", err)
|
return nil, fmt.Errorf("provided config is invalid: %w", err)
|
||||||
}
|
}
|
||||||
service := &Service{
|
service := &Service{
|
||||||
@ -83,6 +101,8 @@ func NewService(ctx context.Context, config Config,
|
|||||||
|
|
||||||
secureHTTPClient: newHTTPClient(config.AllowLoopback, config.AllowPrivateNetwork, false),
|
secureHTTPClient: newHTTPClient(config.AllowLoopback, config.AllowPrivateNetwork, false),
|
||||||
insecureHTTPClient: newHTTPClient(config.AllowLoopback, config.AllowPrivateNetwork, true),
|
insecureHTTPClient: newHTTPClient(config.AllowLoopback, config.AllowPrivateNetwork, true),
|
||||||
|
|
||||||
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := gitReaderFactory.Launch(ctx, eventsReaderGroupName, config.EventReaderName,
|
_, err := gitReaderFactory.Launch(ctx, eventsReaderGroupName, config.EventReaderName,
|
||||||
|
@ -212,7 +212,7 @@ func (s *Service) executeWebhook(ctx context.Context, webhook *types.Webhook, tr
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// create request from webhook and body
|
// create request from webhook and body
|
||||||
req, err := prepareHTTPRequest(ctx, &execution, webhook, body)
|
req, err := s.prepareHTTPRequest(ctx, &execution, triggerType, webhook, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &execution, err
|
return &execution, err
|
||||||
}
|
}
|
||||||
@ -266,10 +266,10 @@ func (s *Service) executeWebhook(ctx context.Context, webhook *types.Webhook, tr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prepareHTTPRequest prepares a new http.Request object for the webhook using the provided body as request body.
|
// prepareHTTPRequest prepares a new http.Request object for the webhook using the provided body as request body.
|
||||||
// All execution.Request.XXX values are set accordingly
|
// All execution.Request.XXX values are set accordingly.
|
||||||
// NOTE: if the body is an io.Reader, the value is used as response body as is, otherwise it'll be JSON serialized.
|
// NOTE: if the body is an io.Reader, the value is used as response body as is, otherwise it'll be JSON serialized.
|
||||||
func prepareHTTPRequest(ctx context.Context, execution *types.WebhookExecution,
|
func (s *Service) prepareHTTPRequest(ctx context.Context, execution *types.WebhookExecution,
|
||||||
webhook *types.Webhook, body any) (*http.Request, error) {
|
triggerType enum.WebhookTrigger, webhook *types.Webhook, body any) (*http.Request, error) {
|
||||||
// set URL as is (already has been validated, any other error will be caught in request creation)
|
// set URL as is (already has been validated, any other error will be caught in request creation)
|
||||||
execution.Request.URL = webhook.URL
|
execution.Request.URL = webhook.URL
|
||||||
|
|
||||||
@ -318,8 +318,12 @@ func prepareHTTPRequest(ctx context.Context, execution *types.WebhookExecution,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setup headers
|
// setup headers
|
||||||
req.Header.Add("User-Agent", fmt.Sprintf("Gitness/%s", version.Version))
|
req.Header.Add("User-Agent", fmt.Sprintf("%s/%s", s.config.UserAgentIdentity, version.Version))
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.Header.Add(s.toXHeader("Trigger"), string(triggerType))
|
||||||
|
req.Header.Add(s.toXHeader("Webhook-Id"), fmt.Sprint(webhook.ID))
|
||||||
|
req.Header.Add(s.toXHeader("Webhook-Parent-Type"), string(webhook.ParentType))
|
||||||
|
req.Header.Add(s.toXHeader("Webhook-Parent-Id"), fmt.Sprint(webhook.ParentID))
|
||||||
|
|
||||||
// add HMAC only if a secret was provided
|
// add HMAC only if a secret was provided
|
||||||
if webhook.Secret != "" {
|
if webhook.Secret != "" {
|
||||||
@ -328,8 +332,7 @@ func prepareHTTPRequest(ctx context.Context, execution *types.WebhookExecution,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to generate SHA256 based HMAC: %w", err)
|
return nil, fmt.Errorf("failed to generate SHA256 based HMAC: %w", err)
|
||||||
}
|
}
|
||||||
// TODO: Take 'Gitness' as config input?
|
req.Header.Add(s.toXHeader("Signature"), hmac)
|
||||||
req.Header.Add("X-Gitness-Signature", hmac)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hBuffer := &bytes.Buffer{}
|
hBuffer := &bytes.Buffer{}
|
||||||
@ -345,6 +348,10 @@ func prepareHTTPRequest(ctx context.Context, execution *types.WebhookExecution,
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) toXHeader(name string) string {
|
||||||
|
return fmt.Sprintf("X-%s-%s", s.config.HeaderIdentity, name)
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:funlen // refactor if needed
|
//nolint:funlen // refactor if needed
|
||||||
func handleWebhookResponse(execution *types.WebhookExecution, resp *http.Response) error {
|
func handleWebhookResponse(execution *types.WebhookExecution, resp *http.Response) error {
|
||||||
// store status (handle status later - want to first read body)
|
// store status (handle status later - want to first read body)
|
||||||
|
Loading…
Reference in New Issue
Block a user