Change:
} // Returns the first security group for an instance, or nil <del> // We only create instances with one security group, so we warn if there are multiple or none func findSecurityGroupForInstance(instance *ec2.Instance) *string { var securityGroupId *string for _, securityGroup := range instance.SecurityGroups { if securityGroup == nil || securityGroup.GroupId == nil { // Not expected, but avoid panic glog.Warning("Unexpected empty security group for instance: ", orEmpty(instance.InstanceId)) </del> <ins> // We only create instances with one security group, so we don't expect multiple security groups. // However, if there are multiple security groups, we will choose the one tagged with our cluster filter. // Otherwise we will return an error. func findSecurityGroupForInstance(instance *ec2.Instance, taggedSecurityGroups map[string]*ec2.SecurityGroup) (*ec2.GroupIdentifier, error) { instanceID := aws.StringValue(instance.InstanceId) var best *ec2.GroupIdentifier for _, group := range instance.SecurityGroups { groupID := aws.StringValue(group.GroupId) if groupID == "" { glog.Warningf("Ignoring security group without id for instance %q: %v", instanceID, group) </ins> continue } <del> if securityGroupId != nil { // We create instances with one SG glog.Warningf("Multiple security groups found for instance (%s); will use first group (%s)", orEmpty(instance.InstanceId), *securityGroupId) </del> <ins> if best == nil { best = group </ins> continue <ins> } _, bestIsTagged := taggedSecurityGroups[*best.GroupId] _, groupIsTagged := taggedSecurityGroups[groupID] if bestIsTagged && !groupIsTagged { // best is still best } else if groupIsTagged && !bestIsTagged { best = group </ins> } else { <del> securityGroupId = securityGroup.GroupId </del> <ins> // We create instances with one SG // If users create multiple SGs, they must tag one of them as being k8s owned return nil, fmt.Errorf("Multiple security groups found for instance (%s); ensure the k8s security group is tagged", instanceID) </ins> } } <del> if securityGroupId == nil { glog.Warning("No security group found for instance ", orEmpty(instance.InstanceId)) </del> <ins> if best == nil { glog.Warning("No security group found for instance ", instanceID) </ins> } <del> return securityGroupId </del> <ins> return best, nil } // Return all the security groups that are tagged as being part of our cluster func (s *AWSCloud) getTaggedSecurityGroups() (map[string]*ec2.SecurityGroup, error) { request := &ec2.DescribeSecurityGroupsInput{} filters := []*ec2.Filter{} request.Filters = s.addFilters(filters) groups, err := s.ec2.DescribeSecurityGroups(request) if err != nil { return nil, fmt.Errorf("error querying security groups: %v", err) } m := make(map[string]*ec2.SecurityGroup) for _, group := range groups { id := aws.StringValue(group.GroupId) if id == "" { glog.Warningf("Ignoring group without id: %v", group) continue } m[id] = group } return m, nil </ins> } // Open security group ingress rules on the instances so that the load balancer can talk to them

Discussion:
If there are two security groups on an instance, we currently make an arbitrary choice. We should choose based on tags, as we normally do. For back-compat, we need to cope if there is one security group and it is not tagged. We should make sure kube-up tags our security groups, though it isn't really required for kube-up, but it is just good behaviour. cf