Skip to content

Commit

Permalink
📦 new: use configbuilder to parse config files into Config struct (#25
Browse files Browse the repository at this point in the history
)

* new: configbuilder.ConfigJSON

* fix: unused wasmClone

* new: configbuilder.ConfigProtoBuf

Plus, make configbuilder no longer an internal package.

* feat: UnmarshalJSON and UnmarshalProto for Config

* deprecated: use of Argv and Env inside WATM

Deprecate the use of `argv` and `env` inside a WATM. A WATM is encouraged to be configured with `Config.TransportModuleConfig`. To prevent the unnecessary warning this may introduce to some undocumented legitimate use, we will not add `Deprecated` flag on the related fields and functions.

Signed-off-by: Gaukas Wang <[email protected]>

---------

Signed-off-by: Gaukas Wang <[email protected]>
  • Loading branch information
gaukas authored Jan 8, 2024
1 parent a667ea2 commit 5a62246
Show file tree
Hide file tree
Showing 9 changed files with 743 additions and 1 deletion.
137 changes: 136 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package water

import (
"encoding/json"
"errors"
"net"
"os"

"github.com/gaukas/water/configbuilder"
"github.com/gaukas/water/internal/log"
"github.com/gaukas/water/internal/wasm"
"google.golang.org/protobuf/proto"
)

// Config defines the configuration for the WATER Dialer/Config interface.
Expand Down Expand Up @@ -56,7 +61,7 @@ func (c *Config) Clone() *Config {
copy(wasmClone, c.TransportModuleBin)

return &Config{
TransportModuleBin: c.TransportModuleBin,
TransportModuleBin: wasmClone,
NetworkDialerFunc: c.NetworkDialerFunc,
NetworkListener: c.NetworkListener,
TransportModuleConfig: c.TransportModuleConfig,
Expand Down Expand Up @@ -126,3 +131,133 @@ func (c *Config) Logger() *log.Logger {

return log.GetDefaultLogger()
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (c *Config) UnmarshalJSON(data []byte) error {
var confJson configbuilder.ConfigJSON

err := json.Unmarshal(data, &confJson)
if err != nil {
return err
}

tmBin, err := os.ReadFile(confJson.TransportModule.BinPath)
if err != nil {
return err
}
c.TransportModuleBin = tmBin

if len(confJson.TransportModule.ConfigPath) > 0 {
c.TransportModuleConfig, err = TransportModuleConfigFromFile(confJson.TransportModule.ConfigPath)
if err != nil {
return err
}
}

if len(confJson.Network.Listener.Network) > 0 && len(confJson.Network.Listener.Address) > 0 {
c.NetworkListener, err = net.Listen(confJson.Network.Listener.Network, confJson.Network.Listener.Address)
if err != nil {
return err
}
}

c.ModuleConfigFactory = wasm.NewModuleConfigFactory()
if len(confJson.Module.Argv) > 0 {
c.ModuleConfigFactory.SetArgv(confJson.Module.Argv)
}

var envKeys []string
var envValues []string
for k, v := range confJson.Module.Env {
envKeys = append(envKeys, k)
envValues = append(envValues, v)
}
if len(envKeys) > 0 {
c.ModuleConfigFactory.SetEnv(envKeys, envValues)
}

if confJson.Module.InheritStdin {
c.ModuleConfigFactory.InheritStdin()
}

if confJson.Module.InheritStdout {
c.ModuleConfigFactory.InheritStdout()
}

if confJson.Module.InheritStderr {
c.ModuleConfigFactory.InheritStderr()
}

for k, v := range confJson.Module.PreopenedDirs {
c.ModuleConfigFactory.SetPreopenDir(k, v)
}

return nil
}

// UnmarshalProto provides a way to unmarshal a protobuf message into a Config.
//
// The message definition is defined in configbuilder/pb/config.proto.
func (c *Config) UnmarshalProto(b []byte) error {
var confProto configbuilder.ConfigProtoBuf

unmarshalOptions := proto.UnmarshalOptions{
AllowPartial: true,
}
err := unmarshalOptions.Unmarshal(b, &confProto)
if err != nil {
return err
}

// Parse TransportModuleBin
c.TransportModuleBin = confProto.GetTransportModule().GetBin()
if len(c.TransportModuleBin) == 0 {
return errors.New("water: transport module binary is not provided in config")
}

// Parse TransportModuleConfig
c.TransportModuleConfig = TransportModuleConfigFromBytes(confProto.GetTransportModule().GetConfig())

// Parse NetworkListener
listenerNetwork, listenerAddress := confProto.GetNetwork().GetListener().GetNetwork(), confProto.GetNetwork().GetListener().GetAddress()
if len(listenerNetwork) > 0 && len(listenerAddress) > 0 {
c.NetworkListener, err = net.Listen(listenerNetwork, listenerAddress)
if err != nil {
return err
}
}

// Parse ModuleConfigFactory
c.ModuleConfigFactory = wasm.NewModuleConfigFactory()
if len(confProto.GetModule().GetArgv()) > 0 {
c.ModuleConfigFactory.SetArgv(confProto.Module.Argv)
}

var envKeys []string
var envValues []string
for k, v := range confProto.GetModule().GetEnv() {
envKeys = append(envKeys, k)
envValues = append(envValues, v)
}
if len(envKeys) > 0 {
c.ModuleConfigFactory.SetEnv(envKeys, envValues)
}

if confProto.GetModule().GetInheritStdin() {
c.ModuleConfigFactory.InheritStdin()
}

if confProto.GetModule().GetInheritStdout() {
c.ModuleConfigFactory.InheritStdout()
}

if confProto.GetModule().GetInheritStderr() {
c.ModuleConfigFactory.InheritStderr()
}

for k, v := range confProto.GetModule().GetPreopenedDirs() {
c.ModuleConfigFactory.SetPreopenDir(k, v)
}

return nil
}
29 changes: 29 additions & 0 deletions configbuilder/config.json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package configbuilder

// ConfigJSON defines the JSON format of the Config.
//
// This struct may fail to fully represent the Config struct, as it is
// non-trivial to represent a func or other non-serialized structures.
type ConfigJSON struct {
TransportModule struct {
BinPath string `json:"bin"` // Path to the transport module binary
ConfigPath string `json:"config,omitempty"` // Path to the transport module config file
} `json:"transport_module"`

Network struct {
// DialerFunc string `json:"dialer_func,omitempty"` // we have no good way to represent a func in JSON format yet
Listener struct {
Network string `json:"network"` // e.g. "tcp"
Address string `json:"address"` // e.g. "0.0.0.0:0"
} `json:"listener,omitempty"`
} `json:"network,omitempty"`

Module struct {
Argv []string `json:"argv,omitempty"` // Warning: this isn't a recommended way to pass configuration to the WebAssembly module. Instead, use TransportModuleConfig for a serializable configuration file.
Env map[string]string `json:"env,omitempty"` // Warning: this isn't a recommended way to pass configuration to the WebAssembly module. Instead, use TransportModuleConfig for a serializable configuration file.
InheritStdin bool `json:"inherit_stdin,omitempty"`
InheritStdout bool `json:"inherit_stdout,omitempty"`
InheritStderr bool `json:"inherit_stderr,omitempty"`
PreopenedDirs map[string]string `json:"preopened_dirs,omitempty"` // hostPath: guestPath
} `json:"module,omitempty"`
}
9 changes: 9 additions & 0 deletions configbuilder/config.pb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package configbuilder

import "github.com/gaukas/water/configbuilder/pb"

// ConfigProtoBuf defines the Protobuf format of the Config.
//
// This struct may fail to fully represent the Config struct, as it is
// non-trivial to represent a func or other non-serialized structures.
type ConfigProtoBuf = pb.Config
4 changes: 4 additions & 0 deletions configbuilder/pb/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
protobuf: config.proto
protoc --go_out=. --go_opt=paths=source_relative $<

.PHONY: protobuf
Loading

0 comments on commit 5a62246

Please sign in to comment.