Clean up client config. Preparation for fixing local dir issues
Signed-off-by: Solomon Hykes <sh.github.6811@hykes.org>
This commit is contained in:
parent
eab6028b70
commit
795b7f585c
150
dagger/client.go
150
dagger/client.go
@ -31,19 +31,19 @@ const (
|
||||
bkBootKey = "boot"
|
||||
bkInputKey = "input"
|
||||
|
||||
defaultBootDir = "."
|
||||
|
||||
// FIXME: rename to defaultConfig ?
|
||||
defaultBootScript = `
|
||||
bootdir: string | *"."
|
||||
bootscript: [
|
||||
// Base client config, for default values & schema validation.
|
||||
baseClientConfig = `
|
||||
close({
|
||||
bootdir: string | *"."
|
||||
boot: [...{do:string,...}] | *[
|
||||
{
|
||||
do: "local"
|
||||
dir: bootdir
|
||||
include: ["*.cue", "cue.mod"]
|
||||
},
|
||||
]
|
||||
`
|
||||
}
|
||||
]
|
||||
})
|
||||
`
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@ -54,26 +54,33 @@ type Client struct {
|
||||
}
|
||||
|
||||
type ClientConfig struct {
|
||||
// Buildkit host address, eg. `docker://buildkitd`
|
||||
Host string
|
||||
// Env boot script, eg. `[{do:"local",dir:"."}]`
|
||||
Boot string
|
||||
// Env boot dir, eg. `.`
|
||||
// May be referenced by boot script.
|
||||
BootDir string
|
||||
// Input overlay, eg. `www: source: #dagger: compute: [{do:"local",dir:"./src"}]`
|
||||
Input string
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, cfg ClientConfig) (*Client, error) {
|
||||
// buildkit client
|
||||
if cfg.Host == "" {
|
||||
cfg.Host = os.Getenv("BUILDKIT_HOST")
|
||||
func NewClient(ctx context.Context, cfg ClientConfig) (result *Client, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// Expand cue errors to get full details
|
||||
err = cueErr(err)
|
||||
}
|
||||
if cfg.Host == "" {
|
||||
cfg.Host = defaultBuildkitHost
|
||||
}
|
||||
if cfg.Boot == "" {
|
||||
cfg.Boot = defaultBootScript
|
||||
}
|
||||
if cfg.BootDir == "" {
|
||||
cfg.BootDir = defaultBootDir
|
||||
}()
|
||||
// Finalize config values
|
||||
localdirs, err := (&cfg).Finalize(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "client config")
|
||||
}
|
||||
log.Ctx(ctx).Debug().
|
||||
Interface("cfg", cfg).
|
||||
Interface("localdirs", localdirs).
|
||||
Msg("finalized client config")
|
||||
c, err := bk.New(ctx, cfg.Host)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "buildkit client")
|
||||
@ -81,29 +88,80 @@ func NewClient(ctx context.Context, cfg ClientConfig) (*Client, error) {
|
||||
return &Client{
|
||||
c: c,
|
||||
cfg: cfg,
|
||||
localdirs: map[string]string{},
|
||||
localdirs: localdirs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) LocalDirs(ctx context.Context) ([]string, error) {
|
||||
boot, err := c.BootScript()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Compile config, fill in final values,
|
||||
// and return a rollup of local directories
|
||||
// referenced in the config.
|
||||
// Localdirs may be referenced in 2 places:
|
||||
// 1. Boot script
|
||||
// 2. Input overlay (FIXME: scan not yet implemented)
|
||||
func (cfg *ClientConfig) Finalize(ctx context.Context) (map[string]string, error) {
|
||||
localdirs := map[string]string{}
|
||||
// buildkit client
|
||||
if cfg.Host == "" {
|
||||
cfg.Host = os.Getenv("BUILDKIT_HOST")
|
||||
}
|
||||
return boot.LocalDirs(ctx)
|
||||
if cfg.Host == "" {
|
||||
cfg.Host = defaultBuildkitHost
|
||||
}
|
||||
// Compile cue template for boot script & boot dir
|
||||
// (using cue because script may reference dir)
|
||||
v, err := cfg.Compile()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid client config")
|
||||
}
|
||||
// Finalize boot script
|
||||
boot, err := v.Get("boot").Script()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid env boot script")
|
||||
}
|
||||
cfg.Boot = string(boot.Value().JSON())
|
||||
// Scan boot script for references to local dirs, to grant access.
|
||||
bootLocalDirs, err := boot.LocalDirs(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "scan boot script for local dir access")
|
||||
}
|
||||
// Finalize boot dir
|
||||
cfg.BootDir, err = v.Get("bootdir").String()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid env boot dir")
|
||||
}
|
||||
// Scan boot script for references to local dirs, to grant access.
|
||||
for _, dir := range bootLocalDirs {
|
||||
// FIXME: randomize local dir references for security
|
||||
// (currently a malicious cue package may guess common local paths
|
||||
// and access the corresponding host directory)
|
||||
localdirs[dir] = dir
|
||||
}
|
||||
// FIXME: scan input overlay for references to local dirs, to grant access.
|
||||
// See issue #41
|
||||
return localdirs, nil
|
||||
}
|
||||
|
||||
func (c *Client) BootScript() (*Script, error) {
|
||||
// Compile client config to a cue value
|
||||
// FIXME: include host and input.
|
||||
func (cfg ClientConfig) Compile() (v *Value, err error) {
|
||||
cc := &Compiler{}
|
||||
src, err := cc.Compile("boot.cue", c.cfg.Boot)
|
||||
v, err = cc.Compile("client.cue", baseClientConfig)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "compile")
|
||||
return nil, errors.Wrap(err, "base client config")
|
||||
}
|
||||
src, err = src.MergeTarget(c.cfg.BootDir, "bootdir")
|
||||
if cfg.BootDir != "" {
|
||||
v, err = v.Merge(cfg.BootDir, "bootdir")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "client config key 'bootdir'")
|
||||
}
|
||||
return src.Get("bootscript").Script()
|
||||
}
|
||||
if cfg.Boot != "" {
|
||||
v, err = v.Merge(cfg.Boot, "boot")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "client config key 'boot'")
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *Client) Compute(ctx context.Context) (*Value, error) {
|
||||
@ -166,22 +224,13 @@ func (c *Client) Compute(ctx context.Context) (*Value, error) {
|
||||
func (c *Client) buildfn(ctx context.Context, ch chan *bk.SolveStatus, w io.WriteCloser) error {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
boot, err := c.BootScript()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "assemble boot script")
|
||||
}
|
||||
bootSource, err := boot.Value().Source()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "serialize boot script")
|
||||
}
|
||||
lg.Debug().Bytes("bootSource", bootSource).Msg("assembled boot script")
|
||||
// Setup solve options
|
||||
opts := bk.SolveOpt{
|
||||
FrontendAttrs: map[string]string{
|
||||
bkInputKey: c.cfg.Input,
|
||||
bkBootKey: string(bootSource),
|
||||
bkBootKey: c.cfg.Boot,
|
||||
},
|
||||
LocalDirs: map[string]string{},
|
||||
LocalDirs: c.localdirs,
|
||||
// FIXME: catch output & return as cue value
|
||||
Exports: []bk.ExportEntry{
|
||||
{
|
||||
@ -192,15 +241,12 @@ func (c *Client) buildfn(ctx context.Context, ch chan *bk.SolveStatus, w io.Writ
|
||||
},
|
||||
},
|
||||
}
|
||||
// Connect local dirs
|
||||
localdirs, err := c.LocalDirs(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "connect local dirs")
|
||||
}
|
||||
for _, dir := range localdirs {
|
||||
opts.LocalDirs[dir] = dir
|
||||
}
|
||||
// Call buildkit solver
|
||||
lg.Debug().
|
||||
Interface("localdirs", opts.LocalDirs).
|
||||
Interface("attrs", opts.FrontendAttrs).
|
||||
Interface("host", c.cfg.Host).
|
||||
Msg("spawning buildkit job")
|
||||
resp, err := c.c.Build(ctx, opts, "", Compute, ch)
|
||||
if err != nil {
|
||||
return errors.Wrap(bkCleanError(err), "buildkit solve")
|
||||
|
@ -73,11 +73,12 @@ func (s *Script) LocalDirs(ctx context.Context) ([]string, error) {
|
||||
var dirs []string
|
||||
err := s.Walk(ctx, func(op *Op) error {
|
||||
if err := op.Validate("#Local"); err != nil {
|
||||
// Ignore all operations except 'do:"local"'
|
||||
return nil
|
||||
}
|
||||
dir, err := op.Get("dir").String()
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "invalid 'local' operation")
|
||||
}
|
||||
dirs = append(dirs, dir)
|
||||
return nil
|
||||
|
@ -85,11 +85,11 @@ func TestWalkBootScript(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
cc := &Compiler{}
|
||||
cfg, err := cc.Compile("clientconfig.cue", defaultBootScript)
|
||||
cfg, err := cc.Compile("clientconfig.cue", baseClientConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
script, err := cfg.Get("bootscript").Script()
|
||||
script, err := cfg.Get("boot").Script()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func TestJSON(t *testing.T) {
|
||||
|
||||
func TestCompileBootScript(t *testing.T) {
|
||||
cc := &Compiler{}
|
||||
cfg, err := cc.Compile("boot.cue", defaultBootScript)
|
||||
cfg, err := cc.Compile("boot.cue", baseClientConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user