Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[receiver/gitlab] add tracing via webhook skeleton #36838

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .chloggen/gl-receiver-skeleton-traces.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement
niwoerner marked this conversation as resolved.
Show resolved Hide resolved

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: gitlabreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Adds webhook skeleton to GitLab receiver to receive events from GitLab for tracing.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [35207]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
This PR adds a skeleton for the GitLab receiver to receive events from GitLab
for tracing via a webhook. The trace portion of this receiver will run and
respond to GET requests for the health check only.

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ receiver/filestatsreceiver/ @open-telemetry/collector-cont
receiver/flinkmetricsreceiver/ @open-telemetry/collector-contrib-approvers @JonathanWamsley
receiver/fluentforwardreceiver/ @open-telemetry/collector-contrib-approvers @dmitryax
receiver/githubreceiver/ @open-telemetry/collector-contrib-approvers @adrielp @andrzej-stencel @crobert-1 @TylerHelmuth
receiver/gitlabreceiver/ @open-telemetry/collector-contrib-approvers @adrielp @atoulme
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you and I have talked about this on the side, but after this contribution goes through and you go through getting added as an OpenTelemetry member, it'd be great to have you added to the list of code owners for this receiver.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is my first bigger PR, I wasn't sure if that's already enough to become a member. So I thought I would update the code-owners with a subsequent PR once this one is merged and I became a member.

If I can become a member already now, I'm happy to update the code-owners right away as I plan to own/maintain this component. What are your thoughts on this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, updates in the next Pr sound good! And you can go ahead and open up the community issue to request to join now.

receiver/googlecloudmonitoringreceiver/ @open-telemetry/collector-contrib-approvers @dashpole @TylerHelmuth @abhishek-at-cloudwerx
receiver/googlecloudpubsubreceiver/ @open-telemetry/collector-contrib-approvers @alexvanboxel
receiver/googlecloudspannerreceiver/ @open-telemetry/collector-contrib-approvers @dashpole @dsimil @KiranmayiB @harishbohara11
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ body:
- receiver/flinkmetrics
- receiver/fluentforward
- receiver/github
- receiver/gitlab
- receiver/googlecloudmonitoring
- receiver/googlecloudpubsub
- receiver/googlecloudspanner
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ body:
- receiver/flinkmetrics
- receiver/fluentforward
- receiver/github
- receiver/gitlab
- receiver/googlecloudmonitoring
- receiver/googlecloudpubsub
- receiver/googlecloudspanner
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/other.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ body:
- receiver/flinkmetrics
- receiver/fluentforward
- receiver/github
- receiver/gitlab
- receiver/googlecloudmonitoring
- receiver/googlecloudpubsub
- receiver/googlecloudspanner
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/unmaintained.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ body:
- receiver/flinkmetrics
- receiver/fluentforward
- receiver/github
- receiver/gitlab
- receiver/googlecloudmonitoring
- receiver/googlecloudpubsub
- receiver/googlecloudspanner
Expand Down
1 change: 1 addition & 0 deletions receiver/gitlabreceiver/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
45 changes: 45 additions & 0 deletions receiver/gitlabreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# GitLab Receiver

## Traces - Getting Started

Workflow tracing support is actively being added to the GitLab receiver.
This is accomplished through the processing of GitLab webhook
events for pipelines. The [`pipeline`](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#pipeline-events) event payloads are then constructed into `trace`
telemetry.

Each GitLab pipeline, along with it's jobs, are converted
niwoerner marked this conversation as resolved.
Show resolved Hide resolved
into trace spans, allowing the observation of workflow execution times,
success, and failure rates.

### Configuration

**IMPORTANT: At this time the tracing portion of this receiver only serves a health check endpoint.**

The WebHook configuration exposes the following settings:

* `endpoint`: (default = `localhost:8080`) - The address and port to bind the WebHook to.
* `path`: (default = `/events`) - The path for Action events to be sent to.
* `health_path`: (default = `/health`) - The path for health checks.
* `secret`: (optional) - The secret used to [validate the payload](https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#custom-headers).
* `required_header`: (optional) - The required header key and value for incoming requests.

The WebHook configuration block also accepts all the [confighttp](https://pkg.go.dev/go.opentelemetry.io/collector/config/confighttp#ServerConfig)
settings.

An example configuration is as follows:

```yaml
receivers:
gitlab:
webhook:
endpoint: localhost:19418
path: /events
health_path: /health
secret: ${env:SECRET_STRING_VAR}
required_header:
key: "X-GitLab-Event"
value: "pipeline"
```

For tracing, all configuration is set under the `webhook` key. The full set
of exposed configuration values can be found in [`config.go`](config.go).
80 changes: 80 additions & 0 deletions receiver/gitlabreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package gitlabreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver"

import (
"errors"
"time"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
"go.uber.org/multierr"
)

const (
defaultReadTimeout = 500 * time.Millisecond
defaultWriteTimeout = 500 * time.Millisecond
defaultPath = "/events"
defaultHealthPath = "/health"
defaultEndpoint = "localhost:8080"
)

var (
errReadTimeoutExceedsMaxValue = errors.New("the duration specified for read_timeout exceeds the maximum allowed value of 10s")
errWriteTimeoutExceedsMaxValue = errors.New("the duration specified for write_timeout exceeds the maximum allowed value of 10s")
errRequiredHeader = errors.New("both key and value are required to assign a required_header")
errConfigNotValid = errors.New("configuration is not valid for the gitlab receiver")
)

// Config that is exposed to this gitlab receiver through the OTEL config.yaml
type Config struct {
WebHook WebHook `mapstructure:"webhook"`
}

type WebHook struct {
confighttp.ServerConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct
Path string `mapstructure:"path"` // path for data collection. Default is /events
HealthPath string `mapstructure:"health_path"` // path for health check api. Default is /health_check
RequiredHeader RequiredHeader `mapstructure:"required_header"` // optional setting to set a required header for all requests to have
Secret string `mapstructure:"secret"` // secret for webhook
}

type RequiredHeader struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking about this for the GitHub receiver as well, but I think this should be converted to RequiredHeaders of map[string]string so that a user can set any set of headers that are required. The main one that would be useful for GitLab would be X-Gitlab-Instance. But if a user has a WAF sitting in front of the receiver (which they should anyway for public traffic) then they can set additional headers of their own choosing.

For the other headers listed in the doc, I think we should map these internal to the receiver.

Key string `mapstructure:"key"`
Value string `mapstructure:"value"`
}

func createDefaultConfig() component.Config {
return &Config{
WebHook: WebHook{
ServerConfig: confighttp.ServerConfig{
Endpoint: defaultEndpoint,
ReadTimeout: defaultReadTimeout,
WriteTimeout: defaultWriteTimeout,
},
Path: defaultPath,
HealthPath: defaultHealthPath,
},
}
}

func (cfg *Config) Validate() error {
var errs error

maxReadWriteTimeout, _ := time.ParseDuration("10s")

if cfg.WebHook.ServerConfig.ReadTimeout > maxReadWriteTimeout {
errs = multierr.Append(errs, errReadTimeoutExceedsMaxValue)
}

if cfg.WebHook.ServerConfig.WriteTimeout > maxReadWriteTimeout {
errs = multierr.Append(errs, errWriteTimeoutExceedsMaxValue)
}

if (cfg.WebHook.RequiredHeader.Key != "" && cfg.WebHook.RequiredHeader.Value == "") || (cfg.WebHook.RequiredHeader.Value != "" && cfg.WebHook.RequiredHeader.Key == "") {
errs = multierr.Append(errs, errRequiredHeader)
}

return errs
}
79 changes: 79 additions & 0 deletions receiver/gitlabreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package gitlabreceiver

import (
"path/filepath"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/otelcol/otelcoltest"

"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver/internal/metadata"
)

func TestCreateDefaultConfig(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()

assert.Equal(t,
&Config{
WebHook: WebHook{
ServerConfig: confighttp.ServerConfig{
Endpoint: defaultEndpoint,
ReadTimeout: defaultReadTimeout,
WriteTimeout: defaultWriteTimeout,
},
Path: defaultPath,
HealthPath: defaultHealthPath,
},
},
cfg, "failed to create default config")

assert.NoError(t, componenttest.CheckConfigStruct(cfg))
}

func TestLoadConfig(t *testing.T) {
factories, err := otelcoltest.NopFactories()
require.NoError(t, err)

factory := NewFactory()
factories.Receivers[metadata.Type] = factory

cfg, err := otelcoltest.LoadConfigAndValidate(filepath.Join("testdata", "config.yaml"), factories)

require.NoError(t, err)
require.NotNil(t, cfg)

assert.Len(t, cfg.Receivers, 2)

expectedConfig := &Config{
WebHook: WebHook{
ServerConfig: confighttp.ServerConfig{
Endpoint: "localhost:8080",
ReadTimeout: 500 * time.Millisecond,
WriteTimeout: 500 * time.Millisecond,
},
Path: "some/path",
HealthPath: "health/path",
RequiredHeader: RequiredHeader{
Key: "key-present",
Value: "value-present",
},
},
}

r0 := cfg.Receivers[component.NewID(metadata.Type)]

assert.Equal(t, expectedConfig, r0)

r1 := cfg.Receivers[component.NewIDWithName(metadata.Type, "customname")].(*Config)

assert.Equal(t, expectedConfig, r1)
}
6 changes: 6 additions & 0 deletions receiver/gitlabreceiver/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

//go:generate mdatagen metadata.yaml

package gitlabreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver"
31 changes: 31 additions & 0 deletions receiver/gitlabreceiver/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package gitlabreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver"

import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/receiver"

"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver/internal/metadata"
)

func createTracesReceiver(_ context.Context, params receiver.Settings, cfg component.Config, consumer consumer.Traces) (receiver.Traces, error) {
// check that the configuration is valid
conf, ok := cfg.(*Config)
if !ok {
return nil, errConfigNotValid
}

return newTracesReceiver(params, conf, consumer)
}

func NewFactory() receiver.Factory {
return receiver.NewFactory(
metadata.Type,
createDefaultConfig,
receiver.WithTraces(createTracesReceiver, component.StabilityLevelDevelopment))
}
23 changes: 23 additions & 0 deletions receiver/gitlabreceiver/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package gitlabreceiver

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/consumer/consumertest"
"go.opentelemetry.io/collector/receiver/receivertest"
)

func TestCreateTracesReceiver(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")

tReceiver, err := factory.CreateTraces(context.Background(), receivertest.NewNopSettings(), cfg, consumertest.NewNop())
assert.NoError(t, err)
assert.NotNil(t, tReceiver, "traces receiver creation failed")
}
Loading
Loading