Skip to content
Open
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
17 changes: 15 additions & 2 deletions config/crd/bases/infrastructure.cluster.x-k8s.io_rosanetworks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,26 @@ spec:
description: ROSANetworkSpec defines the desired state of ROSANetwork
properties:
availabilityZoneCount:
default: 1
description: |-
The number of availability zones to be used for creation of the network infrastructure.
You can specify anything between one and four, depending on the chosen AWS region.
Either AvailabilityZoneCount OR AvailabilityZones must be set.
minimum: 1
type: integer
x-kubernetes-validations:
- message: availabilityZoneCount is immutable
rule: self == oldSelf
availabilityZones:
description: |-
The list of availability zones to be used for creation of the network infrastructure.
You can specify anything between one and four valid availability zones from a given region.
Should you specify both the availabilityZoneCount and availabilityZones, the list of availability zones takes preference.
Either AvailabilityZones OR AvailabilityZoneCount must be set.
items:
type: string
type: array
x-kubernetes-validations:
- message: availabilityZones is immutable
rule: self == oldSelf
cidrBlock:
description: CIDR block to be used for the VPC
format: cidr
Expand Down Expand Up @@ -85,10 +92,16 @@ spec:
description: The AWS region in which the components of ROSA network
infrastruture are to be crated
type: string
x-kubernetes-validations:
- message: region is immutable
rule: self == oldSelf
stackName:
description: The name of the cloudformation stack under which the
network infrastructure would be created
type: string
x-kubernetes-validations:
- message: stackName is immutable
rule: self == oldSelf
stackTags:
additionalProperties:
type: string
Expand Down
1 change: 1 addition & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ rules:
- infrastructure.cluster.x-k8s.io
resources:
- rosamachinepools/finalizers
- rosanetworks/finalizers
- rosaroleconfigs/finalizers
verbs:
- update
Expand Down
44 changes: 44 additions & 0 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,28 @@ webhooks:
resources:
- rosamachinepools
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-infrastructure-cluster-x-k8s-io-v1beta2-rosanetwork
failurePolicy: Fail
matchPolicy: Equivalent
name: default.rosanetwork.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- rosanetworks
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
Expand Down Expand Up @@ -603,6 +625,28 @@ webhooks:
resources:
- rosamachinepools
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-infrastructure-cluster-x-k8s-io-v1beta2-rosanetwork
failurePolicy: Fail
matchPolicy: Equivalent
name: validation.rosanetwork.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- rosanetworks
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
Expand Down
23 changes: 12 additions & 11 deletions exp/api/v1beta2/rosanetwork_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,41 +29,42 @@ const ROSANetworkFinalizer = "rosanetwork.infrastructure.cluster.x-k8s.io"
// ROSANetworkSpec defines the desired state of ROSANetwork
type ROSANetworkSpec struct {
// The name of the cloudformation stack under which the network infrastructure would be created
// +immutable
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="stackName is immutable"
// +kubebuilder:validation:Required
StackName string `json:"stackName"`

// The AWS region in which the components of ROSA network infrastruture are to be crated
// +immutable
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="region is immutable"
// +kubebuilder:validation:Required
Region string `json:"region"`

// The number of availability zones to be used for creation of the network infrastructure.
// You can specify anything between one and four, depending on the chosen AWS region.
// +kubebuilder:default=1
// Either AvailabilityZoneCount OR AvailabilityZones must be set.
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="availabilityZoneCount is immutable"
// +optional
// +immutable
AvailabilityZoneCount int `json:"availabilityZoneCount"`
AvailabilityZoneCount int `json:"availabilityZoneCount,omitempty"`

// The list of availability zones to be used for creation of the network infrastructure.
// You can specify anything between one and four valid availability zones from a given region.
// Should you specify both the availabilityZoneCount and availabilityZones, the list of availability zones takes preference.
// Either AvailabilityZones OR AvailabilityZoneCount must be set.
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="availabilityZones is immutable"
// +optional
// +immutable
AvailabilityZones []string `json:"availabilityZones"`
AvailabilityZones []string `json:"availabilityZones,omitempty"`

// CIDR block to be used for the VPC
// +kubebuilder:validation:Format=cidr
// +immutable
// +kubebuilder:validation:Required
CIDRBlock string `json:"cidrBlock"`

// IdentityRef is a reference to an identity to be used when reconciling rosa network.
// If no identity is specified, the default identity for this controller will be used.
//
// +optional
IdentityRef *infrav1.AWSIdentityReference `json:"identityRef,omitempty"`

// StackTags is an optional set of tags to add to the created cloudformation stack.
// The stack tags will then be automatically applied to the supported AWS resources (VPC, subnets, ...).
//
// +optional
StackTags Tags `json:"stackTags,omitempty"`
}
Expand Down
73 changes: 73 additions & 0 deletions exp/api/v1beta2/rosanetwork_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package v1beta2

import (
"context"
"fmt"

apierrors "k8s.io/apimachinery/pkg/api/errors"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// SetupWebhookWithManager will setup the webhooks for the ROSANetwork.
func (r *ROSANetwork) SetupWebhookWithManager(mgr ctrl.Manager) error {
w := new(rosaNetworkWebhook)
return ctrl.NewWebhookManagedBy(mgr).
For(r).
WithValidator(w).
WithDefaulter(w).
Complete()
}

// +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta2-rosanetwork,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=rosanetworks,versions=v1beta2,name=validation.rosanetwork.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
// +kubebuilder:webhook:verbs=create;update,path=/mutate-infrastructure-cluster-x-k8s-io-v1beta2-rosanetwork,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=rosanetworks,versions=v1beta2,name=default.rosanetwork.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1

type rosaNetworkWebhook struct{}

var _ webhook.CustomDefaulter = &rosaNetworkWebhook{}
var _ webhook.CustomValidator = &rosaNetworkWebhook{}

// ValidateCreate implements admission.Validator.
func (r *rosaNetworkWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
rosaNet, ok := obj.(*ROSANetwork)
if !ok {
return nil, fmt.Errorf("expected an ROSANetwork object but got %T", rosaNet)
}

var allErrs field.ErrorList
if rosaNet.Spec.AvailabilityZoneCount == 0 && len(rosaNet.Spec.AvailabilityZones) == 0 {
err := field.Invalid(field.NewPath("spec.AvailabilityZones"), rosaNet.Spec.AvailabilityZones, "Either AvailabilityZones OR AvailabilityZoneCount must be set.")
allErrs = append(allErrs, err)
}
if rosaNet.Spec.AvailabilityZoneCount != 0 && len(rosaNet.Spec.AvailabilityZones) > 0 {
err := field.Invalid(field.NewPath("spec.AvailabilityZones"), rosaNet.Spec.AvailabilityZones, "Either AvailabilityZones OR AvailabilityZoneCount can be set.")
allErrs = append(allErrs, err)
}

if len(allErrs) > 0 {
return nil, apierrors.NewInvalid(
rosaNet.GroupVersionKind().GroupKind(),
rosaNet.Name,
allErrs)
}

return nil, nil
}

// ValidateUpdate implements admission.Validator.
func (r *rosaNetworkWebhook) ValidateUpdate(ctx context.Context, old runtime.Object, updated runtime.Object) (warnings admission.Warnings, err error) {
return nil, nil
}

// ValidateDelete implements admission.Validator.
func (r *rosaNetworkWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
return nil, nil
}

// Default implements admission.Defaulter.
func (r *rosaNetworkWebhook) Default(ctx context.Context, obj runtime.Object) error {
return nil
}
12 changes: 9 additions & 3 deletions exp/controllers/rosanetwork_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type ROSANetworkReconciler struct {

// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosanetworks,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosanetworks/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosanetworks/finalizers,verbs=update

func (r *ROSANetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, reterr error) {
log := logger.FromContext(ctx)
Expand Down Expand Up @@ -136,15 +137,20 @@ func (r *ROSANetworkReconciler) reconcileNormal(ctx context.Context, rosaNetScop

if r.cfStack == nil { // The CF stack does not exist yet
templateBody := string(rosaCFNetwork.CloudFormationTemplateFile)

zoneCount := 1
if rosaNetScope.ROSANetwork.Spec.AvailabilityZoneCount > 0 {
zoneCount = rosaNetScope.ROSANetwork.Spec.AvailabilityZoneCount
}
cfParams := map[string]string{
"AvailabilityZoneCount": strconv.Itoa(rosaNetScope.ROSANetwork.Spec.AvailabilityZoneCount),
"AvailabilityZoneCount": strconv.Itoa(zoneCount),
"Region": rosaNetScope.ROSANetwork.Spec.Region,
"Name": rosaNetScope.ROSANetwork.Spec.StackName,
"VpcCidr": rosaNetScope.ROSANetwork.Spec.CIDRBlock,
}
// Explicitly specified AZs
for i, zone := range rosaNetScope.ROSANetwork.Spec.AvailabilityZones {
cfParams[fmt.Sprintf("AZ%d", i)] = zone
for idx, zone := range rosaNetScope.ROSANetwork.Spec.AvailabilityZones {
cfParams[fmt.Sprintf("AZ%d", (idx+1))] = zone
}

// Call the AWS CF stack create API
Expand Down
3 changes: 3 additions & 0 deletions exp/controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ func setup() {
if err := (&expinfrav1.ROSARoleConfig{}).SetupWebhookWithManager(testEnv); err != nil {
panic(fmt.Sprintf("Unable to setup ROSARoleConfig webhook: %v", err))
}
if err := (&expinfrav1.ROSANetwork{}).SetupWebhookWithManager(testEnv); err != nil {
panic(fmt.Sprintf("Unable to setup ROSANetwork webhook: %v", err))
}
if err := (&rosacontrolplanev1.ROSAControlPlane{}).SetupWebhookWithManager(testEnv); err != nil {
panic(fmt.Sprintf("Unable to setup ROSAControlPlane webhook: %v", err))
}
Expand Down
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ func main() {
os.Exit(1)
}

if err := (&expinfrav1.ROSANetwork{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "ROSANetwork")
os.Exit(1)
}

setupLog.Debug("enabling ROSA role config controller")
if err = (&expcontrollers.ROSARoleConfigReconciler{
Client: mgr.GetClient(),
Expand Down