Change:
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks! If the matter is security related, please disclose it privately via --What happened: We deployed two Kubernetes clusters to the same Azure Resource Group. Created services of load balancer type on both clusters. One of the services never received a public IP. Upon investigation, we noticed that the first Kubernetes cluster deleted the Azure Public IP that was created for the service by the other cluster. Apparently, all public IPs in the resource group are retrieved: then public IPs which don't match this criteria: are deleted. This logic besides the name of the kubernetes service should also consider the K8s cluster where the public IP originated from to avoid stepping on each other's toes when multiple clusters share the same resource group. What you expected to happen: In case multiple clusters run in the same resource group a cluster should delete only resources created by itself. How to reproduce it (as minimally and precisely as possible): Create two Kubernetes clusters (1.14) with different cluster names to the same resource group than create kubernetes services of load balancer type in each cluster. Anything else we need to know?: Environment: Kubernetes version (use ): 1.4 Cloud provider or hardware configuration: Azure OS (e.g: ): Kernel (e.g. ): Install tools: Network plugin and version (if this is a network-related bug): Others:
Reiterating the mentions to trigger a notification: <detailsIn response to : Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
thanks for reporting the issue, this is the bug we should fix for sharing resource group. /sig azure /kind bug /assign

Discussion:
// ServiceAnnotationLoadBalancerMixedProtocols is the annotation used on the service // to create both TCP and UDP protocols when creating load balancer rules. ServiceAnnotationLoadBalancerMixedProtocols = "service.beta.kubernetes.io/azure-load-balancer-mixed-protocols" <ins> // serviceTagKey is the service key applied for public IP tags. serviceTagKey = "service" // clusterNameKey is the cluster name key applied for public IP tags. clusterNameKey = "kubernetes-cluster-name" </ins> ) var (

return lbStatus.Ingress[0].IP, nil } <del> func (az *Cloud) ensurePublicIPExists(service *v1.Service, pipName string, domainNameLabel string) (*network.PublicIPAddress, error) { </del> <ins> func (az *Cloud) ensurePublicIPExists(service *v1.Service, pipName string, domainNameLabel, clusterName string) (*network.PublicIPAddress, error) { </ins> pipResourceGroup := az.getPublicIPAddressResourceGroup(service) pip, existsPip, err := az.getPublicIPAddress(pipResourceGroup, pipName) if err != nil {

DomainNameLabel: &domainNameLabel, } } <del> pip.Tags = map[string]*string{"service": &serviceName} </del> <ins> pip.Tags = map[string]*string{ serviceTagKey:  &serviceName, clusterNameKey: &clusterName, } </ins> if az.useStandardLoadBalancer() { pip.Sku = &network.PublicIPAddressSku{ Name: network.PublicIPAddressSkuNameStandard,

return nil, err } domainNameLabel := getPublicIPDomainNameLabel(service) <del> pip, err := az.ensurePublicIPExists(service, pipName, domainNameLabel) </del> <ins> pip, err := az.ensurePublicIPExists(service, pipName, domainNameLabel, clusterName) </ins> if err != nil { return nil, err }

for i := range pips { pip := pips[i] <del> if pip.Tags != nil && (pip.Tags)["service"] != nil && *(pip.Tags)["service"] == serviceName { </del> <ins> if serviceOwnsPublicIP(&pip, clusterName, serviceName) { </ins> // We need to process for pips belong to this service pipName := *pip.Name if wantLb && !isInternal && pipName == desiredPipName {

// Confirm desired public ip resource exists var pip *network.PublicIPAddress domainNameLabel := getPublicIPDomainNameLabel(service) <del> if pip, err = az.ensurePublicIPExists(service, desiredPipName, domainNameLabel); err != nil { </del> <ins> if pip, err = az.ensurePublicIPExists(service, desiredPipName, domainNameLabel, clusterName); err != nil { </ins> return nil, err } return pip, nil

return nil, nil } <ins> func serviceOwnsPublicIP(pip *network.PublicIPAddress, clusterName, serviceName string) bool { if pip != nil && pip.Tags != nil { serviceTag := pip.Tags[serviceTagKey] clusterTag := pip.Tags[clusterNameKey] if serviceTag != nil && *serviceTag == serviceName { // Backward compatible for clusters upgraded from old releases. // In such case, only "service" tag is set. if clusterTag == nil { return true } // If cluster name tag is set, then return true if it matches. if *clusterTag == clusterName { return true } } } return false } </ins>

assert.Equal(t, len(result), 0, "TestCase[%d]: %s", i, c.desc) } } <ins> func TestServiceOwnsPublicIP(t *testing.T) { tests := []struct { desc        string pip         *network.PublicIPAddress clusterName string serviceName string expected    bool }{ { desc:        "false should be returned when pip is nil", clusterName: "kubernetes", serviceName: "nginx", expected:    false, }, { desc: "false should be returned when service name tag doesn't match", pip: &network.PublicIPAddress{ Tags: map[string]*string{ serviceTagKey: to.StringPtr("nginx"), }, }, serviceName: "web", expected:    false, }, { desc: "true should be returned when service name tag matches and cluster name tag is not set", pip: &network.PublicIPAddress{ Tags: map[string]*string{ serviceTagKey: to.StringPtr("nginx"), }, }, clusterName: "kubernetes", serviceName: "nginx", expected:    true, }, { desc: "false should be returned when cluster name doesn't match", pip: &network.PublicIPAddress{ Tags: map[string]*string{ serviceTagKey:  to.StringPtr("nginx"), clusterNameKey: to.StringPtr("kubernetes"), }, }, clusterName: "k8s", serviceName: "nginx", expected:    false, }, { desc: "false should be returned when cluster name matches while service name doesn't match", pip: &network.PublicIPAddress{ Tags: map[string]*string{ serviceTagKey:  to.StringPtr("web"), clusterNameKey: to.StringPtr("kubernetes"), }, }, clusterName: "kubernetes", serviceName: "nginx", expected:    false, }, { desc: "true should be returned when both service name tag and cluster name match", pip: &network.PublicIPAddress{ Tags: map[string]*string{ serviceTagKey:  to.StringPtr("nginx"), clusterNameKey: to.StringPtr("kubernetes"), }, }, clusterName: "kubernetes", serviceName: "nginx", expected:    true, }, } for i, c := range tests { owns := serviceOwnsPublicIP(c.pip, c.clusterName, c.serviceName) assert.Equal(t, owns, c.expected, "TestCase[%d]: %s", i, c.desc) } } </ins>

t.Errorf("Expected publicIP resource exists, when it is not an internal service") } <del> if publicIP.Tags == nil || publicIP.Tags["service"] == nil { t.Errorf("Expected publicIP resource has tags[service]") </del> <ins> if publicIP.Tags == nil || publicIP.Tags[serviceTagKey] == nil { t.Errorf("Expected publicIP resource has tags[%s]", serviceTagKey) </ins> } serviceName := getServiceName(service) <del> if serviceName != *(publicIP.Tags["service"]) { t.Errorf("Expected publicIP resource has matching tags[service]") </del> <ins> if serviceName != *(publicIP.Tags[serviceTagKey]) { t.Errorf("Expected publicIP resource has matching tags[%s]", serviceTagKey) </ins> } <ins> if publicIP.Tags[clusterNameKey] == nil { t.Errorf("Expected publicIP resource has tags[%s]", clusterNameKey) } if *(publicIP.Tags[clusterNameKey]) != testClusterName { t.Errorf("Expected publicIP resource has matching tags[%s]", clusterNameKey) } </ins> // We cannot use service.Spec.LoadBalancerIP to compare with // Public IP's IPAddress // Because service properties are updated outside of cloudprovider code