kops/upup/pkg/fi/cloudup/scalewaytasks/loadbalancer.go

290 lines
7.4 KiB
Go

/*
Copyright 2022 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 scalewaytasks
import (
"fmt"
"k8s.io/klog/v2"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
"github.com/scaleway/scaleway-sdk-go/api/lb/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
)
// +kops:fitask
type LoadBalancer struct {
Name *string
Lifecycle fi.Lifecycle
Region *string
LBID *string
LBAddresses []string
Tags []string
ForAPIServer bool
}
var _ fi.CompareWithID = &LoadBalancer{}
var _ fi.HasAddress = &LoadBalancer{}
func (l *LoadBalancer) CompareWithID() *string {
return l.LBID
}
func (l *LoadBalancer) IsForAPIServer() bool {
return l.ForAPIServer
}
func (l *LoadBalancer) Find(context *fi.CloudupContext) (*LoadBalancer, error) {
if fi.ValueOf(l.LBID) == "" {
return nil, nil
}
cloud := context.T.Cloud.(scaleway.ScwCloud)
lbService := cloud.LBService()
loadBalancer, err := lbService.GetLB(&lb.GetLBRequest{
Region: scw.Region(cloud.Region()),
LBID: fi.ValueOf(l.LBID),
})
if err != nil {
return nil, fmt.Errorf("getting load-balancer %s: %s", fi.ValueOf(l.LBID), err)
}
lbIPs := []string(nil)
for _, IP := range loadBalancer.IP {
lbIPs = append(lbIPs, IP.IPAddress)
}
return &LoadBalancer{
Name: &loadBalancer.Name,
LBID: &loadBalancer.ID,
LBAddresses: lbIPs,
Tags: loadBalancer.Tags,
Lifecycle: l.Lifecycle,
ForAPIServer: l.ForAPIServer,
}, nil
}
func (l *LoadBalancer) FindAddresses(context *fi.CloudupContext) ([]string, error) {
cloud := context.T.Cloud.(scaleway.ScwCloud)
lbService := cloud.LBService()
if l.LBID == nil {
return nil, nil
}
loadBalancer, err := lbService.GetLB(&lb.GetLBRequest{
Region: scw.Region(cloud.Region()),
LBID: fi.ValueOf(l.LBID),
})
if err != nil {
return nil, err
}
addresses := []string(nil)
for _, address := range loadBalancer.IP {
addresses = append(addresses, address.IPAddress)
}
return addresses, nil
}
func (l *LoadBalancer) Run(context *fi.CloudupContext) error {
return fi.CloudupDefaultDeltaRunMethod(l, context)
}
func (_ *LoadBalancer) CheckChanges(actual, expected, changes *LoadBalancer) error {
if actual != nil {
if changes.Name != nil {
return fi.CannotChangeField("Name")
}
if changes.LBID != nil {
return fi.CannotChangeField("ID")
}
if changes.Region != nil {
return fi.CannotChangeField("Region")
}
} else {
if expected.Name == nil {
return fi.RequiredField("Name")
}
if expected.Region == nil {
return fi.RequiredField("Region")
}
}
return nil
}
func (l *LoadBalancer) RenderScw(t *scaleway.ScwAPITarget, actual, expected, changes *LoadBalancer) error {
lbService := t.Cloud.LBService()
region := scw.Region(fi.ValueOf(expected.Region))
var loadBalancer *lb.LB
backEndToCreate := true
frontEndToCreate := true
if actual != nil {
klog.Infof("Updating existing load-balancer with name %q", expected.Name)
lbToUpdate, err := lbService.GetLB(&lb.GetLBRequest{
Region: region,
LBID: fi.ValueOf(actual.LBID),
})
if err != nil {
return fmt.Errorf("getting load-balancer %q (%s): %w", fi.ValueOf(actual.Name), fi.ValueOf(actual.LBID), err)
}
// We update the tags
if changes != nil || len(actual.Tags) != len(expected.Tags) {
_, err = lbService.UpdateLB(&lb.UpdateLBRequest{
Region: region,
LBID: lbToUpdate.ID,
Name: lbToUpdate.Name,
Description: lbToUpdate.Description,
SslCompatibilityLevel: lbToUpdate.SslCompatibilityLevel,
Tags: expected.Tags,
})
if err != nil {
return fmt.Errorf("updatings tags for load-balancer %q: %w", fi.ValueOf(expected.Name), err)
}
}
// We check that the back-end exists
backEnds, err := lbService.ListBackends(&lb.ListBackendsRequest{
Region: region,
LBID: lbToUpdate.ID,
Name: scw.StringPtr("lb-backend"),
})
if err != nil {
return fmt.Errorf("listing back-ends for load-balancer %q: %w", fi.ValueOf(expected.Name), err)
}
if backEnds.TotalCount > 0 {
backEndToCreate = false
}
// We check that the front-end exists
frontEnds, err := lbService.ListFrontends(&lb.ListFrontendsRequest{
Region: region,
LBID: lbToUpdate.ID,
Name: scw.StringPtr("lb-frontend"),
})
if err != nil {
return fmt.Errorf("listing front-ends for load-balancer %q: %w", fi.ValueOf(expected.Name), err)
}
if frontEnds.TotalCount > 0 {
frontEndToCreate = false
}
lbIPs := []string(nil)
for _, ip := range lbToUpdate.IP {
lbIPs = append(lbIPs, ip.IPAddress)
}
expected.LBID = &lbToUpdate.ID
expected.LBAddresses = lbIPs
loadBalancer = lbToUpdate
} else {
klog.Infof("Creating new load-balancer with name %q", expected.Name)
lbCreated, err := lbService.CreateLB(&lb.CreateLBRequest{
Region: region,
Name: fi.ValueOf(expected.Name),
Tags: expected.Tags,
})
if err != nil {
return fmt.Errorf("creating load-balancer: %w", err)
}
_, err = lbService.WaitForLb(&lb.WaitForLBRequest{
LBID: lbCreated.ID,
Region: region,
})
if err != nil {
return fmt.Errorf("waiting for load-balancer %s: %w", lbCreated.ID, err)
}
lbIPs := []string(nil)
for _, ip := range lbCreated.IP {
lbIPs = append(lbIPs, ip.IPAddress)
}
expected.LBID = &lbCreated.ID
expected.LBAddresses = lbIPs
loadBalancer = lbCreated
}
backEndID := ""
// We create the load-balancer's backend if needed
if backEndToCreate == true {
backEnd, err := lbService.CreateBackend(&lb.CreateBackendRequest{
Region: region,
LBID: loadBalancer.ID,
Name: "lb-backend",
ForwardProtocol: "tcp",
ForwardPort: 443,
ForwardPortAlgorithm: "roundrobin",
StickySessions: "none",
HealthCheck: &lb.HealthCheck{
CheckMaxRetries: 5,
TCPConfig: &lb.HealthCheckTCPConfig{},
Port: 443,
CheckTimeout: scw.TimeDurationPtr(3000),
CheckDelay: scw.TimeDurationPtr(1001),
},
ProxyProtocol: "proxy_protocol_none",
})
if err != nil {
return fmt.Errorf("creating back-end for load-balancer %s: %w", loadBalancer.ID, err)
}
_, err = lbService.WaitForLb(&lb.WaitForLBRequest{
LBID: loadBalancer.ID,
Region: region,
})
if err != nil {
return fmt.Errorf("waiting for load-balancer %s: %w", loadBalancer.ID, err)
}
backEndID = backEnd.ID
}
// We create the load-balancer's front-end if needed
if frontEndToCreate == true {
_, err := lbService.CreateFrontend(&lb.CreateFrontendRequest{
Region: region,
LBID: loadBalancer.ID,
Name: "lb-frontend",
InboundPort: 443,
BackendID: backEndID,
})
if err != nil {
return fmt.Errorf("creating front-end for load-balancer %s: %w", loadBalancer.ID, err)
}
_, err = lbService.WaitForLb(&lb.WaitForLBRequest{
LBID: loadBalancer.ID,
Region: region,
})
if err != nil {
return fmt.Errorf("waiting for load-balancer %s: %w", loadBalancer.ID, err)
}
}
return nil
}