diff --git a/pkg/machinery/config/configloader/configloader_fuzz_test.go b/pkg/machinery/config/configloader/configloader_fuzz_test.go index daf4a898b3..9c4bcc3708 100644 --- a/pkg/machinery/config/configloader/configloader_fuzz_test.go +++ b/pkg/machinery/config/configloader/configloader_fuzz_test.go @@ -2,8 +2,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -//go:build go1.18 - package configloader_test import ( diff --git a/pkg/machinery/config/configloader/configloader_test.go b/pkg/machinery/config/configloader/configloader_test.go index 4746591c04..39123dbcf9 100644 --- a/pkg/machinery/config/configloader/configloader_test.go +++ b/pkg/machinery/config/configloader/configloader_test.go @@ -48,6 +48,8 @@ func callMethods(t testing.TB, obj reflect.Value, chain ...string) { fallthrough case "Doc": fallthrough + case "APIUrl": + fallthrough case "Endpoint": // t.Logf("Skipping %v", nextChain) continue diff --git a/pkg/machinery/config/configloader/internal/decoder/decoder_test.go b/pkg/machinery/config/configloader/internal/decoder/decoder_test.go index 6e94e5d365..193c1a8335 100644 --- a/pkg/machinery/config/configloader/internal/decoder/decoder_test.go +++ b/pkg/machinery/config/configloader/internal/decoder/decoder_test.go @@ -289,6 +289,19 @@ apiVersion: v1alpha3 omit: false `), }, + { + name: "misspelled apiVersion", + source: []byte(`--- +apiversion: v1alpha1 +kind: ExtensionServicesConfig +config: + - name: nut-client + configFiles: + - content: MONITOR ${upsmonHost} 1 remote pass foo + mountPath: /usr/local/etc/nut/upsmon.conf +`), + expectedErr: "\"ExtensionServicesConfig\" \"\": not registered", + }, } for _, tt := range tests { diff --git a/pkg/machinery/config/configloader/testdata/fuzz/FuzzConfigLoader/dfd1adfe630cffc2 b/pkg/machinery/config/configloader/testdata/fuzz/FuzzConfigLoader/dfd1adfe630cffc2 new file mode 100644 index 0000000000..c181174bba --- /dev/null +++ b/pkg/machinery/config/configloader/testdata/fuzz/FuzzConfigLoader/dfd1adfe630cffc2 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("apiVersion: v1alpha1\nkind: SideroLinkConfig") diff --git a/pkg/machinery/config/configloader/testdata/multidoc2.test b/pkg/machinery/config/configloader/testdata/multidoc2.test new file mode 100644 index 0000000000..588d94533a --- /dev/null +++ b/pkg/machinery/config/configloader/testdata/multidoc2.test @@ -0,0 +1,7 @@ +apiVersion: v1alpha1 +kind: ExtensionServicesConfig +config: + - name: foo + configFiles: + - content: hello + mountPath: /etc/foo diff --git a/pkg/machinery/config/configloader/testdata/multidoc3.test b/pkg/machinery/config/configloader/testdata/multidoc3.test new file mode 100644 index 0000000000..1b8bc99d04 --- /dev/null +++ b/pkg/machinery/config/configloader/testdata/multidoc3.test @@ -0,0 +1,26 @@ +apiVersion: v1alpha1 +kind: NetworkDefaultActionConfig +ingress: block +--- +apiVersion: v1alpha1 +kind: NetworkRuleConfig +name: test +portSelector: + ports: + - 53 + - 8000-9000 + protocol: udp +ingress: + - subnet: 192.168.0.0/16 + except: 192.168.0.3/32 + - subnet: 2001::/16 +--- +apiVersion: v1alpha1 +kind: NetworkRuleConfig +name: www +portSelector: + ports: + - 80 + protocol: tcp +ingress: + - subnet: 192.168.0.0/16 diff --git a/pkg/machinery/config/internal/registry/registry.go b/pkg/machinery/config/internal/registry/registry.go index 7a3f11ed8a..8b6dfe0761 100644 --- a/pkg/machinery/config/internal/registry/registry.go +++ b/pkg/machinery/config/internal/registry/registry.go @@ -23,27 +23,35 @@ var ( // NewDocumentFunc represents a function that creates a new document by version. type NewDocumentFunc func(version string) config.Document -var registry = &Registry{ - registered: map[string]NewDocumentFunc{}, -} +var registry = NewRegistry() // Registry represents the document kind/version registry. +// +// Global registry is available via top-level functions Register and New. type Registry struct { m sync.Mutex registered map[string]NewDocumentFunc } +// NewRegistry creates a new registry. +func NewRegistry() *Registry { + return &Registry{ + registered: map[string]NewDocumentFunc{}, + } +} + // Register registers a manifests with the registry. func Register(kind string, f NewDocumentFunc) { - registry.register(kind, f) + registry.Register(kind, f) } // New creates a new instance of the requested manifest. func New(kind, version string) (config.Document, error) { - return registry.new(kind, version) + return registry.New(kind, version) } -func (r *Registry) register(kind string, f NewDocumentFunc) { +// Register registers a document kind with the registry. +func (r *Registry) Register(kind string, f NewDocumentFunc) { r.m.Lock() defer r.m.Unlock() @@ -54,13 +62,18 @@ func (r *Registry) register(kind string, f NewDocumentFunc) { r.registered[kind] = f } -func (r *Registry) new(kind, version string) (config.Document, error) { +// New creates a new instance of the requested document. +func (r *Registry) New(kind, version string) (config.Document, error) { r.m.Lock() defer r.m.Unlock() f, ok := r.registered[kind] if ok { - return f(version), nil + doc := f(version) + + if doc != nil { + return doc, nil + } } return nil, fmt.Errorf("%q %q: %w", kind, version, ErrNotRegistered) diff --git a/pkg/machinery/config/internal/registry/registry_test.go b/pkg/machinery/config/internal/registry/registry_test.go new file mode 100644 index 0000000000..3ee1835891 --- /dev/null +++ b/pkg/machinery/config/internal/registry/registry_test.go @@ -0,0 +1,77 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package registry_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/siderolabs/talos/pkg/machinery/config/config" + "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" +) + +type mockDocument struct { + kind, version string +} + +func (d mockDocument) Clone() config.Document { + return d +} + +func (d mockDocument) Kind() string { + return d.kind +} + +func (d mockDocument) APIVersion() string { + return d.version +} + +func mockFactory(kind, version string) registry.NewDocumentFunc { + return func(requestedVersion string) config.Document { + if requestedVersion == version { + return mockDocument{kind, version} + } + + return nil + } +} + +func TestRegistry(t *testing.T) { + r := registry.NewRegistry() + + // register document types + r.Register("kind1", mockFactory("kind1", "v1alpha1")) + r.Register("kind2", mockFactory("kind2", "v1alpha1")) + + // register duplicate kind + assert.Panics(t, func() { + r.Register("kind1", mockFactory("kind1", "v1alpha3")) + }) + + // attempt to get unregistered kind + _, err := r.New("unknownKind", "unknownVersion") + require.Error(t, err) + assert.ErrorIs(t, err, registry.ErrNotRegistered) + assert.EqualError(t, err, "\"unknownKind\" \"unknownVersion\": not registered") + + // successful creation of documents + d, err := r.New("kind1", "v1alpha1") + require.NoError(t, err) + assert.Equal(t, "kind1", d.Kind()) + assert.Equal(t, "v1alpha1", d.APIVersion()) + + d, err = r.New("kind2", "v1alpha1") + require.NoError(t, err) + assert.Equal(t, "kind2", d.Kind()) + assert.Equal(t, "v1alpha1", d.APIVersion()) + + // attempt get registered kind, but wrong version + _, err = r.New("kind1", "unknownVersion") + require.Error(t, err) + assert.ErrorIs(t, err, registry.ErrNotRegistered) + assert.EqualError(t, err, "\"kind1\" \"unknownVersion\": not registered") +}