CLI backend to manage layout and inputs
Signed-off-by: Solomon Hykes <sh.github.6811@hykes.org>
This commit is contained in:
parent
9c110716a5
commit
7bf05d3cc9
@ -33,7 +33,7 @@ var downCmd = &cobra.Command{
|
|||||||
|
|
||||||
// TODO: Implement options: --no-cache
|
// TODO: Implement options: --no-cache
|
||||||
if err := route.Down(ctx, nil); err != nil {
|
if err := route.Down(ctx, nil); err != nil {
|
||||||
lg.Fatal().Err(err).Str("route-name", routeName).Str("route-id", route.ID).Msg("failed to up the route")
|
lg.Fatal().Err(err).Str("route-name", routeName).Str("route-id", route.ID()).Msg("failed to up the route")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,12 @@ var newCmd = &cobra.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
lg.Fatal().Err(err).Msg("failed to create route")
|
lg.Fatal().Err(err).Msg("failed to create route")
|
||||||
}
|
}
|
||||||
lg.Info().Str("route-id", route.ID).Str("route-name", routeName).Msg("created route")
|
lg.Info().Str("route-id", route.ID()).Str("route-name", routeName).Msg("created route")
|
||||||
|
|
||||||
if upRoute {
|
if upRoute {
|
||||||
lg.Info().Str("route-id", route.ID).Msg("bringing route online")
|
lg.Info().Str("route-id", route.ID()).Msg("bringing route online")
|
||||||
if err := route.Up(ctx, nil); err != nil {
|
if err := route.Up(ctx, nil); err != nil {
|
||||||
lg.Fatal().Err(err).Str("route-id", route.ID).Msg("failed to create route")
|
lg.Fatal().Err(err).Str("route-id", route.ID()).Msg("failed to create route")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -36,7 +36,7 @@ var queryCmd = &cobra.Command{
|
|||||||
|
|
||||||
out, err := route.Query(ctx, expr, nil)
|
out, err := route.Query(ctx, expr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lg.Fatal().Err(err).Str("route-name", routeName).Str("route-id", route.ID).Msg("failed to query route")
|
lg.Fatal().Err(err).Str("route-name", routeName).Str("route-id", route.ID()).Msg("failed to query route")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(out)
|
fmt.Println(out)
|
||||||
|
@ -33,7 +33,7 @@ var upCmd = &cobra.Command{
|
|||||||
|
|
||||||
// TODO: Implement options: --no-cache
|
// TODO: Implement options: --no-cache
|
||||||
if err := route.Up(ctx, nil); err != nil {
|
if err := route.Up(ctx, nil); err != nil {
|
||||||
lg.Fatal().Err(err).Str("route-name", routeName).Str("route-id", route.ID).Msg("failed to up the route")
|
lg.Fatal().Err(err).Str("route-name", routeName).Str("route-id", route.ID()).Msg("failed to up the route")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
298
dagger/deprecated_input.go
Normal file
298
dagger/deprecated_input.go
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputValue) Value() *compiler.Value {
|
||||||
|
return iv.root
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputValue) String() string {
|
||||||
|
s, _ := iv.root.SourceString()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInputValue(base interface{}) (*InputValue, error) {
|
||||||
|
root, err := compiler.Compile("base", base)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &InputValue{
|
||||||
|
root: root,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapter to receive string values from pflag
|
||||||
|
func (iv *InputValue) StringFlag() pflag.Value {
|
||||||
|
return stringFlag{
|
||||||
|
iv: iv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringFlag struct {
|
||||||
|
iv *InputValue
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dirFlag struct {
|
||||||
|
iv *InputValue
|
||||||
|
include []string
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type gitFlag struct {
|
||||||
|
iv *InputValue
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sourceFlag struct {
|
||||||
|
iv *InputValue
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cueFlag struct {
|
||||||
|
iv *InputValue
|
||||||
|
}
|
||||||
|
|
||||||
|
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(), ""
|
||||||
|
}
|
335
dagger/input.go
335
dagger/input.go
@ -2,297 +2,144 @@ package dagger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"cuelang.org/go/cue"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
|
|
||||||
"dagger.io/go/dagger/compiler"
|
"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,
|
// An input is a value or artifact supplied by the user.
|
||||||
// such as command-line flag parsing.
|
//
|
||||||
type InputValue struct {
|
// - A value is any structured data which can be encoded as cue.
|
||||||
root *compiler.Value
|
//
|
||||||
|
// - 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 Input interface {
|
||||||
|
// Compile to a cue value which can be merged into a route config
|
||||||
|
Compile() (*compiler.Value, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iv *InputValue) Value() *compiler.Value {
|
// An input artifact loaded from a local directory
|
||||||
return iv.root
|
func DirInput(path string, include []string) Input {
|
||||||
|
return &dirInput{
|
||||||
|
Type: "dir",
|
||||||
|
Path: path,
|
||||||
|
Include: include,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iv *InputValue) String() string {
|
type dirInput struct {
|
||||||
s, _ := iv.root.SourceString()
|
Type string
|
||||||
return s
|
Path string
|
||||||
|
Include []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInputValue(base interface{}) (*InputValue, error) {
|
func (dir dirInput) Compile() (*compiler.Value, error) {
|
||||||
root, err := compiler.Compile("base", base)
|
// FIXME: serialize an intermediate struct, instead of generating cue source
|
||||||
|
includeLLB, err := json.Marshal(dir.Include)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &InputValue{
|
llb := fmt.Sprintf(
|
||||||
root: root,
|
`[{do:"local",dir:"%s",include:%s}]`,
|
||||||
}, nil
|
dir.Path,
|
||||||
|
includeLLB,
|
||||||
|
)
|
||||||
|
return compiler.Compile("", llb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iv *InputValue) Set(s string, enc func(string) (interface{}, error)) error {
|
// An input artifact loaded from a git repository
|
||||||
// Split from eg. 'foo.bar={bla:"bla"}`
|
type gitInput struct {
|
||||||
k, vRaw := splitkv(s)
|
Type string
|
||||||
v, err := enc(vRaw)
|
Remote string
|
||||||
if err != nil {
|
Ref string
|
||||||
return err
|
Dir string
|
||||||
}
|
|
||||||
root, err := iv.root.MergePath(v, k)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
iv.root = root
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapter to receive string values from pflag
|
func GitInput(remote, ref, dir string) Input {
|
||||||
func (iv *InputValue) StringFlag() pflag.Value {
|
return &gitInput{
|
||||||
return stringFlag{
|
Type: "git",
|
||||||
iv: iv,
|
Remote: remote,
|
||||||
|
Ref: ref,
|
||||||
|
Dir: dir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type stringFlag struct {
|
func (git gitInput) Compile() (*compiler.Value, error) {
|
||||||
iv *InputValue
|
panic("NOT IMPLEMENTED")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sf stringFlag) Set(s string) error {
|
// An input artifact loaded from a docker container
|
||||||
return sf.iv.Set(s, func(s string) (interface{}, error) {
|
func DockerInput(ref string) Input {
|
||||||
return s, nil
|
return &dockerInput{
|
||||||
})
|
Type: "docker",
|
||||||
}
|
Ref: ref,
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type dirFlag struct {
|
type dockerInput struct {
|
||||||
iv *InputValue
|
Type string
|
||||||
include []string
|
Ref string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f dirFlag) Set(s string) error {
|
func (i dockerInput) Compile() (*compiler.Value, error) {
|
||||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
panic("NOT IMPLEMENTED")
|
||||||
// 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 (f dirFlag) String() string {
|
// An input value encoded as text
|
||||||
return f.iv.String()
|
func TextInput(data string) Input {
|
||||||
}
|
return &textInput{
|
||||||
|
Type: "text",
|
||||||
func (f dirFlag) Type() string {
|
Data: data,
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type gitFlag struct {
|
type textInput struct {
|
||||||
iv *InputValue
|
Type string
|
||||||
|
Data string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f gitFlag) Set(s string) error {
|
func (i textInput) Compile() (*compiler.Value, error) {
|
||||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
panic("NOT IMPLEMENTED")
|
||||||
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 (f gitFlag) String() string {
|
// An input value encoded as JSON
|
||||||
return f.iv.String()
|
func JSONInput(data string) Input {
|
||||||
}
|
return &jsonInput{
|
||||||
|
Type: "json",
|
||||||
func (f gitFlag) Type() string {
|
Data: data,
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type sourceFlag struct {
|
type jsonInput struct {
|
||||||
iv *InputValue
|
Type string
|
||||||
|
// Marshalled JSON data
|
||||||
|
Data string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f sourceFlag) Set(s string) error {
|
func (i jsonInput) Compile() (*compiler.Value, error) {
|
||||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
panic("NOT IMPLEMENTED")
|
||||||
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 (f sourceFlag) String() string {
|
// An input value encoded as YAML
|
||||||
return f.iv.String()
|
func YAMLInput(data string) Input {
|
||||||
}
|
return &yamlInput{
|
||||||
|
Type: "yaml",
|
||||||
func (f sourceFlag) Type() string {
|
Data: data,
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cueFlag struct {
|
type yamlInput struct {
|
||||||
iv *InputValue
|
Type string
|
||||||
|
// Marshalled YAML data
|
||||||
|
Data string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f cueFlag) Set(s string) error {
|
func (i yamlInput) Compile() (*compiler.Value, error) {
|
||||||
return f.iv.Set(s, func(s string) (interface{}, error) {
|
panic("NOT IMPLEMENTED")
|
||||||
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(), ""
|
|
||||||
}
|
}
|
||||||
|
116
dagger/route.go
116
dagger/route.go
@ -8,16 +8,67 @@ import (
|
|||||||
|
|
||||||
// A deployment route
|
// A deployment route
|
||||||
type Route struct {
|
type Route struct {
|
||||||
|
st routeState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Route) ID() string {
|
||||||
|
return r.st.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Route) Name() string {
|
||||||
|
return r.st.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Route) LayoutSource() Input {
|
||||||
|
return r.st.LayoutSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) SetLayoutSource(ctx context.Context, src Input) error {
|
||||||
|
r.st.LayoutSource = src
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) AddInput(ctx context.Context, key string, value Input) error {
|
||||||
|
r.st.Inputs = append(r.st.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 *Route) RemoveInputs(ctx context.Context, key string) error {
|
||||||
|
panic("NOT IMPLEMENTED")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contents of a route serialized to a file
|
||||||
|
type routeState struct {
|
||||||
// Globally unique route ID
|
// Globally unique route ID
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
// Human-friendly route name.
|
// Human-friendly route name.
|
||||||
// A route may have more than one name.
|
// A route may have more than one name.
|
||||||
|
// FIXME: store multiple names?
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
// Cue module containing the route layout
|
||||||
|
// The input's top-level artifact is used as a module directory.
|
||||||
|
LayoutSource Input
|
||||||
|
|
||||||
|
Inputs []inputKV
|
||||||
|
}
|
||||||
|
|
||||||
|
type inputKV struct {
|
||||||
|
Key string
|
||||||
|
Value Input
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRoute(ctx context.Context, name string, o *CreateOpts) (*Route, error) {
|
func CreateRoute(ctx context.Context, name string, o *CreateOpts) (*Route, error) {
|
||||||
panic("NOT IMPLEMENTED")
|
return &Route{
|
||||||
|
st: routeState{
|
||||||
|
ID: "FIXME",
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateOpts struct{}
|
type CreateOpts struct{}
|
||||||
@ -57,66 +108,3 @@ func (r *Route) Query(ctx context.Context, expr interface{}, o *QueryOpts) (*com
|
|||||||
}
|
}
|
||||||
|
|
||||||
type QueryOpts struct{}
|
type QueryOpts struct{}
|
||||||
|
|
||||||
func (r *Route) SetLayout(ctx context.Context, a *Artifact) error {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Route) Layout() (*Artifact, error) {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Route) AddInputArtifact(ctx context.Context, target string, a *Artifact) error {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Route) AddInputValue(ctx context.Context, target string, v *compiler.Value) error {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: how does remove work? Does it require a specific file layout?
|
|
||||||
func (r *Route) RemoveInputs(ctx context.Context, target string) error {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: connect outputs to auto-export values and artifacts.
|
|
||||||
|
|
||||||
// An artifact is a piece of data, like a source code checkout,
|
|
||||||
// binary bundle, container 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 Artifact struct {
|
|
||||||
llb interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Dir(path string, include []string) *Artifact {
|
|
||||||
var llb struct {
|
|
||||||
Do string
|
|
||||||
Include []string
|
|
||||||
}
|
|
||||||
llb.Do = "local"
|
|
||||||
llb.Include = include
|
|
||||||
return &Artifact{
|
|
||||||
llb: llb,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Git(remote, ref, dir string) *Artifact {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Container(ref string) *Artifact {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
func LLB(code interface{}) *Artifact {
|
|
||||||
panic("NOT IMPLEMENTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: manage base
|
|
||||||
// FIXME: manage inputs
|
|
||||||
// FIXME: manage outputs
|
|
||||||
|
Reference in New Issue
Block a user