From c18ea8e67d130d45e79a5549a1c328b71befec56 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Dec 2019 13:22:07 +0100 Subject: [PATCH 1/6] Prepare test case for mixed module --- metricbeat/mb/lightmodules_test.go | 77 ++++++++++--------- .../lightmodules/mixed/light/manifest.yml | 6 ++ .../mb/testdata/lightmodules/mixed/module.yml | 4 + .../lightmodules/mixed/standard/.placeholder | 0 4 files changed, 51 insertions(+), 36 deletions(-) create mode 100644 metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml create mode 100644 metricbeat/mb/testdata/lightmodules/mixed/module.yml create mode 100644 metricbeat/mb/testdata/lightmodules/mixed/standard/.placeholder diff --git a/metricbeat/mb/lightmodules_test.go b/metricbeat/mb/lightmodules_test.go index e24c5d847eb..2e5960c8920 100644 --- a/metricbeat/mb/lightmodules_test.go +++ b/metricbeat/mb/lightmodules_test.go @@ -189,48 +189,53 @@ func TestNewModuleFromConfig(t *testing.T) { expectedQuery QueryParams expectedPeriod time.Duration }{ - "normal module": { - config: common.MapStr{"module": "foo", "metricsets": []string{"bar"}}, + //"normal module": { + // config: common.MapStr{"module": "foo", "metricsets": []string{"bar"}}, + // expectedOption: "default", + //}, + //"light module": { + // config: common.MapStr{"module": "service", "metricsets": []string{"metricset"}}, + // expectedOption: "test", + //}, + //"light module default metricset": { + // config: common.MapStr{"module": "service"}, + // expectedOption: "test", + //}, + //"light module override option": { + // config: common.MapStr{"module": "service", "option": "overriden"}, + // expectedOption: "overriden", + //}, + //"light module with query": { + // config: common.MapStr{"module": "service", "query": common.MapStr{"param": "foo"}}, + // expectedOption: "test", + // expectedQuery: QueryParams{"param": "foo"}, + //}, + //"light module with custom period": { + // config: common.MapStr{"module": "service", "period": "42s"}, + // expectedOption: "test", + // expectedPeriod: 42 * time.Second, + //}, + //"light module is broken": { + // config: common.MapStr{"module": "broken"}, + // err: true, + //}, + //"light metric set doesn't exist": { + // config: common.MapStr{"module": "service", "metricsets": []string{"notexists"}}, + // err: true, + //}, + //"disabled light module": { + // config: common.MapStr{"module": "service", "enabled": false}, + // err: true, + //}, + "mixed module with standard and light metricsets": { + config: common.MapStr{"module": "mixed", "metricsets": []string{"standard", "light"}}, expectedOption: "default", }, - "light module": { - config: common.MapStr{"module": "service", "metricsets": []string{"metricset"}}, - expectedOption: "test", - }, - "light module default metricset": { - config: common.MapStr{"module": "service"}, - expectedOption: "test", - }, - "light module override option": { - config: common.MapStr{"module": "service", "option": "overriden"}, - expectedOption: "overriden", - }, - "light module with query": { - config: common.MapStr{"module": "service", "query": common.MapStr{"param": "foo"}}, - expectedOption: "test", - expectedQuery: QueryParams{"param": "foo"}, - }, - "light module with custom period": { - config: common.MapStr{"module": "service", "period": "42s"}, - expectedOption: "test", - expectedPeriod: 42 * time.Second, - }, - "light module is broken": { - config: common.MapStr{"module": "broken"}, - err: true, - }, - "light metric set doesn't exist": { - config: common.MapStr{"module": "service", "metricsets": []string{"notexists"}}, - err: true, - }, - "disabled light module": { - config: common.MapStr{"module": "service", "enabled": false}, - err: true, - }, } r := NewRegister() r.MustAddMetricSet("foo", "bar", newMetricSetWithOption) + r.MustAddMetricSet("mixed", "standard", newMetricSetWithOption) r.SetSecondarySource(NewLightModulesSource("testdata/lightmodules")) for title, c := range cases { diff --git a/metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml b/metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml new file mode 100644 index 00000000000..c467bea668c --- /dev/null +++ b/metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml @@ -0,0 +1,6 @@ +default: true +input: + module: mixed + metricset: light + defaults: + option: test diff --git a/metricbeat/mb/testdata/lightmodules/mixed/module.yml b/metricbeat/mb/testdata/lightmodules/mixed/module.yml new file mode 100644 index 00000000000..b849286511d --- /dev/null +++ b/metricbeat/mb/testdata/lightmodules/mixed/module.yml @@ -0,0 +1,4 @@ +name: mixed +metricsets: +- light +- standard diff --git a/metricbeat/mb/testdata/lightmodules/mixed/standard/.placeholder b/metricbeat/mb/testdata/lightmodules/mixed/standard/.placeholder new file mode 100644 index 00000000000..e69de29bb2d From 08b3629d075f8e27f06c7989d66624b2d1d4af02 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Dec 2019 14:28:44 +0100 Subject: [PATCH 2/6] Ignore registered modules while loading light metricsets --- metricbeat/mb/lightmodules.go | 33 +++++++++++-------- metricbeat/mb/lightmodules_test.go | 4 ++- metricbeat/mb/registry.go | 8 ++--- .../lightmodules/mixed/light/manifest.yml | 4 +-- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/metricbeat/mb/lightmodules.go b/metricbeat/mb/lightmodules.go index f39e8152ca9..88ff412974e 100644 --- a/metricbeat/mb/lightmodules.go +++ b/metricbeat/mb/lightmodules.go @@ -19,12 +19,10 @@ package mb import ( "fmt" + "github.com/pkg/errors" "io/ioutil" "os" "path/filepath" - "strings" - - "github.com/pkg/errors" "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/logp" @@ -68,8 +66,8 @@ func (s *LightModulesSource) HasModule(moduleName string) bool { } // DefaultMetricSets list the default metricsets for a given module -func (s *LightModulesSource) DefaultMetricSets(moduleName string) ([]string, error) { - module, err := s.loadModule(moduleName) +func (s *LightModulesSource) DefaultMetricSets(r *Register, moduleName string) ([]string, error) { + module, err := s.loadModule(r, moduleName) if err != nil { return nil, errors.Wrapf(err, "failed to get default metricsets for module '%s'", moduleName) } @@ -83,8 +81,8 @@ func (s *LightModulesSource) DefaultMetricSets(moduleName string) ([]string, err } // MetricSets list the available metricsets for a given module -func (s *LightModulesSource) MetricSets(moduleName string) ([]string, error) { - module, err := s.loadModule(moduleName) +func (s *LightModulesSource) MetricSets(r *Register, moduleName string) ([]string, error) { + module, err := s.loadModule(r, moduleName) if err != nil { return nil, errors.Wrapf(err, "failed to get metricsets for module '%s'", moduleName) } @@ -118,7 +116,7 @@ func (s *LightModulesSource) HasMetricSet(moduleName, metricSetName string) bool // MetricSetRegistration obtains a registration for a light metric set func (s *LightModulesSource) MetricSetRegistration(register *Register, moduleName, metricSetName string) (MetricSetRegistration, error) { - lightModule, err := s.loadModule(moduleName) + lightModule, err := s.loadModule(register, moduleName) if err != nil { return MetricSetRegistration{}, errors.Wrapf(err, "failed to load module '%s'", moduleName) } @@ -133,16 +131,17 @@ func (s *LightModulesSource) MetricSetRegistration(register *Register, moduleNam // String returns a string representation of this source, with a list of known metricsets func (s *LightModulesSource) String() string { - var metricSets []string + /*var metricSets []string modules, _ := s.Modules() for _, module := range modules { - moduleMetricSets, _ := s.MetricSets(module) + moduleMetricSets, _ := s.MetricSets(nil, module) // TODO for _, name := range moduleMetricSets { metricSets = append(metricSets, fmt.Sprintf("%s/%s", module, name)) } } - return fmt.Sprintf("LightModules:[%s]", strings.Join(metricSets, ", ")) + return fmt.Sprintf("LightModules:[%s]", strings.Join(metricSets, ", "))*/ + return "fixme" } type lightModuleConfig struct { @@ -156,7 +155,7 @@ type LightModule struct { MetricSets map[string]LightMetricSet } -func (s *LightModulesSource) loadModule(moduleName string) (*LightModule, error) { +func (s *LightModulesSource) loadModule(register *Register, moduleName string) (*LightModule, error) { modulePath, found := s.findModulePath(moduleName) if !found { return nil, fmt.Errorf("module '%s' not found", moduleName) @@ -167,7 +166,7 @@ func (s *LightModulesSource) loadModule(moduleName string) (*LightModule, error) return nil, errors.Wrapf(err, "failed to load light module '%s' definition", moduleName) } - metricSets, err := s.loadMetricSets(filepath.Dir(modulePath), moduleConfig.Name, moduleConfig.MetricSets) + metricSets, err := s.loadMetricSets(register, filepath.Dir(modulePath), moduleConfig.Name, moduleConfig.MetricSets) if err != nil { return nil, errors.Wrapf(err, "failed to load metric sets for light module '%s'", moduleName) } @@ -198,9 +197,15 @@ func (s *LightModulesSource) loadModuleConfig(modulePath string) (*lightModuleCo return &moduleConfig, nil } -func (s *LightModulesSource) loadMetricSets(moduleDirPath, moduleName string, metricSetNames []string) (map[string]LightMetricSet, error) { +func (s *LightModulesSource) loadMetricSets(register *Register, moduleDirPath, moduleName string, metricSetNames []string) (map[string]LightMetricSet, error) { metricSets := make(map[string]LightMetricSet) for _, metricSet := range metricSetNames { + if moduleMetricSets, exists := register.metricSets[moduleName]; exists { + if _, exists := moduleMetricSets[metricSet]; exists { + continue + } + } + manifestPath := filepath.Join(moduleDirPath, metricSet, manifestYML) metricSetConfig, err := s.loadMetricSetConfig(manifestPath) diff --git a/metricbeat/mb/lightmodules_test.go b/metricbeat/mb/lightmodules_test.go index 2e5960c8920..e3af1ee8809 100644 --- a/metricbeat/mb/lightmodules_test.go +++ b/metricbeat/mb/lightmodules_test.go @@ -168,9 +168,10 @@ func TestLoadModule(t *testing.T) { } for _, c := range cases { + register := NewRegister() r := NewLightModulesSource("testdata/lightmodules") t.Run(c.name, func(t *testing.T) { - _, err := r.loadModule(c.name) + _, err := r.loadModule(register, c.name) if c.err { assert.Error(t, err) } @@ -235,6 +236,7 @@ func TestNewModuleFromConfig(t *testing.T) { r := NewRegister() r.MustAddMetricSet("foo", "bar", newMetricSetWithOption) + r.MustAddMetricSet("foo", "light", newMetricSetWithOption) r.MustAddMetricSet("mixed", "standard", newMetricSetWithOption) r.SetSecondarySource(NewLightModulesSource("testdata/lightmodules")) diff --git a/metricbeat/mb/registry.go b/metricbeat/mb/registry.go index 673f6d56b9d..a9eb0234bcf 100644 --- a/metricbeat/mb/registry.go +++ b/metricbeat/mb/registry.go @@ -116,8 +116,8 @@ type Register struct { type ModulesSource interface { Modules() ([]string, error) HasModule(module string) bool - MetricSets(module string) ([]string, error) - DefaultMetricSets(module string) ([]string, error) + MetricSets(r *Register, module string) ([]string, error) + DefaultMetricSets(r *Register, module string) ([]string, error) HasMetricSet(module, name string) bool MetricSetRegistration(r *Register, module, name string) (MetricSetRegistration, error) String() string @@ -280,7 +280,7 @@ func (r *Register) DefaultMetricSets(module string) ([]string, error) { // List also default metrics from secondary sources if source := r.secondarySource; source != nil && source.HasModule(module) { exists = true - sourceDefaults, err := source.DefaultMetricSets(module) + sourceDefaults, err := source.DefaultMetricSets(r, module) if err != nil { r.log.Errorf("Failed to get default metric sets for module '%s' from secondary source: %s", module, err) } else if len(sourceDefaults) > 0 { @@ -352,7 +352,7 @@ func (r *Register) MetricSets(module string) []string { // List also metric sets from secondary sources if source := r.secondarySource; source != nil && source.HasModule(module) { - sourceMetricSets, err := source.MetricSets(module) + sourceMetricSets, err := source.MetricSets(r, module) if err != nil { r.log.Errorf("Failed to get metricsets from secondary source: %s", err) } diff --git a/metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml b/metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml index c467bea668c..af21b012025 100644 --- a/metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml +++ b/metricbeat/mb/testdata/lightmodules/mixed/light/manifest.yml @@ -1,6 +1,4 @@ default: true input: - module: mixed + module: foo metricset: light - defaults: - option: test From b1f5b7b7b35e59ecf30cffe66c08d3a0c186500c Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Dec 2019 14:33:24 +0100 Subject: [PATCH 3/6] Test case: fail on unregistered module --- metricbeat/mb/lightmodules_test.go | 80 ++++++++++--------- .../mixedbroken/light/manifest.yml | 4 + .../lightmodules/mixedbroken/module.yml | 4 + .../mixedbroken/unregistered/.placeholder | 0 4 files changed, 50 insertions(+), 38 deletions(-) create mode 100644 metricbeat/mb/testdata/lightmodules/mixedbroken/light/manifest.yml create mode 100644 metricbeat/mb/testdata/lightmodules/mixedbroken/module.yml create mode 100644 metricbeat/mb/testdata/lightmodules/mixedbroken/unregistered/.placeholder diff --git a/metricbeat/mb/lightmodules_test.go b/metricbeat/mb/lightmodules_test.go index e3af1ee8809..ecd88a386ed 100644 --- a/metricbeat/mb/lightmodules_test.go +++ b/metricbeat/mb/lightmodules_test.go @@ -190,48 +190,52 @@ func TestNewModuleFromConfig(t *testing.T) { expectedQuery QueryParams expectedPeriod time.Duration }{ - //"normal module": { - // config: common.MapStr{"module": "foo", "metricsets": []string{"bar"}}, - // expectedOption: "default", - //}, - //"light module": { - // config: common.MapStr{"module": "service", "metricsets": []string{"metricset"}}, - // expectedOption: "test", - //}, - //"light module default metricset": { - // config: common.MapStr{"module": "service"}, - // expectedOption: "test", - //}, - //"light module override option": { - // config: common.MapStr{"module": "service", "option": "overriden"}, - // expectedOption: "overriden", - //}, - //"light module with query": { - // config: common.MapStr{"module": "service", "query": common.MapStr{"param": "foo"}}, - // expectedOption: "test", - // expectedQuery: QueryParams{"param": "foo"}, - //}, - //"light module with custom period": { - // config: common.MapStr{"module": "service", "period": "42s"}, - // expectedOption: "test", - // expectedPeriod: 42 * time.Second, - //}, - //"light module is broken": { - // config: common.MapStr{"module": "broken"}, - // err: true, - //}, - //"light metric set doesn't exist": { - // config: common.MapStr{"module": "service", "metricsets": []string{"notexists"}}, - // err: true, - //}, - //"disabled light module": { - // config: common.MapStr{"module": "service", "enabled": false}, - // err: true, - //}, + "normal module": { + config: common.MapStr{"module": "foo", "metricsets": []string{"bar"}}, + expectedOption: "default", + }, + "light module": { + config: common.MapStr{"module": "service", "metricsets": []string{"metricset"}}, + expectedOption: "test", + }, + "light module default metricset": { + config: common.MapStr{"module": "service"}, + expectedOption: "test", + }, + "light module override option": { + config: common.MapStr{"module": "service", "option": "overriden"}, + expectedOption: "overriden", + }, + "light module with query": { + config: common.MapStr{"module": "service", "query": common.MapStr{"param": "foo"}}, + expectedOption: "test", + expectedQuery: QueryParams{"param": "foo"}, + }, + "light module with custom period": { + config: common.MapStr{"module": "service", "period": "42s"}, + expectedOption: "test", + expectedPeriod: 42 * time.Second, + }, + "light module is broken": { + config: common.MapStr{"module": "broken"}, + err: true, + }, + "light metric set doesn't exist": { + config: common.MapStr{"module": "service", "metricsets": []string{"notexists"}}, + err: true, + }, + "disabled light module": { + config: common.MapStr{"module": "service", "enabled": false}, + err: true, + }, "mixed module with standard and light metricsets": { config: common.MapStr{"module": "mixed", "metricsets": []string{"standard", "light"}}, expectedOption: "default", }, + "mixed module with unregistered and light metricsets": { + config: common.MapStr{"module": "mixedbroken", "metricsets": []string{"unregistered", "light"}}, + err: true, + }, } r := NewRegister() diff --git a/metricbeat/mb/testdata/lightmodules/mixedbroken/light/manifest.yml b/metricbeat/mb/testdata/lightmodules/mixedbroken/light/manifest.yml new file mode 100644 index 00000000000..af21b012025 --- /dev/null +++ b/metricbeat/mb/testdata/lightmodules/mixedbroken/light/manifest.yml @@ -0,0 +1,4 @@ +default: true +input: + module: foo + metricset: light diff --git a/metricbeat/mb/testdata/lightmodules/mixedbroken/module.yml b/metricbeat/mb/testdata/lightmodules/mixedbroken/module.yml new file mode 100644 index 00000000000..f90fddb81eb --- /dev/null +++ b/metricbeat/mb/testdata/lightmodules/mixedbroken/module.yml @@ -0,0 +1,4 @@ +name: mixedbroken +metricsets: +- light +- unregistered diff --git a/metricbeat/mb/testdata/lightmodules/mixedbroken/unregistered/.placeholder b/metricbeat/mb/testdata/lightmodules/mixedbroken/unregistered/.placeholder new file mode 100644 index 00000000000..e69de29bb2d From 746a9df4440dddde7d35773561670129c04056e3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Dec 2019 14:34:46 +0100 Subject: [PATCH 4/6] Fix: mage fmt --- metricbeat/mb/lightmodules.go | 3 ++- metricbeat/mb/lightmodules_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/metricbeat/mb/lightmodules.go b/metricbeat/mb/lightmodules.go index 88ff412974e..b0d062764a4 100644 --- a/metricbeat/mb/lightmodules.go +++ b/metricbeat/mb/lightmodules.go @@ -19,11 +19,12 @@ package mb import ( "fmt" - "github.com/pkg/errors" "io/ioutil" "os" "path/filepath" + "github.com/pkg/errors" + "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/logp" ) diff --git a/metricbeat/mb/lightmodules_test.go b/metricbeat/mb/lightmodules_test.go index ecd88a386ed..4278adc049b 100644 --- a/metricbeat/mb/lightmodules_test.go +++ b/metricbeat/mb/lightmodules_test.go @@ -233,7 +233,7 @@ func TestNewModuleFromConfig(t *testing.T) { expectedOption: "default", }, "mixed module with unregistered and light metricsets": { - config: common.MapStr{"module": "mixedbroken", "metricsets": []string{"unregistered", "light"}}, + config: common.MapStr{"module": "mixedbroken", "metricsets": []string{"unregistered", "light"}}, err: true, }, } From ca533a70fcb5676b9fc327fdcbdd9fa6cf347c30 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Dec 2019 16:04:54 +0100 Subject: [PATCH 5/6] Fix: replace String with ...Info --- metricbeat/mb/lightmodules.go | 12 ++++++------ metricbeat/mb/registry.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/metricbeat/mb/lightmodules.go b/metricbeat/mb/lightmodules.go index b0d062764a4..67716d9c681 100644 --- a/metricbeat/mb/lightmodules.go +++ b/metricbeat/mb/lightmodules.go @@ -22,6 +22,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/pkg/errors" @@ -130,19 +131,18 @@ func (s *LightModulesSource) MetricSetRegistration(register *Register, moduleNam return ms.Registration(register) } -// String returns a string representation of this source, with a list of known metricsets -func (s *LightModulesSource) String() string { - /*var metricSets []string +// LightModulesInfo returns a string representation of this source, with a list of known metricsets +func (s *LightModulesSource) LightModulesInfo(r *Register) string { + var metricSets []string modules, _ := s.Modules() for _, module := range modules { - moduleMetricSets, _ := s.MetricSets(nil, module) // TODO + moduleMetricSets, _ := s.MetricSets(r, module) for _, name := range moduleMetricSets { metricSets = append(metricSets, fmt.Sprintf("%s/%s", module, name)) } } - return fmt.Sprintf("LightModules:[%s]", strings.Join(metricSets, ", "))*/ - return "fixme" + return fmt.Sprintf("LightModules:[%s]", strings.Join(metricSets, ", ")) } type lightModuleConfig struct { diff --git a/metricbeat/mb/registry.go b/metricbeat/mb/registry.go index a9eb0234bcf..cfa48067fe1 100644 --- a/metricbeat/mb/registry.go +++ b/metricbeat/mb/registry.go @@ -120,7 +120,7 @@ type ModulesSource interface { DefaultMetricSets(r *Register, module string) ([]string, error) HasMetricSet(module, name string) bool MetricSetRegistration(r *Register, module, name string) (MetricSetRegistration, error) - String() string + LightModulesInfo(r *Register) string } // NewRegister creates and returns a new Register. @@ -390,7 +390,7 @@ func (r *Register) String() string { var secondarySource string if source := r.secondarySource; source != nil { - secondarySource = ", " + source.String() + secondarySource = fmt.Sprintf(", LightModules:[%s]", source.LightModulesInfo(r)) } sort.Strings(modules) From 84c463681d49a7b8e59c371fd601c531ae790dc7 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 11 Dec 2019 16:34:37 +0100 Subject: [PATCH 6/6] Rename method to ModulesInfo() --- metricbeat/mb/lightmodules.go | 4 ++-- metricbeat/mb/registry.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/metricbeat/mb/lightmodules.go b/metricbeat/mb/lightmodules.go index 67716d9c681..51269c36394 100644 --- a/metricbeat/mb/lightmodules.go +++ b/metricbeat/mb/lightmodules.go @@ -131,8 +131,8 @@ func (s *LightModulesSource) MetricSetRegistration(register *Register, moduleNam return ms.Registration(register) } -// LightModulesInfo returns a string representation of this source, with a list of known metricsets -func (s *LightModulesSource) LightModulesInfo(r *Register) string { +// ModulesInfo returns a string representation of this source, with a list of known metricsets +func (s *LightModulesSource) ModulesInfo(r *Register) string { var metricSets []string modules, _ := s.Modules() for _, module := range modules { diff --git a/metricbeat/mb/registry.go b/metricbeat/mb/registry.go index cfa48067fe1..d57d1999a5e 100644 --- a/metricbeat/mb/registry.go +++ b/metricbeat/mb/registry.go @@ -120,7 +120,7 @@ type ModulesSource interface { DefaultMetricSets(r *Register, module string) ([]string, error) HasMetricSet(module, name string) bool MetricSetRegistration(r *Register, module, name string) (MetricSetRegistration, error) - LightModulesInfo(r *Register) string + ModulesInfo(r *Register) string } // NewRegister creates and returns a new Register. @@ -390,7 +390,7 @@ func (r *Register) String() string { var secondarySource string if source := r.secondarySource; source != nil { - secondarySource = fmt.Sprintf(", LightModules:[%s]", source.LightModulesInfo(r)) + secondarySource = fmt.Sprintf(", LightModules:[%s]", source.ModulesInfo(r)) } sort.Strings(modules)