-
Notifications
You must be signed in to change notification settings - Fork 486
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Compatible components links generation.
- Loading branch information
Showing
140 changed files
with
2,086 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package metadata | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/grafana/agent/component" | ||
_ "github.com/grafana/agent/component/all" | ||
"github.com/grafana/agent/component/common/loki" | ||
"github.com/grafana/agent/component/discovery" | ||
) | ||
|
||
//TODO(thampiotr): Instead of metadata package reaching into registry, we'll migrate to using a YAML metadata file that | ||
// contains information about all the components. This file will be generated separately from the | ||
// registry and can be used by other tools. | ||
|
||
type Type struct { | ||
Name string | ||
// Returns true if provided args include this type (including nested structs) | ||
ExistsInArgsFn func(args component.Arguments) bool | ||
// Returns true if provided exports include this type (including nested structs) | ||
ExistsInExportsFn func(exports component.Exports) bool | ||
} | ||
|
||
var ( | ||
// TypeTargets represents things that need to be scraped. | ||
TypeTargets = Type{ | ||
Name: "Targets", | ||
ExistsInArgsFn: func(args component.Arguments) bool { | ||
return hasFieldOfType(args, reflect.TypeOf([]discovery.Target{})) | ||
}, | ||
ExistsInExportsFn: func(exports component.Exports) bool { | ||
return hasFieldOfType(exports, reflect.TypeOf([]discovery.Target{})) | ||
}, | ||
} | ||
|
||
// TypeLokiLogs represent logs in Loki format | ||
TypeLokiLogs = Type{ | ||
Name: "Loki `LogsReceiver`", | ||
ExistsInArgsFn: func(args component.Arguments) bool { | ||
return hasFieldOfType(args, reflect.TypeOf([]loki.LogsReceiver{})) | ||
}, | ||
ExistsInExportsFn: func(exports component.Exports) bool { | ||
return hasFieldOfType(exports, reflect.TypeOf(loki.NewLogsReceiver())) | ||
}, | ||
} | ||
|
||
//TODO(thampiotr): add more types | ||
//DataTypeOTELTelemetry = Type("OTEL Telemetry") | ||
//DataTypePromMetrics = Type("Prometheus Metrics") | ||
//DataTypePyroscopeProfiles = Type("Pyroscope Profiles") | ||
|
||
AllTypes = []Type{ | ||
TypeTargets, | ||
TypeLokiLogs, | ||
} | ||
) | ||
|
||
type Metadata struct { | ||
Accepts []Type | ||
Exports []Type | ||
} | ||
|
||
func (m Metadata) Empty() bool { | ||
return len(m.Accepts) == 0 && len(m.Exports) == 0 | ||
} | ||
|
||
func (m Metadata) AcceptsType(t Type) bool { | ||
for _, a := range m.Accepts { | ||
if a.Name == t.Name { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (m Metadata) ExportsType(t Type) bool { | ||
for _, o := range m.Exports { | ||
if o.Name == t.Name { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func ForComponent(name string) (Metadata, error) { | ||
reg, ok := component.Get(name) | ||
if !ok { | ||
return Metadata{}, fmt.Errorf("could not find component %q", name) | ||
} | ||
return inferMetadata(reg.Args, reg.Exports), nil | ||
} | ||
|
||
func inferMetadata(args component.Arguments, exports component.Exports) Metadata { | ||
m := Metadata{} | ||
for _, t := range AllTypes { | ||
if t.ExistsInArgsFn(args) { | ||
m.Accepts = append(m.Accepts, t) | ||
} | ||
if t.ExistsInExportsFn(exports) { | ||
m.Exports = append(m.Exports, t) | ||
} | ||
} | ||
return m | ||
} | ||
|
||
func hasFieldOfType(obj interface{}, fieldType reflect.Type) bool { | ||
objValue := reflect.ValueOf(obj) | ||
|
||
// If the object is a pointer, dereference it | ||
for objValue.Kind() == reflect.Ptr { | ||
objValue = objValue.Elem() | ||
} | ||
|
||
// If the object is not a struct or interface, return false | ||
if objValue.Kind() != reflect.Struct && objValue.Kind() != reflect.Interface { | ||
return false | ||
} | ||
|
||
for i := 0; i < objValue.NumField(); i++ { | ||
fv := objValue.Field(i) | ||
ft := fv.Type() | ||
|
||
// If the field type matches the given type, return true | ||
if ft == fieldType { | ||
return true | ||
} | ||
|
||
if fv.Kind() == reflect.Interface && fieldType.AssignableTo(ft) { | ||
return true | ||
} | ||
|
||
// If the field is a struct, recursively check its fields | ||
if fv.Kind() == reflect.Struct { | ||
if hasFieldOfType(fv.Interface(), fieldType) { | ||
return true | ||
} | ||
} | ||
} | ||
|
||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package metadata | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_inferMetadata(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
expected Metadata | ||
}{ | ||
{ | ||
name: "discovery.dns", | ||
expected: Metadata{Exports: []Type{TypeTargets}}, | ||
}, | ||
{ | ||
name: "discovery.relabel", | ||
expected: Metadata{ | ||
Accepts: []Type{TypeTargets}, | ||
Exports: []Type{TypeTargets}, | ||
}, | ||
}, | ||
{ | ||
name: "loki.echo", | ||
expected: Metadata{Accepts: []Type{TypeLokiLogs}}, | ||
}, | ||
{ | ||
name: "loki.source.file", | ||
expected: Metadata{ | ||
Accepts: []Type{TypeTargets}, | ||
Exports: []Type{TypeLokiLogs}, | ||
}, | ||
}, | ||
{ | ||
name: "loki.process", | ||
expected: Metadata{ | ||
Accepts: []Type{TypeLokiLogs}, | ||
Exports: []Type{TypeLokiLogs}, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
actual, err := ForComponent(tt.name) | ||
require.NoError(t, err) | ||
require.Equal(t, tt.expected, actual) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package docs | ||
|
||
import ( | ||
"flag" | ||
"github.com/grafana/agent/component/metadata" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/grafana/agent/component" | ||
_ "github.com/grafana/agent/component/all" | ||
"github.com/grafana/agent/docs/generator" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// Run the below generate command to automatically update the Markdown docs with generated content | ||
//go:generate go test -fix-tests | ||
|
||
var fixTestsFlag = flag.Bool("fix-tests", false, "update the test files with the current generated content") | ||
|
||
func TestLinksToTypesSectionsUpdated(t *testing.T) { | ||
for _, name := range component.AllNames() { | ||
t.Run(name, func(t *testing.T) { | ||
runForGenerator(t, generator.NewLinksToTypesGenerator(name)) | ||
}) | ||
} | ||
} | ||
|
||
func TestCompatibleComponentsPageUpdated(t *testing.T) { | ||
path := "sources/flow/reference/compatibility/_index.md" | ||
for _, typ := range metadata.AllTypes { | ||
t.Run(typ.Name, func(t *testing.T) { | ||
t.Run("exporters", func(t *testing.T) { | ||
runForGenerator(t, generator.NewExportersListGenerator(typ, path)) | ||
}) | ||
t.Run("consumers", func(t *testing.T) { | ||
runForGenerator(t, generator.NewConsumersListGenerator(typ, path)) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
func runForGenerator(t *testing.T, g generator.DocsGenerator) { | ||
if *fixTestsFlag { | ||
err := g.Write() | ||
require.NoError(t, err, "failed to write generated content for: %q", g.Name()) | ||
t.Log("updated the docs with generated content", g.Name()) | ||
return | ||
} | ||
|
||
generated, err := g.Generate() | ||
require.NoError(t, err, "failed to generate: %q", g.Name()) | ||
|
||
if generated == "" { | ||
actual, err := g.Read() | ||
require.Error(t, err, "expected error when reading existing generated docs for %q", g.Name()) | ||
require.Contains(t, err.Error(), "markers not found", "expected error to be about missing markers") | ||
require.Empty(t, actual, "expected empty actual content for %q", g.Name()) | ||
return | ||
} | ||
|
||
actual, err := g.Read() | ||
require.NoError(t, err, "failed to read existing generated docs for %q, try running 'go generate ./docs'", g.Name()) | ||
require.Contains( | ||
t, | ||
actual, | ||
strings.TrimSpace(generated), | ||
"outdated docs detected when running %q, try updating with 'go generate ./docs'", | ||
g.Name(), | ||
) | ||
} |
Oops, something went wrong.