mirror of
https://github.com/hanzoai/compute.git
synced 2026-10-30 14:50:41 +00:00
198 lines
7.1 KiB
Go
198 lines
7.1 KiB
Go
// Copyright 2026 Hanzo Industries Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package service
|
|
|
|
// catalog.go is what hosted compute sells: every instance type, its shape, the
|
|
// root volume it launches with and what it costs Hanzo. It is the one table the
|
|
// catalog routes, the launch quote, the launch gate and the hourly meter all read,
|
|
// so a size is priced one way everywhere. A type that is not in it cannot be
|
|
// launched and cannot be billed.
|
|
|
|
// GPUSpec is the accelerator detail of a GPU size.
|
|
type GPUSpec struct {
|
|
Count int `json:"count"`
|
|
Model string `json:"model"`
|
|
Vram int `json:"vram"`
|
|
VramUnit string `json:"vramUnit"`
|
|
}
|
|
|
|
// SizeInfo is one size as a caller sees it. Only Hanzo's price is exposed; what
|
|
// it costs Hanzo stays in the catalog.
|
|
type SizeInfo struct {
|
|
Slug string `json:"slug"`
|
|
Vcpus int `json:"vcpus"`
|
|
MemoryMB int `json:"memoryMb"`
|
|
DiskGB int `json:"diskGb"`
|
|
Available bool `json:"available"`
|
|
Regions []string `json:"regions"`
|
|
GPU *GPUSpec `json:"gpu,omitempty"`
|
|
Currency string `json:"currency"`
|
|
// CentsHourly is the price per running hour in whole cents, exactly what the
|
|
// launch and the hourly meter debit.
|
|
CentsHourly int64 `json:"centsHourly"`
|
|
// CentsStopped is the price per stopped hour in whole cents: the root volume,
|
|
// which the hourly meter debits while the machine is stopped.
|
|
CentsStopped int64 `json:"centsStopped"`
|
|
// PriceHourly and PriceMonthly are CentsHourly in dollars, per hour and per
|
|
// 730-hour month.
|
|
PriceHourly float64 `json:"priceHourly"`
|
|
PriceMonthly float64 `json:"priceMonthly"`
|
|
}
|
|
|
|
// RegionInfo is one region a machine can launch in.
|
|
type RegionInfo struct {
|
|
Slug string `json:"slug"`
|
|
Name string `json:"name"`
|
|
Available bool `json:"available"`
|
|
Features []string `json:"features"`
|
|
Sizes []string `json:"sizes"`
|
|
}
|
|
|
|
// offer is one instance type for sale.
|
|
type offer struct {
|
|
slug string // the EC2 instance type
|
|
vcpus int
|
|
memoryMB int
|
|
gpu *GPUSpec
|
|
// diskGB is the gp3 root volume every launch of this size gets, and it is
|
|
// priced in.
|
|
diskGB int64
|
|
// listMicros is AWS's on-demand Linux list price for the type in us-east-1,
|
|
// in micro-dollars per hour.
|
|
listMicros int64
|
|
// burstable marks a T-family type, launched with standard CPU credits so it
|
|
// cannot accrue surplus-credit charges the price does not cover.
|
|
burstable bool
|
|
}
|
|
|
|
// cents is the offer's price per running hour.
|
|
func (o offer) cents() int64 { return hourlyCents(o.listMicros, o.diskGB) }
|
|
|
|
func gpu(count int, model string, vramGiB int) *GPUSpec {
|
|
return &GPUSpec{Count: count, Model: model, Vram: vramGiB, VramUnit: "GiB"}
|
|
}
|
|
|
|
// offers is the catalog. List prices are AWS's published us-east-1 on-demand
|
|
// Linux rates (AWS price list of 2026-09-21); memory is in MiB.
|
|
var offers = []offer{
|
|
{slug: "t3.medium", vcpus: 2, memoryMB: 4 << 10, diskGB: 50, listMicros: 41_600, burstable: true},
|
|
{slug: "m7i.large", vcpus: 2, memoryMB: 8 << 10, diskGB: 50, listMicros: 100_800},
|
|
{slug: "m7i.xlarge", vcpus: 4, memoryMB: 16 << 10, diskGB: 50, listMicros: 201_600},
|
|
{slug: "m7i.2xlarge", vcpus: 8, memoryMB: 32 << 10, diskGB: 50, listMicros: 403_200},
|
|
{slug: "c7i.xlarge", vcpus: 4, memoryMB: 8 << 10, diskGB: 50, listMicros: 178_500},
|
|
{slug: "r7i.xlarge", vcpus: 4, memoryMB: 32 << 10, diskGB: 50, listMicros: 264_600},
|
|
|
|
{slug: "g6.xlarge", vcpus: 4, memoryMB: 16 << 10, gpu: gpu(1, "L4", 24), diskGB: 200, listMicros: 804_800},
|
|
{slug: "g6.12xlarge", vcpus: 48, memoryMB: 192 << 10, gpu: gpu(4, "L4", 24), diskGB: 500, listMicros: 4_601_600},
|
|
{slug: "g5.xlarge", vcpus: 4, memoryMB: 16 << 10, gpu: gpu(1, "A10G", 24), diskGB: 200, listMicros: 1_006_000},
|
|
{slug: "g5.12xlarge", vcpus: 48, memoryMB: 192 << 10, gpu: gpu(4, "A10G", 24), diskGB: 500, listMicros: 5_672_000},
|
|
{slug: "g5.48xlarge", vcpus: 192, memoryMB: 768 << 10, gpu: gpu(8, "A10G", 24), diskGB: 1000, listMicros: 16_288_000},
|
|
{slug: "g6e.xlarge", vcpus: 4, memoryMB: 32 << 10, gpu: gpu(1, "L40S", 48), diskGB: 200, listMicros: 1_861_000},
|
|
{slug: "p4d.24xlarge", vcpus: 96, memoryMB: 1152 << 10, gpu: gpu(8, "A100", 40), diskGB: 1000, listMicros: 21_957_642},
|
|
{slug: "p5.48xlarge", vcpus: 192, memoryMB: 2048 << 10, gpu: gpu(8, "H100", 80), diskGB: 1000, listMicros: 55_040_000},
|
|
}
|
|
|
|
// DefaultLaunchSize is the size a launch gets when the caller names none — the
|
|
// tabs "New cloud machine" button, a bare CLI launch. A shell and `hanzo link`
|
|
// run comfortably on it.
|
|
const DefaultLaunchSize = "t3.medium"
|
|
|
|
// offerFor returns the catalog entry for an instance type.
|
|
func offerFor(slug string) (offer, bool) {
|
|
for _, o := range offers {
|
|
if o.slug == slug {
|
|
return o, true
|
|
}
|
|
}
|
|
return offer{}, false
|
|
}
|
|
|
|
// info is the offer as a caller sees it in region; an empty region means the
|
|
// hosted account is not configured, so nothing is available.
|
|
func (o offer) info(region string) SizeInfo {
|
|
cents := o.cents()
|
|
si := SizeInfo{
|
|
Slug: o.slug,
|
|
Vcpus: o.vcpus,
|
|
MemoryMB: o.memoryMB,
|
|
DiskGB: int(o.diskGB),
|
|
Available: region != "",
|
|
Regions: []string{},
|
|
Currency: "USD",
|
|
CentsHourly: cents,
|
|
CentsStopped: stoppedCents(o.diskGB),
|
|
PriceHourly: float64(cents) / 100,
|
|
PriceMonthly: float64(cents*hoursPerMonth) / 100,
|
|
}
|
|
if region != "" {
|
|
si.Regions = []string{region}
|
|
}
|
|
if o.gpu != nil {
|
|
g := *o.gpu
|
|
si.GPU = &g
|
|
}
|
|
return si
|
|
}
|
|
|
|
// ListSizes returns every size for sale.
|
|
func ListSizes() []SizeInfo {
|
|
region := hostedConfig().Region
|
|
out := make([]SizeInfo, 0, len(offers))
|
|
for _, o := range offers {
|
|
out = append(out, o.info(region))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ListGPUSizes returns the sizes that carry accelerators.
|
|
func ListGPUSizes() []SizeInfo {
|
|
region := hostedConfig().Region
|
|
out := make([]SizeInfo, 0, len(offers))
|
|
for _, o := range offers {
|
|
if o.gpu != nil {
|
|
out = append(out, o.info(region))
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ListRegions returns the region hosted machines launch in: the configured one,
|
|
// or none.
|
|
func ListRegions() []RegionInfo {
|
|
region := hostedConfig().Region
|
|
if region == "" {
|
|
return []RegionInfo{}
|
|
}
|
|
sizes := make([]string, 0, len(offers))
|
|
features := []string{}
|
|
for _, o := range offers {
|
|
sizes = append(sizes, o.slug)
|
|
if o.gpu != nil && len(features) == 0 {
|
|
features = append(features, "gpu")
|
|
}
|
|
}
|
|
return []RegionInfo{{Slug: region, Name: region, Available: true, Features: features, Sizes: sizes}}
|
|
}
|
|
|
|
// SizeBySlug returns the size for a slug, or nil when it is not for sale.
|
|
func SizeBySlug(slug string) *SizeInfo {
|
|
o, ok := offerFor(slug)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
si := o.info(hostedConfig().Region)
|
|
return &si
|
|
}
|