Merge pull request #235 from dagger/cli-query

Implemented cli query command
This commit is contained in:
Andrea Luzzardi 2021-03-31 18:41:01 -07:00 committed by GitHub
commit e0afc6304d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 57 deletions

View File

@ -65,9 +65,12 @@ func DeploymentUp(ctx context.Context, deployment *dagger.Deployment) {
if err != nil { if err != nil {
lg.Fatal().Err(err).Msg("unable to create client") lg.Fatal().Err(err).Msg("unable to create client")
} }
output, err := c.Up(ctx, deployment) output, err := c.Do(ctx, deployment, func(ctx context.Context, deployment *dagger.Deployment, s dagger.Solver) error {
log.Ctx(ctx).Debug().Msg("bringing deployment up")
return deployment.Up(ctx, s, nil)
})
if err != nil { if err != nil {
lg.Fatal().Err(err).Msg("failed to compute") lg.Fatal().Err(err).Msg("failed to up deployment")
} }
fmt.Println(output.JSON()) fmt.Println(output.JSON())
} }

View File

@ -3,18 +3,20 @@ package cmd
import ( import (
"fmt" "fmt"
"cuelang.org/go/cue"
"dagger.io/go/cmd/dagger/cmd/common" "dagger.io/go/cmd/dagger/cmd/common"
"dagger.io/go/cmd/dagger/logger" "dagger.io/go/cmd/dagger/logger"
"dagger.io/go/dagger" "dagger.io/go/dagger"
"dagger.io/go/dagger/compiler"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var queryCmd = &cobra.Command{ var queryCmd = &cobra.Command{
Use: "query [EXPR] [flags]", Use: "query [TARGET] [flags]",
Short: "Query the contents of a deployment", Short: "Query the contents of a deployment",
Args: cobra.ExactArgs(1), Args: cobra.MaximumNArgs(1),
PreRun: func(cmd *cobra.Command, args []string) { PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags: // Fix Viper bug for duplicate flags:
// https://github.com/spf13/viper/issues/233 // https://github.com/spf13/viper/issues/233
@ -26,6 +28,8 @@ var queryCmd = &cobra.Command{
lg := logger.New() lg := logger.New()
ctx := lg.WithContext(cmd.Context()) ctx := lg.WithContext(cmd.Context())
cueOpts := parseQueryFlags()
store, err := dagger.DefaultStore() store, err := dagger.DefaultStore()
if err != nil { if err != nil {
lg.Fatal().Err(err).Msg("failed to load store") lg.Fatal().Err(err).Msg("failed to load store")
@ -33,31 +37,73 @@ var queryCmd = &cobra.Command{
deployment := common.GetCurrentDeployment(ctx, store) deployment := common.GetCurrentDeployment(ctx, store)
expr := args[0] lg = lg.With().
Str("deploymentName", deployment.Name()).
Str("deploymentId", deployment.ID()).
Logger()
out, err := deployment.Query(ctx, expr, nil) cuePath := cue.MakePath()
if err != nil { if len(args) > 0 {
lg. cuePath = cue.ParsePath(args[0])
Fatal().
Err(err).
Str("deploymentName", deployment.Name()).
Str("deploymentId", deployment.ID()).
Msg("failed to query deployment")
} }
fmt.Println(out) c, err := dagger.NewClient(ctx, "")
// TODO: Implement options: --no-*, --format, --revision if err != nil {
lg.Fatal().Err(err).Msg("unable to create client")
}
output, err := c.Do(ctx, deployment, nil)
if err != nil {
lg.Fatal().Err(err).Msg("failed to query deployment")
}
cueVal := output.LookupPath(cuePath)
if viper.GetBool("concrete") {
if err := cueVal.IsConcreteR(); err != nil {
lg.Fatal().Err(compiler.Err(err)).Msg("not concrete")
}
}
out, err := cueVal.Source(cueOpts...)
if err != nil {
lg.Fatal().Err(err).Msg("failed to lookup source")
}
fmt.Println(string(out))
}, },
} }
func init() { func parseQueryFlags() []cue.Option {
queryCmd.Flags().String("revision", "latest", "Query a specific version of the deployment") opts := []cue.Option{
queryCmd.Flags().StringP("format", "f", "", "Output format (json|yaml|cue|text|env)") cue.Definitions(true),
}
queryCmd.Flags().BoolP("--no-input", "I", false, "Exclude inputs from query") if viper.GetBool("concrete") {
queryCmd.Flags().BoolP("--no-output", "O", false, "Exclude outputs from query") opts = append(opts, cue.Concrete(true))
queryCmd.Flags().BoolP("--no-plan", "P", false, "Exclude outputs from query") }
if viper.GetBool("show-optional") {
opts = append(opts, cue.Optional(true))
}
if viper.GetBool("show-attributes") {
opts = append(opts, cue.Attributes(true))
}
return opts
}
func init() {
queryCmd.Flags().BoolP("concrete", "c", false, "Require the evaluation to be concrete")
queryCmd.Flags().BoolP("show-optional", "O", false, "Display optional fields")
queryCmd.Flags().BoolP("show-attributes", "A", false, "Display field attributes")
// FIXME: implement the flags below
// queryCmd.Flags().String("revision", "latest", "Query a specific version of the deployment")
// 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-plan", "P", false, "Exclude outputs from query")
if err := viper.BindPFlags(queryCmd.Flags()); err != nil { if err := viper.BindPFlags(queryCmd.Flags()); err != nil {
panic(err) panic(err)

View File

@ -61,8 +61,10 @@ func NewClient(ctx context.Context, host string) (*Client, error) {
}, nil }, nil
} }
type ClientDoFunc func(context.Context, *Deployment, Solver) error
// FIXME: return completed *Route, instead of *compiler.Value // FIXME: return completed *Route, instead of *compiler.Value
func (c *Client) Up(ctx context.Context, deployment *Deployment) (*compiler.Value, error) { func (c *Client) Do(ctx context.Context, deployment *Deployment, fn ClientDoFunc) (*compiler.Value, error) {
lg := log.Ctx(ctx) lg := log.Ctx(ctx)
eg, gctx := errgroup.WithContext(ctx) eg, gctx := errgroup.WithContext(ctx)
@ -79,7 +81,7 @@ func (c *Client) Up(ctx context.Context, deployment *Deployment) (*compiler.Valu
outr, outw := io.Pipe() outr, outw := io.Pipe()
eg.Go(func() error { eg.Go(func() error {
defer outw.Close() defer outw.Close()
return c.buildfn(gctx, deployment, events, outw) return c.buildfn(gctx, deployment, fn, events, outw)
}) })
// Spawn output retriever // Spawn output retriever
@ -96,7 +98,7 @@ func (c *Client) Up(ctx context.Context, deployment *Deployment) (*compiler.Valu
return out, eg.Wait() return out, eg.Wait()
} }
func (c *Client) buildfn(ctx context.Context, deployment *Deployment, ch chan *bk.SolveStatus, w io.WriteCloser) error { func (c *Client) buildfn(ctx context.Context, deployment *Deployment, fn ClientDoFunc, ch chan *bk.SolveStatus, w io.WriteCloser) error {
lg := log.Ctx(ctx) lg := log.Ctx(ctx)
// Scan local dirs to grant access // Scan local dirs to grant access
@ -138,9 +140,10 @@ func (c *Client) buildfn(ctx context.Context, deployment *Deployment, ch chan *b
} }
// Compute output overlay // Compute output overlay
lg.Debug().Msg("computing deployment") if fn != nil {
if err := deployment.Up(ctx, s, nil); err != nil { if err := fn(ctx, deployment, s); err != nil {
return nil, compiler.Err(err) return nil, compiler.Err(err)
}
} }
// Export deployment to a cue directory // Export deployment to a cue directory
@ -149,9 +152,14 @@ func (c *Client) buildfn(ctx context.Context, deployment *Deployment, ch chan *b
span, _ := opentracing.StartSpanFromContext(ctx, "Deployment.Export") span, _ := opentracing.StartSpanFromContext(ctx, "Deployment.Export")
defer span.Finish() defer span.Finish()
stateSource, err := deployment.State().Source()
if err != nil {
return nil, compiler.Err(err)
}
st := llb.Scratch().File( st := llb.Scratch().File(
llb.Mkfile("state.cue", 0600, deployment.State().JSON()), llb.Mkfile("state.cue", 0600, stateSource),
llb.WithCustomName("[internal] serializing state to JSON"), llb.WithCustomName("[internal] serializing state to CUE"),
) )
ref, err := s.Solve(ctx, st) ref, err := s.Solve(ctx, st)
if err != nil { if err != nil {

View File

@ -204,11 +204,11 @@ func (v *Value) Validate() error {
} }
// Return cue source for this value // Return cue source for this value
func (v *Value) Source() ([]byte, error) { func (v *Value) Source(opts ...cue.Option) ([]byte, error) {
v.cc.rlock() v.cc.rlock()
defer v.cc.runlock() defer v.cc.runlock()
return cueformat.Node(v.val.Eval().Syntax()) return cueformat.Node(v.val.Eval().Syntax(opts...))
} }
func (v *Value) IsEmptyStruct() bool { func (v *Value) IsEmptyStruct() bool {

View File

@ -308,10 +308,6 @@ func (d *Deployment) Down(ctx context.Context, _ *DownOpts) error {
panic("NOT IMPLEMENTED") panic("NOT IMPLEMENTED")
} }
func (d *Deployment) Query(ctx context.Context, expr interface{}, o *QueryOpts) (*compiler.Value, error) {
panic("NOT IMPLEMENTED")
}
type QueryOpts struct{} type QueryOpts struct{}
func newTaskFunc(inst *cue.Instance, runner cueflow.RunnerFunc) cueflow.TaskFunc { func newTaskFunc(inst *cue.Instance, runner cueflow.RunnerFunc) cueflow.TaskFunc {

View File

@ -1,12 +1,15 @@
package test package test
import "dagger.io/dagger" import (
"dagger.io/dagger"
"dagger.io/llb"
)
// Set to `--input-dir=./tests/dockerbuild/testdata` // Set to `--input-dir=./tests/dockerbuild/testdata`
TestData: dagger.#Artifact TestData: dagger.#Artifact
TestInlinedDockerfile: #compute: [ TestInlinedDockerfile: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
dockerfile: """ dockerfile: """
FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
RUN echo hello world RUN echo hello world
@ -15,51 +18,51 @@ TestInlinedDockerfile: #compute: [
] ]
TestOpChaining: #compute: [ TestOpChaining: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
dockerfile: """ dockerfile: """
FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
RUN echo foobar > /output RUN echo foobar > /output
""" """
}, },
dagger.#Exec & { llb.#Exec & {
args: ["sh", "-c", "test $(cat /output) = foobar"] args: ["sh", "-c", "test $(cat /output) = foobar"]
}, },
] ]
TestBuildContext: #compute: [ TestBuildContext: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
context: TestData context: TestData
}, },
dagger.#Exec & { llb.#Exec & {
args: ["sh", "-c", "test $(cat /dir/foo) = foobar"] args: ["sh", "-c", "test $(cat /dir/foo) = foobar"]
}, },
] ]
TestBuildContextAndDockerfile: #compute: [ TestBuildContextAndDockerfile: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
context: TestData context: TestData
dockerfile: """ dockerfile: """
FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
COPY foo /override COPY foo /override
""" """
}, },
dagger.#Exec & { llb.#Exec & {
args: ["sh", "-c", "test $(cat /override) = foobar"] args: ["sh", "-c", "test $(cat /override) = foobar"]
}, },
] ]
TestDockerfilePath: #compute: [ TestDockerfilePath: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
context: TestData context: TestData
dockerfilePath: "./dockerfilepath/Dockerfile.custom" dockerfilePath: "./dockerfilepath/Dockerfile.custom"
}, },
dagger.#Exec & { llb.#Exec & {
args: ["sh", "-c", "test $(cat /test) = dockerfilePath"] args: ["sh", "-c", "test $(cat /test) = dockerfilePath"]
}, },
] ]
TestBuildArgs: #compute: [ TestBuildArgs: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
dockerfile: """ dockerfile: """
FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
ARG TEST=foo ARG TEST=foo
@ -71,7 +74,7 @@ TestBuildArgs: #compute: [
// FIXME: this doesn't test anything beside not crashing // FIXME: this doesn't test anything beside not crashing
TestBuildLabels: #compute: [ TestBuildLabels: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
dockerfile: """ dockerfile: """
FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
""" """
@ -81,7 +84,7 @@ TestBuildLabels: #compute: [
// FIXME: this doesn't test anything beside not crashing // FIXME: this doesn't test anything beside not crashing
TestBuildPlatform: #compute: [ TestBuildPlatform: #compute: [
dagger.#DockerBuild & { llb.#DockerBuild & {
dockerfile: """ dockerfile: """
FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d FROM alpine:latest@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
""" """

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"dagger.io/dagger" "dagger.io/llb"
"dagger.io/alpine" "dagger.io/alpine"
) )
@ -10,11 +10,11 @@ TestPushContainer: {
random: { random: {
string string
#compute: [ #compute: [
dagger.#Load & {from: alpine.#Image}, llb.#Load & {from: alpine.#Image},
dagger.#Exec & { llb.#Exec & {
args: ["sh", "-c", "echo -n $RANDOM > /rand"] args: ["sh", "-c", "echo -n $RANDOM > /rand"]
}, },
dagger.#Export & { llb.#Export & {
source: "/rand" source: "/rand"
}, },
] ]
@ -24,11 +24,11 @@ TestPushContainer: {
push: { push: {
ref: "daggerio/ci-test:\(random)" ref: "daggerio/ci-test:\(random)"
#compute: [ #compute: [
dagger.#WriteFile & { llb.#WriteFile & {
content: random content: random
dest: "/rand" dest: "/rand"
}, },
dagger.#PushContainer & { llb.#PushContainer & {
"ref": ref "ref": ref
}, },
] ]
@ -36,15 +36,15 @@ TestPushContainer: {
// Pull the image back // Pull the image back
pull: #compute: [ pull: #compute: [
dagger.#FetchContainer & { llb.#FetchContainer & {
ref: push.ref ref: push.ref
}, },
] ]
// Check the content // Check the content
check: #compute: [ check: #compute: [
dagger.#Load & {from: alpine.#Image}, llb.#Load & {from: alpine.#Image},
dagger.#Exec & { llb.#Exec & {
args: [ args: [
"sh", "-c", #""" "sh", "-c", #"""
test "$(cat /src/rand)" = "\#(random)" test "$(cat /src/rand)" = "\#(random)"