engine: Make paths relative to the CUE file they're defined in
Fixes #1309 Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
ec69734c51
commit
9c81a155c9
@ -1,6 +1,9 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -282,6 +285,43 @@ func (v *Value) HasAttr(filter ...string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filename returns the CUE filename where the value was defined
|
||||||
|
func (v *Value) Filename() (string, error) {
|
||||||
|
pos := v.Cue().Pos()
|
||||||
|
if !pos.IsValid() {
|
||||||
|
return "", errors.New("invalid token position")
|
||||||
|
}
|
||||||
|
return pos.Filename(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dirname returns the CUE dirname where the value was defined
|
||||||
|
func (v *Value) Dirname() (string, error) {
|
||||||
|
f, err := v.Filename()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Dir(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsPath returns an absolute path contained in Value
|
||||||
|
// Paths are relative to the CUE file they were declared in.
|
||||||
|
func (v *Value) AbsPath() (string, error) {
|
||||||
|
p, err := v.String()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(p) {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := v.Dirname()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return path.Join(d, p), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Value) Dereference() *Value {
|
func (v *Value) Dereference() *Value {
|
||||||
dVal := cue.Dereference(v.val)
|
dVal := cue.Dereference(v.val)
|
||||||
return v.cc.Wrap(dVal)
|
return v.cc.Wrap(dVal)
|
||||||
|
11
plan/plan.go
11
plan/plan.go
@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -97,16 +96,12 @@ func (p *Plan) registerLocalDirs() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range imports {
|
for _, v := range imports {
|
||||||
dir, err := v.Value.Lookup("path").String()
|
dir, err := v.Value.Lookup("path").AbsPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
abs, err := filepath.Abs(dir)
|
if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
|
||||||
if err != nil {
|
return fmt.Errorf("path %q does not exist", dir)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(abs); errors.Is(err, os.ErrNotExist) {
|
|
||||||
return fmt.Errorf("path %q does not exist", abs)
|
|
||||||
}
|
}
|
||||||
p.context.LocalDirs.Add(dir)
|
p.context.LocalDirs.Add(dir)
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,12 @@ type inputDirectoryTask struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *inputDirectoryTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
func (c *inputDirectoryTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
||||||
|
path, err := v.Lookup("path").AbsPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var dir struct {
|
var dir struct {
|
||||||
Path string
|
|
||||||
Include []string
|
Include []string
|
||||||
Exclude []string
|
Exclude []string
|
||||||
}
|
}
|
||||||
@ -29,13 +33,13 @@ func (c *inputDirectoryTask) Run(ctx context.Context, pctx *plancontext.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
lg := log.Ctx(ctx)
|
lg := log.Ctx(ctx)
|
||||||
lg.Debug().Str("path", dir.Path).Msg("loading local directory")
|
lg.Debug().Str("path", path).Msg("loading local directory")
|
||||||
opts := []llb.LocalOption{
|
opts := []llb.LocalOption{
|
||||||
withCustomName(v, "Local %s", dir.Path),
|
withCustomName(v, "Local %s", path),
|
||||||
// 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(s.SessionID()),
|
llb.SessionID(s.SessionID()),
|
||||||
llb.SharedKeyHint(dir.Path),
|
llb.SharedKeyHint(path),
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dir.Include) > 0 {
|
if len(dir.Include) > 0 {
|
||||||
@ -56,13 +60,13 @@ func (c *inputDirectoryTask) Run(ctx context.Context, pctx *plancontext.Context,
|
|||||||
st := llb.Scratch().File(
|
st := llb.Scratch().File(
|
||||||
llb.Copy(
|
llb.Copy(
|
||||||
llb.Local(
|
llb.Local(
|
||||||
dir.Path,
|
path,
|
||||||
opts...,
|
opts...,
|
||||||
),
|
),
|
||||||
"/",
|
"/",
|
||||||
"/",
|
"/",
|
||||||
),
|
),
|
||||||
withCustomName(v, "Local %s [copy]", dir.Path),
|
withCustomName(v, "Local %s [copy]", path),
|
||||||
)
|
)
|
||||||
|
|
||||||
result, err := s.Solve(ctx, st, pctx.Platform.Get())
|
result, err := s.Solve(ctx, st, pctx.Platform.Get())
|
||||||
|
@ -3,6 +3,7 @@ package task
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -35,8 +36,18 @@ func (c *inputSecretExecTask) Run(ctx context.Context, pctx *plancontext.Context
|
|||||||
lg := log.Ctx(ctx)
|
lg := log.Ctx(ctx)
|
||||||
lg.Debug().Str("name", secretExec.Command.Name).Str("args", strings.Join(secretExec.Command.Args, " ")).Str("trimSpace", fmt.Sprintf("%t", secretExec.TrimSpace)).Msg("loading secret")
|
lg.Debug().Str("name", secretExec.Command.Name).Str("args", strings.Join(secretExec.Command.Args, " ")).Str("trimSpace", fmt.Sprintf("%t", secretExec.TrimSpace)).Msg("loading secret")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
//#nosec G204: sec audited by @aluzzardi and @mrjones
|
||||||
|
cmd := exec.CommandContext(ctx, secretExec.Command.Name, secretExec.Command.Args...)
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
cmd.Dir, err = v.Lookup("command.name").Dirname()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// sec audited by @aluzzardi and @mrjones
|
// sec audited by @aluzzardi and @mrjones
|
||||||
out, err := exec.CommandContext(ctx, secretExec.Command.Name, secretExec.Command.Args...).Output() //#nosec G204
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package task
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -20,25 +19,26 @@ type inputSecretFileTask struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *inputSecretFileTask) Run(ctx context.Context, pctx *plancontext.Context, _ solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
func (c *inputSecretFileTask) Run(ctx context.Context, pctx *plancontext.Context, _ solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
||||||
var secretFile struct {
|
lg := log.Ctx(ctx)
|
||||||
Path string
|
|
||||||
TrimSpace bool
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.Decode(&secretFile); err != nil {
|
path, err := v.Lookup("path").AbsPath()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lg := log.Ctx(ctx)
|
lg.Debug().Str("path", path).Msg("loading secret")
|
||||||
lg.Debug().Str("path", secretFile.Path).Str("trimSpace", fmt.Sprintf("%t", secretFile.TrimSpace)).Msg("loading secret")
|
|
||||||
|
|
||||||
fileBytes, err := os.ReadFile(secretFile.Path)
|
fileBytes, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
plaintext := string(fileBytes)
|
plaintext := string(fileBytes)
|
||||||
if secretFile.TrimSpace {
|
trimSpace, err := v.Lookup("trimSpace").Bool()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if trimSpace {
|
||||||
plaintext = strings.TrimSpace(plaintext)
|
plaintext = strings.TrimSpace(plaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ func (c outputDirectoryTask) Run(ctx context.Context, pctx *plancontext.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dest, err := v.Lookup("dest").String()
|
dest, err := v.Lookup("dest").AbsPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -44,11 +44,18 @@ setup() {
|
|||||||
"$DAGGER" --europa up ./plan/inputs/directories/exists.cue
|
"$DAGGER" --europa up ./plan/inputs/directories/exists.cue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "plan/inputs/directories relative directories" {
|
||||||
|
cd "$TESTDIR"
|
||||||
|
cd "$TESTDIR"/plan/inputs
|
||||||
|
|
||||||
|
"$DAGGER" --europa up ./directories/exists.cue
|
||||||
|
}
|
||||||
|
|
||||||
@test "plan/inputs/directories not exists" {
|
@test "plan/inputs/directories not exists" {
|
||||||
cd "$TESTDIR"
|
cd "$TESTDIR"
|
||||||
run "$DAGGER" --europa up ./plan/inputs/directories/not_exists.cue
|
run "$DAGGER" --europa up ./plan/inputs/directories/not_exists.cue
|
||||||
assert_failure
|
assert_failure
|
||||||
assert_output --partial 'tests/fasdfsdfs" does not exist'
|
assert_output --partial 'fasdfsdfs" does not exist'
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "plan/inputs/directories conflicting values" {
|
@test "plan/inputs/directories conflicting values" {
|
||||||
@ -63,6 +70,11 @@ setup() {
|
|||||||
"$DAGGER" --europa up ./plan/inputs/secrets/exec.cue
|
"$DAGGER" --europa up ./plan/inputs/secrets/exec.cue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "plan/inputs/secrets exec relative" {
|
||||||
|
cd "$TESTDIR"
|
||||||
|
"$DAGGER" --europa up ./plan/inputs/secrets/exec.cue
|
||||||
|
}
|
||||||
|
|
||||||
@test "plan/inputs/secrets invalid command" {
|
@test "plan/inputs/secrets invalid command" {
|
||||||
cd "$TESTDIR"
|
cd "$TESTDIR"
|
||||||
run "$DAGGER" --europa up ./plan/inputs/secrets/invalid_command.cue
|
run "$DAGGER" --europa up ./plan/inputs/secrets/invalid_command.cue
|
||||||
@ -78,6 +90,14 @@ setup() {
|
|||||||
assert [ -f "./out/test" ]
|
assert [ -f "./out/test" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "plan/outputs relative paths" {
|
||||||
|
cd "$TESTDIR"/plan
|
||||||
|
|
||||||
|
rm -f "./outputs/out/test"
|
||||||
|
"$DAGGER" --europa up ./outputs/outputs.cue
|
||||||
|
assert [ -f "./outputs/out/test" ]
|
||||||
|
}
|
||||||
|
|
||||||
@test "plan/platform" {
|
@test "plan/platform" {
|
||||||
cd "$TESTDIR"
|
cd "$TESTDIR"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
engine.#Plan & {
|
engine.#Plan & {
|
||||||
inputs: directories: test: path: "./plan/inputs/directories"
|
inputs: directories: test: path: "."
|
||||||
actions: verify: engine.#ReadFile & {
|
actions: verify: engine.#ReadFile & {
|
||||||
input: inputs.directories.test.contents
|
input: inputs.directories.test.contents
|
||||||
path: "test.txt"
|
path: "test.txt"
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
engine.#Plan & {
|
engine.#Plan & {
|
||||||
inputs: directories: test: path: "./plan/inputs/directories"
|
inputs: directories: test: path: "."
|
||||||
actions: verify: engine.#ReadFile & {
|
actions: verify: engine.#ReadFile & {
|
||||||
input: inputs.directories.test.contents
|
input: inputs.directories.test.contents
|
||||||
path: "test.txt"
|
path: "test.txt"
|
||||||
|
33
tests/plan/inputs/secrets/exec_relative.cue
Normal file
33
tests/plan/inputs/secrets/exec_relative.cue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"alpha.dagger.io/europa/dagger/engine"
|
||||||
|
)
|
||||||
|
|
||||||
|
engine.#Plan & {
|
||||||
|
inputs: secrets: echo: command: {
|
||||||
|
name: "cat"
|
||||||
|
args: ["./test.txt"]
|
||||||
|
}
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
|
||||||
|
image: engine.#Pull & {
|
||||||
|
source: "alpine:3.15.0@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3"
|
||||||
|
}
|
||||||
|
|
||||||
|
verify: engine.#Exec & {
|
||||||
|
input: image.output
|
||||||
|
mounts: secret: {
|
||||||
|
dest: "/run/secrets/test"
|
||||||
|
contents: inputs.secrets.echo.contents
|
||||||
|
}
|
||||||
|
args: [
|
||||||
|
"sh", "-c",
|
||||||
|
#"""
|
||||||
|
test "$(cat /run/secrets/test)" = "test"
|
||||||
|
"""#,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
tests/plan/inputs/secrets/test.txt
Normal file
1
tests/plan/inputs/secrets/test.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
test
|
@ -5,7 +5,7 @@ import "alpha.dagger.io/europa/dagger/engine"
|
|||||||
engine.#Plan & {
|
engine.#Plan & {
|
||||||
inputs: secrets: token: command: {
|
inputs: secrets: token: command: {
|
||||||
name: "sops"
|
name: "sops"
|
||||||
args: ["exec-env", "./secrets_sops.yaml", "echo $TestPAT"]
|
args: ["exec-env", "../../secrets_sops.yaml", "echo $TestPAT"]
|
||||||
}
|
}
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
Reference in New Issue
Block a user