Skip to content

Commit a97e864

Browse files
author
Gabriel Silva Bueno
committed
Adds opt-in / opt-out
Adds a opt-in / opt-out mechanism configurable via SUSEConnect for the profile generating collectors
1 parent ba6eca7 commit a97e864

File tree

5 files changed

+247
-4
lines changed

5 files changed

+247
-4
lines changed

internal/collectors/config.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package collectors
2+
3+
import "github.com/SUSE/connect-ng/pkg/profiles"
4+
5+
// defaultCollectorStates defines the default behavior per collector.
6+
// true = opt-out (enabled by default), false = opt-in (disabled by default)
7+
var defaultCollectorStates = map[string]bool{
8+
"pci_devices": true,
9+
"kernel_modules": true,
10+
// Add other collectors here to define their default behavior
11+
}
12+
13+
// DefaultCollectorState safely returns the default state for a given collector.
14+
func DefaultCollectorState(collectorName string) bool {
15+
if state, ok := defaultCollectorStates[collectorName]; ok {
16+
return state
17+
}
18+
return false // Fallback if not found in defaults map
19+
}
20+
21+
// CollectorOptions implements profiles.CollectorOptions interface
22+
// This struct holds the actual collector configuration data
23+
type CollectorOptions struct {
24+
collectors map[string]profiles.CollectorConfig
25+
}
26+
27+
func NewCollectorOptions(collectors map[string]profiles.CollectorConfig) *CollectorOptions {
28+
return &CollectorOptions{
29+
collectors: collectors,
30+
}
31+
}
32+
33+
func (c *CollectorOptions) GetCollectorConfig(collectorName string) *profiles.CollectorConfig {
34+
if config, ok := c.collectors[collectorName]; ok {
35+
return &config
36+
}
37+
38+
return nil
39+
}
40+
41+
func (c *CollectorOptions) IsCollectorEnabled(collectorName string) bool {
42+
config := c.GetCollectorConfig(collectorName)
43+
if config == nil {
44+
return DefaultCollectorState(collectorName)
45+
}
46+
47+
return config.Enabled
48+
}

internal/connect/collectors.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,20 @@ func FetchSystemInformation(arch string) (collectors.Result, error) {
3636

3737
// Fetch system profile information
3838
func FetchSystemProfiles(arch string, updateCache bool) (collectors.Result, error) {
39-
var usedCollectors = []collectors.Collector{
40-
collectors.PCI{UpdateDataIDs: updateCache},
41-
collectors.LSMOD{UpdateDataIDs: updateCache},
39+
// Get global collector configuration
40+
collectorOpts := GetCollectorConfig()
41+
42+
collectorsMap := map[string]collectors.Collector{
43+
"pci_devices": collectors.PCI{UpdateDataIDs: updateCache},
44+
"kernel_modules": collectors.LSMOD{UpdateDataIDs: updateCache},
45+
}
46+
47+
var usedCollectors = []collectors.Collector{}
48+
49+
for collectorName, collector := range collectorsMap {
50+
if collectorOpts.IsCollectorEnabled(collectorName) {
51+
usedCollectors = append(usedCollectors, collector)
52+
}
4253
}
4354

4455
var err error

internal/connect/config.go

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import (
99
"strconv"
1010
"strings"
1111

12+
"github.com/SUSE/connect-ng/internal/collectors"
1213
"github.com/SUSE/connect-ng/internal/util"
14+
"github.com/SUSE/connect-ng/pkg/profiles"
1315
"github.com/SUSE/connect-ng/pkg/registration"
16+
"gopkg.in/yaml.v3"
1417
)
1518

1619
const (
@@ -108,8 +111,29 @@ func ReadFromConfiguration(path string) (*Options, error) {
108111
util.Debug.Printf("Reading configuration from: %s\n", path)
109112
if f, err := os.Open(path); err == nil {
110113
defer f.Close()
111-
return parseFromConfiguration(f, cfg)
114+
115+
content, err := io.ReadAll(f)
116+
if err != nil {
117+
return nil, err
118+
}
119+
120+
cfg.Path = path
121+
cfg, err = parseFromConfiguration(bytes.NewReader(content), cfg)
122+
if err != nil {
123+
return nil, err
124+
}
125+
126+
// Parse collectors section with YAML
127+
err = parseAndSetCollectorConfig(content)
128+
if err != nil {
129+
return nil, err
130+
}
131+
132+
return cfg, nil
112133
}
134+
135+
// Empty config files, use defaults
136+
SetCollectorConfig(nil)
113137
return cfg, nil
114138
}
115139

@@ -118,8 +142,27 @@ func ReadFromConfiguration(path string) (*Options, error) {
118142
// otherwise it will return an empty configuration and an error object.
119143
func parseFromConfiguration(r io.Reader, cfg *Options) (*Options, error) {
120144
scanner := bufio.NewScanner(r)
145+
inCollectorsSection := false
146+
121147
for scanner.Scan() {
122148
line := scanner.Text()
149+
150+
// Skip collectors section entirely (will be parsed separately)
151+
if strings.TrimSpace(line) == "collectors:" {
152+
inCollectorsSection = true
153+
continue
154+
}
155+
156+
// Skip lines inside collectors section
157+
if inCollectorsSection {
158+
// Check if still collectors section
159+
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
160+
continue
161+
}
162+
// Non-indented line, section ended
163+
inCollectorsSection = false
164+
}
165+
123166
parts := strings.SplitN(line, ":", 2)
124167
if len(parts) < 2 {
125168
continue
@@ -166,6 +209,33 @@ func parseFromConfiguration(r io.Reader, cfg *Options) (*Options, error) {
166209
return cfg, nil
167210
}
168211

212+
// parseAndSetCollectorConfig parses only the collectors section
213+
func parseAndSetCollectorConfig(content []byte) error {
214+
var collectorsConfig struct {
215+
Collectors map[string]struct {
216+
Enabled bool `yaml:"enabled"`
217+
} `yaml:"collectors"`
218+
}
219+
220+
yaml.Unmarshal(content, &collectorsConfig)
221+
222+
if collectorsConfig.Collectors == nil {
223+
SetCollectorConfig(nil)
224+
return nil
225+
}
226+
227+
collectorsMap := make(map[string]profiles.CollectorConfig)
228+
for name, cfg := range collectorsConfig.Collectors {
229+
collectorsMap[name] = profiles.CollectorConfig{
230+
Enabled: cfg.Enabled,
231+
}
232+
}
233+
234+
collectorOptions := collectors.NewCollectorOptions(collectorsMap)
235+
SetCollectorConfig(collectorOptions)
236+
return nil
237+
}
238+
169239
// Change the base url to be used when talking to the server to the one being
170240
// provided.
171241
func (opts *Options) ChangeBaseURL(baseUrl string) {
@@ -216,3 +286,20 @@ func (opts *Options) IsScc() bool {
216286
}
217287
return false
218288
}
289+
290+
// Package-level collector configuration (defaults to all enabled)
291+
var collectorOptions profiles.CollectorOptions = profiles.NoCollectorOptions{}
292+
293+
// SetCollectorConfig sets the global collector configuration
294+
func SetCollectorConfig(opts profiles.CollectorOptions) {
295+
if opts == nil {
296+
collectorOptions = profiles.NoCollectorOptions{}
297+
return
298+
}
299+
collectorOptions = opts
300+
}
301+
302+
// GetCollectorConfig returns the current global collector configuration
303+
func GetCollectorConfig() profiles.CollectorOptions {
304+
return collectorOptions
305+
}

internal/connect/config_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"reflect"
66
"strings"
77
"testing"
8+
9+
"github.com/SUSE/connect-ng/internal/collectors"
810
)
911

1012
var cfg1 = `---
@@ -27,6 +29,22 @@ badkey: badval
2729
2830
`
2931

32+
var cfg3 = `---
33+
url: https://scc.suse.com
34+
insecure: false
35+
36+
collectors:
37+
pci_devices:
38+
enabled: false
39+
kernel_modules:
40+
enabled: true
41+
`
42+
43+
var collectorsList = []string{
44+
"pci_devices",
45+
"kernel_modules",
46+
}
47+
3048
func TestParseConfig(t *testing.T) {
3149
r := strings.NewReader(cfg1)
3250

@@ -63,6 +81,53 @@ func TestParseConfig2(t *testing.T) {
6381
}
6482
}
6583

84+
func TestParseConfigWithCollectors(t *testing.T) {
85+
content := []byte(cfg3)
86+
87+
// Parse standard config
88+
opts := DefaultOptions()
89+
r := strings.NewReader(cfg3)
90+
parseFromConfiguration(r, opts)
91+
92+
// Verify standard config parsed correctly
93+
if opts.BaseURL != "https://scc.suse.com" {
94+
t.Errorf("Unexpected BaseURL '%v'; expecting 'https://scc.suse.com'", opts.BaseURL)
95+
}
96+
if opts.Insecure != false {
97+
t.Errorf("Unexpected Insecure '%v'; expecting false", opts.Insecure)
98+
}
99+
100+
// Parse collectors config
101+
parseAndSetCollectorConfig(content)
102+
collectorOpts := GetCollectorConfig()
103+
104+
// Verify collectors config
105+
for _, collector := range collectorsList {
106+
cfg := collectorOpts.GetCollectorConfig(collector)
107+
if cfg == nil {
108+
t.Errorf("collector %s should be present in parsed config", collector)
109+
} else if collectorOpts.IsCollectorEnabled(collector) != cfg.Enabled {
110+
t.Errorf("collector %s enabled state should be %v", collector, cfg.Enabled)
111+
}
112+
}
113+
}
114+
115+
func TestParseConfigWithoutCollectors(t *testing.T) {
116+
content := []byte(cfg1)
117+
118+
// Parse collectors config (no collectors section)
119+
parseAndSetCollectorConfig(content)
120+
collectorOpts := GetCollectorConfig()
121+
122+
// Collectors should use their default state
123+
for _, collector := range collectorsList {
124+
expectedState := collectors.DefaultCollectorState(collector)
125+
if collectorOpts.IsCollectorEnabled(collector) != expectedState {
126+
t.Errorf("%s enabled state should be %v by default", collector, expectedState)
127+
}
128+
}
129+
}
130+
66131
func TestSaveLoad(t *testing.T) {
67132
path := filepath.Join(t.TempDir(), "SUSEConnect.test")
68133
c1 := DefaultOptions()

pkg/profiles/config.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package profiles
2+
3+
// CollectorConfig holds configs for a single collector
4+
type CollectorConfig struct {
5+
Enabled bool
6+
}
7+
8+
// CollectorOptions defines configuration for all data collectors
9+
// This interface allows internal implementations to provide collector
10+
// configuration without exposing internal types
11+
type CollectorOptions interface {
12+
// GetCollectorConfig returns configuration for a specific collector by name
13+
// Returns nil if collector is not configured
14+
GetCollectorConfig(collectorName string) *CollectorConfig
15+
16+
// IsCollectorEnabled checks if a collector is enabled
17+
// Convenience method that handles nil checks
18+
IsCollectorEnabled(collectorName string) bool
19+
}
20+
21+
// NoCollectorOptions is a default implementation that enables all collectors
22+
// Useful for testing and when collector options are not available
23+
type NoCollectorOptions struct{}
24+
25+
func (NoCollectorOptions) GetCollectorConfig(collectorName string) *CollectorConfig {
26+
return nil
27+
}
28+
29+
func (NoCollectorOptions) IsCollectorEnabled(collectorName string) bool {
30+
// Collectors are enabled by default
31+
return true
32+
}

0 commit comments

Comments
 (0)