diff --git a/pkg/loadbalancers/forwarding_rules_ipv6.go b/pkg/loadbalancers/forwarding_rules_ipv6.go index 69661e1eb9..b0a9b0adbd 100644 --- a/pkg/loadbalancers/forwarding_rules_ipv6.go +++ b/pkg/loadbalancers/forwarding_rules_ipv6.go @@ -25,6 +25,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/cloud-provider-gcp/providers/gce" "k8s.io/ingress-gce/pkg/composite" "k8s.io/ingress-gce/pkg/events" @@ -128,19 +129,51 @@ func (l4 *L4) deleteChangedIPv6ForwardingRule(existingFwdRule *composite.Forward func (l4netlb *L4NetLB) ensureIPv6ForwardingRule(bsLink string) (*composite.ForwardingRule, error) { start := time.Now() - expectedIPv6FwdRule, err := l4netlb.buildExpectedIPv6ForwardingRule(bsLink) + klog.V(2).Infof("Ensuring external ipv6 forwarding rule for L4 NetLB Service %s/%s, backend service link: %s", l4netlb.Service.Namespace, l4netlb.Service.Name, bsLink) + defer func() { + klog.V(2).Infof("Finished ensuring external ipv6 forwarding rule for L4 NetLB Service %s/%s, time taken: %v", l4netlb.Service.Namespace, l4netlb.Service.Name, time.Since(start)) + }() + + expectedIPv6FrName := l4netlb.ipv6FRName() + existingIPv6FwdRule, err := l4netlb.forwardingRules.Get(expectedIPv6FrName) if err != nil { - return nil, fmt.Errorf("l4netlb.buildExpectedIPv6ForwardingRule(%s) returned error %w, want nil", bsLink, err) + klog.Errorf("l4netlb.forwardingRules.Get(%s) returned error %v", expectedIPv6FrName, err) + return nil, err } - klog.V(2).Infof("Ensuring external ipv6 forwarding rule %s for L4 NetLB Service %s/%s, backend service link: %s", expectedIPv6FwdRule.Name, l4netlb.Service.Namespace, l4netlb.Service.Name, bsLink) - defer func() { - klog.V(2).Infof("Finished ensuring external ipv6 forwarding rule %s for L4 NetLB Service %s/%s, time taken: %v", expectedIPv6FwdRule.Name, l4netlb.Service.Namespace, l4netlb.Service.Name, time.Since(start)) - }() + customSubnetName := gce.GetLoadBalancerAnnotationSubnet(l4netlb.Service) + subnetworkURL := l4netlb.cloud.SubnetworkURL() + if customSubnetName != "" { + subnetworkURL, err = l4netlb.getSubnetworkURLByName(customSubnetName) + if err != nil { + return nil, err + } + } + + // Determine IP which will be used for this LB. If no forwarding rule has been established + // or specified in the Service spec, then requestedIP = "". + ipv6ToUse := ipv6IPToUse(existingIPv6FwdRule, subnetworkURL) + if !l4netlb.cloud.IsLegacyNetwork() { + nm := types.NamespacedName{Namespace: l4netlb.Service.Namespace, Name: l4netlb.Service.Name}.String() + addrMgr := newAddressManager(l4netlb.cloud, nm, l4netlb.cloud.Region(), subnetworkURL, expectedIPv6FrName, ipv6ToUse, cloud.SchemeExternal, cloud.NetworkTierPremium, IPv6Version) + + ipv6ToUse, _, err = addrMgr.HoldAddress() + if err != nil { + return nil, err + } + klog.V(2).Infof("ensureIPv6ForwardingRule(%v): reserved IP %q for the forwarding rule %s", nm, ipv6ToUse, expectedIPv6FrName) + defer func() { + // Release the address that was reserved, in all cases. If the forwarding rule was successfully created, + // the ephemeral IP is not needed anymore. If it was not created, the address should be released to prevent leaks. + if err := addrMgr.ReleaseAddress(); err != nil { + klog.Errorf("ensureIPv6ForwardingRule: failed to release address reservation, possibly causing an orphan: %v", err) + } + }() + } - existingIPv6FwdRule, err := l4netlb.forwardingRules.Get(expectedIPv6FwdRule.Name) + expectedIPv6FwdRule, err := l4netlb.buildExpectedIPv6ForwardingRule(bsLink, ipv6ToUse, subnetworkURL) if err != nil { - return nil, fmt.Errorf("l4netlb.forwardingRules.GetForwardingRule(%s) returned error %w, want nil", expectedIPv6FwdRule.Name, err) + return nil, fmt.Errorf("l4netlb.buildExpectedIPv6ForwardingRule(%s) returned error %w, want nil", bsLink, err) } if existingIPv6FwdRule != nil { @@ -167,7 +200,7 @@ func (l4netlb *L4NetLB) ensureIPv6ForwardingRule(bsLink string) (*composite.Forw return createdFr, err } -func (l4netlb *L4NetLB) buildExpectedIPv6ForwardingRule(bsLink string) (*composite.ForwardingRule, error) { +func (l4netlb *L4NetLB) buildExpectedIPv6ForwardingRule(bsLink, ipv6ToUse, subnetworkURL string) (*composite.ForwardingRule, error) { frName := l4netlb.ipv6FRName() frDesc, err := utils.MakeL4IPv6ForwardingRuleDescription(l4netlb.Service) @@ -181,13 +214,14 @@ func (l4netlb *L4NetLB) buildExpectedIPv6ForwardingRule(bsLink string) (*composi fr := &composite.ForwardingRule{ Name: frName, Description: frDesc, + IPAddress: ipv6ToUse, IPProtocol: protocol, PortRange: portRange, LoadBalancingScheme: string(cloud.SchemeExternal), BackendService: bsLink, IpVersion: IPv6Version, NetworkTier: cloud.NetworkTierPremium.ToGCEValue(), - Subnetwork: l4netlb.cloud.SubnetworkURL(), + Subnetwork: subnetworkURL, } return fr, nil diff --git a/pkg/loadbalancers/l4netlb.go b/pkg/loadbalancers/l4netlb.go index 8812fb2523..bfcccce132 100644 --- a/pkg/loadbalancers/l4netlb.go +++ b/pkg/loadbalancers/l4netlb.go @@ -472,3 +472,11 @@ func (l4netlb *L4NetLB) hasAnnotation(annotationKey string) bool { func (l4netlb *L4NetLB) frName() string { return utils.LegacyForwardingRuleName(l4netlb.Service) } + +func (l4netlb *L4NetLB) getSubnetworkURLByName(subnetName string) (string, error) { + subnetKey, err := l4netlb.createKey(subnetName) + if err != nil { + return "", err + } + return cloud.SelfLink(meta.VersionGA, l4netlb.cloud.NetworkProjectID(), "subnetworks", subnetKey), nil +}