mirror of
https://github.com/Xevion/vastly.git
synced 2025-12-05 23:16:48 -06:00
repo init
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.env
|
||||
13
.vscode/settings.json
vendored
Normal file
13
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"cSpell.words": ["quickvast", "vmem"],
|
||||
"cSpell.ignorePaths": [
|
||||
"package-lock.json",
|
||||
"node_modules",
|
||||
"vscode-extension",
|
||||
".git/{info,lfs,logs,refs,objects}/**",
|
||||
".git/{index,*refs,*HEAD}",
|
||||
".vscode",
|
||||
".vscode-insiders",
|
||||
"/home/linuxbrew/**"
|
||||
]
|
||||
}
|
||||
25
README.md
Normal file
25
README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# quickvast
|
||||
|
||||
CLI tools for managing Vast.ai instances.
|
||||
|
||||
What I want from this tool:
|
||||
|
||||
- [ ] Quickly choose machine types from a refined, customized list of options.
|
||||
- This would not just be a filter, it would be a customized, weighted scoring system allowing me to quickly choose the best machine for my needs.
|
||||
- It would provide warnings for machines about why they may not be the best choice, such as low RAM, low driver versions, etc.
|
||||
- It would have a nice, colorful UI allowing me to quickly see the best options.
|
||||
- At first this would be a single weighted config, one that could be hard-coded into the tool, but later I could add multiple configs via files perhaps
|
||||
- [ ] Quickly create a new instance.
|
||||
- Using the same feature above, quickly create an instance.
|
||||
- Being able to upload scripts and profiles into the machine easily.
|
||||
- Being able to choose and configure the 'template' of the machine.
|
||||
- [ ] Quickly manage instances.
|
||||
- Being able to quickly retrieve necessary connection information, open the browser for tooling, and copy authentication details.
|
||||
- Being able to quickly SSH into the machine.
|
||||
- [ ] Quickly destroy instances.
|
||||
- Being able to destroy instances quickly.
|
||||
- [ ] Long term monitoring for pricing
|
||||
- My concern is that I might leave an instance running for too long and rack up a huge bill.
|
||||
- I would like each instance to have a time limit for my usage, both a soft and hard limit.
|
||||
- The soft limit will send me a message on Discord, and the hard limit will destroy the instance after another message.
|
||||
-
|
||||
42
api/client.go
Normal file
42
api/client.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package api
|
||||
|
||||
import "net/http"
|
||||
|
||||
// Client handles API communication with the Vast.ai API
|
||||
type Client struct {
|
||||
apiKey string
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// Error represents an API error response
|
||||
type APIError struct {
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// NewClient creates a new Vast.ai API client
|
||||
func NewClient(apiKey string) *Client {
|
||||
return &Client{
|
||||
apiKey: apiKey,
|
||||
baseURL: "https://console.vast.ai/api/v0",
|
||||
httpClient: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
// Apply applies the Bearer token to the request
|
||||
func (c *Client) Apply(req *http.Request) {
|
||||
req.Header.Set("Authorization", "Bearer "+c.apiKey)
|
||||
}
|
||||
|
||||
// makeRequest creates and sends an HTTP request with the provided method, path and body
|
||||
func (c *Client) makeRequest(method, path string, body interface{}) (*http.Response, error) {
|
||||
req, err := http.NewRequest(method, c.baseURL+path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Apply(req)
|
||||
return c.httpClient.Do(req)
|
||||
}
|
||||
3
api/go.mod
Normal file
3
api/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module xevion.dev/quickvast/api
|
||||
|
||||
go 1.23.3
|
||||
52
api/instances.go
Normal file
52
api/instances.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// InstanceGetResponse represents the response from GET /instances
|
||||
type InstanceGetResponse struct {
|
||||
Instances []Instance `json:"instances"`
|
||||
}
|
||||
|
||||
// GetInstances retrieves all instances for the authenticated user
|
||||
func (c *Client) GetInstances() (*InstanceGetResponse, error) {
|
||||
resp, err := c.makeRequest(http.MethodGet, "/instances", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result InstanceGetResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// DeleteInstance removes an instance by ID
|
||||
func (c *Client) DeleteInstance(id int) error {
|
||||
path := fmt.Sprintf("/instances/%d", id)
|
||||
resp, err := c.makeRequest(http.MethodDelete, path, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutInstance updates an instance's status
|
||||
func (c *Client) PutInstance(id int, status string) error {
|
||||
path := fmt.Sprintf("/instances/%d", id)
|
||||
data := map[string]string{"status": status}
|
||||
|
||||
resp, err := c.makeRequest(http.MethodPut, path, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
99
api/types.go
Normal file
99
api/types.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package api
|
||||
|
||||
type PortMapping struct {
|
||||
HostIp string `json:"HostIp"`
|
||||
HostPort string `json:"HostPort"`
|
||||
}
|
||||
|
||||
type Ports struct {
|
||||
TCP22 []PortMapping `json:"22/tcp"`
|
||||
TCP8080 []PortMapping `json:"8080/tcp"`
|
||||
UDP8080 []PortMapping `json:"8080/udp"`
|
||||
}
|
||||
|
||||
type Instance struct {
|
||||
IsBid bool `json:"is_bid"`
|
||||
InetUpBilled float64 `json:"inet_up_billed"`
|
||||
InetDownBilled float64 `json:"inet_down_billed"`
|
||||
External bool `json:"external"`
|
||||
Webpage *string `json:"webpage"`
|
||||
Logo string `json:"logo"`
|
||||
Rentable bool `json:"rentable"`
|
||||
ComputeCap int `json:"compute_cap"`
|
||||
DriverVersion string `json:"driver_version"`
|
||||
CudaMaxGood int `json:"cuda_max_good"`
|
||||
MachineID int `json:"machine_id"`
|
||||
HostingType *string `json:"hosting_type"`
|
||||
PublicIPAddr string `json:"public_ipaddr"`
|
||||
Geolocation string `json:"geolocation"`
|
||||
FlopsPerDPHTotal float64 `json:"flops_per_dphtotal"`
|
||||
DLPerfPerDPHTotal float64 `json:"dlperf_per_dphtotal"`
|
||||
Reliability2 float64 `json:"reliability2"`
|
||||
HostRunTime int64 `json:"host_run_time"`
|
||||
HostID int `json:"host_id"`
|
||||
ID int `json:"id"`
|
||||
BundleID int `json:"bundle_id"`
|
||||
NumGPUs int `json:"num_gpus"`
|
||||
TotalFlops float64 `json:"total_flops"`
|
||||
MinBid float64 `json:"min_bid"`
|
||||
DPHBase float64 `json:"dph_base"`
|
||||
DPHTotal float64 `json:"dph_total"`
|
||||
GPUName string `json:"gpu_name"`
|
||||
GPURam int `json:"gpu_ram"`
|
||||
GPUDisplayActive bool `json:"gpu_display_active"`
|
||||
GPUMemBW float64 `json:"gpu_mem_bw"`
|
||||
BWNVLink int `json:"bw_nvlink"`
|
||||
DirectPortCount int `json:"direct_port_count"`
|
||||
GPULanes int `json:"gpu_lanes"`
|
||||
PCIeBW float64 `json:"pcie_bw"`
|
||||
PCIGen int `json:"pci_gen"`
|
||||
DLPerf float64 `json:"dlperf"`
|
||||
CPUName string `json:"cpu_name"`
|
||||
MoboName string `json:"mobo_name"`
|
||||
CPURam int `json:"cpu_ram"`
|
||||
CPUCores int `json:"cpu_cores"`
|
||||
CPUCoresEffective float64 `json:"cpu_cores_effective"`
|
||||
GPUFrac float64 `json:"gpu_frac"`
|
||||
HasAVX int `json:"has_avx"`
|
||||
DiskSpace float64 `json:"disk_space"`
|
||||
DiskName string `json:"disk_name"`
|
||||
DiskBW float64 `json:"disk_bw"`
|
||||
InetUp float64 `json:"inet_up"`
|
||||
InetDown float64 `json:"inet_down"`
|
||||
StartDate float64 `json:"start_date"`
|
||||
EndDate int64 `json:"end_date"`
|
||||
Duration float64 `json:"duration"`
|
||||
StorageCost float64 `json:"storage_cost"`
|
||||
InetUpCost float64 `json:"inet_up_cost"`
|
||||
InetDownCost float64 `json:"inet_down_cost"`
|
||||
StorageTotalCost float64 `json:"storage_total_cost"`
|
||||
Verification string `json:"verification"`
|
||||
Score float64 `json:"score"`
|
||||
SSHIdx string `json:"ssh_idx"`
|
||||
SSHHost string `json:"ssh_host"`
|
||||
SSHPort int `json:"ssh_port"`
|
||||
ActualStatus string `json:"actual_status"`
|
||||
IntendedStatus string `json:"intended_status"`
|
||||
CurState string `json:"cur_state"`
|
||||
NextState string `json:"next_state"`
|
||||
ImageUUID string `json:"image_uuid"`
|
||||
ImageArgs []string `json:"image_args"`
|
||||
ImageRuntype string `json:"image_runtype"`
|
||||
ExtraEnv string `json:"extra_env"`
|
||||
OnStart string `json:"onstart"`
|
||||
Label *string `json:"label"`
|
||||
JupyterToken string `json:"jupyter_token"`
|
||||
StatusMsg string `json:"status_msg"`
|
||||
GPUUtil float64 `json:"gpu_util"`
|
||||
DiskUtil float64 `json:"disk_util"`
|
||||
GPUTemp float64 `json:"gpu_temp"`
|
||||
LocalIPAddrs string `json:"local_ipaddrs"`
|
||||
DirectPortEnd int `json:"direct_port_end"`
|
||||
DirectPortStart int `json:"direct_port_start"`
|
||||
CPUUtil float64 `json:"cpu_util"`
|
||||
MemUsage float64 `json:"mem_usage"`
|
||||
MemLimit float64 `json:"mem_limit"`
|
||||
VMemUsage float64 `json:"vmem_usage"`
|
||||
MachineDirSSHPort int `json:"machine_dir_ssh_port"`
|
||||
Ports Ports `json:"ports"`
|
||||
}
|
||||
43
cmd/quickvast/main.go
Normal file
43
cmd/quickvast/main.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"xevion.dev/quickvast/api"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load .env file
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
||||
// Get API key from environment
|
||||
apiKey := os.Getenv("VASTAI_API_KEY")
|
||||
if apiKey == "" {
|
||||
log.Fatal("VASTAI_API_KEY not found in environment")
|
||||
}
|
||||
|
||||
// Create client
|
||||
client := api.NewClient(apiKey)
|
||||
|
||||
// Get instances
|
||||
resp, err := client.GetInstances()
|
||||
if err != nil {
|
||||
log.Fatalf("Error getting instances: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Instances) == 0 {
|
||||
fmt.Println("No instances found")
|
||||
return
|
||||
}
|
||||
|
||||
// Print instances
|
||||
for _, instance := range resp.Instances {
|
||||
fmt.Printf("Instance %d: %+v\n", instance.ID, instance)
|
||||
}
|
||||
}
|
||||
9
go.mod
Normal file
9
go.mod
Normal file
@@ -0,0 +1,9 @@
|
||||
module quickvast
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require (
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
)
|
||||
6
go.sum
Normal file
6
go.sum
Normal file
@@ -0,0 +1,6 @@
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
6
go.work.sum
Normal file
6
go.work.sum
Normal file
@@ -0,0 +1,6 @@
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
Reference in New Issue
Block a user