kops/upup/pkg/fi/cloudup/alitasks/loadbalanceracl.go

273 lines
8.2 KiB
Go

/*
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
}