From 2b06e8cfd5de139909b34aa1def503da4f056239 Mon Sep 17 00:00:00 2001 From: Brent Barbachem Date: Fri, 22 Mar 2024 12:23:37 -0400 Subject: [PATCH] Add support for Shared VPC Networking This is an update to the stale PR #991 ** Added support for Host Project for a shared VPC in the Network struct. ** Network Resources will now use the host project name if exists, otherwise the normal project. ** Update the cluster getter interface to include the NetworkProject and Indicator for a shared VPC. ** Update reconcilers for girewall rules, subnets and network. ** Update the services to use the host project for resources when a shared vpc is used. --- api/v1beta1/types.go | 4 ++++ api/v1beta1/zz_generated.deepcopy.go | 5 +++++ cloud/interfaces.go | 3 +++ cloud/scope/cluster.go | 18 +++++++++++++++++- cloud/scope/machine.go | 9 +++++++-- cloud/scope/managedcluster.go | 18 +++++++++++++++++- cloud/services/compute/firewalls/reconcile.go | 8 ++++++++ cloud/services/compute/networks/reconcile.go | 11 +++++++++++ cloud/services/compute/networks/service.go | 9 +++++++-- cloud/services/compute/subnets/reconcile.go | 9 +++++++++ cloud/services/compute/subnets/service.go | 7 ++++++- ...structure.cluster.x-k8s.io_gcpclusters.yaml | 4 ++++ ...e.cluster.x-k8s.io_gcpclustertemplates.yaml | 4 ++++ ...re.cluster.x-k8s.io_gcpmanagedclusters.yaml | 4 ++++ 14 files changed, 106 insertions(+), 7 deletions(-) diff --git a/api/v1beta1/types.go b/api/v1beta1/types.go index de44eacaca..f3fb85906f 100644 --- a/api/v1beta1/types.go +++ b/api/v1beta1/types.go @@ -112,6 +112,10 @@ type NetworkSpec struct { // Allow for configuration of load balancer backend (useful for changing apiserver port) // +optional LoadBalancerBackendPort *int32 `json:"loadBalancerBackendPort,omitempty"` + + // HostProject is the name of the host project hosting the shared VPC network resources. + // +optional + HostProject *string `json:"hostProject,omitempty"` } // SubnetSpec configures an GCP Subnet. diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index d5a60f830b..96957d79c5 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -797,6 +797,11 @@ func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { *out = new(int32) **out = **in } + if in.HostProject != nil { + in, out := &in.HostProject, &out.HostProject + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkSpec. diff --git a/cloud/interfaces.go b/cloud/interfaces.go index 12dfe6f08e..f5f17f7137 100644 --- a/cloud/interfaces.go +++ b/cloud/interfaces.go @@ -46,6 +46,7 @@ type ReconcilerWithResult interface { // Client is an interface which can get cloud client. type Client interface { Cloud() Cloud + NetworkCloud() Cloud } // ClusterGetter is an interface which can get cluster information. @@ -56,6 +57,8 @@ type ClusterGetter interface { Name() string Namespace() string NetworkName() string + NetworkProject() string + IsSharedVpc() bool Network() *infrav1.Network AdditionalLabels() infrav1.Labels FailureDomains() clusterv1.FailureDomains diff --git a/cloud/scope/cluster.go b/cloud/scope/cluster.go index 33ed78ea38..20f390e8da 100644 --- a/cloud/scope/cluster.go +++ b/cloud/scope/cluster.go @@ -90,11 +90,27 @@ func (s *ClusterScope) Cloud() cloud.Cloud { return newCloud(s.Project(), s.GCPServices) } +// NetworkCloud returns initialized cloud. +func (s *ClusterScope) NetworkCloud() cloud.Cloud { + return newCloud(s.NetworkProject(), s.GCPServices) +} + // Project returns the current project name. func (s *ClusterScope) Project() string { return s.GCPCluster.Spec.Project } +// NetworkProject returns the project name where network resources should exist. +// The network project defaults to the Project when one is not supplied. +func (s *ClusterScope) NetworkProject() string { + return ptr.Deref(s.GCPCluster.Spec.Network.HostProject, s.Project()) +} + +// IsSharedVpc returns true If sharedVPC used else , returns false. +func (s *ClusterScope) IsSharedVpc() bool { + return s.NetworkProject() != s.Project() +} + // Region returns the cluster region. func (s *ClusterScope) Region() string { return s.GCPCluster.Spec.Region @@ -117,7 +133,7 @@ func (s *ClusterScope) NetworkName() string { // NetworkLink returns the partial URL for the network. func (s *ClusterScope) NetworkLink() string { - return fmt.Sprintf("projects/%s/global/networks/%s", s.Project(), s.NetworkName()) + return fmt.Sprintf("projects/%s/global/networks/%s", s.NetworkProject(), s.NetworkName()) } // Network returns the cluster network object. diff --git a/cloud/scope/machine.go b/cloud/scope/machine.go index 15b0dc1414..a139cc9b6b 100644 --- a/cloud/scope/machine.go +++ b/cloud/scope/machine.go @@ -94,6 +94,11 @@ func (m *MachineScope) Cloud() cloud.Cloud { return m.ClusterGetter.Cloud() } +// NetworkCloud returns initialized network cloud. +func (m *MachineScope) NetworkCloud() cloud.Cloud { + return m.ClusterGetter.NetworkCloud() +} + // Zone returns the FailureDomain for the GCPMachine. func (m *MachineScope) Zone() string { if m.Machine.Spec.FailureDomain == nil { @@ -318,7 +323,7 @@ func (m *MachineScope) InstanceAdditionalDiskSpec() []*compute.AttachedDisk { // InstanceNetworkInterfaceSpec returns compute network interface spec. func (m *MachineScope) InstanceNetworkInterfaceSpec() *compute.NetworkInterface { networkInterface := &compute.NetworkInterface{ - Network: path.Join("projects", m.ClusterGetter.Project(), "global", "networks", m.ClusterGetter.NetworkName()), + Network: path.Join("projects", m.ClusterGetter.NetworkProject(), "global", "networks", m.ClusterGetter.NetworkName()), } if m.GCPMachine.Spec.PublicIP != nil && *m.GCPMachine.Spec.PublicIP { @@ -331,7 +336,7 @@ func (m *MachineScope) InstanceNetworkInterfaceSpec() *compute.NetworkInterface } if m.GCPMachine.Spec.Subnet != nil { - networkInterface.Subnetwork = path.Join("regions", m.ClusterGetter.Region(), "subnetworks", *m.GCPMachine.Spec.Subnet) + networkInterface.Subnetwork = path.Join("projects", m.ClusterGetter.NetworkProject(), "regions", m.ClusterGetter.Region(), "subnetworks", *m.GCPMachine.Spec.Subnet) } return networkInterface diff --git a/cloud/scope/managedcluster.go b/cloud/scope/managedcluster.go index 4e78c7044e..f30173961f 100644 --- a/cloud/scope/managedcluster.go +++ b/cloud/scope/managedcluster.go @@ -93,6 +93,11 @@ func (s *ManagedClusterScope) Cloud() cloud.Cloud { return newCloud(s.Project(), s.GCPServices) } +// NetworkCloud returns initialized cloud. +func (s *ManagedClusterScope) NetworkCloud() cloud.Cloud { + return newCloud(s.NetworkProject(), s.GCPServices) +} + // Project returns the current project name. func (s *ManagedClusterScope) Project() string { return s.GCPManagedCluster.Spec.Project @@ -118,9 +123,20 @@ func (s *ManagedClusterScope) NetworkName() string { return ptr.Deref(s.GCPManagedCluster.Spec.Network.Name, "default") } +// NetworkProject returns the project name where network resources should exist. +// The network project defaults to the Project when one is not supplied. +func (s *ManagedClusterScope) NetworkProject() string { + return ptr.Deref(s.GCPManagedCluster.Spec.Network.HostProject, s.Project()) +} + +// IsSharedVpc returns true If sharedVPC used else , returns false. +func (s *ManagedClusterScope) IsSharedVpc() bool { + return s.NetworkProject() != s.Project() +} + // NetworkLink returns the partial URL for the network. func (s *ManagedClusterScope) NetworkLink() string { - return fmt.Sprintf("projects/%s/global/networks/%s", s.Project(), s.NetworkName()) + return fmt.Sprintf("projects/%s/global/networks/%s", s.NetworkProject(), s.NetworkName()) } // Network returns the cluster network object. diff --git a/cloud/services/compute/firewalls/reconcile.go b/cloud/services/compute/firewalls/reconcile.go index 9c1daa07fd..63f46424b9 100644 --- a/cloud/services/compute/firewalls/reconcile.go +++ b/cloud/services/compute/firewalls/reconcile.go @@ -27,6 +27,10 @@ import ( // Reconcile reconcile cluster firewall compoenents. func (s *Service) Reconcile(ctx context.Context) error { log := log.FromContext(ctx) + if s.scope.IsSharedVpc() { + log.Info("VPC enabled. Ignore Reconciling firewall resources") + return nil + } log.Info("Reconciling firewall resources") for _, spec := range s.scope.FirewallRulesSpec() { log.V(2).Info("Looking firewall", "name", spec.Name) @@ -49,6 +53,10 @@ func (s *Service) Reconcile(ctx context.Context) error { // Delete delete cluster firewall compoenents. func (s *Service) Delete(ctx context.Context) error { log := log.FromContext(ctx) + if s.scope.IsSharedVpc() { + log.Info("VPC enabled. Ignore Deleting firewall resources") + return nil + } log.Info("Deleting firewall resources") for _, spec := range s.scope.FirewallRulesSpec() { log.V(2).Info("Deleting firewall", "name", spec.Name) diff --git a/cloud/services/compute/networks/reconcile.go b/cloud/services/compute/networks/reconcile.go index 0beaaaf688..70932cd682 100644 --- a/cloud/services/compute/networks/reconcile.go +++ b/cloud/services/compute/networks/reconcile.go @@ -52,6 +52,12 @@ func (s *Service) Reconcile(ctx context.Context) error { // Delete delete cluster network components. func (s *Service) Delete(ctx context.Context) error { log := log.FromContext(ctx) + if s.scope.IsSharedVpc() { + log.Info("VPC enabled. Ignore Deleting network resources") + s.scope.Network().Router = nil + s.scope.Network().SelfLink = nil + return nil + } log.Info("Deleting network resources") networkKey := meta.GlobalKey(s.scope.NetworkName()) log.V(2).Info("Looking for network before deleting", "name", networkKey) @@ -102,6 +108,11 @@ func (s *Service) createOrGetNetwork(ctx context.Context) (*compute.Network, err return nil, err } + if s.scope.IsSharedVpc() { + log.Error(err, "VPC is enabled. Error looking for network", "name", s.scope.NetworkName()) + return nil, err + } + log.V(2).Info("Creating a network", "name", s.scope.NetworkName()) if err := s.networks.Insert(ctx, networkKey, s.scope.NetworkSpec()); err != nil { log.Error(err, "Error creating a network", "name", s.scope.NetworkName()) diff --git a/cloud/services/compute/networks/service.go b/cloud/services/compute/networks/service.go index 6d01d0fafc..47b14ec0aa 100644 --- a/cloud/services/compute/networks/service.go +++ b/cloud/services/compute/networks/service.go @@ -54,9 +54,14 @@ var _ cloud.Reconciler = &Service{} // New returns Service from given scope. func New(scope Scope) *Service { + scopeCloud := scope.Cloud() + if scope.IsSharedVpc() { + scopeCloud = scope.NetworkCloud() + } + return &Service{ scope: scope, - networks: scope.Cloud().Networks(), - routers: scope.Cloud().Routers(), + networks: scopeCloud.Networks(), + routers: scopeCloud.Routers(), } } diff --git a/cloud/services/compute/subnets/reconcile.go b/cloud/services/compute/subnets/reconcile.go index 403c49fb74..4ff1a6ce3c 100644 --- a/cloud/services/compute/subnets/reconcile.go +++ b/cloud/services/compute/subnets/reconcile.go @@ -41,6 +41,10 @@ func (s *Service) Reconcile(ctx context.Context) error { // Delete deletes cluster subnetwork components. func (s *Service) Delete(ctx context.Context) error { logger := log.FromContext(ctx) + if s.scope.IsSharedVpc() { + logger.Info("VPC enabled. Ignore Deleting subnet resources") + return nil + } for _, subnetSpec := range s.scope.SubnetSpecs() { logger.V(2).Info("Deleting a subnet", "name", subnetSpec.Name) subnetKey := meta.RegionalKey(subnetSpec.Name, s.scope.Region()) @@ -68,6 +72,11 @@ func (s *Service) createOrGetSubnets(ctx context.Context) ([]*compute.Subnetwork return subnets, err } + if s.scope.IsSharedVpc() { + logger.Error(err, "VPC is enabled. Error looking for subnetwork", "name", subnetSpec.Name) + return nil, err + } + // Subnet was not found, let's create it logger.V(2).Info("Creating a subnet", "name", subnetSpec.Name) if err := s.subnets.Insert(ctx, subnetKey, subnetSpec); err != nil { diff --git a/cloud/services/compute/subnets/service.go b/cloud/services/compute/subnets/service.go index 14ddad9e1c..69bea34164 100644 --- a/cloud/services/compute/subnets/service.go +++ b/cloud/services/compute/subnets/service.go @@ -46,8 +46,13 @@ var _ cloud.Reconciler = &Service{} // New returns Service from given scope. func New(scope Scope) *Service { + cloudScope := scope.Cloud() + if scope.IsSharedVpc() { + cloudScope = scope.NetworkCloud() + } + return &Service{ scope: scope, - subnets: scope.Cloud().Subnetworks(), + subnets: cloudScope.Subnetworks(), } } diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml index a75541ddf1..f6b98248fb 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml @@ -110,6 +110,10 @@ spec: predetermined range as described in Auto mode VPC network IP ranges. \n Defaults to true." type: boolean + hostProject: + description: HostProject is the name of the host project hosting + the shared VPC network resources. + type: string loadBalancerBackendPort: description: Allow for configuration of load balancer backend (useful for changing apiserver port) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclustertemplates.yaml index 8b1a67f40c..cfa22594a6 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclustertemplates.yaml @@ -125,6 +125,10 @@ spec: region. Each subnet has a predetermined range as described in Auto mode VPC network IP ranges. \n Defaults to true." type: boolean + hostProject: + description: HostProject is the name of the host project + hosting the shared VPC network resources. + type: string loadBalancerBackendPort: description: Allow for configuration of load balancer backend (useful for changing apiserver port) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpmanagedclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpmanagedclusters.yaml index 37c6e0564e..6d148c0165 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpmanagedclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpmanagedclusters.yaml @@ -105,6 +105,10 @@ spec: predetermined range as described in Auto mode VPC network IP ranges. \n Defaults to true." type: boolean + hostProject: + description: HostProject is the name of the host project hosting + the shared VPC network resources. + type: string loadBalancerBackendPort: description: Allow for configuration of load balancer backend (useful for changing apiserver port)