From 28ff173f56c3cb83383d0362932122937e46b4ad Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 16 Feb 2023 08:39:25 +0100 Subject: [PATCH 01/11] Initial implementation --- Cargo.toml | 4 + deploy/helm/kafka-operator/crds/crds.yaml | 958 ++++++++++++++++++++++ rust/crd/src/lib.rs | 50 +- rust/operator/Cargo.toml | 3 + rust/operator/src/kafka_controller.rs | 2 +- rust/operator/src/lib.rs | 74 ++ 6 files changed, 1074 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 98190375..94c16f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,3 +2,7 @@ members = [ "rust/crd", "rust/operator", "rust/operator-binary" ] + +[patch."https://github.com/stackabletech/operator-rs.git"] +stackable-operator = { path = "/home/sbernauer/stackabletech/operator-rs" } +# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feat/affinities" } diff --git a/deploy/helm/kafka-operator/crds/crds.yaml b/deploy/helm/kafka-operator/crds/crds.yaml index 39d26a46..1f66b4de 100644 --- a/deploy/helm/kafka-operator/crds/crds.yaml +++ b/deploy/helm/kafka-operator/crds/crds.yaml @@ -35,6 +35,485 @@ spec: config: default: {} properties: + affinity: + default: + podAffinity: null + podAntiAffinity: null + nodeAffinity: null + nodeSelector: null + properties: + nodeAffinity: + description: Node affinity is a group of node affinity scheduling rules. + nullable: true + 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 + nodeSelector: + nullable: true + type: object + podAffinity: + description: Pod affinity is a group of inter pod affinity scheduling rules. + nullable: true + 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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. + nullable: true + 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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 logging: default: enableVectorAgent: null @@ -217,6 +696,485 @@ spec: config: default: {} properties: + affinity: + default: + podAffinity: null + podAntiAffinity: null + nodeAffinity: null + nodeSelector: null + properties: + nodeAffinity: + description: Node affinity is a group of node affinity scheduling rules. + nullable: true + 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 + nodeSelector: + nullable: true + type: object + podAffinity: + description: Pod affinity is a group of inter pod affinity scheduling rules. + nullable: true + 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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. + nullable: true + 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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 + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + 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 a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector 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 logging: default: enableVectorAgent: null diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index d89aba9c..f57a2571 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -12,6 +12,9 @@ use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{ commons::{ + affinities::{ + anti_affinity_between_role_pods, StackableAffinity, StackableAffinityFragment, + }, product_image_selection::ProductImage, resources::{ CpuLimitsFragment, MemoryLimitsFragment, NoRuntimeLimits, NoRuntimeLimitsFragment, @@ -21,9 +24,10 @@ use stackable_operator::{ config::fragment::{self, Fragment, ValidationError}, config::merge::Merge, k8s_openapi::{ - api::core::v1::PersistentVolumeClaim, apimachinery::pkg::api::resource::Quantity, + api::core::v1::{PersistentVolumeClaim, PodAntiAffinity}, + apimachinery::pkg::api::resource::Quantity, }, - kube::{runtime::reflector::ObjectRef, CustomResource}, + kube::{runtime::reflector::ObjectRef, CustomResource, ResourceExt}, product_config_utils::{ConfigError, Configuration}, product_logging::{self, spec::Logging}, role_utils::{Role, RoleGroup, RoleGroupRef}, @@ -171,7 +175,8 @@ impl KafkaCluster { /// Retrieve and merge resource configs for role and role groups pub fn merged_config(&self, role: &KafkaRole, role_group: &str) -> Result { // Initialize the result with all default values as baseline - let conf_defaults = KafkaConfig::default_config(); + let conf_defaults = + KafkaConfig::default_config(APP_NAME, &self.name_any(), &role.to_string()); let role = self.spec.brokers.as_ref().context(MissingKafkaRoleSnafu { role: role.to_string(), @@ -307,10 +312,12 @@ pub struct KafkaConfig { pub logging: Logging, #[fragment_attrs(serde(default))] pub resources: Resources, + #[fragment_attrs(serde(default))] + pub affinity: StackableAffinity, } impl KafkaConfig { - pub fn default_config() -> KafkaConfigFragment { + pub fn default_config(app_name: &str, cluster_name: &str, role: &str) -> KafkaConfigFragment { KafkaConfigFragment { logging: product_logging::spec::default_logging(), resources: ResourcesFragment { @@ -330,6 +337,17 @@ impl KafkaConfig { }, }, }, + affinity: StackableAffinityFragment { + pod_affinity: None, + pod_anti_affinity: Some(PodAntiAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + anti_affinity_between_role_pods(app_name, cluster_name, role, 70), + ]), + required_during_scheduling_ignored_during_execution: None, + }), + node_affinity: None, + node_selector: None, + }, } } } @@ -414,7 +432,7 @@ mod tests { image: productVersion: 3.3.1 stackableVersion: "23.4.0-rc2" - clusterConfig: + clusterConfig: zookeeperConfigMapName: xyz "#; let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); @@ -435,9 +453,9 @@ mod tests { stackableVersion: "23.4.0-rc2" clusterConfig: tls: - serverSecretClass: simple-kafka-server-tls + serverSecretClass: simple-kafka-server-tls zookeeperConfigMapName: xyz - + "#; let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( @@ -460,8 +478,8 @@ mod tests { stackableVersion: "23.4.0-rc2" clusterConfig: tls: - serverSecretClass: null - zookeeperConfigMapName: xyz + serverSecretClass: null + zookeeperConfigMapName: xyz "#; let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); assert_eq!(get_server_secret_class(&kafka), None); @@ -482,8 +500,8 @@ mod tests { zookeeperConfigMapName: xyz clusterConfig: tls: - internalSecretClass: simple-kafka-internal-tls - zookeeperConfigMapName: xyz + internalSecretClass: simple-kafka-internal-tls + zookeeperConfigMapName: xyz "#; let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); assert_eq!(get_server_secret_class(&kafka), tls::server_tls_default()); @@ -505,7 +523,7 @@ mod tests { productVersion: 3.3.1 stackableVersion: "23.4.0-rc2" clusterConfig: - zookeeperConfigMapName: xyz + zookeeperConfigMapName: xyz "#; let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); assert_eq!(get_server_secret_class(&kafka), tls::server_tls_default()); @@ -525,8 +543,8 @@ mod tests { stackableVersion: "23.4.0-rc2" clusterConfig: tls: - internalSecretClass: simple-kafka-internal-tls - zookeeperConfigMapName: xyz + internalSecretClass: simple-kafka-internal-tls + zookeeperConfigMapName: xyz "#; let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); assert_eq!(get_server_secret_class(&kafka), tls::server_tls_default()); @@ -546,8 +564,8 @@ mod tests { stackableVersion: "23.4.0-rc2" clusterConfig: tls: - serverSecretClass: simple-kafka-server-tls - zookeeperConfigMapName: xyz + serverSecretClass: simple-kafka-server-tls + zookeeperConfigMapName: xyz "#; let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( diff --git a/rust/operator/Cargo.toml b/rust/operator/Cargo.toml index 6075a443..ee223d69 100644 --- a/rust/operator/Cargo.toml +++ b/rust/operator/Cargo.toml @@ -19,3 +19,6 @@ strum = { version = "0.24.1", features = ["derive"] } tokio = { version = "1.25.0", features = ["macros"] } tracing = "0.1.37" snafu = "0.7.4" + +[dev-dependencies] +serde_yaml = "0.9" diff --git a/rust/operator/src/kafka_controller.rs b/rust/operator/src/kafka_controller.rs index 28eae974..6d62f219 100644 --- a/rust/operator/src/kafka_controller.rs +++ b/rust/operator/src/kafka_controller.rs @@ -872,7 +872,7 @@ fn build_broker_rolegroup_statefulset( .add_init_container(cb_get_svc.build()) .add_container(cb_kafka.build()) .add_container(cb_kcat_prober.build()) - .node_selector_opt(rolegroup.selector.clone()) + .affinity(&merged_config.affinity) .add_volume(Volume { name: "config".to_string(), config_map: Some(ConfigMapVolumeSource { diff --git a/rust/operator/src/lib.rs b/rust/operator/src/lib.rs index ce46c24b..bfc5f540 100644 --- a/rust/operator/src/lib.rs +++ b/rust/operator/src/lib.rs @@ -97,3 +97,77 @@ pub async fn create_controller( .collect::<()>() .await; } + +#[cfg(test)] +mod tests { + use super::*; + + use std::collections::BTreeMap; + + use stackable_kafka_crd::KafkaRole; + use stackable_operator::{ + commons::affinities::StackableAffinity, + k8s_openapi::{ + api::core::v1::{PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm}, + apimachinery::pkg::apis::meta::v1::LabelSelector, + }, + }; + + #[test] + fn test_affinity_defaults() { + let input = r#" + apiVersion: kafka.stackable.tech/v1alpha1 + kind: KafkaCluster + metadata: + name: simple-kafka + spec: + image: + productVersion: 3.3.1 + stackableVersion: "23.4.0-rc2" + clusterConfig: + zookeeperConfigMapName: xyz + brokers: + roleGroups: + default: + replicas: 1 + "#; + let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); + let merged_config = kafka.merged_config(&KafkaRole::Broker, "default").unwrap(); + + assert_eq!( + merged_config.affinity, + StackableAffinity { + pod_affinity: None, + pod_anti_affinity: Some(PodAntiAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + WeightedPodAffinityTerm { + pod_affinity_term: PodAffinityTerm { + label_selector: Some(LabelSelector { + match_expressions: None, + match_labels: Some(BTreeMap::from([ + ("app.kubernetes.io/name".to_string(), "kafka".to_string(),), + ( + "app.kubernetes.io/instance".to_string(), + "simple-kafka".to_string(), + ), + ( + "app.kubernetes.io/component".to_string(), + "broker".to_string(), + ) + ])) + }), + namespace_selector: None, + namespaces: None, + topology_key: "kubernetes.io/hostname".to_string(), + }, + weight: 70 + } + ]), + required_during_scheduling_ignored_during_execution: None, + }), + node_affinity: None, + node_selector: None, + } + ); + } +} From d908844a6a8a5ec655e8c3380b11724d4359db1c Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 16 Feb 2023 10:17:09 +0100 Subject: [PATCH 02/11] Refactor default_config() --- rust/crd/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index f57a2571..4bdf7cce 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -175,8 +175,7 @@ impl KafkaCluster { /// Retrieve and merge resource configs for role and role groups pub fn merged_config(&self, role: &KafkaRole, role_group: &str) -> Result { // Initialize the result with all default values as baseline - let conf_defaults = - KafkaConfig::default_config(APP_NAME, &self.name_any(), &role.to_string()); + let conf_defaults = KafkaConfig::default_config(&self.name_any(), &role.to_string()); let role = self.spec.brokers.as_ref().context(MissingKafkaRoleSnafu { role: role.to_string(), @@ -317,7 +316,7 @@ pub struct KafkaConfig { } impl KafkaConfig { - pub fn default_config(app_name: &str, cluster_name: &str, role: &str) -> KafkaConfigFragment { + pub fn default_config(cluster_name: &str, role: &str) -> KafkaConfigFragment { KafkaConfigFragment { logging: product_logging::spec::default_logging(), resources: ResourcesFragment { @@ -341,7 +340,7 @@ impl KafkaConfig { pod_affinity: None, pod_anti_affinity: Some(PodAntiAffinity { preferred_during_scheduling_ignored_during_execution: Some(vec![ - anti_affinity_between_role_pods(app_name, cluster_name, role, 70), + anti_affinity_between_role_pods(APP_NAME, cluster_name, role, 70), ]), required_during_scheduling_ignored_during_execution: None, }), From 9a17730b35ef3bf28b51d468aa7f3699652a0017 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 16 Feb 2023 10:36:40 +0100 Subject: [PATCH 03/11] Add Cargo.lock --- Cargo.lock | 290 +++++++++++++++++++++++++++++------------------------ 1 file changed, 160 insertions(+), 130 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18d87605..0f1b6bea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", "getrandom", @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -118,9 +118,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cargo-lock" @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.32" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ "bitflags", "clap_derive", @@ -179,9 +179,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] @@ -256,9 +256,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" dependencies = [ "cc", "cxxbridge-flags", @@ -268,9 +268,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" dependencies = [ "cc", "codespan-reporting", @@ -283,15 +283,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" [[package]] name = "cxxbridge-macro" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ "proc-macro2", "quote", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" dependencies = [ "darling_core", "darling_macro", @@ -310,9 +310,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" dependencies = [ "fnv", "ident_case", @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" dependencies = [ "darling_core", "quote", @@ -392,9 +392,9 @@ checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encoding" @@ -523,9 +523,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -548,15 +548,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -565,15 +565,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -582,21 +582,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -642,9 +642,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -655,6 +655,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "http" version = "0.2.8" @@ -697,9 +703,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -815,24 +821,24 @@ checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -863,9 +869,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -1025,9 +1031,9 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libgit2-sys" -version = "0.14.1+1.5.0" +version = "0.14.2+1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a07fb2692bc3593bda59de45a502bb3071659f2c515e28c71e728306b038e17" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", @@ -1113,14 +1119,14 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1158,15 +1164,15 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" @@ -1323,22 +1329,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ "base64 0.13.1", ] @@ -1419,9 +1425,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -1529,16 +1535,16 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustix" -version = "0.36.6" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1651,9 +1657,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "indexmap", "itoa", @@ -1675,9 +1681,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834" +checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" dependencies = [ "indexmap", "itoa", @@ -1697,9 +1703,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -1758,7 +1764,7 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml 0.9.16", + "serde_yaml 0.9.17", "snafu", "stackable-operator", "strum", @@ -1772,6 +1778,7 @@ dependencies = [ "futures", "serde", "serde_json", + "serde_yaml 0.9.17", "snafu", "stackable-kafka-crd", "stackable-operator", @@ -1795,8 +1802,7 @@ dependencies = [ [[package]] name = "stackable-operator" -version = "0.33.0" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.33.0#600bc948f2763d070a0f3c354a2b66434cf9f953" +version = "0.35.0" dependencies = [ "chrono", "clap", @@ -1816,7 +1822,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_yaml 0.9.16", + "serde_yaml 0.9.17", "snafu", "stackable-operator-derive", "strum", @@ -1829,8 +1835,7 @@ dependencies = [ [[package]] name = "stackable-operator-derive" -version = "0.33.0" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.33.0#600bc948f2763d070a0f3c354a2b66434cf9f953" +version = "0.35.0" dependencies = [ "darling", "proc-macro2", @@ -1879,9 +1884,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -1908,10 +1913,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -1948,9 +1954,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" @@ -1966,7 +1972,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -2015,9 +2021,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -2030,9 +2036,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] @@ -2180,9 +2186,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" @@ -2264,9 +2270,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2274,9 +2280,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -2289,9 +2295,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2299,9 +2305,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -2312,9 +2318,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "winapi" @@ -2362,47 +2368,71 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "xml-rs" From 2549d128d2361678bc0379db8a0a5adbb7a89fd5 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 16 Feb 2023 11:20:13 +0100 Subject: [PATCH 04/11] Support legacy selector field --- rust/crd/src/lib.rs | 11 +++++ rust/operator/src/lib.rs | 91 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index 4bdf7cce..5dfd0f51 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -191,6 +191,17 @@ impl KafkaCluster { .map(|rg| rg.config.config.clone()) .unwrap_or_default(); + if let Some(RoleGroup { + selector: Some(selector), + .. + }) = role.role_groups.get(role_group) + { + // Migrate old `selector` attribute, see ADR 26 affinities. + // TODO Can be removed after support for the old `selector` field is dropped. + #[allow(deprecated)] + conf_rolegroup.affinity.add_legacy_selector(selector); + } + // Merge more specific configs into default config // Hierarchy is: // 1. RoleGroup diff --git a/rust/operator/src/lib.rs b/rust/operator/src/lib.rs index bfc5f540..98a0be58 100644 --- a/rust/operator/src/lib.rs +++ b/rust/operator/src/lib.rs @@ -106,9 +106,12 @@ mod tests { use stackable_kafka_crd::KafkaRole; use stackable_operator::{ - commons::affinities::StackableAffinity, + commons::affinities::{StackableAffinity, StackableNodeSelector}, k8s_openapi::{ - api::core::v1::{PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm}, + api::core::v1::{ + NodeAffinity, NodeSelector, NodeSelectorRequirement, NodeSelectorTerm, + PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm, + }, apimachinery::pkg::apis::meta::v1::LabelSelector, }, }; @@ -170,4 +173,88 @@ mod tests { } ); } + + #[test] + fn test_affinity_legacy_node_selector() { + let input = r#" + apiVersion: kafka.stackable.tech/v1alpha1 + kind: KafkaCluster + metadata: + name: simple-kafka + spec: + image: + productVersion: 3.3.1 + stackableVersion: "23.4.0-rc2" + clusterConfig: + zookeeperConfigMapName: xyz + brokers: + roleGroups: + default: + replicas: 1 + selector: + matchLabels: + disktype: ssd + matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - antarctica-east1 + - antarctica-west1 + "#; + let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); + let merged_config = kafka.merged_config(&KafkaRole::Broker, "default").unwrap(); + + assert_eq!( + merged_config.affinity, + StackableAffinity { + pod_affinity: None, + pod_anti_affinity: Some(PodAntiAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + WeightedPodAffinityTerm { + pod_affinity_term: PodAffinityTerm { + label_selector: Some(LabelSelector { + match_expressions: None, + match_labels: Some(BTreeMap::from([ + ("app.kubernetes.io/name".to_string(), "kafka".to_string(),), + ( + "app.kubernetes.io/instance".to_string(), + "simple-kafka".to_string(), + ), + ( + "app.kubernetes.io/component".to_string(), + "broker".to_string(), + ) + ])) + }), + namespace_selector: None, + namespaces: None, + topology_key: "kubernetes.io/hostname".to_string(), + }, + weight: 70 + } + ]), + required_during_scheduling_ignored_during_execution: None, + }), + node_affinity: Some(NodeAffinity { + preferred_during_scheduling_ignored_during_execution: None, + required_during_scheduling_ignored_during_execution: Some(NodeSelector { + node_selector_terms: vec![NodeSelectorTerm { + match_expressions: Some(vec![NodeSelectorRequirement { + key: "topology.kubernetes.io/zone".to_string(), + operator: "In".to_string(), + values: Some(vec![ + "antarctica-east1".to_string(), + "antarctica-west1".to_string() + ]), + }]), + match_fields: None, + }] + }), + }), + node_selector: Some(StackableNodeSelector { + node_selector: BTreeMap::from([("disktype".to_string(), "ssd".to_string())]) + }), + } + ); + } } From b244b9cb4c8c9a2f0589391326da95b995ff4298 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 16 Feb 2023 13:36:44 +0100 Subject: [PATCH 05/11] Switch to branch --- Cargo.lock | 2 ++ Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f1b6bea..fc3a2b45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1803,6 +1803,7 @@ dependencies = [ [[package]] name = "stackable-operator" version = "0.35.0" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#9f8e55ffe9e6322b8d888af81ff8ebaf898c634e" dependencies = [ "chrono", "clap", @@ -1836,6 +1837,7 @@ dependencies = [ [[package]] name = "stackable-operator-derive" version = "0.35.0" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#9f8e55ffe9e6322b8d888af81ff8ebaf898c634e" dependencies = [ "darling", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 94c16f74..55f0fa69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,5 @@ members = [ ] [patch."https://github.com/stackabletech/operator-rs.git"] -stackable-operator = { path = "/home/sbernauer/stackabletech/operator-rs" } -# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feat/affinities" } +# stackable-operator = { path = "/home/sbernauer/stackabletech/operator-rs" } +stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feat/affinities" } From cc29e04bb80c80a12c929575ea3e38e92376c808 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Thu, 16 Feb 2023 13:58:30 +0100 Subject: [PATCH 06/11] Rename affinity_between_role_pods dn --- Cargo.lock | 4 ++-- rust/crd/src/lib.rs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc3a2b45..f462a8b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1803,7 +1803,7 @@ dependencies = [ [[package]] name = "stackable-operator" version = "0.35.0" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#9f8e55ffe9e6322b8d888af81ff8ebaf898c634e" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#292e91dcfdadec617c4070d5d48ae8d40769968b" dependencies = [ "chrono", "clap", @@ -1837,7 +1837,7 @@ dependencies = [ [[package]] name = "stackable-operator-derive" version = "0.35.0" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#9f8e55ffe9e6322b8d888af81ff8ebaf898c634e" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#292e91dcfdadec617c4070d5d48ae8d40769968b" dependencies = [ "darling", "proc-macro2", diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index 5dfd0f51..3d8588df 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -12,9 +12,7 @@ use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{ commons::{ - affinities::{ - anti_affinity_between_role_pods, StackableAffinity, StackableAffinityFragment, - }, + affinities::{affinity_between_role_pods, StackableAffinity, StackableAffinityFragment}, product_image_selection::ProductImage, resources::{ CpuLimitsFragment, MemoryLimitsFragment, NoRuntimeLimits, NoRuntimeLimitsFragment, @@ -351,7 +349,7 @@ impl KafkaConfig { pod_affinity: None, pod_anti_affinity: Some(PodAntiAffinity { preferred_during_scheduling_ignored_during_execution: Some(vec![ - anti_affinity_between_role_pods(APP_NAME, cluster_name, role, 70), + affinity_between_role_pods(APP_NAME, cluster_name, role, 70), ]), required_during_scheduling_ignored_during_execution: None, }), From 3ed6f5682cf564c8e378a92874ccfe26f1a8a3e9 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Fri, 17 Feb 2023 14:33:43 +0100 Subject: [PATCH 07/11] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2425fbf..678931af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Enabled logging and log aggregation ([#547]). +- Deploy default and support custom affinities ([#557]). ### Changed @@ -16,6 +17,7 @@ All notable changes to this project will be documented in this file. [#545]: https://github.com/stackabletech/kafka-operator/pull/545 [#547]: https://github.com/stackabletech/kafka-operator/pull/547 +[#557]: https://github.com/stackabletech/kafka-operator/pull/557 ## [23.1.0] - 2023-01-23 From 37a26af4fb06ccb5849d50d55c5cd936997e1fb8 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Fri, 17 Feb 2023 14:41:24 +0100 Subject: [PATCH 08/11] Refactor out into affinity.rs --- Cargo.lock | 4 +- rust/crd/src/affinity.rs | 181 +++++++++++++++++++++++++++++++++++++++ rust/crd/src/lib.rs | 23 ++--- rust/operator/src/lib.rs | 161 ---------------------------------- 4 files changed, 190 insertions(+), 179 deletions(-) create mode 100644 rust/crd/src/affinity.rs diff --git a/Cargo.lock b/Cargo.lock index f462a8b4..e1b18117 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1803,7 +1803,7 @@ dependencies = [ [[package]] name = "stackable-operator" version = "0.35.0" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#292e91dcfdadec617c4070d5d48ae8d40769968b" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#5aab38f9cdd96b98f483ee3834a9cc74d002c0f0" dependencies = [ "chrono", "clap", @@ -1837,7 +1837,7 @@ dependencies = [ [[package]] name = "stackable-operator-derive" version = "0.35.0" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#292e91dcfdadec617c4070d5d48ae8d40769968b" +source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#5aab38f9cdd96b98f483ee3834a9cc74d002c0f0" dependencies = [ "darling", "proc-macro2", diff --git a/rust/crd/src/affinity.rs b/rust/crd/src/affinity.rs new file mode 100644 index 00000000..482f5744 --- /dev/null +++ b/rust/crd/src/affinity.rs @@ -0,0 +1,181 @@ +use stackable_operator::{ + commons::affinities::{affinity_between_role_pods, StackableAffinityFragment}, + k8s_openapi::api::core::v1::PodAntiAffinity, +}; + +use crate::{KafkaRole, APP_NAME}; + +pub fn get_affinity(cluster_name: &str, role: &KafkaRole) -> StackableAffinityFragment { + StackableAffinityFragment { + pod_affinity: None, + pod_anti_affinity: Some(PodAntiAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + affinity_between_role_pods(APP_NAME, cluster_name, &role.to_string(), 70), + ]), + required_during_scheduling_ignored_during_execution: None, + }), + node_affinity: None, + node_selector: None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::collections::BTreeMap; + + use crate::KafkaCluster; + use stackable_operator::{ + commons::affinities::{StackableAffinity, StackableNodeSelector}, + k8s_openapi::{ + api::core::v1::{ + NodeAffinity, NodeSelector, NodeSelectorRequirement, NodeSelectorTerm, + PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm, + }, + apimachinery::pkg::apis::meta::v1::LabelSelector, + }, + }; + + #[test] + fn test_affinity_defaults() { + let input = r#" + apiVersion: kafka.stackable.tech/v1alpha1 + kind: KafkaCluster + metadata: + name: simple-kafka + spec: + image: + productVersion: 3.3.1 + stackableVersion: "23.4.0-rc2" + clusterConfig: + zookeeperConfigMapName: xyz + brokers: + roleGroups: + default: + replicas: 1 + "#; + let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); + let merged_config = kafka.merged_config(&KafkaRole::Broker, "default").unwrap(); + + assert_eq!( + merged_config.affinity, + StackableAffinity { + pod_affinity: None, + pod_anti_affinity: Some(PodAntiAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + WeightedPodAffinityTerm { + pod_affinity_term: PodAffinityTerm { + label_selector: Some(LabelSelector { + match_expressions: None, + match_labels: Some(BTreeMap::from([ + ("app.kubernetes.io/name".to_string(), "kafka".to_string(),), + ( + "app.kubernetes.io/instance".to_string(), + "simple-kafka".to_string(), + ), + ( + "app.kubernetes.io/component".to_string(), + "broker".to_string(), + ) + ])) + }), + namespace_selector: None, + namespaces: None, + topology_key: "kubernetes.io/hostname".to_string(), + }, + weight: 70 + } + ]), + required_during_scheduling_ignored_during_execution: None, + }), + node_affinity: None, + node_selector: None, + } + ); + } + + #[test] + fn test_affinity_legacy_node_selector() { + let input = r#" + apiVersion: kafka.stackable.tech/v1alpha1 + kind: KafkaCluster + metadata: + name: simple-kafka + spec: + image: + productVersion: 3.3.1 + stackableVersion: "23.4.0-rc2" + clusterConfig: + zookeeperConfigMapName: xyz + brokers: + roleGroups: + default: + replicas: 1 + selector: + matchLabels: + disktype: ssd + matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - antarctica-east1 + - antarctica-west1 + "#; + let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); + let merged_config = kafka.merged_config(&KafkaRole::Broker, "default").unwrap(); + + assert_eq!( + merged_config.affinity, + StackableAffinity { + pod_affinity: None, + pod_anti_affinity: Some(PodAntiAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + WeightedPodAffinityTerm { + pod_affinity_term: PodAffinityTerm { + label_selector: Some(LabelSelector { + match_expressions: None, + match_labels: Some(BTreeMap::from([ + ("app.kubernetes.io/name".to_string(), "kafka".to_string(),), + ( + "app.kubernetes.io/instance".to_string(), + "simple-kafka".to_string(), + ), + ( + "app.kubernetes.io/component".to_string(), + "broker".to_string(), + ) + ])) + }), + namespace_selector: None, + namespaces: None, + topology_key: "kubernetes.io/hostname".to_string(), + }, + weight: 70 + } + ]), + required_during_scheduling_ignored_during_execution: None, + }), + node_affinity: Some(NodeAffinity { + preferred_during_scheduling_ignored_during_execution: None, + required_during_scheduling_ignored_during_execution: Some(NodeSelector { + node_selector_terms: vec![NodeSelectorTerm { + match_expressions: Some(vec![NodeSelectorRequirement { + key: "topology.kubernetes.io/zone".to_string(), + operator: "In".to_string(), + values: Some(vec![ + "antarctica-east1".to_string(), + "antarctica-west1".to_string() + ]), + }]), + match_fields: None, + }] + }), + }), + node_selector: Some(StackableNodeSelector { + node_selector: BTreeMap::from([("disktype".to_string(), "ssd".to_string())]) + }), + } + ); + } +} diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index 3d8588df..dada4e9c 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -1,3 +1,4 @@ +pub mod affinity; pub mod authentication; pub mod authorization; pub mod listener; @@ -8,11 +9,12 @@ use crate::authentication::KafkaAuthentication; use crate::authorization::KafkaAuthorization; use crate::tls::KafkaTls; +use affinity::get_affinity; use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{ commons::{ - affinities::{affinity_between_role_pods, StackableAffinity, StackableAffinityFragment}, + affinities::StackableAffinity, product_image_selection::ProductImage, resources::{ CpuLimitsFragment, MemoryLimitsFragment, NoRuntimeLimits, NoRuntimeLimitsFragment, @@ -22,8 +24,7 @@ use stackable_operator::{ config::fragment::{self, Fragment, ValidationError}, config::merge::Merge, k8s_openapi::{ - api::core::v1::{PersistentVolumeClaim, PodAntiAffinity}, - apimachinery::pkg::api::resource::Quantity, + api::core::v1::PersistentVolumeClaim, apimachinery::pkg::api::resource::Quantity, }, kube::{runtime::reflector::ObjectRef, CustomResource, ResourceExt}, product_config_utils::{ConfigError, Configuration}, @@ -173,7 +174,7 @@ impl KafkaCluster { /// Retrieve and merge resource configs for role and role groups pub fn merged_config(&self, role: &KafkaRole, role_group: &str) -> Result { // Initialize the result with all default values as baseline - let conf_defaults = KafkaConfig::default_config(&self.name_any(), &role.to_string()); + let conf_defaults = KafkaConfig::default_config(&self.name_any(), role); let role = self.spec.brokers.as_ref().context(MissingKafkaRoleSnafu { role: role.to_string(), @@ -325,7 +326,7 @@ pub struct KafkaConfig { } impl KafkaConfig { - pub fn default_config(cluster_name: &str, role: &str) -> KafkaConfigFragment { + pub fn default_config(cluster_name: &str, role: &KafkaRole) -> KafkaConfigFragment { KafkaConfigFragment { logging: product_logging::spec::default_logging(), resources: ResourcesFragment { @@ -345,17 +346,7 @@ impl KafkaConfig { }, }, }, - affinity: StackableAffinityFragment { - pod_affinity: None, - pod_anti_affinity: Some(PodAntiAffinity { - preferred_during_scheduling_ignored_during_execution: Some(vec![ - affinity_between_role_pods(APP_NAME, cluster_name, role, 70), - ]), - required_during_scheduling_ignored_during_execution: None, - }), - node_affinity: None, - node_selector: None, - }, + affinity: get_affinity(cluster_name, role), } } } diff --git a/rust/operator/src/lib.rs b/rust/operator/src/lib.rs index 98a0be58..ce46c24b 100644 --- a/rust/operator/src/lib.rs +++ b/rust/operator/src/lib.rs @@ -97,164 +97,3 @@ pub async fn create_controller( .collect::<()>() .await; } - -#[cfg(test)] -mod tests { - use super::*; - - use std::collections::BTreeMap; - - use stackable_kafka_crd::KafkaRole; - use stackable_operator::{ - commons::affinities::{StackableAffinity, StackableNodeSelector}, - k8s_openapi::{ - api::core::v1::{ - NodeAffinity, NodeSelector, NodeSelectorRequirement, NodeSelectorTerm, - PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm, - }, - apimachinery::pkg::apis::meta::v1::LabelSelector, - }, - }; - - #[test] - fn test_affinity_defaults() { - let input = r#" - apiVersion: kafka.stackable.tech/v1alpha1 - kind: KafkaCluster - metadata: - name: simple-kafka - spec: - image: - productVersion: 3.3.1 - stackableVersion: "23.4.0-rc2" - clusterConfig: - zookeeperConfigMapName: xyz - brokers: - roleGroups: - default: - replicas: 1 - "#; - let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); - let merged_config = kafka.merged_config(&KafkaRole::Broker, "default").unwrap(); - - assert_eq!( - merged_config.affinity, - StackableAffinity { - pod_affinity: None, - pod_anti_affinity: Some(PodAntiAffinity { - preferred_during_scheduling_ignored_during_execution: Some(vec![ - WeightedPodAffinityTerm { - pod_affinity_term: PodAffinityTerm { - label_selector: Some(LabelSelector { - match_expressions: None, - match_labels: Some(BTreeMap::from([ - ("app.kubernetes.io/name".to_string(), "kafka".to_string(),), - ( - "app.kubernetes.io/instance".to_string(), - "simple-kafka".to_string(), - ), - ( - "app.kubernetes.io/component".to_string(), - "broker".to_string(), - ) - ])) - }), - namespace_selector: None, - namespaces: None, - topology_key: "kubernetes.io/hostname".to_string(), - }, - weight: 70 - } - ]), - required_during_scheduling_ignored_during_execution: None, - }), - node_affinity: None, - node_selector: None, - } - ); - } - - #[test] - fn test_affinity_legacy_node_selector() { - let input = r#" - apiVersion: kafka.stackable.tech/v1alpha1 - kind: KafkaCluster - metadata: - name: simple-kafka - spec: - image: - productVersion: 3.3.1 - stackableVersion: "23.4.0-rc2" - clusterConfig: - zookeeperConfigMapName: xyz - brokers: - roleGroups: - default: - replicas: 1 - selector: - matchLabels: - disktype: ssd - matchExpressions: - - key: topology.kubernetes.io/zone - operator: In - values: - - antarctica-east1 - - antarctica-west1 - "#; - let kafka: KafkaCluster = serde_yaml::from_str(input).expect("illegal test input"); - let merged_config = kafka.merged_config(&KafkaRole::Broker, "default").unwrap(); - - assert_eq!( - merged_config.affinity, - StackableAffinity { - pod_affinity: None, - pod_anti_affinity: Some(PodAntiAffinity { - preferred_during_scheduling_ignored_during_execution: Some(vec![ - WeightedPodAffinityTerm { - pod_affinity_term: PodAffinityTerm { - label_selector: Some(LabelSelector { - match_expressions: None, - match_labels: Some(BTreeMap::from([ - ("app.kubernetes.io/name".to_string(), "kafka".to_string(),), - ( - "app.kubernetes.io/instance".to_string(), - "simple-kafka".to_string(), - ), - ( - "app.kubernetes.io/component".to_string(), - "broker".to_string(), - ) - ])) - }), - namespace_selector: None, - namespaces: None, - topology_key: "kubernetes.io/hostname".to_string(), - }, - weight: 70 - } - ]), - required_during_scheduling_ignored_during_execution: None, - }), - node_affinity: Some(NodeAffinity { - preferred_during_scheduling_ignored_during_execution: None, - required_during_scheduling_ignored_during_execution: Some(NodeSelector { - node_selector_terms: vec![NodeSelectorTerm { - match_expressions: Some(vec![NodeSelectorRequirement { - key: "topology.kubernetes.io/zone".to_string(), - operator: "In".to_string(), - values: Some(vec![ - "antarctica-east1".to_string(), - "antarctica-west1".to_string() - ]), - }]), - match_fields: None, - }] - }), - }), - node_selector: Some(StackableNodeSelector { - node_selector: BTreeMap::from([("disktype".to_string(), "ssd".to_string())]) - }), - } - ); - } -} From 3455e732f6572b6867c5b1cea52d1552f0a8334e Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Fri, 17 Feb 2023 16:27:26 +0100 Subject: [PATCH 09/11] Use operator-rs 0.36.0 --- Cargo.lock | 8 ++++---- Cargo.toml | 4 ++-- rust/crd/Cargo.toml | 2 +- rust/crd/src/affinity.rs | 4 ++-- rust/crd/src/lib.rs | 2 +- rust/operator-binary/Cargo.toml | 4 ++-- rust/operator/Cargo.toml | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54992be1..96cbc20e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1802,8 +1802,8 @@ dependencies = [ [[package]] name = "stackable-operator" -version = "0.35.0" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#5aab38f9cdd96b98f483ee3834a9cc74d002c0f0" +version = "0.36.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.36.0#e7796ee75dcba3904646e2b7a3ed2029b88651d6" dependencies = [ "chrono", "clap", @@ -1836,8 +1836,8 @@ dependencies = [ [[package]] name = "stackable-operator-derive" -version = "0.35.0" -source = "git+https://github.com/stackabletech//operator-rs.git?branch=feat/affinities#5aab38f9cdd96b98f483ee3834a9cc74d002c0f0" +version = "0.36.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.36.0#e7796ee75dcba3904646e2b7a3ed2029b88651d6" dependencies = [ "darling", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 55f0fa69..bc2c0c4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,6 @@ members = [ "rust/crd", "rust/operator", "rust/operator-binary" ] -[patch."https://github.com/stackabletech/operator-rs.git"] +# [patch."https://github.com/stackabletech/operator-rs.git"] # stackable-operator = { path = "/home/sbernauer/stackabletech/operator-rs" } -stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feat/affinities" } +# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feat/affinities" } diff --git a/rust/crd/Cargo.toml b/rust/crd/Cargo.toml index 6ab7002a..d7d62c33 100644 --- a/rust/crd/Cargo.toml +++ b/rust/crd/Cargo.toml @@ -9,7 +9,7 @@ version = "0.0.0-dev" publish = false [dependencies] -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.33.0" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.36.0" } semver = "1.0.16" serde = { version = "1.0.152", features = ["derive"] } diff --git a/rust/crd/src/affinity.rs b/rust/crd/src/affinity.rs index 482f5744..af497a51 100644 --- a/rust/crd/src/affinity.rs +++ b/rust/crd/src/affinity.rs @@ -1,5 +1,5 @@ use stackable_operator::{ - commons::affinities::{affinity_between_role_pods, StackableAffinityFragment}, + commons::affinity::{affinity_between_role_pods, StackableAffinityFragment}, k8s_openapi::api::core::v1::PodAntiAffinity, }; @@ -27,7 +27,7 @@ mod tests { use crate::KafkaCluster; use stackable_operator::{ - commons::affinities::{StackableAffinity, StackableNodeSelector}, + commons::affinity::{StackableAffinity, StackableNodeSelector}, k8s_openapi::{ api::core::v1::{ NodeAffinity, NodeSelector, NodeSelectorRequirement, NodeSelectorTerm, diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index dada4e9c..7c599d45 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{ commons::{ - affinities::StackableAffinity, + affinity::StackableAffinity, product_image_selection::ProductImage, resources::{ CpuLimitsFragment, MemoryLimitsFragment, NoRuntimeLimits, NoRuntimeLimitsFragment, diff --git a/rust/operator-binary/Cargo.toml b/rust/operator-binary/Cargo.toml index 086cf9c1..58a8364d 100644 --- a/rust/operator-binary/Cargo.toml +++ b/rust/operator-binary/Cargo.toml @@ -12,7 +12,7 @@ publish = false [dependencies] stackable-kafka-crd = { path = "../crd" } stackable-kafka-operator = { path = "../operator" } -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.33.0" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.36.0" } clap = "4.0.26" tokio = { version = "1.25.0", features = ["macros", "rt-multi-thread"] } @@ -20,7 +20,7 @@ tracing = "0.1.37" [build-dependencies] built = { version = "0.5.2", features = ["chrono", "git2"] } -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.33.0" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.36.0" } stackable-kafka-crd = { path = "../crd" } [[bin]] diff --git a/rust/operator/Cargo.toml b/rust/operator/Cargo.toml index 51240021..a9db6a44 100644 --- a/rust/operator/Cargo.toml +++ b/rust/operator/Cargo.toml @@ -10,7 +10,7 @@ publish = false [dependencies] stackable-kafka-crd = { path = "../crd" } -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.33.0" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.36.0" } futures = "0.3.25" serde = { version = "1.0.152", features = ["derive"] } From 3d0fc11bbae8f91af572c2cf78dbb0bc279231b6 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:50:15 +0100 Subject: [PATCH 10/11] Added docs for pod placement. --- docs/modules/kafka/pages/usage.adoc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/modules/kafka/pages/usage.adoc b/docs/modules/kafka/pages/usage.adoc index 9172b820..c819b794 100644 --- a/docs/modules/kafka/pages/usage.adoc +++ b/docs/modules/kafka/pages/usage.adoc @@ -323,3 +323,26 @@ brokers: log_dirs: capacity: 1Gi ---- + +=== Pod Placement + +You can configure Pod placement for Kafka brokers as described in xref:home:concepts:pod_placement.adoc[]. + +By default, the operator configures the following Pod placement constraints: + +[source,yaml] +---- +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/component: broker + app.kubernetes.io/instance: cluster-name + app.kubernetes.io/name: kafka + topologyKey: kubernetes.io/hostname + weight: 70 +---- + +In the example above `cluster-name` is the name of the Kafka custom resource that owns this Pod. From fbf97ead7a2409067b6eb681e41f21e54d354348 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:00:54 +0100 Subject: [PATCH 11/11] Update xref to remove the redundant "home" component. --- docs/modules/kafka/pages/usage.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/kafka/pages/usage.adoc b/docs/modules/kafka/pages/usage.adoc index c819b794..a1e33987 100644 --- a/docs/modules/kafka/pages/usage.adoc +++ b/docs/modules/kafka/pages/usage.adoc @@ -326,7 +326,7 @@ brokers: === Pod Placement -You can configure Pod placement for Kafka brokers as described in xref:home:concepts:pod_placement.adoc[]. +You can configure Pod placement for Kafka brokers as described in xref:concepts:pod_placement.adoc[]. By default, the operator configures the following Pod placement constraints: