diff --git a/api/v1alpha1/toolchainconfig_types.go b/api/v1alpha1/toolchainconfig_types.go index 5a485e3d..1b6c8966 100644 --- a/api/v1alpha1/toolchainconfig_types.go +++ b/api/v1alpha1/toolchainconfig_types.go @@ -459,6 +459,14 @@ type TiersConfig struct { // +optional DefaultSpaceTier *string `json:"defaultSpaceTier,omitempty"` + // FeatureToggles specifies the list of feature toggles/flags + // +optional + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + FeatureToggles []FeatureToggle `json:"featureToggles,omitempty" patchStrategy:"merge" patchMergeKey:"name"` + // DurationBeforeChangeTierRequestDeletion specifies the duration before a ChangeTierRequest resource is deleted // +optional DurationBeforeChangeTierRequestDeletion *string `json:"durationBeforeChangeTierRequestDeletion,omitempty"` @@ -469,6 +477,36 @@ type TiersConfig struct { TemplateUpdateRequestMaxPoolSize *int `json:"templateUpdateRequestMaxPoolSize,omitempty"` } +// FeatureToggle defines a feature toggle/flag. Each feature is supposed to have a unique name. +// Features are represented by kube object manifests in space and user templates. +// Such manifests must have an annotation which refers to the corresponding feature name. +// For example a manifest for a RoleBinding object in a space tier template with the following annotation: +// "toolchain.dev.openshift.com/feature: os-lightspeed" would refer to a feature with "os-lightspeed" name. +// When that template is applied for a new space then that RoleBinding object would be applied conditionally, +// according to its weight. +// +k8s:openapi-gen=true +type FeatureToggle struct { + // A unique name of the feature + Name string `json:"name"` + // Rollout weight of the feature. An integer between 0-100. + // Any number lower than 0 will be treated as 0. Any number higher than 100 will be treated as 100. + // If not set then 100 is used by default. + // 0 means the corresponding feature should not be enabled at all, which means + // that corresponding template objects should not be applied at all. + // 100 means the feature should be always enabled (the template is always applied). + // The features are weighted independently of each other. + // For example if there are two features: + // - feature1, weight=5 + // - feature2, weight=90 + // And tiers (one or many) contain the following object manifests: + // - RoleBinding with "toolchain.dev.openshift.com/feature: feature1" annotation + // - ConfigMap with "toolchain.dev.openshift.com/feature: feature2" annotation + // Then the RoleBinding will be created for the corresponding tiers with probability of 0.05 (around 5 out of every 100 spaces would have it) + // And the ConfigMap will be created with probability of 0.9 (around 90 out of every 100 spaces would have it) + // +optional + Weight *int `json:"weight,omitempty"` +} + // UsersConfig contains all configuration parameters related to users // +k8s:openapi-gen=true type UsersConfig struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9122cc80..b17fcb96 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -446,6 +446,26 @@ func (in *DevSpaces) DeepCopy() *DevSpaces { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FeatureToggle) DeepCopyInto(out *FeatureToggle) { + *out = *in + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureToggle. +func (in *FeatureToggle) DeepCopy() *FeatureToggle { + if in == nil { + return nil + } + out := new(FeatureToggle) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubSecret) DeepCopyInto(out *GitHubSecret) { *out = *in @@ -2997,6 +3017,13 @@ func (in *TiersConfig) DeepCopyInto(out *TiersConfig) { *out = new(string) **out = **in } + if in.FeatureToggles != nil { + in, out := &in.FeatureToggles, &out.FeatureToggles + *out = make([]FeatureToggle, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.DurationBeforeChangeTierRequestDeletion != nil { in, out := &in.DurationBeforeChangeTierRequestDeletion, &out.DurationBeforeChangeTierRequestDeletion *out = new(string) diff --git a/api/v1alpha1/zz_generated.openapi.go b/api/v1alpha1/zz_generated.openapi.go index 56fcfceb..51ccf1fb 100644 --- a/api/v1alpha1/zz_generated.openapi.go +++ b/api/v1alpha1/zz_generated.openapi.go @@ -41,6 +41,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/codeready-toolchain/api/api/v1alpha1.CheStatus": schema_codeready_toolchain_api_api_v1alpha1_CheStatus(ref), "github.com/codeready-toolchain/api/api/v1alpha1.ConsoleConfig": schema_codeready_toolchain_api_api_v1alpha1_ConsoleConfig(ref), "github.com/codeready-toolchain/api/api/v1alpha1.DeactivationConfig": schema_codeready_toolchain_api_api_v1alpha1_DeactivationConfig(ref), + "github.com/codeready-toolchain/api/api/v1alpha1.FeatureToggle": schema_codeready_toolchain_api_api_v1alpha1_FeatureToggle(ref), "github.com/codeready-toolchain/api/api/v1alpha1.GitHubSecret": schema_codeready_toolchain_api_api_v1alpha1_GitHubSecret(ref), "github.com/codeready-toolchain/api/api/v1alpha1.HostConfig": schema_codeready_toolchain_api_api_v1alpha1_HostConfig(ref), "github.com/codeready-toolchain/api/api/v1alpha1.HostOperatorStatus": schema_codeready_toolchain_api_api_v1alpha1_HostOperatorStatus(ref), @@ -627,6 +628,35 @@ func schema_codeready_toolchain_api_api_v1alpha1_DeactivationConfig(ref common.R } } +func schema_codeready_toolchain_api_api_v1alpha1_FeatureToggle(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FeatureToggle defines a feature toggle/flag. Each feature is supposed to have a unique name. Features are represented by kube object manifests in space and user templates. Such manifests must have an annotation which refers to the corresponding feature name. For example a manifest for a RoleBinding object in a space tier template with the following annotation: \"toolchain.dev.openshift.com/feature: os-lightspeed\" would refer to a feature with \"os-lightspeed\" name. When that template is applied for a new space then that RoleBinding object would be applied conditionally, according to its weight.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "A unique name of the feature", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "weight": { + SchemaProps: spec.SchemaProps{ + Description: "Rollout weight of the feature. An integer between 0-100. Any number lower than 0 will be treated as 0. Any number higher than 100 will be treated as 100. If not set then 100 is used by default. 0 means the corresponding feature should not be enabled at all, which means that corresponding template objects should not be applied at all. 100 means the feature should be always enabled (the template is always applied). The features are weighted independently of each other. For example if there are two features: - feature1, weight=5 - feature2, weight=90 And tiers (one or many) contain the following object manifests: - RoleBinding with \"toolchain.dev.openshift.com/feature: feature1\" annotation - ConfigMap with \"toolchain.dev.openshift.com/feature: feature2\" annotation Then the RoleBinding will be created for the corresponding tiers with probability of 0.05 (around 5 out of every 100 spaces would have it) And the ConfigMap will be created with probability of 0.9 (around 90 out of every 100 spaces would have it)", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + func schema_codeready_toolchain_api_api_v1alpha1_GitHubSecret(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4234,6 +4264,30 @@ func schema_codeready_toolchain_api_api_v1alpha1_TiersConfig(ref common.Referenc Format: "", }, }, + "featureToggles": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "FeatureToggles specifies the list of feature toggles/flags", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/codeready-toolchain/api/api/v1alpha1.FeatureToggle"), + }, + }, + }, + }, + }, "durationBeforeChangeTierRequestDeletion": { SchemaProps: spec.SchemaProps{ Description: "DurationBeforeChangeTierRequestDeletion specifies the duration before a ChangeTierRequest resource is deleted", @@ -4251,6 +4305,8 @@ func schema_codeready_toolchain_api_api_v1alpha1_TiersConfig(ref common.Referenc }, }, }, + Dependencies: []string{ + "github.com/codeready-toolchain/api/api/v1alpha1.FeatureToggle"}, } } @@ -5147,7 +5203,7 @@ func schema_codeready_toolchain_api_api_v1alpha1_WebhookConfig(ref common.Refere Properties: map[string]spec.Schema{ "deploy": { SchemaProps: spec.SchemaProps{ - Description: "Defines the flag that determines whether to deploy the Webhook. If the deploy flag is disabled, the Webhook is deployed and deleted immediately after by the memberoperatorconfig controller.", + Description: "Defines the flag that determines whether to deploy the Webhook. If the deploy flag is set to False and the Webhook was deployed previously it will be deleted by the memberoperatorconfig controller.", Type: []string{"boolean"}, Format: "", },