-
-
Notifications
You must be signed in to change notification settings - Fork 17
feat(scaling): Add Scaler CRD #1190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8d69f3b
feat: Add Scaler CRD
Techassi fc4aef4
fix: Adjust status representation/schema
Techassi cf5aa2f
chore: Add dev comments
Techassi 41f2bcc
chore: Add CRD preview for Scaler
Techassi c317311
chore: Merge remote-tracking branch 'origin' into feat/add-scaler-crd
Techassi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| --- | ||
| apiVersion: apiextensions.k8s.io/v1 | ||
| kind: CustomResourceDefinition | ||
| metadata: | ||
| name: scalers.autoscaling.stackable.tech | ||
| spec: | ||
| group: autoscaling.stackable.tech | ||
| names: | ||
| categories: [] | ||
| kind: Scaler | ||
| plural: scalers | ||
| shortNames: [] | ||
| singular: scaler | ||
| scope: Namespaced | ||
| versions: | ||
| - additionalPrinterColumns: [] | ||
| name: v1alpha1 | ||
| schema: | ||
| openAPIV3Schema: | ||
| description: Auto-generated derived type for ScalerSpec via `CustomResource` | ||
| properties: | ||
| spec: | ||
| properties: | ||
| replicas: | ||
| description: |- | ||
| Desired replica count. | ||
|
|
||
| Written by the horizontal pod autoscaling mechanism via the /scale subresource. | ||
|
|
||
| NOTE: This and other replica fields)use a [`u16`] instead of a [`i32`] used by | ||
| [`k8s_openapi`] types to force a non-negative replica count. All [`u16`]s can be | ||
| converted losslessly to [`i32`]s where needed. | ||
|
|
||
| Upstream issues: | ||
|
|
||
| - https://github.com/kubernetes/kubernetes/issues/105533 | ||
| - https://github.com/Arnavion/k8s-openapi/issues/136 | ||
| format: uint16 | ||
| maximum: 65535.0 | ||
| minimum: 0.0 | ||
| type: integer | ||
| required: | ||
| - replicas | ||
| type: object | ||
| status: | ||
| description: Status of a StackableScaler. | ||
| nullable: true | ||
| properties: | ||
| lastTransitionTime: | ||
| description: Timestamp indicating when the scaler state last transitioned. | ||
| format: date-time | ||
| type: string | ||
| replicas: | ||
| description: |- | ||
| The current total number of replicas targeted by the managed StatefulSet. | ||
|
|
||
| Exposed via the `/scale` subresource for horizontal pod autoscaling consumption. | ||
| format: uint16 | ||
| maximum: 65535.0 | ||
| minimum: 0.0 | ||
| type: integer | ||
| selector: | ||
| description: Label selector string for HPA pod counting. Written at `.status.selector`. | ||
| nullable: true | ||
| type: string | ||
| state: | ||
| description: The current state of the scaler state machine. | ||
| properties: | ||
| details: | ||
| properties: | ||
| failedIn: | ||
| description: In which state the scaling operation failed. | ||
| enum: | ||
| - preScaling | ||
| - scaling | ||
| - postScaling | ||
| type: string | ||
| previous_replicas: | ||
| maximum: 65535.0 | ||
| minimum: 0.0 | ||
| type: uint16 | ||
| reason: | ||
| type: string | ||
| type: object | ||
| state: | ||
| enum: | ||
| - idle | ||
| - preScaling | ||
| - scaling | ||
| - postScaling | ||
| - failed | ||
| type: string | ||
| required: | ||
| - state | ||
| type: object | ||
| required: | ||
| - replicas | ||
| - state | ||
| - lastTransitionTime | ||
| type: object | ||
| required: | ||
| - spec | ||
| title: Scaler | ||
| type: object | ||
| served: true | ||
| storage: true | ||
| subresources: | ||
| scale: | ||
| labelSelectorPath: .status.selector | ||
| specReplicasPath: .spec.replicas | ||
| statusReplicasPath: .status.replicas | ||
| status: {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| use std::borrow::Cow; | ||
|
|
||
| use k8s_openapi::apimachinery::pkg::apis::meta::v1::Time; | ||
| use kube::CustomResource; | ||
| use schemars::JsonSchema; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| #[cfg(doc)] | ||
| use crate::kvp::Annotation; | ||
| use crate::versioned::versioned; | ||
|
|
||
| #[versioned(version(name = "v1alpha1"))] | ||
| pub mod versioned { | ||
| #[versioned(crd( | ||
| group = "autoscaling.stackable.tech", | ||
| status = ScalerStatus, | ||
| scale( | ||
| spec_replicas_path = ".spec.replicas", | ||
| status_replicas_path = ".status.replicas", | ||
| label_selector_path = ".status.selector" | ||
| ), | ||
| namespaced | ||
| ))] | ||
| #[derive(Clone, Debug, PartialEq, CustomResource, Deserialize, Serialize, JsonSchema)] | ||
| pub struct ScalerSpec { | ||
| /// Desired replica count. | ||
| /// | ||
| /// Written by the horizontal pod autoscaling mechanism via the /scale subresource. | ||
| /// | ||
| /// NOTE: This and other replica fields)use a [`u16`] instead of a [`i32`] used by | ||
| /// [`k8s_openapi`] types to force a non-negative replica count. All [`u16`]s can be | ||
| /// converted losslessly to [`i32`]s where needed. | ||
| /// | ||
| /// Upstream issues: | ||
| /// | ||
| /// - https://github.com/kubernetes/kubernetes/issues/105533 | ||
| /// - https://github.com/Arnavion/k8s-openapi/issues/136 | ||
| pub replicas: u16, | ||
| } | ||
| } | ||
|
|
||
| /// Status of a StackableScaler. | ||
| #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct ScalerStatus { | ||
| /// The current total number of replicas targeted by the managed StatefulSet. | ||
| /// | ||
| /// Exposed via the `/scale` subresource for horizontal pod autoscaling consumption. | ||
| pub replicas: u16, | ||
|
|
||
| /// Label selector string for HPA pod counting. Written at `.status.selector`. | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub selector: Option<String>, | ||
|
|
||
| /// The current state of the scaler state machine. | ||
| pub state: ScalerState, | ||
|
|
||
| /// Timestamp indicating when the scaler state last transitioned. | ||
| pub last_transition_time: Time, | ||
| } | ||
|
|
||
| // We use `#[serde(tag)]` and `#[serde(content)]` here to circumvent Kubernetes restrictions in their | ||
| // structural schema subset of OpenAPI schemas. They don't allow one variant to be typed as a string | ||
| // and others to be typed as objects. We therefore encode the variant data in a separate details | ||
| // key/object. With this, all variants can be encoded as strings, while the status can still contain | ||
| // additional data in an extra field when needed. | ||
| #[derive(Clone, Debug, Deserialize, Serialize, strum::Display)] | ||
| #[serde( | ||
| tag = "state", | ||
| content = "details", | ||
| rename_all = "camelCase", | ||
| rename_all_fields = "camelCase" | ||
| )] | ||
| #[strum(serialize_all = "camelCase")] | ||
| pub enum ScalerState { | ||
| /// No scaling operation is in progress. | ||
| Idle, | ||
|
|
||
| /// Running the `pre_scale` hook (e.g. data offload). | ||
| PreScaling, | ||
|
|
||
| /// Waiting for the StatefulSet to converge to the new replica count. | ||
| /// | ||
| /// This stage additionally tracks the previous replica count to be able derive the direction | ||
| /// of the scaling operation. | ||
| Scaling { previous_replicas: u16 }, | ||
|
|
||
| /// Running the `post_scale` hook (e.g. cluster rebalance). | ||
| /// | ||
| /// This stage additionally tracks the previous replica count to be able derive the direction | ||
| /// of the scaling operation. | ||
| PostScaling { previous_replicas: u16 }, | ||
|
|
||
| /// A hook returned an error. | ||
| /// | ||
| /// The scaler stays here until the user applies the [`Annotation::autoscaling_retry`] annotation | ||
| /// to trigger a reset to [`ScalerState::Idle`]. | ||
| Failed { | ||
| /// Which stage produced the error. | ||
| failed_in: FailedInState, | ||
|
|
||
| /// Human-readable error message from the hook. | ||
| reason: String, | ||
| }, | ||
| } | ||
|
|
||
| // We manually implement the JSON schema instead of deriving it, because kube's schema transformer | ||
| // cannot handle the derived JsonSchema and proceeds to hit the following error: "Property "state" | ||
| // has the schema ... but was already defined as ... in another subschema. The schemas for a | ||
| // property used in multiple subschemas must be identical". | ||
| impl JsonSchema for ScalerState { | ||
| fn schema_name() -> Cow<'static, str> { | ||
| "ScalerState".into() | ||
| } | ||
|
|
||
| fn json_schema(generator: &mut schemars::generate::SchemaGenerator) -> schemars::Schema { | ||
| schemars::json_schema!({ | ||
| "type": "object", | ||
| "required": ["state"], | ||
| "properties": { | ||
| "state": { | ||
| "type": "string", | ||
| "enum": ["idle", "preScaling", "scaling", "postScaling", "failed"] | ||
| }, | ||
| "details": { | ||
| "type": "object", | ||
| "properties": { | ||
| "failedIn": generator.subschema_for::<FailedInState>(), | ||
| "previous_replicas": { | ||
| "type": "uint16", | ||
| "minimum": u16::MIN, | ||
| "maximum": u16::MAX | ||
| }, | ||
| "reason": { "type": "string" } | ||
| } | ||
| } | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| /// In which state the scaling operation failed. | ||
| #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub enum FailedInState { | ||
| /// The `pre_scale` hook returned an error. | ||
| PreScaling, | ||
|
|
||
| /// The StatefulSet failed to reach the desired replica count. | ||
| Scaling, | ||
|
|
||
| /// The `post_scale` hook returned an error. | ||
| PostScaling, | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.