Skip to content

Commit

Permalink
Merge branch 'main' into webui-settings
Browse files Browse the repository at this point in the history
  • Loading branch information
XandraMcC committed Dec 11, 2023
2 parents 98d6b2c + 1927faa commit 659ae62
Show file tree
Hide file tree
Showing 30 changed files with 1,190 additions and 72 deletions.
12 changes: 6 additions & 6 deletions apps/job-info-consumer/consumer/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.15.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.23.1 // indirect
github.com/aws/smithy-go v1.15.0 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
Expand Down Expand Up @@ -86,7 +86,7 @@ require (
github.com/flynn/noise v1.0.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-git/go-git/v5 v5.8.1 // indirect
Expand All @@ -98,9 +98,9 @@ require (
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.7 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
Expand Down Expand Up @@ -177,7 +177,7 @@ require (
github.com/koron/go-ssdp v0.0.4 // indirect
github.com/labstack/echo/v4 v4.11.1 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/libp2p/go-cidranger v1.1.0 // indirect
github.com/libp2p/go-doh-resolver v0.4.0 // indirect
Expand Down
16 changes: 7 additions & 9 deletions apps/job-info-consumer/consumer/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.22.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9
github.com/aws/aws-sdk-go-v2/service/sts v1.23.1 h1:ASNYk1ypWAxRhJjKS0jBnTUeDl7HROOpeSMu1xDA/I8=
github.com/aws/aws-sdk-go-v2/service/sts v1.23.1/go.mod h1:2cnsAhVT3mqusovc2stUSUrSBGTcX9nh8Tu6xh//2eI=
github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8=
github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
Expand Down Expand Up @@ -271,8 +271,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q=
github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
Expand Down Expand Up @@ -321,14 +320,14 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
Expand Down Expand Up @@ -667,8 +666,8 @@ github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUU
github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
Expand Down Expand Up @@ -1305,7 +1304,6 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
Expand Down
79 changes: 79 additions & 0 deletions cmd/cli/exec/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package exec

import (
"fmt"
"strings"

"github.com/spf13/pflag"
)

// ExtractUnknownArgs extracts any long-form flags (--something) that are not
// currently configured for this command, they must be flags intended for the
// custom job type.
func ExtractUnknownArgs(flags *pflag.FlagSet, args []string) []string {
unknownArgs := []string{}

for i := 0; i < len(args); i++ {
arg := args[i]
var field *pflag.Flag

if arg[0] == '-' {
if arg[1] == '-' {
field = flags.Lookup(strings.SplitN(arg[2:], "=", 2)[0])
} else {
for _, s := range arg[1:] {
field = flags.ShorthandLookup(string(s))
if field == nil {
break
}
}
}
} else {
continue
}

if field != nil {
if field.NoOptDefVal == "" && i+1 < len(args) && field.Value.String() == args[i+1] {
i++
}
continue
}

// Make sure we allow `--code=.` and `--code .`
if !strings.Contains(arg, "=") {
if i+1 < len(args) {
if args[i+1][0] != '-' {
arg = fmt.Sprintf("%s=%s", arg, args[i+1])
}
}
}

if arg == "--" {
continue
}

unknownArgs = append(unknownArgs, arg)
}

return unknownArgs
}

func flagsToMap(flags []string) map[string]string {
m := make(map[string]string)

for _, flag := range flags {
if flag == "--" {
continue // skip the user escaping the cmd args
}

flagString := strings.TrimPrefix(flag, "-")
flagString = strings.TrimPrefix(flagString, "-") // just in case there's a second -
parts := strings.SplitN(flagString, "=", 2)
if len(parts) == 2 {
// if the flag has no value, it's probably a standalone bool
m[parts[0]] = parts[1]
}
}

return m
}
213 changes: 213 additions & 0 deletions cmd/cli/exec/exec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package exec

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"
"k8s.io/kubectl/pkg/util/i18n"

"github.com/bacalhau-project/bacalhau/cmd/util"
"github.com/bacalhau-project/bacalhau/cmd/util/flags/cliflags"
"github.com/bacalhau-project/bacalhau/cmd/util/printer"
"github.com/bacalhau-project/bacalhau/pkg/lib/template"
"github.com/bacalhau-project/bacalhau/pkg/models"
"github.com/bacalhau-project/bacalhau/pkg/models/migration/legacy"
"github.com/bacalhau-project/bacalhau/pkg/publicapi/apimodels"
"github.com/bacalhau-project/bacalhau/pkg/storage/inline"
"github.com/bacalhau-project/bacalhau/pkg/userstrings"
"github.com/bacalhau-project/bacalhau/pkg/util/templates"
)

var (
getLong = templates.LongDesc(i18n.T(`
Execute a specific job type.
`))

//nolint:lll // Documentation
getExample = templates.Examples(i18n.T(`
# Execute the app.py script with Python
bacalhau exec python app.py
# Run a duckdb query against a CSV file
bacalhau exec -i src=...,dst=/inputs/data.csv duckdb "select * from /inputs/data.csv"
`))
)

type ExecOptions struct {
SpecSettings *cliflags.SpecFlagSettings
RunTimeSettings *cliflags.RunTimeSettings
Code string
}

func NewExecOptions() *ExecOptions {
return &ExecOptions{
SpecSettings: cliflags.NewSpecFlagDefaultSettings(),
RunTimeSettings: cliflags.DefaultRunTimeSettings(),
}
}

func NewCmd() *cobra.Command {
options := NewExecOptions()
return NewCmdWithOptions(options)
}

func NewCmdWithOptions(options *ExecOptions) *cobra.Command {
execCmd := &cobra.Command{
Use: "exec [jobtype]",
Short: "Execute a specific job type",
Long: getLong,
Example: getExample,
Args: cobra.MinimumNArgs(1),
PreRunE: util.ClientPreRunHooks,
PostRunE: util.ClientPostRunHooks,
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
Run: func(cmd *cobra.Command, cmdArgs []string) {
// Find the unknown arguments from the original args. We only want to find the
// flags that are unknown. We will only support the long form for custom
// job types as we will want to use them as keys in template completions.
unknownArgs := ExtractUnknownArgs(cmd.Flags(), os.Args[1:])

if err := exec(cmd, cmdArgs, unknownArgs, options); err != nil {
util.Fatal(cmd, err, 1)
}
},
}

execCmd.PersistentFlags().AddFlagSet(cliflags.SpecFlags(options.SpecSettings))
execCmd.PersistentFlags().AddFlagSet(cliflags.NewRunTimeSettingsFlags(options.RunTimeSettings))
execCmd.Flags().StringVar(&options.Code, "code", "", "Specifies the file, or directory of code to send with the request")

return execCmd
}

func exec(cmd *cobra.Command, cmdArgs []string, unknownArgs []string, options *ExecOptions) error {
job, err := PrepareJob(cmd, cmdArgs, unknownArgs, options)
if err != nil {
return err
}

job.Normalize()
err = job.ValidateSubmission()
if err != nil {
return fmt.Errorf("%s: %w", userstrings.JobSpecBad, err)
}

client := util.GetAPIClientV2(cmd.Context())
resp, err := client.Jobs().Put(&apimodels.PutJobRequest{
Job: job,
})
if err != nil {
return fmt.Errorf("failed request: %w", err)
}

if err := printer.PrintJobExecution(cmd.Context(), resp.JobID, cmd, options.RunTimeSettings, client); err != nil {
return fmt.Errorf("failed to print job execution: %w", err)
}

return nil
}

func PrepareJob(cmd *cobra.Command, cmdArgs []string, unknownArgs []string, options *ExecOptions) (*models.Job, error) {
var err error
var jobType, templateString string
var job *models.Job

// Determine the job type and lookup the template for that type. If we
// don't have a template, then we don't know how to submit that job type.
jobType = cmdArgs[0]

tpl, err := NewTemplateMap(embeddedFiles, "templates")
if err != nil {
return nil, fmt.Errorf("failed to find supported job types, templates missing")
}

// Get the template string, or if we can't find one for this type, then
// provide a list of ones we _do_ support.
if templateString, err = tpl.Get(jobType); err != nil {
knownTypes := tpl.AllTemplates()

supportedTypes := ""
if len(knownTypes) > 0 {
supportedTypes = "\nSupported types:\n"

for _, kt := range knownTypes {
supportedTypes = supportedTypes + fmt.Sprintf(" * %s\n", kt)
}
}

return nil, fmt.Errorf("the job type '%s' is not supported."+supportedTypes, jobType)
}

// Convert the unknown args to a map which we can use to fill in the template
replacements := flagsToMap(unknownArgs)

parser, err := template.NewParser(template.ParserParams{
Replacements: replacements,
})

if err != nil {
return nil, fmt.Errorf("failed to create %s job when parsing template: %+w", jobType, err)
}

tplResult, err := parser.ParseBytes([]byte(templateString))
if err != nil {
return nil, fmt.Errorf("%s: %w", userstrings.JobSpecBad, err)
}

// tplResult is now a []byte containing json for the job we will eventually submit.
if err = json.Unmarshal(tplResult, &job); err != nil {
return nil, fmt.Errorf("%s: %w", userstrings.JobSpecBad, err)
}

// Attach the command line arguments that were provided to exec. These are passed through
// to the template as Command/Arguments. e.g. `bacalhau exec python app.py` will set
// Command -> python, and Arguments -> ["app.py"]
job.Tasks[0].Engine.Params["Command"] = jobType
job.Tasks[0].Engine.Params["Arguments"] = cmdArgs[1:]

// Attach any inputs the user specified to the job spec
for _, ss := range options.SpecSettings.Inputs.Values() {
src, err := legacy.FromLegacyStorageSpecToInputSource(ss)
if err != nil {
return nil, fmt.Errorf("failed to process input %s: %w", ss.Name, err)
}

job.Tasks[0].InputSources = append(job.Tasks[0].InputSources, src)
}

// Process --code if anything was specified. In future we may want to try and determine this
// ourselves where it is not specified, but it will likely be dependendent on job type.
if options.Code != "" {
if err = addInlineContent(cmd.Context(), options.Code, job); err != nil {
return nil, err
}
}

return job, nil
}

// addInlineContent will use codeLocation to determine if it is a single file or a
// directory and will attach to the job as an inline attachment.
func addInlineContent(ctx context.Context, codeLocation string, job *models.Job) error {
absPath, err := filepath.Abs(codeLocation)
if err != nil {
return err
}

specConfig, err := inline.NewStorage().Upload(ctx, absPath)
if err != nil {
return fmt.Errorf("failed to attach code '%s' to job submission: %w", codeLocation, err)
}

job.Tasks[0].InputSources = append(job.Tasks[0].InputSources, &models.InputSource{
Source: &specConfig,
Alias: "code",
Target: "/code",
})

return nil
}
Loading

0 comments on commit 659ae62

Please sign in to comment.