Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Copy link
Collaborator

Choose a reason for hiding this comment

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

These files need to be removed and gitignored

Binary file not shown.
Binary file added bin/mist
Binary file not shown.
Binary file added bin/mist.exe
Binary file not shown.
13 changes: 13 additions & 0 deletions cli/cmd/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cmd

type AuthCmd struct {
Login LoginCmd `cmd:"" help:"Log in to your account"`
// Logout LogoutCmd `cmd:"" help:"Log out of your account"`
// Status AuthStatusCmd `cmd:"" help:"Check your authentication status" default:1`
}

func (a *AuthCmd) Run() error {
// Possible fallback if no subcommand is provided
// fmt.Println("(auth root) – try 'mist auth login|logout|status' or mist help")
return nil
}
46 changes: 46 additions & 0 deletions cli/cmd/auth_login.go
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will need to chat about how auth is implemented

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sounds good

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cmd

import (
"bufio"
"errors"
"fmt"
"os"
"strings"
)

// TODO: What credentials are we taking?
type LoginCmd struct {
}

func verifyUser(username, password string) error {
// Placeholder for actual authentication logic
if username == "admin" && password == "password" {
return nil
}
return errors.New("invalid credentials")
}

// TODO: Figure out how to handle password input without exposing it in the terminal historyn (go get golang.org/x/term)
// TODO: Where are we storing auth token? Are we getting JWT?

func (l *LoginCmd) Run() error {
// mist auth login

fmt.Print("Username: ")

reader := bufio.NewReader(os.Stdin)
username, _ := reader.ReadString('\n')
username = strings.TrimSpace(strings.ToLower(username))

fmt.Print("Password: ")
password, _ := reader.ReadString('\n')
password = strings.TrimSpace(strings.ToLower(password))
err := verifyUser(username, password)
if err != nil {
fmt.Println("Error during authentication:", err)
}

fmt.Println("Logging in with username:", username)

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

import "fmt"


// Config flags
type ConfigCmd struct{
DefaultCluster string `help:"Set the default compute cluster." optional: ""`
Show bool `help:"Show current configuration."`

}

func (h *ConfigCmd) Run() error {
// Some dummy config; Call API or something
defaultConfig := map[string]string{
"defaultCluster": "AMD-cluster-1",
"region": "us-east",
}

if h.Show && h.DefaultCluster!= "" {
fmt.Printf("Cannot use --show and --default-cluster together")
return nil
}

if h.DefaultCluster != "" {
// This is not actually set.
fmt.Printf("Setting default cluster to: %s\n", h.DefaultCluster)
return nil
}

if h.Show {
fmt.Println("Current configuration: ")
for key, value := range defaultConfig {
fmt.Printf(" %s: %s \n", key, value)
}
return nil
}

fmt.Println("No config action specified. Use --help for options.")

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

import (
"testing"
// "fmt"
// "bytes"
)

// No Flag config
func TestConfigNoFlags(t *testing.T){
cmd := &ConfigCmd{}
output := CaptureOutput(func(){
_ = cmd.Run()
})
if want := "No config action specified. Use --help for options."; !contains(output, want){
t.Errorf("expected output to contain %q, got %q", want, output)
}
}

// Set Default Cluster to tt-gpu-cluster-1
func TestConfigDefaultCluster(t *testing.T){
cmd := &ConfigCmd{DefaultCluster: "tt-gpu-cluster-1"} // Create config object

output := CaptureOutput(func(){
_ = cmd.Run()
})

// fmt.Printf("Captured the output: %s\n", output)

if want := "Setting default cluster to: tt-gpu-cluster-1"; !contains(output, want) {
t.Errorf("expected output to contain %q, got %q", want, output)
}
}

// Show Config
func TestConfigCmd_Show(t *testing.T){
cmd := &ConfigCmd{Show: true}
output := CaptureOutput(func(){
_ = cmd.Run()
})

if want := "Current configuration:"; !contains(output, want){
t.Errorf("expected output to contain %q, got %q", want, output)
}
}

// Show Error message if both flags are sent
func TestConfigBothFlagError(t *testing.T){
cmd := &ConfigCmd{DefaultCluster: "tt-gpu-cluster-1", Show: true}
output := CaptureOutput(func(){
_ = cmd.Run()
})

if want := "Cannot use --show and --default-cluster together"; !contains(output, want){
t.Errorf("Expected the error message of \"%s\", got %q", want, output)
}

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

import "fmt"

type HelpCmd struct{}

func (h *HelpCmd) Run() error {
fmt.Println("MIST CLI Help")
fmt.Println("Usage: mist [command] [options]")
fmt.Println("Commands:")
fmt.Println(" auth Authentication commands")
fmt.Println(" job Job management commands")
fmt.Println(" config Configuration commands")
fmt.Println(" help Show help information")
return nil
}
16 changes: 16 additions & 0 deletions cli/cmd/job.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cmd

type JobCmd struct {
Submit JobSubmitCmd `cmd:"" help:"Submit a new job"`
Cancel JobCancelCmd `cmd:"" help:"Cancel an existing job"`
// Delete JobDeleteCmd `cmd: "" help: "Delete an existing job"`
Status JobStatusCmd `cmd:"" help:"Check the status of a job"`
// Cancel CancelCmd `cmd:"" help:"Cancel a running job"`
List ListCmd `cmd:"" help:"List all jobs" default:1`
}

func (j *JobCmd) Run() error {
// Possible fallback if no subcommand is providFAre yed
// fmt.Println("(job root) – try 'mist job submit|status|list|cancel' or mist help")
return nil
}
74 changes: 74 additions & 0 deletions cli/cmd/job_cancel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package cmd

import (
"bufio"
"fmt"
"os"
"strings"
"time"
)


type JobCancelCmd struct {
ID string `arg:"" help:"ID of job you want to cancel"`
}

func (c *JobCancelCmd) Run() error {
// Same Mock data from job list.
jobs := []Job{
{
ID: "ID:1",
Name: "docker_container_name_1",
Status: "Running",
GPUType: "AMD",
CreatedAt: time.Now(),
},
{
ID: "ID:2",
Name: "docker_container_name_2",
Status: "Enqueued",
GPUType: "TT",
CreatedAt: time.Now().Add(-time.Hour * 24),
},
{
ID: "ID:3",
Name: "docker_container_name_3",
Status: "Running",
GPUType: "TT",
CreatedAt: time.Now().Add(-time.Hour * 24),
},
}

// Check if job exists
if !jobExists(jobs, c.ID) {
fmt.Printf("%s does not exist in your jobs.\n", c.ID)
fmt.Printf("Use the command \"job list\" for your list of jobs.")
return nil
}


fmt.Printf("Are you sure you want to cancel %s? (y/n): \n", c.ID)

reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(strings.ToLower(input))

if input == "y" || input == "yes"{
fmt.Println("Confirmed, proceeding job cancellation....")

// Confirmed job cancellation logic

fmt.Println("Cancelling job with ID:", c.ID)
fmt.Printf("Job cancelled successfully with ID: %s\n", c.ID)
return nil
} else if input == "n" || input == "no"{
fmt.Println("Cancelled.")
return nil
} else{
fmt.Println("Invalid response.")
return nil
}

return nil

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

import (
"testing"
"fmt"
)


// Added job, with no compute type added
func TestJobCancelJobDoesNotExist(t *testing.T){
// This job should not exist in the dummy
cmd := &JobCancelCmd{ID: "job_12345"}
output := CaptureOutput(func(){
_ = cmd.Run()
})
if want := "job_12345 does not exist in your jobs.\nUse the command \"job list\" for your list of jobs."; !contains(output, want){
t.Errorf("expected output to contain %q, got %q", want, output)
}
}

// Added job, with compute type
func TestJobCancelValid(t *testing.T){
// This job should not exist in the dummy
cmd := &JobCancelCmd{ID: "ID:1"}
output := CaptureOutput(func(){
_ = cmd.Run()
})
if want := "Are you sure you want to cancel ID:1? (y/n):"; !contains(output, want){
t.Errorf("expected output to contain %q, got %q", want, output)
}
}


func TestJobCancelProceed(t *testing.T){
cmd := &JobCancelCmd{ID: "ID:1"}
// Lowkey, we should refactor this into a
output := CaptureOutput(func(){
MockInput("y\n", func() {
_ = cmd.Run()
})
})
if !contains(output, "Confirmed, proceeding job cancellation...."){
t.Errorf("expected 'Confirmed, proceeding job cancellation....' but got:\n%s", output)
}
// fmt.Printf("Got the output %s\n", output)
}
67 changes: 67 additions & 0 deletions cli/cmd/job_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package cmd

import (
"fmt"
"os"
"text/tabwriter"
"time"
)

type ListCmd struct {
All bool `help:"List all jobs, including completed and failed ones." short:"a"`
}

type Job struct {
ID string
Name string
Status string
GPUType string
CreatedAt time.Time
}

func (l *ListCmd) Run() error {
// Mock data - pull from API in real implementation
jobs := []Job{
{
ID: "ID:1",
Name: "docker_container_name_1",
Status: "Running",
GPUType: "AMD",
CreatedAt: time.Now(),
},
{
ID: "ID:2",
Name: "docker_container_name_2",
Status: "Enqueued",
GPUType: "TT",
CreatedAt: time.Now().Add(-time.Hour * 24),
},
{
ID: "ID:3",
Name: "docker_container_name_3",
Status: "Running",
GPUType: "TT",
CreatedAt: time.Now().Add(-time.Hour * 24),
},
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "Job ID\tName\tStatus\tGPU Type\tCreated At")
fmt.Fprintln(w, "--------------------------------------------------------------")

for _, job := range jobs {
// Maybe filter based on running?
fmt.Fprintf(
w,
"%s\t%s\t%s\t%s\t%s\n",
job.ID,
job.Name,
job.Status,
job.GPUType,
job.CreatedAt.Format(time.RFC1123),
)
}

w.Flush()

return nil
}
Loading