Skip to content

Commit 3288160

Browse files
authored
Merge pull request #6617 from ionos-cloud/update-ionos-sdk
ionoscloud: Update ionos-cloud sdk-go and add metrics
2 parents c96aa9b + e390b4a commit 3288160

File tree

230 files changed

+25020
-12248
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

230 files changed

+25020
-12248
lines changed

cluster-autoscaler/cloudprovider/ionoscloud/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ Store the token in a secret:
2525
kubectl create secret generic cloud-config --from-literal=token=MY_TOKEN
2626
```
2727

28-
Edit [`example-values.yaml`](./examples-values.yaml) and deploy using helm:
28+
Edit [`example-values.yaml`](./example-values.yaml) and deploy using helm:
2929

3030
```console
3131
helm install ionoscloud-cluster-autoscaler autoscaler/cluster-autoscaler -f example-values.yaml
3232
```
3333

34+
### Configuration
35+
36+
The `IONOS_ADDITIONAL_HEADERS` environment variable can be used to configure the client to send additional headers on
37+
each Ionos Cloud API request, such as `X-Contract-Number`. This can be useful for users with multiple contracts.
38+
The format is a semicolon-separated list of key:value pairs, e.g. `IONOS_ADDITIONAL_HEADERS="X-Contract-Number:1234657890"`.
39+
3440
## Development
3541

3642
The unit tests use mocks generated by [mockery](https://github.com/vektra/mockery/v2). To update them run:
@@ -40,6 +46,11 @@ mockery --inpackage --testonly --case snake --boilerplate-file ../../../hack/boi
4046
mockery --inpackage --testonly --case snake --boilerplate-file ../../../hack/boilerplate/boilerplate.generatego.txt --name IonosCloudManager
4147
```
4248

49+
### Debugging
50+
51+
The global logging verbosity controlled by the `--v` flag is passed on to the Ionos Cloud SDK client logger.
52+
Verbosity 5 maps to `Debug` and 7 to `Trace`. **Do not enable this in production, as it will print full request and response data.**
53+
4354
### Build an image
4455

4556
Build and push a docker image in the `cluster-autoscaler` directory:

cluster-autoscaler/cloudprovider/ionoscloud/cache.go

Lines changed: 26 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,20 @@ limitations under the License.
1717
package ionoscloud
1818

1919
import (
20+
"maps"
2021
"sync"
21-
"time"
2222

2323
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
2424
"k8s.io/klog/v2"
2525
)
2626

27-
const nodeGroupCacheEntryTimeout = 2 * time.Minute
28-
29-
type nodeGroupCacheEntry struct {
30-
data cloudprovider.NodeGroup
31-
ts time.Time
32-
}
33-
34-
var timeNow = time.Now
35-
3627
// IonosCache caches resources to reduce API calls.
3728
// Cached state includes autoscaling limits, sizes and target sizes, a mapping of instances to node
3829
// groups, and a simple lock mechanism to prevent invalid node group writes.
3930
type IonosCache struct {
4031
mutex sync.Mutex
4132

42-
nodeGroups map[string]nodeGroupCacheEntry
33+
nodeGroups map[string]cloudprovider.NodeGroup
4334
nodesToNodeGroups map[string]string
4435
nodeGroupSizes map[string]int
4536
nodeGroupTargetSizes map[string]int
@@ -49,11 +40,11 @@ type IonosCache struct {
4940
// NewIonosCache initializes a new IonosCache.
5041
func NewIonosCache() *IonosCache {
5142
return &IonosCache{
52-
nodeGroups: map[string]nodeGroupCacheEntry{},
53-
nodesToNodeGroups: map[string]string{},
54-
nodeGroupSizes: map[string]int{},
55-
nodeGroupTargetSizes: map[string]int{},
56-
nodeGroupLockTable: map[string]bool{},
43+
nodeGroups: make(map[string]cloudprovider.NodeGroup),
44+
nodesToNodeGroups: make(map[string]string),
45+
nodeGroupSizes: make(map[string]int),
46+
nodeGroupTargetSizes: make(map[string]int),
47+
nodeGroupLockTable: make(map[string]bool),
5748
}
5849
}
5950

@@ -62,15 +53,7 @@ func (cache *IonosCache) AddNodeGroup(newPool cloudprovider.NodeGroup) {
6253
cache.mutex.Lock()
6354
defer cache.mutex.Unlock()
6455

65-
cache.nodeGroups[newPool.Id()] = nodeGroupCacheEntry{data: newPool}
66-
}
67-
68-
func (cache *IonosCache) removeNodesForNodeGroupNoLock(id string) {
69-
for nodeId, nodeGroupId := range cache.nodesToNodeGroups {
70-
if nodeGroupId == id {
71-
delete(cache.nodesToNodeGroups, nodeId)
72-
}
73-
}
56+
cache.nodeGroups[newPool.Id()] = newPool
7457
}
7558

7659
// RemoveInstanceFromCache deletes an instance and its respective mapping to the node group from
@@ -79,38 +62,39 @@ func (cache *IonosCache) RemoveInstanceFromCache(id string) {
7962
cache.mutex.Lock()
8063
defer cache.mutex.Unlock()
8164

82-
klog.V(5).Infof("Removed instance %s from cache", id)
83-
nodeGroupId := cache.nodesToNodeGroups[id]
84-
delete(cache.nodesToNodeGroups, id)
85-
cache.updateNodeGroupTimestampNoLock(nodeGroupId)
65+
if _, ok := cache.nodesToNodeGroups[id]; ok {
66+
delete(cache.nodesToNodeGroups, id)
67+
klog.V(5).Infof("Removed instance %s from cache", id)
68+
}
8669
}
8770

8871
// SetInstancesCacheForNodeGroup overwrites cached instances and mappings for a node group.
8972
func (cache *IonosCache) SetInstancesCacheForNodeGroup(id string, instances []cloudprovider.Instance) {
9073
cache.mutex.Lock()
9174
defer cache.mutex.Unlock()
9275

93-
cache.removeNodesForNodeGroupNoLock(id)
76+
maps.DeleteFunc(cache.nodesToNodeGroups, func(_, nodeGroupID string) bool {
77+
return nodeGroupID == id
78+
})
9479
cache.setInstancesCacheForNodeGroupNoLock(id, instances)
9580
}
9681

9782
func (cache *IonosCache) setInstancesCacheForNodeGroupNoLock(id string, instances []cloudprovider.Instance) {
9883
for _, instance := range instances {
99-
nodeId := convertToNodeId(instance.Id)
100-
cache.nodesToNodeGroups[nodeId] = id
84+
nodeID := convertToNodeID(instance.Id)
85+
cache.nodesToNodeGroups[nodeID] = id
10186
}
102-
cache.updateNodeGroupTimestampNoLock(id)
10387
}
10488

105-
// GetNodeGroupIds returns an unsorted list of cached node group ids.
106-
func (cache *IonosCache) GetNodeGroupIds() []string {
89+
// GetNodeGroupIDs returns an unsorted list of cached node group ids.
90+
func (cache *IonosCache) GetNodeGroupIDs() []string {
10791
cache.mutex.Lock()
10892
defer cache.mutex.Unlock()
10993

110-
return cache.getNodeGroupIds()
94+
return cache.getNodeGroupIDs()
11195
}
11296

113-
func (cache *IonosCache) getNodeGroupIds() []string {
97+
func (cache *IonosCache) getNodeGroupIDs() []string {
11498
ids := make([]string, 0, len(cache.nodeGroups))
11599
for id := range cache.nodeGroups {
116100
ids = append(ids, id)
@@ -125,26 +109,26 @@ func (cache *IonosCache) GetNodeGroups() []cloudprovider.NodeGroup {
125109

126110
nodeGroups := make([]cloudprovider.NodeGroup, 0, len(cache.nodeGroups))
127111
for id := range cache.nodeGroups {
128-
nodeGroups = append(nodeGroups, cache.nodeGroups[id].data)
112+
nodeGroups = append(nodeGroups, cache.nodeGroups[id])
129113
}
130114
return nodeGroups
131115
}
132116

133117
// GetNodeGroupForNode returns the node group for the given node.
134118
// Returns nil if either the mapping or the node group is not cached.
135-
func (cache *IonosCache) GetNodeGroupForNode(nodeId string) cloudprovider.NodeGroup {
119+
func (cache *IonosCache) GetNodeGroupForNode(nodeID string) cloudprovider.NodeGroup {
136120
cache.mutex.Lock()
137121
defer cache.mutex.Unlock()
138122

139-
id, found := cache.nodesToNodeGroups[nodeId]
123+
nodeGroupID, found := cache.nodesToNodeGroups[nodeID]
140124
if !found {
141125
return nil
142126
}
143-
entry, found := cache.nodeGroups[id]
127+
nodeGroup, found := cache.nodeGroups[nodeGroupID]
144128
if !found {
145129
return nil
146130
}
147-
return entry.data
131+
return nodeGroup
148132
}
149133

150134
// TryLockNodeGroup tries to write a node group lock entry.
@@ -219,19 +203,3 @@ func (cache *IonosCache) InvalidateNodeGroupTargetSize(id string) {
219203

220204
delete(cache.nodeGroupTargetSizes, id)
221205
}
222-
223-
// NodeGroupNeedsRefresh returns true when the instances for the given node group have not been
224-
// updated for more than 2 minutes.
225-
func (cache *IonosCache) NodeGroupNeedsRefresh(id string) bool {
226-
cache.mutex.Lock()
227-
defer cache.mutex.Unlock()
228-
229-
return timeNow().Sub(cache.nodeGroups[id].ts) > nodeGroupCacheEntryTimeout
230-
}
231-
232-
func (cache *IonosCache) updateNodeGroupTimestampNoLock(id string) {
233-
if entry, ok := cache.nodeGroups[id]; ok {
234-
entry.ts = timeNow()
235-
cache.nodeGroups[id] = entry
236-
}
237-
}

cluster-autoscaler/cloudprovider/ionoscloud/cache_test.go

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,11 @@ package ionoscloud
1818

1919
import (
2020
"testing"
21-
"time"
2221

2322
"github.com/stretchr/testify/require"
2423
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
2524
)
2625

27-
func newCacheEntry(data cloudprovider.NodeGroup, ts time.Time) nodeGroupCacheEntry {
28-
return nodeGroupCacheEntry{data: data, ts: ts}
29-
}
30-
3126
func TestCache_AddNodeGroup(t *testing.T) {
3227
cache := NewIonosCache()
3328
require.Empty(t, cache.GetNodeGroups())
@@ -36,17 +31,14 @@ func TestCache_AddNodeGroup(t *testing.T) {
3631
}
3732

3833
func TestCache_RemoveInstanceFromCache(t *testing.T) {
39-
firstTime := timeNow().Add(-2*time.Minute - 1*time.Second)
4034
cache := NewIonosCache()
41-
cache.nodeGroups["2"] = newCacheEntry(&nodePool{id: "2"}, firstTime)
35+
cache.nodeGroups["2"] = &nodePool{id: "2"}
4236
cache.nodesToNodeGroups["b2"] = "2"
4337

4438
require.NotNil(t, cache.GetNodeGroupForNode("b2"))
45-
require.True(t, cache.NodeGroupNeedsRefresh("2"))
4639

4740
cache.RemoveInstanceFromCache("b2")
4841
require.Nil(t, cache.GetNodeGroupForNode("b2"))
49-
require.False(t, cache.NodeGroupNeedsRefresh("2"))
5042
}
5143

5244
func TestCache_SetInstancesCacheForNodeGroup(t *testing.T) {
@@ -65,11 +57,11 @@ func TestCache_SetInstancesCacheForNodeGroup(t *testing.T) {
6557

6658
func TestCache_GetNodeGroupIDs(t *testing.T) {
6759
cache := NewIonosCache()
68-
require.Empty(t, cache.GetNodeGroupIds())
60+
require.Empty(t, cache.GetNodeGroupIDs())
6961
cache.AddNodeGroup(&nodePool{id: "1"})
70-
require.Equal(t, []string{"1"}, cache.GetNodeGroupIds())
62+
require.Equal(t, []string{"1"}, cache.GetNodeGroupIDs())
7163
cache.AddNodeGroup(&nodePool{id: "2"})
72-
require.ElementsMatch(t, []string{"1", "2"}, cache.GetNodeGroupIds())
64+
require.ElementsMatch(t, []string{"1", "2"}, cache.GetNodeGroupIDs())
7365
}
7466

7567
func TestCache_GetNodeGroups(t *testing.T) {
@@ -139,22 +131,3 @@ func TestCache_GetSetNodeGroupTargetSize(t *testing.T) {
139131
require.False(t, found)
140132
require.Zero(t, size)
141133
}
142-
143-
func TestCache_NodeGroupNeedsRefresh(t *testing.T) {
144-
fixedTime := time.Now().Round(time.Second)
145-
timeNow = func() time.Time { return fixedTime }
146-
defer func() { timeNow = time.Now }()
147-
148-
cache := NewIonosCache()
149-
require.True(t, cache.NodeGroupNeedsRefresh("test"))
150-
151-
cache.AddNodeGroup(&nodePool{id: "test"})
152-
require.True(t, cache.NodeGroupNeedsRefresh("test"))
153-
cache.SetInstancesCacheForNodeGroup("test", nil)
154-
require.False(t, cache.NodeGroupNeedsRefresh("test"))
155-
156-
timeNow = func() time.Time { return fixedTime.Add(nodeGroupCacheEntryTimeout) }
157-
require.False(t, cache.NodeGroupNeedsRefresh("test"))
158-
timeNow = func() time.Time { return fixedTime.Add(nodeGroupCacheEntryTimeout + 1*time.Second) }
159-
require.True(t, cache.NodeGroupNeedsRefresh("test"))
160-
}

0 commit comments

Comments
 (0)