-
Notifications
You must be signed in to change notification settings - Fork 4
[draft] DO NOT MERGE: rayapp test prototype #372
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
Open
elliot-barn
wants to merge
15
commits into
main
Choose a base branch
from
elliot-barn/rayapp-anyscale-cli
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
9c9a162
Add isAnyscaleInstalled check and tests for anyscale CLI
elliot-barn 1729794
removing anyscale-cli rayapp command
elliot-barn 7e70483
creating test and anyscale cli scaffolding
elliot-barn 78b1840
updating anyscale cli
elliot-barn 87f5ddb
fixing comment
elliot-barn 17f39f0
adding missing template check
elliot-barn aca755f
adding comprehensive tests for anyscale cli functions
elliot-barn eb85bac
adding unit tests for workspace test runner
elliot-barn e7c49e7
Add waitForWorkspaceState and use it in test runner
elliot-barn 291723e
Update build_rayapp.sh to execute rayapp directly
elliot-barn f5c9cf0
Add parseComputeConfigName to derive compute config from template
elliot-barn 7370b08
Add compute config existence check before creation
elliot-barn 9f5de30
Zip template directory before pushing to workspace
elliot-barn 5ba726f
Use temp directory for template zip before pushing to workspace
elliot-barn b4bf53a
Add automatic conversion of old compute config format to new format
elliot-barn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
The diff you're trying to view is too large. We only load the first 3000 changed files.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| package rayapp | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "errors" | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
| "os/exec" | ||
| "strings" | ||
| ) | ||
|
|
||
| type WorkspaceState int | ||
|
|
||
| const ( | ||
| StateTerminated WorkspaceState = iota | ||
| StateStarting | ||
| StateRunning | ||
| ) | ||
|
|
||
| var WorkspaceStateName = map[WorkspaceState]string{ | ||
| StateTerminated: "TERMINATED", | ||
| StateStarting: "STARTING", | ||
| StateRunning: "RUNNING", | ||
| } | ||
|
|
||
| func (ws WorkspaceState) String() string { | ||
| return WorkspaceStateName[ws] | ||
| } | ||
|
|
||
| type AnyscaleCLI struct { | ||
| token string | ||
| } | ||
|
|
||
| var errAnyscaleNotInstalled = errors.New("anyscale is not installed") | ||
|
|
||
| func NewAnyscaleCLI(token string) *AnyscaleCLI { | ||
| return &AnyscaleCLI{token: token} | ||
| } | ||
|
|
||
| func isAnyscaleInstalled() bool { | ||
| _, err := exec.LookPath("anyscale") | ||
| return err == nil | ||
| } | ||
|
|
||
| func (ac *AnyscaleCLI) Authenticate() error { | ||
| cmd := exec.Command("anyscale", "login") | ||
| err := cmd.Run() | ||
| if err != nil { | ||
| return fmt.Errorf("anyscale auth login failed, please set ANYSCALE_CLI_TOKEN & ANYSCALE_HOST env variables: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // RunAnyscaleCLI runs the anyscale CLI with the given arguments. | ||
| // Returns the combined output and any error that occurred. | ||
| // Output is displayed to the terminal with colors preserved. | ||
| func (ac *AnyscaleCLI) runAnyscaleCLI(args []string) (string, error) { | ||
| if !isAnyscaleInstalled() { | ||
| return "", errAnyscaleNotInstalled | ||
| } | ||
|
|
||
| fmt.Println("anyscale cli args: ", args) | ||
| cmd := exec.Command("anyscale", args...) | ||
|
|
||
| // Capture output while also displaying to terminal with colors | ||
| var outputBuf bytes.Buffer | ||
| cmd.Stdout = io.MultiWriter(os.Stdout, &outputBuf) | ||
| cmd.Stderr = io.MultiWriter(os.Stderr, &outputBuf) | ||
|
|
||
| if err := cmd.Run(); err != nil { | ||
| return outputBuf.String(), fmt.Errorf("anyscale error: %w", err) | ||
| } | ||
|
|
||
| return outputBuf.String(), nil | ||
| } | ||
|
|
||
| func (ac *AnyscaleCLI) createEmptyWorkspace(config *WorkspaceTestConfig) error { | ||
| args := []string{"workspace_v2", "create"} | ||
| // get image URI and ray version from build ID | ||
| imageURI, rayVersion, err := convertBuildIdToImageURI(config.template.ClusterEnv.BuildID) | ||
| if err != nil { | ||
| return fmt.Errorf("convert build ID to image URI failed: %w", err) | ||
| } | ||
| args = append(args, "--name", config.workspaceName) | ||
| args = append(args, "--image-uri", imageURI) | ||
| args = append(args, "--ray-version", rayVersion) | ||
| if config.computeConfig != "" { | ||
| args = append(args, "--compute-config", "tmpl-test-basic-serverless-aws:1") | ||
| } | ||
| output, err := ac.runAnyscaleCLI(args) | ||
| if err != nil { | ||
| return fmt.Errorf("create empty workspace failed: %w", err) | ||
| } | ||
| fmt.Println("create empty workspace output:\n", output) | ||
| return nil | ||
| } | ||
|
|
||
| func (ac *AnyscaleCLI) terminateWorkspace(workspaceName string) error { | ||
| output, err := ac.runAnyscaleCLI([]string{"workspace_v2", "terminate", "--name", workspaceName}) | ||
| if err != nil { | ||
| return fmt.Errorf("delete workspace failed: %w", err) | ||
| } | ||
| fmt.Println("terminate workspace output:\n", output) | ||
| return nil | ||
| } | ||
|
|
||
| func (ac *AnyscaleCLI) copyTemplateToWorkspace(config *WorkspaceTestConfig) error { | ||
| output, err := ac.runAnyscaleCLI([]string{"workspace_v2", "push", "--name", config.workspaceName, "--local-dir", config.template.Dir}) | ||
| if err != nil { | ||
| return fmt.Errorf("copy template to workspace failed: %w", err) | ||
| } | ||
| fmt.Println("copy template to workspace output:\n", output) | ||
| return nil | ||
| } | ||
|
|
||
| func (ac *AnyscaleCLI) runCmdInWorkspace(config *WorkspaceTestConfig, cmd string) error { | ||
| output, err := ac.runAnyscaleCLI([]string{"workspace_v2", "run_command", "--name", config.workspaceName, cmd}) | ||
| if err != nil { | ||
| return fmt.Errorf("run command in workspace failed: %w", err) | ||
| } | ||
| fmt.Println("run command in workspace output:\n", output) | ||
| return nil | ||
| } | ||
|
|
||
| func (ac *AnyscaleCLI) startWorkspace(config *WorkspaceTestConfig) error { | ||
| output, err := ac.runAnyscaleCLI([]string{"workspace_v2", "start", "--name", config.workspaceName}) | ||
| if err != nil { | ||
| return fmt.Errorf("start workspace failed: %w", err) | ||
| } | ||
| fmt.Println("start workspace output:\n", output) | ||
| return nil | ||
| } | ||
|
|
||
| func (ac *AnyscaleCLI) getWorkspaceStatus(workspaceName string) (string, error) { | ||
| output, err := ac.runAnyscaleCLI([]string{"workspace_v2", "status", "--name", workspaceName}) | ||
| if err != nil { | ||
| return "", fmt.Errorf("get workspace state failed: %w", err) | ||
| } | ||
| return output, nil | ||
| } | ||
|
|
||
| func convertBuildIdToImageURI(buildId string) (string, string, error) { | ||
| // Convert build ID like "anyscaleray2441-py312-cu128" to "anyscale/ray:2.44.1-py312-cu128" | ||
| const prefix = "anyscaleray" | ||
| if !strings.HasPrefix(buildId, prefix) { | ||
| return "", "", fmt.Errorf("build ID must start with %q: %s", prefix, buildId) | ||
| } | ||
|
|
||
| // Remove the prefix to get "2441-py312-cu128" | ||
| remainder := strings.TrimPrefix(buildId, prefix) | ||
|
|
||
| // Find the first hyphen to separate version from suffix | ||
| hyphenIdx := strings.Index(remainder, "-") | ||
| var versionStr, suffix string | ||
| if hyphenIdx == -1 { | ||
| versionStr = remainder | ||
| suffix = "" | ||
| } else { | ||
| versionStr = remainder[:hyphenIdx] | ||
| suffix = remainder[hyphenIdx:] // includes the hyphen | ||
| } | ||
|
|
||
| // Parse version: "2441" -> "2.44.1" | ||
| // Format: first digit = major, next two = minor, rest = patch | ||
| if len(versionStr) < 4 { | ||
| return "", "", fmt.Errorf("version string too short: %s", versionStr) | ||
| } | ||
|
|
||
| major := versionStr[0:1] | ||
| minor := versionStr[1:3] | ||
| patch := versionStr[3:] | ||
|
|
||
| return fmt.Sprintf("anyscale/ray:%s.%s.%s%s", major, minor, patch, suffix), fmt.Sprintf("%s.%s.%s", major, minor, patch), nil | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.