Skip to content

Commit 72f1256

Browse files
committed
Added plugin config
1 parent 3af74b1 commit 72f1256

File tree

9 files changed

+148
-44
lines changed

9 files changed

+148
-44
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,21 @@ $(CMD_DIR): go-dep mkdir
5757
@echo Build command $(notdir $@) GOOS=${OS} GOARCH=${ARCH}
5858
@GOOS=${OS} GOARCH=${ARCH} ${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@) ./$@
5959

60+
61+
# Build the plugins
62+
.PHONY: plugins
63+
plugins: $(PLUGIN_DIR) $(NPM_DIR)
64+
6065
# Build the plugins
6166
$(PLUGIN_DIR): go-dep mkdir
6267
@echo Build plugin $(notdir $@) GOOS=${OS} GOARCH=${ARCH}
6368
@GOOS=${OS} GOARCH=${ARCH} ${GO} build -buildmode=plugin ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@).plugin ./$@
6469

6570
# Build the NPM packages
6671
$(NPM_DIR): npm-dep
67-
@echo Build npm $(notdir $@)
72+
@echo Build npm $(notdir $@) GOOS=${OS} GOARCH=${ARCH}
6873
@cd $@ && npm install && npm run prod
74+
@GOOS=${OS} GOARCH=${ARCH} ${GO} build -buildmode=plugin ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@).plugin ./$@
6975

7076
# Build the docker image
7177
.PHONY: docker

cmd/server/service.go

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package main
22

33
import (
44
"context"
5-
"fmt"
5+
"errors"
66
"net/http"
77

88
// Packages
@@ -20,23 +20,24 @@ import (
2020
logger "github.com/mutablelogic/go-server/pkg/logger/config"
2121
pg "github.com/mutablelogic/go-server/pkg/pgmanager/config"
2222
pgqueue "github.com/mutablelogic/go-server/pkg/pgqueue/config"
23-
24-
// Static content
25-
helloworld "github.com/mutablelogic/go-server/npm/helloworld"
2623
)
2724

2825
///////////////////////////////////////////////////////////////////////////////
2926
// TYPES
3027

3128
type ServiceCommands struct {
32-
Run ServiceRunCommand `cmd:"" group:"SERVICE" help:"Run the service"`
29+
Run ServiceRunCommand `cmd:"" group:"SERVICE" help:"Run the service"`
30+
Run2 ServiceRun2Command `cmd:"" group:"SERVICE" help:"Run the service with plugins"`
3331
}
3432

35-
type ServiceRunCommand struct {
33+
type ServiceRun2Command struct {
3634
Plugins []string `help:"Plugin paths"`
37-
Router struct {
35+
}
36+
37+
type ServiceRunCommand struct {
38+
Router struct {
3839
httprouter.Config `embed:"" prefix:"router."` // Router configuration
39-
} `embed:""`
40+
} `embed:"" prefix:""`
4041
Server struct {
4142
httpserver.Config `embed:"" prefix:"server."` // Server configuration
4243
} `embed:""`
@@ -55,24 +56,12 @@ type ServiceRunCommand struct {
5556
Log struct {
5657
logger.Config `embed:"" prefix:"log."` // Logger configuration
5758
} `embed:""`
58-
HelloWorld struct {
59-
helloworld.Config `embed:"" prefix:"helloworld."` // HelloWorld configuration
60-
} `embed:""`
6159
}
6260

6361
///////////////////////////////////////////////////////////////////////////////
6462
// PUBLIC METHODS
6563

6664
func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
67-
// Load plugins
68-
plugins, err := provider.LoadPluginsForPattern(cmd.Plugins...)
69-
if err != nil {
70-
return err
71-
}
72-
for _, plugin := range plugins {
73-
fmt.Println("TODO: Loaded plugins:", plugin.Name())
74-
}
75-
7665
// Set the server listener and router prefix
7766
cmd.Server.Listen = app.GetEndpoint()
7867
cmd.Router.Prefix = types.NormalisePath(cmd.Server.Listen.Path)
@@ -134,19 +123,6 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
134123
// Return the new configuration with the router
135124
return config, nil
136125

137-
case "helloworld":
138-
config := plugin.(helloworld.Config)
139-
140-
// Set the router
141-
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter").(server.HTTPRouter); !ok || router == nil {
142-
return nil, httpresponse.ErrInternalError.Withf("Invalid router %q", "httprouter")
143-
} else {
144-
config.Router = router
145-
}
146-
147-
// Return the new configuration with the router
148-
return config, nil
149-
150126
case "auth":
151127
config := plugin.(auth.Config)
152128

@@ -214,7 +190,41 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
214190

215191
// No-op
216192
return plugin, nil
217-
}, cmd.Log.Config, cmd.Router.Config, cmd.Server.Config, cmd.HelloWorld.Config, cmd.Auth.Config, cmd.PGPool.Config, cmd.PGQueue.Config, cmd.CertManager.Config)
193+
}, cmd.Log.Config, cmd.Router.Config, cmd.Server.Config, cmd.Auth.Config, cmd.PGPool.Config, cmd.PGQueue.Config, cmd.CertManager.Config)
194+
if err != nil {
195+
return err
196+
}
197+
198+
// Run the provider
199+
return provider.Run(app.Context())
200+
}
201+
202+
func (cmd *ServiceRun2Command) Run(app server.Cmd) error {
203+
// Create a provider by loading the plugins
204+
provider, err := provider.NewWithPlugins(nil, cmd.Plugins...)
205+
if err != nil {
206+
return err
207+
}
208+
209+
// Create configurations
210+
err = errors.Join(err, provider.Load("log", "main", func(config server.Plugin) {
211+
logger := config.(*logger.Config)
212+
logger.Debug = app.GetDebug() >= server.Debug
213+
}))
214+
err = errors.Join(err, provider.Load("httprouter", "main", func(config server.Plugin) {
215+
httprouter := config.(*httprouter.Config)
216+
httprouter.Origin = "*"
217+
httprouter.Prefix = types.NormalisePath(app.GetEndpoint().Path)
218+
}))
219+
err = errors.Join(err, provider.Load("httpserver", "main", func(config server.Plugin) {
220+
httpserver := config.(*httpserver.Config)
221+
httpserver.Listen = app.GetEndpoint()
222+
}))
223+
err = errors.Join(err, provider.Load("helloworld", "main", nil))
224+
err = errors.Join(err, provider.Load("auth", "main", nil))
225+
err = errors.Join(err, provider.Load("pgpool", "main", nil))
226+
err = errors.Join(err, provider.Load("pgqueue", "main", nil))
227+
err = errors.Join(err, provider.Load("certmanager", "main", nil))
218228
if err != nil {
219229
return err
220230
}

npm/helloworld/config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package helloworld
1+
package main
22

33
import (
44
"context"
@@ -52,6 +52,10 @@ func (c Config) Description() string {
5252
return "Hello World example static content"
5353
}
5454

55+
func Plugin() server.Plugin {
56+
return Config{}
57+
}
58+
5559
////////////////////////////////////////////////////////////////////////////////
5660
// TASK
5761

npm/helloworld/src/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<body>
1212
<wc-google-auth id="auth"></wc-google-auth>
13-
<code><wc-provider origin="http://localhost:8080/" path="/pg/v1/schema" interval="5" id="databases"></wc-provider></code>
13+
<code><wc-provider origin="/" path="/pg/v1/schema" interval="5" id="databases"></wc-provider></code>
1414
<wc-array provider="#databases" id="#database-array"></wc-array>
1515
<wc-table array="#database-array"></wc-table>
1616
</body>

pkg/parser/meta/meta.go renamed to pkg/provider/meta/meta.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
// Packages
13+
"github.com/mutablelogic/go-server"
1314
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
1415
ast "github.com/mutablelogic/go-server/pkg/parser/ast"
1516
)
@@ -99,13 +100,17 @@ func (m *Meta) Validate(values any) error {
99100
return nil
100101
}
101102

103+
func (m *Meta) New() server.Plugin {
104+
return reflect.New(m.Type).Interface().(server.Plugin)
105+
}
106+
102107
////////////////////////////////////////////////////////////////////////////////
103108
// PRIVATE METHODS
104109

105110
func newMetaField(rf reflect.StructField) (*Meta, error) {
106111
meta := new(Meta)
107112

108-
fmt.Println("newMetaField", rf.Name, rf.Type)
113+
//fmt.Println("newMetaField", rf.Name, rf.Type)
109114

110115
// Name
111116
if name := nameForField(rf, "json", "yaml", "name"); name == "" {
File renamed without changes.

pkg/provider/plugin.go

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import (
55
"os"
66
"path/filepath"
77
"plugin"
8+
"strings"
89

910
// Packages
1011
server "github.com/mutablelogic/go-server"
1112
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
13+
logger "github.com/mutablelogic/go-server/pkg/logger"
14+
meta "github.com/mutablelogic/go-server/pkg/provider/meta"
15+
types "github.com/mutablelogic/go-server/pkg/types"
1216
)
1317

1418
///////////////////////////////////////////////////////////////////////////////
@@ -21,9 +25,80 @@ const (
2125
///////////////////////////////////////////////////////////////////////////////
2226
// PUBLIC METHODS
2327

24-
// LoadPluginsForPattern will load plugins from filesystem
28+
func NewWithPlugins(resolver ResolverFunc, paths ...string) (server.Provider, error) {
29+
self := new(provider)
30+
31+
// Load plugins
32+
plugins, err := loadPluginsForPattern(paths...)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
// Create the prototype map
38+
self.protos = make(map[string]*meta.Meta, len(plugins))
39+
self.plugin = make(map[string]server.Plugin, len(plugins))
40+
self.task = make(map[string]*state, len(plugins))
41+
self.resolver = resolver
42+
self.Logger = logger.New(os.Stderr, logger.Term, false)
43+
for _, plugin := range plugins {
44+
name := plugin.Name()
45+
if _, exists := self.protos[name]; exists {
46+
return nil, httpresponse.ErrBadRequest.Withf("Duplicate plugin: %q", name)
47+
} else if !types.IsIdentifier(name) {
48+
return nil, httpresponse.ErrBadRequest.Withf("Invalid plugin: %q", name)
49+
} else if meta, err := meta.New(plugin, name); err != nil {
50+
return nil, httpresponse.ErrBadRequest.Withf("Invalid plugin: %q", name)
51+
} else {
52+
self.protos[name] = meta
53+
}
54+
}
55+
56+
// Return success
57+
return self, nil
58+
}
59+
60+
// Create a "concrete" plugin from a prototype and a label, using the function to "hook"
61+
// any values into the plugin
62+
func (provider *provider) Load(name, label string, fn func(server.Plugin)) error {
63+
proto, exists := provider.protos[name]
64+
if !exists {
65+
return httpresponse.ErrBadRequest.Withf("Plugin not found: %q", name)
66+
}
67+
68+
// Make the label
69+
if label != "" {
70+
if !types.IsIdentifier(label) {
71+
return httpresponse.ErrBadRequest.Withf("Invalid label: %q", label)
72+
} else {
73+
label = name
74+
}
75+
} else {
76+
label = strings.Join([]string{label, name}, ".")
77+
}
78+
79+
// Register the plugin with the label
80+
if _, exists := provider.plugin[label]; exists {
81+
return httpresponse.ErrBadRequest.Withf("Plugin already exists: %q", label)
82+
} else {
83+
// Create a plugin from the prototype
84+
plugin := proto.New()
85+
if fn != nil {
86+
fn(plugin)
87+
}
88+
provider.plugin[label] = plugin
89+
provider.porder = append(provider.porder, label)
90+
}
91+
92+
// Return success
93+
return nil
94+
}
95+
96+
///////////////////////////////////////////////////////////////////////////////
97+
// PRIVATE METHODS
98+
99+
// loadPluginsForPattern will load plugins from filesystem
25100
// for a given glob pattern
26-
func LoadPluginsForPattern(pattern ...string) ([]server.Plugin, error) {
101+
func loadPluginsForPattern(pattern ...string) ([]server.Plugin, error) {
27102
var result []server.Plugin
28103
var errs error
29104

@@ -52,9 +127,6 @@ func LoadPluginsForPattern(pattern ...string) ([]server.Plugin, error) {
52127
return result, errs
53128
}
54129

55-
///////////////////////////////////////////////////////////////////////////////
56-
// PRIVATE METHODS
57-
58130
// Create a new plugin from a filepath
59131
func pluginWithPath(path string) (server.Plugin, error) {
60132
// Check path to make sure it's a regular file

pkg/provider/provider.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
server "github.com/mutablelogic/go-server"
1313
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
1414
logger "github.com/mutablelogic/go-server/pkg/logger"
15+
meta "github.com/mutablelogic/go-server/pkg/provider/meta"
1516
ref "github.com/mutablelogic/go-server/pkg/ref"
1617
types "github.com/mutablelogic/go-server/pkg/types"
1718
)
@@ -20,6 +21,9 @@ import (
2021
// TYPES
2122

2223
type provider struct {
24+
// Plugin metadata
25+
protos map[string]*meta.Meta
26+
2327
// Order of the plugins
2428
porder []string
2529

@@ -159,7 +163,7 @@ func (provider *provider) Task(ctx context.Context, label string) server.Task {
159163
}
160164

161165
// Create the task
162-
provider.Debug(ctx, "creating a new task for label ", label)
166+
provider.Debugf(ctx, "Creating a new task %q", label)
163167
task, err := plugin.New(ctx)
164168
if err != nil {
165169
provider.Print(ctx, label, ": ", err)

plugin.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ type Task interface {
3737
type Provider interface {
3838
Task
3939

40+
// Load a plugin by name and label
41+
Load(string, string, func(config Plugin)) error
42+
4043
// Return a task given a plugin label
4144
Task(context.Context, string) Task
4245
}

0 commit comments

Comments
 (0)