From a431eb3e43e864628ceba5cd4cada2350e28d2e4 Mon Sep 17 00:00:00 2001 From: chrislovecnm Date: Sun, 24 Sep 2017 18:08:11 -0600 Subject: [PATCH] refactoring to use cloud based GetGroups --- cmd/kops/delete_instancegroup.go | 4 + cmd/kops/rollingupdatecluster.go | 23 +- hack/.packages | 1 + pkg/cloudinstances/cloud_instance_group.go | 146 ++++++++++ pkg/instancegroups/delete.go | 14 +- pkg/instancegroups/instancegroups.go | 217 +++++---------- pkg/instancegroups/rollingupdate.go | 26 +- pkg/instancegroups/rollingupdate_test.go | 267 ++++++++----------- pkg/resources/digitalocean/cloud.go | 9 +- upup/pkg/fi/cloud.go | 21 +- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 75 ++---- upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go | 3 +- upup/pkg/fi/cloudup/baremetal/cloud.go | 9 +- upup/pkg/fi/cloudup/gce/gce_cloud.go | 9 +- upup/pkg/fi/cloudup/gce/mock_gce_cloud.go | 10 +- upup/pkg/fi/cloudup/vsphere/vsphere_cloud.go | 12 +- 16 files changed, 417 insertions(+), 429 deletions(-) create mode 100644 pkg/cloudinstances/cloud_instance_group.go diff --git a/cmd/kops/delete_instancegroup.go b/cmd/kops/delete_instancegroup.go index b648857a051c0..161f53b1f8ad6 100644 --- a/cmd/kops/delete_instancegroup.go +++ b/cmd/kops/delete_instancegroup.go @@ -109,7 +109,11 @@ func NewCmdDeleteInstanceGroup(f *util.Factory, out io.Writer) *cobra.Command { return cmd } +// RunDeleteInstanceGroup runs the deletion of an instance group func RunDeleteInstanceGroup(f *util.Factory, out io.Writer, options *DeleteInstanceGroupOptions) error { + + // TODO make this drain and validate the ig? + // TODO implement drain and validate logic groupName := options.GroupName if groupName == "" { return fmt.Errorf("GroupName is required") diff --git a/cmd/kops/rollingupdatecluster.go b/cmd/kops/rollingupdatecluster.go index 62f821b39d347..99657613ce643 100644 --- a/cmd/kops/rollingupdatecluster.go +++ b/cmd/kops/rollingupdatecluster.go @@ -31,6 +31,7 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/kops/cmd/kops/util" api "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/pkg/featureflag" "k8s.io/kops/pkg/instancegroups" "k8s.io/kops/pkg/pretty" @@ -274,32 +275,32 @@ func RunRollingUpdateCluster(f *util.Factory, out io.Writer, options *RollingUpd return err } - groups, err := instancegroups.FindCloudInstanceGroups(cloud, cluster, instanceGroups, warnUnmatched, nodes) + groups, err := cloud.GetCloudGroups(cluster, instanceGroups, warnUnmatched, nodes) if err != nil { return err } { t := &tables.Table{} - t.AddColumn("NAME", func(r *instancegroups.CloudInstanceGroup) string { + t.AddColumn("NAME", func(r *cloudinstances.CloudInstanceGroup) string { return r.InstanceGroup.ObjectMeta.Name }) - t.AddColumn("STATUS", func(r *instancegroups.CloudInstanceGroup) string { + t.AddColumn("STATUS", func(r *cloudinstances.CloudInstanceGroup) string { return r.Status }) - t.AddColumn("NEEDUPDATE", func(r *instancegroups.CloudInstanceGroup) string { + t.AddColumn("NEEDUPDATE", func(r *cloudinstances.CloudInstanceGroup) string { return strconv.Itoa(len(r.NeedUpdate)) }) - t.AddColumn("READY", func(r *instancegroups.CloudInstanceGroup) string { + t.AddColumn("READY", func(r *cloudinstances.CloudInstanceGroup) string { return strconv.Itoa(len(r.Ready)) }) - t.AddColumn("MIN", func(r *instancegroups.CloudInstanceGroup) string { - return strconv.Itoa(r.MinSize()) + t.AddColumn("MIN", func(r *cloudinstances.CloudInstanceGroup) string { + return strconv.Itoa(r.MinSize) }) - t.AddColumn("MAX", func(r *instancegroups.CloudInstanceGroup) string { - return strconv.Itoa(r.MaxSize()) + t.AddColumn("MAX", func(r *cloudinstances.CloudInstanceGroup) string { + return strconv.Itoa(r.MaxSize) }) - t.AddColumn("NODES", func(r *instancegroups.CloudInstanceGroup) string { + t.AddColumn("NODES", func(r *cloudinstances.CloudInstanceGroup) string { var nodes []*v1.Node for _, i := range r.Ready { if i.Node != nil { @@ -313,7 +314,7 @@ func RunRollingUpdateCluster(f *util.Factory, out io.Writer, options *RollingUpd } return strconv.Itoa(len(nodes)) }) - var l []*instancegroups.CloudInstanceGroup + var l []*cloudinstances.CloudInstanceGroup for _, v := range groups { l = append(l, v) } diff --git a/hack/.packages b/hack/.packages index 3546cecffba10..e245c46f97eb0 100644 --- a/hack/.packages +++ b/hack/.packages @@ -56,6 +56,7 @@ k8s.io/kops/pkg/client/clientset_generated/internalclientset/typed/kops/v1alpha2 k8s.io/kops/pkg/client/clientset_generated/internalclientset/typed/kops/v1alpha2/fake k8s.io/kops/pkg/client/simple k8s.io/kops/pkg/client/simple/vfsclientset +k8s.io/kops/pkg/cloudinstances k8s.io/kops/pkg/diff k8s.io/kops/pkg/dns k8s.io/kops/pkg/edit diff --git a/pkg/cloudinstances/cloud_instance_group.go b/pkg/cloudinstances/cloud_instance_group.go new file mode 100644 index 0000000000000..e7e1bae169c28 --- /dev/null +++ b/pkg/cloudinstances/cloud_instance_group.go @@ -0,0 +1,146 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudinstances + +import ( + "fmt" + + "github.com/golang/glog" + "k8s.io/client-go/pkg/api/v1" + api "k8s.io/kops/pkg/apis/kops" +) + +// CloudInstanceGroup is the cloud backing of InstanceGroup. +type CloudInstanceGroup struct { + InstanceGroup *api.InstanceGroup + GroupName string + GroupTemplateName string + Status string + Ready []*CloudInstanceMember + NeedUpdate []*CloudInstanceMember + MinSize int + MaxSize int +} + +// CloudInstanceGroupInstance describes an instances in a CloudInstanceGroup group. +type CloudInstanceMember struct { + ID *string + Node *v1.Node +} + +// NewCloudInstanceGroup creates a CloudInstanceGroup and validates its initial values. +func NewCloudInstanceGroup(groupName string, groupTemplateName string, ig *api.InstanceGroup, minSize int, maxSize int) (*CloudInstanceGroup, error) { + if groupName == "" { + return nil, fmt.Errorf("group name for cloud instance group must be set") + } + if groupTemplateName == "" { + return nil, fmt.Errorf("group template name for cloud instance group must be set") + } + if ig == nil { + return nil, fmt.Errorf("kops instance group for cloud instance group must be set") + } + + if minSize < 0 { + return nil, fmt.Errorf("cloud instance group min size must be zero or greater") + } + if maxSize < 0 { + return nil, fmt.Errorf("cloud instance group max size must be zero or greater") + } + + cg := &CloudInstanceGroup{ + GroupName: groupName, + GroupTemplateName: groupTemplateName, + InstanceGroup: ig, + MinSize: minSize, + MaxSize: maxSize, + } + + return cg, nil +} + +// NewCloudInstanceMember creates a new CloudInstanceGroupMember +func (c *CloudInstanceGroup) NewCloudInstanceMember(instanceId *string, newGroupName string, currentGroupName string, nodeMap map[string]*v1.Node) error { + if instanceId == nil { + return fmt.Errorf("instance id for cloud instance member cannot be nil") + } + cm := &CloudInstanceMember{ + ID: instanceId, + } + id := *instanceId + node := nodeMap[id] + if node != nil { + cm.Node = node + } else { + glog.V(8).Infof("unable to find node for instance: %s", id) + } + + if newGroupName == currentGroupName { + c.Ready = append(c.Ready, cm) + } else { + c.NeedUpdate = append(c.NeedUpdate, cm) + } + + return nil +} + +// MarkIsReady sets the CloudInstanceGroup status for Ready or NeedsUpdate +func (c *CloudInstanceGroup) MarkIsReady() { + if len(c.NeedUpdate) == 0 { + c.Status = "Ready" + } else { + c.Status = "NeedsUpdate" + } +} + +// GetNodeMap returns a list of nodes keyed by there external id +func GetNodeMap(nodes []v1.Node) map[string]*v1.Node { + nodeMap := make(map[string]*v1.Node) + for i := range nodes { + node := &nodes[i] + nodeMap[node.Spec.ExternalID] = node + } + + return nodeMap +} + +// GetInstanceGroup filters a list of instancegroups for recognized cloud groups +func GetInstanceGroup(name string, clusterName string, instancegroups []*api.InstanceGroup) (*api.InstanceGroup, error) { + var instancegroup *api.InstanceGroup + for _, g := range instancegroups { + var groupName string + switch g.Spec.Role { + case api.InstanceGroupRoleMaster: + groupName = g.ObjectMeta.Name + ".masters." + clusterName + case api.InstanceGroupRoleNode: + groupName = g.ObjectMeta.Name + "." + clusterName + case api.InstanceGroupRoleBastion: + groupName = g.ObjectMeta.Name + "." + clusterName + default: + glog.Warningf("Ignoring InstanceGroup of unknown role %q", g.Spec.Role) + continue + } + + if name == groupName { + if instancegroup != nil { + return nil, fmt.Errorf("found multiple instance groups matching ASG %q", groupName) + } + instancegroup = g + } + } + + return instancegroup, nil +} diff --git a/pkg/instancegroups/delete.go b/pkg/instancegroups/delete.go index 724fc0eab3688..43f5542738855 100644 --- a/pkg/instancegroups/delete.go +++ b/pkg/instancegroups/delete.go @@ -32,28 +32,32 @@ type DeleteInstanceGroup struct { Clientset simple.Clientset } -func (c *DeleteInstanceGroup) DeleteInstanceGroup(group *api.InstanceGroup) error { - groups, err := FindCloudInstanceGroups(c.Cloud, c.Cluster, []*api.InstanceGroup{group}, false, nil) +// DeleteInstanceGroup deletes a cloud instance group +func (d *DeleteInstanceGroup) DeleteInstanceGroup(group *api.InstanceGroup) error { + + groups, err := d.Cloud.GetCloudGroups(d.Cluster, []*api.InstanceGroup{group}, false, nil) if err != nil { return fmt.Errorf("error finding CloudInstanceGroups: %v", err) } + + // TODO should we drain nodes and validate the cluster? cig := groups[group.ObjectMeta.Name] if cig == nil { glog.Warningf("AutoScalingGroup %q not found in cloud - skipping delete", group.ObjectMeta.Name) } else { if len(groups) != 1 { - return fmt.Errorf("Multiple InstanceGroup resources found in cloud") + return fmt.Errorf("multiple InstanceGroup resources found in cloud") } glog.Infof("Deleting AutoScalingGroup %q", group.ObjectMeta.Name) - err = cig.Delete(c.Cloud) + err = d.Cloud.DeleteGroup(cig.GroupName, cig.GroupTemplateName) if err != nil { return fmt.Errorf("error deleting cloud resources for InstanceGroup: %v", err) } } - err = c.Clientset.InstanceGroupsFor(c.Cluster).Delete(group.ObjectMeta.Name, nil) + err = d.Clientset.InstanceGroupsFor(d.Cluster).Delete(group.ObjectMeta.Name, nil) if err != nil { return err } diff --git a/pkg/instancegroups/instancegroups.go b/pkg/instancegroups/instancegroups.go index 6c010d378dc51..e55179402a63c 100644 --- a/pkg/instancegroups/instancegroups.go +++ b/pkg/instancegroups/instancegroups.go @@ -21,141 +21,48 @@ import ( "os" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/autoscaling" "github.com/golang/glog" "github.com/spf13/cobra" - "k8s.io/client-go/pkg/api/v1" api "k8s.io/kops/pkg/apis/kops" - "k8s.io/kops/pkg/client/simple" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/pkg/featureflag" - "k8s.io/kops/pkg/resources" "k8s.io/kops/pkg/validation" "k8s.io/kops/upup/pkg/fi" - "k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kubernetes/pkg/kubectl/cmd" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" ) -// FindCloudInstanceGroups joins data from the cloud and the instance groups into a map that can be used for updates. -func FindCloudInstanceGroups(cloud fi.Cloud, cluster *api.Cluster, instancegroups []*api.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*CloudInstanceGroup, error) { - awsCloud := cloud.(awsup.AWSCloud) - - groups := make(map[string]*CloudInstanceGroup) - - tags := awsCloud.Tags() - - asgs, err := resources.FindAutoscalingGroups(awsCloud, tags) - if err != nil { - return nil, err - } - - nodeMap := make(map[string]*v1.Node) - for i := range nodes { - node := &nodes[i] - awsID := node.Spec.ExternalID - nodeMap[awsID] = node - } - - for _, asg := range asgs { - name := aws.StringValue(asg.AutoScalingGroupName) - var instancegroup *api.InstanceGroup - for _, g := range instancegroups { - var asgName string - switch g.Spec.Role { - case api.InstanceGroupRoleMaster: - asgName = g.ObjectMeta.Name + ".masters." + cluster.ObjectMeta.Name - case api.InstanceGroupRoleNode: - asgName = g.ObjectMeta.Name + "." + cluster.ObjectMeta.Name - case api.InstanceGroupRoleBastion: - asgName = g.ObjectMeta.Name + "." + cluster.ObjectMeta.Name - default: - glog.Warningf("Ignoring InstanceGroup of unknown role %q", g.Spec.Role) - continue - } - - if name == asgName { - if instancegroup != nil { - return nil, fmt.Errorf("Found multiple instance groups matching ASG %q", asgName) - } - instancegroup = g - } - } - if instancegroup == nil { - if warnUnmatched { - glog.Warningf("Found ASG with no corresponding instance group %q", name) - } - continue - } - group := buildCloudInstanceGroup(instancegroup, asg, nodeMap) - groups[instancegroup.ObjectMeta.Name] = group - } - - return groups, nil +// RollingUpdateInstanceGroup is the AWS ASG backing an InstanceGroup. +type RollingUpdateInstanceGroup struct { + // Cloud is the kops cloud provider + Cloud fi.Cloud + // CloudGroup is the kops cloud provider groups + CloudGroup *cloudinstances.CloudInstanceGroup + + // TODO should remove the need to have rollingupdate struct and add: + // TODO - the kubernetes client + // TODO - the cluster name + // TODO - the client config + // TODO - fail on validate + // TODO - fail on drain + // TODO - cloudonly } - - -// CloudInstanceGroup is the AWS ASG backing an InstanceGroup. -type CloudInstanceGroup struct { - InstanceGroup *api.InstanceGroup - ASGName string - Status string - Ready []*CloudInstanceGroupInstance - NeedUpdate []*CloudInstanceGroupInstance - - asg *autoscaling.Group -} - -func buildCloudInstanceGroup(ig *api.InstanceGroup, g *autoscaling.Group, nodeMap map[string]*v1.Node) *CloudInstanceGroup { - n := &CloudInstanceGroup{ - ASGName: aws.StringValue(g.AutoScalingGroupName), - InstanceGroup: ig, - asg: g, +// NewRollingUpdateInstanceGroup create a new struct +func NewRollingUpdateInstanceGroup(cloud fi.Cloud, cloudGroup *cloudinstances.CloudInstanceGroup) (*RollingUpdateInstanceGroup, error) { + if cloud == nil { + return nil, fmt.Errorf("cloud provider is required") } - - readyLaunchConfigurationName := aws.StringValue(g.LaunchConfigurationName) - - for _, i := range g.Instances { - c := &CloudInstanceGroupInstance{ASGInstance: i} - - node := nodeMap[aws.StringValue(i.InstanceId)] - if node != nil { - c.Node = node - } - - if readyLaunchConfigurationName == aws.StringValue(i.LaunchConfigurationName) { - n.Ready = append(n.Ready, c) - } else { - n.NeedUpdate = append(n.NeedUpdate, c) - } - } - - if len(n.NeedUpdate) == 0 { - n.Status = "Ready" - } else { - n.Status = "NeedsUpdate" + if cloudGroup == nil { + return nil, fmt.Errorf("cloud group is required") } - return n -} - -// CloudInstanceGroupInstance describes an instance in an autoscaling group. -type CloudInstanceGroupInstance struct { - ASGInstance *autoscaling.Instance - Node *v1.Node -} + // TODO check more values in cloudGroup that they are set properly -func (n *CloudInstanceGroup) String() string { - return "CloudInstanceGroup:" + n.ASGName -} - -func (c *CloudInstanceGroup) MinSize() int { - return int(aws.Int64Value(c.asg.MinSize)) -} - -func (c *CloudInstanceGroup) MaxSize() int { - return int(aws.Int64Value(c.asg.MaxSize)) + return &RollingUpdateInstanceGroup{ + Cloud: cloud, + CloudGroup: cloudGroup, + }, nil } // TODO: Temporarily increase size of ASG? @@ -163,7 +70,7 @@ func (c *CloudInstanceGroup) MaxSize() int { // TODO: Batch termination, like a rolling-update // RollingUpdate performs a rolling update on a list of ec2 instances. -func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList, isBastion bool, t time.Duration) (err error) { +func (r *RollingUpdateInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList, isBastion bool, t time.Duration) (err error) { // we should not get here, but hey I am going to check. if rollingUpdateData == nil { @@ -179,11 +86,9 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust return fmt.Errorf("rollingUpdate is missing the InstanceGroupList") } - c := rollingUpdateData.Cloud - - update := n.NeedUpdate + update := r.CloudGroup.NeedUpdate if rollingUpdateData.Force { - update = append(update, n.Ready...) + update = append(update, r.CloudGroup.Ready...) } if len(update) == 0 { @@ -195,7 +100,7 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust } else if rollingUpdateData.CloudOnly { glog.V(3).Info("Not validating cluster as validation is turned off via the cloud-only flag.") } else if featureflag.DrainAndValidateRollingUpdate.Enabled() { - if err = n.ValidateCluster(rollingUpdateData, instanceGroupList); err != nil { + if err = r.ValidateCluster(rollingUpdateData, instanceGroupList); err != nil { if rollingUpdateData.FailOnValidate { return fmt.Errorf("error validating cluster: %v", err) } else { @@ -207,7 +112,7 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust for _, u := range update { - instanceId := aws.StringValue(u.ASGInstance.InstanceId) + instanceId := fi.StringValue(u.ID) nodeName := "" if u.Node != nil { @@ -215,7 +120,7 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust } if isBastion { - if err = n.DeleteInstance(u, instanceId, nodeName, c); err != nil { + if err = r.DeleteInstance(u); err != nil { glog.Errorf("Error deleting aws instance %q: %v", instanceId, err) return err } @@ -233,9 +138,9 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust if u.Node != nil { glog.Infof("Draining the node: %q.", nodeName) - if err = n.DrainNode(u, rollingUpdateData); err != nil { + if err = r.DrainNode(u, rollingUpdateData); err != nil { if rollingUpdateData.FailOnDrainError { - return fmt.Errorf("Failed to drain node %q: %v", nodeName, err) + return fmt.Errorf("failed to drain node %q: %v", nodeName, err) } else { glog.Infof("Ignoring error draining node %q: %v", nodeName, err) } @@ -245,7 +150,7 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust } } - if err = n.DeleteInstance(u, instanceId, nodeName, c); err != nil { + if err = r.DeleteInstance(u); err != nil { glog.Errorf("Error deleting aws instance %q, node %q: %v", instanceId, nodeName, err) return err } @@ -262,7 +167,7 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust glog.Infof("Validating the cluster.") - if err = n.ValidateClusterWithDuration(rollingUpdateData, instanceGroupList, t); err != nil { + if err = r.ValidateClusterWithDuration(rollingUpdateData, instanceGroupList, t); err != nil { if rollingUpdateData.FailOnValidate { glog.Errorf("Cluster did not validate within the set duration of %q, you can retry, and maybe extend the duration", t) @@ -278,12 +183,12 @@ func (n *CloudInstanceGroup) RollingUpdate(rollingUpdateData *RollingUpdateClust } // ValidateClusterWithDuration runs validation.ValidateCluster until either we get positive result or the timeout expires -func (n *CloudInstanceGroup) ValidateClusterWithDuration(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList, duration time.Duration) error { +func (r *RollingUpdateInstanceGroup) ValidateClusterWithDuration(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList, duration time.Duration) error { // TODO should we expose this to the UI? tickDuration := 30 * time.Second // Try to validate cluster at least once, this will handle durations that are lower // than our tick time - if n.tryValidateCluster(rollingUpdateData, instanceGroupList, duration, tickDuration) { + if r.tryValidateCluster(rollingUpdateData, instanceGroupList, duration, tickDuration) { return nil } @@ -297,7 +202,7 @@ func (n *CloudInstanceGroup) ValidateClusterWithDuration(rollingUpdateData *Roll return fmt.Errorf("cluster did not validate within a duation of %q", duration) case <-tick: // Got a tick, validate cluster - if n.tryValidateCluster(rollingUpdateData, instanceGroupList, duration, tickDuration) { + if r.tryValidateCluster(rollingUpdateData, instanceGroupList, duration, tickDuration) { return nil } // ValidateCluster didn't work yet, so let's try again @@ -306,7 +211,7 @@ func (n *CloudInstanceGroup) ValidateClusterWithDuration(rollingUpdateData *Roll } } -func (n *CloudInstanceGroup) tryValidateCluster(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList, duration time.Duration, tickDuration time.Duration) bool { +func (r *RollingUpdateInstanceGroup) tryValidateCluster(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList, duration time.Duration, tickDuration time.Duration) bool { if _, err := validation.ValidateCluster(rollingUpdateData.ClusterName, instanceGroupList, rollingUpdateData.K8sClient); err != nil { glog.Infof("Cluster did not validate, will try again in %q util duration %q expires: %v.", tickDuration, duration, err) return false @@ -317,7 +222,7 @@ func (n *CloudInstanceGroup) tryValidateCluster(rollingUpdateData *RollingUpdate } // ValidateCluster runs our validation methods on the K8s Cluster. -func (n *CloudInstanceGroup) ValidateCluster(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList) error { +func (r *RollingUpdateInstanceGroup) ValidateCluster(rollingUpdateData *RollingUpdateCluster, instanceGroupList *api.InstanceGroupList) error { if _, err := validation.ValidateCluster(rollingUpdateData.ClusterName, instanceGroupList, rollingUpdateData.K8sClient); err != nil { return fmt.Errorf("cluster %q did not pass validation: %v", rollingUpdateData.ClusterName, err) @@ -328,19 +233,21 @@ func (n *CloudInstanceGroup) ValidateCluster(rollingUpdateData *RollingUpdateClu } // DeleteInstance deletes an Cloud Instance. -func (n *CloudInstanceGroup) DeleteInstance(u *CloudInstanceGroupInstance, instanceId string, nodeName string, c fi.Cloud) error { +func (r *RollingUpdateInstanceGroup) DeleteInstance(u *cloudinstances.CloudInstanceMember) error { + + id := fi.StringValue(u.ID) - if nodeName != "" { - glog.Infof("Stopping instance %q, node %q, in AWS ASG %q.", instanceId, nodeName, n.ASGName) + if u.Node != nil { + glog.Infof("Stopping instance %q, node %q, in group %q.", id, u.Node.Name, r.CloudGroup.GroupName) } else { - glog.Infof("Stopping instance %q, in AWS ASG %q.", instanceId, n.asg.AutoScalingGroupName) + glog.Infof("Stopping instance %q, in group %q.", fi.StringValue(u.ID), r.CloudGroup.GroupName) } - if err := c.DeleteInstance(u.ASGInstance.InstanceId); err != nil { - if nodeName != "" { - return fmt.Errorf("error deleting instance %q, node %q: %v", instanceId, nodeName, err) + if err := r.Cloud.DeleteInstance(u.ID); err != nil { + if u.Node.Name != "" { + return fmt.Errorf("error deleting instance %q, node %q: %v", id, u.Node.Name, err) } - return fmt.Errorf("error deleting instance %q: %v", instanceId, err) + return fmt.Errorf("error deleting instance %q: %v", id, err) } return nil @@ -348,9 +255,13 @@ func (n *CloudInstanceGroup) DeleteInstance(u *CloudInstanceGroupInstance, insta } // DrainNode drains a K8s node. -func (n *CloudInstanceGroup) DrainNode(u *CloudInstanceGroupInstance, rollingUpdateData *RollingUpdateCluster) error { +func (r *RollingUpdateInstanceGroup) DrainNode(u *cloudinstances.CloudInstanceMember, rollingUpdateData *RollingUpdateCluster) error { if rollingUpdateData.ClientConfig == nil { - return fmt.Errorf("ClientConfig not set") + return fmt.Errorf("clientConfig not set") + } + + if u.Node.Name == "" { + return fmt.Errorf("node name not set") } f := cmdutil.NewFactory(rollingUpdateData.ClientConfig) @@ -395,8 +306,16 @@ func (n *CloudInstanceGroup) DrainNode(u *CloudInstanceGroupInstance, rollingUpd } // Delete and CloudInstanceGroups -func (g *CloudInstanceGroup) Delete(cloud fi.Cloud) error { - +func (r *RollingUpdateInstanceGroup) Delete() error { + if r.CloudGroup == nil { + return fmt.Errorf("group has to be set") + } + if r.CloudGroup.GroupName == "" { + return fmt.Errorf("group name has to be set") + } + if r.CloudGroup.GroupTemplateName == "" { + return fmt.Errorf("group template name has to be set") + } // TODO: Leaving func in place in order to cordon nd drain nodes - return cloud.DeleteGroup(*g.asg.AutoScalingGroupName, *g.asg.LaunchConfigurationName) + return r.Cloud.DeleteGroup(r.CloudGroup.GroupName, r.CloudGroup.GroupTemplateName) } diff --git a/pkg/instancegroups/rollingupdate.go b/pkg/instancegroups/rollingupdate.go index 170940e0b931a..6127425a73244 100644 --- a/pkg/instancegroups/rollingupdate.go +++ b/pkg/instancegroups/rollingupdate.go @@ -25,6 +25,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" api "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" ) @@ -48,7 +49,7 @@ type RollingUpdateCluster struct { } // RollingUpdate performs a rolling update on a K8s Cluster. -func (c *RollingUpdateCluster) RollingUpdate(groups map[string]*CloudInstanceGroup, instanceGroups *api.InstanceGroupList) error { +func (c *RollingUpdateCluster) RollingUpdate(groups map[string]*cloudinstances.CloudInstanceGroup, instanceGroups *api.InstanceGroupList) error { if len(groups) == 0 { glog.Infof("Cloud Instance Group length is zero. Not doing a rolling-update.") return nil @@ -57,9 +58,9 @@ func (c *RollingUpdateCluster) RollingUpdate(groups map[string]*CloudInstanceGro var resultsMutex sync.Mutex results := make(map[string]error) - masterGroups := make(map[string]*CloudInstanceGroup) - nodeGroups := make(map[string]*CloudInstanceGroup) - bastionGroups := make(map[string]*CloudInstanceGroup) + masterGroups := make(map[string]*cloudinstances.CloudInstanceGroup) + nodeGroups := make(map[string]*cloudinstances.CloudInstanceGroup) + bastionGroups := make(map[string]*cloudinstances.CloudInstanceGroup) for k, group := range groups { switch group.InstanceGroup.Spec.Role { case api.InstanceGroupRoleNode: @@ -79,14 +80,17 @@ func (c *RollingUpdateCluster) RollingUpdate(groups map[string]*CloudInstanceGro for k, bastionGroup := range bastionGroups { wg.Add(1) - go func(k string, group *CloudInstanceGroup) { + go func(k string, group *cloudinstances.CloudInstanceGroup) { resultsMutex.Lock() results[k] = fmt.Errorf("function panic bastions") resultsMutex.Unlock() defer wg.Done() - err := group.RollingUpdate(c, instanceGroups, true, c.BastionInterval) + g, err := NewRollingUpdateInstanceGroup(c.Cloud, group) + if err == nil { + err = g.RollingUpdate(c, instanceGroups, true, c.BastionInterval) + } resultsMutex.Lock() results[k] = err @@ -116,7 +120,10 @@ func (c *RollingUpdateCluster) RollingUpdate(groups map[string]*CloudInstanceGro defer wg.Done() for k, group := range masterGroups { - err := group.RollingUpdate(c, instanceGroups, false, c.MasterInterval) + g, err := NewRollingUpdateInstanceGroup(c.Cloud, group) + if err == nil { + err = g.RollingUpdate(c, instanceGroups, false, c.MasterInterval) + } resultsMutex.Lock() results[k] = err @@ -151,7 +158,10 @@ func (c *RollingUpdateCluster) RollingUpdate(groups map[string]*CloudInstanceGro defer wg.Done() for k, group := range nodeGroups { - err := group.RollingUpdate(c, instanceGroups, false, c.NodeInterval) + g, err := NewRollingUpdateInstanceGroup(c.Cloud, group) + if err == nil { + err = g.RollingUpdate(c, instanceGroups, false, c.NodeInterval) + } resultsMutex.Lock() results[k] = err diff --git a/pkg/instancegroups/rollingupdate_test.go b/pkg/instancegroups/rollingupdate_test.go index 11e946bdc5d57..341188abc26de 100644 --- a/pkg/instancegroups/rollingupdate_test.go +++ b/pkg/instancegroups/rollingupdate_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/cloudmock/aws/mockautoscaling" kopsapi "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" ) @@ -97,10 +98,10 @@ func TestRollingUpdateAllNeedUpdate(t *testing.T) { setUpCloud(c) asgGroups, _ := cloud.Autoscaling().DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{}) - groups := make(map[string]*CloudInstanceGroup) - groups["node-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[0], + groups := make(map[string]*cloudinstances.CloudInstanceGroup) + groups["node-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[0].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-1", @@ -109,39 +110,31 @@ func TestRollingUpdateAllNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleNode, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1a"), - }, + ID: aws.String("node-1a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1b"), - }, + ID: aws.String("node-1b"), Node: &v1.Node{}, }, }, - NeedUpdate: []*CloudInstanceGroupInstance{ + NeedUpdate: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1a"), - }, + ID: aws.String("node-1a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1b"), - }, + ID: aws.String("node-1b"), Node: &v1.Node{}, }, }, } - groups["node-2"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[1], + groups["node-2"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[1].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-2", @@ -150,39 +143,31 @@ func TestRollingUpdateAllNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleNode, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2a"), - }, + ID: aws.String("node-2a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2b"), - }, + ID: aws.String("node-2b"), Node: &v1.Node{}, }, }, - NeedUpdate: []*CloudInstanceGroupInstance{ + NeedUpdate: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2a"), - }, + ID: aws.String("node-2a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2b"), - }, + ID: aws.String("node-2b"), Node: &v1.Node{}, }, }, } - groups["master-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[2], + groups["master-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[2].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "master-1", @@ -191,27 +176,23 @@ func TestRollingUpdateAllNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleMaster, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("master-1a"), - }, + ID: aws.String("master-1a"), Node: &v1.Node{}, }, }, - NeedUpdate: []*CloudInstanceGroupInstance{ + NeedUpdate: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("master-1a"), - }, + ID: aws.String("master-1a"), Node: &v1.Node{}, }, }, } - groups["bastion-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[3], + groups["bastion-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[3].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "bastion-1", @@ -220,19 +201,15 @@ func TestRollingUpdateAllNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleBastion, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("bastion-1a"), - }, + ID: aws.String("bastion-1a"), Node: &v1.Node{}, }, }, - NeedUpdate: []*CloudInstanceGroupInstance{ + NeedUpdate: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("bastion-1a"), - }, + ID: aws.String("bastion-1a"), Node: &v1.Node{}, }, }, @@ -271,10 +248,10 @@ func TestRollingUpdateNoneNeedUpdate(t *testing.T) { asgGroups, _ := cloud.Autoscaling().DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{}) - groups := make(map[string]*CloudInstanceGroup) - groups["node-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[0], + groups := make(map[string]*cloudinstances.CloudInstanceGroup) + groups["node-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[0].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-1", @@ -283,25 +260,21 @@ func TestRollingUpdateNoneNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleNode, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1a"), - }, + ID: aws.String("node-1a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1b"), - }, + ID: aws.String("node-1b"), Node: &v1.Node{}, }, }, } - groups["node-2"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[1], + groups["node-2"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[1].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-2", @@ -310,25 +283,21 @@ func TestRollingUpdateNoneNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleNode, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2a"), - }, + ID: aws.String("node-2a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2b"), - }, + ID: aws.String("node-2b"), Node: &v1.Node{}, }, }, } - groups["master-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[2], + groups["master-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[2].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "master-1", @@ -337,19 +306,17 @@ func TestRollingUpdateNoneNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleMaster, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("master-1a"), - }, + ID: aws.String("master-1a"), Node: &v1.Node{}, }, }, } - groups["bastion-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[3], + groups["bastion-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[3].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "bastion-1", @@ -358,11 +325,9 @@ func TestRollingUpdateNoneNeedUpdate(t *testing.T) { Role: kopsapi.InstanceGroupRoleBastion, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("bastion-1a"), - }, + ID: aws.String("bastion-1a"), Node: &v1.Node{}, }, }, @@ -429,10 +394,10 @@ func TestRollingUpdateNoneNeedUpdateWithForce(t *testing.T) { asgGroups, _ := cloud.Autoscaling().DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{}) - groups := make(map[string]*CloudInstanceGroup) - groups["node-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[0], + groups := make(map[string]*cloudinstances.CloudInstanceGroup) + groups["node-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[0].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-1", @@ -441,25 +406,21 @@ func TestRollingUpdateNoneNeedUpdateWithForce(t *testing.T) { Role: kopsapi.InstanceGroupRoleNode, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1a"), - }, + ID: aws.String("node-1a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1b"), - }, + ID: aws.String("node-1b"), Node: &v1.Node{}, }, }, } - groups["node-2"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[1], + groups["node-2"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[1].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-2", @@ -468,25 +429,21 @@ func TestRollingUpdateNoneNeedUpdateWithForce(t *testing.T) { Role: kopsapi.InstanceGroupRoleNode, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2a"), - }, + ID: aws.String("node-2a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2b"), - }, + ID: aws.String("node-2b"), Node: &v1.Node{}, }, }, } - groups["master-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[2], + groups["master-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[2].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "master-1", @@ -495,19 +452,17 @@ func TestRollingUpdateNoneNeedUpdateWithForce(t *testing.T) { Role: kopsapi.InstanceGroupRoleMaster, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("master-1a"), - }, + ID: aws.String("master-1a"), Node: &v1.Node{}, }, }, } - groups["bastion-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[3], + groups["bastion-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[3].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "bastion-1", @@ -516,11 +471,9 @@ func TestRollingUpdateNoneNeedUpdateWithForce(t *testing.T) { Role: kopsapi.InstanceGroupRoleBastion, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("bastion-1a"), - }, + ID: aws.String("bastion-1a"), Node: &v1.Node{}, }, }, @@ -557,7 +510,7 @@ func TestRollingUpdateEmptyGroup(t *testing.T) { setUpCloud(c) asgGroups, _ := cloud.Autoscaling().DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{}) - groups := make(map[string]*CloudInstanceGroup) + groups := make(map[string]*cloudinstances.CloudInstanceGroup) err := c.RollingUpdate(groups, &kopsapi.InstanceGroupList{}) if err != nil { @@ -620,10 +573,10 @@ func TestRollingUpdateUnknownRole(t *testing.T) { asgGroups, _ := cloud.Autoscaling().DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{}) - groups := make(map[string]*CloudInstanceGroup) - groups["node-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[0], + groups := make(map[string]*cloudinstances.CloudInstanceGroup) + groups["node-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[0].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[0].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-1", @@ -632,25 +585,21 @@ func TestRollingUpdateUnknownRole(t *testing.T) { Role: "Unknown", }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1a"), - }, + ID: aws.String("node-1a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-1b"), - }, + ID: aws.String("node-1b"), Node: &v1.Node{}, }, }, } - groups["node-2"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[1], + groups["node-2"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[1].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[1].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "node-2", @@ -659,25 +608,21 @@ func TestRollingUpdateUnknownRole(t *testing.T) { Role: kopsapi.InstanceGroupRoleNode, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2a"), - }, + ID: aws.String("node-2a"), Node: &v1.Node{}, }, { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("node-2b"), - }, + ID: aws.String("node-2b"), Node: &v1.Node{}, }, }, } - groups["master-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[2], + groups["master-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[2].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[2].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "master-1", @@ -686,19 +631,17 @@ func TestRollingUpdateUnknownRole(t *testing.T) { Role: kopsapi.InstanceGroupRoleMaster, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("master-1a"), - }, + ID: aws.String("master-1a"), Node: &v1.Node{}, }, }, } - groups["bastion-1"] = &CloudInstanceGroup{ - ASGName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), - asg: asgGroups.AutoScalingGroups[3], + groups["bastion-1"] = &cloudinstances.CloudInstanceGroup{ + GroupName: aws.StringValue(asgGroups.AutoScalingGroups[3].AutoScalingGroupName), + GroupTemplateName: aws.StringValue(asgGroups.AutoScalingGroups[3].LaunchConfigurationName), InstanceGroup: &kopsapi.InstanceGroup{ ObjectMeta: v1meta.ObjectMeta{ Name: "bastion-1", @@ -707,11 +650,9 @@ func TestRollingUpdateUnknownRole(t *testing.T) { Role: kopsapi.InstanceGroupRoleBastion, }, }, - Ready: []*CloudInstanceGroupInstance{ + Ready: []*cloudinstances.CloudInstanceMember{ { - ASGInstance: &autoscaling.Instance{ - InstanceId: aws.String("bastion-1a"), - }, + ID: aws.String("bastion-1a"), Node: &v1.Node{}, }, }, diff --git a/pkg/resources/digitalocean/cloud.go b/pkg/resources/digitalocean/cloud.go index 61b466d8d71ed..5e10ba0407a44 100644 --- a/pkg/resources/digitalocean/cloud.go +++ b/pkg/resources/digitalocean/cloud.go @@ -28,6 +28,7 @@ import ( "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/pkg/resources/digitalocean/dns" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubernetes/federation/pkg/dnsprovider" @@ -81,21 +82,21 @@ func NewCloud(region string) (*Cloud, error) { } // GetCloudGroups is not implemented yet, that needs to return the instances and groups that back a kops cluster. -func (c *Cloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*fi.CloudGroup, error) { +func (c *Cloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) { glog.V(8).Infof("digitalocean cloud provider GetCloudGroups not implemented yet") - return nil, fmt.Errorf("digital ocean cloud provider does not support getting cloud groups at this time.") + return nil, fmt.Errorf("digital ocean cloud provider does not support getting cloud groups at this time") } // DeleteGroup is not implemented yet, is a func that needs to delete a DO instance group. func (c *Cloud) DeleteGroup(name string, template string) error { glog.V(8).Infof("digitalocean cloud provider DeleteGroup not implemented yet") - return fmt.Errorf("digital ocean cloud provider does not support deleting cloud groups at this time.") + return fmt.Errorf("digital ocean cloud provider does not support deleting cloud groups at this time") } // DeleteInstance is not implemented yet, is func needs to delete a DO instance. func (c *Cloud) DeleteInstance(id *string) error { glog.V(8).Infof("digitalocean cloud provider DeleteInstance not implemented yet") - return fmt.Errorf("digital ocean cloud provider does not support deleting cloud instances at this time.") + return fmt.Errorf("digital ocean cloud provider does not support deleting cloud instances at this time") } // ProviderID returns the kops api identifier for DigitalOcean cloud provider diff --git a/upup/pkg/fi/cloud.go b/upup/pkg/fi/cloud.go index 931ea5f12369d..8e1eaeceaacbc 100644 --- a/upup/pkg/fi/cloud.go +++ b/upup/pkg/fi/cloud.go @@ -19,6 +19,7 @@ package fi import ( "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kubernetes/federation/pkg/dnsprovider" ) @@ -37,7 +38,7 @@ type Cloud interface { DeleteGroup(name string, template string) error // GetCloudGroups returns a map of cloud instances that back a kops cluster - GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*CloudGroup, error) + GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) } type VPCInfo struct { @@ -54,24 +55,6 @@ type SubnetInfo struct { CIDR string } -// CloudInstanceGroup is the cloud backing of InstanceGroup. -type CloudGroup struct { - InstanceGroup *kops.InstanceGroup - GroupName string - GroupTemplateName string - Status string - Ready []*CloudGroupInstance - NeedUpdate []*CloudGroupInstance - MinSize int - MaxSize int -} - -// CloudInstanceGroupInstance describes an instance in an autoscaling group. -type CloudGroupInstance struct { - ID *string - Node *v1.Node -} - // zonesToCloud allows us to infer from certain well-known zones to a cloud // Note it is safe to "overmap" zones that don't exist: we'll check later if the zones actually exist var zonesToCloud = map[string]kops.CloudProviderID{ diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index bd9189cfa9f51..af66047fbd9e6 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -38,6 +38,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubernetes/federation/pkg/dnsprovider" dnsproviderroute53 "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/aws/route53" @@ -286,9 +287,11 @@ func (c *awsCloudImplementation) DeleteInstance(id *string) error { // TODO not used yet, as this requires a major refactor of rolling-update code, slowly but surely -// GetCloudGroups returns a groups of instanaces that back a kops instance groups -func (c *awsCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*fi.CloudGroup, error) { - var groups map[string]*fi.CloudGroup +// GetCloudGroups returns a groups of instances that back a kops instance groups +func (c *awsCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) { + nodeMap := cloudinstances.GetNodeMap(nodes) + + groups := make(map[string]*cloudinstances.CloudInstanceGroup) asgs, err := c.FindAutoscalingGroups() if err != nil { return nil, fmt.Errorf("unable to find autoscale groups: %v", err) @@ -296,27 +299,9 @@ func (c *awsCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instanceg for _, asg := range asgs { name := aws.StringValue(asg.AutoScalingGroupName) - var instancegroup *kops.InstanceGroup - for _, g := range instancegroups { - var asgName string - switch g.Spec.Role { - case kops.InstanceGroupRoleMaster: - asgName = g.ObjectMeta.Name + ".masters." + cluster.ObjectMeta.Name - case kops.InstanceGroupRoleNode: - asgName = g.ObjectMeta.Name + "." + cluster.ObjectMeta.Name - case kops.InstanceGroupRoleBastion: - asgName = g.ObjectMeta.Name + "." + cluster.ObjectMeta.Name - default: - glog.Warningf("Ignoring InstanceGroup of unknown role %q", g.Spec.Role) - continue - } - - if name == asgName { - if instancegroup != nil { - return nil, fmt.Errorf("Found multiple instance groups matching ASG %q", asgName) - } - instancegroup = g - } + instancegroup, err := cloudinstances.GetInstanceGroup(name, cluster.ObjectMeta.Name, instancegroups) + if err != nil { + return nil, fmt.Errorf("error getting instance group for ASG %q", name) } if instancegroup == nil { if warnUnmatched { @@ -325,7 +310,10 @@ func (c *awsCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instanceg continue } - groups[instancegroup.ObjectMeta.Name] = c.awsBuildCloudInstanceGroup(instancegroup, asg, nodeMap) + groups[instancegroup.ObjectMeta.Name], err = c.awsBuildCloudInstanceGroup(instancegroup, asg, nodeMap) + if err != nil { + return nil, fmt.Errorf("error getting cloud instance group %q: %v", instancegroup.ObjectMeta.Name, err) + } } return groups, nil @@ -418,40 +406,23 @@ func MatchesAsgTags(tags map[string]string, actual []*autoscaling.TagDescription return true } -func (c *awsCloudImplementation) awsBuildCloudInstanceGroup(ig *kops.InstanceGroup, g *autoscaling.Group, nodeMap map[string]*v1.Node) *fi.CloudGroup { - - n := &fi.CloudGroup{ - GroupName: aws.StringValue(g.AutoScalingGroupName), - InstanceGroup: ig, - GroupTemplateName: aws.StringValue(g.LaunchConfigurationName), +func (c *awsCloudImplementation) awsBuildCloudInstanceGroup(ig *kops.InstanceGroup, g *autoscaling.Group, nodeMap map[string]*v1.Node) (*cloudinstances.CloudInstanceGroup, error) { + newLaunchConfigName := aws.StringValue(g.LaunchConfigurationName) + n, err := cloudinstances.NewCloudInstanceGroup(aws.StringValue(g.AutoScalingGroupName), newLaunchConfigName, ig, int(aws.Int64Value(g.MinSize)), int(aws.Int64Value(g.MaxSize))) + if err != nil { + return nil, fmt.Errorf("error creating cloud instance group: %v", err) } - readyLaunchConfigurationName := aws.StringValue(g.LaunchConfigurationName) - for _, i := range g.Instances { - c := &fi.CloudGroupInstance{ - ID: i.InstanceId, - } - - node := nodeMap[aws.StringValue(i.InstanceId)] - if node != nil { - c.Node = node - } - - if readyLaunchConfigurationName == aws.StringValue(i.LaunchConfigurationName) { - n.Ready = append(n.Ready, c) - } else { - n.NeedUpdate = append(n.NeedUpdate, c) + err = n.NewCloudInstanceMember(i.InstanceId, newLaunchConfigName, aws.StringValue(i.LaunchConfigurationName), nodeMap) + if err != nil { + return nil, fmt.Errorf("error creating cloud instance group member: %v", err) } } - if len(n.NeedUpdate) == 0 { - n.Status = "Ready" - } else { - n.Status = "NeedsUpdate" - } + n.MarkIsReady() - return n + return n, nil } func (c *awsCloudImplementation) Tags() map[string]string { diff --git a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go index 4343fd441d8c5..abaab0a36f229 100644 --- a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go @@ -31,6 +31,7 @@ import ( "github.com/golang/glog" "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubernetes/federation/pkg/dnsprovider" dnsproviderroute53 "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/aws/route53" @@ -116,7 +117,7 @@ func (c *MockAWSCloud) DeleteInstance(id *string) error { return nil } -func (c *MockAWSCloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*fi.CloudGroup, error) { +func (c *MockAWSCloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) { return nil, fmt.Errorf("not implemented yet") } diff --git a/upup/pkg/fi/cloudup/baremetal/cloud.go b/upup/pkg/fi/cloudup/baremetal/cloud.go index 973211f779bba..947b2b81c0f2f 100644 --- a/upup/pkg/fi/cloudup/baremetal/cloud.go +++ b/upup/pkg/fi/cloudup/baremetal/cloud.go @@ -23,6 +23,7 @@ import ( "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubernetes/federation/pkg/dnsprovider" ) @@ -51,21 +52,21 @@ func (c *Cloud) FindVPCInfo(id string) (*fi.VPCInfo, error) { // GetCloudGroups is not implemented yet, that needs to return the instances and groups that back a kops cluster. // Baremetal may not support this. -func (c *Cloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*fi.CloudGroup, error) { +func (c *Cloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) { glog.V(8).Infof("baremetal cloud GetCloudGroups not implemented yet") - return nil, fmt.Errorf("baremetal provider does not support getting cloud groups at this time.") + return nil, fmt.Errorf("baremetal provider does not support getting cloud groups at this time") } // DeleteGroup is not implemented yet, is a func that needs to delete a DO instance group. // Baremetal may not support this. func (c *Cloud) DeleteGroup(name string, template string) error { glog.V(8).Infof("digitalocean cloud provider DeleteGroup not implemented yet") - return fmt.Errorf("digital ocean cloud provider does not support deleting cloud groups at this time.") + return fmt.Errorf("digital ocean cloud provider does not support deleting cloud groups at this time") } //DeleteInstance is not implemented yet, is func needs to delete a DO instance. //Baremetal may not support this. func (c *Cloud) DeleteInstance(id *string) error { glog.V(8).Infof("baremetal cloud provider DeleteInstance not implemented yet") - return fmt.Errorf("baremetal cloud provider does not support deleting cloud instances at this time.") + return fmt.Errorf("baremetal cloud provider does not support deleting cloud instances at this time") } diff --git a/upup/pkg/fi/cloudup/gce/gce_cloud.go b/upup/pkg/fi/cloudup/gce/gce_cloud.go index 745bf6db7cd49..69899140d5c68 100644 --- a/upup/pkg/fi/cloudup/gce/gce_cloud.go +++ b/upup/pkg/fi/cloudup/gce/gce_cloud.go @@ -27,6 +27,7 @@ import ( "google.golang.org/api/storage/v1" "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubernetes/federation/pkg/dnsprovider" "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns" @@ -220,19 +221,19 @@ func (c *gceCloudImplementation) GetApiIngressStatus(cluster *kops.Cluster) ([]k // DeleteGroup deletes a cloud of instances controlled by an Instance Group Manager func (c *gceCloudImplementation) DeleteGroup(name string, template string) error { glog.V(8).Infof("gce cloud provider DeleteGroup not implemented yet") - return fmt.Errorf("gce cloud provider does not support deleting cloud groups at this time.") + return fmt.Errorf("gce cloud provider does not support deleting cloud groups at this time") } // DeleteInstance deletes a GCE instance func (c *gceCloudImplementation) DeleteInstance(id *string) error { glog.V(8).Infof("gce cloud provider DeleteInstance not implemented yet") - return fmt.Errorf("gce cloud provider does not support deleting cloud instances at this time.") + return fmt.Errorf("gce cloud provider does not support deleting cloud instances at this time") } // GetCloudGroups returns a map of CloudGroup that backs a list of instance groups -func (c *gceCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*fi.CloudGroup, error) { +func (c *gceCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) { glog.V(8).Infof("gce cloud provider GetCloudGroups not implemented yet") - return nil, fmt.Errorf("gce cloud provider does not support getting cloud groups at this time.") + return nil, fmt.Errorf("gce cloud provider does not support getting cloud groups at this time") } // FindInstanceTemplates finds all instance templates that are associated with the current cluster diff --git a/upup/pkg/fi/cloudup/gce/mock_gce_cloud.go b/upup/pkg/fi/cloudup/gce/mock_gce_cloud.go index ce8e512722775..dd5c27e933d20 100644 --- a/upup/pkg/fi/cloudup/gce/mock_gce_cloud.go +++ b/upup/pkg/fi/cloudup/gce/mock_gce_cloud.go @@ -18,11 +18,13 @@ package gce import ( "fmt" + "github.com/golang/glog" compute "google.golang.org/api/compute/v0.beta" "google.golang.org/api/storage/v1" "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubernetes/federation/pkg/dnsprovider" dnsproviderclouddns "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns" @@ -51,21 +53,21 @@ func buildMockGCECloud(region string, project string) *mockGCECloud { } // GetCloudGroups is not implemented yet -func (c *mockGCECloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*fi.CloudGroup, error) { +func (c *mockGCECloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) { glog.V(8).Infof("mockGCECloud cloud provider GetCloudGroups not implemented yet") - return nil, fmt.Errorf("mockGCECloud cloud provider does not support getting cloud groups at this time.") + return nil, fmt.Errorf("mockGCECloud cloud provider does not support getting cloud groups at this time") } // DeleteGroup is not implemented yet func (c *mockGCECloud) DeleteGroup(name string, template string) error { glog.V(8).Infof("mockGCECloud cloud provider DeleteGroup not implemented yet") - return fmt.Errorf("mockGCECloud cloud provider does not support deleting cloud groups at this time.") + return fmt.Errorf("mockGCECloud cloud provider does not support deleting cloud groups at this time") } // DeleteInstance is not implemented yet func (c *mockGCECloud) DeleteInstance(id *string) error { glog.V(8).Infof("mockGCECloud cloud provider DeleteInstance not implemented yet") - return fmt.Errorf("mockGCECloud cloud provider does not support deleting cloud instances at this time.") + return fmt.Errorf("mockGCECloud cloud provider does not support deleting cloud instances at this time") } // Zones is not implemented yet diff --git a/upup/pkg/fi/cloudup/vsphere/vsphere_cloud.go b/upup/pkg/fi/cloudup/vsphere/vsphere_cloud.go index bc59c01741e11..0e1a8e5b69c2d 100644 --- a/upup/pkg/fi/cloudup/vsphere/vsphere_cloud.go +++ b/upup/pkg/fi/cloudup/vsphere/vsphere_cloud.go @@ -22,6 +22,10 @@ import ( "bytes" "context" "fmt" + "net/url" + "os" + "strings" + "github.com/golang/glog" "github.com/pkg/errors" "github.com/vmware/govmomi" @@ -34,12 +38,10 @@ import ( "github.com/vmware/govmomi/vim25/types" "k8s.io/client-go/pkg/api/v1" "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubernetes/federation/pkg/dnsprovider" k8scoredns "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/coredns" - "net/url" - "os" - "strings" ) // VSphereCloud represents a vSphere cloud instance. @@ -105,9 +107,9 @@ func NewVSphereCloud(spec *kops.ClusterSpec) (*VSphereCloud, error) { } // GetCloudGroups is not implemented yet, that needs to return the instances and groups that back a kops cluster. -func (c *VSphereCloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodeMap map[string]*v1.Node) (map[string]*fi.CloudGroup, error) { +func (c *VSphereCloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) { glog.V(8).Infof("vSphere cloud provider GetCloudGroups not implemented yet") - return nil, fmt.Errorf("vSphere cloud provider does not support getting cloud groups at this time.") + return nil, fmt.Errorf("vSphere cloud provider does not support getting cloud groups at this time") } // DeleteGroup is not implemented yet, is a func that needs to delete a vSphere instance group.