commit
e503e12cff
73
cmd/dagger/cmd/common/common.go
Normal file
73
cmd/dagger/cmd/common/common.go
Normal file
@ -0,0 +1,73 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// getCurrentRoute returns the current selected route based on its abs path
|
||||
func GetCurrentRoute(ctx context.Context, store *dagger.Store) *dagger.Route {
|
||||
lg := log.Ctx(ctx)
|
||||
st := GetCurrentRouteState(ctx, store)
|
||||
|
||||
route, err := dagger.NewRoute(st)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Interface("routeState", st).
|
||||
Msg("failed to init route")
|
||||
}
|
||||
|
||||
return route
|
||||
}
|
||||
|
||||
func GetCurrentRouteState(ctx context.Context, store *dagger.Store) *dagger.RouteState {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
routeName := viper.GetString("route")
|
||||
if routeName != "" {
|
||||
st, err := store.LookupRouteByName(ctx, routeName)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("routeName", routeName).
|
||||
Msg("failed to lookup route by name")
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("cannot get current working directory")
|
||||
}
|
||||
st, err := store.LookupRouteByPath(ctx, wd)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("routePath", wd).
|
||||
Msg("failed to lookup route by path")
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
func RouteUp(ctx context.Context, route *dagger.Route) {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
c, err := dagger.NewClient(ctx, "")
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("unable to create client")
|
||||
}
|
||||
output, err := c.Up(ctx, route)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to compute")
|
||||
}
|
||||
fmt.Println(output.JSON())
|
||||
}
|
@ -1,20 +1,22 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"go.mozilla.org/sops"
|
||||
"go.mozilla.org/sops/decrypt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
input *dagger.InputValue
|
||||
updater *dagger.InputValue
|
||||
)
|
||||
|
||||
var computeCmd = &cobra.Command{
|
||||
Use: "compute CONFIG",
|
||||
Short: "Compute a configuration",
|
||||
@ -30,51 +32,116 @@ var computeCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
env, err := dagger.NewEnv()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("unable to initialize environment")
|
||||
}
|
||||
if err := updater.SourceFlag().Set(args[0]); err != nil {
|
||||
lg.Fatal().Err(err).Msg("invalid local source")
|
||||
st := &dagger.RouteState{
|
||||
ID: uuid.New().String(),
|
||||
Name: "FIXME",
|
||||
LayoutSource: dagger.DirInput(args[0], []string{"*.cue", "cue.mod"}),
|
||||
}
|
||||
|
||||
if err := env.SetUpdater(updater.Value()); err != nil {
|
||||
lg.Fatal().Err(err).Msg("invalid updater script")
|
||||
for _, input := range viper.GetStringSlice("input-string") {
|
||||
parts := strings.SplitN(input, "=", 2)
|
||||
k, v := parts[0], parts[1]
|
||||
err := st.AddInput(k, dagger.TextInput(v))
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("input", k).
|
||||
Msg("failed to add input")
|
||||
}
|
||||
}
|
||||
if err := env.SetInput(input.Value()); err != nil {
|
||||
lg.Fatal().Err(err).Msg("invalid input")
|
||||
|
||||
for _, input := range viper.GetStringSlice("input-dir") {
|
||||
parts := strings.SplitN(input, "=", 2)
|
||||
k, v := parts[0], parts[1]
|
||||
err := st.AddInput(k, dagger.DirInput(v, []string{}))
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("input", k).
|
||||
Msg("failed to add input")
|
||||
}
|
||||
}
|
||||
c, err := dagger.NewClient(ctx, "")
|
||||
|
||||
for _, input := range viper.GetStringSlice("input-git") {
|
||||
parts := strings.SplitN(input, "=", 2)
|
||||
k, v := parts[0], parts[1]
|
||||
err := st.AddInput(k, dagger.GitInput(v, "", ""))
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("input", k).
|
||||
Msg("failed to add input")
|
||||
}
|
||||
}
|
||||
|
||||
if f := viper.GetString("input-json"); f != "" {
|
||||
lg := lg.With().Str("path", f).Logger()
|
||||
|
||||
content, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to read file")
|
||||
}
|
||||
|
||||
plaintext, err := decrypt.Data(content, "json")
|
||||
if err != nil && !errors.Is(err, sops.MetadataNotFound) {
|
||||
lg.Fatal().Err(err).Msg("unable to decrypt")
|
||||
}
|
||||
|
||||
if len(plaintext) > 0 {
|
||||
content = plaintext
|
||||
}
|
||||
|
||||
if !json.Valid(content) {
|
||||
lg.Fatal().Msg("invalid json")
|
||||
}
|
||||
|
||||
err = st.AddInput("", dagger.JSONInput(string(content)))
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to add input")
|
||||
}
|
||||
}
|
||||
|
||||
if f := viper.GetString("input-yaml"); f != "" {
|
||||
lg := lg.With().Str("path", f).Logger()
|
||||
|
||||
content, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to read file")
|
||||
}
|
||||
|
||||
plaintext, err := decrypt.Data(content, "yaml")
|
||||
if err != nil && !errors.Is(err, sops.MetadataNotFound) {
|
||||
lg.Fatal().Err(err).Msg("unable to decrypt")
|
||||
}
|
||||
|
||||
if len(plaintext) > 0 {
|
||||
content = plaintext
|
||||
}
|
||||
|
||||
err = st.AddInput("", dagger.YAMLInput(string(content)))
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to add input")
|
||||
}
|
||||
}
|
||||
|
||||
route, err := dagger.NewRoute(st)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("unable to create client")
|
||||
lg.Fatal().Err(err).Msg("unable to initialize route")
|
||||
}
|
||||
output, err := c.Compute(ctx, env)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to compute")
|
||||
}
|
||||
fmt.Println(output.JSON())
|
||||
|
||||
common.RouteUp(ctx, route)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
// Setup --input-* flags
|
||||
input, err = dagger.NewInputValue("{}")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
computeCmd.Flags().Var(input.StringFlag(), "input-string", "TARGET=STRING")
|
||||
computeCmd.Flags().Var(input.DirFlag(), "input-dir", "TARGET=PATH")
|
||||
computeCmd.Flags().Var(input.GitFlag(), "input-git", "TARGET=REMOTE#REF")
|
||||
computeCmd.Flags().Var(input.CueFlag(), "input-cue", "CUE")
|
||||
computeCmd.Flags().Var(input.JSONFlag(), "input-json", "JSON")
|
||||
computeCmd.Flags().Var(input.YAMLFlag(), "input-yaml", "YAML")
|
||||
|
||||
// Setup (future) --from-* flags
|
||||
updater, err = dagger.NewInputValue("[...{do:string, ...}]")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
computeCmd.Flags().StringSlice("input-string", []string{}, "TARGET=STRING")
|
||||
computeCmd.Flags().StringSlice("input-dir", []string{}, "TARGET=PATH")
|
||||
computeCmd.Flags().StringSlice("input-git", []string{}, "TARGET=REMOTE#REF")
|
||||
computeCmd.Flags().String("input-json", "", "JSON")
|
||||
computeCmd.Flags().String("input-yaml", "", "YAML")
|
||||
|
||||
if err := viper.BindPFlags(computeCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
|
31
cmd/dagger/cmd/delete.go
Normal file
31
cmd/dagger/cmd/delete.go
Normal file
@ -0,0 +1,31 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete a route after taking it offline (WARNING: may destroy infrastructure)",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(deleteCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
52
cmd/dagger/cmd/down.go
Normal file
52
cmd/dagger/cmd/down.go
Normal file
@ -0,0 +1,52 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var downCmd = &cobra.Command{
|
||||
Use: "down",
|
||||
Short: "Take a route offline (WARNING: may destroy infrastructure)",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
route := common.GetCurrentRoute(ctx, store)
|
||||
|
||||
// TODO: Implement options: --no-cache
|
||||
if err := route.Down(ctx, nil); err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("routeName", route.Name()).
|
||||
Str("routeId", route.ID()).
|
||||
Msg("failed to up the route")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
downCmd.Flags().Bool("--no-cache", false, "Disable all run cache")
|
||||
|
||||
if err := viper.BindPFlags(downCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/history.go
Normal file
31
cmd/dagger/cmd/history.go
Normal file
@ -0,0 +1,31 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var historyCmd = &cobra.Command{
|
||||
Use: "history",
|
||||
Short: "List past changes to a route",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(historyCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
33
cmd/dagger/cmd/input/container.go
Normal file
33
cmd/dagger/cmd/input/container.go
Normal file
@ -0,0 +1,33 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var containerCmd = &cobra.Command{
|
||||
Use: "container TARGET CONTAINER-IMAGE",
|
||||
Short: "Add a container image as input artifact",
|
||||
Args: cobra.ExactArgs(2),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
updateRouteInput(ctx, args[0], dagger.DockerInput(args[1]))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(containerCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
33
cmd/dagger/cmd/input/dir.go
Normal file
33
cmd/dagger/cmd/input/dir.go
Normal file
@ -0,0 +1,33 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var dirCmd = &cobra.Command{
|
||||
Use: "dir TARGET PATH",
|
||||
Short: "Add a local directory as input artifact",
|
||||
Args: cobra.ExactArgs(2),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
updateRouteInput(ctx, args[0], dagger.DirInput(args[1], []string{}))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(dirCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
38
cmd/dagger/cmd/input/git.go
Normal file
38
cmd/dagger/cmd/input/git.go
Normal file
@ -0,0 +1,38 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var gitCmd = &cobra.Command{
|
||||
Use: "git TARGET REMOTE REF [SUBDIR]",
|
||||
Short: "Add a git repository as input artifact",
|
||||
Args: cobra.RangeArgs(3, 4),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
subDir := ""
|
||||
if len(args) > 3 {
|
||||
subDir = args[3]
|
||||
}
|
||||
|
||||
updateRouteInput(ctx, args[0], dagger.GitInput(args[1], args[2], subDir))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(gitCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
43
cmd/dagger/cmd/input/root.go
Normal file
43
cmd/dagger/cmd/input/root.go
Normal file
@ -0,0 +1,43 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Cmd exposes the top-level command
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "input",
|
||||
Short: "Manage a route's inputs",
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.AddCommand(
|
||||
dirCmd,
|
||||
gitCmd,
|
||||
containerCmd,
|
||||
secretCmd,
|
||||
textCmd,
|
||||
)
|
||||
}
|
||||
|
||||
func updateRouteInput(ctx context.Context, target string, input dagger.Input) {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
st := common.GetCurrentRouteState(ctx, store)
|
||||
st.AddInput(target, input)
|
||||
|
||||
if err := store.UpdateRoute(ctx, st, nil); err != nil {
|
||||
lg.Fatal().Err(err).Str("routeId", st.ID).Str("routeName", st.Name).Msg("cannot update route")
|
||||
}
|
||||
lg.Info().Str("routeId", st.ID).Str("routeName", st.Name).Msg("updated route")
|
||||
}
|
31
cmd/dagger/cmd/input/secret.go
Normal file
31
cmd/dagger/cmd/input/secret.go
Normal file
@ -0,0 +1,31 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var secretCmd = &cobra.Command{
|
||||
Use: "secret TARGET VALUE",
|
||||
Short: "Add an encrypted input secret",
|
||||
Args: cobra.ExactArgs(2),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(secretCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
33
cmd/dagger/cmd/input/text.go
Normal file
33
cmd/dagger/cmd/input/text.go
Normal file
@ -0,0 +1,33 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var textCmd = &cobra.Command{
|
||||
Use: "text TARGET VALUE",
|
||||
Short: "Add an input text",
|
||||
Args: cobra.ExactArgs(2),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
updateRouteInput(ctx, args[0], dagger.TextInput(args[1]))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(textCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/layout/dir.go
Normal file
31
cmd/dagger/cmd/layout/dir.go
Normal file
@ -0,0 +1,31 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var dirCmd = &cobra.Command{
|
||||
Use: "dir PATH",
|
||||
Short: "Load layout from a local directory",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(dirCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/layout/file.go
Normal file
31
cmd/dagger/cmd/layout/file.go
Normal file
@ -0,0 +1,31 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var fileCmd = &cobra.Command{
|
||||
Use: "file PATH|-",
|
||||
Short: "Load layout from a cue file",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(fileCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/layout/git.go
Normal file
31
cmd/dagger/cmd/layout/git.go
Normal file
@ -0,0 +1,31 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var gitCmd = &cobra.Command{
|
||||
Use: "git REMOTE REF [SUBDIR]",
|
||||
Short: "Load layout from a git package",
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(gitCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/layout/package.go
Normal file
31
cmd/dagger/cmd/layout/package.go
Normal file
@ -0,0 +1,31 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var packageCmd = &cobra.Command{
|
||||
Use: "package PKG",
|
||||
Short: "Load layout from a cue package",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(packageCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
18
cmd/dagger/cmd/layout/root.go
Normal file
18
cmd/dagger/cmd/layout/root.go
Normal file
@ -0,0 +1,18 @@
|
||||
package layout
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
// Cmd exposes the top-level command
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "layout",
|
||||
Short: "Manage a route's layout",
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.AddCommand(
|
||||
packageCmd,
|
||||
dirCmd,
|
||||
gitCmd,
|
||||
fileCmd,
|
||||
)
|
||||
}
|
49
cmd/dagger/cmd/list.go
Normal file
49
cmd/dagger/cmd/list.go
Normal file
@ -0,0 +1,49 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List available routes",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
routes, err := store.ListRoutes(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("cannot list routes")
|
||||
}
|
||||
|
||||
for _, r := range routes {
|
||||
fmt.Println(r.Name)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(listCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/login.go
Normal file
31
cmd/dagger/cmd/login.go
Normal file
@ -0,0 +1,31 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var loginCmd = &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Login to Dagger Cloud",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(loginCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/logout.go
Normal file
31
cmd/dagger/cmd/logout.go
Normal file
@ -0,0 +1,31 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var logoutCmd = &cobra.Command{
|
||||
Use: "logout",
|
||||
Short: "Logout from Dagger Cloud",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(logoutCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
115
cmd/dagger/cmd/new.go
Normal file
115
cmd/dagger/cmd/new.go
Normal file
@ -0,0 +1,115 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var newCmd = &cobra.Command{
|
||||
Use: "new",
|
||||
Short: "Create a new route",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
st := &dagger.RouteState{
|
||||
Name: getNewRouteName(ctx),
|
||||
LayoutSource: getLayoutSource(ctx),
|
||||
}
|
||||
|
||||
err = store.CreateRoute(ctx, st)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to create route")
|
||||
}
|
||||
lg.
|
||||
Info().
|
||||
Str("routeId", st.ID).
|
||||
Str("routeName", st.Name).
|
||||
Msg("route created")
|
||||
|
||||
route, err := dagger.NewRoute(st)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to initialize route")
|
||||
}
|
||||
|
||||
if viper.GetBool("up") {
|
||||
common.RouteUp(ctx, route)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getNewRouteName(ctx context.Context) string {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
routeName := viper.GetString("route")
|
||||
if routeName != "" {
|
||||
return routeName
|
||||
}
|
||||
|
||||
workDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to get current working dir")
|
||||
}
|
||||
|
||||
currentDir := filepath.Base(workDir)
|
||||
if currentDir == "/" {
|
||||
return "root"
|
||||
}
|
||||
|
||||
return currentDir
|
||||
}
|
||||
|
||||
// FIXME: Implement options: --layout-*
|
||||
func getLayoutSource(ctx context.Context) dagger.Input {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("cannot get current working directory")
|
||||
}
|
||||
|
||||
return dagger.DirInput(wd, []string{"*.cue", "cue.mod"})
|
||||
}
|
||||
|
||||
func init() {
|
||||
newCmd.Flags().StringP("name", "n", "", "Specify a route name")
|
||||
newCmd.Flags().BoolP("up", "u", false, "Bring the route online")
|
||||
|
||||
newCmd.Flags().String("layout-dir", "", "Load layout from a local directory")
|
||||
newCmd.Flags().String("layout-git", "", "Load layout from a git repository")
|
||||
newCmd.Flags().String("layout-package", "", "Load layout from a cue package")
|
||||
newCmd.Flags().String("layout-file", "", "Load layout from a cue or json file")
|
||||
|
||||
newCmd.Flags().String("setup", "auto", "Specify whether to prompt user for initial setup (no|yes|auto)")
|
||||
|
||||
if err := viper.BindPFlags(newCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
cmd/dagger/cmd/output/dir.go
Normal file
31
cmd/dagger/cmd/output/dir.go
Normal file
@ -0,0 +1,31 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var dirCmd = &cobra.Command{
|
||||
Use: "dir PATH",
|
||||
Short: "Add a local directory as output artifact",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// lg := logger.New()
|
||||
// ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(dirCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
15
cmd/dagger/cmd/output/root.go
Normal file
15
cmd/dagger/cmd/output/root.go
Normal file
@ -0,0 +1,15 @@
|
||||
package output
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
// Cmd exposes the top-level command
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "output",
|
||||
Short: "Manage a route's outputs",
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.AddCommand(
|
||||
dirCmd,
|
||||
)
|
||||
}
|
65
cmd/dagger/cmd/query.go
Normal file
65
cmd/dagger/cmd/query.go
Normal file
@ -0,0 +1,65 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var queryCmd = &cobra.Command{
|
||||
Use: "query [EXPR] [flags]",
|
||||
Short: "Query the contents of a route",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
route := common.GetCurrentRoute(ctx, store)
|
||||
|
||||
expr := args[0]
|
||||
|
||||
out, err := route.Query(ctx, expr, nil)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("routeName", route.Name()).
|
||||
Str("routeId", route.ID()).
|
||||
Msg("failed to query route")
|
||||
}
|
||||
|
||||
fmt.Println(out)
|
||||
// TODO: Implement options: --no-*, --format, --revision
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
queryCmd.Flags().String("revision", "latest", "Query a specific version of the route")
|
||||
queryCmd.Flags().StringP("format", "f", "", "Output format (json|yaml|cue|text|env)")
|
||||
|
||||
queryCmd.Flags().BoolP("--no-input", "I", false, "Exclude inputs from query")
|
||||
queryCmd.Flags().BoolP("--no-output", "O", false, "Exclude outputs from query")
|
||||
queryCmd.Flags().BoolP("--no-layout", "L", false, "Exclude outputs from query")
|
||||
|
||||
if err := viper.BindPFlags(queryCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -4,6 +4,9 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
inputCmd "dagger.io/go/cmd/dagger/cmd/input"
|
||||
"dagger.io/go/cmd/dagger/cmd/layout"
|
||||
"dagger.io/go/cmd/dagger/cmd/output"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"github.com/moby/buildkit/util/appcontext"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
@ -14,24 +17,28 @@ import (
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "dagger",
|
||||
Short: "Open-source workflow engine",
|
||||
Short: "A system for application delivery as code (ADC)",
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().String("log-format", "", "Log format (json, pretty). Defaults to json if the terminal is not a tty")
|
||||
rootCmd.PersistentFlags().StringP("log-level", "l", "debug", "Log level")
|
||||
rootCmd.PersistentFlags().StringP("route", "r", "", "Select a route")
|
||||
|
||||
rootCmd.AddCommand(
|
||||
computeCmd,
|
||||
// Create an env
|
||||
// Change settings on an env
|
||||
// View or edit env serti
|
||||
// settingsCmd,
|
||||
// Query the state of an env
|
||||
// getCmd,
|
||||
// unsetCmd,
|
||||
// computeCmd,
|
||||
// listCmd,
|
||||
newCmd,
|
||||
listCmd,
|
||||
queryCmd,
|
||||
upCmd,
|
||||
downCmd,
|
||||
deleteCmd,
|
||||
historyCmd,
|
||||
loginCmd,
|
||||
logoutCmd,
|
||||
layout.Cmd,
|
||||
inputCmd.Cmd,
|
||||
output.Cmd,
|
||||
)
|
||||
|
||||
if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil {
|
||||
|
44
cmd/dagger/cmd/up.go
Normal file
44
cmd/dagger/cmd/up.go
Normal file
@ -0,0 +1,44 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var upCmd = &cobra.Command{
|
||||
Use: "up",
|
||||
Short: "Bring a route online with latest layout and inputs",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
route := common.GetCurrentRoute(ctx, store)
|
||||
|
||||
// TODO: Implement options: --no-cache
|
||||
common.RouteUp(ctx, route)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
newCmd.Flags().Bool("--no-cache", false, "Disable all run cache")
|
||||
|
||||
if err := viper.BindPFlags(upCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -60,8 +60,8 @@ func NewClient(ctx context.Context, host string) (*Client, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FIXME: return completed *Env, instead of *compiler.Value
|
||||
func (c *Client) Compute(ctx context.Context, env *Env) (*compiler.Value, error) {
|
||||
// FIXME: return completed *Route, instead of *compiler.Value
|
||||
func (c *Client) Up(ctx context.Context, route *Route) (*compiler.Value, error) {
|
||||
lg := log.Ctx(ctx)
|
||||
eg, gctx := errgroup.WithContext(ctx)
|
||||
|
||||
@ -78,7 +78,7 @@ func (c *Client) Compute(ctx context.Context, env *Env) (*compiler.Value, error)
|
||||
outr, outw := io.Pipe()
|
||||
eg.Go(func() error {
|
||||
defer outw.Close()
|
||||
return c.buildfn(gctx, env, events, outw)
|
||||
return c.buildfn(gctx, route, events, outw)
|
||||
})
|
||||
|
||||
// Spawn output retriever
|
||||
@ -95,11 +95,11 @@ func (c *Client) Compute(ctx context.Context, env *Env) (*compiler.Value, error)
|
||||
return out, compiler.Err(eg.Wait())
|
||||
}
|
||||
|
||||
func (c *Client) buildfn(ctx context.Context, env *Env, ch chan *bk.SolveStatus, w io.WriteCloser) error {
|
||||
func (c *Client) buildfn(ctx context.Context, route *Route, ch chan *bk.SolveStatus, w io.WriteCloser) error {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
// Scan local dirs to grant access
|
||||
localdirs := env.LocalDirs()
|
||||
localdirs := route.LocalDirs()
|
||||
for label, dir := range localdirs {
|
||||
abs, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
@ -132,24 +132,24 @@ func (c *Client) buildfn(ctx context.Context, env *Env, ch chan *bk.SolveStatus,
|
||||
s := NewSolver(c.c, gw, ch)
|
||||
|
||||
lg.Debug().Msg("loading configuration")
|
||||
if err := env.Update(ctx, s); err != nil {
|
||||
if err := route.LoadLayout(ctx, s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compute output overlay
|
||||
lg.Debug().Msg("computing env")
|
||||
if err := env.Compute(ctx, s); err != nil {
|
||||
lg.Debug().Msg("computing route")
|
||||
if err := route.Up(ctx, s, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Export env to a cue directory
|
||||
// Export route to a cue directory
|
||||
// FIXME: this should be elsewhere
|
||||
lg.Debug().Msg("exporting env")
|
||||
span, _ := opentracing.StartSpanFromContext(ctx, "Env.Export")
|
||||
lg.Debug().Msg("exporting route")
|
||||
span, _ := opentracing.StartSpanFromContext(ctx, "Route.Export")
|
||||
defer span.Finish()
|
||||
|
||||
st := llb.Scratch().File(
|
||||
llb.Mkfile("state.cue", 0600, env.State().JSON()),
|
||||
llb.Mkfile("state.cue", 0600, route.State().JSON()),
|
||||
llb.WithCustomName("[internal] serializing state to JSON"),
|
||||
)
|
||||
ref, err := s.Solve(ctx, st)
|
||||
@ -178,7 +178,7 @@ func (c *Client) buildfn(ctx context.Context, env *Env, ch chan *bk.SolveStatus,
|
||||
func (c *Client) outputfn(ctx context.Context, r io.Reader) (*compiler.Value, error) {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
// FIXME: merge this into env output.
|
||||
// FIXME: merge this into route output.
|
||||
out := compiler.EmptyStruct()
|
||||
|
||||
tr := tar.NewReader(r)
|
||||
|
@ -2,55 +2,34 @@ package compiler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Test that a non-existing field is detected correctly
|
||||
func TestFieldNotExist(t *testing.T) {
|
||||
c := &Compiler{}
|
||||
root, err := c.Compile("test.cue", `foo: "bar"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v := root.Get("foo"); !v.Exists() {
|
||||
// value should exist
|
||||
t.Fatal(v)
|
||||
}
|
||||
if v := root.Get("bar"); v.Exists() {
|
||||
// value should NOT exist
|
||||
t.Fatal(v)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.True(t, root.Get("foo").Exists())
|
||||
require.False(t, root.Get("bar").Exists())
|
||||
}
|
||||
|
||||
// Test that a non-existing definition is detected correctly
|
||||
func TestDefNotExist(t *testing.T) {
|
||||
c := &Compiler{}
|
||||
root, err := c.Compile("test.cue", `foo: #bla: "bar"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v := root.Get("foo.#bla"); !v.Exists() {
|
||||
// value should exist
|
||||
t.Fatal(v)
|
||||
}
|
||||
if v := root.Get("foo.#nope"); v.Exists() {
|
||||
// value should NOT exist
|
||||
t.Fatal(v)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.True(t, root.Get("foo.#bla").Exists())
|
||||
require.False(t, root.Get("foo.#nope").Exists())
|
||||
}
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
c := &Compiler{}
|
||||
v, err := c.Compile("", `foo: hello: "world"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b1 := v.JSON()
|
||||
if string(b1) != `{"foo":{"hello":"world"}}` {
|
||||
t.Fatal(b1)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `{"foo":{"hello":"world"}}`, string(v.JSON()))
|
||||
|
||||
// Reproduce a bug where Value.Get().JSON() ignores Get()
|
||||
b2 := v.Get("foo").JSON()
|
||||
if string(b2) != `{"hello":"world"}` {
|
||||
t.Fatal(b2)
|
||||
}
|
||||
require.Equal(t, `{"hello":"world"}`, string(v.Get("foo").JSON()))
|
||||
}
|
||||
|
@ -123,11 +123,6 @@ func (v *Value) Int64() (int64, error) {
|
||||
return v.val.Int64()
|
||||
}
|
||||
|
||||
func (v *Value) SourceUnsafe() string {
|
||||
s, _ := v.SourceString()
|
||||
return s
|
||||
}
|
||||
|
||||
// Proxy function to the underlying cue.Value
|
||||
func (v *Value) Path() cue.Path {
|
||||
return v.val.Path()
|
||||
@ -236,12 +231,6 @@ func (v *Value) Source() ([]byte, error) {
|
||||
return cueformat.Node(v.val.Eval().Syntax())
|
||||
}
|
||||
|
||||
// Return cue source for this value, as a Go string
|
||||
func (v *Value) SourceString() (string, error) {
|
||||
b, err := v.Source()
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (v *Value) IsEmptyStruct() bool {
|
||||
if st, err := v.Struct(); err == nil {
|
||||
if st.Len() == 0 {
|
||||
|
@ -1,56 +0,0 @@
|
||||
package dagger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"dagger.io/go/dagger/compiler"
|
||||
)
|
||||
|
||||
func TestLocalDirs(t *testing.T) {
|
||||
env := mkEnv(t,
|
||||
`#compute: [
|
||||
{
|
||||
do: "local"
|
||||
dir: "bar"
|
||||
}
|
||||
]`,
|
||||
`dir: #compute: [
|
||||
{
|
||||
do: "local"
|
||||
dir: "foo"
|
||||
}
|
||||
]`,
|
||||
)
|
||||
dirs := env.LocalDirs()
|
||||
if len(dirs) != 2 {
|
||||
t.Fatal(dirs)
|
||||
}
|
||||
if _, ok := dirs["foo"]; !ok {
|
||||
t.Fatal(dirs)
|
||||
}
|
||||
if _, ok := dirs["bar"]; !ok {
|
||||
t.Fatal(dirs)
|
||||
}
|
||||
}
|
||||
|
||||
func mkEnv(t *testing.T, updater, input string) *Env {
|
||||
env, err := NewEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u, err := compiler.Compile("updater.cue", updater)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := env.SetUpdater(u); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i, err := compiler.Compile("input.cue", input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := env.SetInput(i); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return env
|
||||
}
|
379
dagger/input.go
379
dagger/input.go
@ -2,297 +2,188 @@ package dagger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"dagger.io/go/dagger/compiler"
|
||||
|
||||
"go.mozilla.org/sops"
|
||||
"go.mozilla.org/sops/decrypt"
|
||||
)
|
||||
|
||||
// A mutable cue value with an API suitable for user inputs,
|
||||
// such as command-line flag parsing.
|
||||
type InputValue struct {
|
||||
root *compiler.Value
|
||||
// An input is a value or artifact supplied by the user.
|
||||
//
|
||||
// - A value is any structured data which can be encoded as cue.
|
||||
//
|
||||
// - An artifact is a piece of data, like a source code checkout,
|
||||
// binary bundle, docker image, database backup etc.
|
||||
//
|
||||
// Artifacts can be passed as inputs, generated dynamically from
|
||||
// other inputs, and received as outputs.
|
||||
// Under the hood, an artifact is encoded as a LLB pipeline, and
|
||||
// attached to the cue configuration as a
|
||||
//
|
||||
type InputType string
|
||||
|
||||
const (
|
||||
InputTypeDir InputType = "dir"
|
||||
InputTypeGit InputType = "git"
|
||||
InputTypeDocker InputType = "docker"
|
||||
InputTypeText InputType = "text"
|
||||
InputTypeJSON InputType = "json"
|
||||
InputTypeYAML InputType = "yaml"
|
||||
)
|
||||
|
||||
type Input struct {
|
||||
Type InputType `json:"type,omitempty"`
|
||||
|
||||
Dir *dirInput `json:"dir,omitempty"`
|
||||
Git *gitInput `json:"git,omitempty"`
|
||||
Docker *dockerInput `json:"docker,omitempty"`
|
||||
Text *textInput `json:"text,omitempty"`
|
||||
JSON *jsonInput `json:"json,omitempty"`
|
||||
YAML *yamlInput `json:"yaml,omitempty"`
|
||||
}
|
||||
|
||||
func (iv *InputValue) Value() *compiler.Value {
|
||||
return iv.root
|
||||
func (i Input) Compile() (*compiler.Value, error) {
|
||||
switch i.Type {
|
||||
case InputTypeDir:
|
||||
return i.Dir.Compile()
|
||||
case InputTypeGit:
|
||||
return i.Git.Compile()
|
||||
case InputTypeDocker:
|
||||
return i.Docker.Compile()
|
||||
case InputTypeText:
|
||||
return i.Text.Compile()
|
||||
case InputTypeJSON:
|
||||
return i.JSON.Compile()
|
||||
case InputTypeYAML:
|
||||
return i.YAML.Compile()
|
||||
case "":
|
||||
return nil, fmt.Errorf("input has not been set")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported input type: %s", i.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (iv *InputValue) String() string {
|
||||
s, _ := iv.root.SourceString()
|
||||
return s
|
||||
// An input artifact loaded from a local directory
|
||||
func DirInput(path string, include []string) Input {
|
||||
return Input{
|
||||
Type: InputTypeDir,
|
||||
Dir: &dirInput{
|
||||
Path: path,
|
||||
Include: include,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewInputValue(base interface{}) (*InputValue, error) {
|
||||
root, err := compiler.Compile("base", base)
|
||||
type dirInput struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Include []string `json:"include,omitempty"`
|
||||
}
|
||||
|
||||
func (dir dirInput) Compile() (*compiler.Value, error) {
|
||||
// FIXME: serialize an intermediate struct, instead of generating cue source
|
||||
includeLLB, err := json.Marshal(dir.Include)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &InputValue{
|
||||
root: root,
|
||||
}, nil
|
||||
llb := fmt.Sprintf(
|
||||
`#compute: [{do:"local",dir:"%s", include:%s}]`,
|
||||
dir.Path,
|
||||
includeLLB,
|
||||
)
|
||||
return compiler.Compile("", llb)
|
||||
}
|
||||
|
||||
func (iv *InputValue) Set(s string, enc func(string) (interface{}, error)) error {
|
||||
// Split from eg. 'foo.bar={bla:"bla"}`
|
||||
k, vRaw := splitkv(s)
|
||||
v, err := enc(vRaw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root, err := iv.root.MergePath(v, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iv.root = root
|
||||
return nil
|
||||
// An input artifact loaded from a git repository
|
||||
type gitInput struct {
|
||||
Remote string `json:"remote,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Dir string `json:"dir,omitempty"`
|
||||
}
|
||||
|
||||
// Adapter to receive string values from pflag
|
||||
func (iv *InputValue) StringFlag() pflag.Value {
|
||||
return stringFlag{
|
||||
iv: iv,
|
||||
func GitInput(remote, ref, dir string) Input {
|
||||
return Input{
|
||||
Type: InputTypeGit,
|
||||
Git: &gitInput{
|
||||
Remote: remote,
|
||||
Ref: ref,
|
||||
Dir: dir,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type stringFlag struct {
|
||||
iv *InputValue
|
||||
func (git gitInput) Compile() (*compiler.Value, error) {
|
||||
panic("NOT IMPLEMENTED")
|
||||
}
|
||||
|
||||
func (sf stringFlag) Set(s string) error {
|
||||
return sf.iv.Set(s, func(s string) (interface{}, error) {
|
||||
return s, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (sf stringFlag) String() string {
|
||||
return sf.iv.String()
|
||||
}
|
||||
|
||||
func (sf stringFlag) Type() string {
|
||||
return "STRING"
|
||||
}
|
||||
|
||||
// DIR FLAG
|
||||
// Receive a local directory path and translate it into a component
|
||||
func (iv *InputValue) DirFlag(include ...string) pflag.Value {
|
||||
if include == nil {
|
||||
include = []string{}
|
||||
}
|
||||
return dirFlag{
|
||||
iv: iv,
|
||||
include: include,
|
||||
// An input artifact loaded from a docker container
|
||||
func DockerInput(ref string) Input {
|
||||
return Input{
|
||||
Type: InputTypeDocker,
|
||||
Docker: &dockerInput{
|
||||
Ref: ref,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type dirFlag struct {
|
||||
iv *InputValue
|
||||
include []string
|
||||
type dockerInput struct {
|
||||
Ref string `json:"ref,omitempty"`
|
||||
}
|
||||
|
||||
func (f dirFlag) Set(s string) error {
|
||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
||||
// FIXME: this is a hack because cue API can't merge into a list
|
||||
include, err := json.Marshal(f.include)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return compiler.Compile("", fmt.Sprintf(
|
||||
`#compute: [{do:"local",dir:"%s", include:%s}]`,
|
||||
s,
|
||||
include,
|
||||
))
|
||||
})
|
||||
func (i dockerInput) Compile() (*compiler.Value, error) {
|
||||
panic("NOT IMPLEMENTED")
|
||||
}
|
||||
|
||||
func (f dirFlag) String() string {
|
||||
return f.iv.String()
|
||||
}
|
||||
|
||||
func (f dirFlag) Type() string {
|
||||
return "PATH"
|
||||
}
|
||||
|
||||
// GIT FLAG
|
||||
// Receive a git repository reference and translate it into a component
|
||||
func (iv *InputValue) GitFlag() pflag.Value {
|
||||
return gitFlag{
|
||||
iv: iv,
|
||||
// An input value encoded as text
|
||||
func TextInput(data string) Input {
|
||||
return Input{
|
||||
Type: InputTypeText,
|
||||
Text: &textInput{
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type gitFlag struct {
|
||||
iv *InputValue
|
||||
type textInput struct {
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (f gitFlag) Set(s string) error {
|
||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid git url")
|
||||
}
|
||||
ref := u.Fragment // eg. #main
|
||||
u.Fragment = ""
|
||||
remote := u.String()
|
||||
|
||||
return compiler.Compile("", fmt.Sprintf(
|
||||
`#compute: [{do:"fetch-git", remote:"%s", ref:"%s"}]`,
|
||||
remote,
|
||||
ref,
|
||||
))
|
||||
})
|
||||
func (i textInput) Compile() (*compiler.Value, error) {
|
||||
return compiler.Compile("", fmt.Sprintf("%q", i.Data))
|
||||
}
|
||||
|
||||
func (f gitFlag) String() string {
|
||||
return f.iv.String()
|
||||
}
|
||||
|
||||
func (f gitFlag) Type() string {
|
||||
return "REMOTE,REF"
|
||||
}
|
||||
|
||||
// SOURCE FLAG
|
||||
// Adapter to receive a simple source description and translate it to a loader script.
|
||||
// For example 'git+https://github.com/cuelang/cue#master` -> [{do:"git",remote:"https://github.com/cuelang/cue",ref:"master"}]
|
||||
|
||||
func (iv *InputValue) SourceFlag() pflag.Value {
|
||||
return sourceFlag{
|
||||
iv: iv,
|
||||
// An input value encoded as JSON
|
||||
func JSONInput(data string) Input {
|
||||
return Input{
|
||||
Type: InputTypeJSON,
|
||||
JSON: &jsonInput{
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type sourceFlag struct {
|
||||
iv *InputValue
|
||||
type jsonInput struct {
|
||||
// Marshalled JSON data
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (f sourceFlag) Set(s string) error {
|
||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch u.Scheme {
|
||||
case "", "file":
|
||||
return compiler.Compile(
|
||||
"source",
|
||||
// FIXME: include only cue files as a shortcut. Make this configurable somehow.
|
||||
fmt.Sprintf(`[{do:"local",dir:"%s",include:["*.cue","cue.mod"]}]`, u.Host+u.Path),
|
||||
)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported source scheme: %q", u.Scheme)
|
||||
}
|
||||
})
|
||||
func (i jsonInput) Compile() (*compiler.Value, error) {
|
||||
return compiler.DecodeJSON("", []byte(i.Data))
|
||||
}
|
||||
|
||||
func (f sourceFlag) String() string {
|
||||
return f.iv.String()
|
||||
}
|
||||
|
||||
func (f sourceFlag) Type() string {
|
||||
return "PATH | file://PATH | git+ssh://HOST/PATH | git+https://HOST/PATH"
|
||||
}
|
||||
|
||||
// RAW CUE FLAG
|
||||
// Adapter to receive raw cue values from pflag
|
||||
func (iv *InputValue) CueFlag() pflag.Value {
|
||||
return cueFlag{
|
||||
iv: iv,
|
||||
// An input value encoded as YAML
|
||||
func YAMLInput(data string) Input {
|
||||
return Input{
|
||||
Type: InputTypeYAML,
|
||||
YAML: &yamlInput{
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type cueFlag struct {
|
||||
iv *InputValue
|
||||
type yamlInput struct {
|
||||
// Marshalled YAML data
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (f cueFlag) Set(s string) error {
|
||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
||||
return compiler.Compile("cue input", s)
|
||||
})
|
||||
}
|
||||
|
||||
func (f cueFlag) String() string {
|
||||
return f.iv.String()
|
||||
}
|
||||
|
||||
func (f cueFlag) Type() string {
|
||||
return "CUE"
|
||||
}
|
||||
|
||||
func (iv *InputValue) YAMLFlag() pflag.Value {
|
||||
return fileFlag{
|
||||
iv: iv,
|
||||
format: "yaml",
|
||||
}
|
||||
}
|
||||
|
||||
func (iv *InputValue) JSONFlag() pflag.Value {
|
||||
return fileFlag{
|
||||
iv: iv,
|
||||
format: "json",
|
||||
}
|
||||
}
|
||||
|
||||
type fileFlag struct {
|
||||
format string
|
||||
iv *InputValue
|
||||
}
|
||||
|
||||
func (f fileFlag) Set(s string) error {
|
||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
||||
content, err := os.ReadFile(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plaintext, err := decrypt.Data(content, f.format)
|
||||
if err != nil && !errors.Is(err, sops.MetadataNotFound) {
|
||||
return nil, fmt.Errorf("unable to decrypt %q: %w", s, err)
|
||||
}
|
||||
|
||||
if len(plaintext) > 0 {
|
||||
content = plaintext
|
||||
}
|
||||
|
||||
switch f.format {
|
||||
case "json":
|
||||
return compiler.DecodeJSON(s, content)
|
||||
case "yaml":
|
||||
return compiler.DecodeYAML(s, content)
|
||||
default:
|
||||
panic("unsupported file format")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (f fileFlag) String() string {
|
||||
return f.iv.String()
|
||||
}
|
||||
|
||||
func (f fileFlag) Type() string {
|
||||
return strings.ToUpper(f.format)
|
||||
}
|
||||
|
||||
// UTILITIES
|
||||
|
||||
func splitkv(kv string) (cue.Path, string) {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
if parts[0] == "." || parts[0] == "" {
|
||||
return cue.MakePath(), parts[1]
|
||||
}
|
||||
return cue.ParsePath(parts[0]), parts[1]
|
||||
}
|
||||
if len(parts) == 1 {
|
||||
return cue.MakePath(), parts[0]
|
||||
}
|
||||
return cue.MakePath(), ""
|
||||
func (i yamlInput) Compile() (*compiler.Value, error) {
|
||||
return compiler.DecodeYAML("", []byte(i.Data))
|
||||
}
|
||||
|
@ -2,30 +2,21 @@ package dagger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEnvInputFlag(t *testing.T) {
|
||||
env, err := NewEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
func TestInputDir(t *testing.T) {
|
||||
st := &RouteState{
|
||||
LayoutSource: DirInput("/tmp/source", []string{}),
|
||||
}
|
||||
require.NoError(t, st.AddInput("www.source", DirInput(".", []string{})))
|
||||
|
||||
input, err := NewInputValue(`{}`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := input.DirFlag().Set("www.source=."); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := env.SetInput(input.Value()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
route, err := NewRoute(st)
|
||||
require.NoError(t, err)
|
||||
|
||||
localdirs := env.LocalDirs()
|
||||
if len(localdirs) != 1 {
|
||||
t.Fatal(localdirs)
|
||||
}
|
||||
if dir, ok := localdirs["."]; !ok || dir != "." {
|
||||
t.Fatal(localdirs)
|
||||
}
|
||||
localdirs := route.LocalDirs()
|
||||
require.Len(t, localdirs, 2)
|
||||
require.Contains(t, localdirs, ".")
|
||||
require.Contains(t, localdirs, "/tmp/source")
|
||||
}
|
||||
|
@ -84,7 +84,11 @@ func ops(code ...*compiler.Value) ([]*compiler.Value, error) {
|
||||
ops = append(ops, xops...)
|
||||
} else {
|
||||
// 4. error
|
||||
return nil, fmt.Errorf("not executable: %s", x.SourceUnsafe())
|
||||
source, err := x.Source()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return nil, fmt.Errorf("not executable: %s", source)
|
||||
}
|
||||
}
|
||||
return ops, nil
|
||||
|
@ -18,17 +18,59 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
// Contents of a route serialized to a file
|
||||
type RouteState struct {
|
||||
// Globally unique route ID
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// Human-friendly route name.
|
||||
// A route may have more than one name.
|
||||
// FIXME: store multiple names?
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Cue module containing the route layout
|
||||
// The input's top-level artifact is used as a module directory.
|
||||
LayoutSource Input `json:"layout,omitempty"`
|
||||
|
||||
Inputs []inputKV `json:"inputs,omitempty"`
|
||||
}
|
||||
|
||||
type inputKV struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Value Input `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (r *RouteState) AddInput(key string, value Input) error {
|
||||
r.Inputs = append(r.Inputs, inputKV{Key: key, Value: value})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove all inputs at the given key, including sub-keys.
|
||||
// For example RemoveInputs("foo.bar") will remove all inputs
|
||||
// at foo.bar, foo.bar.baz, etc.
|
||||
func (r *RouteState) RemoveInputs(key string) error {
|
||||
newInputs := make([]inputKV, 0, len(r.Inputs))
|
||||
for _, i := range r.Inputs {
|
||||
if i.Key == key {
|
||||
continue
|
||||
}
|
||||
newInputs = append(newInputs, i)
|
||||
}
|
||||
r.Inputs = newInputs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
st *RouteState
|
||||
|
||||
// Env boot script, eg. `[{do:"local",dir:"."}]`
|
||||
// FIXME: rename to 'update' (script to update the env config)
|
||||
// FIXME: embed update script in base as '#update' ?
|
||||
// FIXME: simplify Env by making it single layer? Each layer is one env.
|
||||
// FIXME: simplify Env by making it single layer? Each layer is one r.
|
||||
|
||||
// How to update the base configuration
|
||||
updater *compiler.Value
|
||||
|
||||
// Layer 1: base configuration
|
||||
base *compiler.Value
|
||||
// Layer 1: layout configuration
|
||||
layout *compiler.Value
|
||||
|
||||
// Layer 2: user inputs
|
||||
input *compiler.Value
|
||||
@ -36,67 +78,82 @@ type Env struct {
|
||||
// Layer 3: computed values
|
||||
output *compiler.Value
|
||||
|
||||
// All layers merged together: base + input + output
|
||||
// All layers merged together: layout + input + output
|
||||
state *compiler.Value
|
||||
}
|
||||
|
||||
func (env *Env) Updater() *compiler.Value {
|
||||
return env.updater
|
||||
}
|
||||
|
||||
// Set the updater script for this environment.
|
||||
func (env *Env) SetUpdater(v *compiler.Value) error {
|
||||
if v == nil {
|
||||
var err error
|
||||
v, err = compiler.Compile("", "[]")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
env.updater = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewEnv() (*Env, error) {
|
||||
func NewRoute(st *RouteState) (*Route, error) {
|
||||
empty := compiler.EmptyStruct()
|
||||
env := &Env{
|
||||
base: empty,
|
||||
r := &Route{
|
||||
st: st,
|
||||
layout: empty,
|
||||
input: empty,
|
||||
output: empty,
|
||||
}
|
||||
if err := env.mergeState(); err != nil {
|
||||
|
||||
// Prepare inputs
|
||||
for _, input := range st.Inputs {
|
||||
v, err := input.Value.Compile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.Key == "" {
|
||||
r.input, err = r.input.Merge(v)
|
||||
} else {
|
||||
r.input, err = r.input.MergeTarget(v, input.Key)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := r.mergeState(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := env.SetUpdater(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return env, nil
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (env *Env) State() *compiler.Value {
|
||||
return env.state
|
||||
func (r *Route) ID() string {
|
||||
return r.st.ID
|
||||
}
|
||||
|
||||
func (env *Env) Input() *compiler.Value {
|
||||
return env.input
|
||||
func (r *Route) Name() string {
|
||||
return r.st.Name
|
||||
}
|
||||
|
||||
func (env *Env) SetInput(i *compiler.Value) error {
|
||||
if i == nil {
|
||||
i = compiler.EmptyStruct()
|
||||
}
|
||||
env.input = i
|
||||
return env.mergeState()
|
||||
func (r *Route) LayoutSource() Input {
|
||||
return r.st.LayoutSource
|
||||
}
|
||||
|
||||
// Update the base configuration
|
||||
func (env *Env) Update(ctx context.Context, s Solver) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "Env.Update")
|
||||
func (r *Route) Layout() *compiler.Value {
|
||||
return r.layout
|
||||
}
|
||||
|
||||
func (r *Route) Input() *compiler.Value {
|
||||
return r.input
|
||||
}
|
||||
|
||||
func (r *Route) Output() *compiler.Value {
|
||||
return r.output
|
||||
}
|
||||
|
||||
func (r *Route) State() *compiler.Value {
|
||||
return r.state
|
||||
}
|
||||
|
||||
// LoadLayout loads the layout
|
||||
func (r *Route) LoadLayout(ctx context.Context, s Solver) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "route.Update")
|
||||
defer span.Finish()
|
||||
|
||||
layoutSource, err := r.st.LayoutSource.Compile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := NewPipeline("[internal] source", s, nil)
|
||||
// execute updater script
|
||||
if err := p.Do(ctx, env.updater); err != nil {
|
||||
if err := p.Do(ctx, layoutSource); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -105,28 +162,21 @@ func (env *Env) Update(ctx context.Context, s Solver) error {
|
||||
stdlib.Path: stdlib.FS,
|
||||
"/": p.FS(),
|
||||
}
|
||||
base, err := compiler.Build(sources)
|
||||
layout, err := compiler.Build(sources)
|
||||
if err != nil {
|
||||
return fmt.Errorf("base config: %w", err)
|
||||
return fmt.Errorf("layout config: %w", err)
|
||||
}
|
||||
env.base = base
|
||||
r.layout = layout
|
||||
|
||||
// Commit
|
||||
return env.mergeState()
|
||||
}
|
||||
|
||||
func (env *Env) Base() *compiler.Value {
|
||||
return env.base
|
||||
}
|
||||
|
||||
func (env *Env) Output() *compiler.Value {
|
||||
return env.output
|
||||
return r.mergeState()
|
||||
}
|
||||
|
||||
// Scan all scripts in the environment for references to local directories (do:"local"),
|
||||
// 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.
|
||||
func (env *Env) LocalDirs() map[string]string {
|
||||
func (r *Route) LocalDirs() map[string]string {
|
||||
dirs := map[string]string{}
|
||||
localdirs := func(code ...*compiler.Value) {
|
||||
Analyze(
|
||||
@ -135,6 +185,7 @@ func (env *Env) LocalDirs() map[string]string {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// FIXME: merge Env into Route, or fix the linter error
|
||||
if do != "local" {
|
||||
return nil
|
||||
}
|
||||
@ -150,19 +201,24 @@ func (env *Env) LocalDirs() map[string]string {
|
||||
}
|
||||
// 1. Scan the environment state
|
||||
// FIXME: use a common `flow` instance to avoid rescanning the tree.
|
||||
inst := env.state.CueInst()
|
||||
inst := r.state.CueInst()
|
||||
flow := cueflow.New(&cueflow.Config{}, inst, newTaskFunc(inst, noOpRunner))
|
||||
for _, t := range flow.Tasks() {
|
||||
v := compiler.Wrap(t.Value(), inst)
|
||||
localdirs(v.Get("#compute"))
|
||||
}
|
||||
// 2. Scan the environment updater
|
||||
localdirs(env.Updater())
|
||||
|
||||
// 2. Scan the layout
|
||||
layout, err := r.st.LayoutSource.Compile()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
localdirs(layout)
|
||||
return dirs
|
||||
}
|
||||
|
||||
// FIXME: this is just a 3-way merge. Add var args to compiler.Value.Merge.
|
||||
func (env *Env) mergeState() error {
|
||||
func (r *Route) mergeState() error {
|
||||
// FIXME: make this cleaner in *compiler.Value by keeping intermediary instances
|
||||
// FIXME: state.CueInst() must return an instance with the same
|
||||
// contents as state.v, for the purposes of cueflow.
|
||||
@ -175,15 +231,15 @@ func (env *Env) mergeState() error {
|
||||
err error
|
||||
)
|
||||
|
||||
stateInst, err = stateInst.Fill(env.base.Cue())
|
||||
stateInst, err = stateInst.Fill(r.layout.Cue())
|
||||
if err != nil {
|
||||
return fmt.Errorf("merge base & input: %w", err)
|
||||
}
|
||||
stateInst, err = stateInst.Fill(env.input.Cue())
|
||||
stateInst, err = stateInst.Fill(r.input.Cue())
|
||||
if err != nil {
|
||||
return fmt.Errorf("merge base & input: %w", err)
|
||||
}
|
||||
stateInst, err = stateInst.Fill(env.output.Cue())
|
||||
stateInst, err = stateInst.Fill(r.output.Cue())
|
||||
if err != nil {
|
||||
return fmt.Errorf("merge output with base & input: %w", err)
|
||||
}
|
||||
@ -191,22 +247,24 @@ func (env *Env) mergeState() error {
|
||||
state = compiler.Wrap(stateInst.Value(), stateInst)
|
||||
|
||||
// commit
|
||||
env.state = state
|
||||
r.state = state
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compute missing values in env configuration, and write them to state.
|
||||
func (env *Env) Compute(ctx context.Context, s Solver) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "Env.Compute")
|
||||
type UpOpts struct{}
|
||||
|
||||
// Up missing values in env configuration, and write them to state.
|
||||
func (r *Route) Up(ctx context.Context, s Solver, _ *UpOpts) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "r.Compute")
|
||||
defer span.Finish()
|
||||
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
// Cueflow cue instance
|
||||
inst := env.state.CueInst()
|
||||
inst := r.state.CueInst()
|
||||
|
||||
// Reset the output
|
||||
env.output = compiler.EmptyStruct()
|
||||
r.output = compiler.EmptyStruct()
|
||||
|
||||
// Cueflow config
|
||||
flowCfg := &cueflow.Config{
|
||||
@ -226,7 +284,7 @@ func (env *Env) Compute(ctx context.Context, s Solver) error {
|
||||
}
|
||||
// Merge task value into output
|
||||
var err error
|
||||
env.output, err = env.output.MergePath(t.Value(), t.Path())
|
||||
r.output, err = r.output.MergePath(t.Value(), t.Path())
|
||||
if err != nil {
|
||||
lg.
|
||||
Error().
|
||||
@ -244,13 +302,25 @@ func (env *Env) Compute(ctx context.Context, s Solver) error {
|
||||
}
|
||||
|
||||
{
|
||||
span, _ := opentracing.StartSpanFromContext(ctx, "Env.Compute: merge state")
|
||||
span, _ := opentracing.StartSpanFromContext(ctx, "merge state")
|
||||
defer span.Finish()
|
||||
|
||||
return env.mergeState()
|
||||
return r.mergeState()
|
||||
}
|
||||
}
|
||||
|
||||
type DownOpts struct{}
|
||||
|
||||
func (r *Route) Down(ctx context.Context, _ *DownOpts) error {
|
||||
panic("NOT IMPLEMENTED")
|
||||
}
|
||||
|
||||
func (r *Route) Query(ctx context.Context, expr interface{}, o *QueryOpts) (*compiler.Value, error) {
|
||||
panic("NOT IMPLEMENTED")
|
||||
}
|
||||
|
||||
type QueryOpts struct{}
|
||||
|
||||
func newTaskFunc(inst *cue.Instance, runner cueflow.RunnerFunc) cueflow.TaskFunc {
|
||||
return func(flowVal cue.Value) (cueflow.Runner, error) {
|
||||
v := compiler.Wrap(flowVal, inst)
|
226
dagger/store.go
Normal file
226
dagger/store.go
Normal file
@ -0,0 +1,226 @@
|
||||
package dagger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRouteExist = errors.New("route already exists")
|
||||
ErrRouteNotExist = errors.New("route doesn't exist")
|
||||
)
|
||||
|
||||
const (
|
||||
defaultStoreRoot = "$HOME/.config/dagger/routes"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
root string
|
||||
|
||||
l sync.RWMutex
|
||||
|
||||
routes map[string]*RouteState
|
||||
|
||||
// Various indices for fast lookups
|
||||
routesByName map[string]*RouteState
|
||||
routesByPath map[string]*RouteState
|
||||
pathsByRoute map[string][]string
|
||||
}
|
||||
|
||||
func NewStore(root string) (*Store, error) {
|
||||
store := &Store{
|
||||
root: root,
|
||||
routes: make(map[string]*RouteState),
|
||||
routesByName: make(map[string]*RouteState),
|
||||
routesByPath: make(map[string]*RouteState),
|
||||
pathsByRoute: make(map[string][]string),
|
||||
}
|
||||
return store, store.loadAll()
|
||||
}
|
||||
|
||||
func DefaultStore() (*Store, error) {
|
||||
return NewStore(os.ExpandEnv(defaultStoreRoot))
|
||||
}
|
||||
|
||||
func (s *Store) routePath(name string) string {
|
||||
return path.Join(s.root, name, "route.json")
|
||||
}
|
||||
|
||||
func (s *Store) loadAll() error {
|
||||
files, err := os.ReadDir(s.root)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
if err := s.loadRoute(f.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) loadRoute(name string) error {
|
||||
data, err := os.ReadFile(s.routePath(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var st RouteState
|
||||
if err := json.Unmarshal(data, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
s.indexRoute(&st)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) syncRoute(r *RouteState) error {
|
||||
p := s.routePath(r.Name)
|
||||
|
||||
if err := os.MkdirAll(path.Dir(p), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(r, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(p, data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.reindexRoute(r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) indexRoute(r *RouteState) {
|
||||
s.routes[r.ID] = r
|
||||
s.routesByName[r.Name] = r
|
||||
|
||||
mapPath := func(i Input) {
|
||||
if i.Type != InputTypeDir {
|
||||
return
|
||||
}
|
||||
s.routesByPath[i.Dir.Path] = r
|
||||
s.pathsByRoute[r.ID] = append(s.pathsByRoute[r.ID], i.Dir.Path)
|
||||
}
|
||||
|
||||
mapPath(r.LayoutSource)
|
||||
for _, i := range r.Inputs {
|
||||
mapPath(i.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) deindexRoute(id string) {
|
||||
r, ok := s.routes[id]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
delete(s.routes, r.ID)
|
||||
delete(s.routesByName, r.Name)
|
||||
|
||||
for _, p := range s.pathsByRoute[r.ID] {
|
||||
delete(s.routesByPath, p)
|
||||
}
|
||||
delete(s.pathsByRoute, r.ID)
|
||||
}
|
||||
|
||||
func (s *Store) reindexRoute(r *RouteState) {
|
||||
s.deindexRoute(r.ID)
|
||||
s.indexRoute(r)
|
||||
}
|
||||
|
||||
func (s *Store) CreateRoute(ctx context.Context, st *RouteState) error {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
if _, ok := s.routesByName[st.Name]; ok {
|
||||
return fmt.Errorf("%s: %w", st.Name, ErrRouteExist)
|
||||
}
|
||||
|
||||
st.ID = uuid.New().String()
|
||||
return s.syncRoute(st)
|
||||
}
|
||||
|
||||
type UpdateOpts struct{}
|
||||
|
||||
func (s *Store) UpdateRoute(ctx context.Context, r *RouteState, o *UpdateOpts) error {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
return s.syncRoute(r)
|
||||
}
|
||||
|
||||
type DeleteOpts struct{}
|
||||
|
||||
func (s *Store) DeleteRoute(ctx context.Context, r *RouteState, o *DeleteOpts) error {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
if err := os.Remove(s.routePath(r.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
s.deindexRoute(r.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) LookupRouteByID(ctx context.Context, id string) (*RouteState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
st, ok := s.routes[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: %w", id, ErrRouteNotExist)
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) LookupRouteByName(ctx context.Context, name string) (*RouteState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
st, ok := s.routesByName[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: %w", name, ErrRouteNotExist)
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) LookupRouteByPath(ctx context.Context, path string) (*RouteState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
st, ok := s.routesByPath[path]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: %w", path, ErrRouteNotExist)
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListRoutes(ctx context.Context) ([]*RouteState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
routes := make([]*RouteState, 0, len(s.routes))
|
||||
|
||||
for _, st := range s.routes {
|
||||
routes = append(routes, st)
|
||||
}
|
||||
|
||||
return routes, nil
|
||||
}
|
99
dagger/store_test.go
Normal file
99
dagger/store_test.go
Normal file
@ -0,0 +1,99 @@
|
||||
package dagger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStoreLoad(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
root, err := os.MkdirTemp(os.TempDir(), "dagger-*")
|
||||
require.NoError(t, err)
|
||||
store, err := NewStore(root)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = store.LookupRouteByName(ctx, "notexist")
|
||||
require.Error(t, err)
|
||||
require.True(t, errors.Is(err, ErrRouteNotExist))
|
||||
|
||||
st := &RouteState{
|
||||
Name: "test",
|
||||
}
|
||||
require.NoError(t, store.CreateRoute(ctx, st))
|
||||
|
||||
checkRoutes := func(store *Store) {
|
||||
r, err := store.LookupRouteByID(ctx, st.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, r)
|
||||
require.Equal(t, "test", r.Name)
|
||||
|
||||
r, err = store.LookupRouteByName(ctx, "test")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, r)
|
||||
require.Equal(t, "test", r.Name)
|
||||
|
||||
routes, err := store.ListRoutes(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, routes, 1)
|
||||
require.Equal(t, "test", routes[0].Name)
|
||||
}
|
||||
|
||||
checkRoutes(store)
|
||||
|
||||
// Reload the routes from disk and check again
|
||||
newStore, err := NewStore(root)
|
||||
require.NoError(t, err)
|
||||
checkRoutes(newStore)
|
||||
}
|
||||
|
||||
func TestStoreLookupByPath(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
root, err := os.MkdirTemp(os.TempDir(), "dagger-*")
|
||||
require.NoError(t, err)
|
||||
store, err := NewStore(root)
|
||||
require.NoError(t, err)
|
||||
|
||||
st := &RouteState{
|
||||
Name: "test",
|
||||
}
|
||||
require.NoError(t, st.AddInput("foo", DirInput("/test/path", []string{})))
|
||||
require.NoError(t, store.CreateRoute(ctx, st))
|
||||
|
||||
// Lookup by path
|
||||
r, err := store.LookupRouteByPath(ctx, "/test/path")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, r)
|
||||
require.Equal(t, st.ID, r.ID)
|
||||
|
||||
// Add a new path
|
||||
require.NoError(t, st.AddInput("bar", DirInput("/test/anotherpath", []string{})))
|
||||
require.NoError(t, store.UpdateRoute(ctx, st, nil))
|
||||
|
||||
// Lookup by the previous path
|
||||
r, err = store.LookupRouteByPath(ctx, "/test/path")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, st.ID, r.ID)
|
||||
|
||||
// Lookup by the new path
|
||||
r, err = store.LookupRouteByPath(ctx, "/test/anotherpath")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, st.ID, r.ID)
|
||||
|
||||
// Remove a path
|
||||
require.NoError(t, st.RemoveInputs("foo"))
|
||||
require.NoError(t, store.UpdateRoute(ctx, st, nil))
|
||||
|
||||
// Lookup by the removed path should fail
|
||||
_, err = store.LookupRouteByPath(ctx, "/test/path")
|
||||
require.Error(t, err)
|
||||
|
||||
// Lookup by the other path should still work
|
||||
_, err = store.LookupRouteByPath(ctx, "/test/anotherpath")
|
||||
require.NoError(t, err)
|
||||
}
|
2
go.mod
2
go.mod
@ -8,6 +8,7 @@ require (
|
||||
github.com/containerd/console v1.0.1
|
||||
github.com/docker/distribution v2.7.1+incompatible
|
||||
github.com/emicklei/proto v1.9.0 // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
||||
github.com/moby/buildkit v0.8.2
|
||||
@ -18,6 +19,7 @@ require (
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.5.1 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
|
||||
github.com/uber/jaeger-client-go v2.25.0+incompatible
|
||||
|
2
go.sum
2
go.sum
@ -659,6 +659,8 @@ github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
|
||||
github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE=
|
||||
github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
|
@ -129,7 +129,7 @@ test::exec(){
|
||||
test::one "Exec: env valid" --exit=0 --stdout={} \
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute "$d"/exec/env/valid
|
||||
test::one "Exec: env with overlay" --exit=0 \
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute --input-cue 'bar: "overlay environment"' "$d"/exec/env/overlay
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute --input-string 'bar=overlay environment' "$d"/exec/env/overlay
|
||||
|
||||
test::one "Exec: non existent dir" --exit=0 --stdout={} \
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute "$d"/exec/dir/doesnotexist
|
||||
@ -230,16 +230,15 @@ test::input() {
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute "$d"/input/simple
|
||||
|
||||
test::one "Input: simple input" --exit=0 --stdout='{"in":"foobar","test":"received: foobar"}' \
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute --input-cue 'in: "foobar"' "$d"/input/simple
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute --input-string 'in=foobar' "$d"/input/simple
|
||||
|
||||
test::one "Input: default values" --exit=0 --stdout='{"in":"default input","test":"received: default input"}' \
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute "$d"/input/default
|
||||
|
||||
test::one "Input: override default value" --exit=0 --stdout='{"in":"foobar","test":"received: foobar"}' \
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute --input-cue 'in: "foobar"' "$d"/input/default
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute --input-string 'in=foobar' "$d"/input/default
|
||||
}
|
||||
|
||||
|
||||
test::subdir() {
|
||||
test::one "Subdir: simple usage" --exit=0 --stdout='{"hello":"world"}' \
|
||||
"$dagger" "${DAGGER_BINARY_ARGS[@]}" compute "$d"/subdir/simple
|
||||
|
Reference in New Issue
Block a user