Skip to content

Commit

Permalink
feat: add control-plane load balancer scheme patch
Browse files Browse the repository at this point in the history
  • Loading branch information
dkoshkin committed Oct 16, 2023
1 parent d070314 commit 5c37269
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 3 deletions.
37 changes: 35 additions & 2 deletions api/v1alpha1/aws_clusterconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ package v1alpha1

import (
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"

"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables"
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
)

type AWSSpec struct {
Expand All @@ -13,6 +16,8 @@ type AWSSpec struct {
Region *Region `json:"region,omitempty"`
// +optional
Network *AWSNetwork `json:"network,omitempty"`
// +optional
ControlPlaneLoadBalancer *AWSLoadBalancerSpec `json:"controlPlaneLoadBalancer,omitempty"`
}

func (AWSSpec) VariableSchema() clusterv1.VariableSchema {
Expand All @@ -21,8 +26,9 @@ func (AWSSpec) VariableSchema() clusterv1.VariableSchema {
Description: "AWS cluster configuration",
Type: "object",
Properties: map[string]clusterv1.JSONSchemaProps{
"region": Region("").VariableSchema().OpenAPIV3Schema,
"network": AWSNetwork{}.VariableSchema().OpenAPIV3Schema,
"region": Region("").VariableSchema().OpenAPIV3Schema,
"network": AWSNetwork{}.VariableSchema().OpenAPIV3Schema,
"controlPlaneLoadBalancer": AWSLoadBalancerSpec{}.VariableSchema().OpenAPIV3Schema,
},
},
}
Expand Down Expand Up @@ -114,3 +120,30 @@ func (SubnetSpec) VariableSchema() clusterv1.VariableSchema {
},
}
}

// AWSLoadBalancerSpec configures an AWS control-plane LoadBalancer.
type AWSLoadBalancerSpec struct {
// Scheme sets the scheme of the load balancer (defaults to internet-facing)
// +kubebuilder:default=internet-facing
// +kubebuilder:validation:Enum=internet-facing;internal
// +optional
Scheme *capav1.ELBScheme `json:"scheme,omitempty"`
}

func (AWSLoadBalancerSpec) VariableSchema() clusterv1.VariableSchema {
supportedScheme := []capav1.ELBScheme{capav1.ELBSchemeInternetFacing, capav1.ELBSchemeInternal}

return clusterv1.VariableSchema{
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
Description: "AWS control-plane LoadBalancer configuration",
Type: "object",
Properties: map[string]clusterv1.JSONSchemaProps{
"scheme": {
Description: "Scheme sets the scheme of the load balancer (defaults to internet-facing)",
Type: "string",
Enum: variables.MustMarshalValuesToEnumJSON(supportedScheme...),
},
},
},
}
}
28 changes: 27 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions docs/content/customization/aws/controlplaneloadbalancer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
+++
title = "Control Plane Load Balancer"
+++

The control-plane load balancer customization allows the user
to modify the load balancer configuration for the control-plane's API server.

This customization will be available when the
[provider-specific cluster configuration patch]({{< ref "..">}}) is included in the `ClusterClass`.

## Example

To use an internal ELB scheme, use the following configuration:

```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: <NAME>
spec:
topology:
variables:
- name: clusterConfig
value:
aws:
controlPlaneLoadBalancer:
scheme: internal
```

Applying this configuration will result in the following value being set:

- `AWSClusterTemplate`:

- ```yaml
spec:
controlPlaneLoadBalancer:
scheme: internal
```
106 changes: 106 additions & 0 deletions pkg/handlers/aws/mutation/controlplaneloadbalancer/inject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2023 D2iQ, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package controlplaneloadbalancer

import (
"context"
_ "embed"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables"
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig"
)

const (
// VariableName is the external patch variable name.
VariableName = "controlPlaneLoadBalancer"
)

type awsControlPlaneLoadBalancer struct {
variableName string
variableFieldPath []string
}

func NewPatch() *awsControlPlaneLoadBalancer {
return newAWSControlPlaneLoadBalancer(
clusterconfig.MetaVariableName,
v1alpha1.AWSVariableName,
VariableName,
)
}

func newAWSControlPlaneLoadBalancer(
variableName string,
variableFieldPath ...string,
) *awsControlPlaneLoadBalancer {
return &awsControlPlaneLoadBalancer{
variableName: variableName,
variableFieldPath: variableFieldPath,
}
}

func (h *awsControlPlaneLoadBalancer) Mutate(
ctx context.Context,
obj *unstructured.Unstructured,
vars map[string]apiextensionsv1.JSON,
holderRef runtimehooksv1.HolderReference,
_ client.ObjectKey,
) error {
log := ctrl.LoggerFrom(ctx).WithValues(
"holderRef", holderRef,
)

controlPlaneLoadBalancerVar, found, err := variables.Get[v1alpha1.AWSLoadBalancerSpec](
vars,
h.variableName,
h.variableFieldPath...,
)
if err != nil {
return err
}
if !found {
log.V(5).Info("AWS ControlPlaneLoadBalancer variable not defined")
return nil
}

log = log.WithValues(
"variableName",
h.variableName,
"variableFieldPath",
h.variableFieldPath,
"variableValue",
controlPlaneLoadBalancerVar,
)

return patches.MutateIfApplicable(
obj,
vars,
&holderRef,
selectors.InfrastructureCluster(capav1.GroupVersion.Version, "AWSClusterTemplate"),
log,
func(obj *capav1.AWSClusterTemplate) error {
log.WithValues(
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
"patchedObjectName", client.ObjectKeyFromObject(obj),
).Info("setting ControlPlaneLoadBalancer in AWSCluster spec")

controlPlaneLoadBalancer := obj.Spec.Template.Spec.ControlPlaneLoadBalancer
if controlPlaneLoadBalancer == nil {
obj.Spec.Template.Spec.ControlPlaneLoadBalancer = &capav1.AWSLoadBalancerSpec{}
}
obj.Spec.Template.Spec.ControlPlaneLoadBalancer.Scheme = controlPlaneLoadBalancerVar.Scheme

return nil
},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2023 D2iQ, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package tests

import (
"testing"

"github.com/onsi/gomega"
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"

"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation"
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest/request"
)

func TestGeneratePatches(
t *testing.T,
generatorFunc func() mutation.GeneratePatches,
variableName string,
variablePath ...string,
) {
t.Helper()

capitest.ValidateGeneratePatches(
t,
generatorFunc,
capitest.PatchTestDef{
Name: "unset variable",
},
capitest.PatchTestDef{
Name: "ControlPlaneLoadbalancer scheme set to internet-facing",
Vars: []runtimehooksv1.Variable{
capitest.VariableWithValue(
variableName,
v1alpha1.AWSLoadBalancerSpec{
Scheme: &capav1.ELBSchemeInternetFacing,
},
variablePath...,
),
},
RequestItem: request.NewAWSClusterTemplateRequestItem("1234"),
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{
Operation: "add",
Path: "/spec/template/spec/controlPlaneLoadBalancer",
ValueMatcher: gomega.HaveKeyWithValue(
"scheme", "internet-facing",
),
}},
},
capitest.PatchTestDef{
Name: "ControlPlaneLoadbalancer scheme set to internal",
Vars: []runtimehooksv1.Variable{
capitest.VariableWithValue(
variableName,
v1alpha1.AWSLoadBalancerSpec{
Scheme: &capav1.ELBSchemeInternal,
},
variablePath...,
),
},
RequestItem: request.NewAWSClusterTemplateRequestItem("1234"),
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{
Operation: "add",
Path: "/spec/template/spec/controlPlaneLoadBalancer",
ValueMatcher: gomega.HaveKeyWithValue(
"scheme", "internal",
),
}},
},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2023 D2iQ, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package controlplaneloadbalancer

import (
"testing"

capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"

"k8s.io/utils/ptr"

"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest"
awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig"
)

func TestVariableValidation(t *testing.T) {
capitest.ValidateDiscoverVariables(
t,
clusterconfig.MetaVariableName,
ptr.To(v1alpha1.ClusterConfigSpec{AWS: &v1alpha1.AWSSpec{}}.VariableSchema()),
true,
awsclusterconfig.NewVariable,
capitest.VariableTestDef{
Name: "specified internet-facing scheme",
Vals: v1alpha1.ClusterConfigSpec{
AWS: &v1alpha1.AWSSpec{
ControlPlaneLoadBalancer: &v1alpha1.AWSLoadBalancerSpec{
Scheme: &capav1.ELBSchemeInternetFacing,
},
},
},
},
capitest.VariableTestDef{
Name: "specified internal scheme",
Vals: v1alpha1.ClusterConfigSpec{
AWS: &v1alpha1.AWSSpec{
ControlPlaneLoadBalancer: &v1alpha1.AWSLoadBalancerSpec{
Scheme: &capav1.ELBSchemeInternal,
},
},
},
},
capitest.VariableTestDef{
Name: "specified invalid scheme",
Vals: v1alpha1.ClusterConfigSpec{
AWS: &v1alpha1.AWSSpec{
ControlPlaneLoadBalancer: &v1alpha1.AWSLoadBalancerSpec{
Scheme: ptr.To(capav1.ELBScheme("invalid")),
},
},
},
ExpectError: true,
},
)
}
Loading

0 comments on commit 5c37269

Please sign in to comment.