Skip to content

Commit

Permalink
feat(gh): update github receiver to actually work as a metrics receiver
Browse files Browse the repository at this point in the history
  • Loading branch information
Adriel Perkins committed Apr 21, 2023
1 parent 5d2bfdf commit 99890b3
Show file tree
Hide file tree
Showing 15 changed files with 798 additions and 108 deletions.
13 changes: 11 additions & 2 deletions receiver/githubreceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ package githubreceiver // import "github.com/liatrio/otel-liatrio-contrib/receiv
import (
"fmt"
"time"

"github.com/liatrio/otel-liatrio-contrib/receiver/githubreceiver/internal/metadata"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/receiver/scraperhelper"
)

// Configuration that is exposed to this github receiver through the OTEL config.yaml
type Config struct {
Interval string `mapstructure:"interval"`
GitHubOrg string `mapstructure:"github_org"`
scraperhelper.ScraperControllerSettings `mapstructure:",squash"`
confighttp.HTTPClientSettings `mapstructure:",squash"`
//TODO: define this
//MetricsBuilderConfig `mapstructure:",squash"`
metadata.MetricsBuilderConfig `mapstructure:",squash"`
Interval string `mapstructure:"interval"`
GitHubOrg string `mapstructure:"github_org"`
//Endpoint string `mapstructure:"endpoint"`
}

Expand Down
18 changes: 18 additions & 0 deletions receiver/githubreceiver/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Generate a test metrics builder from a sample metrics set covering all configuration options.
//go:generate mdatagen metadata.yaml

package githubreceiver
27 changes: 27 additions & 0 deletions receiver/githubreceiver/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[comment]: <> (Code generated by mdatagen. DO NOT EDIT.)

# githubreceiver

## Default Metrics

The following metrics are emitted by default. Each of them can be disabled by applying the following configuration:

```yaml
metrics:
<metric_name>:
enabled: false
```
### gh.repo.count
Number of repositories that exist in an organization
| Unit | Metric Type | Value Type |
| ---- | ----------- | ---------- |
| ratio | Gauge | Int |
#### Attributes
| Name | Description | Values |
| ---- | ----------- | ------ |
| gh.org | The organization owning the repository | Any Str |
102 changes: 78 additions & 24 deletions receiver/githubreceiver/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package githubreceiver // import "github.com/liatrio/otel-liatrio-contrib/receiv
import (
"context"
"errors"
"fmt"
//"fmt"
"time"

"github.com/liatrio/otel-liatrio-contrib/receiver/githubreceiver/internal/metadata"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/receiver"
"go.opentelemetry.io/collector/receiver/scraperhelper"
)

const (
Expand All @@ -23,10 +26,31 @@ var (
ghConfigNotValid = errors.New("config is not a valid github receiver configuration")
)

// Create ethe default config based on the const(s) defined above.
// NewFactory creates a factory for the githubreceiver according to OTEL's conventions
func NewFactory() receiver.Factory {
return receiver.NewFactory(
typeStr,
createDefaultConfig,
// TODO: pattern seems to create internal/metadata from where stability is defined
// look into this later
receiver.WithMetrics(createMetricsReceiver, stability),
)
}

// Create the default config based on the const(s) defined above.
func createDefaultConfig() component.Config {
//logger.Sugar().Infof("Creating the default config")
return &Config{
Interval: fmt.Sprint(defaultInterval),
// Not sure if this is right but we'll see
ScraperControllerSettings: scraperhelper.ScraperControllerSettings{
CollectionInterval: 10 * time.Second,
},
HTTPClientSettings: confighttp.HTTPClientSettings{
Timeout: defaultTimeout,
},
MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),

//Interval: fmt.sprintf("%v", defaultInterval),
//InsecureSkipVerify: defaultIgnoreTLS,
}
}
Expand All @@ -37,34 +61,64 @@ func createMetricsReceiver(
ctx context.Context,
params receiver.CreateSettings,
cfg component.Config,
consumer consumer.Metrics) (receiver.Metrics, error) {

// if the next consumer (processer or exporter) in the pipeline has an issue
// or is passed as nil then through the next consumer error
if consumer == nil {
return nil, component.ErrNilNextConsumer
}
consumer consumer.Metrics,
) (receiver.Metrics, error) {

ghCfg, ok := cfg.(*Config)
// check that the configuration is valid
conf, ok := cfg.(*Config)
if !ok {
return nil, ghConfigNotValid
}

logger := params.Logger
params.Logger.Sugar().Infof("Creating the metrics receiver")
sc := newScraper(conf, params)

ghRcvr := &ghReceiver{
logger: logger,
nextConsumer: consumer,
config: ghCfg,
params.Logger.Sugar().Infof("Creating the scraper")
sch, err := scraperhelper.NewScraper(typeStr, sc.scrape, scraperhelper.WithStart(sc.start))
if err != nil {
params.Logger.Sugar().Fatalf("Error creating scraper", err)
return nil, err
}

return ghRcvr, nil
}
//httpClient, err := ghCfg.HTTPClientSettings.ToClient(host component.Host, settings params.TelemetrySettings{})
// create the http client to pass to the scraper
//httpClient, err := ghCfg.HTTPClientSettings.ToClient(component.Host, params.TelemetrySettings)

// NewFactory creates a factory for the githubreceiver according to OTEL's conventions
func NewFactory() receiver.Factory {
return receiver.NewFactory(
typeStr,
createDefaultConfig,
receiver.WithMetrics(createMetricsReceiver, stability))
//logger := params.Logger
//ghs := &ghScraper{
// client: httpClient,
// logger: logger,
// nextConsumer: consumer,
// config: ghCfg,
//}

//httpClient, err := ghCfg.HTTPClientSettings.ToClient(host component.Host, settings params.TelemetrySettings{} )
//hc, err := ghCfg.HTTPClientSettings.ToClient(host component.Host, settings component.TelemetrySettings{})

//if err != nil {
// params.Logger.Sugar().Fatalf("Error creating HTTP client", err)
//}

// create the scraper passing the httpclient that was created
//sc := newScraper(httpClient, params.Logger, consumer, ghCfg)

//scHelper, err := scraperhelper.NewScraper(typeStr, sc, scraperhelper.WithStart(sc.start), scraperhelper.WithShutdown(sc.shutdown))

// if the next consumer (processer or exporter) in the pipeline has an issue
// or is passed as nil then throw the next consumer error
//if consumer == nil {
// return nil, component.ErrNilNextConsumer
//}

params.Logger.Sugar().Infof("Returning the scraper")
return scraperhelper.NewScraperControllerReceiver(&conf.ScraperControllerSettings, params, consumer, scraperhelper.AddScraper(sch))
//return sch, nil

// TODO:
// define a variable create a new scraper function which takes config and params
// then instatiate the scraper with scraper helper new scraper taking in the type string
// the scrape from the scraper, and a with start.
// make the scraper with start pass the *http.Client to the GH client and actually scrape
// return the proper metrics
// note: the metrics might need to come from a metrics builder which possibly is generated from metadata
}
96 changes: 63 additions & 33 deletions receiver/githubreceiver/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,79 @@ package githubreceiver // import "github.com/liatrio/otel-liatrio-contrib/receiv

import (
"context"
"github.com/liatrio/otel-liatrio-contrib/receiver/githubreceiver/internal/metadata"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
"testing"
"time"
"go.opentelemetry.io/collector/receiver/scraperhelper"

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

func TestNewFactory(t *testing.T) {
factory := NewFactory()
if factory == nil {
t.Error("NewFactory() should not return nil")
}

cfg := factory.CreateDefaultConfig()
if cfg == nil {
t.Error("CreateDefaultConfig() should not return nil")
}
tc := []struct {
desc string
tf func(*testing.T)
}{
{
desc: "create new factory with the correct type",
tf: func(t *testing.T) {
f := NewFactory()
assert.EqualValues(t, typeStr, f.Type())
},
},
{
desc: "create new factory with default config",
tf: func(t *testing.T) {
factory := NewFactory()

typ := factory.Type()
if typ != typeStr {
t.Errorf("factory.Type() should return %s, got %s", typeStr, typ)
}
}
var expectefCfg component.Config = &Config{
ScraperControllerSettings: scraperhelper.ScraperControllerSettings{
CollectionInterval: 10 * time.Second,
},
HTTPClientSettings: confighttp.HTTPClientSettings{
Timeout: 15 * time.Second,
},
MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
}
assert.Equal(t, expectefCfg, factory.CreateDefaultConfig())
},
},
{
desc: "create new factory and metric receiver without returing an error",
tf: func(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
_, err := factory.CreateMetricsReceiver(
context.Background(),
receivertest.NewNopCreateSettings(),
cfg,
consumertest.NewNop(),
)
assert.NoError(t, err)
},
},
{
desc: "create new factory and metric receiver returning errors and invalid config",
tf: func(t *testing.T) {
factory := NewFactory()
_, err := factory.CreateMetricsReceiver(
context.Background(),
receivertest.NewNopCreateSettings(),
nil,
consumertest.NewNop(),
)
assert.ErrorIs(t, err, ghConfigNotValid)

func TestCreateMetricsReceiver(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
r, err := factory.CreateMetricsReceiver(context.Background(), receiver.CreateSettings{}, cfg, consumertest.NewNop())

if err != nil {
t.Errorf("failed to create metrics receiver: %v", err)
},
},
}

if r == nil {
t.Error("CreateMetricsReceiver() should not return nil")
}
}

func TestCreateMetricsReceiverNilNextConsumer(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
_, err := factory.CreateMetricsReceiver(context.Background(), receiver.CreateSettings{}, cfg, nil)

if err == nil {
t.Error("CreateMetricsReceiver() should return an error when next consumer is nil")
for _, tt := range tc {
t.Run(tt.desc, tt.tf)
}
}
19 changes: 16 additions & 3 deletions receiver/githubreceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,41 @@ go 1.20

require (
github.com/google/go-github/v50 v50.2.0
github.com/stretchr/testify v1.8.2
go.opentelemetry.io/collector v0.74.0
go.opentelemetry.io/collector/component v0.74.0
go.opentelemetry.io/collector/confmap v0.74.0
go.opentelemetry.io/collector/consumer v0.74.0
go.opentelemetry.io/collector/pdata v1.0.0-rc8
go.opentelemetry.io/collector/receiver v0.74.0
go.uber.org/zap v1.24.0
)

require (
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/knadh/koanf v1.5.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
go.opentelemetry.io/collector v0.74.0 // indirect
go.opentelemetry.io/collector/confmap v0.74.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/cors v1.8.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/collector/exporter v0.74.0 // indirect
go.opentelemetry.io/collector/featuregate v0.74.0 // indirect
go.opentelemetry.io/collector/pdata v1.0.0-rc8 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/metric v0.37.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
Expand All @@ -41,4 +53,5 @@ require (
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.29.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 99890b3

Please sign in to comment.