Skip to content

Commit 5b56ad3

Browse files
authored
Few extra improvements in ExportModes (#403)
* Few extra improvements in ExportMetrics * add test case for undefined vs nil value
1 parent 83757ee commit 5b56ad3

File tree

7 files changed

+71
-60
lines changed

7 files changed

+71
-60
lines changed

pkg/components/discover/typer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func samplerFromConfig(s *services.SamplerConfig) trace.Sampler {
9797
func makeServiceAttrs(processMatch *ProcessMatch) svc.Attrs {
9898
var name string
9999
var namespace string
100-
var exportModes *services.ExportModes
100+
exportModes := services.ExportModeUnset
101101
var samplerConfig *services.SamplerConfig
102102

103103
for _, s := range processMatch.Criteria {
@@ -109,7 +109,7 @@ func makeServiceAttrs(processMatch *ProcessMatch) svc.Attrs {
109109
namespace = n
110110
}
111111

112-
if m := s.GetExportModes(); m != nil {
112+
if m := s.GetExportModes(); m != services.ExportModeUnset {
113113
exportModes = m
114114
}
115115

pkg/components/svc/svc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ type Attrs struct {
100100

101101
flags idFlags
102102

103-
ExportModes *services.ExportModes
103+
ExportModes services.ExportModes
104104

105105
Sampler trace.Sampler
106106
}

pkg/services/attr_glob.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ type GlobAttributes struct {
7777
// Configures what to export. Allowed values are 'metrics', 'traces',
7878
// or an empty array (disabled). An unspecified value (nil) will use the
7979
// default configuration value
80-
ExportModes *ExportModes `yaml:"exports"`
80+
ExportModes ExportModes `yaml:"exports"`
8181

8282
SamplerConfig *SamplerConfig `yaml:"sampler"`
8383
}
@@ -177,7 +177,7 @@ func (ga *GlobAttributes) RangePodAnnotations() iter.Seq2[string, StringMatcher]
177177
}
178178
}
179179

180-
func (ga *GlobAttributes) GetExportModes() *ExportModes { return ga.ExportModes }
180+
func (ga *GlobAttributes) GetExportModes() ExportModes { return ga.ExportModes }
181181

182182
func (ga *GlobAttributes) GetSamplerConfig() *SamplerConfig { return ga.SamplerConfig }
183183

pkg/services/attr_regex.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ type RegexSelector struct {
8383
// Configures what to export. Allowed values are 'metrics', 'traces',
8484
// or an empty array (disabled). An unspecified value (nil) will use the
8585
// default configuration value
86-
ExportModes *ExportModes `yaml:"exports"`
86+
ExportModes ExportModes `yaml:"exports"`
8787

8888
SamplerConfig *SamplerConfig `yaml:"sampler"`
8989
}
@@ -181,6 +181,6 @@ func (a *RegexSelector) RangePodAnnotations() iter.Seq2[string, StringMatcher] {
181181
}
182182
}
183183

184-
func (a *RegexSelector) GetExportModes() *ExportModes { return a.ExportModes }
184+
func (a *RegexSelector) GetExportModes() ExportModes { return a.ExportModes }
185185

186186
func (a *RegexSelector) GetSamplerConfig() *SamplerConfig { return a.SamplerConfig }

pkg/services/criteria.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ type Selector interface {
131131
RangeMetadata() iter.Seq2[string, StringMatcher]
132132
RangePodLabels() iter.Seq2[string, StringMatcher]
133133
RangePodAnnotations() iter.Seq2[string, StringMatcher]
134-
GetExportModes() *ExportModes
134+
GetExportModes() ExportModes
135135
GetSamplerConfig() *SamplerConfig
136136
}
137137

pkg/services/export.go

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,61 @@ import (
1212
)
1313

1414
const (
15-
exportMetrics = maps.Bits(1 << iota)
16-
exportTraces
15+
// if the corresponding bit is set to 1, this means that this signal
16+
// is not going to be emitted
17+
blockMetrics = maps.Bits(1 << iota)
18+
blockTraces
1719
)
1820

1921
var modeForText = map[string]maps.Bits{
20-
"metrics": exportMetrics,
21-
"traces": exportTraces,
22+
"metrics": blockMetrics,
23+
"traces": blockTraces,
2224
}
2325

24-
type ExportModes struct {
25-
items maps.Bits
26-
}
26+
const (
27+
// the zero-value of ExportModes (blockSignal == 0) means that the value is unset.
28+
// This is, all the signals are allowed.
29+
// In the corresponding YAML is a null or undefined value
30+
unset = maps.Bits(0)
31+
// the -1 (all bits to one) value of ExportModes means that the value is set to
32+
// an empty set: all the signals are blocked.
33+
// In the corresponding YAML it's an empty sequence []
34+
blockAll = maps.Bits(^uint(0))
35+
)
2736

28-
func (modes *ExportModes) canExport(mode maps.Bits) bool {
29-
if modes == nil {
30-
return true
31-
}
32-
return modes.items.Has(mode)
37+
// ExportModeUnset corresponds to an undefined export mode in the configuration YAML
38+
// (null or undefined value). This means that all the signals (traces, metrics) are
39+
// going to be exported
40+
var ExportModeUnset = ExportModes{blockSignal: unset}
41+
42+
// ExportModes specifies which signals are going to be exported for a given service,
43+
// via the public methods CanExportTraces and CanExportMetrics.
44+
// Internally, it has three modes of operation depending on how it is defined in the YAML:
45+
// - When it is undefined or null in the YAML, it will allow exporting all the signals
46+
// (as no blocking signals are defined)
47+
// - When it is defined as an empty list in the YAML, it will block all the signals. No
48+
// metrics nor traces are exported.
49+
// - When it is defined as a non-empty list, it will only allow the explicitly specified signals.
50+
type ExportModes struct {
51+
blockSignal maps.Bits
3352
}
3453

3554
// CanExportTraces reports whether traces can be exported.
3655
// It's provided as a convenience function.
37-
func (modes *ExportModes) CanExportTraces() bool {
38-
return modes.canExport(exportTraces)
56+
func (modes ExportModes) CanExportTraces() bool {
57+
return !modes.blockSignal.Has(blockTraces)
3958
}
4059

4160
// CanExportMetrics reports whether metrics can be exported.
4261
// It's provided as a convenience function.
43-
func (modes *ExportModes) CanExportMetrics() bool {
44-
return modes.canExport(exportMetrics)
62+
func (modes ExportModes) CanExportMetrics() bool {
63+
return !modes.blockSignal.Has(blockMetrics)
4564
}
4665

4766
func (modes *ExportModes) UnmarshalYAML(value *yaml.Node) error {
67+
// by default, everything is blocked, and we will unblock each signal
68+
// as long as we parse them in the YAML
69+
modes.blockSignal = blockAll
4870
if value.Kind != yaml.SequenceNode {
4971
return fmt.Errorf("ExportModes: unexpected YAML node kind %d", value.Kind)
5072
}
@@ -55,33 +77,27 @@ func (modes *ExportModes) UnmarshalYAML(value *yaml.Node) error {
5577
if mode, ok := modeForText[inner.Value]; !ok {
5678
return fmt.Errorf("ExportModes[%d]: unknown export mode %q", i, inner.Value)
5779
} else {
58-
modes.items |= mode
80+
// a given signal is defined. Remove it from the blocking list
81+
modes.blockSignal ^= mode
5982
}
6083
}
61-
return modes.UnmarshalText([]byte(value.Value))
62-
}
63-
64-
func (modes *ExportModes) UnmarshalText(text []byte) error {
65-
var options []string
66-
if err := yaml.Unmarshal(text, &options); err != nil {
67-
return fmt.Errorf("invalid export_modes: %w", err)
68-
}
69-
if options == nil {
70-
return nil
71-
}
72-
modes.items = maps.MappedBits(options, modeForText)
7384
return nil
7485
}
7586

76-
func (modes *ExportModes) MarshalYAML() (any, error) {
77-
if modes == nil {
87+
func (modes ExportModes) MarshalYAML() (any, error) {
88+
if modes.blockSignal == unset {
7889
return nil, nil
7990
}
8091
node := yaml.Node{
8192
Kind: yaml.SequenceNode,
8293
}
94+
// return an empty sequence, in opposition of "null" in the unset case
95+
if modes.blockSignal == blockAll {
96+
return node, nil
97+
}
8398
for text, mode := range modeForText {
84-
if modes.items.Has(mode) {
99+
// the given signal is not explicitly blocked, so we can list it as allowed
100+
if !modes.blockSignal.Has(mode) {
85101
node.Content = append(node.Content, &yaml.Node{
86102
Kind: yaml.ScalarNode,
87103
Value: text,
@@ -90,16 +106,3 @@ func (modes *ExportModes) MarshalYAML() (any, error) {
90106
}
91107
return node, nil
92108
}
93-
94-
func (modes *ExportModes) MarshalText() ([]byte, error) {
95-
var options []string
96-
if modes != nil {
97-
options = make([]string, 0, len(modeForText))
98-
for text, mode := range modeForText {
99-
if modes.items.Has(mode) {
100-
options = append(options, text)
101-
}
102-
}
103-
}
104-
return yaml.Marshal(options)
105-
}

pkg/services/export_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,32 @@ import (
1313

1414
func TestYAMLMarshal_Exports(t *testing.T) {
1515
type tc struct {
16-
Exports *ExportModes
16+
Exports ExportModes
1717
}
1818
t.Run("nil value", func(t *testing.T) {
1919
yamlOut, err := yaml.Marshal(&tc{
20-
Exports: nil,
20+
Exports: ExportModes{},
2121
})
2222
require.NoError(t, err)
2323
assert.YAMLEq(t, `exports: null`, string(yamlOut))
2424
})
2525
t.Run("empty value", func(t *testing.T) {
2626
yamlOut, err := yaml.Marshal(&tc{
27-
Exports: &ExportModes{},
27+
Exports: ExportModes{blockSignal: blockAll},
2828
})
2929
require.NoError(t, err)
3030
assert.YAMLEq(t, `exports: []`, string(yamlOut))
3131
})
3232
t.Run("some value", func(t *testing.T) {
3333
yamlOut, err := yaml.Marshal(&tc{
34-
Exports: &ExportModes{items: exportMetrics},
34+
Exports: ExportModes{blockSignal: ^blockMetrics},
3535
})
3636
require.NoError(t, err)
3737
assert.YAMLEq(t, `exports: ["metrics"]`, string(yamlOut))
3838
})
3939
t.Run("all values", func(t *testing.T) {
4040
yamlOut, err := yaml.Marshal(&tc{
41-
Exports: &ExportModes{items: exportMetrics | exportTraces},
41+
Exports: ExportModes{blockSignal: ^(blockMetrics | blockTraces)},
4242
})
4343
require.NoError(t, err)
4444
assert.YAMLEq(t, `exports: ["metrics", "traces"]`, string(yamlOut))
@@ -47,13 +47,21 @@ func TestYAMLMarshal_Exports(t *testing.T) {
4747

4848
func TestYAMLUnmarshal_Exports(t *testing.T) {
4949
type tc struct {
50-
Exports *ExportModes
50+
Exports ExportModes
5151
}
52+
t.Run("undefined value", func(t *testing.T) {
53+
var tc tc
54+
err := yaml.Unmarshal([]byte(``), &tc)
55+
require.NoError(t, err)
56+
assert.True(t, tc.Exports.CanExportMetrics())
57+
assert.True(t, tc.Exports.CanExportTraces())
58+
})
5259
t.Run("nil value", func(t *testing.T) {
5360
var tc tc
5461
err := yaml.Unmarshal([]byte(`exports: null`), &tc)
5562
require.NoError(t, err)
56-
assert.Nil(t, tc.Exports)
63+
assert.True(t, tc.Exports.CanExportMetrics())
64+
assert.True(t, tc.Exports.CanExportTraces())
5765
})
5866
t.Run("empty value", func(t *testing.T) {
5967
var tc tc

0 commit comments

Comments
 (0)