Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smart Agent Receiver skeleton #53

Merged
merged 1 commit into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require (
github.com/securego/gosec/v2 v2.5.0
github.com/stretchr/testify v1.6.1
go.opentelemetry.io/collector v0.15.0
go.uber.org/zap v1.16.0
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211
honnef.co/go/tools v0.0.1-2020.1.6
)
Expand Down
40 changes: 0 additions & 40 deletions go.sum

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions internal/receiver/smartagentreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# SignalFx Smart Agent Receiver

The Smart Agent Receiver allows you to utilize existing [SignalFx Smart Agent monitors](https://github.com/signalfx/signalfx-agent#monitors)
as OpenTelemetry Collector metric receivers. It assumes that you have a properly configured environment with a
functional [Smart Agent release bundle](https://github.com/signalfx/signalfx-agent/releases/latest) on your system.

**pre-alpha: Not intended to be used and no stability or functional guarantees are made of any kind at this time.**

## Configuration

Each `smartagent` receiver configuration acts a drop-in replacement for each supported Smart Agent Monitor
[configuration](https://github.com/signalfx/signalfx-agent/blob/master/docs/monitor-config.md) with some exceptions:

1. In lieu of `discoveryRule` support, the Collector's
[`receivercreator`](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/master/receiver/receivercreator/README.md)
and associated [Observer extensions](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/master/extension/observer/README.md)
should be used.
1. All metric content replacement and transformation rules should utilize existing
[Collector processors](https://github.com/open-telemetry/opentelemetry-collector/blob/master/processor/README.md).

Example:

```yaml
receivers:
smartagent/haproxy:
type: haproxy
host: myhaproxyinstance
port: 8080
smartagent/postgresql:
type: postgresql
host: mypostgresinstance
port: 5432
```

The full list of settings exposed for this receiver are documented for
[each monitor](https://github.com/signalfx/signalfx-agent/tree/master/docs/monitors), and the implementation is
[here](./config.go)
33 changes: 33 additions & 0 deletions internal/receiver/smartagentreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2021, 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 smartagentreceiver

import (
"fmt"

"go.opentelemetry.io/collector/config/configmodels"
)

type Config struct {
configmodels.ReceiverSettings `mapstructure:",squash"`
}

func (cfg *Config) validate() error {
if cfg.NameVal == "" || cfg.TypeVal == "" {
// validation placeholder for coverage
return fmt.Errorf("name and type are required")
}
return nil
}
51 changes: 51 additions & 0 deletions internal/receiver/smartagentreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2021, 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 smartagentreceiver

import (
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/config/configtest"
)

func TestLoadConfig(t *testing.T) {
factories, err := componenttest.ExampleComponents()
assert.Nil(t, err)

factory := NewFactory()
factories.Receivers[configmodels.Type(typeStr)] = factory
cfg, err := configtest.LoadConfigFile(
t, path.Join(".", "testdata", "config.yaml"), factories,
)

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

assert.Equal(t, len(cfg.Receivers), 1)

smartAgentCfg := cfg.Receivers["smartagent"].(*Config)
require.Equal(t, &Config{
ReceiverSettings: configmodels.ReceiverSettings{
TypeVal: typeStr,
NameVal: typeStr,
},
}, smartAgentCfg)
require.NoError(t, smartAgentCfg.validate())
}
61 changes: 61 additions & 0 deletions internal/receiver/smartagentreceiver/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2021, 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 smartagentreceiver

import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/receiver/receiverhelper"
)

const (
typeStr = "smartagent"
)

func NewFactory() component.ReceiverFactory {
return receiverhelper.NewFactory(
typeStr,
CreateDefaultConfig,
receiverhelper.WithMetrics(createMetricsReceiver),
)
}

func CreateDefaultConfig() configmodels.Receiver {
return &Config{
ReceiverSettings: configmodels.ReceiverSettings{
TypeVal: typeStr,
NameVal: typeStr,
},
}
}

func createMetricsReceiver(
_ context.Context,
params component.ReceiverCreateParams,
cfg configmodels.Receiver,
consumer consumer.MetricsConsumer,
) (component.MetricsReceiver, error) {
receiverConfig := cfg.(*Config)

err := receiverConfig.validate()
if err != nil {
return nil, err
}

return NewReceiver(params.Logger, *receiverConfig, consumer), nil
}
55 changes: 55 additions & 0 deletions internal/receiver/smartagentreceiver/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2021, 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 smartagentreceiver

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configcheck"
"go.opentelemetry.io/collector/consumer/consumertest"
"go.uber.org/zap"
)

func TestCreateDefaultConfig(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")
assert.NoError(t, configcheck.ValidateConfig(cfg))
}

func TestCreateReceiver(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()

params := component.ReceiverCreateParams{Logger: zap.NewNop()}
receiver, err := factory.CreateMetricsReceiver(context.Background(), params, cfg, consumertest.NewMetricsNop())
assert.NoError(t, err)
assert.NotNil(t, receiver)
}

func TestCreateReceiverWithInvalidConfig(t *testing.T) {
factory := NewFactory()
cfg := &Config{}
require.Error(t, cfg.validate())

params := component.ReceiverCreateParams{Logger: zap.NewNop()}
receiver, err := factory.CreateMetricsReceiver(context.Background(), params, cfg, consumertest.NewMetricsNop())
assert.Error(t, err)
assert.Nil(t, receiver)
}
47 changes: 47 additions & 0 deletions internal/receiver/smartagentreceiver/receiver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2021, 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 smartagentreceiver

import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/consumer"
"go.uber.org/zap"
)

type Receiver struct {
logger *zap.Logger
config *Config
nextConsumer consumer.MetricsConsumer
}

var _ component.MetricsReceiver = (*Receiver)(nil)

func NewReceiver(logger *zap.Logger, config Config, nextConsumer consumer.MetricsConsumer) *Receiver {
return &Receiver{
logger: logger,
config: &config,
nextConsumer: nextConsumer,
}
}

func (r *Receiver) Start(_ context.Context, host component.Host) error {
return nil
}

func (r *Receiver) Shutdown(context.Context) error {
return nil
}
61 changes: 61 additions & 0 deletions internal/receiver/smartagentreceiver/receiver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2021, 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 smartagentreceiver

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/consumer/consumertest"
"go.uber.org/zap"
)

func TestSmartAgentReceiver(t *testing.T) {
type args struct {
config Config
nextConsumer consumer.MetricsConsumer
}
tests := []struct {
name string
args args
}{
{
name: "default",
args: args{
config: Config{
ReceiverSettings: configmodels.ReceiverSettings{
TypeVal: typeStr,
NameVal: typeStr + "/default",
},
},
nextConsumer: consumertest.NewMetricsNop(),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
consumer := tt.args.nextConsumer
receiver := NewReceiver(zap.NewNop(), tt.args.config, consumer)
err := receiver.Start(context.Background(), componenttest.NewNopHost())
assert.NoError(t, err)
err = receiver.Shutdown(context.Background())
assert.NoError(t, err)
})
}
}
15 changes: 15 additions & 0 deletions internal/receiver/smartagentreceiver/testdata/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
receivers:
smartagent:

processors:
exampleprocessor:

exporters:
exampleexporter:

service:
pipelines:
metrics:
receivers: [smartagent]
processors: [exampleprocessor]
exporters: [exampleexporter]