From dafbc17c5a78ae6d55f8dce0b3a45649b803357d Mon Sep 17 00:00:00 2001 From: Lily <21621231@zju.edu.cn> Date: Sat, 26 Jan 2019 14:43:04 +0800 Subject: [PATCH] add natGateways tasks for ALICloud --- pkg/model/alimodel/context.go | 27 +++ pkg/model/alimodel/network.go | 29 +++ pkg/resources/ali/ali.go | 139 ++++++++++++++ pkg/resources/ops/BUILD.bazel | 2 + pkg/resources/ops/collector.go | 4 + upup/pkg/fi/cloudup/alitasks/BUILD.bazel | 6 + upup/pkg/fi/cloudup/alitasks/eip_fitask.go | 75 ++++++++ .../alitasks/eip_natgateway_association.go | 165 +++++++++++++++++ upup/pkg/fi/cloudup/alitasks/natgateway.go | 129 +++++++++++++ .../fi/cloudup/alitasks/natgateway_fitask.go | 75 ++++++++ upup/pkg/fi/cloudup/alitasks/vswitchSNAT.go | 175 ++++++++++++++++++ .../fi/cloudup/alitasks/vswitchsnat_fitask.go | 75 ++++++++ upup/pkg/fi/cloudup/aliup/ali_cloud.go | 7 + 13 files changed, 908 insertions(+) create mode 100644 upup/pkg/fi/cloudup/alitasks/eip_fitask.go create mode 100644 upup/pkg/fi/cloudup/alitasks/eip_natgateway_association.go create mode 100644 upup/pkg/fi/cloudup/alitasks/natgateway.go create mode 100644 upup/pkg/fi/cloudup/alitasks/natgateway_fitask.go create mode 100644 upup/pkg/fi/cloudup/alitasks/vswitchSNAT.go create mode 100644 upup/pkg/fi/cloudup/alitasks/vswitchsnat_fitask.go diff --git a/pkg/model/alimodel/context.go b/pkg/model/alimodel/context.go index 1ca6ef36f7..bf337005db 100644 --- a/pkg/model/alimodel/context.go +++ b/pkg/model/alimodel/context.go @@ -49,6 +49,33 @@ func (c *ALIModelContext) GetNameForVSwitch(subnetName string) string { return subnetName + "." + c.ClusterName() } +// LinkToNateGateway returns the NatGateway object the cluster is located in +func (c *ALIModelContext) LinkToNatGateway() *alitasks.NatGateway { + return &alitasks.NatGateway{Name: s(c.GetNameForNatGateway())} +} + +func (c *ALIModelContext) GetNameForNatGateway() string { + return c.ClusterName() +} + +// LinkToEIP returns the EIP object the NateGatway is associated to +func (c *ALIModelContext) LinkToEIP() *alitasks.EIP { + return &alitasks.EIP{Name: s(c.GetNameForEIP())} +} + +func (c *ALIModelContext) GetNameForEIP() string { + return c.ClusterName() +} + +// LinkToVSwitchSNAT returns the VSwitchSNAT object the cluster is located in +func (c *ALIModelContext) LinkToVSwitchSNAT(subnetName string) *alitasks.VSwitch { + return &alitasks.VSwitch{Name: s(c.GetNameForVSwitch(subnetName))} +} + +func (c *ALIModelContext) GetNameForVSwitchSNAT(subnetName string) string { + return subnetName + "." + c.ClusterName() +} + // LinkLoadBalancer returns the LoadBalancer object the cluster is located in func (c *ALIModelContext) LinkLoadBalancer() *alitasks.LoadBalancer { return &alitasks.LoadBalancer{Name: s(c.GetNameForLoadBalancer())} diff --git a/pkg/model/alimodel/network.go b/pkg/model/alimodel/network.go index c9ecaf1b4b..15359012e8 100644 --- a/pkg/model/alimodel/network.go +++ b/pkg/model/alimodel/network.go @@ -50,6 +50,21 @@ func (b *NetworkModelBuilder) Build(c *fi.ModelBuilderContext) error { c.AddTask(vpc) } + natGateway := &alitasks.NatGateway{ + Name: s(b.GetNameForNatGateway()), + Lifecycle: b.Lifecycle, + VPC: b.LinkToVPC(), + } + c.AddTask(natGateway) + + eip := &alitasks.EIP{ + Name: s(b.GetNameForEIP()), + Lifecycle: b.Lifecycle, + NatGateway: b.LinkToNatGateway(), + Available: fi.Bool(false), + } + c.AddTask(eip) + for i := range b.Cluster.Spec.Subnets { subnetSpec := &b.Cluster.Spec.Subnets[i] @@ -69,6 +84,20 @@ func (b *NetworkModelBuilder) Build(c *fi.ModelBuilderContext) error { c.AddTask(vswitch) + vswitchSNAT := &alitasks.VSwitchSNAT{ + Name: s(b.GetNameForVSwitchSNAT(subnetSpec.Name)), + Lifecycle: b.Lifecycle, + NatGateway: b.LinkToNatGateway(), + VSwitch: b.LinkToVSwitch(subnetSpec.Name), + EIP: b.LinkToEIP(), + } + + if subnetSpec.ProviderID != "" { + vswitchSNAT.Shared = fi.Bool(true) + } + + c.AddTask(vswitchSNAT) + } return nil diff --git a/pkg/resources/ali/ali.go b/pkg/resources/ali/ali.go index 602be61ca1..a6adc66a91 100644 --- a/pkg/resources/ali/ali.go +++ b/pkg/resources/ali/ali.go @@ -48,6 +48,8 @@ const ( typeVolume = "Volume" typeSSHKey = "SSHKey" typeVPC = "VPC" + typeNatGateway = "NatGateway" + typeEIP = "EIP" ) type clusterDiscoveryALI struct { @@ -596,6 +598,8 @@ func (d *clusterDiscoveryALI) ListVPC() ([]*resources.Resource, error) { } resourceTrackers = append(resourceTrackers, resourceTracker) } + + ListNatGateway(d, vpcsToDelete[0], &resourceTrackers) } return resourceTrackers, nil @@ -689,3 +693,138 @@ func DeleteVolume(cloud fi.Cloud, r *resources.Resource) error { } return nil } + +func ListNatGateway(d *clusterDiscoveryALI, vpcID string, resourceTrackers *[]*resources.Resource) error { + + // Delete NateGateway with specified name. All of the EIPs will be deleted. + // We think the NateGateway which owns the designated name is owned. + natGatewaysToDelete := []string{} + eipToDelete := []string{} + + name := d.clusterName + pageNumber := 1 + pageSize := 50 + for { + describeNatGatewaysArgs := &ecs.DescribeNatGatewaysArgs{ + RegionId: common.Region(d.aliCloud.Region()), + VpcId: vpcID, + Pagination: common.Pagination{ + PageNumber: pageNumber, + PageSize: pageSize, + }, + } + + natGateways, _, err := d.aliCloud.VpcClient().DescribeNatGateways(describeNatGatewaysArgs) + if err != nil { + return fmt.Errorf("err listing NatGateway:%v", err) + } + + if len(natGateways) != 0 { + for _, nateGateway := range natGateways { + natGatewaysToDelete = append(natGatewaysToDelete, nateGateway.NatGatewayId) + for _, ip := range nateGateway.IpLists.IpList { + eipToDelete = append(eipToDelete, ip.AllocationId) + } + } + } + if len(natGateways) < pageSize { + break + } else { + pageNumber++ + } + } + + if len(natGatewaysToDelete) > 1 { + glog.V(8).Infof("Found multiple natGateways with name %q", name) + } else if len(natGatewaysToDelete) == 1 { + natGatwayTracker := &resources.Resource{ + Name: name, + ID: natGatewaysToDelete[0], + Type: typeNatGateway, + Deleter: DeleteNatGateway, + } + *resourceTrackers = append(*resourceTrackers, natGatwayTracker) + natGatwayTracker.Blocks = append(natGatwayTracker.Blocks, typeVPC+":"+vpcID) + for _, eip := range eipToDelete { + + resourceTracker := &resources.Resource{ + Name: name, + ID: eip, + Type: typeEIP, + Deleter: DeleteEIP, + } + resourceTracker.Blocked = append(resourceTracker.Blocked, typeNatGateway+":"+natGatwayTracker.ID) + *resourceTrackers = append(*resourceTrackers, resourceTracker) + } + } + + return nil +} + +func DeleteNatGateway(cloud fi.Cloud, r *resources.Resource) error { + c := cloud.(aliup.ALICloud) + + describeNatGatewaysArgs := &ecs.DescribeNatGatewaysArgs{ + RegionId: common.Region(c.Region()), + NatGatewayId: r.ID, + } + + natGateways, _, err := c.VpcClient().DescribeNatGateways(describeNatGatewaysArgs) + if err != nil { + return fmt.Errorf("err listing NatGateway:%v", err) + } + + if len(natGateways) > 1 { + glog.V(8).Infof("Found multiple natGateways with ID %q", r.ID) + } else if len(natGateways) == 1 { + for _, snatTableId := range natGateways[0].SnatTableIds.SnatTableId { + describeSnatTableEntriesArgs := &ecs.DescribeSnatTableEntriesArgs{ + RegionId: common.Region(c.Region()), + SnatTableId: snatTableId, + } + snatTableEntries, _, err := c.VpcClient().DescribeSnatTableEntries(describeSnatTableEntriesArgs) + if err != nil { + return fmt.Errorf("err Listing snatTableEntries:%v", err) + } + for _, snatTableEntry := range snatTableEntries { + deleteSnatEntryArgs := &ecs.DeleteSnatEntryArgs{ + RegionId: common.Region(c.Region()), + SnatTableId: snatTableId, + SnatEntryId: snatTableEntry.SnatEntryId, + } + err := c.VpcClient().DeleteSnatEntry(deleteSnatEntryArgs) + if err != nil { + return fmt.Errorf("err deleting SnatEntryArgs:%v", err) + } + } + } + for _, ip := range natGateways[0].IpLists.IpList { + err := c.VpcClient().UnassociateEipAddress(ip.AllocationId, r.ID) + if err != nil { + return fmt.Errorf("err unassociating EIP:%v", err) + } + } + } + + glog.V(2).Infof("Removing NatGateway with Id %s", r.ID) + deleteNatGatewayArgs := &ecs.DeleteNatGatewayArgs{ + RegionId: common.Region(c.Region()), + NatGatewayId: r.ID, + } + err = c.EcsClient().DeleteNatGateway(deleteNatGatewayArgs) + if err != nil { + return fmt.Errorf("err deleting NatGateway:%v", err) + } + return nil +} + +func DeleteEIP(cloud fi.Cloud, r *resources.Resource) error { + c := cloud.(aliup.ALICloud) + glog.V(2).Infof("Removing EIP with Id %s", r.ID) + + err := c.VpcClient().ReleaseEipAddress(r.ID) + if err != nil { + return fmt.Errorf("err deleting EIP:%v", err) + } + return nil +} diff --git a/pkg/resources/ops/BUILD.bazel b/pkg/resources/ops/BUILD.bazel index b1d6b00a2b..997b848405 100644 --- a/pkg/resources/ops/BUILD.bazel +++ b/pkg/resources/ops/BUILD.bazel @@ -11,11 +11,13 @@ go_library( deps = [ "//pkg/apis/kops:go_default_library", "//pkg/resources:go_default_library", + "//pkg/resources/ali:go_default_library", "//pkg/resources/aws:go_default_library", "//pkg/resources/digitalocean:go_default_library", "//pkg/resources/gce:go_default_library", "//pkg/resources/openstack:go_default_library", "//upup/pkg/fi:go_default_library", + "//upup/pkg/fi/cloudup/aliup:go_default_library", "//upup/pkg/fi/cloudup/awsup:go_default_library", "//upup/pkg/fi/cloudup/gce:go_default_library", "//upup/pkg/fi/cloudup/openstack:go_default_library", diff --git a/pkg/resources/ops/collector.go b/pkg/resources/ops/collector.go index 0e5698461d..a80e472988 100644 --- a/pkg/resources/ops/collector.go +++ b/pkg/resources/ops/collector.go @@ -21,11 +21,13 @@ import ( "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/resources" + "k8s.io/kops/pkg/resources/ali" "k8s.io/kops/pkg/resources/aws" "k8s.io/kops/pkg/resources/digitalocean" "k8s.io/kops/pkg/resources/gce" "k8s.io/kops/pkg/resources/openstack" "k8s.io/kops/upup/pkg/fi" + cloudali "k8s.io/kops/upup/pkg/fi/cloudup/aliup" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" cloudgce "k8s.io/kops/upup/pkg/fi/cloudup/gce" cloudopenstack "k8s.io/kops/upup/pkg/fi/cloudup/openstack" @@ -45,6 +47,8 @@ func ListResources(cloud fi.Cloud, clusterName string, region string) (map[strin return openstack.ListResources(cloud.(cloudopenstack.OpenstackCloud), clusterName) case kops.CloudProviderVSphere: return resources.ListResourcesVSphere(cloud.(*vsphere.VSphereCloud), clusterName) + case kops.CloudProviderALI: + return ali.ListResourcesALI(cloud.(cloudali.ALICloud), clusterName, region) default: return nil, fmt.Errorf("delete on clusters on %q not (yet) supported", cloud.ProviderID()) } diff --git a/upup/pkg/fi/cloudup/alitasks/BUILD.bazel b/upup/pkg/fi/cloudup/alitasks/BUILD.bazel index 12bedd92d2..0a9e39e489 100644 --- a/upup/pkg/fi/cloudup/alitasks/BUILD.bazel +++ b/upup/pkg/fi/cloudup/alitasks/BUILD.bazel @@ -5,6 +5,8 @@ go_library( srcs = [ "disk.go", "disk_fitask.go", + "eip_fitask.go", + "eip_natgateway_association.go", "launchconfiguration.go", "launchconfiguration_fitask.go", "loadbalancer.go", @@ -13,6 +15,8 @@ go_library( "loadbalancerlistener_fitask.go", "loadbalancerwhitelist.go", "loadbalancerwhitelist_fitask.go", + "natgateway.go", + "natgateway_fitask.go", "rampolicy.go", "rampolicy_fitask.go", "ramrole.go", @@ -28,7 +32,9 @@ go_library( "vpc.go", "vpc_fitask.go", "vswitch.go", + "vswitchSNAT.go", "vswitch_fitask.go", + "vswitchsnat_fitask.go", ], importpath = "k8s.io/kops/upup/pkg/fi/cloudup/alitasks", visibility = ["//visibility:public"], diff --git a/upup/pkg/fi/cloudup/alitasks/eip_fitask.go b/upup/pkg/fi/cloudup/alitasks/eip_fitask.go new file mode 100644 index 0000000000..72d0ff45e6 --- /dev/null +++ b/upup/pkg/fi/cloudup/alitasks/eip_fitask.go @@ -0,0 +1,75 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ""fitask" -type=EIP"; DO NOT EDIT + +package alitasks + +import ( + "encoding/json" + + "k8s.io/kops/upup/pkg/fi" +) + +// EIP + +// JSON marshaling boilerplate +type realEIP EIP + +// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string +func (o *EIP) UnmarshalJSON(data []byte) error { + var jsonName string + if err := json.Unmarshal(data, &jsonName); err == nil { + o.Name = &jsonName + return nil + } + + var r realEIP + if err := json.Unmarshal(data, &r); err != nil { + return err + } + *o = EIP(r) + return nil +} + +var _ fi.HasLifecycle = &EIP{} + +// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle +func (o *EIP) GetLifecycle() *fi.Lifecycle { + return o.Lifecycle +} + +// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle +func (o *EIP) SetLifecycle(lifecycle fi.Lifecycle) { + o.Lifecycle = &lifecycle +} + +var _ fi.HasName = &EIP{} + +// GetName returns the Name of the object, implementing fi.HasName +func (o *EIP) GetName() *string { + return o.Name +} + +// SetName sets the Name of the object, implementing fi.SetName +func (o *EIP) SetName(name string) { + o.Name = &name +} + +// String is the stringer function for the task, producing readable output using fi.TaskAsString +func (o *EIP) String() string { + return fi.TaskAsString(o) +} diff --git a/upup/pkg/fi/cloudup/alitasks/eip_natgateway_association.go b/upup/pkg/fi/cloudup/alitasks/eip_natgateway_association.go new file mode 100644 index 0000000000..d27167695e --- /dev/null +++ b/upup/pkg/fi/cloudup/alitasks/eip_natgateway_association.go @@ -0,0 +1,165 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package alitasks + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/golang/glog" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/aliup" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" +) + +const ( + NatType = "Nat" +) + +//go:generate fitask -type=EIP +type EIP struct { + Name *string + Lifecycle *fi.Lifecycle + + Region *string + ID *string + IpAddress *string + NatGateway *NatGateway + Available *bool +} + +var _ fi.CompareWithID = &EIP{} + +func (e *EIP) CompareWithID() *string { + return e.Name +} + +func (e *EIP) Find(c *fi.Context) (*EIP, error) { + if e.NatGateway == nil || e.NatGateway.ID == nil { + glog.V(4).Infof("NatGateway / NatGatewayId not found for %s, skipping Find", fi.StringValue(e.Name)) + return nil, nil + } + + cloud := c.Cloud.(aliup.ALICloud) + describeEipAddressesArgs := &ecs.DescribeEipAddressesArgs{ + RegionId: common.Region(cloud.Region()), + AssociatedInstanceType: ecs.AssociatedInstanceTypeNat, + AssociatedInstanceId: fi.StringValue(e.NatGateway.ID), + } + + eipAddresses, _, err := cloud.VpcClient().DescribeEipAddresses(describeEipAddressesArgs) + if err != nil { + return nil, fmt.Errorf("error finding EIPs: %v", err) + } + // Don't exist EIPs with specified NatGateway. + if len(eipAddresses) == 0 { + return nil, nil + } + if len(eipAddresses) > 1 { + glog.V(4).Infof("The number of specified EIPs with the same NatGatewayId exceeds 1, eipName:%q", *e.Name) + } + + glog.V(2).Infof("found matching EIPs: %q", *e.Name) + + actual := &EIP{} + actual.IpAddress = fi.String(eipAddresses[0].IpAddress) + actual.ID = fi.String(eipAddresses[0].AllocationId) + actual.Available = fi.Bool(eipAddresses[0].Status == ecs.EipStatusAvailable) + if eipAddresses[0].InstanceId != "" { + actual.NatGateway = &NatGateway{ + ID: fi.String(eipAddresses[0].InstanceId), + } + actual.Region = fi.String(cloud.Region()) + } + // Ignore "system" fields + actual.Lifecycle = e.Lifecycle + actual.Name = e.Name + e.ID = actual.ID + e.Available = actual.Available + glog.V(4).Infof("found matching EIP %v", actual) + return actual, nil +} + +func (e *EIP) Run(c *fi.Context) error { + return fi.DefaultDeltaRunMethod(e, c) +} + +func (_ *EIP) CheckChanges(a, e, changes *EIP) error { + return nil +} + +func (_ *EIP) RenderALI(t *aliup.ALIAPITarget, a, e, changes *EIP) error { + + if a == nil { + glog.V(2).Infof("Creating new EIP for NatGateway:%q", fi.StringValue(e.NatGateway.Name)) + + allocateEipAddressArgs := &ecs.AllocateEipAddressArgs{ + RegionId: common.Region(t.Cloud.Region()), + } + + eipAddress, allocationId, err := t.Cloud.VpcClient().AllocateEipAddress(allocateEipAddressArgs) + if err != nil { + return fmt.Errorf("error creating eip: %v", err) + } + e.IpAddress = fi.String(eipAddress) + e.ID = fi.String(allocationId) + e.Available = fi.Bool(true) + } + + associateEipAddressArgs := &ecs.AssociateEipAddressArgs{ + AllocationId: fi.StringValue(e.ID), + InstanceId: fi.StringValue(e.NatGateway.ID), + InstanceType: ecs.Nat, + } + + if fi.BoolValue(e.Available) { + err := t.Cloud.VpcClient().NewAssociateEipAddress(associateEipAddressArgs) + if err != nil { + return fmt.Errorf("error associating eip to natGateway: %v", err) + } + } + + return nil +} + +type terraformEip struct { +} + +type terraformEipAssociation struct { + InstanceID *terraform.Literal `json:"instance_id,omitempty"` + AllocationID *terraform.Literal `json:"allocation_id,omitempty"` +} + +func (_ *EIP) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *EIP) error { + tf := &terraformEip{} + err := t.RenderResource("alicloud_eip", *e.Name, tf) + if err != nil { + return err + } + + associationtf := &terraformEipAssociation{ + InstanceID: e.NatGateway.TerraformLink(), + AllocationID: e.TerraformLink(), + } + + return t.RenderResource("alicloud_eip_association", *e.Name+"_asso", associationtf) +} + +func (e *EIP) TerraformLink() *terraform.Literal { + return terraform.LiteralProperty("alicloud_eip", *e.Name, "id") +} diff --git a/upup/pkg/fi/cloudup/alitasks/natgateway.go b/upup/pkg/fi/cloudup/alitasks/natgateway.go new file mode 100644 index 0000000000..0f5baf1815 --- /dev/null +++ b/upup/pkg/fi/cloudup/alitasks/natgateway.go @@ -0,0 +1,129 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package alitasks + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/golang/glog" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/aliup" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" +) + +//go:generate fitask -type=NatGateway +type NatGateway struct { + Name *string + Lifecycle *fi.Lifecycle + + VPC *VPC + Region *string + ID *string +} + +var _ fi.CompareWithID = &NatGateway{} + +func (e *NatGateway) CompareWithID() *string { + return e.ID +} + +func (e *NatGateway) Find(c *fi.Context) (*NatGateway, error) { + if e.VPC == nil || e.VPC.ID == nil { + glog.V(4).Infof("VPC / VPCID not found for %s, skipping Find", fi.StringValue(e.Name)) + return nil, nil + } + + cloud := c.Cloud.(aliup.ALICloud) + request := &ecs.DescribeNatGatewaysArgs{ + RegionId: common.Region(cloud.Region()), + VpcId: fi.StringValue(e.VPC.ID), + } + + natGateways, _, err := cloud.VpcClient().DescribeNatGateways(request) + if err != nil { + return nil, fmt.Errorf("error listing NatGateways: %v", err) + } + + // Don't exist NatGateways with specified VPC. + if len(natGateways) == 0 { + return nil, nil + } + if len(natGateways) != 1 { + return nil, fmt.Errorf("found multiple NatGateways for %q", fi.StringValue(e.ID)) + } + actual := &NatGateway{} + actual.ID = fi.String(natGateways[0].NatGatewayId) + + // Ignore "system" fields + actual.Lifecycle = e.Lifecycle + actual.Name = e.Name + actual.Region = e.Region + + e.ID = actual.ID + glog.V(4).Infof("found matching NatGateway %v", actual) + return actual, nil +} + +func (s *NatGateway) CheckChanges(a, e, changes *NatGateway) error { + if a == nil { + if e.Name == nil { + return fi.RequiredField("Name") + } + } + return nil +} + +func (e *NatGateway) Run(c *fi.Context) error { + return fi.DefaultDeltaRunMethod(e, c) +} + +func (_ *NatGateway) RenderALI(t *aliup.ALIAPITarget, a, e, changes *NatGateway) error { + if a == nil { + request := &ecs.CreateNatGatewayArgs{ + RegionId: common.Region(t.Cloud.Region()), + VpcId: fi.StringValue(e.VPC.ID), + Name: fi.StringValue(e.Name), + } + + response, err := t.Cloud.VpcClient().CreateNatGateway(request) + if err != nil { + return fmt.Errorf("error creating NatGateway: %v", err) + } + e.ID = fi.String(response.NatGatewayId) + } + return nil +} + +type terraformNatGateway struct { + Name *string `json:"name,omitempty"` + VpcId *terraform.Literal `json:"vpc_id,omitempty"` +} + +func (_ *NatGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *NatGateway) error { + tf := &terraformNatGateway{ + Name: e.Name, + VpcId: e.VPC.TerraformLink(), + } + + return t.RenderResource("alicloud_nat_gateway", *e.Name, tf) +} + +func (e *NatGateway) TerraformLink() *terraform.Literal { + return terraform.LiteralProperty("alicloud_nat_gateway", *e.Name, "id") +} diff --git a/upup/pkg/fi/cloudup/alitasks/natgateway_fitask.go b/upup/pkg/fi/cloudup/alitasks/natgateway_fitask.go new file mode 100644 index 0000000000..1c0868ac28 --- /dev/null +++ b/upup/pkg/fi/cloudup/alitasks/natgateway_fitask.go @@ -0,0 +1,75 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ""fitask" -type=NatGateway"; DO NOT EDIT + +package alitasks + +import ( + "encoding/json" + + "k8s.io/kops/upup/pkg/fi" +) + +// NatGateway + +// JSON marshaling boilerplate +type realNatGateway NatGateway + +// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string +func (o *NatGateway) UnmarshalJSON(data []byte) error { + var jsonName string + if err := json.Unmarshal(data, &jsonName); err == nil { + o.Name = &jsonName + return nil + } + + var r realNatGateway + if err := json.Unmarshal(data, &r); err != nil { + return err + } + *o = NatGateway(r) + return nil +} + +var _ fi.HasLifecycle = &NatGateway{} + +// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle +func (o *NatGateway) GetLifecycle() *fi.Lifecycle { + return o.Lifecycle +} + +// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle +func (o *NatGateway) SetLifecycle(lifecycle fi.Lifecycle) { + o.Lifecycle = &lifecycle +} + +var _ fi.HasName = &NatGateway{} + +// GetName returns the Name of the object, implementing fi.HasName +func (o *NatGateway) GetName() *string { + return o.Name +} + +// SetName sets the Name of the object, implementing fi.SetName +func (o *NatGateway) SetName(name string) { + o.Name = &name +} + +// String is the stringer function for the task, producing readable output using fi.TaskAsString +func (o *NatGateway) String() string { + return fi.TaskAsString(o) +} diff --git a/upup/pkg/fi/cloudup/alitasks/vswitchSNAT.go b/upup/pkg/fi/cloudup/alitasks/vswitchSNAT.go new file mode 100644 index 0000000000..92c4e1538c --- /dev/null +++ b/upup/pkg/fi/cloudup/alitasks/vswitchSNAT.go @@ -0,0 +1,175 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package alitasks + +import ( + "fmt" + + "github.com/golang/glog" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/aliup" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" +) + +//go:generate fitask -type=VSwitchSNAT +type VSwitchSNAT struct { + Name *string + Lifecycle *fi.Lifecycle + ID *string + + VSwitch *VSwitch + NatGateway *NatGateway + EIP *EIP + SnatTableId *string + // Shared is set if this is a shared VSwitch + Shared *bool +} + +var _ fi.CompareWithID = &VSwitchSNAT{} + +func (v *VSwitchSNAT) CompareWithID() *string { + return v.Name +} + +func (v *VSwitchSNAT) Find(c *fi.Context) (*VSwitchSNAT, error) { + if v.VSwitch == nil || v.VSwitch.VSwitchId == nil { + glog.V(4).Infof("VSwitch / VSwitchId not found for %s, skipping Find", fi.StringValue(v.Name)) + return nil, nil + } + if v.NatGateway == nil || v.NatGateway.ID == nil { + glog.V(4).Infof("NatGateway / NatGatewayId not found for %s, skipping Find", fi.StringValue(v.Name)) + return nil, nil + } + if v.EIP == nil || v.EIP.IpAddress == nil { + glog.V(4).Infof("EIP / EIP not found for %s, skipping Find", fi.StringValue(v.Name)) + return nil, nil + } + + cloud := c.Cloud.(aliup.ALICloud) + + describeNatGatewaysArgs := &ecs.DescribeNatGatewaysArgs{ + RegionId: common.Region(cloud.Region()), + NatGatewayId: fi.StringValue(v.NatGateway.ID), + } + + natGateways, _, err := cloud.VpcClient().DescribeNatGateways(describeNatGatewaysArgs) + if err != nil { + return nil, fmt.Errorf("error listing NatGateways: %v", err) + } + if len(natGateways) == 0 { + glog.V(4).Infof("NatGateway not found for %s, skipping Find", fi.StringValue(v.Name)) + return nil, nil + } + if len(natGateways[0].SnatTableIds.SnatTableId) == 0 { + return nil, nil + } + + for _, snatTableId := range natGateways[0].SnatTableIds.SnatTableId { + + describeSnatTableEntriesArgs := &ecs.DescribeSnatTableEntriesArgs{ + RegionId: common.Region(cloud.Region()), + SnatTableId: snatTableId, + } + snatTableEntries, _, err := cloud.VpcClient().DescribeSnatTableEntries(describeSnatTableEntriesArgs) + if err != nil { + return nil, fmt.Errorf("error listing snatTableEntries: %v", err) + } + if len(snatTableEntries) == 0 { + continue + } + + for _, snatEntry := range snatTableEntries { + if snatEntry.SourceVSwitchId == fi.StringValue(v.VSwitch.VSwitchId) { + actual := &VSwitchSNAT{} + actual.ID = fi.String(snatEntry.SnatEntryId) + v.ID = actual.ID + actual.VSwitch = v.VSwitch + actual.NatGateway = &NatGateway{ID: v.NatGateway.ID} + actual.SnatTableId = fi.String(snatTableId) + v.SnatTableId = actual.SnatTableId + // Prevent spurious changes + actual.Shared = v.Shared + actual.Name = v.Name + actual.Lifecycle = v.Lifecycle + + return actual, nil + } + } + } + v.SnatTableId = fi.String(natGateways[0].SnatTableIds.SnatTableId[0]) + return nil, nil +} + +func (v *VSwitchSNAT) Run(c *fi.Context) error { + return fi.DefaultDeltaRunMethod(v, c) +} + +func (v *VSwitchSNAT) CheckChanges(a, e, changes *VSwitchSNAT) error { + if e.VSwitch == nil { + return fi.RequiredField("VPC") + } + + if e.NatGateway == nil { + return fi.RequiredField("CIDRBlock") + } + + if a != nil && changes != nil { + if changes.VSwitch != nil { + return fi.CannotChangeField("VSwitch") + } + + if changes.NatGateway != nil { + return fi.CannotChangeField("NatGateway") + } + } + return nil +} + +func (_ *VSwitchSNAT) RenderALI(t *aliup.ALIAPITarget, a, e, changes *VSwitchSNAT) error { + + if a == nil { + createSnatEntryArgs := &ecs.CreateSnatEntryArgs{ + RegionId: common.Region(t.Cloud.Region()), + SnatTableId: fi.StringValue(e.SnatTableId), + SourceVSwitchId: fi.StringValue(e.VSwitch.VSwitchId), + SnatIp: fi.StringValue(e.EIP.IpAddress), + } + resp, err := t.Cloud.VpcClient().CreateSnatEntry(createSnatEntryArgs) + if err != nil { + return fmt.Errorf("error creating SnatEntry: %v,%v", err, createSnatEntryArgs) + } + e.ID = fi.String(resp.SnatEntryId) + } + return nil +} + +type terraformVSwitchSNAT struct { + SnatTableId *string `json:"snat_table_id,omitempty"` + VSwitchId *terraform.Literal `json:"source_vswitch_id,omitempty"` +} + +func (_ *VSwitchSNAT) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *VSwitchSNAT) error { + tf := &terraformVSwitchSNAT{ + SnatTableId: e.SnatTableId, + VSwitchId: e.VSwitch.TerraformLink(), + } + + return t.RenderResource("alicloud_snat_entry", *e.Name, tf) +} diff --git a/upup/pkg/fi/cloudup/alitasks/vswitchsnat_fitask.go b/upup/pkg/fi/cloudup/alitasks/vswitchsnat_fitask.go new file mode 100644 index 0000000000..2b060cd1df --- /dev/null +++ b/upup/pkg/fi/cloudup/alitasks/vswitchsnat_fitask.go @@ -0,0 +1,75 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ""fitask" -type=VSwitchSNAT"; DO NOT EDIT + +package alitasks + +import ( + "encoding/json" + + "k8s.io/kops/upup/pkg/fi" +) + +// VSwitchSNAT + +// JSON marshaling boilerplate +type realVSwitchSNAT VSwitchSNAT + +// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string +func (o *VSwitchSNAT) UnmarshalJSON(data []byte) error { + var jsonName string + if err := json.Unmarshal(data, &jsonName); err == nil { + o.Name = &jsonName + return nil + } + + var r realVSwitchSNAT + if err := json.Unmarshal(data, &r); err != nil { + return err + } + *o = VSwitchSNAT(r) + return nil +} + +var _ fi.HasLifecycle = &VSwitchSNAT{} + +// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle +func (o *VSwitchSNAT) GetLifecycle() *fi.Lifecycle { + return o.Lifecycle +} + +// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle +func (o *VSwitchSNAT) SetLifecycle(lifecycle fi.Lifecycle) { + o.Lifecycle = &lifecycle +} + +var _ fi.HasName = &VSwitchSNAT{} + +// GetName returns the Name of the object, implementing fi.HasName +func (o *VSwitchSNAT) GetName() *string { + return o.Name +} + +// SetName sets the Name of the object, implementing fi.SetName +func (o *VSwitchSNAT) SetName(name string) { + o.Name = &name +} + +// String is the stringer function for the task, producing readable output using fi.TaskAsString +func (o *VSwitchSNAT) String() string { + return fi.TaskAsString(o) +} diff --git a/upup/pkg/fi/cloudup/aliup/ali_cloud.go b/upup/pkg/fi/cloudup/aliup/ali_cloud.go index 9e9352d82f..1067b5b27c 100644 --- a/upup/pkg/fi/cloudup/aliup/ali_cloud.go +++ b/upup/pkg/fi/cloudup/aliup/ali_cloud.go @@ -51,6 +51,7 @@ type ALICloud interface { SlbClient() *slb.Client RamClient() *ram.RamClient EssClient() *ess.Client + VpcClient() *ecs.Client Region() string AddClusterTags(tags map[string]string) @@ -67,6 +68,7 @@ type aliCloudImplementation struct { slbClient *slb.Client ramClient *ram.RamClient essClient *ess.Client + vpcClient *ecs.Client region string tags map[string]string @@ -95,6 +97,7 @@ func NewALICloud(region string, tags map[string]string) (ALICloud, error) { ramclient := ram.NewClient(accessKeyId, accessKeySecret) c.ramClient = ramclient.(*ram.RamClient) c.essClient = ess.NewClient(accessKeyId, accessKeySecret) + c.vpcClient = ecs.NewVPCClient(accessKeyId, accessKeySecret, common.Region(region)) c.tags = tags @@ -117,6 +120,10 @@ func (c *aliCloudImplementation) EssClient() *ess.Client { return c.essClient } +func (c *aliCloudImplementation) VpcClient() *ecs.Client { + return c.vpcClient +} + func (c *aliCloudImplementation) Region() string { return c.region }