Skip to content

Commit

Permalink
Merge pull request #528 from tiffanyfay/cfgmap-ng-delete
Browse files Browse the repository at this point in the history
Cfgmap ng delete
  • Loading branch information
errordeveloper authored Feb 14, 2019
2 parents 281eaef + 406357a commit 163e361
Show file tree
Hide file tree
Showing 13 changed files with 376 additions and 178 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ eksctl get nodegroup --cluster=<clusterName> [--name=<nodegroupName>]
A nodegroup can be scaled by using the `eksctl scale nodegroup` command:

```
eksctl delete nodegroup --cluster=<clusterName> --nodes=<desiredCount> --name=<nodegroupName>
eksctl scale nodegroup --cluster=<clusterName> --nodes=<desiredCount> --name=<nodegroupName>
```

For example, to scale nodegroup `ng-a345f4e1` in `cluster-1` to 5 nodes, run:
Expand Down
1 change: 1 addition & 0 deletions humans.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Daniel Herman @dcherman
Takuma Hashimoto @af12066
Yasuhiro Hara @toricls
Jiaxin Shan @Jeffwan
tiffany jernigan @tiffanyfay

/* Thanks */

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

import (
"fmt"

"github.com/kris-nova/logger"
"github.com/pkg/errors"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha4"

yaml "gopkg.in/yaml.v2"

corev1 "k8s.io/api/core/v1"
kerr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
)

type mapRolesData []map[string]interface{}

const (
objectName = "aws-auth"
objectNamespace = "kube-system"
)

// ObjectMeta constructs metadata for the configmap
func ObjectMeta() metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: objectName,
Namespace: objectNamespace,
}
}

func makeMapRolesData() mapRolesData { return []map[string]interface{}{} }

func appendNodeRole(mapRoles *mapRolesData, ngInstanceRoleARN string) {
newEntry := map[string]interface{}{
"rolearn": ngInstanceRoleARN,
"username": "system:node:{{EC2PrivateDNSName}}",
"groups": []string{
"system:bootstrappers",
"system:nodes",
},
}
*mapRoles = append(*mapRoles, newEntry)
}

func new(mapRoles *mapRolesData) (*corev1.ConfigMap, error) {
mapRolesBytes, err := yaml.Marshal(*mapRoles)
if err != nil {
return nil, err
}
cm := &corev1.ConfigMap{
ObjectMeta: ObjectMeta(),
Data: map[string]string{
"mapRoles": string(mapRolesBytes),
},
}
return cm, nil
}

func update(cm *corev1.ConfigMap, mapRoles *mapRolesData) error {
mapRolesBytes, err := yaml.Marshal(*mapRoles)
if err != nil {
return err
}
cm.Data["mapRoles"] = string(mapRolesBytes)
return nil
}

// NewForRole creates ConfigMap with a single role ARN
func NewForRole(arn string) (*corev1.ConfigMap, error) {
mapRoles := makeMapRolesData()
appendNodeRole(&mapRoles, arn)
return new(&mapRoles)
}

// AddRole updates ConfigMap by appending a single nodegroup ARN
func AddRole(cm *corev1.ConfigMap, arn string) error {
mapRoles := makeMapRolesData()
if err := yaml.Unmarshal([]byte(cm.Data["mapRoles"]), &mapRoles); err != nil {
return err
}
appendNodeRole(&mapRoles, arn)
return update(cm, &mapRoles)
}

// AddNodeGroup creates or adds a nodegroup IAM role in the auth config map for the given nodegroup
func AddNodeGroup(clientSet *clientset.Clientset, ng *api.NodeGroup) error {
cm := &corev1.ConfigMap{}
client := clientSet.CoreV1().ConfigMaps(objectNamespace)
create := false

// check if object exists
if existing, err := client.Get(objectName, metav1.GetOptions{}); err != nil {
if kerr.IsNotFound(err) {
create = true // doesn't exsits, will create
} else {
// something must have gone terribly wrong
return errors.Wrapf(err, "getting auth ConfigMap")
}
} else {
*cm = *existing // use existing object
}

if create {
// build new object with the given role
cm, err := NewForRole(ng.IAM.InstanceRoleARN)
if err != nil {
return errors.Wrap(err, "constructing auth ConfigMap")
}
// and create it in the cluster
if _, err := client.Create(cm); err != nil {
return errors.Wrap(err, "creating auth ConfigMap")
}
logger.Debug("created auth ConfigMap for %s", ng.Name)
return nil
}

// in case we already have an onject, and the given role to it
if err := AddRole(cm, ng.IAM.InstanceRoleARN); err != nil {
return errors.Wrap(err, "creating an update for auth ConfigMap")
}
// and update it in the cluster
if _, err := client.Update(cm); err != nil {
return errors.Wrap(err, "updating auth ConfigMap")
}
logger.Debug("updated auth ConfigMap for %s", ng.Name)
return nil
}

func doRemoveRole(mapRoles *mapRolesData, arn string) (mapRolesData, error) {
found := false
mapRolesUpdated := makeMapRolesData()

for _, role := range *mapRoles {
if role["rolearn"] == arn && !found {
found = true
logger.Info("removing %s from config map", arn)
} else {
mapRolesUpdated = append(mapRolesUpdated, role)
}
}

if !found {
return nil, fmt.Errorf("instance role ARN %s not found in config map", arn)
}

return mapRolesUpdated, nil
}

// RemoveRole removes a nodegroup's instance mapped role from the config map
func RemoveRole(cm *corev1.ConfigMap, ngInstanceRoleARN string) error {
if ngInstanceRoleARN == "" {
return errors.New("config map is unchanged as the nodegroup instance ARN is not set")
}
mapRoles := makeMapRolesData()

if err := yaml.Unmarshal([]byte(cm.Data["mapRoles"]), &mapRoles); err != nil {
return err
}
mapRolesUpdated, err := doRemoveRole(&mapRoles, ngInstanceRoleARN)
if err != nil {
return err
}
return update(cm, &mapRolesUpdated)
}

// RemoveNodeGroup removes a nodegroup from the config map and does a client update
func RemoveNodeGroup(clientSet *clientset.Clientset, ng *api.NodeGroup) error {
client := clientSet.CoreV1().ConfigMaps(objectNamespace)

cm, err := client.Get(objectName, metav1.GetOptions{})
if err != nil {
return errors.Wrapf(err, "getting auth ConfigMap")
}

if err := RemoveRole(cm, ng.IAM.InstanceRoleARN); err != nil {
return errors.Wrapf(err, "removing nodegroup from auth ConfigMap")
}
if _, err := client.Update(cm); err != nil {
return errors.Wrap(err, "updating auth ConfigMap and removing instance role")
}
logger.Debug("updated auth ConfigMap for %s", ng.Name)
return nil
}
11 changes: 11 additions & 0 deletions pkg/authconfigmap/authconfigmap_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package authconfigmap_test

import (
"testing"

"github.com/weaveworks/eksctl/pkg/testutils"
)

func TestSuite(t *testing.T) {
testutils.RegisterAndRun(t)
}
95 changes: 95 additions & 0 deletions pkg/authconfigmap/authconfigmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package authconfigmap_test

import (
"strings"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

. "github.com/weaveworks/eksctl/pkg/authconfigmap"
)

var _ = Describe("Auth ConfigMap", func() {
Describe("create new ConfigMap", func() {

cm, err := NewForRole("arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng1-p-NodeInstanceRole-NNH3ISP12CX")

expected := []string{
`- username: 'system:node:{{EC2PrivateDNSName}}'`,
` groups: [ 'system:bootstrappers', 'system:nodes' ]`,
` rolearn: 'arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng1-p-NodeInstanceRole-NNH3ISP12CX'`,
}

It("should create correct configuration for a new nodegroup", func() {
Expect(err).To(Not(HaveOccurred()))
Expect(cm).To(Not(BeNil()))

Expect(cm.ObjectMeta).To(Equal(ObjectMeta()))

Expect(cm.Data).To(HaveKey("mapRoles"))
Expect(cm.Data["mapRoles"]).To(MatchYAML(strings.Join(expected, "\n")))
})
It("should add a new node group ARN to the configmap", func() {
err = AddRole(cm, "arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng2-p-NodeInstanceRole-1L35GCVYSTW4E")

Expect(err).To(Not(HaveOccurred()))
Expect(cm).To(Not(BeNil()))

expected = append(expected,
`- username: 'system:node:{{EC2PrivateDNSName}}'`,
` groups: [ 'system:bootstrappers', 'system:nodes' ]`,
` rolearn: 'arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng2-p-NodeInstanceRole-1L35GCVYSTW4E'`,
)

Expect(cm.ObjectMeta).To(Equal(ObjectMeta()))

Expect(cm.Data).To(HaveKey("mapRoles"))
Expect(cm.Data["mapRoles"]).To(MatchYAML(strings.Join(expected, "\n")))
})

It("should remove arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng2-p-NodeInstanceRole-1L35GCVYSTW4E from ConfigMap", func() {
err = RemoveRole(cm, "arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng2-p-NodeInstanceRole-1L35GCVYSTW4E")

Expect(err).To(Not(HaveOccurred()))
Expect(cm).To(Not(BeNil()))

expected = []string{
`- username: 'system:node:{{EC2PrivateDNSName}}'`,
` groups: [ 'system:bootstrappers', 'system:nodes' ]`,
` rolearn: 'arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng1-p-NodeInstanceRole-NNH3ISP12CX'`,
}

Expect(cm.ObjectMeta).To(Equal(ObjectMeta()))

Expect(cm.Data).To(HaveKey("mapRoles"))
Expect(cm.Data["mapRoles"]).To(MatchYAML(strings.Join(expected, "\n")))
})

It("should fail if an role ARN is is not in the config map", func() {
err = RemoveRole(cm, "arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng1-p-NodeInstanceRole-ABCDEFGH'")

Expect(err).To(HaveOccurred())
Expect(cm.Data["mapRoles"]).To(MatchYAML(strings.Join(expected, "\n")))
})

It("should remove arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng1-p-NodeInstanceRole-NNH3ISP12CX and make mapRoles be []", func() {
err = RemoveRole(cm, "arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng1-p-NodeInstanceRole-NNH3ISP12CX")

Expect(err).To(Not(HaveOccurred()))
Expect(cm).To(Not(BeNil()))

Expect(cm.ObjectMeta).To(Equal(ObjectMeta()))

Expect(cm.Data).To(HaveKey("mapRoles"))
Expect(cm.Data["mapRoles"]).To(MatchYAML("[]"))
})

It("should fail if you try removing a role when the mapRole is empty", func() {
err = RemoveRole(cm, "arn:aws:iam::122333:role/eksctl-cluster-5a-nodegroup-ng1-p-NodeInstanceRole-ABCDEFGH'")

Expect(err).To(HaveOccurred())
Expect(cm.Data).To(HaveKey("mapRoles"))
Expect(cm.Data["mapRoles"]).To(MatchYAML("[]"))
})
})
})
2 changes: 1 addition & 1 deletion pkg/cfn/manager/nodegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (c *StackCollection) GetNodeGroupSummaries(name string) ([]*NodeGroupSummar
for _, s := range stacks {
summary, err := c.mapStackToNodeGroupSummary(s)
if err != nil {
return nil, errors.Wrap(err, "mapping stack to nodegorup summary")
return nil, errors.Wrap(err, "mapping stack to nodegroup summary")
}

if name == "" {
Expand Down
3 changes: 2 additions & 1 deletion pkg/ctl/create/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/weaveworks/eksctl/pkg/ami"
api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha4"
"github.com/weaveworks/eksctl/pkg/authconfigmap"
"github.com/weaveworks/eksctl/pkg/ctl/cmdutils"
"github.com/weaveworks/eksctl/pkg/eks"
"github.com/weaveworks/eksctl/pkg/kops"
Expand Down Expand Up @@ -509,7 +510,7 @@ func doCreateCluster(p *api.ProviderConfig, cfg *api.ClusterConfig, nameArg stri

err = checkEachNodeGroup(cfg, func(_ int, ng *api.NodeGroup) error {
// authorise nodes to join
if err = ctl.CreateOrUpdateNodeGroupAuthConfigMap(clientSet, ng); err != nil {
if err = authconfigmap.AddNodeGroup(clientSet, ng); err != nil {
return err
}

Expand Down
5 changes: 3 additions & 2 deletions pkg/ctl/create/nodegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/spf13/pflag"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha4"
"github.com/weaveworks/eksctl/pkg/authconfigmap"
"github.com/weaveworks/eksctl/pkg/ctl/cmdutils"
"github.com/weaveworks/eksctl/pkg/eks"
"github.com/weaveworks/eksctl/pkg/printers"
Expand Down Expand Up @@ -123,7 +124,7 @@ func doCreateNodeGroup(p *api.ProviderConfig, cfg *api.ClusterConfig, ng *api.No
}

{
logger.Info("will create a Cloudformation stack for nodegroup %s in cluster %s", ng.Name, cfg.Metadata.Name)
logger.Info("will create a CloudFormation stack for nodegroup %s in cluster %s", ng.Name, cfg.Metadata.Name)
errs := stackManager.CreateOneNodeGroup(ng)
if len(errs) > 0 {
logger.Info("%d error(s) occurred and nodegroup hasn't been created properly, you may wish to check CloudFormation console", len(errs))
Expand Down Expand Up @@ -151,7 +152,7 @@ func doCreateNodeGroup(p *api.ProviderConfig, cfg *api.ClusterConfig, ng *api.No
}

// authorise nodes to join
if err = ctl.CreateOrUpdateNodeGroupAuthConfigMap(clientSet, ng); err != nil {
if err = authconfigmap.AddNodeGroup(clientSet, ng); err != nil {
return err
}

Expand Down
Loading

0 comments on commit 163e361

Please sign in to comment.