Skip to content

Commit 72780f6

Browse files
authored
feat: add control-plane load balancer scheme patch (#228)
Fixes #202 Depends on #220 Tested manually setting the `internal` ``` spec: bastion: allowedCIDRBlocks: - 0.0.0.0/0 enabled: false controlPlaneEndpoint: host: "" port: 0 controlPlaneLoadBalancer: crossZoneLoadBalancing: false loadBalancerType: classic scheme: internal ```
1 parent 22e4c6d commit 72780f6

File tree

8 files changed

+348
-3
lines changed

8 files changed

+348
-3
lines changed

api/v1alpha1/aws_clusterconfig_types.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ package v1alpha1
55

66
import (
77
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
8+
9+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables"
10+
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
811
)
912

1013
type AWSSpec struct {
@@ -13,6 +16,8 @@ type AWSSpec struct {
1316
Region *Region `json:"region,omitempty"`
1417
// +optional
1518
Network *AWSNetwork `json:"network,omitempty"`
19+
// +optional
20+
ControlPlaneLoadBalancer *AWSLoadBalancerSpec `json:"controlPlaneLoadBalancer,omitempty"`
1621
}
1722

1823
func (AWSSpec) VariableSchema() clusterv1.VariableSchema {
@@ -21,8 +26,9 @@ func (AWSSpec) VariableSchema() clusterv1.VariableSchema {
2126
Description: "AWS cluster configuration",
2227
Type: "object",
2328
Properties: map[string]clusterv1.JSONSchemaProps{
24-
"region": Region("").VariableSchema().OpenAPIV3Schema,
25-
"network": AWSNetwork{}.VariableSchema().OpenAPIV3Schema,
29+
"region": Region("").VariableSchema().OpenAPIV3Schema,
30+
"network": AWSNetwork{}.VariableSchema().OpenAPIV3Schema,
31+
"controlPlaneLoadBalancer": AWSLoadBalancerSpec{}.VariableSchema().OpenAPIV3Schema,
2632
},
2733
},
2834
}
@@ -114,3 +120,30 @@ func (SubnetSpec) VariableSchema() clusterv1.VariableSchema {
114120
},
115121
}
116122
}
123+
124+
// AWSLoadBalancerSpec configures an AWS control-plane LoadBalancer.
125+
type AWSLoadBalancerSpec struct {
126+
// Scheme sets the scheme of the load balancer (defaults to internet-facing)
127+
// +kubebuilder:default=internet-facing
128+
// +kubebuilder:validation:Enum=internet-facing;internal
129+
// +optional
130+
Scheme *capav1.ELBScheme `json:"scheme,omitempty"`
131+
}
132+
133+
func (AWSLoadBalancerSpec) VariableSchema() clusterv1.VariableSchema {
134+
supportedScheme := []capav1.ELBScheme{capav1.ELBSchemeInternetFacing, capav1.ELBSchemeInternal}
135+
136+
return clusterv1.VariableSchema{
137+
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
138+
Description: "AWS control-plane LoadBalancer configuration",
139+
Type: "object",
140+
Properties: map[string]clusterv1.JSONSchemaProps{
141+
"scheme": {
142+
Description: "Scheme sets the scheme of the load balancer (defaults to internet-facing)",
143+
Type: "string",
144+
Enum: variables.MustMarshalValuesToEnumJSON(supportedScheme...),
145+
},
146+
},
147+
},
148+
}
149+
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 27 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
+++
2+
title = "Control Plane Load Balancer"
3+
+++
4+
5+
The control-plane load balancer customization allows the user
6+
to modify the load balancer configuration for the control-plane's API server.
7+
8+
This customization will be available when the
9+
[provider-specific cluster configuration patch]({{< ref "..">}}) is included in the `ClusterClass`.
10+
11+
## Example
12+
13+
To use an internal ELB scheme, use the following configuration:
14+
15+
```yaml
16+
apiVersion: cluster.x-k8s.io/v1beta1
17+
kind: Cluster
18+
metadata:
19+
name: <NAME>
20+
spec:
21+
topology:
22+
variables:
23+
- name: clusterConfig
24+
value:
25+
aws:
26+
controlPlaneLoadBalancer:
27+
scheme: internal
28+
```
29+
30+
Applying this configuration will result in the following value being set:
31+
32+
- `AWSClusterTemplate`:
33+
34+
- ```yaml
35+
spec:
36+
controlPlaneLoadBalancer:
37+
scheme: internal
38+
```
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package controlplaneloadbalancer
5+
6+
import (
7+
"context"
8+
9+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
10+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
11+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
12+
ctrl "sigs.k8s.io/controller-runtime"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
15+
"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
16+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches"
17+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors"
18+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables"
19+
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
20+
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig"
21+
)
22+
23+
const (
24+
// VariableName is the external patch variable name.
25+
VariableName = "controlPlaneLoadBalancer"
26+
)
27+
28+
type awsControlPlaneLoadBalancer struct {
29+
variableName string
30+
variableFieldPath []string
31+
}
32+
33+
func NewPatch() *awsControlPlaneLoadBalancer {
34+
return newAWSControlPlaneLoadBalancer(
35+
clusterconfig.MetaVariableName,
36+
v1alpha1.AWSVariableName,
37+
VariableName,
38+
)
39+
}
40+
41+
func newAWSControlPlaneLoadBalancer(
42+
variableName string,
43+
variableFieldPath ...string,
44+
) *awsControlPlaneLoadBalancer {
45+
return &awsControlPlaneLoadBalancer{
46+
variableName: variableName,
47+
variableFieldPath: variableFieldPath,
48+
}
49+
}
50+
51+
func (h *awsControlPlaneLoadBalancer) Mutate(
52+
ctx context.Context,
53+
obj *unstructured.Unstructured,
54+
vars map[string]apiextensionsv1.JSON,
55+
holderRef runtimehooksv1.HolderReference,
56+
_ client.ObjectKey,
57+
) error {
58+
log := ctrl.LoggerFrom(ctx).WithValues(
59+
"holderRef", holderRef,
60+
)
61+
62+
controlPlaneLoadBalancerVar, found, err := variables.Get[v1alpha1.AWSLoadBalancerSpec](
63+
vars,
64+
h.variableName,
65+
h.variableFieldPath...,
66+
)
67+
if err != nil {
68+
return err
69+
}
70+
if !found {
71+
log.V(5).Info("AWS ControlPlaneLoadBalancer variable not defined")
72+
return nil
73+
}
74+
75+
log = log.WithValues(
76+
"variableName",
77+
h.variableName,
78+
"variableFieldPath",
79+
h.variableFieldPath,
80+
"variableValue",
81+
controlPlaneLoadBalancerVar,
82+
)
83+
84+
return patches.MutateIfApplicable(
85+
obj,
86+
vars,
87+
&holderRef,
88+
selectors.InfrastructureCluster(capav1.GroupVersion.Version, "AWSClusterTemplate"),
89+
log,
90+
func(obj *capav1.AWSClusterTemplate) error {
91+
log.WithValues(
92+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
93+
"patchedObjectName", client.ObjectKeyFromObject(obj),
94+
).Info("setting ControlPlaneLoadBalancer in AWSCluster spec")
95+
96+
controlPlaneLoadBalancer := obj.Spec.Template.Spec.ControlPlaneLoadBalancer
97+
if controlPlaneLoadBalancer == nil {
98+
obj.Spec.Template.Spec.ControlPlaneLoadBalancer = &capav1.AWSLoadBalancerSpec{}
99+
}
100+
obj.Spec.Template.Spec.ControlPlaneLoadBalancer.Scheme = controlPlaneLoadBalancerVar.Scheme
101+
102+
return nil
103+
},
104+
)
105+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package tests
5+
6+
import (
7+
"testing"
8+
9+
"github.com/onsi/gomega"
10+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
11+
12+
"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
13+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation"
14+
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
15+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest"
16+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest/request"
17+
)
18+
19+
func TestGeneratePatches(
20+
t *testing.T,
21+
generatorFunc func() mutation.GeneratePatches,
22+
variableName string,
23+
variablePath ...string,
24+
) {
25+
t.Helper()
26+
27+
capitest.ValidateGeneratePatches(
28+
t,
29+
generatorFunc,
30+
capitest.PatchTestDef{
31+
Name: "unset variable",
32+
},
33+
capitest.PatchTestDef{
34+
Name: "ControlPlaneLoadbalancer scheme set to internet-facing",
35+
Vars: []runtimehooksv1.Variable{
36+
capitest.VariableWithValue(
37+
variableName,
38+
v1alpha1.AWSLoadBalancerSpec{
39+
Scheme: &capav1.ELBSchemeInternetFacing,
40+
},
41+
variablePath...,
42+
),
43+
},
44+
RequestItem: request.NewAWSClusterTemplateRequestItem("1234"),
45+
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{
46+
Operation: "add",
47+
Path: "/spec/template/spec/controlPlaneLoadBalancer",
48+
ValueMatcher: gomega.HaveKeyWithValue(
49+
"scheme", "internet-facing",
50+
),
51+
}},
52+
},
53+
capitest.PatchTestDef{
54+
Name: "ControlPlaneLoadbalancer scheme set to internal",
55+
Vars: []runtimehooksv1.Variable{
56+
capitest.VariableWithValue(
57+
variableName,
58+
v1alpha1.AWSLoadBalancerSpec{
59+
Scheme: &capav1.ELBSchemeInternal,
60+
},
61+
variablePath...,
62+
),
63+
},
64+
RequestItem: request.NewAWSClusterTemplateRequestItem("1234"),
65+
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{
66+
Operation: "add",
67+
Path: "/spec/template/spec/controlPlaneLoadBalancer",
68+
ValueMatcher: gomega.HaveKeyWithValue(
69+
"scheme", "internal",
70+
),
71+
}},
72+
},
73+
)
74+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package controlplaneloadbalancer
5+
6+
import (
7+
"testing"
8+
9+
"k8s.io/utils/ptr"
10+
11+
"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
12+
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
13+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest"
14+
awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig"
15+
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig"
16+
)
17+
18+
func TestVariableValidation(t *testing.T) {
19+
capitest.ValidateDiscoverVariables(
20+
t,
21+
clusterconfig.MetaVariableName,
22+
ptr.To(v1alpha1.ClusterConfigSpec{AWS: &v1alpha1.AWSSpec{}}.VariableSchema()),
23+
true,
24+
awsclusterconfig.NewVariable,
25+
capitest.VariableTestDef{
26+
Name: "specified internet-facing scheme",
27+
Vals: v1alpha1.ClusterConfigSpec{
28+
AWS: &v1alpha1.AWSSpec{
29+
ControlPlaneLoadBalancer: &v1alpha1.AWSLoadBalancerSpec{
30+
Scheme: &capav1.ELBSchemeInternetFacing,
31+
},
32+
},
33+
},
34+
},
35+
capitest.VariableTestDef{
36+
Name: "specified internal scheme",
37+
Vals: v1alpha1.ClusterConfigSpec{
38+
AWS: &v1alpha1.AWSSpec{
39+
ControlPlaneLoadBalancer: &v1alpha1.AWSLoadBalancerSpec{
40+
Scheme: &capav1.ELBSchemeInternal,
41+
},
42+
},
43+
},
44+
},
45+
capitest.VariableTestDef{
46+
Name: "specified invalid scheme",
47+
Vals: v1alpha1.ClusterConfigSpec{
48+
AWS: &v1alpha1.AWSSpec{
49+
ControlPlaneLoadBalancer: &v1alpha1.AWSLoadBalancerSpec{
50+
Scheme: ptr.To(capav1.ELBScheme("invalid")),
51+
},
52+
},
53+
},
54+
ExpectError: true,
55+
},
56+
)
57+
}

0 commit comments

Comments
 (0)