Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ name: E2E Tests
on:
pull_request:
branches: [ "main" ]
workflow_dispatch:
branches: [ "main" ]

jobs:
test-e2e:
name: Run on Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- name: Clone the code
uses: actions/checkout@v4
Expand Down
7 changes: 0 additions & 7 deletions api/v1alpha1/scalewaycluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const ClusterFinalizer = "scalewaycluster.infrastructure.cluster.x-k8s.io/sc-pro
// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlanePrivateDNS)) == (has(oldSelf.network) && has(oldSelf.network.controlPlanePrivateDNS))",message="controlPlanePrivateDNS cannot be added or removed"
// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.privateNetwork)) == (has(oldSelf.network) && has(oldSelf.network.privateNetwork))",message="privateNetwork cannot be added or removed"
//
// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.port)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.port))",message="port cannot be added or removed"
// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.private)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.private))",message="private cannot be added or removed"
// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.ip)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.ip))",message="ip cannot be added or removed"
// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.zone)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.zone))",message="zone cannot be added or removed"
Expand Down Expand Up @@ -127,12 +126,6 @@ type ControlPlaneLoadBalancerSpec struct {
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.privateIP) || self.privateIP == oldSelf.privateIP",message="privateIP is immutable"
LoadBalancerSpec `json:",inline"`

// Port configured on the Load Balancer. It must be valid port range (1-65535).
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
Port *int32 `json:"port,omitempty"`

// AllowedRanges allows to set a list of allowed IP ranges that can access
// the cluster through the loadbalancer. When unset, all IP ranges are allowed.
// To allow the cluster to work properly, public IPs of nodes and Public
Expand Down
5 changes: 0 additions & 5 deletions api/v1alpha1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,6 @@ spec:
description: IP to use when creating a loadbalancer.
format: ipv4
type: string
port:
description: Port configured on the Load Balancer. It must
be valid port range (1-65535).
format: int32
maximum: 65535
minimum: 1
type: integer
x-kubernetes-validations:
- message: Value is immutable
rule: self == oldSelf
private:
description: Private disables the creation of a public IP
on the LoadBalancers when it's set to true.
Expand Down Expand Up @@ -360,10 +350,6 @@ spec:
- message: privateNetwork cannot be added or removed
rule: (has(self.network) && has(self.network.privateNetwork)) == (has(oldSelf.network)
&& has(oldSelf.network.privateNetwork))
- message: port cannot be added or removed
rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer)
&& has(self.network.controlPlaneLoadBalancer.port)) == (has(oldSelf.network)
&& has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.port))
- message: private cannot be added or removed
rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer)
&& has(self.network.controlPlaneLoadBalancer.private)) == (has(oldSelf.network)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,6 @@ spec:
description: IP to use when creating a loadbalancer.
format: ipv4
type: string
port:
description: Port configured on the Load Balancer.
It must be valid port range (1-65535).
format: int32
maximum: 65535
minimum: 1
type: integer
x-kubernetes-validations:
- message: Value is immutable
rule: self == oldSelf
private:
description: Private disables the creation of a public
IP on the LoadBalancers when it's set to true.
Expand Down Expand Up @@ -362,10 +352,6 @@ spec:
- message: privateNetwork cannot be added or removed
rule: (has(self.network) && has(self.network.privateNetwork))
== (has(oldSelf.network) && has(oldSelf.network.privateNetwork))
- message: port cannot be added or removed
rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer)
&& has(self.network.controlPlaneLoadBalancer.port)) == (has(oldSelf.network)
&& has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.port))
- message: private cannot be added or removed
rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer)
&& has(self.network.controlPlaneLoadBalancer.private)) ==
Expand Down
25 changes: 22 additions & 3 deletions docs/scalewaycluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,28 @@ For more information about private DNS, please refer to the [Understanding Scale
When creating a `ScalewayCluster`, a "main" Load Balancer is always created.
It is also possible to specify "extra" Load Balancers to achieve regional redundancy.

#### Frontend API server port

The kube-apiserver Load Balancer's frontend port can be set at the cluster creation in the `Cluster` object.

Here is an example of a Load Balancer frontend port configuration:

```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: my-cluster
namespace: default
spec:
# ...
clusterNetwork:
apiServerPort: 443
# ...
```

- The `apiServerPort` field specifies the port of the Load Balancer frontend that exposes the kube-apiserver(s).
This field should not be changed after the creation of the cluster.

#### Main Load Balancer

The main Load Balancer is always created by default, it is not possible to disable it.
Expand All @@ -168,7 +190,6 @@ spec:
network:
controlPlaneLoadBalancer:
type: LB-S
port: 443
zone: fr-par-1
ip: 42.42.42.42 # optional
# private: true
Expand All @@ -177,8 +198,6 @@ spec:
```

- The `type` field can be updated to migrate the Load Balancer to another type.
- The `port` field specifies the port of the Load Balancer frontend that exposes the kube-apiserver(s).
This field is immutable.
- The `zone` field specifies where the Load Balancer will be created. Must be in the same region
as the `ScalewayCluster` region. This defaults to the first availability zone of the region.
This field is immutable.
Expand Down
7 changes: 3 additions & 4 deletions internal/scope/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,9 @@ func (c *Cluster) PrivateNetworkID() (string, error) {
func (c *Cluster) ControlPlaneLoadBalancerPort() int32 {
var port int32 = defaultFrontendControlPlanePort

if c.ScalewayCluster.Spec.Network != nil &&
c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer != nil &&
c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.Port != nil {
port = *c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.Port
if c.Cluster.Spec.ClusterNetwork != nil &&
c.Cluster.Spec.ClusterNetwork.APIServerPort != nil {
port = *c.Cluster.Spec.ClusterNetwork.APIServerPort
}

return port
Expand Down
17 changes: 8 additions & 9 deletions internal/scope/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client"
"github.com/scaleway/scaleway-sdk-go/scw"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/util/patch"
)

Expand Down Expand Up @@ -311,7 +312,7 @@ func TestCluster_PrivateNetworkID(t *testing.T) {
func TestCluster_ControlPlaneLoadBalancerPort(t *testing.T) {
t.Parallel()
type fields struct {
ScalewayCluster *infrav1.ScalewayCluster
Cluster *clusterv1.Cluster
}
tests := []struct {
name string
Expand All @@ -321,19 +322,17 @@ func TestCluster_ControlPlaneLoadBalancerPort(t *testing.T) {
{
name: "empty spec",
fields: fields{
ScalewayCluster: &infrav1.ScalewayCluster{},
Cluster: &clusterv1.Cluster{},
},
want: defaultFrontendControlPlanePort,
},
{
name: "override with 443",
fields: fields{
ScalewayCluster: &infrav1.ScalewayCluster{
Spec: infrav1.ScalewayClusterSpec{
Network: &infrav1.NetworkSpec{
ControlPlaneLoadBalancer: &infrav1.ControlPlaneLoadBalancerSpec{
Port: scw.Int32Ptr(443),
},
Cluster: &clusterv1.Cluster{
Spec: clusterv1.ClusterSpec{
ClusterNetwork: &clusterv1.ClusterNetwork{
APIServerPort: scw.Int32Ptr(443),
},
},
},
Expand All @@ -345,7 +344,7 @@ func TestCluster_ControlPlaneLoadBalancerPort(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
c := &Cluster{
ScalewayCluster: tt.fields.ScalewayCluster,
Cluster: tt.fields.Cluster,
}
if got := c.ControlPlaneLoadBalancerPort(); got != tt.want {
t.Errorf("Cluster.ControlPlaneLoadBalancerPort() = %v, want %v", got, tt.want)
Expand Down
87 changes: 87 additions & 0 deletions internal/service/scaleway/lb/lb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"github.com/scaleway/scaleway-sdk-go/scw"
"go.uber.org/mock/gomock"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/cluster-api/api/v1beta1"
)

const (
Expand Down Expand Up @@ -69,6 +71,12 @@ func TestService_Reconcile(t *testing.T) {
Namespace: "default",
},
},
Cluster: &v1beta1.Cluster{
ObjectMeta: v1.ObjectMeta{
Name: "cluster",
Namespace: "default",
},
},
},
},
args: args{
Expand Down Expand Up @@ -121,6 +129,79 @@ func TestService_Reconcile(t *testing.T) {
g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(Equal(scw.StringPtr("42.42.42.42")))
},
},
{
name: "custom public LB, no extra LB, no Private Network, no ACL: create",
fields: fields{
Cluster: &scope.Cluster{
ScalewayCluster: &v1alpha1.ScalewayCluster{
ObjectMeta: v1.ObjectMeta{
Name: "cluster",
Namespace: "default",
},
},
Cluster: &v1beta1.Cluster{
ObjectMeta: v1.ObjectMeta{
Name: "cluster",
Namespace: "default",
},
Spec: v1beta1.ClusterSpec{
ClusterNetwork: &v1beta1.ClusterNetwork{
APIServerPort: ptr.To(int32(4242)),
},
},
},
},
},
args: args{
ctx: context.TODO(),
},
expect: func(i *mock_client.MockInterfaceMockRecorder) {
tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"}

// Main LB
i.GetZoneOrDefault(nil).Return(scw.ZoneFrPar1, nil)
i.FindLB(gomock.Any(), scw.ZoneFrPar1, append(tags, CAPSMainLBTag)).Return(nil, client.ErrNoItemFound)
i.CreateLB(gomock.Any(), scw.ZoneFrPar1, "cluster", "LB-S", nil, false, append(tags, CAPSMainLBTag)).Return(&lb.LB{
ID: lbID,
Name: "cluster",
Status: lb.LBStatusReady,
Zone: scw.ZoneFrPar1,
IP: []*lb.IP{{IPAddress: "42.42.42.42"}},
}, nil)

// Extra LBs
i.FindLBs(gomock.Any(), append(tags, CAPSExtraLBTag)).Return([]*lb.LB{}, nil)

// Backend
i.FindBackend(gomock.Any(), scw.ZoneFrPar1, lbID, BackendName).Return(nil, client.ErrNoItemFound)
i.CreateBackend(gomock.Any(), scw.ZoneFrPar1, lbID, BackendName, nil, backendControlPlanePort).Return(&lb.Backend{
ID: backendID,
LB: &lb.LB{
ID: lbID,
Zone: scw.ZoneFrPar1,
},
}, nil)

// Frontend
i.FindFrontend(gomock.Any(), scw.ZoneFrPar1, lbID, FrontendName).Return(nil, client.ErrNoItemFound)
i.CreateFrontend(gomock.Any(), scw.ZoneFrPar1, lbID, FrontendName, backendID, int32(4242)).Return(&lb.Frontend{
ID: frontendID,
LB: &lb.LB{
ID: lbID,
Zone: scw.ZoneFrPar1,
},
}, nil)

// ACL
i.FindLBACLByName(gomock.Any(), scw.ZoneFrPar1, frontendID, allowedRangesACLName).Return(nil, client.ErrNoItemFound)
i.FindLBACLByName(gomock.Any(), scw.ZoneFrPar1, frontendID, publicGatewayACLName).Return(nil, client.ErrNoItemFound)
i.FindLBACLByName(gomock.Any(), scw.ZoneFrPar1, frontendID, denyAllACLName).Return(nil, client.ErrNoItemFound)
},
asserts: func(g *WithT, c *scope.Cluster) {
g.Expect(c.ScalewayCluster.Status.Network).ToNot(BeNil())
g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(Equal(scw.StringPtr("42.42.42.42")))
},
},
{
name: "public LB, extra LBs, Private Network, ACL: up-to-date",
fields: fields{
Expand Down Expand Up @@ -151,6 +232,12 @@ func TestService_Reconcile(t *testing.T) {
},
},
},
Cluster: &v1beta1.Cluster{
ObjectMeta: v1.ObjectMeta{
Name: "cluster",
Namespace: "default",
},
},
},
},
args: args{
Expand Down
1 change: 1 addition & 0 deletions templates/cluster-template-private-network.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ metadata:
name: ${CLUSTER_NAME}
spec:
clusterNetwork:
apiServerPort: ${API_SERVER_PORT:=null}
pods:
cidrBlocks:
- 10.244.0.0/16
Expand Down
1 change: 1 addition & 0 deletions templates/cluster-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ metadata:
name: ${CLUSTER_NAME}
spec:
clusterNetwork:
apiServerPort: ${API_SERVER_PORT:=null}
pods:
cidrBlocks:
- 10.244.0.0/16
Expand Down
18 changes: 18 additions & 0 deletions test/e2e/scaleway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,22 @@ var _ = Describe("Workload cluster creation", func() {
}
})
})

Context("Running the CAPSClusterDeploymentSpec with front api custom port", func() {
CAPSClusterDeploymentSpec(func() CAPSClusterDeploymentSpecInput {
return CAPSClusterDeploymentSpecInput{
E2EConfig: e2eConfig,
ClusterctlConfigPath: clusterctlConfigPath,
BootstrapClusterProxy: bootstrapClusterProxy,
ArtifactFolder: artifactFolder,
SkipCleanup: skipCleanup,
ControlPlaneMachineCount: 3,
WorkerMachineCount: 2,
Flavor: "",
ClusterctlVariables: map[string]string{
"API_SERVER_PORT": "443",
},
}
})
})
})