Add support for deleting an InstanceGroup

This commit is contained in:
Justin Santa Barbara 2016-07-10 13:22:58 -04:00
parent c4f2fbfcaf
commit 0aed68c260
7 changed files with 242 additions and 19 deletions

View File

@ -0,0 +1,81 @@
package main
import (
"fmt"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/kutil"
)
type DeleteInstanceceGroupCmd struct {
}
var deleteInstanceceGroupCmd DeleteInstanceceGroupCmd
func init() {
cmd := &cobra.Command{
Use: "instancegroup",
Aliases: []string{"instancegroups", "ig"},
Short: "Delete instancegroup",
Long: `Delete an instancegroup configuration.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
glog.Exitf("Specify name of instance group to delete")
}
if len(args) != 1 {
glog.Exitf("Can only edit one instance group at a time!")
}
err := deleteInstanceceGroupCmd.Run(args[0])
if err != nil {
glog.Exitf("%v", err)
}
},
}
deleteCmd.AddCommand(cmd)
}
func (c *DeleteInstanceceGroupCmd) Run(groupName string) error {
if groupName == "" {
return fmt.Errorf("name is required")
}
_, cluster, err := rootCommand.Cluster()
if err != nil {
return err
}
registry, err := rootCommand.InstanceGroupRegistry()
if err != nil {
return err
}
group, err := registry.Find(groupName)
if err != nil {
return fmt.Errorf("error reading InstanceGroup %q: %v", groupName, err)
}
if group == nil {
return fmt.Errorf("InstanceGroup %q not found", groupName)
}
cloud, err := cloudup.BuildCloud(cluster)
if err != nil {
return err
}
d := &kutil.DeleteInstanceGroup{}
d.Cluster = cluster
d.Cloud = cloud
d.InstanceGroupRegistry = registry
err = d.DeleteInstanceGroup(group)
if err != nil {
return err
}
fmt.Printf("InstanceGroup %q deleted\n", group.Name)
return nil
}

View File

@ -61,11 +61,10 @@ func (c *RollingUpdateClusterCmd) Run() error {
}
d := &kutil.RollingUpdateCluster{}
d.Cluster = cluster
d.Cloud = cloud
groups, err := d.ListInstanceGroups(instancegroups)
warnUnmatched := true
groups, err := kutil.FindCloudInstanceGroups(cloud, cluster, instancegroups, warnUnmatched)
if err != nil {
return err
}
@ -84,12 +83,18 @@ func (c *RollingUpdateClusterCmd) Run() error {
t.AddColumn("READY", func(r *kutil.CloudInstanceGroup) string {
return strconv.Itoa(len(r.Ready))
})
t.AddColumn("MIN", func(r *kutil.CloudInstanceGroup) string {
return strconv.Itoa(r.MinSize())
})
t.AddColumn("MAX", func(r *kutil.CloudInstanceGroup) string {
return strconv.Itoa(r.MaxSize())
})
var l []*kutil.CloudInstanceGroup
for _, v := range groups {
l = append(l, v)
}
err := t.Render(l, os.Stdout, "NAME", "STATUS", "NEEDUPDATE", "READY")
err := t.Render(l, os.Stdout, "NAME", "STATUS", "NEEDUPDATE", "READY", "MIN", "MAX")
if err != nil {
return err
}

View File

@ -33,6 +33,7 @@ To preview the change:
> `kops create cluster --name <clustername> --dryrun`
```
...
Will modify resources:
*awstasks.LaunchConfiguration launchConfiguration/mycluster.mydomain.com
InstanceType t2.medium -> t2.large
@ -66,14 +67,54 @@ The procedure to resize an instance group works the same way:
## Creating a new instance group
Suppose you want to add a new group of nodes
TODO
Suppose you want to add a new group of nodes, perhaps with a different instance type. You do this using
`kops create ig <InstanceGroupName>`. Currently it opens an editor with a skeleton configuration, allowing
you to edit it before creation.
## Deleting an instance group
So the procedure is:
TODO
* `kops create ig morenodes`, edit and save
* Preview: `kops create cluster --name <clustername> --dryrun`
* Apply: `kops create cluster --name <clustername>`
* (no instances need to be relaunched, so no rolling-update is needed)
## Converting an instance group to use spot instances
TODO
Follow the normal procedure for reconfiguring an InstanceGroup, but set the maxPrice property to your bid.
For example, "0.10" represents a spot-price bid of $0.10 (10 cents) per hour.
Warning: the t2 family is not currently supported with spot pricing. You'll need to choose a different
instance type.
An example spec looks like this:
```
metadata:
creationTimestamp: "2016-07-10T15:47:14Z"
name: nodes
spec:
machineType: m3.medium
maxPrice: "0.1"
maxSize: 3
minSize: 3
role: Node
```
($0.10 per hour is a huge over-bid for an m3.medium - this is only an example!)
So the procedure is:
* Edit: `kops edit ig nodes`
* Preview: `kops create cluster --name <clustername> --dryrun`
* Apply: `kops create cluster --name <clustername>`
* Rolling-update, only if you want to apply changes immediately: `kops rolling-update cluster`
## Deleting an instance group
If you decide you don't need an InstanceGroup any more, you delete it using: `kops delete ig <name>`
Example: `kops delete ig morenodes`
No rolling-update is needed (and note this is not currently graceful, so there may be interruptions to
workloads where the pods are running on those nodes).

View File

@ -182,6 +182,18 @@ func (r *InstanceGroupRegistry) Find(name string) (*InstanceGroup, error) {
return group, nil
}
func (r *InstanceGroupRegistry) Delete(name string) (bool, error) {
p := r.stateStore.VFSPath().Join("instancegroup", name)
err := p.Remove()
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("error deleting instancegroup configuration %q: %v", name, err)
}
return true, nil
}
func (r *InstanceGroupRegistry) ReadAll() ([]*InstanceGroup, error) {
names, err := r.List()
if err != nil {

View File

@ -0,0 +1,38 @@
package kutil
import (
"fmt"
"k8s.io/kops/upup/pkg/api"
"k8s.io/kops/upup/pkg/fi"
)
// DeleteInstanceGroup removes the cloud resources for an InstanceGroup
type DeleteInstanceGroup struct {
Cluster *api.Cluster
Cloud fi.Cloud
InstanceGroupRegistry *api.InstanceGroupRegistry
}
func (c *DeleteInstanceGroup) DeleteInstanceGroup(group *api.InstanceGroup) error {
groups, err := FindCloudInstanceGroups(c.Cloud, c.Cluster, []*api.InstanceGroup{group}, false)
cig := groups[group.Name]
if cig == nil {
return fmt.Errorf("InstanceGroup not found in cloud")
}
if len(groups) != 1 {
return fmt.Errorf("Multiple InstanceGroup resources found in cloud")
}
err = cig.Delete(c.Cloud)
if err != nil {
return fmt.Errorf("error deleting cloud resources for InstanceGroup: %v", err)
}
_, err = c.InstanceGroupRegistry.Delete(group.Name)
if err != nil {
return err
}
return nil
}

View File

@ -15,18 +15,17 @@ import (
// RollingUpdateCluster restarts cluster nodes
type RollingUpdateCluster struct {
Cluster *api.Cluster
Cloud fi.Cloud
Cloud fi.Cloud
}
func (c *RollingUpdateCluster) ListInstanceGroups(instancegroups []*api.InstanceGroup) (map[string]*CloudInstanceGroup, error) {
cloud := c.Cloud.(*awsup.AWSCloud)
func FindCloudInstanceGroups(cloud fi.Cloud, cluster *api.Cluster, instancegroups []*api.InstanceGroup, warnUnmatched bool) (map[string]*CloudInstanceGroup, error) {
awsCloud := cloud.(*awsup.AWSCloud)
groups := make(map[string]*CloudInstanceGroup)
tags := cloud.Tags()
tags := awsCloud.Tags()
asgs, err := findAutoscalingGroups(cloud, tags)
asgs, err := findAutoscalingGroups(awsCloud, tags)
if err != nil {
return nil, err
}
@ -38,9 +37,9 @@ func (c *RollingUpdateCluster) ListInstanceGroups(instancegroups []*api.Instance
var asgName string
switch g.Spec.Role {
case api.InstanceGroupRoleMaster:
asgName = g.Name + ".masters." + c.Cluster.Name
asgName = g.Name + ".masters." + cluster.Name
case api.InstanceGroupRoleNode:
asgName = g.Name + "." + c.Cluster.Name
asgName = g.Name + "." + cluster.Name
default:
glog.Warningf("Ignoring InstanceGroup of unknown role %q", g.Spec.Role)
continue
@ -54,7 +53,9 @@ func (c *RollingUpdateCluster) ListInstanceGroups(instancegroups []*api.Instance
}
}
if instancegroup == nil {
glog.Warningf("Found ASG with no corresponding instance group: %q", name)
if warnUnmatched {
glog.Warningf("Found ASG with no corresponding instance group: %q", name)
}
continue
}
group := buildCloudInstanceGroup(instancegroup, asg)
@ -107,12 +108,23 @@ type CloudInstanceGroup struct {
Status string
Ready []*autoscaling.Instance
NeedUpdate []*autoscaling.Instance
asg *autoscaling.Group
}
func (c *CloudInstanceGroup) MinSize() int {
return int(aws.Int64Value(c.asg.MinSize))
}
func (c *CloudInstanceGroup) MaxSize() int {
return int(aws.Int64Value(c.asg.MaxSize))
}
func buildCloudInstanceGroup(ig *api.InstanceGroup, g *autoscaling.Group) *CloudInstanceGroup {
n := &CloudInstanceGroup{
ASGName: aws.StringValue(g.AutoScalingGroupName),
InstanceGroup: ig,
asg: g,
}
findLaunchConfigurationName := aws.StringValue(g.LaunchConfigurationName)
@ -163,6 +175,41 @@ func (n *CloudInstanceGroup) RollingUpdate(cloud fi.Cloud) error {
return nil
}
func (g *CloudInstanceGroup) Delete(cloud fi.Cloud) error {
c := cloud.(*awsup.AWSCloud)
// TODO: Graceful?
// Delete ASG
{
asgName := aws.StringValue(g.asg.AutoScalingGroupName)
glog.V(2).Infof("Deleting autoscaling group %q", asgName)
request := &autoscaling.DeleteAutoScalingGroupInput{
AutoScalingGroupName: g.asg.AutoScalingGroupName,
ForceDelete: aws.Bool(true),
}
_, err := c.Autoscaling.DeleteAutoScalingGroup(request)
if err != nil {
return fmt.Errorf("error deleting autoscaling group %q: %v", asgName, err)
}
}
// Delete LaunchConfig
{
lcName := aws.StringValue(g.asg.LaunchConfigurationName)
glog.V(2).Infof("Deleting autoscaling launch configuration %q", lcName)
request := &autoscaling.DeleteLaunchConfigurationInput{
LaunchConfigurationName: g.asg.LaunchConfigurationName,
}
_, err := c.Autoscaling.DeleteLaunchConfiguration(request)
if err != nil {
return fmt.Errorf("error deleting autoscaling launch configuration %q: %v", lcName, err)
}
}
return nil
}
func (n *CloudInstanceGroup) String() string {
return "CloudInstanceGroup:" + n.ASGName
}

View File

@ -43,7 +43,6 @@ func findAutoscalingGroups(cloud *awsup.AWSCloud, tags map[string]string) ([]*au
if err != nil {
return nil, fmt.Errorf("error listing autoscaling cluster tags: %v", err)
}
}
if len(asgNames) != 0 {