Skip to content

Commit

Permalink
Add factory and new-style config for Prometheus receiver (#42)
Browse files Browse the repository at this point in the history
This is part of remaining migration to new configuration format.

Github issue: open-telemetry/opentelemetry-collector#33

Testing done: make
  • Loading branch information
tigrannajaryan authored Jun 21, 2019
1 parent 8e633da commit 4138e51
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 0 deletions.
31 changes: 31 additions & 0 deletions receiver/prometheusreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2019, OpenCensus 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.

package prometheusreceiver

import (
"time"

"github.com/prometheus/prometheus/config"

"github.com/open-telemetry/opentelemetry-service/internal/configmodels"
)

// ConfigV2 defines configuration for Prometheus receiver.
type ConfigV2 struct {
configmodels.ReceiverSettings `mapstructure:",squash"`
PrometheusConfig *config.Config `mapstructure:"-"`
BufferPeriod time.Duration `mapstructure:"buffer_period"`
BufferCount int `mapstructure:"buffer_count"`
}
54 changes: 54 additions & 0 deletions receiver/prometheusreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2019, OpenCensus 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.

package prometheusreceiver

import (
"path"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/open-telemetry/opentelemetry-service/internal/configmodels"
"github.com/open-telemetry/opentelemetry-service/internal/configv2"
"github.com/open-telemetry/opentelemetry-service/internal/factories"
)

var _ = configv2.RegisterTestFactories()

func TestLoadConfig(t *testing.T) {
factory := factories.GetReceiverFactory(typeStr)

config, err := configv2.LoadConfigFile(t, path.Join(".", "testdata", "config.yaml"))

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

assert.Equal(t, len(config.Receivers), 2)

r0 := config.Receivers["prometheus"]
assert.Equal(t, r0, factory.CreateDefaultConfig())

r1 := config.Receivers["prometheus/customname"].(*ConfigV2)
assert.Equal(t, r1.ReceiverSettings,
configmodels.ReceiverSettings{
TypeVal: typeStr,
NameVal: "prometheus/customname",
Endpoint: "1.2.3.4:456",
})
assert.Equal(t, r1.PrometheusConfig.ScrapeConfigs[0].JobName, "demo")
assert.Equal(t, time.Duration(r1.PrometheusConfig.ScrapeConfigs[0].ScrapeInterval), 5*time.Second)
}
131 changes: 131 additions & 0 deletions receiver/prometheusreceiver/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2019, OpenCensus 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.

package prometheusreceiver

import (
"context"
"fmt"

"github.com/spf13/viper"
"gopkg.in/yaml.v2"

"github.com/open-telemetry/opentelemetry-service/consumer"
"github.com/open-telemetry/opentelemetry-service/internal/configmodels"
"github.com/open-telemetry/opentelemetry-service/internal/factories"
"github.com/open-telemetry/opentelemetry-service/receiver"
)

// This file implements config V2 for Prometheus receiver.

var _ = factories.RegisterReceiverFactory(&ReceiverFactory{})

const (
// The value of "type" key in configuration.
typeStr = "prometheus"
)

// ReceiverFactory is the factory for receiver.
type ReceiverFactory struct {
}

// Type gets the type of the Receiver config created by this factory.
func (f *ReceiverFactory) Type() string {
return typeStr
}

// CustomUnmarshaler returns custom unmarshaler for this config.
func (f *ReceiverFactory) CustomUnmarshaler() factories.CustomUnmarshaler {
return CustomUnmarshalerFunc
}

// CustomUnmarshalerFunc performs custom unmarshaling of config.
func CustomUnmarshalerFunc(v *viper.Viper, viperKey string, intoCfg interface{}) error {
// We need custom unmarshaling because prometheus "config" subkey defines its own
// YAML unmarshaling routines so we need to do it explicitly.

// Unmarshal our config values (using viper's mapstructure)
err := v.UnmarshalKey(viperKey, intoCfg)
if err != nil {
return fmt.Errorf("prometheus receiver failed to parse config: %s", err)
}

// Unmarshal prometheus's config values. Since prometheus uses `yaml` tags, so use `yaml`.
vSub := v.Sub(viperKey)
if vSub == nil || !vSub.IsSet(prometheusConfigKey) {
return nil
}
promCfgMap := vSub.Sub(prometheusConfigKey).AllSettings()
out, err := yaml.Marshal(promCfgMap)
if err != nil {
return fmt.Errorf("prometheus receiver failed to marshal config to yaml: %s", err)
}

config := intoCfg.(*ConfigV2)

err = yaml.Unmarshal(out, &config.PrometheusConfig)
if err != nil {
return fmt.Errorf("prometheus receiver failed to unmarshal yaml to prometheus config: %s", err)
}
if len(config.PrometheusConfig.ScrapeConfigs) == 0 {
return errNilScrapeConfig
}
return nil
}

// CreateDefaultConfig creates the default configuration for receiver.
func (f *ReceiverFactory) CreateDefaultConfig() configmodels.Receiver {
return &ConfigV2{
ReceiverSettings: configmodels.ReceiverSettings{
TypeVal: typeStr,
NameVal: typeStr,
Endpoint: "127.0.0.1:9090",
},
}
}

// CreateTraceReceiver creates a trace receiver based on provided config.
func (f *ReceiverFactory) CreateTraceReceiver(
ctx context.Context,
cfg configmodels.Receiver,
nextConsumer consumer.TraceConsumer,
) (receiver.TraceReceiver, error) {
// Prometheus does not support traces
return nil, factories.ErrDataTypeIsNotSupported
}

// CreateMetricsReceiver creates a metrics receiver based on provided config.
func (f *ReceiverFactory) CreateMetricsReceiver(
cfg configmodels.Receiver,
consumer consumer.MetricsConsumer,
) (receiver.MetricsReceiver, error) {

rCfg := cfg.(*ConfigV2)

// Create receiver Configuration from our input cfg
config := Configuration{
BufferCount: rCfg.BufferCount,
BufferPeriod: rCfg.BufferPeriod,
ScrapeConfig: rCfg.PrometheusConfig,
}

if config.ScrapeConfig == nil || len(config.ScrapeConfig.ScrapeConfigs) == 0 {
return nil, errNilScrapeConfig
}
pr := &Preceiver{
cfg: &config,
consumer: consumer,
}
return pr, nil
}
45 changes: 45 additions & 0 deletions receiver/prometheusreceiver/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2019, OpenCensus 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.

package prometheusreceiver

import (
"context"
"testing"

"github.com/stretchr/testify/assert"

"github.com/open-telemetry/opentelemetry-service/internal/factories"
)

func TestCreateDefaultConfig(t *testing.T) {
factory := factories.GetReceiverFactory(typeStr)
cfg := factory.CreateDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")
}

func TestCreateReceiver(t *testing.T) {
factory := factories.GetReceiverFactory(typeStr)
cfg := factory.CreateDefaultConfig()

tReceiver, err := factory.CreateTraceReceiver(context.Background(), cfg, nil)
assert.Equal(t, err, factories.ErrDataTypeIsNotSupported)
assert.Nil(t, tReceiver)

// The default config does not provide scrape_config so we expect that metrics receiver
// creation must also fail.
mReceiver, err := factory.CreateMetricsReceiver(cfg, nil)
assert.Equal(t, err, errNilScrapeConfig)
assert.Nil(t, mReceiver)
}
22 changes: 22 additions & 0 deletions receiver/prometheusreceiver/testdata/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
receivers:
prometheus:
prometheus/customname:
endpoint: "1.2.3.4:456"
buffer_period: 234
buffer_count: 45
config:
scrape_configs:
- job_name: 'demo'
scrape_interval: 5s

processors:
exampleprocessor:

exporters:
exampleexporter:

pipelines:
traces:
receivers: [prometheus]
processors: [exampleprocessor]
exporters: [exampleexporter]

0 comments on commit 4138e51

Please sign in to comment.