diff --git a/deploy/crds/app.stacks_runtimecomponents_crd.yaml b/deploy/crds/app.stacks_runtimecomponents_crd.yaml index 88191f9fb..84afd463e 100644 --- a/deploy/crds/app.stacks_runtimecomponents_crd.yaml +++ b/deploy/crds/app.stacks_runtimecomponents_crd.yaml @@ -66,6 +66,586 @@ spec: spec: description: RuntimeComponentSpec defines the desired state of RuntimeComponent properties: + affinity: + description: RuntimeComponentAffinity deployment affinity settings + properties: + architecture: + items: + type: string + type: array + nodeAffinity: + description: Node affinity is a group of node affinity scheduling + rules. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + nodeAffinityLabels: + additionalProperties: + type: string + type: object + podAffinity: + description: Pod affinity is a group of inter pod affinity scheduling + rules. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Pod anti affinity is a group of inter pod anti affinity + scheduling rules. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object applicationImage: type: string applicationName: diff --git a/pkg/apis/appstacks/v1beta1/runtimecomponent_types.go b/pkg/apis/appstacks/v1beta1/runtimecomponent_types.go index 6ce8238b9..bae2c6aa5 100644 --- a/pkg/apis/appstacks/v1beta1/runtimecomponent_types.go +++ b/pkg/apis/appstacks/v1beta1/runtimecomponent_types.go @@ -53,6 +53,18 @@ type RuntimeComponentSpec struct { SidecarContainers []corev1.Container `json:"sidecarContainers,omitempty"` Route *RuntimeComponentRoute `json:"route,omitempty"` Bindings *RuntimeComponentBindings `json:"bindings,omitempty"` + Affinity *RuntimeComponentAffinity `json:"affinity,omitempty"` +} + +// RuntimeComponentAffinity deployment affinity settings +// +k8s:openapi-gen=true +type RuntimeComponentAffinity struct { + NodeAffinity *corev1.NodeAffinity `json:"nodeAffinity,omitempty"` + PodAffinity *corev1.PodAffinity `json:"podAffinity,omitempty"` + PodAntiAffinity *corev1.PodAntiAffinity `json:"podAntiAffinity,omitempty"` + // +listType=set + Architecture []string `json:"architecture,omitempty"` + NodeAffinityLabels map[string]string `json:"nodeAffinityLabels,omitempty"` } // RuntimeComponentAutoScaling ... @@ -379,6 +391,14 @@ func (cr *RuntimeComponent) GetBindings() common.BaseComponentBindings { return cr.Spec.Bindings } +// GetAffinity returns deployment's node and pod affinity settings +func (cr *RuntimeComponent) GetAffinity() common.BaseComponentAffinity { + if cr.Spec.Affinity == nil { + return nil + } + return cr.Spec.Affinity +} + // GetResolvedBindings returns a map of all the service names to be consumed by the application func (s *RuntimeComponentStatus) GetResolvedBindings() []string { return s.ResolvedBindings @@ -630,6 +650,31 @@ func (r *RuntimeComponentBindings) GetEmbedded() *runtime.RawExtension { return r.Embedded } +// GetNodeAffinity returns node affinity +func (a *RuntimeComponentAffinity) GetNodeAffinity() *corev1.NodeAffinity { + return a.NodeAffinity +} + +// GetPodAffinity returns pod affinity +func (a *RuntimeComponentAffinity) GetPodAffinity() *corev1.PodAffinity { + return a.PodAffinity +} + +// GetPodAntiAffinity returns pod anti-affinity +func (a *RuntimeComponentAffinity) GetPodAntiAffinity() *corev1.PodAntiAffinity { + return a.PodAntiAffinity +} + +// GetArchitecture returns list of architecture names +func (a *RuntimeComponentAffinity) GetArchitecture() []string { + return a.Architecture +} + +// GetNodeAffinityLabels returns list of architecture names +func (a *RuntimeComponentAffinity) GetNodeAffinityLabels() map[string]string { + return a.NodeAffinityLabels +} + // Initialize the RuntimeComponent instance func (cr *RuntimeComponent) Initialize() { if cr.Spec.PullPolicy == nil { diff --git a/pkg/apis/appstacks/v1beta1/zz_generated.deepcopy.go b/pkg/apis/appstacks/v1beta1/zz_generated.deepcopy.go index 7abf453c0..7aa394b7c 100644 --- a/pkg/apis/appstacks/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/appstacks/v1beta1/zz_generated.deepcopy.go @@ -94,6 +94,49 @@ func (in *RuntimeComponent) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RuntimeComponentAffinity) DeepCopyInto(out *RuntimeComponentAffinity) { + *out = *in + if in.NodeAffinity != nil { + in, out := &in.NodeAffinity, &out.NodeAffinity + *out = new(corev1.NodeAffinity) + (*in).DeepCopyInto(*out) + } + if in.PodAffinity != nil { + in, out := &in.PodAffinity, &out.PodAffinity + *out = new(corev1.PodAffinity) + (*in).DeepCopyInto(*out) + } + if in.PodAntiAffinity != nil { + in, out := &in.PodAntiAffinity, &out.PodAntiAffinity + *out = new(corev1.PodAntiAffinity) + (*in).DeepCopyInto(*out) + } + if in.Architecture != nil { + in, out := &in.Architecture, &out.Architecture + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NodeAffinityLabels != nil { + in, out := &in.NodeAffinityLabels, &out.NodeAffinityLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeComponentAffinity. +func (in *RuntimeComponentAffinity) DeepCopy() *RuntimeComponentAffinity { + if in == nil { + return nil + } + out := new(RuntimeComponentAffinity) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RuntimeComponentAutoScaling) DeepCopyInto(out *RuntimeComponentAutoScaling) { *out = *in @@ -445,6 +488,11 @@ func (in *RuntimeComponentSpec) DeepCopyInto(out *RuntimeComponentSpec) { *out = new(RuntimeComponentBindings) (*in).DeepCopyInto(*out) } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(RuntimeComponentAffinity) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/appstacks/v1beta1/zz_generated.openapi.go b/pkg/apis/appstacks/v1beta1/zz_generated.openapi.go index 6a88a6f60..6254e305e 100644 --- a/pkg/apis/appstacks/v1beta1/zz_generated.openapi.go +++ b/pkg/apis/appstacks/v1beta1/zz_generated.openapi.go @@ -11,15 +11,16 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponent": schema_pkg_apis_appstacks_v1beta1_RuntimeComponent(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentAutoScaling": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentAutoScaling(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentRoute": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentRoute(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentService": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentService(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentSpec": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentStatus": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentStatus(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingConsumes": schema_pkg_apis_appstacks_v1beta1_ServiceBindingConsumes(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingProvides": schema_pkg_apis_appstacks_v1beta1_ServiceBindingProvides(ref), - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.StatusCondition": schema_pkg_apis_appstacks_v1beta1_StatusCondition(ref), + "./pkg/apis/appstacks/v1beta1.RuntimeComponent": schema_pkg_apis_appstacks_v1beta1_RuntimeComponent(ref), + "./pkg/apis/appstacks/v1beta1.RuntimeComponentAffinity": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentAffinity(ref), + "./pkg/apis/appstacks/v1beta1.RuntimeComponentAutoScaling": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentAutoScaling(ref), + "./pkg/apis/appstacks/v1beta1.RuntimeComponentRoute": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentRoute(ref), + "./pkg/apis/appstacks/v1beta1.RuntimeComponentService": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentService(ref), + "./pkg/apis/appstacks/v1beta1.RuntimeComponentSpec": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref), + "./pkg/apis/appstacks/v1beta1.RuntimeComponentStatus": schema_pkg_apis_appstacks_v1beta1_RuntimeComponentStatus(ref), + "./pkg/apis/appstacks/v1beta1.ServiceBindingConsumes": schema_pkg_apis_appstacks_v1beta1_ServiceBindingConsumes(ref), + "./pkg/apis/appstacks/v1beta1.ServiceBindingProvides": schema_pkg_apis_appstacks_v1beta1_ServiceBindingProvides(ref), + "./pkg/apis/appstacks/v1beta1.StatusCondition": schema_pkg_apis_appstacks_v1beta1_StatusCondition(ref), } } @@ -51,19 +52,81 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponent(ref common.ReferenceCall }, "spec": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentSpec"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentSpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentStatus"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentStatus"), }, }, }, }, }, Dependencies: []string{ - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentSpec", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "./pkg/apis/appstacks/v1beta1.RuntimeComponentSpec", "./pkg/apis/appstacks/v1beta1.RuntimeComponentStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RuntimeComponentAffinity deployment affinity settings", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeAffinity": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeAffinity"), + }, + }, + "podAffinity": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodAffinity"), + }, + }, + "podAntiAffinity": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodAntiAffinity"), + }, + }, + "architecture": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "nodeAffinityLabels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeAffinity", "k8s.io/api/core/v1.PodAffinity", "k8s.io/api/core/v1.PodAntiAffinity"}, } } @@ -133,7 +196,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentRoute(ref common.Referenc }, "certificate": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.Certificate"), + Ref: ref("./pkg/apis/appstacks/v1beta1.Certificate"), }, }, "certificateSecretRef": { @@ -158,7 +221,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentRoute(ref common.Referenc }, }, Dependencies: []string{ - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.Certificate"}, + "./pkg/apis/appstacks/v1beta1.Certificate"}, } } @@ -236,7 +299,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentService(ref common.Refere Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingConsumes"), + Ref: ref("./pkg/apis/appstacks/v1beta1.ServiceBindingConsumes"), }, }, }, @@ -244,12 +307,12 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentService(ref common.Refere }, "provides": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingProvides"), + Ref: ref("./pkg/apis/appstacks/v1beta1.ServiceBindingProvides"), }, }, "certificate": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.Certificate"), + Ref: ref("./pkg/apis/appstacks/v1beta1.Certificate"), }, }, "certificateSecretRef": { @@ -262,7 +325,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentService(ref common.Refere }, }, Dependencies: []string{ - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.Certificate", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingConsumes", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingProvides", "k8s.io/api/core/v1.ServicePort"}, + "./pkg/apis/appstacks/v1beta1.Certificate", "./pkg/apis/appstacks/v1beta1.ServiceBindingConsumes", "./pkg/apis/appstacks/v1beta1.ServiceBindingProvides", "k8s.io/api/core/v1.ServicePort"}, } } @@ -293,7 +356,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref common.Reference }, "autoscaling": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentAutoScaling"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentAutoScaling"), }, }, "pullPolicy": { @@ -360,7 +423,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref common.Reference }, "service": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentService"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentService"), }, }, "expose": { @@ -430,7 +493,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref common.Reference }, "storage": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentStorage"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentStorage"), }, }, "createKnativeService": { @@ -441,7 +504,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref common.Reference }, "monitoring": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentMonitoring"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentMonitoring"), }, }, "createAppDefinition": { @@ -494,12 +557,17 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref common.Reference }, "route": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentRoute"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentRoute"), }, }, "bindings": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentBindings"), + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentBindings"), + }, + }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Ref: ref("./pkg/apis/appstacks/v1beta1.RuntimeComponentAffinity"), }, }, }, @@ -507,7 +575,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentSpec(ref common.Reference }, }, Dependencies: []string{ - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentAutoScaling", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentBindings", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentMonitoring", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentRoute", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentService", "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.RuntimeComponentStorage", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, + "./pkg/apis/appstacks/v1beta1.RuntimeComponentAffinity", "./pkg/apis/appstacks/v1beta1.RuntimeComponentAutoScaling", "./pkg/apis/appstacks/v1beta1.RuntimeComponentBindings", "./pkg/apis/appstacks/v1beta1.RuntimeComponentMonitoring", "./pkg/apis/appstacks/v1beta1.RuntimeComponentRoute", "./pkg/apis/appstacks/v1beta1.RuntimeComponentService", "./pkg/apis/appstacks/v1beta1.RuntimeComponentStorage", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } @@ -529,7 +597,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentStatus(ref common.Referen Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.StatusCondition"), + Ref: ref("./pkg/apis/appstacks/v1beta1.StatusCondition"), }, }, }, @@ -584,7 +652,7 @@ func schema_pkg_apis_appstacks_v1beta1_RuntimeComponentStatus(ref common.Referen }, }, Dependencies: []string{ - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.StatusCondition"}, + "./pkg/apis/appstacks/v1beta1.StatusCondition"}, } } @@ -653,7 +721,7 @@ func schema_pkg_apis_appstacks_v1beta1_ServiceBindingProvides(ref common.Referen }, "auth": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingAuth"), + Ref: ref("./pkg/apis/appstacks/v1beta1.ServiceBindingAuth"), }, }, }, @@ -661,7 +729,7 @@ func schema_pkg_apis_appstacks_v1beta1_ServiceBindingProvides(ref common.Referen }, }, Dependencies: []string{ - "github.com/application-stacks/runtime-component-operator/pkg/apis/appstacks/v1beta1.ServiceBindingAuth"}, + "./pkg/apis/appstacks/v1beta1.ServiceBindingAuth"}, } } diff --git a/pkg/common/types.go b/pkg/common/types.go index cae12d190..5e2ab4fdb 100644 --- a/pkg/common/types.go +++ b/pkg/common/types.go @@ -141,6 +141,15 @@ const ( ServiceBindingCategoryOpenAPI ServiceBindingCategory = "openapi" ) +// BaseComponentAffinity describes deployment and pod affinity +type BaseComponentAffinity interface { + GetNodeAffinity() *corev1.NodeAffinity + GetPodAffinity() *corev1.PodAffinity + GetPodAntiAffinity() *corev1.PodAntiAffinity + GetArchitecture() []string + GetNodeAffinityLabels() map[string]string +} + // BaseComponent represents basic kubernetes application type BaseComponent interface { GetApplicationImage() string @@ -173,6 +182,7 @@ type BaseComponent interface { GetGroupName() string GetRoute() BaseComponentRoute GetBindings() BaseComponentBindings + GetAffinity() BaseComponentAffinity } // Certificate returns cert-manager CertificateSpec diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index f1938e182..4ac1a800f 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "sort" "strconv" "strings" @@ -268,6 +269,104 @@ func CustomizeServiceBindingSecret(secret *corev1.Secret, auth map[string]string secret.Data = secretdata } +// CustomizeAffinity ... +func CustomizeAffinity(affinity *corev1.Affinity, ba common.BaseComponent) { + + archs := ba.GetArchitecture() + + if ba.GetAffinity() != nil { + affinity.NodeAffinity = ba.GetAffinity().GetNodeAffinity() + affinity.PodAffinity = ba.GetAffinity().GetPodAffinity() + affinity.PodAntiAffinity = ba.GetAffinity().GetPodAntiAffinity() + + if len(archs) == 0 { + archs = ba.GetAffinity().GetArchitecture() + } + + if len(ba.GetAffinity().GetNodeAffinityLabels()) > 0 { + if affinity.NodeAffinity == nil { + affinity.NodeAffinity = &corev1.NodeAffinity{} + } + if affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil { + affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{} + } + nodeSelector := affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution + + if len(nodeSelector.NodeSelectorTerms) == 0 { + nodeSelector.NodeSelectorTerms = append(nodeSelector.NodeSelectorTerms, corev1.NodeSelectorTerm{}) + } + labels := ba.GetAffinity().GetNodeAffinityLabels() + + keys := make([]string, 0, len(labels)) + for k := range labels { + keys = append(keys, k) + } + sort.Strings(keys) + + for i := range nodeSelector.NodeSelectorTerms { + + for _, key := range keys { + values := strings.Split(labels[key], ",") + for i := range values { + values[i] = strings.TrimSpace(values[i]) + } + + requirement := corev1.NodeSelectorRequirement{ + Key: key, + Operator: corev1.NodeSelectorOpIn, + Values: values, + } + + nodeSelector.NodeSelectorTerms[i].MatchExpressions = append(nodeSelector.NodeSelectorTerms[i].MatchExpressions, requirement) + } + + } + } + } + + if len(archs) > 0 { + if affinity.NodeAffinity == nil { + affinity.NodeAffinity = &corev1.NodeAffinity{} + } + if affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil { + affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{} + } + + nodeSelector := affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution + + if len(nodeSelector.NodeSelectorTerms) == 0 { + nodeSelector.NodeSelectorTerms = append(nodeSelector.NodeSelectorTerms, corev1.NodeSelectorTerm{}) + } + + for i := range nodeSelector.NodeSelectorTerms { + nodeSelector.NodeSelectorTerms[i].MatchExpressions = append(nodeSelector.NodeSelectorTerms[i].MatchExpressions, + corev1.NodeSelectorRequirement{ + Key: "kubernetes.io/arch", + Operator: corev1.NodeSelectorOpIn, + Values: archs, + }, + ) + } + + for i := range archs { + term := corev1.PreferredSchedulingTerm{ + Weight: int32(len(archs)) - int32(i), + Preference: corev1.NodeSelectorTerm{ + MatchExpressions: []corev1.NodeSelectorRequirement{ + { + Key: "kubernetes.io/arch", + Operator: corev1.NodeSelectorOpIn, + Values: []string{archs[i]}, + }, + }, + }, + } + affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append(affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution, term) + } + } + +} + // CustomizePodSpec ... func CustomizePodSpec(pts *corev1.PodTemplateSpec, ba common.BaseComponent) { obj := ba.(metav1.Object) @@ -351,9 +450,11 @@ func CustomizePodSpec(pts *corev1.PodTemplateSpec, ba common.BaseComponent) { pts.Spec.RestartPolicy = corev1.RestartPolicyAlways pts.Spec.DNSPolicy = corev1.DNSClusterFirst - if len(ba.GetArchitecture()) > 0 { + if len(ba.GetArchitecture()) > 0 || ba.GetAffinity() != nil { pts.Spec.Affinity = &corev1.Affinity{} CustomizeAffinity(pts.Spec.Affinity, ba) + } else { + pts.Spec.Affinity = nil } } @@ -497,43 +598,6 @@ func CustomizeServiceAccount(sa *corev1.ServiceAccount, ba common.BaseComponent) } } -// CustomizeAffinity ... -func CustomizeAffinity(a *corev1.Affinity, ba common.BaseComponent) { - a.NodeAffinity = &corev1.NodeAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ - NodeSelectorTerms: []corev1.NodeSelectorTerm{ - { - MatchExpressions: []corev1.NodeSelectorRequirement{ - { - Operator: corev1.NodeSelectorOpIn, - Values: ba.GetArchitecture(), - Key: "beta.kubernetes.io/arch", - }, - }, - }, - }, - }, - } - - archs := len(ba.GetArchitecture()) - for i := range ba.GetArchitecture() { - arch := ba.GetArchitecture()[i] - term := corev1.PreferredSchedulingTerm{ - Weight: int32(archs - i), - Preference: corev1.NodeSelectorTerm{ - MatchExpressions: []corev1.NodeSelectorRequirement{ - { - Operator: corev1.NodeSelectorOpIn, - Values: []string{arch}, - Key: "beta.kubernetes.io/arch", - }, - }, - }, - } - a.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append(a.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution, term) - } -} - // CustomizeKnativeService ... func CustomizeKnativeService(ksvc *servingv1alpha1.Service, ba common.BaseComponent) { obj := ba.(metav1.Object) @@ -726,6 +790,13 @@ func CustomizeServiceMonitor(sm *prometheusv1.ServiceMonitor, ba common.BaseComp if endpoints[0].BearerTokenFile != "" { sm.Spec.Endpoints[0].BearerTokenFile = endpoints[0].BearerTokenFile } + sm.Spec.Endpoints[0].BearerTokenSecret = endpoints[0].BearerTokenSecret + sm.Spec.Endpoints[0].ProxyURL = endpoints[0].ProxyURL + sm.Spec.Endpoints[0].RelabelConfigs = endpoints[0].RelabelConfigs + sm.Spec.Endpoints[0].MetricRelabelConfigs = endpoints[0].MetricRelabelConfigs + sm.Spec.Endpoints[0].HonorTimestamps = endpoints[0].HonorTimestamps + sm.Spec.Endpoints[0].HonorLabels = endpoints[0].HonorLabels + } }