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

nodeadm: retry IMDS 404 errors #1970

Merged
merged 1 commit into from
Oct 8, 2024
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
16 changes: 10 additions & 6 deletions nodeadm/cmd/nodeadm/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import (

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/integrii/flaggy"
"go.uber.org/zap"
"k8s.io/utils/strings/slices"

"github.com/awslabs/amazon-eks-ami/nodeadm/internal/api"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/aws/ecr"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/aws/imds"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/cli"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/configprovider"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/containerd"
Expand Down Expand Up @@ -146,14 +146,18 @@ func (c *initCmd) Run(log *zap.Logger, opts *cli.GlobalOptions) error {
// perform in-place updates when allowed by the user
func enrichConfig(log *zap.Logger, cfg *api.NodeConfig) error {
log.Info("Fetching instance details..")
imdsClient := imds.New(imds.Options{})
awsConfig, err := config.LoadDefaultConfig(context.TODO(), config.WithClientLogMode(aws.LogRetries), config.WithEC2IMDSRegion(func(o *config.UseEC2IMDSRegion) {
o.Client = imdsClient
}))
awsConfig, err := config.LoadDefaultConfig(context.TODO(),
config.WithClientLogMode(aws.LogRetries),
config.WithEC2IMDSRegion(func(o *config.UseEC2IMDSRegion) {
// Use our pre-configured IMDS client to avoid hitting common retry
// issues with the default config.
o.Client = imds.Client
}),
)
if err != nil {
return err
}
instanceDetails, err := api.GetInstanceDetails(context.TODO(), cfg.Spec.FeatureGates, imdsClient, ec2.NewFromConfig(awsConfig))
instanceDetails, err := api.GetInstanceDetails(context.TODO(), cfg.Spec.FeatureGates, ec2.NewFromConfig(awsConfig))
if err != nil {
return err
}
Expand Down
15 changes: 5 additions & 10 deletions nodeadm/internal/api/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,29 @@ package api
import (
"context"
"fmt"
"io"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2extra "github.com/awslabs/amazon-eks-ami/nodeadm/internal/aws/ec2"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/aws/imds"
)

// Fetch information about the ec2 instance using IMDS data.
// This information is stored into the internal config to avoid redundant calls
// to IMDS when looking for instance metadata
func GetInstanceDetails(ctx context.Context, featureGates map[Feature]bool, imdsClient *imds.Client, ec2Client *ec2.Client) (*InstanceDetails, error) {
instanceIdenitityDocument, err := imdsClient.GetInstanceIdentityDocument(ctx, &imds.GetInstanceIdentityDocumentInput{})
func GetInstanceDetails(ctx context.Context, featureGates map[Feature]bool, ec2Client *ec2.Client) (*InstanceDetails, error) {
instanceIdenitityDocument, err := imds.GetInstanceIdentityDocument(ctx)
if err != nil {
return nil, err
}

macResponse, err := imdsClient.GetMetadata(ctx, &imds.GetMetadataInput{Path: "mac"})
if err != nil {
return nil, err
}
mac, err := io.ReadAll(macResponse.Content)
mac, err := imds.GetProperty(ctx, "mac")
if err != nil {
return nil, err
}

privateDNSName := ""
var privateDNSName string
if !IsFeatureEnabled(InstanceIdNodeName, featureGates) {
privateDNSName, err = getPrivateDNSName(ec2Client, instanceIdenitityDocument.InstanceID)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion nodeadm/internal/aws/ecr/ecr.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (r *ECRRegistry) GetSandboxImage() string {

func GetEKSRegistry(region string) (ECRRegistry, error) {
account, region := getEKSRegistryCoordinates(region)
servicesDomain, err := imds.GetProperty(imds.ServicesDomain)
servicesDomain, err := imds.GetProperty(context.TODO(), imds.ServicesDomain)
if err != nil {
return "", err
}
Expand Down
32 changes: 23 additions & 9 deletions nodeadm/internal/aws/imds/imds.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@ import (
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
)

var client *imds.Client
var Client *imds.Client

func init() {
client = imds.New(imds.Options{
Client = imds.New(imds.Options{
DisableDefaultTimeout: true,
Retryer: retry.NewStandard(func(so *retry.StandardOptions) {
so.MaxAttempts = 15
so.MaxBackoff = 1 * time.Second
so.Retryables = append(so.Retryables,
&retry.RetryableHTTPStatusCode{
Codes: map[int]struct{}{
// Retry 404s due to the rare occurrence that
// credentials take longer to propagate through IMDS and
// fail on the first call.
404: {},
},
},
)
}),
})
}
Expand All @@ -27,24 +37,28 @@ const (
ServicesDomain IMDSProperty = "services/domain"
)

func GetUserData() ([]byte, error) {
resp, err := client.GetUserData(context.TODO(), &imds.GetUserDataInput{})
func GetInstanceIdentityDocument(ctx context.Context) (*imds.GetInstanceIdentityDocumentOutput, error) {
return Client.GetInstanceIdentityDocument(ctx, &imds.GetInstanceIdentityDocumentInput{})
}

func GetUserData(ctx context.Context) ([]byte, error) {
res, err := Client.GetUserData(ctx, &imds.GetUserDataInput{})
if err != nil {
return nil, err
}
return io.ReadAll(resp.Content)
return io.ReadAll(res.Content)
}

func GetProperty(prop IMDSProperty) (string, error) {
bytes, err := GetPropertyBytes(prop)
func GetProperty(ctx context.Context, prop IMDSProperty) (string, error) {
bytes, err := GetPropertyBytes(ctx, prop)
if err != nil {
return "", err
}
return string(bytes), nil
}

func GetPropertyBytes(prop IMDSProperty) ([]byte, error) {
res, err := client.GetMetadata(context.TODO(), &imds.GetMetadataInput{Path: string(prop)})
func GetPropertyBytes(ctx context.Context, prop IMDSProperty) ([]byte, error) {
res, err := Client.GetMetadata(ctx, &imds.GetMetadataInput{Path: string(prop)})
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion nodeadm/internal/configprovider/userdata.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package configprovider

import (
"context"
"fmt"

"github.com/awslabs/amazon-eks-ami/nodeadm/api"
Expand All @@ -22,7 +23,7 @@ type userDataProvider interface {
type imdsUserDataProvider struct{}

func (p *imdsUserDataProvider) GetUserData() ([]byte, error) {
return imds.GetUserData()
return imds.GetUserData(context.TODO())
}

type userDataConfigProvider struct {
Expand Down
27 changes: 7 additions & 20 deletions nodeadm/internal/kubelet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
_ "embed"
"encoding/json"
"fmt"
"io"
"net"
"net/url"
"os"
Expand All @@ -20,10 +19,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8skubelet "k8s.io/kubelet/config/v1beta1"

"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/smithy-go/ptr"

"github.com/awslabs/amazon-eks-ami/nodeadm/internal/api"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/aws/imds"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/containerd"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/system"
"github.com/awslabs/amazon-eks-ami/nodeadm/internal/util"
Expand Down Expand Up @@ -203,7 +202,7 @@ func (ksc *kubeletConfig) withOutpostSetup(cfg *api.NodeConfig) error {
}

func (ksc *kubeletConfig) withNodeIp(cfg *api.NodeConfig, flags map[string]string) error {
nodeIp, err := getNodeIp(context.TODO(), imds.New(imds.Options{}), cfg)
nodeIp, err := getNodeIp(context.TODO(), cfg)
if err != nil {
return err
}
Expand Down Expand Up @@ -407,36 +406,24 @@ func getProviderId(availabilityZone, instanceId string) string {
}

// Get the IP of the node depending on the ipFamily configured for the cluster
func getNodeIp(ctx context.Context, imdsClient *imds.Client, cfg *api.NodeConfig) (string, error) {
func getNodeIp(ctx context.Context, cfg *api.NodeConfig) (string, error) {
ipFamily, err := api.GetCIDRIpFamily(cfg.Spec.Cluster.CIDR)
if err != nil {
return "", err
}
switch ipFamily {
case api.IPFamilyIPv4:
ipv4Response, err := imdsClient.GetMetadata(ctx, &imds.GetMetadataInput{
Path: "local-ipv4",
})
ipv4, err := imds.GetProperty(ctx, "local-ipv4")
if err != nil {
return "", err
}
ip, err := io.ReadAll(ipv4Response.Content)
if err != nil {
return "", err
}
return string(ip), nil
return ipv4, nil
case api.IPFamilyIPv6:
ipv6Response, err := imdsClient.GetMetadata(ctx, &imds.GetMetadataInput{
Path: fmt.Sprintf("network/interfaces/macs/%s/ipv6s", cfg.Status.Instance.MAC),
})
if err != nil {
return "", err
}
ip, err := io.ReadAll(ipv6Response.Content)
ipv6, err := imds.GetProperty(ctx, imds.IMDSProperty(fmt.Sprintf("network/interfaces/macs/%s/ipv6s", cfg.Status.Instance.MAC)))
if err != nil {
return "", err
}
return string(ip), nil
return ipv6, nil
default:
return "", fmt.Errorf("invalid ip-family. %s is not one of %v", ipFamily, []api.IPFamily{api.IPFamilyIPv4, api.IPFamilyIPv6})
}
Expand Down
Loading