Skip to content

Commit

Permalink
Refactor component and factory interface definitions
Browse files Browse the repository at this point in the history
As we are preparing for Beta release we want to cleanup publicly exported
types and interfaces and do all necessary refactoring and breaking changes
now, before the Beta release. We will have a lot less leeway for breaking
changes after the Beta release.

Component related type declarations are now all in the `component` package.
This makes it possible for the interfaces to reference each other. This
was were very restricted earlier because component interfaces were in 5
different packages and many proposals were impossible to implement because
they would result in circular dependencies between packages.

(An example upcoming new capability that is enabled by this refactoring
is for components to query the Host for other components and for factories).

List of changes in this commit:

- Move all factory interfaces and component interfaces to component package.
- Rename old factories and components interfaces to use V1 suffix for clarity.
- Eliminate forced checks that components implement factories. This is already
  enforced by the compiler when the factory is added to the Defaults() and
  was unnecessary code.
- Eliminated some unnecessary codes (removed overall over 200 lines).
- Run `go mod tidy` on testbed.

Warning: this is a breaking change to publicly exported types and function
signatures. We announced that a breaking change is comming. Once we agree
to merge this commit we will need to announce the exact list of changes
and guide component authors to modify their components accordingly.

Further proposed future changes:
- Once all components are migrated to V2 internal representation, delete all
  V1 definitions and get rid of V2 suffix. This will likely be done while
  we are still in Beta phase, before the Stable release.
  • Loading branch information
Tigran Najaryan committed Mar 23, 2020
1 parent 0912b29 commit b7a73e4
Show file tree
Hide file tree
Showing 96 changed files with 651 additions and 865 deletions.
6 changes: 6 additions & 0 deletions component/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ type Host interface {
// operations.
Context() context.Context
}

// Factory interface must be implemented by all component factories.
type Factory interface {
// Type gets the type of the component created by this factory.
Type() string
}
84 changes: 50 additions & 34 deletions exporter/factory.go → component/exporter.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,60 @@
// Copyright 2019, OpenTelemetry Authors
// Copyright 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
// 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 exporter
package component

import (
"context"
"fmt"

"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-collector/config/configmodels"
"github.com/open-telemetry/opentelemetry-collector/consumer"
)

// BaseFactory defines the common functions for all exporter factories.
type BaseFactory interface {
// Type gets the type of the Exporter created by this factory.
Type() string
// Exporter defines functions that trace and metric exporters must implement.
type Exporter interface {
Component
}

// TraceExporterV1 is a TraceConsumer that is also an Exporter.
type TraceExporterV1 interface {
consumer.TraceConsumer
Exporter
}

// TraceExporterV2 is an TraceConsumerV2 that is also an Exporter.
type TraceExporterV2 interface {
consumer.TraceConsumerV2
Exporter
}

// MetricsExporterV1 is a MetricsConsumer that is also an Exporter.
type MetricsExporterV1 interface {
consumer.MetricsConsumer
Exporter
}

// MetricsExporterV2 is a MetricsConsumerV2 that is also an Exporter.
type MetricsExporterV2 interface {
consumer.MetricsConsumerV2
Exporter
}

// ExporterFactory defines the common functions for all exporter factories.
type ExporterFactory interface {
Factory

// CreateDefaultConfig creates the default configuration for the Exporter.
// This method can be called multiple times depending on the pipeline
Expand All @@ -38,50 +66,38 @@ type BaseFactory interface {
CreateDefaultConfig() configmodels.Exporter
}

// Factory can create TraceExporter and MetricsExporter.
type Factory interface {
BaseFactory
// ExporterFactoryV1 can create TraceExporterV1 and MetricsExporterV1.
type ExporterFactoryV1 interface {
ExporterFactory

// CreateTraceExporter creates a trace exporter based on this config.
CreateTraceExporter(logger *zap.Logger, cfg configmodels.Exporter) (TraceExporter, error)
CreateTraceExporter(logger *zap.Logger, cfg configmodels.Exporter) (TraceExporterV1, error)

// CreateMetricsExporter creates a metrics exporter based on this config.
CreateMetricsExporter(logger *zap.Logger, cfg configmodels.Exporter) (MetricsExporter, error)
CreateMetricsExporter(logger *zap.Logger, cfg configmodels.Exporter) (MetricsExporterV1, error)
}

// CreationParams is passed to Create* functions in FactoryV2.
type CreationParams struct {
// ExporterCreateParams is passed to ExporterFactoryV2.Create* functions.
type ExporterCreateParams struct {
// Logger that the factory can use during creation and can pass to the created
// component to be used later as well.
Logger *zap.Logger
}

// FactoryV2 can create TraceExporterV2 and MetricsExporterV2. This is the
// ExporterFactoryV2 can create TraceExporterV2 and MetricsExporterV2. This is the
// new factory type that can create new style exporters.
type FactoryV2 interface {
BaseFactory
type ExporterFactoryV2 interface {
ExporterFactory

// CreateTraceExporter creates a trace exporter based on this config.
// If the exporter type does not support tracing or if the config is not valid
// error will be returned instead.
CreateTraceExporter(ctx context.Context, params CreationParams, cfg configmodels.Exporter) (TraceExporterV2, error)
CreateTraceExporter(ctx context.Context, params ExporterCreateParams,
cfg configmodels.Exporter) (TraceExporterV2, error)

// CreateMetricsExporter creates a metrics exporter based on this config.
// If the exporter type does not support metrics or if the config is not valid
// error will be returned instead.
CreateMetricsExporter(ctx context.Context, params CreationParams, cfg configmodels.Exporter) (MetricsExporterV2, error)
}

// Build takes a list of exporter factories and returns a map of type map[string]Factory
// with factory type as keys. It returns a non-nil error when more than one factories
// have the same type.
func Build(factories ...Factory) (map[string]Factory, error) {
fMap := map[string]Factory{}
for _, f := range factories {
if _, ok := fMap[f.Type()]; ok {
return fMap, fmt.Errorf("duplicate exporter factory %q", f.Type())
}
fMap[f.Type()] = f
}
return fMap, nil
CreateMetricsExporter(ctx context.Context, params ExporterCreateParams,
cfg configmodels.Exporter) (MetricsExporterV2, error)
}
42 changes: 21 additions & 21 deletions exporter/factory_test.go → component/exporter_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// Copyright 2019, OpenTelemetry Authors
// Copyright 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
// 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 exporter
package component

import (
"testing"
Expand All @@ -23,60 +23,60 @@ import (
"github.com/open-telemetry/opentelemetry-collector/config/configmodels"
)

type TestFactory struct {
type TestExporterFactory struct {
name string
}

// Type gets the type of the Exporter config created by this factory.
func (f *TestFactory) Type() string {
func (f *TestExporterFactory) Type() string {
return f.name
}

// CreateDefaultConfig creates the default configuration for the Exporter.
func (f *TestFactory) CreateDefaultConfig() configmodels.Exporter {
func (f *TestExporterFactory) CreateDefaultConfig() configmodels.Exporter {
return nil
}

// CreateTraceExporter creates a trace exporter based on this config.
func (f *TestFactory) CreateTraceExporter(logger *zap.Logger, cfg configmodels.Exporter) (TraceExporter, error) {
func (f *TestExporterFactory) CreateTraceExporter(logger *zap.Logger, cfg configmodels.Exporter) (TraceExporterV1, error) {
return nil, nil
}

// CreateMetricsExporter creates a metrics exporter based on this config.
func (f *TestFactory) CreateMetricsExporter(logger *zap.Logger, cfg configmodels.Exporter) (MetricsExporter, error) {
func (f *TestExporterFactory) CreateMetricsExporter(logger *zap.Logger, cfg configmodels.Exporter) (MetricsExporterV1, error) {
return nil, nil
}

func TestFactoriesBuilder(t *testing.T) {
func TestBuildExporters(t *testing.T) {
type testCase struct {
in []Factory
out map[string]Factory
in []ExporterFactory
out map[string]ExporterFactory
err bool
}

testCases := []testCase{
{
in: []Factory{
&TestFactory{"exp1"},
&TestFactory{"exp2"},
in: []ExporterFactory{
&TestExporterFactory{"exp1"},
&TestExporterFactory{"exp2"},
},
out: map[string]Factory{
"exp1": &TestFactory{"exp1"},
"exp2": &TestFactory{"exp2"},
out: map[string]ExporterFactory{
"exp1": &TestExporterFactory{"exp1"},
"exp2": &TestExporterFactory{"exp2"},
},
err: false,
},
{
in: []Factory{
&TestFactory{"exp1"},
&TestFactory{"exp1"},
in: []ExporterFactory{
&TestExporterFactory{"exp1"},
&TestExporterFactory{"exp1"},
},
err: true,
},
}

for _, c := range testCases {
out, err := Build(c.in...)
out, err := MakeExporterFactoryMap(c.in...)
if c.err {
assert.NotNil(t, err)
continue
Expand Down
34 changes: 26 additions & 8 deletions extension/extension.go → component/extension.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
// Copyright 2019, OpenTelemetry Authors
// Copyright 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
// 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 extension defines service extensions that can be added to the OpenTelemetry
// service but that not interact if the data pipelines, but provide some functionality
// to the service, examples: health check endpoint, z-pages, etc.
package extension
package component

import (
"go.uber.org/zap"

import "github.com/open-telemetry/opentelemetry-collector/component"
"github.com/open-telemetry/opentelemetry-collector/config/configmodels"
)

// ServiceExtension is the interface for objects hosted by the OpenTelemetry Collector that
// don't participate directly on data pipelines but provide some functionality
// to the service, examples: health check endpoint, z-pages, etc.
type ServiceExtension interface {
component.Component
Component
}

// PipelineWatcher is an extra interface for ServiceExtension hosted by the OpenTelemetry
Expand All @@ -42,3 +43,20 @@ type PipelineWatcher interface {
// appropriate action before that happens.
NotReady() error
}

// ExtensionFactory is a factory interface for extensions to the service.
type ExtensionFactory interface {
Factory

// CreateDefaultConfig creates the default configuration for the Extension.
// This method can be called multiple times depending on the pipeline
// configuration and should not cause side-effects that prevent the creation
// of multiple instances of the Extension.
// The object returned by this method needs to pass the checks implemented by
// 'configcheck.ValidateConfig'. It is recommended to have such check in the
// tests of any implementation of the Factory interface.
CreateDefaultConfig() configmodels.Extension

// CreateExtension creates a service extension based on the given config.
CreateExtension(logger *zap.Logger, cfg configmodels.Extension) (ServiceExtension, error)
}
73 changes: 73 additions & 0 deletions component/factory_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 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.

package component

import "fmt"

// MakeReceiverFactoryMap takes a list of receiver factories and returns a map
// with factory type as keys. It returns a non-nil error when more than one factories
// have the same type.
func MakeReceiverFactoryMap(factories ...ReceiverFactory) (map[string]ReceiverFactory, error) {
fMap := map[string]ReceiverFactory{}
for _, f := range factories {
if _, ok := fMap[f.Type()]; ok {
return fMap, fmt.Errorf("duplicate receiver factory %q", f.Type())
}
fMap[f.Type()] = f
}
return fMap, nil
}

// MakeProcessorFactoryMap takes a list of processor factories and returns a map
// with factory type as keys. It returns a non-nil error when more than one factories
// have the same type.
func MakeProcessorFactoryMap(factories ...ProcessorFactory) (map[string]ProcessorFactory, error) {
fMap := map[string]ProcessorFactory{}
for _, f := range factories {
if _, ok := fMap[f.Type()]; ok {
return fMap, fmt.Errorf("duplicate processor factory %q", f.Type())
}
fMap[f.Type()] = f
}
return fMap, nil
}

// MakeExporterFactoryMap takes a list of exporter factories and returns a map
// with factory type as keys. It returns a non-nil error when more than one factories
// have the same type.
func MakeExporterFactoryMap(factories ...ExporterFactory) (map[string]ExporterFactory, error) {
fMap := map[string]ExporterFactory{}
for _, f := range factories {
if _, ok := fMap[f.Type()]; ok {
return fMap, fmt.Errorf("duplicate exporter factory %q", f.Type())
}
fMap[f.Type()] = f
}
return fMap, nil
}

// MakeExtensionFactoryMap takes a list of extension factories and returns a map
// with factory type as keys. It returns a non-nil error when more than one factories
// have the same type.
func MakeExtensionFactoryMap(factories ...ExtensionFactory) (map[string]ExtensionFactory, error) {
fMap := map[string]ExtensionFactory{}
for _, f := range factories {
if _, ok := fMap[f.Type()]; ok {
return fMap, fmt.Errorf("duplicate extension factory %q", f.Type())
}
fMap[f.Type()] = f
}
return fMap, nil
}
Loading

0 comments on commit b7a73e4

Please sign in to comment.