Skip to content

Commit

Permalink
feat: look up official EKS AMI when appropriate
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Rudoi committed Aug 31, 2020
1 parent e7ffeaa commit 27c055d
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ func (t Template) controllersPolicy() *iamv1.PolicyDocument {
"iam:GetRole",
"iam:ListAttachedRolePolicies",
}
statement = append(statement, iamv1.StatementEntry{
Effect: iamv1.EffectAllow,
Resource: iamv1.Resources{
"arn:aws:ssm:*:*:parameter/aws/service/eks/optimized-ami/*",
},
Action: iamv1.Actions{
"ssm:GetParameter",
},
})

if t.Spec.ClusterAPIControllers.EKS.IAMRoleCreation {
allowedIAMActions = append(allowedIAMActions, iamv1.Actions{
"iam:DetachRolePolicy",
Expand Down Expand Up @@ -206,6 +216,7 @@ func (t Template) controllersPolicy() *iamv1.PolicyDocument {
},
}...)
}

return &iamv1.PolicyDocument{
Version: iamv1.CurrentVersion,
Statement: statement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ Resources:
Effect: Allow
Resource:
- arn:*:secretsmanager:*:*:secret:aws.cluster.x-k8s.io/*
- Action:
- ssm:GetParameter
Effect: Allow
Resource:
- arn:aws:ssm:*:*:parameter/aws/service/eks/optimized-ami/*
- Action:
- iam:GetRole
- iam:ListAttachedRolePolicies
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.13
require (
github.com/aws/aws-sdk-go v1.34.10
github.com/awslabs/goformation/v4 v4.11.0
github.com/blang/semver v3.5.1+incompatible
github.com/go-logr/logr v0.1.0
github.com/golang/mock v1.4.3
github.com/google/goexpect v0.0.0-20200703111054-623d5ca06f56
Expand Down
12 changes: 12 additions & 0 deletions pkg/cloud/scope/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/aws-sdk-go/service/sts/stsiface"

Expand Down Expand Up @@ -112,6 +114,16 @@ func NewSTSClient(scopeUser cloud.ScopeUsage, session cloud.Session, target runt
return stsClient
}

// NewSSMClient creates a new Secrets API client for a given session
func NewSSMClient(scopeUser cloud.ScopeUsage, session cloud.Session, target runtime.Object) ssmiface.SSMAPI {
ssmClient := ssm.New(session.Session())
ssmClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
ssmClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
ssmClient.Handlers.Complete.PushBack(recordAWSPermissionsIssue(target))

return ssmClient
}

func recordAWSPermissionsIssue(target runtime.Object) func(r *request.Request) {
return func(r *request.Request) {
if awsErr, ok := r.Error.(awserr.Error); ok {
Expand Down
41 changes: 41 additions & 0 deletions pkg/cloud/services/ec2/ami.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ package ec2

import (
"bytes"
"fmt"
"sort"
"strings"
"text/template"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/blang/semver"
"github.com/pkg/errors"
"sigs.k8s.io/cluster-api-provider-aws/pkg/record"
)
Expand All @@ -48,6 +51,9 @@ const (

// Amazon's AMI timestamp format
createDateTimestampFormat = "2006-01-02T15:04:05.000Z"

// EKS AMI ID SSM Parameter name
eksAmiSSMParameterFormat = "/aws/service/eks/optimized-ami/%s/amazon-linux-2/recommended/image_id"
)

// AMILookup contains the parameters used to template AMI names used for lookup.
Expand Down Expand Up @@ -202,3 +208,38 @@ func (s *Service) defaultBastionAMILookup(region string) string {
return "unknown region"
}
}

func (s *Service) eksAMILookup(kubernetesVersion string) (string, error) {
// format ssm parameter path properly
formattedVersion, err := formatVersionForEKS(kubernetesVersion)
if err != nil {
return "", err
}

paramName := fmt.Sprintf(eksAmiSSMParameterFormat, formattedVersion)

input := &ssm.GetParameterInput{
Name: aws.String(paramName),
}

out, err := s.SSMClient.GetParameter(input)
if err != nil {
record.Eventf(s.scope.InfraCluster(), "FailedGetParameter", "Failed to get ami SSM parameter %q: %v", paramName, err)
return "", errors.Wrapf(err, "failed to get ami SSM parameter: %q", paramName)
}

if out.Parameter.Value == nil {
return "", errors.Errorf("SSM parameter returned with nil value: %q", paramName)
}

return aws.StringValue(out.Parameter.Value), nil
}

func formatVersionForEKS(version string) (string, error) {
parsed, err := semver.ParseTolerant(version)
if err != nil {
return "", err
}

return fmt.Sprintf("%d.%d", parsed.Major, parsed.Minor), nil
}
43 changes: 28 additions & 15 deletions pkg/cloud/services/ec2/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,24 +140,31 @@ func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte) (*i
return nil, err
}

imageLookupFormat := scope.AWSMachine.Spec.ImageLookupFormat
if imageLookupFormat == "" {
imageLookupFormat = scope.InfraCluster.ImageLookupFormat()
}
if s.shouldUseEksAMI() {
input.ImageID, err = s.eksAMILookup(*scope.Machine.Spec.Version)
if err != nil {
return nil, err
}
} else {
imageLookupFormat := scope.AWSMachine.Spec.ImageLookupFormat
if imageLookupFormat == "" {
imageLookupFormat = scope.InfraCluster.ImageLookupFormat()
}

imageLookupOrg := scope.AWSMachine.Spec.ImageLookupOrg
if imageLookupOrg == "" {
imageLookupOrg = scope.InfraCluster.ImageLookupOrg()
}
imageLookupOrg := scope.AWSMachine.Spec.ImageLookupOrg
if imageLookupOrg == "" {
imageLookupOrg = scope.InfraCluster.ImageLookupOrg()
}

imageLookupBaseOS := scope.AWSMachine.Spec.ImageLookupBaseOS
if imageLookupBaseOS == "" {
imageLookupBaseOS = scope.InfraCluster.ImageLookupBaseOS()
}
imageLookupBaseOS := scope.AWSMachine.Spec.ImageLookupBaseOS
if imageLookupBaseOS == "" {
imageLookupBaseOS = scope.InfraCluster.ImageLookupBaseOS()
}

input.ImageID, err = s.defaultAMILookup(imageLookupFormat, imageLookupOrg, imageLookupBaseOS, *scope.Machine.Spec.Version)
if err != nil {
return nil, err
input.ImageID, err = s.defaultAMILookup(imageLookupFormat, imageLookupOrg, imageLookupBaseOS, *scope.Machine.Spec.Version)
if err != nil {
return nil, err
}
}
}

Expand Down Expand Up @@ -812,6 +819,12 @@ func (s *Service) DetachSecurityGroupsFromNetworkInterface(groups []string, inte
return nil
}

func (s *Service) shouldUseEksAMI() bool {
gvk := s.scope.InfraCluster().GetObjectKind().GroupVersionKind()

return gvk.Kind == "AWSManagedControlPlane"
}

// filterGroups filters a list for a string.
func filterGroups(list []string, strToFilter string) (newList []string) {
for _, item := range list {
Expand Down
5 changes: 5 additions & 0 deletions pkg/cloud/services/ec2/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package ec2

import (
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"

"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
)
Expand All @@ -28,12 +29,16 @@ import (
type Service struct {
scope scope.EC2Scope
EC2Client ec2iface.EC2API

// SSMClient is used to look up the official EKS AMI ID
SSMClient ssmiface.SSMAPI
}

// NewService returns a new service given the ec2 api client.
func NewService(clusterScope scope.EC2Scope) *Service {
return &Service{
scope: clusterScope,
EC2Client: scope.NewEC2Client(clusterScope, clusterScope, clusterScope.InfraCluster()),
SSMClient: scope.NewSSMClient(clusterScope, clusterScope, clusterScope.InfraCluster()),
}
}

0 comments on commit 27c055d

Please sign in to comment.