Skip to content

Commit

Permalink
0.4.2 - fix existing IAM support (#65)
Browse files Browse the repository at this point in the history
* add instanceProfileName - 0.4.2

* fix test

* Update instancegroup_types.go
  • Loading branch information
eytan-avisror authored Nov 12, 2019
1 parent 3c9d62f commit 26126d0
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 44 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [v0.4.2-alpha] - 2019-12-07

### Fixed

- Support scenarios where existing IAM role is different than existing instance profile (#64)

## [v0.4.1-alpha] - 2019-11-07

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![codecov](https://codecov.io/gh/keikoproj/instance-manager/branch/master/graph/badge.svg)](https://codecov.io/gh/keikoproj/instance-manager)
[![Go Report Card](https://goreportcard.com/badge/github.com/keikoproj/instance-manager)](https://goreportcard.com/report/github.com/keikoproj/instance-manager)
[![slack](https://img.shields.io/badge/slack-join%20the%20conversation-ff69b4.svg)][SlackUrl]
![version](https://img.shields.io/badge/version-0.4.1-blue.svg?cacheSeconds=2592000)
![version](https://img.shields.io/badge/version-0.4.2-blue.svg?cacheSeconds=2592000)
> Create and manage instance groups with Kubernetes.
instance-manager simplifies the creation of worker nodes from within a Kubernetes cluster, create `InstanceGroup` objects in your cluster and instance-manager will provision the actual machines and bootstrap them to the cluster.
Expand Down
38 changes: 24 additions & 14 deletions api/v1alpha1/instancegroup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,19 +163,20 @@ type EKSCFSpec struct {

// EKSCFConfiguration defines the context of an AWS Instance Group using EKSCF
type EKSCFConfiguration struct {
EksClusterName string `json:"clusterName,omitempty"`
KeyPairName string `json:"keyPairName"`
Image string `json:"image"`
InstanceType string `json:"instanceType"`
NodeSecurityGroups []string `json:"securityGroups"`
VolSize int32 `json:"volSize,omitempty"`
Subnets []string `json:"subnets,omitempty"`
BootstrapArguments string `json:"bootstrapArguments,omitempty"`
SpotPrice string `json:"spotPrice,omitempty"`
Tags []map[string]string `json:"tags,omitempty"`
ExistingRoleName string `json:"roleName,omitempty"`
ManagedPolicies []string `json:"managedPolicies,omitempty"`
MetricsCollection []string `json:"metricsCollection,omitempty"`
EksClusterName string `json:"clusterName,omitempty"`
KeyPairName string `json:"keyPairName"`
Image string `json:"image"`
InstanceType string `json:"instanceType"`
NodeSecurityGroups []string `json:"securityGroups"`
VolSize int32 `json:"volSize,omitempty"`
Subnets []string `json:"subnets,omitempty"`
BootstrapArguments string `json:"bootstrapArguments,omitempty"`
SpotPrice string `json:"spotPrice,omitempty"`
Tags []map[string]string `json:"tags,omitempty"`
ExistingRoleName string `json:"roleName,omitempty"`
ExistingInstanceProfileName string `json:"instanceProfileName,omitempty"`
ManagedPolicies []string `json:"managedPolicies,omitempty"`
MetricsCollection []string `json:"metricsCollection,omitempty"`
}

// InstanceGroupStatus defines the schema of resource Status
Expand All @@ -188,7 +189,7 @@ type InstanceGroupStatus struct {
ActiveScalingGroupName string `json:"activeScalingGroupName,omitempty"`
NodesArn string `json:"nodesInstanceRoleArn,omitempty"`
StrategyResourceName string `json:"strategyResourceName,omitempty"`
UsingSpotRecommendation bool `json:"usingSpotRecommendation"`
UsingSpotRecommendation bool `json:"usingSpotRecommendation,omitempty"`
Lifecycle string `json:"lifecycle,omitempty"`
}

Expand Down Expand Up @@ -427,6 +428,15 @@ func (conf *EKSCFConfiguration) GetRoleName() string {
func (conf *EKSCFConfiguration) SetRoleName(role string) {
conf.ExistingRoleName = role
}

func (conf *EKSCFConfiguration) GetInstanceProfileName() string {
return conf.ExistingInstanceProfileName
}

func (conf *EKSCFConfiguration) SetInstanceProfileName(profile string) {
conf.ExistingInstanceProfileName = profile
}

func (conf *EKSCFConfiguration) GetTags() []map[string]string {
return conf.Tags
}
Expand Down
6 changes: 5 additions & 1 deletion config/crd/bases/instance-manager-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ data:
Description: Optional - an existing iam role name to use for bootstrapping
Type: String
Default: ""
ExistingInstanceProfileName:
Description: Optional - an existing iam instance profile name to use for bootstrapping
Type: String
Default: ""
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
Expand Down Expand Up @@ -225,7 +229,7 @@ data:
{{if not .Spec.EKSCFSpec.EKSCFConfiguration.ExistingRoleName}}
IamInstanceProfile: !Ref NodeInstanceProfile
{{else}}
IamInstanceProfile: !Ref ExistingRoleName
IamInstanceProfile: !Ref ExistingInstanceProfileName
{{end}}
ImageId: !Ref NodeImageId
InstanceType: !Ref NodeInstanceType
Expand Down
4 changes: 2 additions & 2 deletions config/crd/bases/instancemgr.keikoproj.io_instancegroups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,8 @@ spec:
type: string
image:
type: string
instanceProfileName:
type: string
instanceType:
type: string
keyPairName:
Expand Down Expand Up @@ -549,8 +551,6 @@ spec:
type: string
usingSpotRecommendation:
type: boolean
required:
- usingSpotRecommendation
type: object
required:
- metadata
Expand Down
29 changes: 22 additions & 7 deletions controllers/provisioners/ekscloudformation/ekscloudformation.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"

"regexp"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/keikoproj/instance-manager/api/v1alpha1"
"github.com/keikoproj/instance-manager/controllers/common"
awsprovider "github.com/keikoproj/instance-manager/controllers/providers/aws"
"github.com/sirupsen/logrus"
"regexp"
)

var (
Expand Down Expand Up @@ -463,6 +464,8 @@ func (ctx *EksCfInstanceGroupContext) processParameters() error {
specConfig.SetVolSize(32)
}

existingRole, existingProfile := getExistingIAM(specConfig.GetRoleName(), specConfig.GetInstanceProfileName())

rawParameters := map[string]string{
"KeyName": specConfig.GetKeyName(),
"NodeImageId": specConfig.GetImage(),
Expand All @@ -478,7 +481,8 @@ func (ctx *EksCfInstanceGroupContext) processParameters() error {
"VpcId": ctx.VpcID,
"ManagedPolicyARNs": getManagedPolicyARNs(specConfig.ManagedPolicies),
"NodeAutoScalingGroupMetrics": getNodeAutoScalingGroupMetrics(specConfig.GetMetricsCollection()),
"ExistingRoleName": getExistingRoleName(specConfig.GetRoleName()),
"ExistingRoleName": existingRole,
"ExistingInstanceProfileName": existingProfile,
}

var parameters []*cloudformation.Parameter
Expand All @@ -494,22 +498,33 @@ func (ctx *EksCfInstanceGroupContext) processParameters() error {
return nil
}

func getExistingRoleName(role string) string {
func getExistingIAM(role, profile string) (string, string) {
var (
rolePrefix = ":role/"
existingRoleName string
rolePrefix = ":role/"
instanceProfilePrefix = ":instance-profile/"
existingRoleName, existingInstanceProfileName string
)

if role == "" {
return role
return "", ""
} else if strings.Contains(role, rolePrefix) {
trim := strings.Split(role, rolePrefix)
existingRoleName = trim[1]
} else {
existingRoleName = role
}

return existingRoleName
if profile == "" {
// use role name if profile not provided
existingInstanceProfileName = existingRoleName
} else if strings.Contains(profile, instanceProfilePrefix) {
trim := strings.Split(profile, instanceProfilePrefix)
existingInstanceProfileName = trim[1]
} else {
existingInstanceProfileName = profile
}

return existingRoleName, existingInstanceProfileName
}

// getManagedPolicyARNs constructs managed policy arns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1205,33 +1205,58 @@ defaultArns:
}
}

func TestGetExistingRoleName(t *testing.T) {
func TestGetExistingIAM(t *testing.T) {
tt := []struct {
testCase string
input string
expected string
testCase string
inputRole string
inputProfile string
expectedRole string
expectedProfile string
}{
{
testCase: "custom resource creation with no/empty role",
input: "",
expected: "",
testCase: "custom resource creation with no/empty role",
inputRole: "",
inputProfile: "",
expectedRole: "",
expectedProfile: "",
},
{
testCase: "custom resource creation with profile only",
inputRole: "",
inputProfile: "arn:aws:iam::0123456789123:instance-profile/some-profile",
expectedRole: "",
expectedProfile: "",
},
{
testCase: "custom resource creation with an existing role with prefix",
input: "arn:aws:iam::0123456789123:role/some-role",
expected: "some-role",
testCase: "custom resource creation with an existing role with prefix",
inputRole: "arn:aws:iam::0123456789123:role/some-role",
inputProfile: "",
expectedRole: "some-role",
expectedProfile: "some-role",
},
{
testCase: "custom resource creation with an existing role without prefix",
input: "some-role",
expected: "some-role",
testCase: "custom resource creation with an existing role with prefix",
inputRole: "arn:aws:iam::0123456789123:role/some-role",
inputProfile: "arn:aws:iam::0123456789123:instance-profile/some-profile",
expectedRole: "some-role",
expectedProfile: "some-profile",
},
{
testCase: "custom resource creation with an existing role without prefix",
inputRole: "some-role",
inputProfile: "some-profile",
expectedRole: "some-role",
expectedProfile: "some-profile",
},
}

for _, tc := range tt {
resp := getExistingRoleName(tc.input)
if !reflect.DeepEqual(resp, tc.expected) {
t.Fatalf("Test Case [%s] Failed Expected [%s] Got [%s]\n", tc.testCase, tc.expected, resp)
respRole, respProfile := getExistingIAM(tc.inputRole, tc.inputProfile)
if !reflect.DeepEqual(respRole, tc.expectedRole) {
t.Fatalf("Test Case [%s] Failed Expected Role [%s] Got [%s]\n", tc.testCase, tc.expectedRole, respRole)
}
if !reflect.DeepEqual(respProfile, tc.expectedProfile) {
t.Fatalf("Test Case [%s] Failed Expected Profile [%s] Got [%s]\n", tc.testCase, tc.expectedProfile, respProfile)
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions docs/04_instance-manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ spec:
type: string
image:
type: string
instanceProfileName:
type: string
instanceType:
type: string
keyPairName:
Expand Down Expand Up @@ -553,8 +555,6 @@ spec:
type: string
usingSpotRecommendation:
type: boolean
required:
- usingSpotRecommendation
type: object
required:
- metadata
Expand Down Expand Up @@ -788,6 +788,10 @@ data:
Description: Optional - an existing iam role name to use for bootstrapping
Type: String
Default: ""
ExistingInstanceProfileName:
Description: Optional - an existing iam instance profile name to use for bootstrapping
Type: String
Default: ""
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
Expand Down Expand Up @@ -895,7 +899,7 @@ data:
{{if not .Spec.EKSCFSpec.EKSCFConfiguration.ExistingRoleName}}
IamInstanceProfile: !Ref NodeInstanceProfile
{{else}}
IamInstanceProfile: !Ref ExistingRoleName
IamInstanceProfile: !Ref ExistingInstanceProfileName
{{end}}
ImageId: !Ref NodeImageId
InstanceType: !Ref NodeInstanceType
Expand Down Expand Up @@ -940,6 +944,7 @@ data:
LaunchConfigName:
Description: The launch config name
Value: !Ref NodeLaunchConfig
---
apiVersion: extensions/v1beta1
kind: Deployment
Expand Down

0 comments on commit 26126d0

Please sign in to comment.