Skip to content

Commit

Permalink
Deployer types (#9)
Browse files Browse the repository at this point in the history
* add deployment type to interface

* refactor discriminator id to deployer_id

* Progress towards multiple deployment types

* refactor to name

* fix broken tests

* remove dead code

---------

Co-authored-by: Jared O'Connell <[email protected]>
Co-authored-by: Matt Leader <[email protected]>
  • Loading branch information
3 people authored Nov 1, 2023
1 parent 8139b32 commit 2469684
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 29 deletions.
8 changes: 6 additions & 2 deletions any.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ type anyDeployerFactory[T any] struct {
factory ConnectorFactory[T]
}

func (a anyDeployerFactory[T]) ID() string {
return a.factory.ID()
func (a anyDeployerFactory[T]) Name() string {
return a.factory.Name()
}

func (a anyDeployerFactory[T]) DeploymentType() DeploymentType {
return a.factory.DeploymentType()
}

func (a anyDeployerFactory[T]) ConfigurationSchema() schema.Object {
Expand Down
8 changes: 6 additions & 2 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,23 @@ import (
"go.flow.arcalot.io/pluginsdk/schema"
)

type DeploymentType string

// ConnectorFactory is an abstraction that hides away the complexity of instantiating a Connector. Its main purpose is
// to provide the configuration schema for the connector and then create an instance of said connector.
type ConnectorFactory[ConfigType any] interface {
ID() string
Name() string
ConfigurationSchema() *schema.TypedScopeSchema[ConfigType]
Create(config ConfigType, logger log.Logger) (Connector, error)
DeploymentType() DeploymentType
}

// AnyConnectorFactory is the untyped version of ConnectorFactory.
type AnyConnectorFactory interface {
ID() string
Name() string
ConfigurationSchema() schema.Object
Create(config any, logger log.Logger) (Connector, error)
DeploymentType() DeploymentType
}

// Connector is responsible for deploying a container image on the specified target. Once deployed and ready, the
Expand Down
14 changes: 13 additions & 1 deletion registry/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,25 @@ import (
type testConfig struct {
}

var testConfigInput = map[string]any{
"test-type": map[string]any{
"deployer_name": "test",
},
}

type testNewFactory struct {
}

func (t testNewFactory) ID() string {
var testDeploymentType = deployer.DeploymentType("test-type")

func (t testNewFactory) Name() string {
return "test"
}

func (t testNewFactory) DeploymentType() deployer.DeploymentType {
return "test-type"
}

func (t testNewFactory) ConfigurationSchema() schema.Object {
return schema.NewTypedScopeSchema[testConfig](
schema.NewStructMappedObjectSchema[testConfig](
Expand Down
16 changes: 12 additions & 4 deletions registry/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ import (

// New creates a new registry with the given factories.
func New(factory ...deployer.AnyConnectorFactory) Registry {
factories := make(map[string]deployer.AnyConnectorFactory, len(factory))
factories := make(map[deployer.DeploymentType]map[string]deployer.AnyConnectorFactory, len(factory))

for _, f := range factory {
if v, ok := factories[f.ID()]; ok {
panic(fmt.Errorf("duplicate deployer factory ID: %s (first: %T, second: %T)", f.ID(), v, f))
deploymentType := f.DeploymentType()
category, categoryCreated := factories[deploymentType]
if !categoryCreated {
category = make(map[string]deployer.AnyConnectorFactory)
factories[deploymentType] = category
}
factories[f.ID()] = f

if v, ok := category[f.Name()]; ok {
panic(fmt.Errorf("duplicate deployer factory Name for deployment type %s: %s (first: %T, second: %T)",
deploymentType, f.Name(), v, f))
}
category[f.Name()] = f
}

return &registry{
Expand Down
47 changes: 36 additions & 11 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,67 @@ import (
type Registry interface {
// List lists the registered deployers with their scopes.
List() map[string]schema.Object
// Schema returns a composite schema for the registry.
Schema() schema.OneOf[string]
// DeploymentTypes returns a slice of all deployment types in the registry.
DeploymentTypes() []deployer.DeploymentType
// DeployConfigSchema returns a composite schema for all types in the registry of that deployment type.
DeployConfigSchema(deploymentType deployer.DeploymentType) schema.OneOf[string]
// Create creates a connector with the given configuration type. The registry must identify the correct deployer
// based on the type passed.
Create(config any, logger log.Logger) (deployer.Connector, error)
Create(deploymentType deployer.DeploymentType, config any, logger log.Logger) (deployer.Connector, error)
}

type registry struct {
deployerFactories map[string]deployer.AnyConnectorFactory
deployerFactories map[deployer.DeploymentType]map[string]deployer.AnyConnectorFactory
}

func (r registry) DeploymentTypes() []deployer.DeploymentType {
typeList := make([]deployer.DeploymentType, len(r.deployerFactories))

i := 0
for k := range r.deployerFactories {
typeList[i] = k
i++
}
return typeList
}

func (r registry) List() map[string]schema.Object {
result := make(map[string]schema.Object, len(r.deployerFactories))
for id, factory := range r.deployerFactories {
result[id] = factory.ConfigurationSchema()
for _, factories := range r.deployerFactories {
for id, factory := range factories {
result[id] = factory.ConfigurationSchema()
}
}
return result
}

func (r registry) Schema() schema.OneOf[string] {
func (r registry) Slice() []deployer.AnyConnectorFactory {
slc := make([]deployer.AnyConnectorFactory, 0)
for _, factories := range r.deployerFactories {
for _, factory := range factories {
slc = append(slc, factory)
}
}
return slc
}

func (r registry) DeployConfigSchema(deploymentType deployer.DeploymentType) schema.OneOf[string] {
schemas := make(map[string]schema.Object, len(r.deployerFactories))
for id, factory := range r.deployerFactories {
for id, factory := range r.deployerFactories[deploymentType] {
schemas[id] = factory.ConfigurationSchema()
}
return schema.NewOneOfStringSchema[any](
schemas,
"type",
"deployer_name",
)
}

func (r registry) Create(config any, logger log.Logger) (deployer.Connector, error) {
func (r registry) Create(deploymentType deployer.DeploymentType, config any, logger log.Logger) (deployer.Connector, error) {
if config == nil {
return nil, fmt.Errorf("the deployer configuration cannot be nil")
}
reflectedConfig := reflect.ValueOf(config)
for _, factory := range r.deployerFactories {
for _, factory := range r.deployerFactories[deploymentType] {
if factory.ConfigurationSchema().ReflectedType() == reflectedConfig.Type() {
return factory.Create(config, logger)
}
Expand Down
21 changes: 12 additions & 9 deletions registry/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ func testRegistrySchemaIncorrectInput(t *testing.T) {
r := registry.New(
&testNewFactory{},
)
schema := r.Schema()
schema := r.DeployConfigSchema("test-type")

if _, err := schema.Unserialize(map[string]any{"type": "non-existent"}); err == nil {
if _, err := schema.Unserialize(map[string]any{
"deployer_name": "non-existent",
}); err == nil {
t.Fatalf("No error returned")
}

Expand All @@ -34,9 +36,11 @@ func testRegistrySchemaCorrectInput(t *testing.T) {
r := registry.New(
&testNewFactory{},
)
schema := r.Schema()
schema := r.DeployConfigSchema("test-type")

unserializedData, err := schema.Unserialize(map[string]any{"type": "test"})
unserializedData, err := schema.Unserialize(map[string]any{
"deployer_name": "test",
})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
Expand All @@ -57,7 +61,8 @@ func testRegistryCreateCorrectCreation(t *testing.T) {
r := registry.New(
&testNewFactory{},
)
connector, err := r.Create(testConfig{}, log.NewTestLogger(t))

connector, err := r.Create(testDeploymentType, testConfig{}, log.NewTestLogger(t))
assert.NoError(t, err)
if _, ok := connector.(*testConnector); !ok {
t.Fatalf("Incorrect connector returned: %T", connector)
Expand All @@ -66,13 +71,11 @@ func testRegistryCreateCorrectCreation(t *testing.T) {

func testRegistryCreateIncorrectConfigType(t *testing.T) {
t.Parallel()
type testStruct struct {
}

r := registry.New(
&testNewFactory{},
)
_, err := r.Create(testStruct{}, log.NewTestLogger(t))
_, err := r.Create(testDeploymentType, map[string]any{}, log.NewTestLogger(t))
if err == nil {
t.Fatalf("expected error, no error returned")
}
Expand All @@ -83,7 +86,7 @@ func testRegistryCreateNilConfig(t *testing.T) {
r := registry.New(
&testNewFactory{},
)
_, err := r.Create(nil, log.NewTestLogger(t))
_, err := r.Create(testDeploymentType, nil, log.NewTestLogger(t))
if err == nil {
t.Fatalf("expected error, no error returned")
}
Expand Down

0 comments on commit 2469684

Please sign in to comment.