From ec0f92be2fc8b19dde39f46cfdb8d0ea2e9d54cd Mon Sep 17 00:00:00 2001 From: Snehal Amrutkar Date: Mon, 20 Jul 2020 11:19:04 +0530 Subject: [PATCH 1/2] Auto create Cloud NAT if network is created by CAPG --- api/v1alpha3/types.go | 5 ++ api/v1alpha3/zz_generated.deepcopy.go | 5 ++ cloud/services/compute/network.go | 66 +++++++++++++++++++ ...tructure.cluster.x-k8s.io_gcpclusters.yaml | 4 ++ 4 files changed, 80 insertions(+) diff --git a/api/v1alpha3/types.go b/api/v1alpha3/types.go index b927ce956..bd81168f4 100644 --- a/api/v1alpha3/types.go +++ b/api/v1alpha3/types.go @@ -44,6 +44,11 @@ type Network struct { // +optional FirewallRules map[string]string `json:"firewallRules,omitempty"` + // Router is the full reference to the router created within the network + // it'll contain the cloud nat gateway + // +optional + Router *string `json:"router,omitempty"` + // APIServerAddress is the IPV4 global address assigned to the load balancer // created for the API Server. // +optional diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index ce76efd2c..07a1b94bd 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -457,6 +457,11 @@ func (in *Network) DeepCopyInto(out *Network) { (*out)[key] = val } } + if in.Router != nil { + in, out := &in.Router, &out.Router + *out = new(string) + **out = **in + } if in.APIServerAddress != nil { in, out := &in.APIServerAddress, &out.APIServerAddress *out = new(string) diff --git a/cloud/services/compute/network.go b/cloud/services/compute/network.go index 20622d475..d32e4f738 100644 --- a/cloud/services/compute/network.go +++ b/cloud/services/compute/network.go @@ -17,6 +17,7 @@ limitations under the License. package compute import ( + "fmt" "github.com/pkg/errors" "google.golang.org/api/compute/v1" "k8s.io/utils/pointer" @@ -31,7 +32,9 @@ func (s *Service) ReconcileNetwork() error { // Create Network spec := s.getNetworkSpec() network, err := s.networks.Get(s.scope.Project(), spec.Name).Do() + autoCreateCloudNat := false if gcperrors.IsNotFound(err) { + autoCreateCloudNat = true op, err := s.networks.Insert(s.scope.Project(), spec).Do() if err != nil { return errors.Wrapf(err, "failed to create network") @@ -47,6 +50,12 @@ func (s *Service) ReconcileNetwork() error { return errors.Wrapf(err, "failed to describe network") } + if autoCreateCloudNat { + if err := s.createCloudNat(network); err != nil { + return errors.Wrapf(err, "failed to create cloudnat gateway") + } + } + s.scope.GCPCluster.Spec.Network.Name = pointer.StringPtr(network.Name) s.scope.GCPCluster.Spec.Network.AutoCreateSubnetworks = pointer.BoolPtr(network.AutoCreateSubnetworks) s.scope.GCPCluster.Status.Network.SelfLink = pointer.StringPtr(network.SelfLink) @@ -89,3 +98,60 @@ func (s *Service) DeleteNetwork() error { s.scope.GCPCluster.Spec.Network.Name = nil return nil } + +func (s *Service) createCloudNat(network *compute.Network) error { + router, err := s.routers.Get(s.scope.Project(), s.scope.Region(), getRouterName(s.scope.NetworkName())).Do() + if gcperrors.IsNotFound(err) { + router = s.getRouterSpec(network) + op, err := s.routers.Insert(s.scope.Project(), s.scope.Region(), router).Do() + if err != nil { + return errors.Wrapf(err, "failed to create router") + } + if err := wait.ForComputeOperation(s.scope.Compute, s.scope.Project(), op); err != nil { + return errors.Wrapf(err, "failed to wait for create router operation") + } + router, err = s.routers.Get(s.scope.Project(), s.scope.Region(), router.Name).Do() + if err != nil { + return errors.Wrapf(err, "failed to get router after create") + } + } else if err != nil { + return errors.Wrapf(err, "failed to get routers") + } + + if len(router.Nats) == 0 { + router.Nats = []*compute.RouterNat{s.getRouterNatSpec()} + op, err := s.routers.Patch(s.scope.Project(), s.scope.Region(), router.Name, router).Do() + if err != nil { + return errors.Wrapf(err, "failed to patch router to create nat") + } + if err := wait.ForComputeOperation(s.scope.Compute, s.scope.Project(), op); err != nil { + return errors.Wrapf(err, "failed to wait for patch router operation") + } + } + + s.scope.GCPCluster.Status.Network.Router = pointer.StringPtr(router.SelfLink) + return nil +} + +func (s *Service) getRouterSpec(network *compute.Network) *compute.Router { + return &compute.Router{ + Name: getRouterName(network.Name), + Network: network.SelfLink, + Nats: []*compute.RouterNat{s.getRouterNatSpec()}, + } +} + +func (s *Service) getRouterNatSpec() *compute.RouterNat { + return &compute.RouterNat{ + Name: getRouterNatName(s.scope.NetworkName()), + NatIpAllocateOption: "AUTO_ONLY", + SourceSubnetworkIpRangesToNat: "ALL_SUBNETWORKS_ALL_IP_RANGES", + } +} + +func getRouterName(network string) string { + return fmt.Sprintf("%s-%s", network, "router") +} +func getRouterNatName(network string) string { + return fmt.Sprintf("%s-%s", network, "nat") +} 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 857c4d4ff..b80c8489e 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml @@ -212,6 +212,10 @@ spec: description: FirewallRules is a map from the name of the rule to its full reference. type: object + router: + description: Router is the full reference to the router created + within the network it'll contain the cloud nat gateway + type: string selfLink: description: SelfLink is the link to the Network used for this cluster. From f321e5d79c59b5122af808cd3d62d9e4b31f4749 Mon Sep 17 00:00:00 2001 From: Snehal Amrutkar Date: Wed, 22 Jul 2020 16:51:53 +0530 Subject: [PATCH 2/2] Handle delete for router --- cloud/services/compute/network.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cloud/services/compute/network.go b/cloud/services/compute/network.go index d32e4f738..5734004d5 100644 --- a/cloud/services/compute/network.go +++ b/cloud/services/compute/network.go @@ -87,6 +87,22 @@ func (s *Service) DeleteNetwork() error { return nil } + // Delete Router. + router, err := s.routers.Get(s.scope.Project(), s.scope.Region(), getRouterName(s.scope.NetworkName())).Do() + if err == nil { + op, err := s.routers.Delete(s.scope.Project(), s.scope.Region(), router.Name).Do() + if err != nil { + return errors.Wrapf(err, "failed to delete router") + } + if err := wait.ForComputeOperation(s.scope.Compute, s.scope.Project(), op); err != nil { + return errors.Wrapf(err, "failed to wait for delete router") + } + } else { + if !gcperrors.IsNotFound(err) { + return errors.Wrapf(err, "failed to get router to delete") + } + } + // Delete Network. op, err := s.networks.Delete(s.scope.Project(), network.Name).Do() if err != nil {