Skip to content

Commit 527b12e

Browse files
committed
Cache architecture for each instance type in memory
1 parent 04a4f62 commit 527b12e

File tree

6 files changed

+82
-9
lines changed

6 files changed

+82
-9
lines changed

controllers/awsmachine_controller_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func TestAWSMachineReconcilerIntegrationTests(t *testing.T) {
147147
ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateRunning
148148
ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabel: ""}
149149

150-
ec2Svc := ec2Service.NewService(cs)
150+
ec2Svc := ec2Service.NewService(cs).WithInstanceTypeArchitectureCache(nil)
151151
ec2Svc.EC2Client = ec2Mock
152152
reconciler.ec2ServiceFactory = func(scope scope.EC2Scope) services.EC2Interface {
153153
return ec2Svc

pkg/cloud/services/ec2/ami.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/endpoints"
3939
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/common"
4040
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/record"
41+
"sigs.k8s.io/cluster-api-provider-aws/v2/util/cache"
4142
)
4243

4344
const (
@@ -124,6 +125,15 @@ func GenerateAmiName(amiNameFormat, baseOS, kubernetesVersion string) (string, e
124125

125126
// Determine architecture based on instance type.
126127
func (s *Service) pickArchitectureForInstanceType(instanceType ec2types.InstanceType) (string, error) {
128+
logger := s.scope.GetLogger().WithValues("instance type", instanceType)
129+
130+
if s.InstanceTypeArchitectureCache != nil {
131+
if entry, ok := s.InstanceTypeArchitectureCache.Has(string(instanceType)); ok {
132+
logger.Info("Chosen architecture from cache", "architecture", entry.Architecture)
133+
return entry.Architecture, nil
134+
}
135+
}
136+
127137
descInstanceTypeInput := &ec2.DescribeInstanceTypesInput{
128138
InstanceTypes: []ec2types.InstanceType{instanceType},
129139
}
@@ -144,7 +154,7 @@ func (s *Service) pickArchitectureForInstanceType(instanceType ec2types.Instance
144154

145155
supportedArchs := describeInstanceTypeResult.InstanceTypes[0].ProcessorInfo.SupportedArchitectures
146156

147-
logger := s.scope.GetLogger().WithValues("instance type", instanceType, "supported architectures", supportedArchs)
157+
logger = logger.WithValues("supportedArchs", supportedArchs)
148158
logger.Info("Obtained a list of supported architectures for instance type")
149159

150160
// Loop over every supported architecture for the instance type
@@ -165,6 +175,13 @@ archCheck:
165175
return "", fmt.Errorf("unable to find preferred architecture for instance type %q", instanceType)
166176
}
167177

178+
if s.InstanceTypeArchitectureCache != nil {
179+
s.InstanceTypeArchitectureCache.Add(cache.InstanceTypeArchitectureCacheEntry{
180+
InstanceType: instanceType,
181+
Architecture: architecture,
182+
})
183+
}
184+
168185
logger.Info("Chosen architecture", "architecture", architecture)
169186

170187
return architecture, nil

pkg/cloud/services/ec2/instances_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5971,7 +5971,7 @@ func TestCreateInstance(t *testing.T) {
59715971
machineScope.AWSMachine.Spec = *tc.machineConfig
59725972
tc.expect(ec2Mock.EXPECT())
59735973

5974-
s := NewService(clusterScope)
5974+
s := NewService(clusterScope).WithInstanceTypeArchitectureCache(nil)
59755975
s.EC2Client = ec2Mock
59765976

59775977
instance, err := s.CreateInstance(context.TODO(), machineScope, data, "")

pkg/cloud/services/ec2/launchtemplate_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2028,7 +2028,7 @@ func TestDiscoverLaunchTemplateAMI(t *testing.T) {
20282028
tc.expect(ec2Mock.EXPECT())
20292029
}
20302030

2031-
s := NewService(cs)
2031+
s := NewService(cs).WithInstanceTypeArchitectureCache(nil)
20322032
s.EC2Client = ec2Mock
20332033

20342034
id, err := s.DiscoverLaunchTemplateAMI(context.TODO(), ms)
@@ -2104,7 +2104,7 @@ func TestDiscoverLaunchTemplateAMIForEKS(t *testing.T) {
21042104
tc.expectSSM(ssmMock.EXPECT())
21052105
}
21062106

2107-
s := NewService(mcps)
2107+
s := NewService(mcps).WithInstanceTypeArchitectureCache(nil)
21082108
s.EC2Client = ec2Mock
21092109
s.SSMClient = ssmMock
21102110

pkg/cloud/services/ec2/service.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/common"
2323
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/network"
2424
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/ssm"
25+
"sigs.k8s.io/cluster-api-provider-aws/v2/util/cache"
2526
)
2627

2728
// Service holds a collection of interfaces.
@@ -38,14 +39,23 @@ type Service struct {
3839
// RetryEC2Client is used for dedicated host operations with enhanced retry configuration
3940
// If nil, a new retry client will be created as needed
4041
RetryEC2Client common.EC2API
42+
43+
InstanceTypeArchitectureCache cache.InstanceTypeArchitectureCache
4144
}
4245

4346
// NewService returns a new service given the ec2 api client.
4447
func NewService(clusterScope scope.EC2Scope) *Service {
4548
return &Service{
46-
scope: clusterScope,
47-
EC2Client: scope.NewEC2Client(clusterScope, clusterScope, clusterScope, clusterScope.InfraCluster()),
48-
SSMClient: scope.NewSSMClient(clusterScope, clusterScope, clusterScope, clusterScope.InfraCluster()),
49-
netService: network.NewService(clusterScope.(scope.NetworkScope)),
49+
scope: clusterScope,
50+
EC2Client: scope.NewEC2Client(clusterScope, clusterScope, clusterScope, clusterScope.InfraCluster()),
51+
SSMClient: scope.NewSSMClient(clusterScope, clusterScope, clusterScope, clusterScope.InfraCluster()),
52+
netService: network.NewService(clusterScope.(scope.NetworkScope)),
53+
InstanceTypeArchitectureCache: cache.InstanceTypeArchitectureCacheSingleton,
5054
}
5155
}
56+
57+
// WithInstanceTypeArchitectureCache overrides the cache for InstanceTypeArchitectureCacheEntry items (nil disables caching).
58+
func (s *Service) WithInstanceTypeArchitectureCache(instanceTypeArchitectureCache cache.InstanceTypeArchitectureCache) *Service {
59+
s.InstanceTypeArchitectureCache = instanceTypeArchitectureCache
60+
return s
61+
}

util/cache/cache.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package cache implements caching helper functions.
18+
package cache
19+
20+
import (
21+
"time"
22+
23+
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
24+
25+
capicache "sigs.k8s.io/cluster-api/util/cache"
26+
)
27+
28+
// InstanceTypeArchitectureCacheEntry caches DescribeInstanceTypes results since they are not expected to change.
29+
type InstanceTypeArchitectureCacheEntry struct {
30+
InstanceType ec2types.InstanceType
31+
Architecture string
32+
}
33+
34+
// Key returns the cache key of a InstanceTypeArchitectureCacheEntry.
35+
func (e InstanceTypeArchitectureCacheEntry) Key() string {
36+
return string(e.InstanceType)
37+
}
38+
39+
// InstanceTypeArchitectureCache stores InstanceTypeArchitectureCacheEntry items.
40+
type InstanceTypeArchitectureCache = capicache.Cache[InstanceTypeArchitectureCacheEntry]
41+
42+
var (
43+
// InstanceTypeArchitectureCacheSingleton is the singleton cache for InstanceTypeArchitectureCacheEntry items.
44+
// It should be used in all relevant controllers (and possibly disabled for unit tests).
45+
InstanceTypeArchitectureCacheSingleton InstanceTypeArchitectureCache = capicache.New[InstanceTypeArchitectureCacheEntry](2 * time.Hour)
46+
)

0 commit comments

Comments
 (0)