Change:
In some custom setup cases the prefix-matching logic for GCE load-balancers can break and end up creating a firewall rule with no tags. We should instead refuse to create the rule. In fact, if we find it, we should probably delete the rule, but that's maybe too aggressive. Looking at the logic, if we return error in the tag-selection, we'll abort the whole load-balancer creation, which is unfortunate, but not TERRIBLE. We could just silently not create the firewall but not return error, which will just retry over and over.
/cc
Are you active on this? may have some starter-project bandwidth to help.
Not active. It would indeed be a good starter project :)

Discussion:
for ix := range ports { allowedPorts[ix] = strconv.Itoa(int(ports[ix].Port)) } <del> hostTags, err := gce.computeHostTags(hosts) if err != nil { return nil, err </del> <ins> // If the node tags to be used for this cluster have been predefined in the // provider config, just use them. Otherwise, invoke computeHostTags method to get the tags. hostTags := gce.nodeTags if len(hostTags) == 0 { var err error if hostTags, err = gce.computeHostTags(hosts); err != nil { return nil, fmt.Errorf("No node tags supplied and also failed to parse the given lists of hosts for tags. Abort creating firewall rule.") } </ins> } firewall := &compute.Firewall{ Name:         makeFirewallName(name), Description:  desc,

return firewall, nil } <del> // If the node tags to be used for this cluster have been predefined in the // provider config, just use them. Otherwise, grab all tags from all relevant // instances: </del> <ins> // ComputeHostTags grabs all tags from all instances being added to the pool. </ins> // * The longest tag that is a prefix of the instance name is used <del> // * If any instance has a prefix tag, all instances must // * If no instances have a prefix tag, no tags are used </del> <ins> // * If any instance has no matching prefix tag, return error // Invoking this method to get host tags is risky since it depends on the format // of the host names in the cluster. Only use it as a fallback if gce.nodeTags // is unspecified </ins> func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) { <del> if len(gce.nodeTags) > 0 { return gce.nodeTags, nil } </del> // TODO: We could store the tags in gceInstance, so we could have already fetched it hostNamesByZone := make(map[string][]string) for _, host := range hosts {

} if len(longest_tag) > 0 { tags.Insert(longest_tag) <del> } else if len(tags) > 0 { return nil, fmt.Errorf("Some, but not all, instances have prefix tags (%s is missing)", instance.Name) </del> <ins> } else { return nil, fmt.Errorf("Could not find any tag that is a prefix of instance name for instance %s", instance.Name) </ins> } } }

glog.Errorf("computeHostTags exceeded maxPages=%d for Instances.List: truncating.", maxPages) } } if len(tags) == 0 { <del> glog.V(2).Info("No instances had tags, creating rule without target tags") </del> <ins> return nil, fmt.Errorf("No instances found") </ins> } return tags.List(), nil }

} } <ins> func TestCreateFirewallFails(t *testing.T) { name := "loadbalancer" region := "us-central1" desc := "description" gce := &GCECloud{} if err := gce.createFirewall(name, region, desc, nil, nil, nil); err == nil { t.Errorf("error expected when creating firewall without any tags found") } } </ins> func TestRestrictTargetPool(t *testing.T) { const maxInstances = 5 tests := []struct {