Skip to content

Commit ceec9c7

Browse files
authored
Cloud Foundry metrics receiver #1 - config, factory, docs (#4626)
Adds a Cloud Foundry metric receiver which reads metrics from Cloud Foundry Reverse Log Proxy Gateway. More details available in the `README.md`. `make gotidy` seems to have made plenty of subtle changes to `go.sum` files, not sure if this is normal. This PR contains the overall structure, documentation, implementation for config and factory, but does NOT contain the implementation of the receiver and does not enable the component, as that will come in separate PRs later. **Link to tracking Issue:** #5320 **Testing:** Unit tests. Manual testing was performed against Tanzu Application Service (TAS) versions 2.7, 2.8 and 2.11. Considered adding an integration test with mocked HTTP servers acting as endpoints where the HTTP server would provide a constant response (prerecorded from the real TAS traffic), but not sure if mocks would make more sense. **Documentation:** `README.md` and `doc.go` for the new receiver module were added.
1 parent 5fcb6dc commit ceec9c7

File tree

14 files changed

+2327
-0
lines changed

14 files changed

+2327
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ receiver/awscontainerinsightreceiver/ @open-telemetry/collector-c
6969
receiver/awsecscontainermetricsreceiver/ @open-telemetry/collector-contrib-approvers @kbrockhoff @anuraaga
7070
receiver/awsxrayreceiver/ @open-telemetry/collector-contrib-approvers @kbrockhoff @anuraaga
7171
receiver/carbonreceiver/ @open-telemetry/collector-contrib-approvers @pjanotti
72+
receiver/cloudfoundryreceiver/ @open-telemetry/collector-contrib-approvers @agoallikmaa @pellared
7273
receiver/collectdreceiver/ @open-telemetry/collector-contrib-approvers @owais
7374
receiver/dockerstatsreceiver/ @open-telemetry/collector-contrib-approvers @rmfitzpatrick
7475
receiver/dotnetdiagnosticsreceiver/ @open-telemetry/collector-contrib-approvers @pmcollins @davmason

.github/dependabot.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ updates:
401401
directory: "/receiver/carbonreceiver"
402402
schedule:
403403
interval: "weekly"
404+
- package-ecosystem: "gomod"
405+
directory: "/receiver/cloudfoundryreceiver"
406+
schedule:
407+
interval: "weekly"
404408
- package-ecosystem: "gomod"
405409
directory: "/receiver/collectdreceiver"
406410
schedule:

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxr
559559

560560
replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/carbonreceiver => ./receiver/carbonreceiver
561561

562+
replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/cloudfoundryreceiver => ./receiver/cloudfoundryreceiver
563+
562564
replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/collectdreceiver => ./receiver/collectdreceiver
563565

564566
replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/dotnetdiagnosticsreceiver => ./receiver/dotnetdiagnosticsreceiver
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include ../../Makefile.Common
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Cloud Foundry Receiver
2+
3+
The Cloud Foundry receiver connects to the RLP (Reverse Log Proxy) Gateway of the Cloud Foundry installation, typically
4+
available at the URL `https://log-stream.<cf-system-domain>`.
5+
6+
## Authentication
7+
8+
RLP Gateway authentication is performed by adding the Oauth2 token as the `Authorization` header. To obtain an OAuth2
9+
token to use for the RLP Gateway, a request is made to the UAA component which acts as the OAuth2 provider (URL
10+
specified by `uaa_url` configuration option, which typically is `https://uaa.<cf-system-domain>`). To authenticate with
11+
UAA, username and password/secret combination is used (`uaa_username` and `uaa_password` configuration options). This
12+
UAA user must have the `client_credentials` and `refresh_token` authorized grant types, and `logs.admin` authority.
13+
14+
The following is an example sequence of commands to create the UAA user using the `uaac` command line utility:
15+
16+
* `uaac target https://uaa.<cf-system-domain>`
17+
* `uaac token client get identity -s <identity-user-secret>`
18+
* `uaac client add <uaa_username> --name opentelemetry --secret <uaa_password> --authorized_grant_types client_credentials,refresh_token --authorities logs.admin`
19+
20+
The `<uaa_username>` and `<uaa_password>` above can be set to anything as long as they match the values provided to the
21+
receiver configuration. The admin account (which is `identity` here) which has the permissions to create new clients may
22+
have a different name on different setups. The value of `--name` is not used for receiver configuration.
23+
24+
## Configuration
25+
26+
The receiver takes the following configuration options:
27+
28+
| Field | Default | Description |
29+
| --- | --- | --- |
30+
| `rlp_gateway_url` | required | URL of the RLP gateway, typically `https://log-stream.<cf-system-domain>` |
31+
| `rlp_gateway_skip_tls_verify` | `false` | whether to skip TLS verify for the RLP Gateway endpoint |
32+
| `rlp_gateway_shard_id` | `opentelemetry` | metrics are load balanced among receivers that use the same shard ID, therefore this must only be set if there are multiple receivers which must both receive all the metrics instead of them being balanced between them |
33+
| `uaa_url` | required | URL of the UAA provider, typically `https://uaa.<cf-system-domain>` |
34+
| `uaa_skip_tls_verify` | `false` | whether to skip TLS verify for the UAA endpoint |
35+
| `uaa_username` | required | name of the UAA user (required grant types/authorities described above) |
36+
| `uaa_password` | required | password of the UAA user |
37+
| `http_timeout` | `10s` | HTTP socket timeout used for RLP Gateway |
38+
39+
Example:
40+
41+
```yaml
42+
receivers:
43+
cloudfoundry:
44+
rlp_gateway_url: "https://log-stream.sys.example.internal"
45+
rlp_gateway_skip_tls_verify: false
46+
rlp_gateway_shard_id: "opentelemetry"
47+
uaa_url: "https://uaa.sys.example.internal"
48+
uaa_skip_tls_verify: false
49+
uaa_username: "otelclient"
50+
uaa_password: "changeit"
51+
http_timeout: "20s"
52+
```
53+
54+
The full list of settings exposed for this receiver are documented [here](./config.go)
55+
with detailed sample configurations [here](./testdata/config.yaml).
56+
57+
## Metrics
58+
59+
Reported metrics are grouped under an instrumentation library named `otelcol/cloudfoundry`. Metric names are as
60+
specified by [Cloud Foundry metrics documentation](https://docs.cloudfoundry.org/running/all_metrics.html), but the
61+
origin name is prepended to the metric name with `.` separator. All metrics either of type `Gauge` or `Sum`.
62+
63+
### Attributes
64+
65+
All the metrics have the following attributes:
66+
* `origin` - origin name as documented by Cloud Foundry
67+
* `source` - for applications, the GUID of the application, otherwise equal to `origin`
68+
69+
For CF/TAS deployed in BOSH, the following attributes are also present, using their canonical BOSH meanings:
70+
* `deployment` - BOSH deployment name
71+
* `index` - BOSH instance ID (GUID)
72+
* `ip` - BOSH instance IP
73+
* `job` - BOSH job name
74+
75+
For metrics originating with `rep` origin name (specific to applications), the following metrics are present:
76+
* `instance_id` - numerical index of the application instance. However, also present for `bbs` origin, where it matches
77+
the value of `index`
78+
* `process_id` - process ID (GUID). For a process of type "web" which is the main process of an application, this is
79+
equal to `source_id` and `app_id`
80+
* `process_instance_id` - unique ID of a process instance, should be treated as an opaque string
81+
* `process_type` - process type. Each application has exactly one process of type `web`, but many have any number of
82+
other processes
83+
84+
On TAS/PCF versions 2.8.0+ and cf-deployment versions v11.1.0+, the following additional attributes are present for
85+
application metrics: `app_id`, `app_name`, `space_id`, `space_name`, `organization_id`, `organization_name` which
86+
provide the GUID and name of application, space and organization respectively.
87+
88+
This might not be a comprehensive list of attributes, as the receiver passes on whatever attributes the gateway
89+
provides, which may include some that are specific to TAS and possibly new ones in future Cloud Foundry versions as
90+
well.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2019, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cloudfoundryreceiver
16+
17+
import (
18+
"fmt"
19+
"net/url"
20+
"time"
21+
22+
"go.opentelemetry.io/collector/config"
23+
)
24+
25+
// Config defines configuration for Collectd receiver.
26+
type Config struct {
27+
config.ReceiverSettings `mapstructure:",squash"`
28+
29+
RLPGatewayURL string `mapstructure:"rlp_gateway_url"`
30+
RLPGatewaySkipTLSVerify bool `mapstructure:"rlp_gateway_skip_tls_verify"`
31+
RLPGatewayShardID string `mapstructure:"rlp_gateway_shard_id"`
32+
UAAUrl string `mapstructure:"uaa_url"`
33+
UAASkipTLSVerify bool `mapstructure:"uaa_skip_tls_verify"`
34+
UAAUsername string `mapstructure:"uaa_username"`
35+
UAAPassword string `mapstructure:"uaa_password"`
36+
HTTPTimeout time.Duration `mapstructure:"http_timeout"`
37+
}
38+
39+
func (c *Config) Validate() error {
40+
err := validateURLOption("rlp_gateway_url", c.RLPGatewayURL)
41+
if err != nil {
42+
return err
43+
}
44+
45+
err = validateURLOption("uaa_url", c.UAAUrl)
46+
if err != nil {
47+
return err
48+
}
49+
50+
if c.UAAUsername == "" {
51+
return fmt.Errorf("username not specified")
52+
}
53+
54+
return nil
55+
}
56+
57+
func validateURLOption(name string, value string) error {
58+
if value == "" {
59+
return fmt.Errorf("%s not specified", name)
60+
}
61+
62+
_, err := url.Parse(value)
63+
if err != nil {
64+
return fmt.Errorf("failed to parse %s as url: %v", name, err)
65+
}
66+
67+
return nil
68+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2019, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cloudfoundryreceiver
16+
17+
import (
18+
"path"
19+
"testing"
20+
"time"
21+
22+
"github.com/stretchr/testify/assert"
23+
"github.com/stretchr/testify/require"
24+
"go.opentelemetry.io/collector/component/componenttest"
25+
"go.opentelemetry.io/collector/config"
26+
"go.opentelemetry.io/collector/config/configtest"
27+
)
28+
29+
// todo implement - implement negative tests in stage 2 PR
30+
func TestLoadConfig(t *testing.T) {
31+
factories, err := componenttest.NopFactories()
32+
require.NoError(t, err)
33+
34+
factory := NewFactory()
35+
factories.Receivers[typeStr] = factory
36+
cfg, err := configtest.LoadConfigAndValidate(path.Join(".", "testdata", "config.yaml"), factories)
37+
38+
require.NoError(t, err)
39+
require.NotNil(t, cfg)
40+
41+
require.Len(t, cfg.Receivers, 2)
42+
43+
r0 := cfg.Receivers[config.NewID(typeStr)]
44+
assert.Equal(t, factory.CreateDefaultConfig(), r0)
45+
46+
r1 := cfg.Receivers[config.NewIDWithName(typeStr, "one")].(*Config)
47+
assert.Equal(t,
48+
&Config{
49+
ReceiverSettings: config.NewReceiverSettings(config.NewIDWithName(typeStr, "one")),
50+
RLPGatewayURL: "https://log-stream.sys.example.internal",
51+
RLPGatewaySkipTLSVerify: true,
52+
RLPGatewayShardID: "otel-test",
53+
UAAUrl: "https://uaa.sys.example.internal",
54+
UAASkipTLSVerify: true,
55+
UAAUsername: "admin",
56+
UAAPassword: "test",
57+
HTTPTimeout: time.Second * 20,
58+
}, r1)
59+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2019, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package cloudfoundryreceiver implements a receiver that can be used by the
16+
// Opentelemetry collector to receive Cloud Foundry metrics via its Reverse
17+
// Log Proxy (RLP) Gateway component. The protocol is handled by the
18+
// go-loggregator library, which uses HTTP to connect to the gateway and receive
19+
// JSON-protobuf encoded v2 Envelope messages as documented by loggregator-api.
20+
package cloudfoundryreceiver
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2019, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cloudfoundryreceiver
16+
17+
import (
18+
"context"
19+
20+
"go.opentelemetry.io/collector/component"
21+
"go.opentelemetry.io/collector/config"
22+
"go.opentelemetry.io/collector/consumer"
23+
"go.opentelemetry.io/collector/receiver/receiverhelper"
24+
)
25+
26+
// This file implements factory for Cloud Foundry receiver.
27+
28+
const (
29+
typeStr = "cloudfoundry"
30+
defaultUAAUsername = "admin"
31+
defaultRLPGatewayShardID = "opentelemetry"
32+
defaultURL = "https://localhost"
33+
)
34+
35+
// NewFactory creates a factory for collectd receiver.
36+
func NewFactory() component.ReceiverFactory {
37+
return receiverhelper.NewFactory(
38+
typeStr,
39+
createDefaultConfig,
40+
receiverhelper.WithMetrics(createMetricsReceiver))
41+
}
42+
43+
func createDefaultConfig() config.Receiver {
44+
return &Config{
45+
ReceiverSettings: config.NewReceiverSettings(config.NewID(typeStr)),
46+
RLPGatewayURL: defaultURL,
47+
RLPGatewaySkipTLSVerify: false,
48+
RLPGatewayShardID: defaultRLPGatewayShardID,
49+
UAAUsername: defaultUAAUsername,
50+
UAAPassword: "",
51+
UAAUrl: defaultURL,
52+
UAASkipTLSVerify: false,
53+
}
54+
}
55+
56+
func createMetricsReceiver(
57+
_ context.Context,
58+
params component.ReceiverCreateSettings,
59+
cfg config.Receiver,
60+
nextConsumer consumer.Metrics,
61+
) (component.MetricsReceiver, error) {
62+
c := cfg.(*Config)
63+
return newCloudFoundryReceiver(params.Logger, *c, nextConsumer)
64+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2019, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cloudfoundryreceiver
16+
17+
import (
18+
"context"
19+
"testing"
20+
21+
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
23+
"go.opentelemetry.io/collector/component/componenttest"
24+
"go.opentelemetry.io/collector/config/configcheck"
25+
"go.opentelemetry.io/collector/consumer/consumertest"
26+
)
27+
28+
func TestCreateDefaultConfig(t *testing.T) {
29+
factory := NewFactory()
30+
cfg := factory.CreateDefaultConfig()
31+
require.NotNil(t, cfg, "failed to create default config")
32+
assert.NoError(t, configcheck.ValidateConfig(cfg))
33+
}
34+
35+
func TestCreateReceiver(t *testing.T) {
36+
factory := NewFactory()
37+
cfg := factory.CreateDefaultConfig()
38+
39+
params := componenttest.NewNopReceiverCreateSettings()
40+
tReceiver, err := factory.CreateMetricsReceiver(context.Background(), params, cfg, consumertest.NewNop())
41+
assert.NoError(t, err)
42+
assert.NotNil(t, tReceiver, "receiver creation failed")
43+
}

0 commit comments

Comments
 (0)