Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: migrate docker & wasm run to v2 api #3970

Closed
wants to merge 6 commits into from

Conversation

frrist
Copy link
Member

@frrist frrist commented May 2, 2024

This change removes the pkg/model dependency from the docker run and wasm run commands and migrates both of them to the new API methods. The flags on each command remain unchanged.

makes progress towards #3831 and #3832

TODO:

  • fix some todo's I'll call out in review

@frrist frrist marked this pull request as ready for review May 2, 2024 01:04
Copy link

coderabbitai bot commented May 2, 2024

Important

Review Skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Member Author

@frrist frrist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left some comments to guide review

cmd/cli/docker/docker_run.go Outdated Show resolved Hide resolved
labels, err := parse.Labels(ctx, opts.SpecSettings.Labels)
if err != nil {
return nil, err
// TODO(forrest) [refactor]: this logic is duplicated in wasm_run
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should extract the common logic here before merging.

),
)
task, err := models.NewTaskBuilder().
Name("TODO").
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently don't have a means for users to specify a task name with the current flags on wasm run or docker run I could stub in a UUID here and add a --task-name flag later, thoughts?

Copy link
Collaborator

@wdbaruni wdbaruni May 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use "main" as the default task name. Task names are only useful when we support multiple tasks per job, and that won't be doable through docker run and wasm run as they will most likely submit a single task per job. So calling it "main" or any other standard name is acceptable

actually this is what we are currently doing

Comment on lines 247 to 248
Name: "TODO",
Namespace: "TODO",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar point to task name in my comment above - these could be UUIDs, values from flags we add, or something else?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a default namespace for bacalhau job run

DefaultNamespace = "default"

though we are still using clientID for docker run. I think we should use the default namespace, but expose a flag to set the desired namespace. This means users won't be able to filter their own jobs in the public network, but that might be acceptable based on our longer term plans. Thoughts?

Comment on lines 249 to 250
Type: models.JobTypeBatch,
Priority: 0,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it is assumed that docker and wasm jobs are batch when run over the CLI, but we could easily change that. Additionally priority doesn't have a flag. Gut says we need flags for these - ideas on good default values appreciated.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a --target flag that controls this:

      --target all|any                          Whether to target the minimum number of matching nodes ("any") (default) or all matching nodes ("all") (default any)

any is a batch job, where all is an ops job

Copy link
Member Author

@frrist frrist May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comment here was with respect to the priority fields. Are we okay adding a flag called --priority that sets the field?

With respect to the job type, currently controlled via --target:
Would we be willing to drop the target flag in favor of a --type flag whose default value is batch and other possible values are service, ops, and daemon? This would bring the CLI more inline with the fields of the job spec.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets do the migration one step at a time. Lets avoid exposing or renaming flags as this PR's main focus should be internal and to just switch to new APIs.

To answer your questions though, --priority is not fully implemented as the compute nodes for example don't respect priority when queueing executions locally in their buffer. Will make sense to expose priority, blog about it and make sure it is documented when it is fully implemented. We should use whatever default value bacalhau job run has.

As for --target, we don't and should not support service or daemon jobs using imperative job submissions (i.e. docker run and wasm run). We should allow users to easily update their long running jobs, compare what is deployed in the network and what their local job spec says, and utilize gitops to deploy and update jobs based on committed specs. All that is not feasible with imperative job submissions, and we shouldn't make it easy for users to do bad things to their network.

Comment on lines 65 to 66
`name:path of the output data volumes. `+
`'outputs:/outputs' is always added unless '/outputs' is mapped to a different name.`,
`name=path of the output data volumes. `+
`'outputs=/outputs' is always added unless '/outputs' is mapped to a different name.`,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is true anymore. Will need to address this before merging.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why changed from name:path to name=path? this will break users. Also we will need to define a default output path outputs:/outputs for both docker run and wasm run to avoid breaking existing behavour and breaking all our examples and docs. I don't think it is a bad design anyways for those commands, but worth exploring the right long term option with @aronchick

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context here @aronchick: This comment is about a change in behavior of the output flag from expecting : as a separator to = as a separator.

When using a flag that requires pairs of strings (key,value) - for example environment variables - and in the case here ResultPaths I find a StringToStringVar flag type (a map) to provide clearer validation compared to accepting a StringArrayVar flag (a slice). As an example, consider:

Valid:
bacalhau docker run -o outputs=/outputs -o inputs=/inputs

Invalid:
bacalhau docker run -o outputs=/outputs -o inputs

Cobra, our CLI library, will immediately catch the invalid case and return an error since inputs is missing a value field.

Comparatively, the behavior prior to this change accepted both options, and it was on us (developers) to write the validation logic to catch the invalid case of a key missing a value.

If we feel this is too large of a change for our users to understand - replacing : with = - then I can write a bespoke implementation of a map flag parser that uses : as the separator instead of = to preserve existing behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please stop changing behaviour or adding more complexity to the migration story? The migration of the APIs and CLIs is a huge and complex story as is, and we've discussed that no change in behaviour to be done while we migrate to avoid as many issues as possible.

While I appreciate you identifying areas of improvements as you work on this task, the right way to handle this is by cutting issues to track them.

Comment on lines +73 to +74
// TODO(forrest) [correctness]: need to allow aliases to be provided over CLI
alias := "TODO"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to address, not sure if the alias should be passed by the client or picked by the CLI.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly, alias is the replacement for name. How did we set names before?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was ported from the model package. We will be deleting the model package at which point the duplicated code will be remove.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also ported from model.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also ported from model.

@frrist frrist requested a review from wdbaruni May 2, 2024 01:21
Comment on lines 138 to +147
if opts.RunTimeSettings.DryRun {
// Converting job to yaml
var yamlBytes []byte
yamlBytes, err = yaml.Marshal(j)
out, err := helpers.JobToYaml(job)
if err != nil {
return fmt.Errorf("converting job to yaml: %w", err)
return err
}
cmd.Print(string(yamlBytes))
cmd.Print(out)
return nil
}

executingJob, err := util.ExecuteJob(ctx, j, opts.RunTimeSettings)
api := util.GetAPIClientV2(cmd)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we do things in a way where docker run and wasm run only generate job spec from flags, and eventually call job run or the same method that job run calls? I assume --dry-run, --follow, --id-only and similar flags apply to all and can be handled by job run

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I can unify the method calls across the docker, wasm, exec, and run commands in this PR. Great way to de-dupe some of this code!

Comment on lines +170 to +175
engineSpec, err := models.DockerSpecBuilder(image).
WithParameters(parameters...).
WithWorkingDirectory(opts.WorkingDirectory).
WithEntrypoint(opts.Entrypoint...).
WithEnvironmentVariables(opts.EnvironmentVariables...).
Build()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we started to define the specs related to each publisher, source and executor under their package instead of models. This helps with initializing new types, validation and serde from and to models.SpecConfig. There is already a defined type under pkg/executor/docker/models/types.go:22 that should be used

There is also a builder in that package, but I don't think it is correct/useful as it is building a models.SpecConfig directly instead of EngineSpec

Also feel free to move EngineSpec from executor/docker/models to executor/docker

Comment on lines +194 to +196
// Function for validating the workdir of a docker command.
func validateWorkingDir(jobWorkingDir string) error {
if jobWorkingDir != "" {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add these validations under EngineSpec.Validate() method

Comment on lines +16 to +18
// users are not permitted to set, like ID, Version, ModifyTime, State, etc.
// The solution here is to have a "JobSubmission" type that is different from the actual job spec.
func JobToYaml(job *models.Job) (string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hear your point. My preference is to delay any decision to change our API specs and have different ways to submit jobs until we collect more feedback about current APIs, and have more dedicated time to explore alternatives and align on the right option. Options might include splitting the job type into spec and state top level fields, where state hold the system defined fields, and spec is what the user submits. Again, I truly don't recommend figuring this out just yet.

A low hanging fruit is to introduce SanitizeForSubmission() method under models.Job that will clear out all fields that are reserved by the system so that something like bacalhau job describe <job_id> --output yaml | bacalhau job run would work

Comment on lines +73 to +74
// TODO(forrest) [correctness]: need to allow aliases to be provided over CLI
alias := "TODO"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly, alias is the replacement for name. How did we set names before?

Comment on lines +28 to +29
func DecodeSpecConfig[T any](spec *SpecConfig) (*T, error) {
params, err := json.Marshal(spec.Params)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how useful is this method? today we have each type implements its own way to decode SpecConfig to the right type. e.g. pkg/s3/types.go:92

Comment on lines +12 to +15
//nolint:gocyclo
func StorageStringToSpecConfig(sourceURI, destinationPath, alias string, options map[string]string) (*InputSource, error) {
sourceURI = strings.Trim(sourceURI, " '\"")
destinationPath = strings.Trim(destinationPath, " '\"")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as to the publisher parser

Comment on lines +11 to +13
// WasmEngineSpec contains necessary parameters to execute a wasm job.
type WasmEngineSpec struct {
// EntryModule is a Spec containing the WASM code to start running.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar comment as in the docker engine spec

Comment on lines +11 to +13
// WasmEngineSpec contains necessary parameters to execute a wasm job.
type WasmEngineSpec struct {
// EntryModule is a Spec containing the WASM code to start running.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar comment as in the docker engine spec

Comment on lines +17 to +22

// TaskNamePrefix is the prefix of a system generated task name.
TaskNamePrefix = "t-name-"

// JobNamePrefix is the prefix of a system generated job name.
JobNamePrefix = "j-name-"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this? Names are not system generated and our expectation is for the user to provide them as a reference to their jobs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, me either, I just needed something to generate a name if one wasn't provided. What is the expected behavior if the user doesn't provide a name? The bacalhau job run command will fail, should docker run and wasm run do the same? Note, this would be a change in their existing behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right about job name. This PR should help #4005
For task, use main

@frrist
Copy link
Member Author

frrist commented May 22, 2024

Scope of this change is larger than expected and there are some breaking changes in the new job spec. Descoping this to a smaller change via #4020.

@frrist frrist closed this May 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

3 participants