-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add inmem fs and simple builtin controller
- Loading branch information
Showing
10 changed files
with
423 additions
and
7 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
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,81 @@ | ||
package api_test | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.flipt.io/cup/pkg/api" | ||
"go.flipt.io/cup/pkg/api/core" | ||
"go.flipt.io/cup/pkg/controllers/simple" | ||
"go.flipt.io/cup/pkg/fs/mem" | ||
) | ||
|
||
var testDef = &core.ResourceDefinition{ | ||
APIVersion: "cup.flipt.io/v1alpha1", | ||
Kind: "ResourceDefinition", | ||
Metadata: core.Metadata{ | ||
Name: "resources.test.cup.flipt.io", | ||
}, | ||
Names: core.Names{ | ||
Kind: "Resource", | ||
Singular: "resource", | ||
Plural: "resources", | ||
}, | ||
Spec: core.ResourceDefinitionSpec{ | ||
Group: "test.cup.flipt.io", | ||
Controller: core.ResourceDefinitionController{}, | ||
Versions: map[string]json.RawMessage{ | ||
"v1alpha1": []byte("null"), | ||
}, | ||
}, | ||
} | ||
|
||
func Test_Server_Source(t *testing.T) { | ||
fss := mem.New() | ||
server, err := api.NewServer(fss) | ||
require.NoError(t, err) | ||
|
||
cntrl := simple.New(testDef) | ||
server.RegisterController("cup", cntrl) | ||
|
||
srv := httptest.NewServer(server) | ||
t.Cleanup(srv.Close) | ||
|
||
resp, err := http.Get(srv.URL + "/apis") | ||
require.NoError(t, err) | ||
|
||
defer resp.Body.Close() | ||
|
||
var sources []string | ||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&sources)) | ||
|
||
assert.Equal(t, []string{"cup"}, sources) | ||
} | ||
|
||
func Test_Server_SourceDefinitions(t *testing.T) { | ||
fss := mem.New() | ||
server, err := api.NewServer(fss) | ||
require.NoError(t, err) | ||
|
||
cntrl := simple.New(testDef) | ||
server.RegisterController("cup", cntrl) | ||
|
||
srv := httptest.NewServer(server) | ||
t.Cleanup(srv.Close) | ||
|
||
resp, err := http.Get(srv.URL + "/apis/cup") | ||
require.NoError(t, err) | ||
|
||
defer resp.Body.Close() | ||
|
||
var definitions map[string]*core.ResourceDefinition | ||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&definitions)) | ||
|
||
assert.Equal(t, map[string]*core.ResourceDefinition{ | ||
"test.cup.flipt.io/v1alpha1/Resource": testDef, | ||
}, definitions) | ||
} |
File renamed without changes.
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,118 @@ | ||
package simple | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"io/fs" | ||
"path" | ||
|
||
"go.flipt.io/cup/pkg/api/core" | ||
"go.flipt.io/cup/pkg/containers" | ||
"go.flipt.io/cup/pkg/controller" | ||
"go.flipt.io/cup/pkg/encoding" | ||
) | ||
|
||
type ResourceEncoding interface { | ||
Extension() string | ||
NewEncoder(io.Writer) encoding.TypedEncoder[core.Resource] | ||
NewDecoder(io.Reader) encoding.TypedDecoder[core.Resource] | ||
} | ||
|
||
// Controller is mostly used for testing purposes (for now). | ||
// It is a built-in controller implementation for cup. | ||
// It simply organizes resources on the underlying filesystem by { namespace }/{ name } | ||
// encoding them using the provided marshaller. | ||
type Controller struct { | ||
definition *core.ResourceDefinition | ||
encoding ResourceEncoding | ||
} | ||
|
||
// New constructs and configures a new *Controller. | ||
// By default it uses a JSON encoding which can be overriden via WithResourceEncoding. | ||
func New(def *core.ResourceDefinition, opts ...containers.Option[Controller]) *Controller { | ||
controller := &Controller{ | ||
definition: def, | ||
encoding: encoding.NewJSONEncoding[core.Resource](), | ||
} | ||
|
||
containers.ApplyAll(controller, opts...) | ||
|
||
return controller | ||
} | ||
|
||
// WithResourceEncoding overrides the default resource encoding. | ||
func WithResourceEncoding(e ResourceEncoding) containers.Option[Controller] { | ||
return func(c *Controller) { | ||
c.encoding = e | ||
} | ||
} | ||
|
||
// Definition returns the core resource definition handled by the Controller. | ||
func (c *Controller) Definition() *core.ResourceDefinition { | ||
return c.definition | ||
} | ||
|
||
func (c *Controller) Get(_ context.Context, req *controller.GetRequest) (*core.Resource, error) { | ||
fi, err := req.FSConfig.ToFS().Open(path.Join(req.Namespace, req.Name+"."+c.encoding.Extension())) | ||
if err != nil { | ||
return nil, fmt.Errorf("get: %w", err) | ||
} | ||
defer fi.Close() | ||
|
||
return c.encoding.NewDecoder(fi).Decode() | ||
} | ||
|
||
// List finds all the resources on the provided FS in the folder { namespace } | ||
// The result set is filtered by any specified labels. | ||
func (c *Controller) List(_ context.Context, req *controller.ListRequest) (resources []*core.Resource, _ error) { | ||
ffs := req.FSConfig.ToFS() | ||
return resources, fs.WalkDir(req.FSConfig.ToFS(), req.Namespace, func(p string, d fs.DirEntry, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if d.IsDir() { | ||
return fs.SkipDir | ||
} | ||
|
||
if ext := path.Ext(p); ext == "" || ext[1:] != c.encoding.Extension() { | ||
// skip files without expected extension | ||
return nil | ||
} | ||
|
||
fi, err := ffs.Open(p) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer fi.Close() | ||
|
||
resource, err := c.encoding.NewDecoder(fi).Decode() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, kv := range req.Labels { | ||
// skip adding resource if any of the specified labels | ||
// do not match as expected | ||
if v, ok := resource.Metadata.Labels[kv[0]]; !ok || v != kv[1] { | ||
return nil | ||
} | ||
} | ||
|
||
resources = append(resources, resource) | ||
|
||
return nil | ||
}) | ||
} | ||
|
||
// Put for now is a silent noop as we dont have a writable filesystem abstraction | ||
func (c *Controller) Put(_ context.Context, _ *controller.PutRequest) error { | ||
return nil | ||
} | ||
|
||
// Delete for now is a silent noop as we dont have a writable filesystem abstraction | ||
func (c *Controller) Delete(_ context.Context, _ *controller.DeleteRequest) error { | ||
return nil | ||
} |
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,26 @@ | ||
package encoding | ||
|
||
import "io" | ||
|
||
type AnyDecoder interface { | ||
Decode(any) error | ||
} | ||
|
||
type DecodeBuilder interface { | ||
NewDecoder(io.Reader) AnyDecoder | ||
} | ||
|
||
type Decoder[B DecodeBuilder, T any] struct { | ||
decoder AnyDecoder | ||
} | ||
|
||
func NewDecoder[B DecodeBuilder, T any](r io.Reader) Decoder[B, T] { | ||
var b DecodeBuilder | ||
|
||
return Decoder[B, T]{decoder: b.NewDecoder(r)} | ||
} | ||
|
||
func (d Decoder[B, T]) Decode() (*T, error) { | ||
var t T | ||
return &t, d.decoder.Decode(&t) | ||
} |
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,59 @@ | ||
package encoding | ||
|
||
import ( | ||
"io" | ||
) | ||
|
||
type AnyEncoder interface { | ||
Encode(any) error | ||
} | ||
|
||
type EncoderBuilder interface { | ||
NewEncoder(io.Writer) AnyEncoder | ||
} | ||
|
||
type Encoder[B EncoderBuilder, T any] struct { | ||
encoder AnyEncoder | ||
} | ||
|
||
func NewEncoder[B EncoderBuilder, T any](w io.Writer) Encoder[B, T] { | ||
var b EncoderBuilder | ||
|
||
return Encoder[B, T]{encoder: b.NewEncoder(w)} | ||
} | ||
|
||
func (e Encoder[B, T]) Encode(t *T) error { | ||
return e.encoder.Encode(t) | ||
} | ||
|
||
type AnyEncodingBuilder interface { | ||
Extension() string | ||
EncoderBuilder | ||
DecodeBuilder | ||
} | ||
|
||
type TypedEncoder[T any] interface { | ||
Encode(*T) error | ||
} | ||
|
||
type TypedDecoder[T any] interface { | ||
Decode() (*T, error) | ||
} | ||
|
||
type EncodingBuilder[B AnyEncodingBuilder, T any] struct { | ||
b B | ||
Encoder[B, T] | ||
Decoder[B, T] | ||
} | ||
|
||
func (e EncodingBuilder[B, T]) Extension() string { | ||
return e.b.Extension() | ||
} | ||
|
||
func (e EncodingBuilder[B, T]) NewEncoder(w io.Writer) TypedEncoder[T] { | ||
return NewEncoder[B, T](w) | ||
} | ||
|
||
func (e EncodingBuilder[B, T]) NewDecoder(r io.Reader) TypedDecoder[T] { | ||
return NewDecoder[B, T](r) | ||
} |
Oops, something went wrong.