Change:
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

Discussion:
} } <ins> if filterTags[TagNameKubernetesCluster] == "" { glog.Errorf("Tag %q not found; Kuberentes may behave unexpectedly.", TagNameKubernetesCluster) } </ins> awsCloud.filterTags = filterTags if len(filterTags) > 0 { glog.Infof("AWS cloud filtering on tags: %v", filterTags)

describeSecurityGroupsRequest := &ec2.DescribeSecurityGroupsInput{ GroupIds: []*string{&securityGroupId}, } <ins> // We don't apply our tag filters because we are retrieving by ID </ins> groups, err := s.ec2.DescribeSecurityGroups(describeSecurityGroupsRequest) if err != nil {

return nil } <del> // Makes sure the security group exists </del> <ins> // Makes sure the security group exists. // For multi-cluster isolation, name must be globally unique, for example derived from the service UUID. </ins> // Returns the security group id or error func (s *AWSCloud) ensureSecurityGroup(name string, description string, vpcID string) (string, error) { groupID := ""

} // 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

return fmt.Errorf("error querying security groups: %v", err) } <ins> taggedSecurityGroups, err := s.getTaggedSecurityGroups() if err != nil { return fmt.Errorf("error querying for tagged security groups: %v", err) } </ins> // Open the firewall from the load balancer to the instance // We don't actually have a trivial way to know in advance which security group the instance is in // (it is probably the minion security group, but we don't easily have that).

// Scan instances for groups we want open for _, instance := range allInstances { <del> securityGroupId := findSecurityGroupForInstance(instance) if isNilOrEmpty(securityGroupId) { </del> <ins> securityGroup, err := findSecurityGroupForInstance(instance, taggedSecurityGroups) if err != nil { return err } if securityGroup == nil { </ins> glog.Warning("Ignoring instance without security group: ", orEmpty(instance.InstanceId)) continue } <ins> id := aws.StringValue(securityGroup.GroupId) if id == "" { glog.Warningf("found security group without id: %v", securityGroup) continue } </ins> <del> instanceSecurityGroupIds[*securityGroupId] = true </del> <ins> instanceSecurityGroupIds[id] = true </ins> } // Compare to actual groups for _, actualGroup := range actualGroups { <del> if isNilOrEmpty(actualGroup.GroupId) { </del> <ins> actualGroupID := aws.StringValue(actualGroup.GroupId) if actualGroupID == "" { </ins> glog.Warning("Ignoring group without ID: ", actualGroup) continue } <del> actualGroupID := *actualGroup.GroupId </del> adding, found := instanceSecurityGroupIds[actualGroupID] if found && adding { // We don't need to make a change; the permission is already in place