mirror of
https://github.com/Xevion/vastly.git
synced 2025-12-06 03:16:59 -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