Skip to content

Commit

Permalink
Refactor component and factory interface definitions (#683)
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 "Old" 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.

Future changes:
- Once all components are migrated to the new internal representation,
  delete all "Old"-suffixed definitions. This will likely be done while
  we are still in Beta phase, before the Stable release.
  • Loading branch information
tigrannajaryan authored Mar 25, 2020
1 parent 9b61b53 commit c931b98
Show file tree
Hide file tree
Showing 121 changed files with 883 additions and 1,098 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
}

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

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

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

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

// ExporterFactoryBase defines the common functions for all exporter factories.
type ExporterFactoryBase 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
// ExporterFactoryOld can create TraceExporterOld and MetricsExporterOld.
type ExporterFactoryOld interface {
ExporterFactoryBase

// 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) (TraceExporterOld, 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) (MetricsExporterOld, error)
}

// CreationParams is passed to Create* functions in FactoryV2.
type CreationParams struct {
// ExporterCreateParams is passed to ExporterFactory.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
// ExporterFactory can create TraceExporter and MetricsExporter. This is the
// new factory type that can create new style exporters.
type FactoryV2 interface {
BaseFactory
type ExporterFactory interface {
ExporterFactoryBase

// 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) (TraceExporter, 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) (MetricsExporter, 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) (TraceExporterOld, 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) (MetricsExporterOld, error) {
return nil, nil
}

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

testCases := []testCase{
{
in: []Factory{
&TestFactory{"exp1"},
&TestFactory{"exp2"},
in: []ExporterFactoryBase{
&TestExporterFactory{"exp1"},
&TestExporterFactory{"exp2"},
},
out: map[string]Factory{
"exp1": &TestFactory{"exp1"},
"exp2": &TestFactory{"exp2"},
out: map[string]ExporterFactoryBase{
"exp1": &TestExporterFactory{"exp1"},
"exp2": &TestExporterFactory{"exp2"},
},
err: false,
},
{
in: []Factory{
&TestFactory{"exp1"},
&TestFactory{"exp1"},
in: []ExporterFactoryBase{
&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 ...ReceiverFactoryBase) (map[string]ReceiverFactoryBase, error) {
fMap := map[string]ReceiverFactoryBase{}
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 ...ProcessorFactoryBase) (map[string]ProcessorFactoryBase, error) {
fMap := map[string]ProcessorFactoryBase{}
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 ...ExporterFactoryBase) (map[string]ExporterFactoryBase, error) {
fMap := map[string]ExporterFactoryBase{}
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 c931b98

Please sign in to comment.