Skip to content

Commit

Permalink
Merge pull request #4764 from muraee/rosa-machinepool-version
Browse files Browse the repository at this point in the history
✨ ROSA: Reconcile ROSAMachinePool version
  • Loading branch information
k8s-ci-robot committed Feb 20, 2024
2 parents 2a030a7 + d5c8ce7 commit 3cfa2a8
Show file tree
Hide file tree
Showing 18 changed files with 552 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,6 @@ spec:
type: string
version:
description: Openshift version, for example "4.14.5".
pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
type: string
workerRoleARN:
type: string
Expand Down Expand Up @@ -362,8 +361,13 @@ spec:
AKS, EKS, GKE, etc.
type: boolean
failureMessage:
description: ErrorMessage indicates that there is a terminal problem
reconciling the state, and will be set to a descriptive error message.
description: "FailureMessage will be set in the event that there is
a terminal problem reconciling the state and will be set to a descriptive
error message. \n This field should not be set for transitive errors
that a controller faces that are expected to be fixed automatically
over time (like service outages), but instead indicate that something
is fundamentally wrong with the spec or the configuration of the
controller, and that manual intervention is required."
type: string
id:
description: ID is the cluster ID given by ROSA.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ spec:
type: array
subnet:
type: string
version:
description: Version specifies the penshift version of the nodes associated
with this machinepool. ROSAControlPlane version is used if not set.
type: string
required:
- nodePoolName
type: object
Expand Down Expand Up @@ -146,6 +150,15 @@ spec:
- type
type: object
type: array
failureMessage:
description: "FailureMessage will be set in the event that there is
a terminal problem reconciling the state and will be set to a descriptive
error message. \n This field should not be set for transitive errors
that a controller faces that are expected to be fixed automatically
over time (like service outages), but instead indicate that something
is fundamentally wrong with the spec or the configuration of the
controller, and that manual intervention is required."
type: string
id:
description: ID is the ID given by ROSA.
type: string
Expand Down
4 changes: 2 additions & 2 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
- rosamachinenepools
- rosamachinepools
verbs:
- delete
- get
Expand All @@ -412,7 +412,7 @@ rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
- rosamachinenepools/status
- rosamachinepools/status
verbs:
- get
- patch
Expand Down
88 changes: 88 additions & 0 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,28 @@ webhooks:
resources:
- awsmanagedmachinepools
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-infrastructure-cluster-x-k8s-io-v1beta2-rosamachinepool
failurePolicy: Fail
matchPolicy: Equivalent
name: default.rosamachinepool.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- rosamachinepools
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
Expand Down Expand Up @@ -267,6 +289,28 @@ webhooks:
resources:
- awsmanagedcontrolplanes
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-controlplane-cluster-x-k8s-io-v1beta2-rosacontrolplane
failurePolicy: Fail
matchPolicy: Equivalent
name: default.rosacontrolplanes.controlplane.cluster.x-k8s.io
rules:
- apiGroups:
- controlplane.cluster.x-k8s.io
apiVersions:
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- rosacontrolplanes
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
Expand Down Expand Up @@ -493,6 +537,28 @@ webhooks:
resources:
- awsmanagedmachinepools
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-infrastructure-cluster-x-k8s-io-v1beta2-rosamachinepool
failurePolicy: Fail
matchPolicy: Equivalent
name: validation.rosamachinepool.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- rosamachinepools
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
Expand Down Expand Up @@ -559,3 +625,25 @@ webhooks:
resources:
- awsmanagedcontrolplanes
sideEffects: None
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-controlplane-cluster-x-k8s-io-v1beta2-rosacontrolplane
failurePolicy: Fail
matchPolicy: Equivalent
name: validation.rosacontrolplanes.controlplane.cluster.x-k8s.io
rules:
- apiGroups:
- controlplane.cluster.x-k8s.io
apiVersions:
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- rosacontrolplanes
sideEffects: None
13 changes: 9 additions & 4 deletions controlplane/rosa/api/v1beta2/rosacontrolplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ type RosaControlPlaneSpec struct { //nolint: maligned
Region *string `json:"region"`

// Openshift version, for example "4.14.5".
//
// +kubebuilder:validation:Pattern:=`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`
Version string `json:"version"`

// ControlPlaneEndpoint represents the endpoint used to communicate with the control plane.
Expand Down Expand Up @@ -473,8 +471,15 @@ type RosaControlPlaneStatus struct {
// Ready denotes that the ROSAControlPlane API Server is ready to receive requests.
// +kubebuilder:default=false
Ready bool `json:"ready"`
// ErrorMessage indicates that there is a terminal problem reconciling the
// state, and will be set to a descriptive error message.
// FailureMessage will be set in the event that there is a terminal problem
// reconciling the state and will be set to a descriptive error message.
//
// This field should not be set for transitive errors that a controller
// faces that are expected to be fixed automatically over
// time (like service outages), but instead indicate that something is
// fundamentally wrong with the spec or the configuration of
// the controller, and that manual intervention is required.
//
// +optional
FailureMessage *string `json:"failureMessage,omitempty"`
// Conditions specifies the cpnditions for the managed control plane
Expand Down
81 changes: 81 additions & 0 deletions controlplane/rosa/api/v1beta2/rosacontrolplane_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package v1beta2

import (
"github.com/blang/semver"
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 ROSAControlPlane.
func (r *ROSAControlPlane) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

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

var _ webhook.Defaulter = &ROSAControlPlane{}
var _ webhook.Validator = &ROSAControlPlane{}

// ValidateCreate implements admission.Validator.
func (r *ROSAControlPlane) ValidateCreate() (warnings admission.Warnings, err error) {
var allErrs field.ErrorList

if err := r.validateVersion(); err != nil {
allErrs = append(allErrs, err)
}

if len(allErrs) == 0 {
return nil, nil
}

return nil, apierrors.NewInvalid(
r.GroupVersionKind().GroupKind(),
r.Name,
allErrs,
)
}

// ValidateUpdate implements admission.Validator.
func (r *ROSAControlPlane) ValidateUpdate(old runtime.Object) (warnings admission.Warnings, err error) {
var allErrs field.ErrorList

if err := r.validateVersion(); err != nil {
allErrs = append(allErrs, err)
}

if len(allErrs) == 0 {
return nil, nil
}

return nil, apierrors.NewInvalid(
r.GroupVersionKind().GroupKind(),
r.Name,
allErrs,
)
}

// ValidateDelete implements admission.Validator.
func (r *ROSAControlPlane) ValidateDelete() (warnings admission.Warnings, err error) {
return nil, nil
}

func (r *ROSAControlPlane) validateVersion() *field.Error {
_, err := semver.Parse(r.Spec.Version)
if err != nil {
return field.Invalid(field.NewPath("spec.version"), r.Spec.Version, "version must be a valid semantic version")
}

return nil
}

// Default implements admission.Defaulter.
func (r *ROSAControlPlane) Default() {
SetObjectDefaults_ROSAControlPlane(r)
}
2 changes: 1 addition & 1 deletion controlplane/rosa/api/v1beta2/zz_generated.deepcopy.go

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

23 changes: 11 additions & 12 deletions controlplane/rosa/controllers/rosacontrolplane_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,16 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
}
defer rosaClient.Close()

isValid, err := validateControlPlaneSpec(rosaClient, rosaScope)
failureMessage, err := validateControlPlaneSpec(rosaClient, rosaScope)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to validate ROSAControlPlane.spec: %w", err)
}
if !isValid {
if failureMessage != nil {
rosaScope.ControlPlane.Status.FailureMessage = failureMessage
// dont' requeue because input is invalid and manual intervention is needed.
return ctrl.Result{}, nil
} else {
rosaScope.ControlPlane.Status.FailureMessage = nil
}

cluster, err := rosaClient.GetCluster()
Expand Down Expand Up @@ -270,7 +273,7 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
DisableUserWorkloadMonitoring(true).
Version(
cmv1.NewVersion().
ID(fmt.Sprintf("openshift-v%s", rosaScope.ControlPlane.Spec.Version)).
ID(rosa.VersionID(rosaScope.ControlPlane.Spec.Version)).
ChannelGroup("stable"),
).
ExpirationTimestamp(time.Now().Add(1 * time.Hour)).
Expand Down Expand Up @@ -411,7 +414,7 @@ func (r *ROSAControlPlaneReconciler) reconcileDelete(ctx context.Context, rosaSc

func (r *ROSAControlPlaneReconciler) reconcileClusterVersion(rosaScope *scope.ROSAControlPlaneScope, rosaClient *rosa.RosaClient, cluster *cmv1.Cluster) error {
version := rosaScope.ControlPlane.Spec.Version
if version == cluster.Version().RawID() {
if version == rosa.RawVersionID(cluster.Version()) {
conditions.MarkFalse(rosaScope.ControlPlane, rosacontrolplanev1.ROSAControlPlaneUpgradingCondition, "upgraded", clusterv1.ConditionSeverityInfo, "")
return nil
}
Expand Down Expand Up @@ -560,24 +563,20 @@ func (r *ROSAControlPlaneReconciler) reconcileClusterAdminPassword(ctx context.C
return password, nil
}

func validateControlPlaneSpec(rosaClient *rosa.RosaClient, rosaScope *scope.ROSAControlPlaneScope) (bool, error) {
// reset previous message.
rosaScope.ControlPlane.Status.FailureMessage = nil

func validateControlPlaneSpec(rosaClient *rosa.RosaClient, rosaScope *scope.ROSAControlPlaneScope) (*string, error) {
version := rosaScope.ControlPlane.Spec.Version
isSupported, err := rosaClient.IsVersionSupported(version)
if err != nil {
return false, err
return nil, fmt.Errorf("failed to verify if version is supported: %w", err)
}

if !isSupported {
message := fmt.Sprintf("version %s is not supported", version)
rosaScope.ControlPlane.Status.FailureMessage = &message
return false, nil
return &message, nil
}

// TODO: add more input validations
return true, nil
return nil, nil
}

func (r *ROSAControlPlaneReconciler) rosaClusterToROSAControlPlane(log *logger.Logger) handler.MapFunc {
Expand Down
4 changes: 2 additions & 2 deletions docs/book/src/topics/rosa/creating-a-cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ CAPA controller requires an API token in order to be able to provision ROSA clus
--from-literal=ocmToken='eyJhbGciOiJIUzI1NiIsI....' \
--from-literal=ocmApiUrl='https://api.openshift.com'
```

Alternatively, you can edit CAPA controller deployment to provide the credentials:
```shell
kubectl edit deployment -n capa-system capa-controller-manager
Expand All @@ -36,7 +36,7 @@ Once Step 3 is done, you will be ready to proceed with creating a ROSA cluster u

1. Prepare the environment:
```bash
export OPENSHIFT_VERSION="openshift-v4.14.5"
export OPENSHIFT_VERSION="4.14.5"
export CLUSTER_NAME="capi-rosa-quickstart"
export AWS_REGION="us-west-2"
export AWS_AVAILABILITY_ZONE="us-west-2a"
Expand Down
4 changes: 3 additions & 1 deletion exp/api/v1beta2/conditions_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ const (
)

const (
// RosaMachinePoolReadyCondition condition reports on the successful reconciliation of rosa control plane.
// RosaMachinePoolReadyCondition condition reports on the successful reconciliation of rosa machinepool.
RosaMachinePoolReadyCondition clusterv1.ConditionType = "RosaMchinePoolReady"
// RosaMachinePoolUpgradingCondition condition reports whether ROSAMachinePool is upgrading or not.
RosaMachinePoolUpgradingCondition clusterv1.ConditionType = "RosaMchinePoolUpgrading"
// WaitingForRosaControlPlaneReason used when the machine pool is waiting for
// ROSA control plane infrastructure to be ready before proceeding.
WaitingForRosaControlPlaneReason = "WaitingForRosaControlPlane"
Expand Down

0 comments on commit 3cfa2a8

Please sign in to comment.