diff --git a/test/infrastructure/docker/api/v1alpha3/conversion.go b/test/infrastructure/docker/api/v1alpha3/conversion.go new file mode 100644 index 000000000000..c2efe2a9dc25 --- /dev/null +++ b/test/infrastructure/docker/api/v1alpha3/conversion.go @@ -0,0 +1,69 @@ +/* +Copyright 2021 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 v1alpha3 + +import ( + apiconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1alpha4" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *DockerCluster) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1alpha4.DockerCluster) + + if err := Convert_v1alpha3_DockerCluster_To_v1alpha4_DockerCluster(src, dst, nil); err != nil { + return err + } + + // Manually restore data. + restored := &v1alpha4.DockerCluster{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + if restored.Spec.LoadBalancer.ImageRepository != "" { + dst.Spec.LoadBalancer.ImageRepository = restored.Spec.LoadBalancer.ImageRepository + } + + if restored.Spec.LoadBalancer.ImageTag != "" { + dst.Spec.LoadBalancer.ImageTag = restored.Spec.LoadBalancer.ImageTag + } + + return nil +} + +func (dst *DockerCluster) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1alpha4.DockerCluster) + + if err := Convert_v1alpha4_DockerCluster_To_v1alpha3_DockerCluster(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion except for metadata + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + + return nil +} + +// Convert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec is an autogenerated conversion function. +func Convert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in *v1alpha4.DockerClusterSpec, out *DockerClusterSpec, s apiconversion.Scope) error { + // DockerClusterSpec.LoadBalancer was added in v1alpha4, so automatic conversion is not possible + return autoConvert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in, out, s) +} diff --git a/test/infrastructure/docker/api/v1alpha3/zz_generated.conversion.go b/test/infrastructure/docker/api/v1alpha3/zz_generated.conversion.go index 6edba55eea91..7c914c4663a1 100644 --- a/test/infrastructure/docker/api/v1alpha3/zz_generated.conversion.go +++ b/test/infrastructure/docker/api/v1alpha3/zz_generated.conversion.go @@ -72,11 +72,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1alpha4.DockerClusterSpec)(nil), (*DockerClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(a.(*v1alpha4.DockerClusterSpec), b.(*DockerClusterSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*DockerClusterStatus)(nil), (*v1alpha4.DockerClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha3_DockerClusterStatus_To_v1alpha4_DockerClusterStatus(a.(*DockerClusterStatus), b.(*v1alpha4.DockerClusterStatus), scope) }); err != nil { @@ -177,6 +172,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1alpha4.DockerClusterSpec)(nil), (*DockerClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(a.(*v1alpha4.DockerClusterSpec), b.(*DockerClusterSpec), scope) + }); err != nil { + return err + } return nil } @@ -318,14 +318,10 @@ func autoConvert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in *v1 } else { out.FailureDomains = nil } + // WARNING: in.LoadBalancer requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec is an autogenerated conversion function. -func Convert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in *v1alpha4.DockerClusterSpec, out *DockerClusterSpec, s conversion.Scope) error { - return autoConvert_v1alpha4_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in, out, s) -} - func autoConvert_v1alpha3_DockerClusterStatus_To_v1alpha4_DockerClusterStatus(in *DockerClusterStatus, out *v1alpha4.DockerClusterStatus, s conversion.Scope) error { out.Ready = in.Ready if in.FailureDomains != nil { diff --git a/test/infrastructure/docker/api/v1alpha4/conversion.go b/test/infrastructure/docker/api/v1alpha4/conversion.go new file mode 100644 index 000000000000..cd8b992bcd0d --- /dev/null +++ b/test/infrastructure/docker/api/v1alpha4/conversion.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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 v1alpha4 + +func (*DockerCluster) Hub() {} +func (*DockerClusterList) Hub() {} diff --git a/test/infrastructure/docker/api/v1alpha4/dockercluster_types.go b/test/infrastructure/docker/api/v1alpha4/dockercluster_types.go index 8ad64d5f7042..ba08d5e4b502 100644 --- a/test/infrastructure/docker/api/v1alpha4/dockercluster_types.go +++ b/test/infrastructure/docker/api/v1alpha4/dockercluster_types.go @@ -42,6 +42,30 @@ type DockerClusterSpec struct { // controllers to do what they will with the defined failure domains. // +optional FailureDomains clusterv1.FailureDomains `json:"failureDomains,omitempty"` + + // LoadBalancer allows defining configurations for the cluster load balancer. + // +optional + LoadBalancer DockerLoadBalancer `json:"loadBalancer,omitempty"` +} + +// DockerLoadBalancer allows defining configurations for the cluster load balancer. +type DockerLoadBalancer struct { + // ImageMeta allows customizing the image used for the cluster load balancer. + ImageMeta `json:",inline"` +} + +// ImageMeta allows customizing the image used for components that are not +// originated from the Kubernetes/Kubernetes release process. +type ImageMeta struct { + // ImageRepository sets the container registry to pull the haproxy image from. + // if not set, "kindest" will be used instead. + // +optional + ImageRepository string `json:"imageRepository,omitempty"` + + // ImageTag allows to specify a tag for the haproxy image. + // if not set, "v20210715-a6da3463" will be used instead. + // +optional + ImageTag string `json:"imageTag,omitempty"` } // DockerClusterStatus defines the observed state of DockerCluster. diff --git a/test/infrastructure/docker/api/v1alpha4/zz_generated.deepcopy.go b/test/infrastructure/docker/api/v1alpha4/zz_generated.deepcopy.go index 31a18396f264..9bb9c2567f94 100644 --- a/test/infrastructure/docker/api/v1alpha4/zz_generated.deepcopy.go +++ b/test/infrastructure/docker/api/v1alpha4/zz_generated.deepcopy.go @@ -110,6 +110,7 @@ func (in *DockerClusterSpec) DeepCopyInto(out *DockerClusterSpec) { (*out)[key] = *val.DeepCopy() } } + out.LoadBalancer = in.LoadBalancer } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerClusterSpec. @@ -151,6 +152,22 @@ func (in *DockerClusterStatus) DeepCopy() *DockerClusterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DockerLoadBalancer) DeepCopyInto(out *DockerLoadBalancer) { + *out = *in + out.ImageMeta = in.ImageMeta +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerLoadBalancer. +func (in *DockerLoadBalancer) DeepCopy() *DockerLoadBalancer { + if in == nil { + return nil + } + out := new(DockerLoadBalancer) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DockerMachine) DeepCopyInto(out *DockerMachine) { *out = *in @@ -357,6 +374,21 @@ func (in *DockerMachineTemplateSpec) DeepCopy() *DockerMachineTemplateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageMeta) DeepCopyInto(out *ImageMeta) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageMeta. +func (in *ImageMeta) DeepCopy() *ImageMeta { + if in == nil { + return nil + } + out := new(ImageMeta) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Mount) DeepCopyInto(out *Mount) { *out = *in diff --git a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml index e2b52df40758..a1df827763a3 100644 --- a/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml +++ b/test/infrastructure/docker/config/crd/bases/infrastructure.cluster.x-k8s.io_dockerclusters.yaml @@ -213,6 +213,19 @@ spec: will simply copy these into the Status and allow the Cluster API controllers to do what they will with the defined failure domains. type: object + loadBalancer: + description: LoadBalancer allows defining configurations for the cluster + load balancer. + properties: + imageRepository: + description: ImageRepository sets the container registry to pull + the haproxy image from. if not set, "kindest" will be used instead. + type: string + imageTag: + description: ImageTag allows to specify a tag for the haproxy + image. if not set, "v20210715-a6da3463" will be used instead. + type: string + type: object type: object status: description: DockerClusterStatus defines the observed state of DockerCluster. diff --git a/test/infrastructure/docker/controllers/dockercluster_controller.go b/test/infrastructure/docker/controllers/dockercluster_controller.go index 32a435e1f6e5..7728826f6cd8 100644 --- a/test/infrastructure/docker/controllers/dockercluster_controller.go +++ b/test/infrastructure/docker/controllers/dockercluster_controller.go @@ -73,7 +73,7 @@ func (r *DockerClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques log = log.WithValues("cluster", cluster.Name) // Create a helper for managing a docker container hosting the loadbalancer. - externalLoadBalancer, err := docker.NewLoadBalancer(cluster) + externalLoadBalancer, err := docker.NewLoadBalancer(cluster, dockerCluster) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "failed to create helper for managing the externalLoadBalancer") } diff --git a/test/infrastructure/docker/controllers/dockermachine_controller.go b/test/infrastructure/docker/controllers/dockermachine_controller.go index 69c9d731beb9..bb50bcdcc1a2 100644 --- a/test/infrastructure/docker/controllers/dockermachine_controller.go +++ b/test/infrastructure/docker/controllers/dockermachine_controller.go @@ -147,7 +147,7 @@ func (r *DockerMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques // NB. the machine controller has to manage the cluster load balancer because the current implementation of the // docker load balancer does not support auto-discovery of control plane nodes, so CAPD should take care of // updating the cluster load balancer configuration when control plane machines are added/removed - externalLoadBalancer, err := docker.NewLoadBalancer(cluster) + externalLoadBalancer, err := docker.NewLoadBalancer(cluster, dockerCluster) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "failed to create helper for managing the externalLoadBalancer") } diff --git a/test/infrastructure/docker/docker/loadbalancer.go b/test/infrastructure/docker/docker/loadbalancer.go index b5570e88c3a8..362891f35e9d 100644 --- a/test/infrastructure/docker/docker/loadbalancer.go +++ b/test/infrastructure/docker/docker/loadbalancer.go @@ -24,6 +24,7 @@ import ( "github.com/pkg/errors" clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4" "sigs.k8s.io/cluster-api/test/infrastructure/container" + "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1alpha4" "sigs.k8s.io/cluster-api/test/infrastructure/docker/docker/types" "sigs.k8s.io/cluster-api/test/infrastructure/docker/third_party/forked/loadbalancer" ctrl "sigs.k8s.io/controller-runtime" @@ -37,13 +38,14 @@ type lbCreator interface { // LoadBalancer manages the load balancer for a specific docker cluster. type LoadBalancer struct { name string + image string container *types.Node ipFamily clusterv1.ClusterIPFamily lbCreator lbCreator } // NewLoadBalancer returns a new helper for managing a docker loadbalancer with a given name. -func NewLoadBalancer(cluster *clusterv1.Cluster) (*LoadBalancer, error) { +func NewLoadBalancer(cluster *clusterv1.Cluster, dockerCluster *v1alpha4.DockerCluster) (*LoadBalancer, error) { if cluster.Name == "" { return nil, errors.New("create load balancer: cluster name is empty") } @@ -65,14 +67,37 @@ func NewLoadBalancer(cluster *clusterv1.Cluster) (*LoadBalancer, error) { return nil, fmt.Errorf("create load balancer: %s", err) } + image := getLoadBalancerImage(dockerCluster) + return &LoadBalancer{ name: cluster.Name, + image: image, container: container, ipFamily: ipFamily, lbCreator: &Manager{}, }, nil } +// getLoadBalancerImage will return the image (e.g. "kindest/haproxy:2.1.1-alpine") to use for +// the load balancer. +func getLoadBalancerImage(dockerCluster *v1alpha4.DockerCluster) string { + // Check if a non-default image was provided + image := loadbalancer.Image + imageRepo := loadbalancer.DefaultImageRepository + imageTag := loadbalancer.DefaultImageTag + + if dockerCluster != nil { + if dockerCluster.Spec.LoadBalancer.ImageRepository != "" { + imageRepo = dockerCluster.Spec.LoadBalancer.ImageRepository + } + if dockerCluster.Spec.LoadBalancer.ImageTag != "" { + imageTag = dockerCluster.Spec.LoadBalancer.ImageTag + } + } + + return fmt.Sprintf("%s/%s:%s", imageRepo, image, imageTag) +} + // ContainerName is the name of the docker container with the load balancer. func (s *LoadBalancer) containerName() string { return fmt.Sprintf("%s-lb", s.name) @@ -94,7 +119,7 @@ func (s *LoadBalancer) Create(ctx context.Context) error { s.container, err = s.lbCreator.CreateExternalLoadBalancerNode( ctx, s.containerName(), - loadbalancer.Image, + s.image, s.name, listenAddr, 0, diff --git a/test/infrastructure/docker/third_party/forked/loadbalancer/const.go b/test/infrastructure/docker/third_party/forked/loadbalancer/const.go index 260e02910025..82bf261452ad 100644 --- a/test/infrastructure/docker/third_party/forked/loadbalancer/const.go +++ b/test/infrastructure/docker/third_party/forked/loadbalancer/const.go @@ -16,8 +16,14 @@ limitations under the License. package loadbalancer -// Image defines the loadbalancer image:tag -const Image = "kindest/haproxy:v20210715-a6da3463" +// Image defines the loadbalancer image name +const Image = "haproxy" + +// DefaultImageRepository defines the loadbalancer image repository +const DefaultImageRepository = "kindest" + +// DefaultImageTag defines the loadbalancer image tag +const DefaultImageTag = "v20210715-a6da3463" // ConfigPath defines the path to the config file in the image const ConfigPath = "/usr/local/etc/haproxy/haproxy.cfg"