Skip to content

Commit d07e8ba

Browse files
chrroberts-puresbylica-splunk
authored andcommitted
[receiver/purefa] Pure Storage FlashArray - Purity v6.6.11+ Native OpenMetrics Support (open-telemetry#36251)
This PR implements the changes required to scrape metrics from arrays with newer Purity versions. In Pure Storage FlashArray Purity version 6.6.11, they released a feature that allows OpenMetrics to be scraped directly from the array without the use of an OpenMetrics Exporter. These changes include - Enabling support to disable TLS Insecure Skip Verify if required (default is to verify) - Enabling support for the `namespace` HTTP param that is supported by the Purity OpenMetrics exporter. #### Link to tracking issue None <!--Describe what testing was performed and which tests were added.--> #### Testing Tests have been updated for the scraper and default configs for the new Namespace and TLSInsecureSkipVerify configurations. I have tested this with a live FlashArray running 6.6.11 #### Documentation The Readme was updated to show a new example of a scrape using Purity version 6.6.11+.
1 parent 310aba1 commit d07e8ba

File tree

10 files changed

+100
-32
lines changed

10 files changed

+100
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: 'enhancement'
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: purefareceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Implements support for scraping Pure Storage FlashArray with Purity version 6.6.11+
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [36251]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

receiver/purefareceiver/README.md

+19-13
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@
1212
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
1313
<!-- end autogenerated section -->
1414

15-
The Pure Storage FlashArray receiver, receives metrics from Pure Storage internal services hosts.
15+
The Pure Storage FlashArray receiver, receives metrics from the Pure Storage FlashArray.
1616

1717
## Configuration
1818

1919
The following settings are required:
20-
- `endpoint` (default: `http://172.0.0.0:9490/metrics/array`): The URL of the scraper selected endpoint
20+
- `endpoint` (default: `http://127.0.0.0:9490/metrics/array`): The URL of the scraper selected endpoint.
21+
- `fa_array_name` (no default): The array's pretty name to be used as a metrics label.
22+
- `namespace` (default:purefa): The selected Pure Storage OpenMetrics Namespace to query.
23+
24+
In the below examples array01 is using the [Pure Storage FlashArray OpenMetrics exporter](https://github.com/PureStorage-OpenConnect/pure-fa-openmetrics-exporter), while array02 is using the native on-box metrics provided in Purity//FA v6.6.11+.
2125

2226
Example:
2327

@@ -55,15 +59,17 @@ receivers:
5559
env: dev
5660
settings:
5761
reload_intervals:
58-
array: 10s
59-
hosts: 13s
60-
directories: 15s
61-
pods: 30s
62-
volumes: 25s
62+
array: 20s
63+
hosts: 60s
64+
directories: 60s
65+
pods: 60s
66+
volumes: 60s
6367

6468
purefa/array02:
6569
fa_array_name: foobar02
66-
endpoint: http://127.0.0.1:9490/metrics
70+
endpoint: https://127.0.0.1/metrics
71+
tls:
72+
insecure_skip_verify: true
6773
array:
6874
- address: array02
6975
auth:
@@ -87,11 +93,11 @@ receivers:
8793
env: production
8894
settings:
8995
reload_intervals:
90-
array: 15s
91-
hosts: 15s
92-
directories: 15s
93-
pods: 30s
94-
volumes: 25s
96+
array: 20s
97+
hosts: 60s
98+
directories: 60s
99+
pods: 60s
100+
volumes: 60s
95101

96102
service:
97103
extensions: [bearertokenauth/array01,bearertokenauth/array02]

receiver/purefareceiver/config.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ type Config struct {
4141
// Env represents the respective environment value valid to scrape
4242
Env string `mapstructure:"env"`
4343

44-
// ArrayName represents the display name that is appended to the received metrics, as the `host` label if not provided by OpenMetrics output, and to the `fa_array_name` label always.
44+
// ArrayName represents the display name that is appended to the received metrics, as the `host` label if not provided by OpenMetrics output, and to the `fa_array_name` label always
4545
ArrayName string `mapstructure:"fa_array_name"`
46+
47+
// Namespace selects the OpenMetrics namespace requested
48+
Namespace string `mapstructure:"namespace"`
4649
}
4750

4851
type Settings struct {
@@ -63,6 +66,9 @@ func (c *Config) Validate() error {
6366
if c.ArrayName == "" {
6467
errs = multierr.Append(errs, errors.New("the array's pretty name as 'fa_array_name' must be provided"))
6568
}
69+
if c.Namespace == "" {
70+
errs = multierr.Append(errs, errors.New("a specified namespace must be provided"))
71+
}
6672
if c.Settings.ReloadIntervals.Array == 0 {
6773
errs = multierr.Append(errs, errors.New("reload interval for 'array' must be provided"))
6874
}

receiver/purefareceiver/config_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ func TestLoadConfig(t *testing.T) {
3434
expected: &Config{
3535
ClientConfig: clientConfig,
3636
ArrayName: "foobar.example.com",
37+
Namespace: "purefa",
3738
Settings: &Settings{
3839
ReloadIntervals: &ReloadIntervals{
39-
Array: 15 * time.Second,
40-
Hosts: 15 * time.Second,
41-
Directories: 15 * time.Second,
42-
Pods: 15 * time.Second,
43-
Volumes: 15 * time.Second,
40+
Array: 60 * time.Second,
41+
Hosts: 60 * time.Second,
42+
Directories: 60 * time.Second,
43+
Pods: 60 * time.Second,
44+
Volumes: 60 * time.Second,
4445
},
4546
},
4647
},

receiver/purefareceiver/factory.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ func NewFactory() receiver.Factory {
2929
func createDefaultConfig() component.Config {
3030
return &Config{
3131
ArrayName: "foobar.example.com",
32+
Namespace: "purefa",
3233
ClientConfig: confighttp.NewDefaultClientConfig(),
3334
Settings: &Settings{
3435
ReloadIntervals: &ReloadIntervals{
35-
Array: 15 * time.Second,
36-
Hosts: 15 * time.Second,
37-
Directories: 15 * time.Second,
38-
Pods: 15 * time.Second,
39-
Volumes: 15 * time.Second,
36+
Array: 60 * time.Second,
37+
Hosts: 60 * time.Second,
38+
Directories: 60 * time.Second,
39+
Pods: 60 * time.Second,
40+
Volumes: 60 * time.Second,
4041
},
4142
},
4243
}

receiver/purefareceiver/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
go.opentelemetry.io/collector/component/componenttest v0.115.1-0.20241206185113-3f3e208e71b8
1313
go.opentelemetry.io/collector/config/configauth v0.115.1-0.20241206185113-3f3e208e71b8
1414
go.opentelemetry.io/collector/config/confighttp v0.115.1-0.20241206185113-3f3e208e71b8
15+
go.opentelemetry.io/collector/config/configtls v1.21.1-0.20241206185113-3f3e208e71b8
1516
go.opentelemetry.io/collector/confmap v1.21.1-0.20241206185113-3f3e208e71b8
1617
go.opentelemetry.io/collector/consumer v1.21.1-0.20241206185113-3f3e208e71b8
1718
go.opentelemetry.io/collector/consumer/consumertest v0.115.1-0.20241206185113-3f3e208e71b8
@@ -144,7 +145,6 @@ require (
144145
go.opentelemetry.io/collector/config/configcompression v1.21.1-0.20241206185113-3f3e208e71b8 // indirect
145146
go.opentelemetry.io/collector/config/configopaque v1.21.1-0.20241206185113-3f3e208e71b8 // indirect
146147
go.opentelemetry.io/collector/config/configtelemetry v0.115.1-0.20241206185113-3f3e208e71b8 // indirect
147-
go.opentelemetry.io/collector/config/configtls v1.21.1-0.20241206185113-3f3e208e71b8 // indirect
148148
go.opentelemetry.io/collector/config/internal v0.115.1-0.20241206185113-3f3e208e71b8 // indirect
149149
go.opentelemetry.io/collector/consumer/consumererror v0.115.1-0.20241206185113-3f3e208e71b8 // indirect
150150
go.opentelemetry.io/collector/consumer/consumerprofiles v0.115.1-0.20241206185113-3f3e208e71b8 // indirect

receiver/purefareceiver/internal/scraper.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/prometheus/prometheus/config"
1515
"github.com/prometheus/prometheus/discovery"
1616
"go.opentelemetry.io/collector/component"
17+
"go.opentelemetry.io/collector/config/configtls"
1718
"go.opentelemetry.io/collector/receiver"
1819
)
1920

@@ -34,6 +35,8 @@ const (
3435
type scraper struct {
3536
scraperType ScraperType
3637
endpoint string
38+
namespace string
39+
tlsSettings configtls.ClientConfig
3740
configs []ScraperConfig
3841
scrapeInterval time.Duration
3942
labels model.LabelSet
@@ -42,13 +45,17 @@ type scraper struct {
4245
func NewScraper(_ context.Context,
4346
scraperType ScraperType,
4447
endpoint string,
48+
namespace string,
49+
tlsSettings configtls.ClientConfig,
4550
configs []ScraperConfig,
4651
scrapeInterval time.Duration,
4752
labels model.LabelSet,
4853
) Scraper {
4954
return &scraper{
5055
scraperType: scraperType,
5156
endpoint: endpoint,
57+
namespace: namespace,
58+
tlsSettings: tlsSettings,
5259
configs: configs,
5360
scrapeInterval: scrapeInterval,
5461
labels: labels,
@@ -71,17 +78,26 @@ func (h *scraper) ToPrometheusReceiverConfig(host component.Host, _ receiver.Fac
7178

7279
httpConfig := configutil.HTTPClientConfig{}
7380
httpConfig.BearerToken = configutil.Secret(bearerToken)
81+
httpConfig.TLSConfig = configutil.TLSConfig{
82+
CAFile: h.tlsSettings.CAFile,
83+
CertFile: h.tlsSettings.CertFile,
84+
KeyFile: h.tlsSettings.KeyFile,
85+
InsecureSkipVerify: h.tlsSettings.InsecureSkipVerify,
86+
ServerName: h.tlsSettings.ServerName,
87+
}
7488

7589
scrapeConfig := &config.ScrapeConfig{
7690
HTTPClientConfig: httpConfig,
91+
ScrapeProtocols: config.DefaultScrapeProtocols,
7792
ScrapeInterval: model.Duration(h.scrapeInterval),
7893
ScrapeTimeout: model.Duration(h.scrapeInterval),
7994
JobName: fmt.Sprintf("%s/%s/%s", "purefa", h.scraperType, arr.Address),
8095
HonorTimestamps: true,
8196
Scheme: u.Scheme,
8297
MetricsPath: fmt.Sprintf("/metrics/%s", h.scraperType),
8398
Params: url.Values{
84-
"endpoint": {arr.Address},
99+
"endpoint": {arr.Address},
100+
"namespace": {h.namespace},
85101
},
86102

87103
ServiceDiscoveryConfigs: discovery.Configs{

receiver/purefareceiver/internal/scraper_test.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/stretchr/testify/require"
1414
"go.opentelemetry.io/collector/component"
1515
"go.opentelemetry.io/collector/config/configauth"
16+
"go.opentelemetry.io/collector/config/configtls"
1617
"go.opentelemetry.io/collector/extension/extensiontest"
1718

1819
"github.com/open-telemetry/opentelemetry-collector-contrib/extension/bearertokenauthextension"
@@ -37,6 +38,11 @@ func TestToPrometheusConfig(t *testing.T) {
3738
}
3839

3940
endpoint := "http://example.com"
41+
namespace := "purefa"
42+
tlsSettings := configtls.ClientConfig{
43+
InsecureSkipVerify: true,
44+
ServerName: "TestThisServerName",
45+
}
4046
interval := 15 * time.Second
4147
cfgs := []ScraperConfig{
4248
{
@@ -47,15 +53,19 @@ func TestToPrometheusConfig(t *testing.T) {
4753
},
4854
}
4955

50-
scraper := NewScraper(context.Background(), "hosts", endpoint, cfgs, interval, model.LabelSet{})
56+
scraper := NewScraper(context.Background(), "hosts", endpoint, namespace, tlsSettings, cfgs, interval, model.LabelSet{})
5157

5258
// test
5359
scCfgs, err := scraper.ToPrometheusReceiverConfig(host, prFactory)
5460

5561
// verify
5662
assert.NoError(t, err)
5763
assert.Len(t, scCfgs, 1)
64+
assert.NotNil(t, scCfgs[0].ScrapeProtocols)
65+
assert.EqualValues(t, "purefa", scCfgs[0].Params.Get("namespace"))
5866
assert.EqualValues(t, "the-token", scCfgs[0].HTTPClientConfig.BearerToken)
67+
assert.True(t, scCfgs[0].HTTPClientConfig.TLSConfig.InsecureSkipVerify)
68+
assert.Equal(t, "TestThisServerName", scCfgs[0].HTTPClientConfig.TLSConfig.ServerName)
5969
assert.Equal(t, "array01", scCfgs[0].Params.Get("endpoint"))
6070
assert.Equal(t, "/metrics/hosts", scCfgs[0].MetricsPath)
6171
assert.Equal(t, "purefa/hosts/array01", scCfgs[0].JobName)

receiver/purefareceiver/receiver.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -53,35 +53,35 @@ func (r *purefaReceiver) Start(ctx context.Context, compHost component.Host) err
5353
"fa_array_name": ArrayName,
5454
}
5555

56-
arrScraper := internal.NewScraper(ctx, internal.ScraperTypeArray, r.cfg.Endpoint, r.cfg.Array, r.cfg.Settings.ReloadIntervals.Array, commomLabel)
56+
arrScraper := internal.NewScraper(ctx, internal.ScraperTypeArray, r.cfg.Endpoint, r.cfg.Namespace, r.cfg.TLSSetting, r.cfg.Array, r.cfg.Settings.ReloadIntervals.Array, commomLabel)
5757
if scCfgs, err := arrScraper.ToPrometheusReceiverConfig(compHost, fact); err == nil {
5858
scrapeCfgs = append(scrapeCfgs, scCfgs...)
5959
} else {
6060
return err
6161
}
6262

63-
hostScraper := internal.NewScraper(ctx, internal.ScraperTypeHosts, r.cfg.Endpoint, r.cfg.Hosts, r.cfg.Settings.ReloadIntervals.Hosts, labelSet)
63+
hostScraper := internal.NewScraper(ctx, internal.ScraperTypeHosts, r.cfg.Endpoint, r.cfg.Namespace, r.cfg.TLSSetting, r.cfg.Hosts, r.cfg.Settings.ReloadIntervals.Hosts, labelSet)
6464
if scCfgs, err := hostScraper.ToPrometheusReceiverConfig(compHost, fact); err == nil {
6565
scrapeCfgs = append(scrapeCfgs, scCfgs...)
6666
} else {
6767
return err
6868
}
6969

70-
directoriesScraper := internal.NewScraper(ctx, internal.ScraperTypeDirectories, r.cfg.Endpoint, r.cfg.Directories, r.cfg.Settings.ReloadIntervals.Directories, commomLabel)
70+
directoriesScraper := internal.NewScraper(ctx, internal.ScraperTypeDirectories, r.cfg.Endpoint, r.cfg.Namespace, r.cfg.TLSSetting, r.cfg.Directories, r.cfg.Settings.ReloadIntervals.Directories, commomLabel)
7171
if scCfgs, err := directoriesScraper.ToPrometheusReceiverConfig(compHost, fact); err == nil {
7272
scrapeCfgs = append(scrapeCfgs, scCfgs...)
7373
} else {
7474
return err
7575
}
7676

77-
podsScraper := internal.NewScraper(ctx, internal.ScraperTypePods, r.cfg.Endpoint, r.cfg.Pods, r.cfg.Settings.ReloadIntervals.Pods, commomLabel)
77+
podsScraper := internal.NewScraper(ctx, internal.ScraperTypePods, r.cfg.Endpoint, r.cfg.Namespace, r.cfg.TLSSetting, r.cfg.Pods, r.cfg.Settings.ReloadIntervals.Pods, commomLabel)
7878
if scCfgs, err := podsScraper.ToPrometheusReceiverConfig(compHost, fact); err == nil {
7979
scrapeCfgs = append(scrapeCfgs, scCfgs...)
8080
} else {
8181
return err
8282
}
8383

84-
volumesScraper := internal.NewScraper(ctx, internal.ScraperTypeVolumes, r.cfg.Endpoint, r.cfg.Volumes, r.cfg.Settings.ReloadIntervals.Volumes, labelSet)
84+
volumesScraper := internal.NewScraper(ctx, internal.ScraperTypeVolumes, r.cfg.Endpoint, r.cfg.Namespace, r.cfg.TLSSetting, r.cfg.Volumes, r.cfg.Settings.ReloadIntervals.Volumes, labelSet)
8585
if scCfgs, err := volumesScraper.ToPrometheusReceiverConfig(compHost, fact); err == nil {
8686
scrapeCfgs = append(scrapeCfgs, scCfgs...)
8787
} else {

receiver/purefareceiver/testdata/config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ receivers:
55

66
purefa/with_custom_intervals:
77
fa_array_name: foobar.example.com
8+
namespace: purefa
89
endpoint: http://172.31.60.208:9490/metrics
910
array:
1011
- address: array01

0 commit comments

Comments
 (0)