dagger.#FS support
- Implement dagger.#FS support - Migrate `context.imports` to dagger.#FS - Backward compat: dagger.#FS can be passed in lieu of a dagger.#Artifact - For instance, an import (`dagger.#FS`) can be passed to the current `yarn.#Package` implementation Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
6bedfb7c63
commit
0aea10d23e
@ -75,7 +75,7 @@ func New(ctx context.Context, host string, cfg Config) (*Client, error) {
|
|||||||
type DoFunc func(context.Context, solver.Solver) error
|
type DoFunc func(context.Context, solver.Solver) error
|
||||||
|
|
||||||
// FIXME: return completed *Route, instead of *compiler.Value
|
// FIXME: return completed *Route, instead of *compiler.Value
|
||||||
func (c *Client) Do(ctx context.Context, pctx *plancontext.Context, localdirs map[string]string, fn DoFunc) error {
|
func (c *Client) Do(ctx context.Context, pctx *plancontext.Context, fn DoFunc) error {
|
||||||
lg := log.Ctx(ctx)
|
lg := log.Ctx(ctx)
|
||||||
eg, gctx := errgroup.WithContext(ctx)
|
eg, gctx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
@ -90,13 +90,13 @@ func (c *Client) Do(ctx context.Context, pctx *plancontext.Context, localdirs ma
|
|||||||
|
|
||||||
// Spawn build function
|
// Spawn build function
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
return c.buildfn(gctx, pctx, localdirs, fn, events)
|
return c.buildfn(gctx, pctx, fn, events)
|
||||||
})
|
})
|
||||||
|
|
||||||
return eg.Wait()
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) buildfn(ctx context.Context, pctx *plancontext.Context, localdirs map[string]string, fn DoFunc, ch chan *bk.SolveStatus) error {
|
func (c *Client) buildfn(ctx context.Context, pctx *plancontext.Context, fn DoFunc, ch chan *bk.SolveStatus) error {
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
// Close output channel
|
// Close output channel
|
||||||
@ -111,6 +111,11 @@ func (c *Client) buildfn(ctx context.Context, pctx *plancontext.Context, localdi
|
|||||||
// buildkit auth provider (registry)
|
// buildkit auth provider (registry)
|
||||||
auth := solver.NewRegistryAuthProvider()
|
auth := solver.NewRegistryAuthProvider()
|
||||||
|
|
||||||
|
localdirs, err := pctx.LocalDirs.Paths()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Setup solve options
|
// Setup solve options
|
||||||
opts := bk.SolveOpt{
|
opts := bk.SolveOpt{
|
||||||
LocalDirs: localdirs,
|
LocalDirs: localdirs,
|
||||||
|
@ -198,7 +198,7 @@ var computeCmd = &cobra.Command{
|
|||||||
lg.Fatal().Err(err).Msg("unable to create environment")
|
lg.Fatal().Err(err).Msg("unable to create environment")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||||
// check that all inputs are set
|
// check that all inputs are set
|
||||||
checkInputs(ctx, env)
|
checkInputs(ctx, env)
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ var editCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
cl := common.NewClient(ctx)
|
cl := common.NewClient(ctx)
|
||||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||||
// check for cue errors by scanning all the inputs
|
// check for cue errors by scanning all the inputs
|
||||||
_, err := env.ScanInputs(ctx, true)
|
_, err := env.ScanInputs(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -47,7 +47,7 @@ var listCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
cl := common.NewClient(ctx)
|
cl := common.NewClient(ctx)
|
||||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||||
inputs, err := env.ScanInputs(ctx, false)
|
inputs, err := env.ScanInputs(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -59,7 +59,7 @@ func updateEnvironmentInput(ctx context.Context, cmd *cobra.Command, target stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
cl := common.NewClient(ctx)
|
cl := common.NewClient(ctx)
|
||||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||||
// the inputs are set, check for cue errors by scanning all the inputs
|
// the inputs are set, check for cue errors by scanning all the inputs
|
||||||
_, err := env.ScanInputs(ctx, true)
|
_, err := env.ScanInputs(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -46,7 +46,7 @@ var listCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
cl := common.NewClient(ctx)
|
cl := common.NewClient(ctx)
|
||||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||||
return ListOutputs(ctx, env, true)
|
return ListOutputs(ctx, env, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ var upCmd = &cobra.Command{
|
|||||||
lg.Fatal().Err(err).Msg("unable to create environment")
|
lg.Fatal().Err(err).Msg("unable to create environment")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||||
// check that all inputs are set
|
// check that all inputs are set
|
||||||
if err := checkInputs(ctx, env); err != nil {
|
if err := checkInputs(ctx, env); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -119,11 +119,7 @@ func europaUp(ctx context.Context, cl *client.Client, path string) error {
|
|||||||
lg.Fatal().Err(err).Msg("failed to load plan")
|
lg.Fatal().Err(err).Msg("failed to load plan")
|
||||||
}
|
}
|
||||||
|
|
||||||
localdirs, err := p.LocalDirectories()
|
return cl.Do(ctx, p.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return cl.Do(ctx, p.Context(), localdirs, func(ctx context.Context, s solver.Solver) error {
|
|
||||||
if err := p.Up(ctx, s); err != nil {
|
if err := p.Up(ctx, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,10 @@ func NewValue() *Value {
|
|||||||
return DefaultCompiler.NewValue()
|
return DefaultCompiler.NewValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewValueWithContent(x interface{}, selectors ...cue.Selector) (*Value, error) {
|
||||||
|
return DefaultCompiler.NewValueWithContent(x, selectors...)
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME can be refactored away now?
|
// FIXME can be refactored away now?
|
||||||
func Wrap(v cue.Value) *Value {
|
func Wrap(v cue.Value) *Value {
|
||||||
return DefaultCompiler.Wrap(v)
|
return DefaultCompiler.Wrap(v)
|
||||||
@ -80,6 +84,14 @@ func (c *Compiler) NewValue() *Value {
|
|||||||
return empty
|
return empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) NewValueWithContent(x interface{}, selectors ...cue.Selector) (*Value, error) {
|
||||||
|
v := c.NewValue()
|
||||||
|
if err := v.FillPath(cue.MakePath(selectors...), x); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) Compile(name string, src string) (*Value, error) {
|
func (c *Compiler) Compile(name string, src string) (*Value, error) {
|
||||||
c.lock()
|
c.lock()
|
||||||
defer c.unlock()
|
defer c.unlock()
|
||||||
|
@ -20,6 +20,18 @@ _No input._
|
|||||||
|
|
||||||
_No output._
|
_No output._
|
||||||
|
|
||||||
|
## dagger.#FS
|
||||||
|
|
||||||
|
A reference to a filesystem tree. For example: - The root filesystem of a container - A source code repository - A directory containing binary artifacts Rule of thumb: if it fits in a tar archive, it fits in a #FS.
|
||||||
|
|
||||||
|
### dagger.#FS Inputs
|
||||||
|
|
||||||
|
_No input._
|
||||||
|
|
||||||
|
### dagger.#FS Outputs
|
||||||
|
|
||||||
|
_No output._
|
||||||
|
|
||||||
## dagger.#Plan
|
## dagger.#Plan
|
||||||
|
|
||||||
A deployment plan executed by `dagger up`
|
A deployment plan executed by `dagger up`
|
||||||
@ -34,7 +46,7 @@ _No output._
|
|||||||
|
|
||||||
## dagger.#Secret
|
## dagger.#Secret
|
||||||
|
|
||||||
Secret value
|
A reference to an external secret, for example: - A password - A SSH private key - An API token Secrets are never merged in the Cue tree. They can only be used by a special filesystem mount designed to minimize leak risk.
|
||||||
|
|
||||||
### dagger.#Secret Inputs
|
### dagger.#Secret Inputs
|
||||||
|
|
||||||
|
@ -40,6 +40,13 @@ const (
|
|||||||
StateCompleted = State("completed")
|
StateCompleted = State("completed")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fsIDPath = cue.MakePath(
|
||||||
|
cue.Hid("_fs", "alpha.dagger.io/dagger"),
|
||||||
|
cue.Str("id"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// An execution pipeline
|
// An execution pipeline
|
||||||
type Pipeline struct {
|
type Pipeline struct {
|
||||||
code *compiler.Value
|
code *compiler.Value
|
||||||
@ -95,8 +102,19 @@ func IsComponent(v *compiler.Value) bool {
|
|||||||
return v.Lookup("#up").Exists()
|
return v.Lookup("#up").Exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isFS(v *compiler.Value) bool {
|
||||||
|
return v.LookupPath(fsIDPath).Exists()
|
||||||
|
}
|
||||||
|
|
||||||
func ops(code *compiler.Value) ([]*compiler.Value, error) {
|
func ops(code *compiler.Value) ([]*compiler.Value, error) {
|
||||||
ops := []*compiler.Value{}
|
ops := []*compiler.Value{}
|
||||||
|
|
||||||
|
// dagger.#FS forward compat
|
||||||
|
// FIXME: remove this
|
||||||
|
if isFS(code) {
|
||||||
|
ops = append(ops, code)
|
||||||
|
}
|
||||||
|
|
||||||
// 1. attachment array
|
// 1. attachment array
|
||||||
if IsComponent(code) {
|
if IsComponent(code) {
|
||||||
xops, err := code.Lookup("#up").List()
|
xops, err := code.Lookup("#up").List()
|
||||||
@ -138,6 +156,12 @@ func Analyze(fn func(*compiler.Value) error, code *compiler.Value) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func analyzeOp(fn func(*compiler.Value) error, op *compiler.Value) error {
|
func analyzeOp(fn func(*compiler.Value) error, op *compiler.Value) error {
|
||||||
|
// dagger.#FS forward compat
|
||||||
|
// FIXME: remove this
|
||||||
|
if isFS(op) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := fn(op); err != nil {
|
if err := fn(op); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -243,6 +267,21 @@ func (p *Pipeline) run(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pipeline) doOp(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
|
func (p *Pipeline) doOp(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
|
||||||
|
// dagger.#FS forward compat
|
||||||
|
// FIXME: remove this
|
||||||
|
if isFS(op) {
|
||||||
|
id, err := op.LookupPath(fsIDPath).String()
|
||||||
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := p.pctx.FS.Get(plancontext.ContextKey(id))
|
||||||
|
if fs == nil {
|
||||||
|
return st, fmt.Errorf("fs %q not found", id)
|
||||||
|
}
|
||||||
|
return fs.Result.ToState()
|
||||||
|
}
|
||||||
|
|
||||||
do, err := op.Lookup("do").String()
|
do, err := op.Lookup("do").String()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return st, err
|
return st, err
|
||||||
@ -361,31 +400,49 @@ func (p *Pipeline) Copy(ctx context.Context, op *compiler.Value, st llb.State) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pipeline) Local(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
|
func (p *Pipeline) Local(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
|
||||||
id, err := op.Lookup("id").String()
|
dir, err := op.Lookup("dir").String()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return st, err
|
return st, err
|
||||||
}
|
}
|
||||||
dir := p.pctx.Directories.Get(plancontext.ContextKey(id))
|
|
||||||
if dir == nil {
|
|
||||||
return st, fmt.Errorf("directory %q not found", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := []llb.LocalOption{
|
opts := []llb.LocalOption{
|
||||||
llb.WithCustomName(p.vertexNamef("Local %s", dir.Path)),
|
llb.WithCustomName(p.vertexNamef("Local %s", dir)),
|
||||||
// Without hint, multiple `llb.Local` operations on the
|
// Without hint, multiple `llb.Local` operations on the
|
||||||
// same path get a different digest.
|
// same path get a different digest.
|
||||||
llb.SessionID(p.s.SessionID()),
|
llb.SessionID(p.s.SessionID()),
|
||||||
llb.SharedKeyHint(dir.Path),
|
llb.SharedKeyHint(dir),
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dir.Include) > 0 {
|
includes, err := op.Lookup("include").List()
|
||||||
opts = append(opts, llb.IncludePatterns(dir.Include))
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
|
if len(includes) > 0 {
|
||||||
|
includePatterns := []string{}
|
||||||
|
for _, i := range includes {
|
||||||
|
pattern, err := i.String()
|
||||||
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
|
includePatterns = append(includePatterns, pattern)
|
||||||
|
}
|
||||||
|
opts = append(opts, llb.IncludePatterns(includePatterns))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
excludes, err := op.Lookup("exclude").List()
|
||||||
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
// Excludes .dagger directory by default
|
// Excludes .dagger directory by default
|
||||||
excludePatterns := []string{"**/.dagger/"}
|
excludePatterns := []string{"**/.dagger/"}
|
||||||
if len(dir.Exclude) > 0 {
|
if len(excludes) > 0 {
|
||||||
excludePatterns = dir.Exclude
|
for _, i := range excludes {
|
||||||
|
pattern, err := i.String()
|
||||||
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
|
excludePatterns = append(excludePatterns, pattern)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
opts = append(opts, llb.ExcludePatterns(excludePatterns))
|
opts = append(opts, llb.ExcludePatterns(excludePatterns))
|
||||||
|
|
||||||
@ -396,13 +453,13 @@ func (p *Pipeline) Local(ctx context.Context, op *compiler.Value, st llb.State)
|
|||||||
return st.File(
|
return st.File(
|
||||||
llb.Copy(
|
llb.Copy(
|
||||||
llb.Local(
|
llb.Local(
|
||||||
dir.Path,
|
dir,
|
||||||
opts...,
|
opts...,
|
||||||
),
|
),
|
||||||
"/",
|
"/",
|
||||||
"/",
|
"/",
|
||||||
),
|
),
|
||||||
llb.WithCustomName(p.vertexNamef("Local %s [copy]", dir.Path)),
|
llb.WithCustomName(p.vertexNamef("Local %s [copy]", dir)),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
31
plan/plan.go
31
plan/plan.go
@ -3,7 +3,6 @@ package plan
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -35,10 +34,16 @@ func Load(ctx context.Context, path, pkg string) (*Plan, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Plan{
|
p := &Plan{
|
||||||
context: plancontext.New(),
|
context: plancontext.New(),
|
||||||
source: v,
|
source: v,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
if err := p.registerLocalDirs(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plan) Context() *plancontext.Context {
|
func (p *Plan) Context() *plancontext.Context {
|
||||||
@ -49,30 +54,26 @@ func (p *Plan) Source() *compiler.Value {
|
|||||||
return p.source
|
return p.source
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalDirectories scans the context for local imports.
|
// registerLocalDirectories scans the context for local imports.
|
||||||
// BuildKit requires to known the list of directories ahead of time.
|
// BuildKit requires to known the list of directories ahead of time.
|
||||||
func (p *Plan) LocalDirectories() (map[string]string, error) {
|
func (p *Plan) registerLocalDirs() error {
|
||||||
dirs := map[string]string{}
|
|
||||||
|
|
||||||
imports, err := p.source.Lookup("context.imports").Fields()
|
imports, err := p.source.Lookup("context.imports").Fields()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range imports {
|
for _, v := range imports {
|
||||||
dir, err := v.Value.Lookup("path").String()
|
dir, err := v.Value.Lookup("path").String()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
|
||||||
abs, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dirs[dir] = abs
|
p.context.LocalDirs.Register(&plancontext.LocalDir{
|
||||||
|
Path: dir,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return dirs, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up executes the plan
|
// Up executes the plan
|
||||||
|
@ -2,9 +2,9 @@ package task
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
|
"cuelang.org/go/cue"
|
||||||
|
"github.com/moby/buildkit/client/llb"
|
||||||
"go.dagger.io/dagger/compiler"
|
"go.dagger.io/dagger/compiler"
|
||||||
"go.dagger.io/dagger/plancontext"
|
"go.dagger.io/dagger/plancontext"
|
||||||
"go.dagger.io/dagger/solver"
|
"go.dagger.io/dagger/solver"
|
||||||
@ -17,21 +17,64 @@ func init() {
|
|||||||
type importTask struct {
|
type importTask struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c importTask) Run(ctx context.Context, pctx *plancontext.Context, _ solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
func (c importTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
||||||
var dir *plancontext.Directory
|
var dir struct {
|
||||||
|
Path string
|
||||||
|
Include []string
|
||||||
|
Exclude []string
|
||||||
|
}
|
||||||
|
|
||||||
if err := v.Decode(&dir); err != nil {
|
if err := v.Decode(&dir); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that directory exists
|
opts := []llb.LocalOption{
|
||||||
if _, err := os.Stat(dir.Path); os.IsNotExist(err) {
|
withCustomName(v, "Local %s", dir.Path),
|
||||||
return nil, fmt.Errorf("%q dir doesn't exist", dir.Path)
|
// Without hint, multiple `llb.Local` operations on the
|
||||||
|
// same path get a different digest.
|
||||||
|
llb.SessionID(s.SessionID()),
|
||||||
|
llb.SharedKeyHint(dir.Path),
|
||||||
}
|
}
|
||||||
|
|
||||||
id := pctx.Directories.Register(dir)
|
if len(dir.Include) > 0 {
|
||||||
return compiler.Compile("", fmt.Sprintf(
|
opts = append(opts, llb.IncludePatterns(dir.Include))
|
||||||
`fs: #up: [{do: "local", id: %q}]`,
|
}
|
||||||
id,
|
|
||||||
))
|
// Excludes .dagger directory by default
|
||||||
|
excludePatterns := []string{"**/.dagger/"}
|
||||||
|
if len(dir.Exclude) > 0 {
|
||||||
|
excludePatterns = dir.Exclude
|
||||||
|
}
|
||||||
|
opts = append(opts, llb.ExcludePatterns(excludePatterns))
|
||||||
|
|
||||||
|
// FIXME: Remove the `Copy` and use `Local` directly.
|
||||||
|
//
|
||||||
|
// Copy'ing is a costly operation which should be unnecessary.
|
||||||
|
// However, using llb.Local directly breaks caching sometimes for unknown reasons.
|
||||||
|
st := llb.Scratch().File(
|
||||||
|
llb.Copy(
|
||||||
|
llb.Local(
|
||||||
|
dir.Path,
|
||||||
|
opts...,
|
||||||
|
),
|
||||||
|
"/",
|
||||||
|
"/",
|
||||||
|
),
|
||||||
|
withCustomName(v, "Local %s [copy]", dir.Path),
|
||||||
|
)
|
||||||
|
|
||||||
|
result, err := s.Solve(ctx, st, pctx.Platform.Get())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id := pctx.FS.Register(&plancontext.FS{
|
||||||
|
Result: result,
|
||||||
|
})
|
||||||
|
|
||||||
|
return compiler.NewValueWithContent(id,
|
||||||
|
cue.Str("fs"),
|
||||||
|
cue.Hid("_fs", "alpha.dagger.io/dagger"),
|
||||||
|
cue.Str("id"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,8 @@ func (c secretEnvTask) Run(ctx context.Context, pctx *plancontext.Context, _ sol
|
|||||||
PlainText: env,
|
PlainText: env,
|
||||||
})
|
})
|
||||||
|
|
||||||
out := compiler.NewValue()
|
return compiler.NewValueWithContent(id,
|
||||||
if err := out.FillPath(cue.ParsePath("contents.id"), id); err != nil {
|
cue.Str("contents"),
|
||||||
return nil, err
|
cue.Str("id"),
|
||||||
}
|
)
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,8 @@ func (c secretFileTask) Run(ctx context.Context, pctx *plancontext.Context, _ so
|
|||||||
PlainText: string(data),
|
PlainText: string(data),
|
||||||
})
|
})
|
||||||
|
|
||||||
out := compiler.NewValue()
|
return compiler.NewValueWithContent(id,
|
||||||
if err := out.FillPath(cue.ParsePath("contents.id"), id); err != nil {
|
cue.Str("contents"),
|
||||||
return nil, err
|
cue.Str("id"),
|
||||||
}
|
)
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
14
plan/task/util.go
Normal file
14
plan/task/util.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/moby/buildkit/client/llb"
|
||||||
|
"go.dagger.io/dagger/compiler"
|
||||||
|
)
|
||||||
|
|
||||||
|
func withCustomName(v *compiler.Value, format string, a ...interface{}) llb.ConstraintsOpt {
|
||||||
|
prefix := fmt.Sprintf("@%s@", v.Path().String())
|
||||||
|
name := fmt.Sprintf(format, a...)
|
||||||
|
return llb.WithCustomName(prefix + " " + name)
|
||||||
|
}
|
@ -15,10 +15,11 @@ type ContextKey string
|
|||||||
// id := ctx.Secrets.Register("mysecret")
|
// id := ctx.Secrets.Register("mysecret")
|
||||||
// secret := ctx.Secrets.Get(id)
|
// secret := ctx.Secrets.Get(id)
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Platform *platformContext
|
Platform *platformContext
|
||||||
Directories *directoryContext
|
FS *fsContext
|
||||||
Secrets *secretContext
|
LocalDirs *localDirContext
|
||||||
Services *serviceContext
|
Secrets *secretContext
|
||||||
|
Services *serviceContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Context {
|
func New() *Context {
|
||||||
@ -26,8 +27,11 @@ func New() *Context {
|
|||||||
Platform: &platformContext{
|
Platform: &platformContext{
|
||||||
platform: defaultPlatform,
|
platform: defaultPlatform,
|
||||||
},
|
},
|
||||||
Directories: &directoryContext{
|
FS: &fsContext{
|
||||||
store: make(map[ContextKey]*Directory),
|
store: make(map[ContextKey]*FS),
|
||||||
|
},
|
||||||
|
LocalDirs: &localDirContext{
|
||||||
|
store: make(map[ContextKey]*LocalDir),
|
||||||
},
|
},
|
||||||
Secrets: &secretContext{
|
Secrets: &secretContext{
|
||||||
store: make(map[ContextKey]*Secret),
|
store: make(map[ContextKey]*Secret),
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
package plancontext
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
type Directory struct {
|
|
||||||
Path string
|
|
||||||
Include []string
|
|
||||||
Exclude []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type directoryContext struct {
|
|
||||||
l sync.RWMutex
|
|
||||||
store map[ContextKey]*Directory
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *directoryContext) Register(directory *Directory) ContextKey {
|
|
||||||
c.l.Lock()
|
|
||||||
defer c.l.Unlock()
|
|
||||||
|
|
||||||
id := hashID(directory)
|
|
||||||
c.store[id] = directory
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *directoryContext) Get(id ContextKey) *Directory {
|
|
||||||
c.l.RLock()
|
|
||||||
defer c.l.RUnlock()
|
|
||||||
|
|
||||||
return c.store[id]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *directoryContext) List() []*Directory {
|
|
||||||
c.l.RLock()
|
|
||||||
defer c.l.RUnlock()
|
|
||||||
|
|
||||||
directories := make([]*Directory, 0, len(c.store))
|
|
||||||
for _, d := range c.store {
|
|
||||||
directories = append(directories, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
return directories
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *directoryContext) Paths() map[string]string {
|
|
||||||
c.l.RLock()
|
|
||||||
defer c.l.RUnlock()
|
|
||||||
|
|
||||||
directories := make(map[string]string)
|
|
||||||
for _, d := range c.store {
|
|
||||||
directories[d.Path] = d.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
return directories
|
|
||||||
}
|
|
32
plancontext/fs.go
Normal file
32
plancontext/fs.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package plancontext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
bkgw "github.com/moby/buildkit/frontend/gateway/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FS struct {
|
||||||
|
Result bkgw.Reference
|
||||||
|
}
|
||||||
|
|
||||||
|
type fsContext struct {
|
||||||
|
l sync.RWMutex
|
||||||
|
store map[ContextKey]*FS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fsContext) Register(fs *FS) ContextKey {
|
||||||
|
c.l.Lock()
|
||||||
|
defer c.l.Unlock()
|
||||||
|
|
||||||
|
id := hashID(fs)
|
||||||
|
c.store[id] = fs
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fsContext) Get(id ContextKey) *FS {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
|
|
||||||
|
return c.store[id]
|
||||||
|
}
|
60
plancontext/localdir.go
Normal file
60
plancontext/localdir.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package plancontext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LocalDir struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type localDirContext struct {
|
||||||
|
l sync.RWMutex
|
||||||
|
store map[ContextKey]*LocalDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *localDirContext) Register(directory *LocalDir) ContextKey {
|
||||||
|
c.l.Lock()
|
||||||
|
defer c.l.Unlock()
|
||||||
|
|
||||||
|
id := hashID(directory)
|
||||||
|
c.store[id] = directory
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *localDirContext) Get(id ContextKey) *LocalDir {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
|
|
||||||
|
return c.store[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *localDirContext) List() []*LocalDir {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
|
|
||||||
|
directories := make([]*LocalDir, 0, len(c.store))
|
||||||
|
for _, d := range c.store {
|
||||||
|
directories = append(directories, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return directories
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *localDirContext) Paths() (map[string]string, error) {
|
||||||
|
c.l.RLock()
|
||||||
|
defer c.l.RUnlock()
|
||||||
|
|
||||||
|
directories := make(map[string]string)
|
||||||
|
for _, d := range c.store {
|
||||||
|
abs, err := filepath.Abs(d.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
directories[d.Path] = abs
|
||||||
|
}
|
||||||
|
|
||||||
|
return directories, nil
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@ -82,6 +83,27 @@ type dirInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dir dirInput) Compile(state *State) (*compiler.Value, error) {
|
func (dir dirInput) Compile(state *State) (*compiler.Value, error) {
|
||||||
|
// FIXME: serialize an intermediate struct, instead of generating cue source
|
||||||
|
|
||||||
|
// json.Marshal([]string{}) returns []byte("null"), which wreaks havoc
|
||||||
|
// in Cue because `null` is not a `[...string]`
|
||||||
|
includeLLB := []byte("[]")
|
||||||
|
if len(dir.Include) > 0 {
|
||||||
|
var err error
|
||||||
|
includeLLB, err = json.Marshal(dir.Include)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
excludeLLB := []byte("[]")
|
||||||
|
if len(dir.Exclude) > 0 {
|
||||||
|
var err error
|
||||||
|
excludeLLB, err = json.Marshal(dir.Exclude)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p := dir.Path
|
p := dir.Path
|
||||||
if !filepath.IsAbs(p) {
|
if !filepath.IsAbs(p) {
|
||||||
p = filepath.Clean(path.Join(state.Project, dir.Path))
|
p = filepath.Clean(path.Join(state.Project, dir.Path))
|
||||||
@ -94,15 +116,20 @@ func (dir dirInput) Compile(state *State) (*compiler.Value, error) {
|
|||||||
return nil, fmt.Errorf("%q dir doesn't exist", dir.Path)
|
return nil, fmt.Errorf("%q dir doesn't exist", dir.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
id := state.Context.Directories.Register(&plancontext.Directory{
|
dirPath, err := json.Marshal(p)
|
||||||
Path: p,
|
if err != nil {
|
||||||
Include: dir.Include,
|
return nil, err
|
||||||
Exclude: dir.Exclude,
|
}
|
||||||
|
|
||||||
|
state.Context.LocalDirs.Register(&plancontext.LocalDir{
|
||||||
|
Path: p,
|
||||||
})
|
})
|
||||||
|
|
||||||
llb := fmt.Sprintf(
|
llb := fmt.Sprintf(
|
||||||
`#up: [{do:"local", id: "%s"}]`,
|
`#up: [{do: "local", dir: %s, include: %s, exclude: %s}]`,
|
||||||
id,
|
dirPath,
|
||||||
|
includeLLB,
|
||||||
|
excludeLLB,
|
||||||
)
|
)
|
||||||
return compiler.Compile("", llb)
|
return compiler.Compile("", llb)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,16 @@ import (
|
|||||||
"alpha.dagger.io/dagger/op"
|
"alpha.dagger.io/dagger/op"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// A reference to a filesystem tree.
|
||||||
|
// For example:
|
||||||
|
// - The root filesystem of a container
|
||||||
|
// - A source code repository
|
||||||
|
// - A directory containing binary artifacts
|
||||||
|
// Rule of thumb: if it fits in a tar archive, it fits in a #FS.
|
||||||
|
#FS: {
|
||||||
|
_fs: id: string
|
||||||
|
}
|
||||||
|
|
||||||
// An artifact such as source code checkout, container image, binary archive...
|
// An artifact such as source code checkout, container image, binary archive...
|
||||||
// May be passed as user input, or computed by a buildkit pipeline
|
// May be passed as user input, or computed by a buildkit pipeline
|
||||||
#Artifact: {
|
#Artifact: {
|
||||||
@ -21,7 +31,12 @@ import (
|
|||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secret value
|
// A reference to an external secret, for example:
|
||||||
|
// - A password
|
||||||
|
// - A SSH private key
|
||||||
|
// - An API token
|
||||||
|
// Secrets are never merged in the Cue tree. They can only be used
|
||||||
|
// by a special filesystem mount designed to minimize leak risk.
|
||||||
#Secret: {
|
#Secret: {
|
||||||
@dagger(secret)
|
@dagger(secret)
|
||||||
|
|
||||||
|
@ -20,8 +20,10 @@ package op
|
|||||||
}
|
}
|
||||||
|
|
||||||
#Local: {
|
#Local: {
|
||||||
do: "local"
|
do: "local"
|
||||||
id: string
|
dir: string
|
||||||
|
include: [...string]
|
||||||
|
exclude: [...string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: bring back load (more efficient than copy)
|
// FIXME: bring back load (more efficient than copy)
|
||||||
|
@ -20,7 +20,7 @@ package dagger
|
|||||||
path: string
|
path: string
|
||||||
include?: [...string]
|
include?: [...string]
|
||||||
exclude?: [...string]
|
exclude?: [...string]
|
||||||
fs: #Artifact
|
fs: #FS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Securely load external secrets
|
// Securely load external secrets
|
||||||
|
Reference in New Issue
Block a user