-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathoci_shape.go
238 lines (204 loc) · 7.3 KB
/
oci_shape.go
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*
Copyright 2021-2023 Oracle and/or its affiliates.
*/
package common
import (
"context"
"fmt"
"sync"
"time"
"github.com/pkg/errors"
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/oci/vendor-internal/github.com/oracle/oci-go-sdk/v65/common"
oke "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/oci/vendor-internal/github.com/oracle/oci-go-sdk/v65/containerengine"
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/oci/vendor-internal/github.com/oracle/oci-go-sdk/v65/core"
"k8s.io/klog/v2"
)
// ShapeGetter returns the oci shape attributes for the pool.
type ShapeGetter interface {
GetNodePoolShape(*oke.NodePool, int64) (*Shape, error)
GetInstancePoolShape(pool *core.InstancePool) (*Shape, error)
Refresh()
}
// ShapeClient is an interface around the GetInstanceConfiguration and ListShapes calls.
type ShapeClient interface {
GetInstanceConfiguration(context.Context, core.GetInstanceConfigurationRequest) (core.GetInstanceConfigurationResponse, error)
ListShapes(context.Context, core.ListShapesRequest) (core.ListShapesResponse, error)
}
// ShapeClientImpl is the implementation for fetching shape information.
type ShapeClientImpl struct {
// Can fetch instance configs (flexible shapes)
ComputeMgmtClient core.ComputeManagementClient
// Can fetch shapes directly
ComputeClient core.ComputeClient
}
// GetInstanceConfiguration gets the instance configuration.
func (cc ShapeClientImpl) GetInstanceConfiguration(ctx context.Context, req core.GetInstanceConfigurationRequest) (core.GetInstanceConfigurationResponse, error) {
return cc.ComputeMgmtClient.GetInstanceConfiguration(ctx, req)
}
// ListShapes lists the shapes.
func (cc ShapeClientImpl) ListShapes(ctx context.Context, req core.ListShapesRequest) (core.ListShapesResponse, error) {
return cc.ComputeClient.ListShapes(ctx, req)
}
// Shape includes the resource attributes of a given shape which should be used
// for constructing node templates.
type Shape struct {
Name string
CPU float32
GPU int
MemoryInBytes float32
EphemeralStorageInBytes float32
}
// CreateShapeGetter creates a new oci shape getter.
func CreateShapeGetter(shapeClient ShapeClient) ShapeGetter {
return &shapeGetterImpl{
shapeClient: shapeClient,
cache: map[string]*Shape{},
}
}
type shapeGetterImpl struct {
shapeClient ShapeClient
cache map[string]*Shape
mu sync.Mutex
}
// Refresh clears out the cache to be populated again as the pool shapes are re-requested
func (osf *shapeGetterImpl) Refresh() {
// For now, just clear the cache
osf.cache = map[string]*Shape{}
}
// GetNodePoolShape gets the shape by querying the node pool's configuration
func (osf *shapeGetterImpl) GetNodePoolShape(np *oke.NodePool, ephemeralStorage int64) (*Shape, error) {
shapeName := *np.NodeShape
if np.NodeShapeConfig != nil {
return &Shape{
Name: shapeName,
CPU: *np.NodeShapeConfig.Ocpus * 2,
// num_bytes * kilo * mega * giga
MemoryInBytes: *np.NodeShapeConfig.MemoryInGBs * 1024 * 1024 * 1024,
GPU: 0,
EphemeralStorageInBytes: float32(ephemeralStorage),
}, nil
}
osf.mu.Lock()
defer osf.mu.Unlock()
// check cache first
shape, ok := osf.cache[shapeName]
if ok {
return shape, nil
}
// refresh cache if we have a miss.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := osf.shapeClient.ListShapes(ctx, core.ListShapesRequest{
CompartmentId: np.CompartmentId,
Limit: common.Int(500),
})
if err != nil {
return nil, errors.Wrap(err, "unable to ListShapes")
}
// Update the cache based on latest results
for _, s := range resp.Items {
osf.cache[*s.Shape] = &Shape{
Name: shapeName,
CPU: getFloat32(s.Ocpus) * 2, // convert ocpu to vcpu
GPU: getInt(s.Gpus),
MemoryInBytes: getFloat32(s.MemoryInGBs) * 1024 * 1024 * 1024,
EphemeralStorageInBytes: float32(ephemeralStorage),
}
}
// fetch value from updated cache... if it exists.
shape, ok = osf.cache[shapeName]
if ok {
return shape, nil
}
return nil, fmt.Errorf("shape %q does not exist", shapeName)
}
// GetInstancePoolShape gets the shape by querying the instance pool's configuration
func (osf *shapeGetterImpl) GetInstancePoolShape(ip *core.InstancePool) (*Shape, error) {
// First, check instance pool shape cache
shape, ok := osf.cache[*ip.Id]
if ok {
return shape, nil
}
klog.V(5).Info("fetching shape configuration details for instance-pool " + *ip.Id)
shape = &Shape{}
instanceConfig, err := osf.shapeClient.GetInstanceConfiguration(context.Background(), core.GetInstanceConfigurationRequest{
InstanceConfigurationId: ip.InstanceConfigurationId,
})
if err != nil {
return nil, err
}
if instanceConfig.InstanceDetails == nil {
return nil, fmt.Errorf("instance configuration details for instance %s has not been set", *ip.Id)
}
if instanceDetails, ok := instanceConfig.InstanceDetails.(core.ComputeInstanceDetails); ok {
// flexible shape use details or look up the static shape details below.
if instanceDetails.LaunchDetails != nil && instanceDetails.LaunchDetails.ShapeConfig != nil {
if instanceDetails.LaunchDetails.Shape != nil {
shape.Name = *instanceDetails.LaunchDetails.Shape
}
if instanceDetails.LaunchDetails.ShapeConfig.Ocpus != nil {
shape.CPU = *instanceDetails.LaunchDetails.ShapeConfig.Ocpus
// Minimum amount of memory unless explicitly set higher
shape.MemoryInBytes = *instanceDetails.LaunchDetails.ShapeConfig.Ocpus * 1024 * 1024 * 1024
}
if instanceDetails.LaunchDetails.ShapeConfig.MemoryInGBs != nil {
shape.MemoryInBytes = *instanceDetails.LaunchDetails.ShapeConfig.MemoryInGBs * 1024 * 1024 * 1024
}
} else {
// Fetch the shape object by name
var page *string
var everyShape []core.Shape
for {
// List all available shapes
lisShapesReq := core.ListShapesRequest{}
lisShapesReq.CompartmentId = instanceConfig.CompartmentId
lisShapesReq.Page = page
lisShapesReq.Limit = common.Int(50)
listShapes, err := osf.shapeClient.ListShapes(context.Background(), lisShapesReq)
if err != nil {
return nil, err
}
everyShape = append(everyShape, listShapes.Items...)
if page = listShapes.OpcNextPage; listShapes.OpcNextPage == nil {
break
}
}
for _, nextShape := range everyShape {
if *nextShape.Shape == *instanceDetails.LaunchDetails.Shape {
shape.Name = *nextShape.Shape
if nextShape.Ocpus != nil {
shape.CPU = *nextShape.Ocpus
}
if nextShape.MemoryInGBs != nil {
shape.MemoryInBytes = *nextShape.MemoryInGBs * 1024 * 1024 * 1024
}
if nextShape.Gpus != nil {
shape.GPU = *nextShape.Gpus
}
}
}
}
} else {
return nil, fmt.Errorf("(compute) instance configuration for instance-pool %s not found", *ip.Id)
}
// Didn't find a match
if shape.Name == "" {
return nil, fmt.Errorf("shape information for instance-pool %s not found", *ip.Id)
}
osf.cache[*ip.Id] = shape
return shape, nil
}
// getFloat32 is a helper to get a float32 pointer value or default to 0.
func getFloat32(f *float32) float32 {
if f == nil {
return 0.0
}
return *f
}
// getInt is a helper to get an int pointer value or default to 0.
func getInt(i *int) int {
if i == nil {
return 0
}
return *i
}