Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support get instanceType from node anno #650

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions daemon/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,22 +171,39 @@
}

func (b *NetworkServiceBuilder) initInstanceLimit() error {
instanceType := instance.GetInstanceMeta().InstanceType

node := b.service.k8s.Node()
if node == nil {
return fmt.Errorf("k8s node not found")

Check warning on line 176 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L174-L176

Added lines #L174 - L176 were not covered by tests
}
provider := client.LimitProviders["ecs"]
if os.Getenv("TERWAY_DEPLOY_ENV") == envEFLO {
instanceType = instance.GetInstanceMeta().InstanceID

provider = client.LimitProviders["eflo"]
limit, err := provider.GetLimitFromAnno(node.Annotations)
if err != nil {
return err

Check warning on line 183 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L181-L183

Added lines #L181 - L183 were not covered by tests
}
if limit == nil {
limit, err = provider.GetLimit(b.aliyunClient, instance.GetInstanceMeta().InstanceID)
if err != nil {
return fmt.Errorf("upable get instance limit, %w", err)

Check warning on line 188 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L185-L188

Added lines #L185 - L188 were not covered by tests
}
}
b.limit = limit
} else {
limit, err := provider.GetLimitFromAnno(node.Annotations)
if err != nil {
return err

Check warning on line 195 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L191-L195

Added lines #L191 - L195 were not covered by tests
}
if limit == nil || instance.GetInstanceMeta().InstanceType != limit.InstanceTypeID {
limit, err = provider.GetLimit(b.aliyunClient, instance.GetInstanceMeta().InstanceType)
if err != nil {
return fmt.Errorf("upable get instance limit, %w", err)

Check warning on line 200 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L197-L200

Added lines #L197 - L200 were not covered by tests
}
}
b.limit = limit

Check warning on line 203 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L203

Added line #L203 was not covered by tests
}
limit, err := provider.GetLimit(b.aliyunClient, instanceType)
if err != nil {
return fmt.Errorf("upable get instance limit, %w", err)
}

b.limit = limit

b.service.enableIPv4, b.service.enableIPv6 = checkInstance(limit, b.daemonMode, b.config)
b.service.enableIPv4, b.service.enableIPv6 = checkInstance(b.limit, b.daemonMode, b.config)

Check warning on line 206 in daemon/builder.go

View check run for this annotation

Codecov / codecov/patch

daemon/builder.go#L206

Added line #L206 was not covered by tests
return nil
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/aliyun/client/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,14 +461,14 @@
resp, err := a.ClientSet.ECS().DescribeInstanceTypes(req)
metric.OpenAPILatency.WithLabelValues("DescribeInstanceTypes", fmt.Sprint(err != nil)).Observe(metric.MsSince(start))

l := logf.FromContext(ctx).WithValues(
LogFieldAPI, "DescribeInstanceTypes",
)
l := LogFields(logf.FromContext(ctx), req)

Check warning on line 464 in pkg/aliyun/client/ecs.go

View check run for this annotation

Codecov / codecov/patch

pkg/aliyun/client/ecs.go#L464

Added line #L464 was not covered by tests

if err != nil {
err = apiErr.WarpError(err)
l.WithValues(LogFieldRequestID, apiErr.ErrRequestID(err)).Error(err, "describe instance types failed")
return nil, err
}
l.WithValues(LogFieldRequestID, resp.RequestId).Info("success")

Check warning on line 471 in pkg/aliyun/client/ecs.go

View check run for this annotation

Codecov / codecov/patch

pkg/aliyun/client/ecs.go#L471

Added line #L471 was not covered by tests

result = append(result, resp.InstanceTypes.InstanceType...)

Expand Down
89 changes: 54 additions & 35 deletions pkg/aliyun/client/limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"k8s.io/apimachinery/pkg/util/cache"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)

// Limits specifies the IPAM relevant instance limits
type Limits struct {
InstanceTypeID string

// Adapters specifies the maximum number of interfaces that can be
// attached to the instance
Adapters int
Expand Down Expand Up @@ -71,6 +75,7 @@

type LimitProvider interface {
GetLimit(client interface{}, instanceType string) (*Limits, error)
GetLimitFromAnno(anno map[string]string) (*Limits, error)
}

type EfloLimitProvider struct{}
Expand All @@ -96,6 +101,10 @@
}, nil
}

func (e *EfloLimitProvider) GetLimitFromAnno(anno map[string]string) (*Limits, error) {
return nil, nil

Check warning on line 105 in pkg/aliyun/client/limit.go

View check run for this annotation

Codecov / codecov/patch

pkg/aliyun/client/limit.go#L104-L105

Added lines #L104 - L105 were not covered by tests
}

type ECSLimitProvider struct {
cache cache.LRUExpireCache
ttl time.Duration
Expand Down Expand Up @@ -128,42 +137,11 @@

for _, instanceTypeInfo := range ins {
instanceTypeID := instanceTypeInfo.InstanceTypeId
adapterLimit := instanceTypeInfo.EniQuantity
ipv4PerAdapter := instanceTypeInfo.EniPrivateIpAddressQuantity
ipv6PerAdapter := instanceTypeInfo.EniIpv6AddressQuantity
memberAdapterLimit := instanceTypeInfo.EniTotalQuantity - instanceTypeInfo.EniQuantity
eRdmaLimit := instanceTypeInfo.EriQuantity
// exclude eth0 eth1
maxMemberAdapterLimit := instanceTypeInfo.EniTotalQuantity - 2
if !instanceTypeInfo.EniTrunkSupported {
memberAdapterLimit = 0
maxMemberAdapterLimit = 0
}

d.cache.Add(instanceTypeID, &Limits{
Adapters: adapterLimit,
TotalAdapters: instanceTypeInfo.EniTotalQuantity,
IPv4PerAdapter: max(ipv4PerAdapter, 0),
IPv6PerAdapter: max(ipv6PerAdapter, 0),
MemberAdapterLimit: max(memberAdapterLimit, 0),
MaxMemberAdapterLimit: max(maxMemberAdapterLimit, 0),
ERdmaAdapters: max(eRdmaLimit, 0),
InstanceBandwidthRx: instanceTypeInfo.InstanceBandwidthRx,
InstanceBandwidthTx: instanceTypeInfo.InstanceBandwidthTx,
}, d.ttl)
logf.Log.WithValues(
"instance-type", instanceType,
"adapters", adapterLimit,
"total-adapters", instanceTypeInfo.EniTotalQuantity,
"ipv4", ipv4PerAdapter,
"ipv6", ipv6PerAdapter,
"member-adapters", memberAdapterLimit,
"erdma-adapters", eRdmaLimit,
"max-member-adapters", maxMemberAdapterLimit,
"bandwidth-rx", instanceTypeInfo.InstanceBandwidthRx,
"bandwidth-tx", instanceTypeInfo.InstanceBandwidthTx,
).Info("instance limit")

limit := getInstanceType(&instanceTypeInfo)

Check warning on line 141 in pkg/aliyun/client/limit.go

View check run for this annotation

Codecov / codecov/patch

pkg/aliyun/client/limit.go#L141

Added line #L141 was not covered by tests

d.cache.Add(instanceTypeID, limit, d.ttl)
logf.Log.Info("instance limit", instanceTypeID, limit)

Check warning on line 144 in pkg/aliyun/client/limit.go

View check run for this annotation

Codecov / codecov/patch

pkg/aliyun/client/limit.go#L143-L144

Added lines #L143 - L144 were not covered by tests
}
if instanceType == "" {
return nil, nil
Expand All @@ -176,6 +154,47 @@
return v.(*Limits), nil
}

func (d *ECSLimitProvider) GetLimitFromAnno(anno map[string]string) (*Limits, error) {
v, ok := anno["alibabacloud.com/instance-type-info"]
if !ok {
return nil, nil
}

instanceType := &ecs.InstanceType{}
err := json.Unmarshal([]byte(v), instanceType)
if err != nil {
return nil, err

Check warning on line 166 in pkg/aliyun/client/limit.go

View check run for this annotation

Codecov / codecov/patch

pkg/aliyun/client/limit.go#L166

Added line #L166 was not covered by tests
}

return getInstanceType(instanceType), nil
}

func getInstanceType(instanceTypeInfo *ecs.InstanceType) *Limits {
adapterLimit := instanceTypeInfo.EniQuantity
ipv4PerAdapter := instanceTypeInfo.EniPrivateIpAddressQuantity
ipv6PerAdapter := instanceTypeInfo.EniIpv6AddressQuantity
memberAdapterLimit := instanceTypeInfo.EniTotalQuantity - instanceTypeInfo.EniQuantity
eRdmaLimit := instanceTypeInfo.EriQuantity
// exclude eth0 eth1
maxMemberAdapterLimit := instanceTypeInfo.EniTotalQuantity - 2
if !instanceTypeInfo.EniTrunkSupported {
memberAdapterLimit = 0
maxMemberAdapterLimit = 0
}
return &Limits{
InstanceTypeID: instanceTypeInfo.InstanceTypeId,
Adapters: adapterLimit,
TotalAdapters: instanceTypeInfo.EniTotalQuantity,
IPv4PerAdapter: max(ipv4PerAdapter, 0),
IPv6PerAdapter: max(ipv6PerAdapter, 0),
MemberAdapterLimit: max(memberAdapterLimit, 0),
MaxMemberAdapterLimit: max(maxMemberAdapterLimit, 0),
ERdmaAdapters: max(eRdmaLimit, 0),
InstanceBandwidthRx: instanceTypeInfo.InstanceBandwidthRx,
InstanceBandwidthTx: instanceTypeInfo.InstanceBandwidthTx,
}
}

var ecsProvider LimitProvider
var efloProvider LimitProvider

Expand Down
126 changes: 126 additions & 0 deletions pkg/aliyun/client/limit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package client

import (
"fmt"
"testing"

"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/stretchr/testify/assert"
)

func TestGetInstanceType(t *testing.T) {
tests := []struct {
name string
input *ecs.InstanceType
expected *Limits
}{
{
name: "Basic instance type",
input: &ecs.InstanceType{
EniQuantity: 4,
EniPrivateIpAddressQuantity: 5,
EniIpv6AddressQuantity: 10,
EniTotalQuantity: 6,
EriQuantity: 2,
InstanceBandwidthRx: 1000,
InstanceBandwidthTx: 500,
EniTrunkSupported: true,
},
expected: &Limits{
Adapters: 4,
TotalAdapters: 6,
IPv4PerAdapter: 5,
IPv6PerAdapter: 10,
MemberAdapterLimit: 2,
MaxMemberAdapterLimit: 4,
ERdmaAdapters: 2,
InstanceBandwidthRx: 1000,
InstanceBandwidthTx: 500,
},
},
{
name: "Trunk not supported",
input: &ecs.InstanceType{
EniQuantity: 4,
EniPrivateIpAddressQuantity: 5,
EniIpv6AddressQuantity: 10,
EniTotalQuantity: 6,
EriQuantity: 2,
InstanceBandwidthRx: 1000,
InstanceBandwidthTx: 500,
EniTrunkSupported: false,
},
expected: &Limits{
Adapters: 4,
TotalAdapters: 6,
IPv4PerAdapter: 5,
IPv6PerAdapter: 10,
MemberAdapterLimit: 0,
MaxMemberAdapterLimit: 0,
ERdmaAdapters: 2,
InstanceBandwidthRx: 1000,
InstanceBandwidthTx: 500,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := getInstanceType(tt.input)
assert.Equal(t, tt.expected, actual)
})
}
}

func TestECSLimitProvider_GetLimitFromAnno(t *testing.T) {

type args struct {
anno map[string]string
}
tests := []struct {
name string
args args
want *Limits
wantErr assert.ErrorAssertionFunc
}{
{
name: "test value",
args: args{
anno: map[string]string{
"alibabacloud.com/instance-type-info": "{\"InstancePpsTx\":24000000,\"NvmeSupport\":\"unsupported\",\"PrimaryEniQueueNumber\":32,\"TotalEniQueueQuantity\":528,\"EniTrunkSupported\":true,\"InstanceTypeFamily\":\"ecs.ebmre7p\",\"InstancePpsRx\":24000000,\"EriQuantity\":0,\"InstanceBandwidthRx\":65536000,\"EnhancedNetwork\":{},\"InstanceBandwidthTx\":65536000,\"SecondaryEniQueueNumber\":16,\"LocalStorageCategory\":\"\",\"InstanceTypeId\":\"ecs.ebmre7p.32xlarge\",\"EniIpv6AddressQuantity\":1,\"EniTotalQuantity\":110,\"EniQuantity\":32,\"DiskQuantity\":17,\"EniPrivateIpAddressQuantity\":15}",
},
},
want: &Limits{
InstanceTypeID: "ecs.ebmre7p.32xlarge",
Adapters: 32,
TotalAdapters: 110,
IPv4PerAdapter: 15,
IPv6PerAdapter: 1,
MemberAdapterLimit: 78,
MaxMemberAdapterLimit: 108,
ERdmaAdapters: 0,
InstanceBandwidthRx: 65536000,
InstanceBandwidthTx: 65536000,
},
wantErr: assert.NoError,
},
{
name: "test empty",
args: args{
anno: map[string]string{},
},
want: nil,
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &ECSLimitProvider{}
got, err := d.GetLimitFromAnno(tt.args.anno)
if !tt.wantErr(t, err, fmt.Sprintf("GetLimitFromAnno(%v)", tt.args.anno)) {
return
}
assert.Equalf(t, tt.want, got, "GetLimitFromAnno(%v)", tt.args.anno)
})
}
}
1 change: 1 addition & 0 deletions pkg/eni/local_windows.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eni

import (
"context"
"strings"
"time"

Expand Down
4 changes: 4 additions & 0 deletions pkg/eni/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,7 @@ func (f *FakeK8s) GetClient() client.Client {
func (f *FakeK8s) PodExist(namespace, name string) (bool, error) {
panic("implement me")
}

func (f *FakeK8s) Node() *corev1.Node {
panic("implement me")
}
6 changes: 6 additions & 0 deletions pkg/k8s/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ type Kubernetes interface {
GetClient() client.Client

NodeName() string

Node() *corev1.Node
}

// NewK8S return Kubernetes service by pod spec and daemon mode
Expand Down Expand Up @@ -549,6 +551,10 @@ func (k *k8s) NodeName() string {
return k.nodeName
}

func (k *k8s) Node() *corev1.Node {
return k.node
}

func podNetworkType(daemonMode string, pod *corev1.Pod) string {
switch daemonMode {
case daemon.ModeENIMultiIP:
Expand Down
20 changes: 20 additions & 0 deletions pkg/k8s/mocks/Kubernetes.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading