Refactor Alicloud LoadBalancerWhiteList to LoadBalancerACL

This commit is contained in:
Xiaoyu Zhong 2020-01-10 14:20:04 +08:00
parent 074af1c962
commit 709e7ef33c
5 changed files with 312 additions and 186 deletions

View File

@ -19,7 +19,6 @@ package alimodel
import (
"errors"
"fmt"
"strings"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/dns"
@ -106,28 +105,28 @@ func (b *APILoadBalancerModelBuilder) Build(c *fi.ModelBuilderContext) error {
c.AddTask(loadbalancerlistener)
}
// Create LoadBalancerWhiteList for API ELB
var loadbalancerwhiteList *alitasks.LoadBalancerWhiteList
// Allow traffic into the SLB from KubernetesAPIAccess CIDRs
var loadBalancerACL *alitasks.LoadBalancerACL
{
sourceItems := ""
var cidrs []string
var cidrs []*string
for _, cidr := range b.Cluster.Spec.KubernetesAPIAccess {
if cidr != "0.0.0.0" && cidr != "0.0.0.0/0" {
cidrs = append(cidrs, cidr)
cc := cidr
cidrs = append(cidrs, &cc)
}
}
sourceItems = strings.Join(cidrs, ",")
loadbalancerwhiteList = &alitasks.LoadBalancerWhiteList{
Name: s("api." + b.ClusterName()),
Lifecycle: b.Lifecycle,
LoadBalancer: loadbalancer,
LoadBalancerListener: loadbalancerlistener,
SourceItems: s(sourceItems),
if len(cidrs) != 0 {
loadBalancerACL = &alitasks.LoadBalancerACL{
Name: s("api." + b.ClusterName()),
Lifecycle: b.Lifecycle,
LoadBalancer: loadbalancer,
LoadBalancerListener: loadbalancerlistener,
SourceItems: cidrs,
}
c.AddTask(loadBalancerACL)
}
c.AddTask(loadbalancerwhiteList)
}
// Temporarily do not know the role of the following function

View File

@ -0,0 +1,272 @@
/*
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 (
"encoding/json"
"fmt"
"sort"
"k8s.io/klog"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
slbnew "github.com/aliyun/alibaba-cloud-sdk-go/services/slb"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/aliup"
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
)
type LoadBalancerACL struct {
ID *string
Name *string
LoadBalancer *LoadBalancer
LoadBalancerListener *LoadBalancerListener
SourceItems []*string
Lifecycle *fi.Lifecycle
}
type AclEntry struct {
Entry string `json:"entry"`
Comment string `json:"comment"`
}
var _ fi.CompareWithID = &LoadBalancerACL{}
func (l *LoadBalancerACL) CompareWithID() *string {
return l.Name
}
func (l *LoadBalancerACL) Find(c *fi.Context) (*LoadBalancerACL, error) {
if l.LoadBalancer == nil || l.LoadBalancer.LoadbalancerId == nil {
klog.V(4).Infof("LoadBalancer / LoadbalancerId not found for %s, skipping Find", fi.StringValue(l.Name))
return nil, nil
}
if l.LoadBalancerListener == nil || l.LoadBalancerListener.ListenerPort == nil {
klog.V(4).Infof("LoadBalancerListener / LoadbalancerListenerPort not found for %s, skipping Find", fi.StringValue(l.Name))
return nil, nil
}
cloud := c.Cloud.(aliup.ALICloud)
describeAclReq := slbnew.CreateDescribeAccessControlListsRequest()
describeAclReq.AclName = fi.StringValue(l.Name)
describeAclResp, err := cloud.SLB().DescribeAccessControlLists(describeAclReq)
if err != nil {
return nil, fmt.Errorf("error listing LoadBalancerAccessControlList: %v", err)
}
acls := describeAclResp.Acls.Acl
if len(acls) == 0 {
return nil, nil
}
if len(acls) > 1 {
return nil, fmt.Errorf("found multiple LoadBalancerAccessControlList with name %s", fi.StringValue(l.Name))
}
acl := acls[0]
klog.V(2).Infof("found matching LoadBalancerAccessControlList: %s", acl.AclId)
describeAclAttrReq := slbnew.CreateDescribeAccessControlListAttributeRequest()
describeAclAttrReq.AclId = acl.AclId
describeAclAttrResp, err := cloud.SLB().DescribeAccessControlListAttribute(describeAclAttrReq)
if err != nil {
return nil, fmt.Errorf("error describing LoadBalancerAccessControlListAttribute: %v", err)
}
var sourceItems []*string
for _, entry := range describeAclAttrResp.AclEntrys.AclEntry {
ip := entry.AclEntryIP
sourceItems = append(sourceItems, &ip)
}
actual := &LoadBalancerACL{
ID: fi.String(acl.AclId),
Name: fi.String(describeAclAttrResp.AclName),
SourceItems: sourceItems,
}
listeners := describeAclAttrResp.RelatedListeners.RelatedListener
if len(listeners) != 1 {
actual.LoadBalancerListener = nil
actual.LoadBalancer = nil
} else {
listener := listeners[0]
lb := &LoadBalancer{LoadbalancerId: fi.String(listener.LoadBalancerId)}
actual.LoadBalancer = lb
actual.LoadBalancerListener = &LoadBalancerListener{
LoadBalancer: lb,
ListenerPort: fi.Int(listener.ListenerPort),
}
}
// Ignore "system" fields
l.ID = actual.ID
actual.Lifecycle = l.Lifecycle
return actual, nil
}
func (l *LoadBalancerACL) Run(c *fi.Context) error {
l.Normalize()
return fi.DefaultDeltaRunMethod(l, c)
}
func (l *LoadBalancerACL) Normalize() {
// We need to sort our arrays consistently, so we don't get spurious changes
sort.Stable(StringPointers(l.SourceItems))
}
// StringPointers implements sort.Interface for []*string
type StringPointers []*string
func (s StringPointers) Len() int { return len(s) }
func (s StringPointers) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s StringPointers) Less(i, j int) bool {
return fi.StringValue(s[i]) < fi.StringValue(s[j])
}
func (_ *LoadBalancerACL) CheckChanges(a, e, changes *LoadBalancerACL) error {
if a == nil {
if e.Name == nil {
return fi.RequiredField("Name")
}
}
return nil
}
func (_ *LoadBalancerACL) RenderALI(t *aliup.ALIAPITarget, a, e, changes *LoadBalancerACL) error {
if a == nil {
klog.V(2).Infof("Creating LoadBalancerAccessControlList with name: %q for SLB %q", *e.Name, *e.LoadBalancer.LoadbalancerId)
if err := createAcl(t.Cloud, e); err != nil {
return err
}
return e.on(t.Cloud)
} else {
if changes.SourceItems != nil {
klog.V(2).Info("Turning off LoadBalancerACL for SLB")
if err := e.off(t.Cloud); err != nil {
return err
}
klog.V(2).Infof("Deleting LoadBalancerAccessControlList %q", *a.Name)
err := deleteAcl(t.Cloud, a)
if err != nil {
return fmt.Errorf("error deleting LoadBalancerAccessControlList: %v", err)
}
klog.V(2).Infof("Creating LoadBalancerAccessControlList with name: %q for SLB %q", *e.Name, *e.LoadBalancer.LoadbalancerId)
if err := createAcl(t.Cloud, e); err != nil {
return err
}
return e.on(t.Cloud)
}
if changes.LoadBalancer != nil || changes.LoadBalancerListener != nil {
klog.V(2).Info("Turning on LoadBalancerACL for SLB")
return e.on(t.Cloud)
}
}
return nil
}
func (l *LoadBalancerACL) on(alicloud aliup.ALICloud) error {
setLBTCPlistenerAttrReq := slbnew.CreateSetLoadBalancerTCPListenerAttributeRequest()
setLBTCPlistenerAttrReq.AclId = fi.StringValue(l.ID)
setLBTCPlistenerAttrReq.AclType = "white"
setLBTCPlistenerAttrReq.AclStatus = "on"
setLBTCPlistenerAttrReq.LoadBalancerId = fi.StringValue(l.LoadBalancer.LoadbalancerId)
setLBTCPlistenerAttrReq.ListenerPort = requests.NewInteger(fi.IntValue(l.LoadBalancerListener.ListenerPort))
_, err := alicloud.SLB().SetLoadBalancerTCPListenerAttribute(setLBTCPlistenerAttrReq)
if err != nil {
return fmt.Errorf("error turning on LoadBalancerACL %v", err)
}
return nil
}
func (l *LoadBalancerACL) off(alicloud aliup.ALICloud) error {
setLBTCPlistenerAttrReq := slbnew.CreateSetLoadBalancerTCPListenerAttributeRequest()
setLBTCPlistenerAttrReq.AclStatus = "off"
setLBTCPlistenerAttrReq.LoadBalancerId = fi.StringValue(l.LoadBalancer.LoadbalancerId)
setLBTCPlistenerAttrReq.ListenerPort = requests.NewInteger(fi.IntValue(l.LoadBalancerListener.ListenerPort))
_, err := alicloud.SLB().SetLoadBalancerTCPListenerAttribute(setLBTCPlistenerAttrReq)
if err != nil {
return fmt.Errorf("error turning off LoadBalancerACL %v", err)
}
return nil
}
func createAcl(alicloud aliup.ALICloud, acl *LoadBalancerACL) error {
createAclReq := slbnew.CreateCreateAccessControlListRequest()
createAclReq.AclName = fi.StringValue(acl.Name)
resp, err := alicloud.SLB().CreateAccessControlList(createAclReq)
if err != nil {
return fmt.Errorf("error creating LoadBalancerAccessControlList: %v", err)
}
aclID := resp.AclId
acl.ID = fi.String(aclID)
var aclEntries []AclEntry
for _, each := range acl.SourceItems {
aclEntries = append(aclEntries, AclEntry{Entry: *each})
}
aclEntriesBytes, err := json.Marshal(aclEntries)
if err != nil {
return fmt.Errorf("error marshalling %v : %v", aclEntries, err)
}
addAclEntryReq := slbnew.CreateAddAccessControlListEntryRequest()
addAclEntryReq.AclId = aclID
addAclEntryReq.AclEntrys = string(aclEntriesBytes)
_, err = alicloud.SLB().AddAccessControlListEntry(addAclEntryReq)
if err != nil {
return fmt.Errorf("error adding AclEntries %v: %v", addAclEntryReq, err)
}
return nil
}
func deleteAcl(alicloud aliup.ALICloud, acl *LoadBalancerACL) error {
deleteAclReq := slbnew.CreateDeleteAccessControlListRequest()
deleteAclReq.AclId = fi.StringValue(acl.ID)
_, err := alicloud.SLB().DeleteAccessControlList(deleteAclReq)
if err != nil {
return fmt.Errorf("error deleting LoadBalancerAccessControlList %q, %v", fi.StringValue(acl.ID), err)
}
return nil
}
func (_ *LoadBalancerACL) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *LoadBalancerACL) error {
klog.Warningf("terraform does not support LoadBalancerAccessControlList on ALI cloud")
return nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by ""fitask" -type=LoadBalancerWhiteList"; DO NOT EDIT
// Code generated by ""fitask" -type=LoadBalancerACL"; DO NOT EDIT
package alitasks
@ -24,52 +24,52 @@ import (
"k8s.io/kops/upup/pkg/fi"
)
// LoadBalancerWhiteList
// LoadBalancerACL
// JSON marshaling boilerplate
type realLoadBalancerWhiteList LoadBalancerWhiteList
type realLoadBalancerACL LoadBalancerACL
// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string
func (o *LoadBalancerWhiteList) UnmarshalJSON(data []byte) error {
func (o *LoadBalancerACL) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realLoadBalancerWhiteList
var r realLoadBalancerACL
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = LoadBalancerWhiteList(r)
*o = LoadBalancerACL(r)
return nil
}
var _ fi.HasLifecycle = &LoadBalancerWhiteList{}
var _ fi.HasLifecycle = &LoadBalancerACL{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *LoadBalancerWhiteList) GetLifecycle() *fi.Lifecycle {
func (o *LoadBalancerACL) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *LoadBalancerWhiteList) SetLifecycle(lifecycle fi.Lifecycle) {
func (o *LoadBalancerACL) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &LoadBalancerWhiteList{}
var _ fi.HasName = &LoadBalancerACL{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *LoadBalancerWhiteList) GetName() *string {
func (o *LoadBalancerACL) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *LoadBalancerWhiteList) SetName(name string) {
func (o *LoadBalancerACL) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *LoadBalancerWhiteList) String() string {
func (o *LoadBalancerACL) String() string {
return fi.TaskAsString(o)
}

View File

@ -1,145 +0,0 @@
/*
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"
"strings"
"k8s.io/klog"
"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=LoadBalancerWhiteList
type LoadBalancerWhiteList struct {
LoadBalancer *LoadBalancer
LoadBalancerListener *LoadBalancerListener
Name *string
SourceItems *string
Lifecycle *fi.Lifecycle
}
var _ fi.CompareWithID = &LoadBalancerWhiteList{}
func (l *LoadBalancerWhiteList) CompareWithID() *string {
return l.Name
}
func (l *LoadBalancerWhiteList) Find(c *fi.Context) (*LoadBalancerWhiteList, error) {
if l.LoadBalancer == nil || l.LoadBalancer.LoadbalancerId == nil {
klog.V(4).Infof("LoadBalancer / LoadbalancerId not found for %s, skipping Find", fi.StringValue(l.Name))
return nil, nil
}
if l.LoadBalancerListener == nil || l.LoadBalancerListener.ListenerPort == nil {
klog.V(4).Infof("LoadBalancerListener / LoadbalancerListenerPort not found for %s, skipping Find", fi.StringValue(l.Name))
return nil, nil
}
cloud := c.Cloud.(aliup.ALICloud)
loadBalancerId := fi.StringValue(l.LoadBalancer.LoadbalancerId)
listenertPort := fi.IntValue(l.LoadBalancerListener.ListenerPort)
response, err := cloud.SlbClient().DescribeListenerAccessControlAttribute(loadBalancerId, listenertPort)
if err != nil {
return nil, fmt.Errorf("error finding LoadBalancerWhiteList: %v", err)
}
if response.SourceItems == "" {
klog.V(2).Infof("can't find matching LoadBalancerWhiteList of ListenerPort: %v", listenertPort)
return nil, nil
}
klog.V(2).Infof("found matching LoadBalancerWhiteList of ListenerPort: %v", listenertPort)
actual := &LoadBalancerWhiteList{}
actual.SourceItems = fi.String(response.SourceItems)
// Ignore "system" fields
actual.Name = l.Name
actual.LoadBalancer = l.LoadBalancer
actual.LoadBalancerListener = l.LoadBalancerListener
actual.Lifecycle = l.Lifecycle
return actual, nil
}
func (l *LoadBalancerWhiteList) Run(c *fi.Context) error {
return fi.DefaultDeltaRunMethod(l, c)
}
func (_ *LoadBalancerWhiteList) CheckChanges(a, e, changes *LoadBalancerWhiteList) error {
if a == nil {
if e.Name == nil {
return fi.RequiredField("Name")
}
}
return nil
}
func (_ *LoadBalancerWhiteList) RenderALI(t *aliup.ALIAPITarget, a, e, changes *LoadBalancerWhiteList) error {
klog.V(2).Infof("Updating LoadBalancerWhiteList of ListenerPort: %q", *e.LoadBalancerListener.ListenerPort)
loadBalancerId := fi.StringValue(e.LoadBalancer.LoadbalancerId)
listenertPort := fi.IntValue(e.LoadBalancerListener.ListenerPort)
sourceItems := fi.StringValue(e.SourceItems)
if sourceItems != "" {
err := t.Cloud.SlbClient().AddListenerWhiteListItem(loadBalancerId, listenertPort, sourceItems)
if err != nil {
return fmt.Errorf("error adding LoadBalancerWhiteListItems: %v", err)
}
}
if a != nil && changes.SourceItems != nil {
itemsToDelete := e.getWhiteItemsToDelete(fi.StringValue(a.SourceItems))
if itemsToDelete != "" {
err := t.Cloud.SlbClient().RemoveListenerWhiteListItem(loadBalancerId, listenertPort, itemsToDelete)
if err != nil {
return fmt.Errorf("error removing LoadBalancerWhiteListItems: %v", err)
}
}
}
return nil
}
func (l *LoadBalancerWhiteList) getWhiteItemsToDelete(currentWhiteItems string) string {
currentWhiteItemsList := strings.Split(currentWhiteItems, ",")
expectedWhiteItemsList := strings.Split(fi.StringValue(l.SourceItems), ",")
itemsToDelete := ""
for _, currentItem := range currentWhiteItemsList {
expected := false
if currentItem == "" {
continue
}
for _, expectedItem := range expectedWhiteItemsList {
if currentItem == expectedItem {
expected = true
}
}
if !expected {
itemsToDelete = itemsToDelete + "," + currentItem
}
}
return itemsToDelete
}
func (_ *LoadBalancerWhiteList) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *LoadBalancerWhiteList) error {
klog.Warningf("terraform does not support LoadBalancerWhiteList on ALI cloud")
return nil
}

View File

@ -468,19 +468,19 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) error {
aliCloud := cloud.(aliup.ALICloud)
region = aliCloud.Region()
l.AddTypes(map[string]interface{}{
"Vpc": &alitasks.VPC{},
"VSwitch": &alitasks.VSwitch{},
"Disk": &alitasks.Disk{},
"SecurityGroup": &alitasks.SecurityGroup{},
"SecurityGroupRule": &alitasks.SecurityGroupRule{},
"LoadBalancer": &alitasks.LoadBalancer{},
"LoadBalancerListener": &alitasks.LoadBalancerListener{},
"LoadBalancerWhiteList": &alitasks.LoadBalancerWhiteList{},
"AutoscalingGroup": &alitasks.ScalingGroup{},
"LaunchConfiguration": &alitasks.LaunchConfiguration{},
"RAMPolicy": &alitasks.RAMPolicy{},
"RAMRole": &alitasks.RAMRole{},
"SSHKey": &alitasks.SSHKey{},
"Vpc": &alitasks.VPC{},
"VSwitch": &alitasks.VSwitch{},
"Disk": &alitasks.Disk{},
"SecurityGroup": &alitasks.SecurityGroup{},
"SecurityGroupRule": &alitasks.SecurityGroupRule{},
"LoadBalancer": &alitasks.LoadBalancer{},
"LoadBalancerListener": &alitasks.LoadBalancerListener{},
"LoadBalancerACL": &alitasks.LoadBalancerACL{},
"AutoscalingGroup": &alitasks.ScalingGroup{},
"LaunchConfiguration": &alitasks.LaunchConfiguration{},
"RAMPolicy": &alitasks.RAMPolicy{},
"RAMRole": &alitasks.RAMRole{},
"SSHKey": &alitasks.SSHKey{},
})
if len(sshPublicKeys) == 0 {