Skip to content

Commit

Permalink
feat(github): migrate the gitproviderreceiver githubscraper to github…
Browse files Browse the repository at this point in the history
…receiver (#523)
  • Loading branch information
gnmeyer authored Sep 16, 2024
1 parent dcd913d commit 92993a5
Show file tree
Hide file tree
Showing 44 changed files with 32,909 additions and 1 deletion.
40 changes: 39 additions & 1 deletion config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,44 @@ receivers:
static_configs:
- targets: [0.0.0.0:8888]

github:
initial_delay: 10s
collection_interval: 60s
scrapers:
github:
metrics:
#Default Metrics (enabled by default)
vcs.repository.count:
enabled: true
vcs.repository.change.count:
enabled: false
vcs.repository.change.time_open:
enabled: false
vcs.repository.change.time_to_approval:
enabled: false
vcs.repository.change.time_to_merge:
enabled: false
vcs.repository.ref.count:
enabled: false
vcs.repository.ref.lines_added:
enabled: false
vcs.repository.ref.lines_deleted:
enabled: false
vcs.repository.ref.revisions_ahead:
enabled: false
vcs.repository.ref.revisions_behind:
enabled: false
vcs.repository.ref.time:
enabled: false
#Optional Metrics
vcs.repository.contributor.count:
enabled: false
github_org: ${env:GH_ORG}
search_query: ${env:SEARCH_QUERY} #Recommended optional query override, defaults to "{org,user}:<github_org>"
#endpoint: https://selfmanagedenterpriseserver.com
auth:
authenticator: bearertokenauth/github

gitprovider:
initial_delay: 1s
collection_interval: 60s
Expand Down Expand Up @@ -78,6 +116,6 @@ service:
extensions: [health_check, pprof, zpages, bearertokenauth/github]
pipelines:
metrics:
receivers: [gitprovider, prometheus]
receivers: [github, gitprovider, prometheus]
processors: []
exporters: [debug]
2 changes: 2 additions & 0 deletions config/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ processors:

receivers:
- gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.109.0
- gomod: github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver v0.1.0
- gomod: github.com/liatrio/liatrio-otel-collector/receiver/gitproviderreceiver v0.1.0
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.109.0
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.109.0
Expand All @@ -66,5 +67,6 @@ providers:
- gomod: go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.109.0

replaces:
- github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver => ../receiver/githubreceiver/
- github.com/liatrio/liatrio-otel-collector/receiver/gitproviderreceiver => ../receiver/gitproviderreceiver/
- github.com/liatrio/liatrio-otel-collector/extension/githubappauthextension => ../extension/githubappauthextension/
1 change: 1 addition & 0 deletions receiver/githubreceiver/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
93 changes: 93 additions & 0 deletions receiver/githubreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# GitHub Receiver

<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Stability | [development]: metrics |
| Distributions | [liatrio] |
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Fgithub%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Fgithub) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Fgithub%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Fgithub) |

[development]: https://github.com/open-telemetry/opentelemetry-collector#development
[liatrio]:
<!-- end autogenerated section -->

The GitHub receiver receives data from [GitHub](https://github.com). As a
starting point it scrapes metrics from repositories but will be extended to
include traces and logs.

The current default set of metrics can be found in
[documentation.md](./documentation.md).

These metrics can be used as leading indicators ([capabilities][doracap])
to the [DORA][dorafour] metrics; helping provide insight into modern-day
engineering practices.

[doracap]: https://dora.dev/capabilities/
[dorafour]: https://dora.dev/guides/dora-metrics-four-keys/

## Getting Started

The collection interval is common to all scrapers and is set to 30 seconds by default.

> Note: Generally speaking, if the vendor allows for anonymous API calls, then you
> won't have to configure any authentication, but you may only see public repositories
> and organizations. You may run into significantly more rate limiting.
```yaml
github:
collection_interval: <duration> #default = 30s recommended 300s
scrapers:
<scraper1>:
<scraper2>:
...
```

A more complete example using the GitHub scrapers with authentication is as follows:

```yaml
extensions:
bearertokenauth/github:
token: ${env:GH_PAT}

receivers:
github:
initial_delay: 1s
collection_interval: 60s
scrapers:
github:
metrics:
vcs.repository.contributor.count:
enabled: true
github_org: myfancyorg
search_query: "org:myfancyorg topic:o11yalltheway" #Recommended optional query override, defaults to "{org,user}:<github_org>"
endpoint: "https://selfmanagedenterpriseserver.com"
auth:
authenticator: bearertokenauth/github
service:
extensions: [bearertokenauth/github]
pipelines:
metrics:
receivers: [..., github]
processors: []
exporters: [...]
```
A Grafana Dashboard exists on the marketplace for metrics from this receiver
and can be found
[here](https://grafana.com/grafana/dashboards/20976-engineering-effectiveness-metrics/).
## Scraping
> Important:
> * The GitHub scraper does not emit metrics for branches that have not had
> changes since creation from the default branch (trunk).
> * Due to GitHub API limitations, it is possible for the branch time metric to
> change when rebases occur, recreating the commits with new timestamps.
<!-- TODO: Combine this documentation once the scraper code is restructured due scope change -->
For additional context on GitHub scraper limitations and inner workings please
see the [Scraping README][ghsread].
[ghsread]: internal/scraper/githubscraper/README.md#github-limitations
82 changes: 82 additions & 0 deletions receiver/githubreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package githubreceiver // import "github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver"

import (
"errors"
"fmt"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/receiver/scraperhelper"

"github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver/internal"
"github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver/internal/metadata"
)

const (
scrapersKey = "scrapers"
)

// Config that is exposed to this github receiver through the OTEL config.yaml
type Config struct {
scraperhelper.ControllerConfig `mapstructure:",squash"`
Scrapers map[string]internal.Config `mapstructure:"scrapers"`
metadata.MetricsBuilderConfig `mapstructure:",squash"`
}

var _ component.Config = (*Config)(nil)
var _ confmap.Unmarshaler = (*Config)(nil)

// Validate the configuration passed through the OTEL config.yaml
func (cfg *Config) Validate() error {
if len(cfg.Scrapers) == 0 {
return errors.New("must specify at least one scraper")
}
return nil
}

// Unmarshal a config.Parser into the config struct.
func (cfg *Config) Unmarshal(componentParser *confmap.Conf) error {
if componentParser == nil {
return nil
}

// load the non-dynamic config normally
err := componentParser.Unmarshal(cfg, confmap.WithIgnoreUnused())
if err != nil {
return err
}

// dynamically load the individual collector configs based on the key name

cfg.Scrapers = map[string]internal.Config{}

scrapersSection, err := componentParser.Sub(scrapersKey)
if err != nil {
return err
}

for key := range scrapersSection.ToStringMap() {
factory, ok := getScraperFactory(key)
if !ok {
return fmt.Errorf("invalid scraper key: %q", key)
}

collectorCfg := factory.CreateDefaultConfig()
collectorSection, err := scrapersSection.Sub(key)
if err != nil {
return err
}

err = collectorSection.Unmarshal(collectorCfg)
if err != nil {
return fmt.Errorf("error reading settings for scraper type %q: %w", key, err)
}

cfg.Scrapers[key] = collectorCfg
}

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

package githubreceiver

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

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/otelcol/otelcoltest"
"go.opentelemetry.io/collector/receiver/scraperhelper"

"github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver/internal"
"github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver/internal/metadata"
"github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver/internal/scraper/githubscraper"
)

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

factory := NewFactory()
factories.Receivers[metadata.Type] = factory
// https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/33594
// nolint:staticcheck
cfg, err := otelcoltest.LoadConfigAndValidate(filepath.Join("testdata", "config.yaml"), factories)

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

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

r0 := cfg.Receivers[component.NewID(metadata.Type)]
defaultConfigGitHubScraper := factory.CreateDefaultConfig()
defaultConfigGitHubScraper.(*Config).Scrapers = map[string]internal.Config{
githubscraper.TypeStr: (&githubscraper.Factory{}).CreateDefaultConfig(),
}

assert.Equal(t, defaultConfigGitHubScraper, r0)

r1 := cfg.Receivers[component.NewIDWithName(metadata.Type, "customname")].(*Config)
expectedConfig := &Config{
ControllerConfig: scraperhelper.ControllerConfig{
CollectionInterval: 30 * time.Second,
InitialDelay: 1 * time.Second,
},
Scrapers: map[string]internal.Config{
githubscraper.TypeStr: (&githubscraper.Factory{}).CreateDefaultConfig(),
},
}

assert.Equal(t, expectedConfig, r1)
}

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

factory := NewFactory()
factories.Receivers[metadata.Type] = factory
// https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/33594
// nolint:staticcheck
_, err = otelcoltest.LoadConfigAndValidate(filepath.Join("testdata", "config-noscrapers.yaml"), factories)

require.Contains(t, err.Error(), "must specify at least one scraper")
}

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

factory := NewFactory()
factories.Receivers[metadata.Type] = factory
// https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/33594
// nolint:staticcheck
_, err = otelcoltest.LoadConfigAndValidate(filepath.Join("testdata", "config-invalidscraperkey.yaml"), factories)

require.Contains(t, err.Error(), "error reading configuration for \"github\": invalid scraper key: \"invalidscraperkey\"")
}

func TestConfig_Unmarshal(t *testing.T) {
type fields struct {
ControllerConfig scraperhelper.ControllerConfig
Scrapers map[string]internal.Config
MetricsBuilderConfig metadata.MetricsBuilderConfig
}

type args struct {
componentParser *confmap.Conf
}

tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "Empty Component Parser",
fields: fields{},
args: args{componentParser: nil},
wantErr: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := &Config{
ControllerConfig: test.fields.ControllerConfig,
Scrapers: test.fields.Scrapers,
MetricsBuilderConfig: test.fields.MetricsBuilderConfig,
}
if err := cfg.Unmarshal(test.args.componentParser); (err != nil) != test.wantErr {
t.Errorf("Config.Unmarshal() error = %v, wantErr %v", err, test.wantErr)
}
})
}
}
6 changes: 6 additions & 0 deletions receiver/githubreceiver/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 ../../.tools/mdatagen metadata.yaml

package githubreceiver // import "github.com/liatrio/liatrio-otel-collector/receiver/githubreceiver"
Loading

0 comments on commit 92993a5

Please sign in to comment.