2021-05-26 01:30:49 +02:00
|
|
|
package environment
|
2021-03-23 08:22:50 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-03-25 01:55:21 +01:00
|
|
|
"fmt"
|
2021-03-23 08:22:50 +01:00
|
|
|
|
2021-03-25 01:55:21 +01:00
|
|
|
"cuelang.org/go/cue"
|
|
|
|
cueflow "cuelang.org/go/tools/flow"
|
2021-05-26 01:53:26 +02:00
|
|
|
"go.dagger.io/dagger/compiler"
|
|
|
|
"go.dagger.io/dagger/solver"
|
|
|
|
"go.dagger.io/dagger/state"
|
2021-03-24 23:03:05 +01:00
|
|
|
|
2021-07-02 14:26:35 +02:00
|
|
|
"go.opentelemetry.io/otel"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
|
|
|
|
2021-03-25 01:55:21 +01:00
|
|
|
"github.com/rs/zerolog/log"
|
2021-03-24 23:03:05 +01:00
|
|
|
)
|
|
|
|
|
2021-04-27 20:59:04 +02:00
|
|
|
type Environment struct {
|
2021-05-07 23:45:15 +02:00
|
|
|
state *state.State
|
2021-04-09 03:09:10 +02:00
|
|
|
|
|
|
|
// Layer 1: plan configuration
|
|
|
|
plan *compiler.Value
|
|
|
|
|
|
|
|
// Layer 2: user inputs
|
|
|
|
input *compiler.Value
|
|
|
|
|
2021-08-20 15:52:58 +02:00
|
|
|
// plan + inputs
|
|
|
|
src *compiler.Value
|
|
|
|
|
2021-04-09 03:09:10 +02:00
|
|
|
// Layer 3: computed values
|
|
|
|
computed *compiler.Value
|
2021-03-24 18:37:50 +01:00
|
|
|
}
|
|
|
|
|
2021-05-26 01:30:49 +02:00
|
|
|
func New(st *state.State) (*Environment, error) {
|
2021-08-20 15:52:58 +02:00
|
|
|
var err error
|
|
|
|
|
2021-05-07 23:39:12 +02:00
|
|
|
e := &Environment{
|
2021-04-09 03:09:10 +02:00
|
|
|
state: st,
|
2021-08-20 15:52:58 +02:00
|
|
|
}
|
2021-04-09 03:09:10 +02:00
|
|
|
|
2021-08-20 15:52:58 +02:00
|
|
|
e.plan, err = st.CompilePlan(context.TODO())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-03-25 01:55:21 +01:00
|
|
|
}
|
2021-03-24 18:37:50 +01:00
|
|
|
|
2021-08-20 15:52:58 +02:00
|
|
|
e.input, err = st.CompileInputs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
e.computed = compiler.NewValue()
|
|
|
|
|
|
|
|
e.src = compiler.NewValue()
|
|
|
|
if err := e.src.FillPath(cue.MakePath(), e.plan); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := e.src.FillPath(cue.MakePath(), e.input); err != nil {
|
|
|
|
return nil, err
|
2021-03-25 01:55:21 +01:00
|
|
|
}
|
|
|
|
|
2021-05-07 23:39:12 +02:00
|
|
|
return e, nil
|
2021-03-24 18:37:50 +01:00
|
|
|
}
|
|
|
|
|
2021-05-07 23:39:12 +02:00
|
|
|
func (e *Environment) Name() string {
|
|
|
|
return e.state.Name
|
2021-03-27 00:44:13 +01:00
|
|
|
}
|
|
|
|
|
2021-05-07 23:39:12 +02:00
|
|
|
func (e *Environment) Computed() *compiler.Value {
|
|
|
|
return e.computed
|
2021-03-24 18:37:50 +01:00
|
|
|
}
|
|
|
|
|
2021-04-27 20:59:04 +02:00
|
|
|
// Scan all scripts in the environment for references to local directories (do:"local"),
|
2021-03-25 01:55:21 +01:00
|
|
|
// and return all referenced directory names.
|
|
|
|
// This is used by clients to grant access to local directories when they are referenced
|
|
|
|
// by user-specified scripts.
|
2021-05-07 23:39:12 +02:00
|
|
|
func (e *Environment) LocalDirs() map[string]string {
|
2021-03-25 01:55:21 +01:00
|
|
|
dirs := map[string]string{}
|
2021-05-05 01:26:56 +02:00
|
|
|
localdirs := func(code *compiler.Value) {
|
2021-03-25 01:55:21 +01:00
|
|
|
Analyze(
|
|
|
|
func(op *compiler.Value) error {
|
2021-03-31 22:40:46 +02:00
|
|
|
do, err := op.Lookup("do").String()
|
2021-03-25 01:55:21 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if do != "local" {
|
|
|
|
return nil
|
|
|
|
}
|
2021-03-31 22:40:46 +02:00
|
|
|
dir, err := op.Lookup("dir").String()
|
2021-03-25 01:55:21 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
dirs[dir] = dir
|
|
|
|
return nil
|
|
|
|
},
|
2021-05-05 01:26:56 +02:00
|
|
|
code,
|
2021-03-25 01:55:21 +01:00
|
|
|
)
|
|
|
|
}
|
2021-04-27 20:59:04 +02:00
|
|
|
// 1. Scan the environment state
|
2021-03-25 01:55:21 +01:00
|
|
|
// FIXME: use a common `flow` instance to avoid rescanning the tree.
|
2021-05-01 03:05:37 +02:00
|
|
|
src := compiler.NewValue()
|
2021-05-07 23:39:12 +02:00
|
|
|
if err := src.FillPath(cue.MakePath(), e.plan); err != nil {
|
2021-05-01 03:05:37 +02:00
|
|
|
return nil
|
|
|
|
}
|
2021-05-07 23:39:12 +02:00
|
|
|
if err := src.FillPath(cue.MakePath(), e.input); err != nil {
|
2021-05-01 03:05:37 +02:00
|
|
|
return nil
|
2021-04-07 02:43:12 +02:00
|
|
|
}
|
|
|
|
flow := cueflow.New(
|
|
|
|
&cueflow.Config{},
|
2021-05-01 03:05:37 +02:00
|
|
|
src.Cue(),
|
|
|
|
newTaskFunc(noOpRunner),
|
2021-04-07 02:43:12 +02:00
|
|
|
)
|
2021-03-25 01:55:21 +01:00
|
|
|
for _, t := range flow.Tasks() {
|
2021-05-01 03:05:37 +02:00
|
|
|
v := compiler.Wrap(t.Value())
|
2021-04-02 23:58:12 +02:00
|
|
|
localdirs(v.Lookup("#up"))
|
2021-03-25 01:55:21 +01:00
|
|
|
}
|
2021-03-23 08:22:50 +01:00
|
|
|
|
2021-03-25 01:55:21 +01:00
|
|
|
return dirs
|
2021-03-23 08:22:50 +01:00
|
|
|
}
|
2021-03-23 23:27:16 +01:00
|
|
|
|
2021-05-27 18:45:24 +02:00
|
|
|
// Up missing values in environment configuration, and write them to state.
|
|
|
|
func (e *Environment) Up(ctx context.Context, s solver.Solver) error {
|
2021-07-02 14:26:35 +02:00
|
|
|
tr := otel.Tracer("environment")
|
|
|
|
ctx, span := tr.Start(ctx, "environment.Up")
|
|
|
|
defer span.End()
|
2021-05-27 18:45:24 +02:00
|
|
|
|
2021-03-25 01:55:21 +01:00
|
|
|
// Orchestrate execution with cueflow
|
2021-04-07 02:43:12 +02:00
|
|
|
flow := cueflow.New(
|
2021-04-08 03:41:44 +02:00
|
|
|
&cueflow.Config{},
|
2021-08-20 15:52:58 +02:00
|
|
|
e.src.Cue(),
|
2021-05-07 23:39:12 +02:00
|
|
|
newTaskFunc(newPipelineRunner(e.computed, s)),
|
2021-04-07 02:43:12 +02:00
|
|
|
)
|
2021-03-25 01:55:21 +01:00
|
|
|
if err := flow.Run(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-08-20 15:52:58 +02:00
|
|
|
// FIXME: canceling the context makes flow return `nil`
|
|
|
|
// Check explicitly if the context is canceled.
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
2021-03-23 08:22:50 +01:00
|
|
|
}
|
2021-03-23 23:27:16 +01:00
|
|
|
|
2021-03-23 23:41:26 +01:00
|
|
|
type DownOpts struct{}
|
2021-03-23 08:22:50 +01:00
|
|
|
|
2021-05-07 23:39:12 +02:00
|
|
|
func (e *Environment) Down(ctx context.Context, _ *DownOpts) error {
|
2021-03-25 01:55:21 +01:00
|
|
|
panic("NOT IMPLEMENTED")
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:41:26 +01:00
|
|
|
type QueryOpts struct{}
|
2021-03-25 01:55:21 +01:00
|
|
|
|
2021-05-01 03:05:37 +02:00
|
|
|
func newTaskFunc(runner cueflow.RunnerFunc) cueflow.TaskFunc {
|
2021-03-25 01:55:21 +01:00
|
|
|
return func(flowVal cue.Value) (cueflow.Runner, error) {
|
2021-05-01 03:05:37 +02:00
|
|
|
v := compiler.Wrap(flowVal)
|
2021-03-25 01:55:21 +01:00
|
|
|
if !isComponent(v) {
|
|
|
|
// No compute script
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return runner, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func noOpRunner(t *cueflow.Task) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-05-26 01:30:49 +02:00
|
|
|
func newPipelineRunner(computed *compiler.Value, s solver.Solver) cueflow.RunnerFunc {
|
2021-03-25 01:55:21 +01:00
|
|
|
return cueflow.RunnerFunc(func(t *cueflow.Task) error {
|
|
|
|
ctx := t.Context()
|
|
|
|
lg := log.
|
|
|
|
Ctx(ctx).
|
|
|
|
With().
|
|
|
|
Str("component", t.Path().String()).
|
|
|
|
Logger()
|
|
|
|
ctx = lg.WithContext(ctx)
|
2021-07-02 14:26:35 +02:00
|
|
|
|
|
|
|
tr := otel.Tracer("environment")
|
|
|
|
ctx, span := tr.Start(ctx, fmt.Sprintf("compute: %s", t.Path().String()))
|
|
|
|
defer span.End()
|
2021-03-25 01:55:21 +01:00
|
|
|
|
|
|
|
for _, dep := range t.Dependencies() {
|
|
|
|
lg.
|
|
|
|
Debug().
|
|
|
|
Str("dependency", dep.Path().String()).
|
|
|
|
Msg("dependency detected")
|
|
|
|
}
|
2021-05-01 03:05:37 +02:00
|
|
|
v := compiler.Wrap(t.Value())
|
2021-05-05 01:26:56 +02:00
|
|
|
p := NewPipeline(v, s)
|
|
|
|
err := p.Run(ctx)
|
2021-03-25 01:55:21 +01:00
|
|
|
if err != nil {
|
2021-07-02 14:26:35 +02:00
|
|
|
// Record the error
|
|
|
|
span.AddEvent("command", trace.WithAttributes(
|
|
|
|
attribute.String("error", err.Error()),
|
|
|
|
))
|
2021-03-25 01:55:21 +01:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
2021-04-08 03:41:44 +02:00
|
|
|
|
|
|
|
// Mirror the computed values in both `Task` and `Result`
|
2021-04-09 03:09:10 +02:00
|
|
|
if p.Computed().IsEmptyStruct() {
|
2021-04-08 03:41:44 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-09 03:09:10 +02:00
|
|
|
if err := t.Fill(p.Computed().Cue()); err != nil {
|
2021-04-08 03:41:44 +02:00
|
|
|
lg.
|
|
|
|
Error().
|
|
|
|
Err(err).
|
|
|
|
Msg("failed to fill task")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merge task value into output
|
2021-04-09 03:09:10 +02:00
|
|
|
if err := computed.FillPath(t.Path(), p.Computed()); err != nil {
|
2021-04-08 03:41:44 +02:00
|
|
|
lg.
|
|
|
|
Error().
|
|
|
|
Err(err).
|
|
|
|
Msg("failed to fill task result")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-25 01:55:21 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
2021-04-19 22:44:21 +02:00
|
|
|
|
2021-05-27 18:45:24 +02:00
|
|
|
func (e *Environment) ScanInputs(ctx context.Context, mergeUserInputs bool) ([]*compiler.Value, error) {
|
|
|
|
src := e.plan
|
|
|
|
|
|
|
|
if mergeUserInputs {
|
2021-08-20 15:52:58 +02:00
|
|
|
src = e.src
|
2021-05-27 18:45:24 +02:00
|
|
|
}
|
|
|
|
|
2021-06-02 16:22:38 +02:00
|
|
|
return ScanInputs(ctx, src), nil
|
2021-04-19 22:44:21 +02:00
|
|
|
}
|
2021-05-31 15:24:42 +02:00
|
|
|
|
|
|
|
func (e *Environment) ScanOutputs(ctx context.Context) ([]*compiler.Value, error) {
|
2021-08-20 15:52:58 +02:00
|
|
|
src := compiler.NewValue()
|
|
|
|
if err := src.FillPath(cue.MakePath(), e.plan); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := src.FillPath(cue.MakePath(), e.input); err != nil {
|
2021-05-31 15:24:42 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-01 14:14:45 +02:00
|
|
|
if e.state.Computed != "" {
|
|
|
|
computed, err := compiler.DecodeJSON("", []byte(e.state.Computed))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-05-31 15:24:42 +02:00
|
|
|
|
2021-06-01 14:14:45 +02:00
|
|
|
if err := src.FillPath(cue.MakePath(), computed); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-05-31 15:24:42 +02:00
|
|
|
}
|
|
|
|
|
2021-06-02 16:22:38 +02:00
|
|
|
return ScanOutputs(ctx, src), nil
|
2021-05-31 15:24:42 +02:00
|
|
|
}
|