File size: 6,024 Bytes
daa8246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package ionet

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/samber/lo"
)

// GetAvailableReplicas retrieves available replicas per location for specified hardware
func (c *Client) GetAvailableReplicas(hardwareID int, gpuCount int) (*AvailableReplicasResponse, error) {
	if hardwareID <= 0 {
		return nil, fmt.Errorf("hardware_id must be greater than 0")
	}
	if gpuCount < 1 {
		return nil, fmt.Errorf("gpu_count must be at least 1")
	}

	params := map[string]interface{}{
		"hardware_id":  hardwareID,
		"hardware_qty": gpuCount,
	}

	endpoint := "/available-replicas" + buildQueryParams(params)

	resp, err := c.makeRequest("GET", endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get available replicas: %w", err)
	}

	type availableReplicaPayload struct {
		ID                int    `json:"id"`
		ISO2              string `json:"iso2"`
		Name              string `json:"name"`
		AvailableReplicas int    `json:"available_replicas"`
	}
	var payload []availableReplicaPayload

	if err := decodeData(resp.Body, &payload); err != nil {
		return nil, fmt.Errorf("failed to parse available replicas response: %w", err)
	}

	replicas := lo.Map(payload, func(item availableReplicaPayload, _ int) AvailableReplica {
		return AvailableReplica{
			LocationID:     item.ID,
			LocationName:   item.Name,
			HardwareID:     hardwareID,
			HardwareName:   "",
			AvailableCount: item.AvailableReplicas,
			MaxGPUs:        gpuCount,
		}
	})

	return &AvailableReplicasResponse{Replicas: replicas}, nil
}

// GetMaxGPUsPerContainer retrieves the maximum number of GPUs available per hardware type
func (c *Client) GetMaxGPUsPerContainer() (*MaxGPUResponse, error) {
	resp, err := c.makeRequest("GET", "/hardware/max-gpus-per-container", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get max GPUs per container: %w", err)
	}

	var maxGPUResp MaxGPUResponse
	if err := decodeData(resp.Body, &maxGPUResp); err != nil {
		return nil, fmt.Errorf("failed to parse max GPU response: %w", err)
	}

	return &maxGPUResp, nil
}

// ListHardwareTypes retrieves available hardware types using the max GPUs endpoint
func (c *Client) ListHardwareTypes() ([]HardwareType, int, error) {
	maxGPUResp, err := c.GetMaxGPUsPerContainer()
	if err != nil {
		return nil, 0, fmt.Errorf("failed to list hardware types: %w", err)
	}

	mapped := lo.Map(maxGPUResp.Hardware, func(hw MaxGPUInfo, _ int) HardwareType {
		name := strings.TrimSpace(hw.HardwareName)
		if name == "" {
			name = fmt.Sprintf("Hardware %d", hw.HardwareID)
		}

		return HardwareType{
			ID:             hw.HardwareID,
			Name:           name,
			GPUType:        "",
			GPUMemory:      0,
			MaxGPUs:        hw.MaxGPUsPerContainer,
			CPU:            "",
			Memory:         0,
			Storage:        0,
			HourlyRate:     0,
			Available:      hw.Available > 0,
			BrandName:      strings.TrimSpace(hw.BrandName),
			AvailableCount: hw.Available,
		}
	})

	totalAvailable := maxGPUResp.Total
	if totalAvailable == 0 {
		totalAvailable = lo.SumBy(maxGPUResp.Hardware, func(hw MaxGPUInfo) int {
			return hw.Available
		})
	}

	return mapped, totalAvailable, nil
}

// ListLocations retrieves available deployment locations (if supported by the API)
func (c *Client) ListLocations() (*LocationsResponse, error) {
	resp, err := c.makeRequest("GET", "/locations", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to list locations: %w", err)
	}

	var locations LocationsResponse
	if err := decodeData(resp.Body, &locations); err != nil {
		return nil, fmt.Errorf("failed to parse locations response: %w", err)
	}

	locations.Locations = lo.Map(locations.Locations, func(location Location, _ int) Location {
		location.ISO2 = strings.ToUpper(strings.TrimSpace(location.ISO2))
		return location
	})

	if locations.Total == 0 {
		locations.Total = lo.SumBy(locations.Locations, func(location Location) int {
			return location.Available
		})
	}

	return &locations, nil
}

// GetHardwareType retrieves details about a specific hardware type
func (c *Client) GetHardwareType(hardwareID int) (*HardwareType, error) {
	if hardwareID <= 0 {
		return nil, fmt.Errorf("hardware ID must be greater than 0")
	}

	endpoint := fmt.Sprintf("/hardware/types/%d", hardwareID)

	resp, err := c.makeRequest("GET", endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get hardware type: %w", err)
	}

	// API response format not documented, assuming direct format
	var hardwareType HardwareType
	if err := json.Unmarshal(resp.Body, &hardwareType); err != nil {
		return nil, fmt.Errorf("failed to parse hardware type: %w", err)
	}

	return &hardwareType, nil
}

// GetLocation retrieves details about a specific location
func (c *Client) GetLocation(locationID int) (*Location, error) {
	if locationID <= 0 {
		return nil, fmt.Errorf("location ID must be greater than 0")
	}

	endpoint := fmt.Sprintf("/locations/%d", locationID)

	resp, err := c.makeRequest("GET", endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get location: %w", err)
	}

	// API response format not documented, assuming direct format
	var location Location
	if err := json.Unmarshal(resp.Body, &location); err != nil {
		return nil, fmt.Errorf("failed to parse location: %w", err)
	}

	return &location, nil
}

// GetLocationAvailability retrieves real-time availability for a specific location
func (c *Client) GetLocationAvailability(locationID int) (*LocationAvailability, error) {
	if locationID <= 0 {
		return nil, fmt.Errorf("location ID must be greater than 0")
	}

	endpoint := fmt.Sprintf("/locations/%d/availability", locationID)

	resp, err := c.makeRequest("GET", endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get location availability: %w", err)
	}

	// API response format not documented, assuming direct format
	var availability LocationAvailability
	if err := json.Unmarshal(resp.Body, &availability); err != nil {
		return nil, fmt.Errorf("failed to parse location availability: %w", err)
	}

	return &availability, nil
}