From 6b52326c13a054697c5cad52182fd8a392f051d9 Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 13:57:35 -0400 Subject: [PATCH 1/8] rbac Signed-off-by: Jet Chiang --- config/rbac/role.yaml | 1 + helm/kmcp-crds/templates/mcpserver-crd.yaml | 3350 ++++++++++++++++- helm/kmcp/templates/_helpers.tpl | 4 + helm/kmcp/templates/rbac/clusterrole.yaml | 37 +- .../templates/rbac/clusterrolebinding.yaml | 22 + .../__snapshot__/deployment_test.yaml.snap | 48 +- .../__snapshot__/integration_test.yaml.snap | 30 +- .../tests/__snapshot__/rbac_test.yaml.snap | 256 +- .../tests/__snapshot__/service_test.yaml.snap | 8 +- helm/kmcp/tests/deployment_test.yaml | 44 +- helm/kmcp/tests/rbac_test.yaml | 55 +- helm/kmcp/values.yaml | 8 + pkg/app/app.go | 48 +- pkg/app/app_test.go | 106 + pkg/controller/mcpserver_controller.go | 1 + 15 files changed, 3903 insertions(+), 115 deletions(-) create mode 100644 pkg/app/app_test.go diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 227bdf5..4bdc672 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -8,6 +8,7 @@ rules: - "" resources: - configmaps + - serviceaccounts - services verbs: - create diff --git a/helm/kmcp-crds/templates/mcpserver-crd.yaml b/helm/kmcp-crds/templates/mcpserver-crd.yaml index c488197..de1da0a 100644 --- a/helm/kmcp-crds/templates/mcpserver-crd.yaml +++ b/helm/kmcp-crds/templates/mcpserver-crd.yaml @@ -52,6 +52,943 @@ spec: description: Configuration to Deploy the MCP Server using a docker container properties: + affinity: + description: |- + Affinity defines the affinity rules for the pod. + Use this to control pod placement based on node labels, pod labels, + or other scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + 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 + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + 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. + If it's null, this PodAffinityTerm matches with no 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + 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 + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + 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. + If it's null, this PodAffinityTerm matches with no 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + 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 + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + 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. + If it's null, this PodAffinityTerm matches with no 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + 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 + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + 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. + If it's null, this PodAffinityTerm matches with no 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + x-kubernetes-map-type: atomic + 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 + x-kubernetes-list-type: atomic + 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 + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: |- + Annotations defines additional annotations to add to the pod template. + These annotations will be merged with the default annotations. + type: object args: description: Args defines the arguments to pass to the command. items: @@ -61,62 +998,2390 @@ spec: description: Cmd defines the command to run in the container to start the mcp server. type: string - configMapRefs: + configMapRefs: + description: |- + ConfigMapRefs defines the list of Kubernetes configmaps to reference. + These configmaps will be mounted as volumes to the MCP server container. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + env: + additionalProperties: + type: string + description: Env defines the environment variables to set in the + container. + type: object + image: + description: Image defines the container image to to deploy the + MCP server. + type: string + imagePullPolicy: + description: ImagePullPolicy defines the pull policy for the container + image. + enum: + - Always + - Never + - IfNotPresent + type: string + imagePullSecrets: + description: ImagePullSecrets defines the list of secrets to use + for pulling container images. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + initContainer: + description: |- + InitContainer defines the configuration for the init container that copies + the transport adapter binary. This is used for stdio transport type. + properties: + image: + description: |- + Image defines the full image reference for the init container. + If specified, this overrides the default transport adapter image. + Example: "myregistry.com/agentgateway/agentgateway:0.9.0-musl" + type: string + imagePullPolicy: + description: ImagePullPolicy defines the pull policy for the + init container image. + enum: + - Always + - Never + - IfNotPresent + type: string + resources: + description: |- + Resources defines the compute resource requirements for the init container. + Use this to specify CPU and memory requests and limits for the init container. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: |- + SecurityContext defines the security context for the init container. + If not specified, the main container's security context will be used. + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of + the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + type: object + labels: + additionalProperties: + type: string + description: |- + Labels defines additional labels to add to the pod template. + These labels will be merged with the default labels. + type: object + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector defines the node selector for the pod. + Use this to constrain pods to nodes with specific labels. + type: object + podSecurityContext: + description: |- + PodSecurityContext defines the security context for the entire pod. + Use this to configure pod-level security settings such as: + - runAsUser/runAsGroup: Default user/group for all containers + - fsGroup: Group ownership of mounted volumes + - seccompProfile: Seccomp profile for the pod + - sysctls: Kernel parameters to set + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxChangePolicy: + description: |- + seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod. + It has no effect on nodes that do not support SELinux or to volumes does not support SELinux. + Valid values are "MountOption" and "Recursive". + + "Recursive" means relabeling of all files on all Pod volumes by the container runtime. + This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node. + + "MountOption" mounts all eligible Pod volumes with `-o context` mount option. + This requires all Pods that share the same volume to use the same SELinux label. + It is not possible to share the same volume among privileged and unprivileged Pods. + Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes + whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their + CSIDriver instance. Other volumes are always re-labelled recursively. + "MountOption" value is allowed only when SELinuxMount feature gate is enabled. + + If not specified and SELinuxMount feature gate is enabled, "MountOption" is used. + If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes + and "Recursive" for all other volumes. + + This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers. + + All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state. + Note that this field cannot be set when spec.os.name is windows. + type: string + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + port: + default: 3000 + description: Port defines the port on which the MCP server will + listen. + type: integer + replicas: + default: 1 + description: |- + Replicas defines the number of desired pod replicas. + Defaults to 1 if not specified. + format: int32 + type: integer + resources: + description: |- + Resources defines the compute resource requirements for the main MCP server container. + Use this to specify CPU and memory requests and limits. + Example: + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "500m" + memory: "512Mi" + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secretRefs: + description: |- + SecretRefs defines the list of Kubernetes secrets to reference. + These secrets will be mounted as volumes to the MCP server container. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + securityContext: + description: |- + SecurityContext defines the security context for the main MCP server container. + Use this to configure container-level security settings such as: + - runAsUser/runAsGroup: Run as specific user/group + - runAsNonRoot: Ensure container doesn't run as root + - readOnlyRootFilesystem: Make root filesystem read-only + - allowPrivilegeEscalation: Prevent privilege escalation + - capabilities: Add or drop Linux capabilities + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + serviceAccount: + description: ServiceAccount defines the configuration for the + ServiceAccount to be created. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the ServiceAccount. + This is useful for configuring AWS IRSA (IAM Roles for Service Accounts) + or other cloud provider integrations. + Example: {"eks.amazonaws.com/role-arn": "arn:aws:iam::123456789012:role/my-role"} + type: object + labels: + additionalProperties: + type: string + description: Labels to add to the ServiceAccount. + type: object + type: object + serviceAccountName: + description: ServiceAccountName is the name of an existing ServiceAccount + to use. + type: string + sidecars: description: |- - ConfigMapRefs defines the list of Kubernetes configmaps to reference. - These configmaps will be mounted as volumes to the MCP server container. + Sidecars defines additional containers to run alongside the MCP server container. + These containers will share the same pod and can share volumes with the main container. items: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. + description: A single application container that you want to + run within a pod. properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. Must + be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + items: + description: EnvFromSource represents the source of a + set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object name: - default: "" description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource + resize policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This field may only be set for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Setting the RestartPolicy as "Always" for the init container will have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. type: string + required: + - name type: object - x-kubernetes-map-type: atomic type: array - env: - additionalProperties: - type: string - description: Env defines the environment variables to set in the - container. - type: object - image: - description: Image defines the container image to to deploy the - MCP server. - type: string - port: - default: 3000 - description: Port defines the port on which the MCP server will - listen. - type: integer - secretRefs: + tolerations: description: |- - SecretRefs defines the list of Kubernetes secrets to reference. - These secrets will be mounted as volumes to the MCP server container. + Tolerations defines the tolerations for the pod. + Use this to schedule pods on nodes with matching taints. items: description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: - name: - default: "" + effect: description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object - x-kubernetes-map-type: atomic type: array volumeMounts: description: |- @@ -1978,6 +5243,9 @@ spec: type: object type: array type: object + x-kubernetes-validations: + - message: serviceAccount and serviceAccountName are mutually exclusive + rule: '!(has(self.serviceAccount) && has(self.serviceAccountName))' httpTransport: description: HTTPTransport defines the configuration for a Streamable HTTP transport. diff --git a/helm/kmcp/templates/_helpers.tpl b/helm/kmcp/templates/_helpers.tpl index 064e72f..edf4980 100644 --- a/helm/kmcp/templates/_helpers.tpl +++ b/helm/kmcp/templates/_helpers.tpl @@ -83,5 +83,9 @@ Create controller manager container args {{- if .Values.controller.metrics.enabled }} {{- $args = append $args (printf "--metrics-bind-address=%s" .Values.controller.metrics.bindAddress) }} {{- end }} +{{- if not .Values.rbac.clusterScoped }} +{{- $namespaces := .Values.rbac.namespaces | default (list (include "kmcp.namespace" .)) }} +{{- $args = append $args (printf "--watch-namespaces=%s" (join "," $namespaces)) }} +{{- end }} {{- toYaml $args }} {{- end }} \ No newline at end of file diff --git a/helm/kmcp/templates/rbac/clusterrole.yaml b/helm/kmcp/templates/rbac/clusterrole.yaml index caad865..602cf7b 100644 --- a/helm/kmcp/templates/rbac/clusterrole.yaml +++ b/helm/kmcp/templates/rbac/clusterrole.yaml @@ -1,11 +1,4 @@ -{{- if .Values.rbac.create }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "kmcp.fullname" . }}-manager-role - labels: - {{- include "kmcp.labels" . | nindent 4 }} -rules: +{{- define "kmcp.manager.rules" -}} - apiGroups: - "" resources: @@ -58,4 +51,32 @@ rules: - get - patch - update +{{- end -}} + +{{- if .Values.rbac.create }} + +{{- if .Values.rbac.clusterScoped }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "kmcp.fullname" . }}-manager-role + labels: + {{- include "kmcp.labels" . | nindent 4 }} +rules: + {{- include "kmcp.manager.rules" . | nindent 2 }} +{{- else }} +{{- $namespaces := .Values.rbac.namespaces | default (list (include "kmcp.namespace" .)) }} +{{- range $namespace := $namespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "kmcp.fullname" $ }}-manager-role + namespace: {{ $namespace }} + labels: + {{- include "kmcp.labels" $ | nindent 4 }} +rules: + {{- include "kmcp.manager.rules" $ | nindent 2 }} +{{- end }} +{{- end }} {{- end }} \ No newline at end of file diff --git a/helm/kmcp/templates/rbac/clusterrolebinding.yaml b/helm/kmcp/templates/rbac/clusterrolebinding.yaml index 38683fc..6386867 100644 --- a/helm/kmcp/templates/rbac/clusterrolebinding.yaml +++ b/helm/kmcp/templates/rbac/clusterrolebinding.yaml @@ -1,4 +1,5 @@ {{- if .Values.rbac.create }} +{{- if .Values.rbac.clusterScoped }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -13,4 +14,25 @@ subjects: - kind: ServiceAccount name: {{ include "kmcp.serviceAccountName" . }} namespace: {{ include "kmcp.namespace" . }} +{{- else }} +{{- $namespaces := .Values.rbac.namespaces | default (list (include "kmcp.namespace" .)) }} +{{- range $namespace := $namespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "kmcp.fullname" $ }}-manager-rolebinding + namespace: {{ $namespace }} + labels: + {{- include "kmcp.labels" $ | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "kmcp.fullname" $ }}-manager-role +subjects: +- kind: ServiceAccount + name: {{ include "kmcp.serviceAccountName" $ }} + namespace: {{ include "kmcp.namespace" $ }} +{{- end }} +{{- end }} {{- end }} \ No newline at end of file diff --git a/helm/kmcp/tests/__snapshot__/deployment_test.yaml.snap b/helm/kmcp/tests/__snapshot__/deployment_test.yaml.snap index 4898dac..f7bf18a 100644 --- a/helm/kmcp/tests/__snapshot__/deployment_test.yaml.snap +++ b/helm/kmcp/tests/__snapshot__/deployment_test.yaml.snap @@ -8,7 +8,7 @@ should create deployment with default values: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -73,7 +73,7 @@ should create deployment with default values: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should include health probe ports when health probe enabled: @@ -86,7 +86,7 @@ should include health probe ports when health probe enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -151,7 +151,7 @@ should include health probe ports when health probe enabled: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should include image pull secrets when specified: @@ -164,7 +164,7 @@ should include image pull secrets when specified: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -231,7 +231,7 @@ should include image pull secrets when specified: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should include metrics port when metrics enabled: @@ -244,7 +244,7 @@ should include metrics port when metrics enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -309,7 +309,7 @@ should include metrics port when metrics enabled: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should include node selector when specified: @@ -322,7 +322,7 @@ should include node selector when specified: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -389,7 +389,7 @@ should include node selector when specified: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should include pod annotations when specified: @@ -402,7 +402,7 @@ should include pod annotations when specified: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -469,7 +469,7 @@ should include pod annotations when specified: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should include tolerations when specified: @@ -482,7 +482,7 @@ should include tolerations when specified: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -547,7 +547,7 @@ should include tolerations when specified: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 tolerations: - effect: NoSchedule @@ -565,7 +565,7 @@ should set custom replica count: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -630,7 +630,7 @@ should set custom replica count: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should set custom resources: @@ -643,7 +643,7 @@ should set custom resources: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -708,7 +708,7 @@ should set custom resources: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should set pod security context: @@ -721,7 +721,7 @@ should set pod security context: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -786,7 +786,7 @@ should set pod security context: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should set security context: @@ -799,7 +799,7 @@ should set security context: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -864,7 +864,7 @@ should set security context: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should set termination grace period: @@ -877,7 +877,7 @@ should set termination grace period: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -942,6 +942,6 @@ should set termination grace period: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] diff --git a/helm/kmcp/tests/__snapshot__/integration_test.yaml.snap b/helm/kmcp/tests/__snapshot__/integration_test.yaml.snap index 15aa41a..1db023f 100644 --- a/helm/kmcp/tests/__snapshot__/integration_test.yaml.snap +++ b/helm/kmcp/tests/__snapshot__/integration_test.yaml.snap @@ -8,7 +8,7 @@ should create deployment when metrics enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -73,7 +73,7 @@ should create deployment when metrics enabled: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should create deployment with default values: @@ -86,7 +86,7 @@ should create deployment with default values: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -151,7 +151,7 @@ should create deployment with default values: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should create deployment with minimal configuration: @@ -164,7 +164,7 @@ should create deployment with minimal configuration: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -238,7 +238,7 @@ should include metrics port in deployment when metrics enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -303,7 +303,7 @@ should include metrics port in deployment when metrics enabled: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should use custom namespace: @@ -316,7 +316,7 @@ should use custom namespace: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager namespace: NAMESPACE spec: @@ -381,7 +381,7 @@ should use custom namespace: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-controller-manager terminationGracePeriodSeconds: 10 volumes: [] should use custom release name: @@ -392,17 +392,17 @@ should use custom release name: labels: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: kmcp + app.kubernetes.io/name: custom-release control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 - name: RELEASE-NAME-controller-manager + helm.sh/chart: kmcp-1.0.0 + name: RELEASE-NAME-custom-release-controller-manager namespace: NAMESPACE spec: replicas: 1 selector: matchLabels: app.kubernetes.io/instance: RELEASE-NAME - app.kubernetes.io/name: kmcp + app.kubernetes.io/name: custom-release control-plane: controller-manager template: metadata: @@ -410,7 +410,7 @@ should use custom release name: kubectl.kubernetes.io/default-container: manager labels: app.kubernetes.io/instance: RELEASE-NAME - app.kubernetes.io/name: kmcp + app.kubernetes.io/name: custom-release control-plane: controller-manager spec: containers: @@ -459,6 +459,6 @@ should use custom release name: runAsNonRoot: true seccompProfile: type: RuntimeDefault - serviceAccountName: RELEASE-NAME-kmcp-controller-manager + serviceAccountName: RELEASE-NAME-custom-release-controller-manager terminationGracePeriodSeconds: 10 volumes: [] diff --git a/helm/kmcp/tests/__snapshot__/rbac_test.yaml.snap b/helm/kmcp/tests/__snapshot__/rbac_test.yaml.snap index 18ae092..4068ab0 100644 --- a/helm/kmcp/tests/__snapshot__/rbac_test.yaml.snap +++ b/helm/kmcp/tests/__snapshot__/rbac_test.yaml.snap @@ -1,3 +1,90 @@ +should create Role when clusterScoped is false: + 1: | + apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: kmcp + control-plane: controller-manager + helm.sh/chart: kmcp-1.0.0 + name: RELEASE-NAME-manager-role + namespace: NAMESPACE + rules: + - apiGroups: + - "" + resources: + - configmaps + - services + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kagent.dev + resources: + - mcpservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kagent.dev + resources: + - mcpservers/finalizers + verbs: + - update + - apiGroups: + - kagent.dev + resources: + - mcpservers/status + verbs: + - get + - patch + - update +should create RoleBinding when clusterScoped is false: + 1: | + apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: kmcp + control-plane: controller-manager + helm.sh/chart: kmcp-1.0.0 + name: RELEASE-NAME-manager-rolebinding + namespace: NAMESPACE + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: RELEASE-NAME-manager-role + subjects: + - kind: ServiceAccount + name: RELEASE-NAME-controller-manager + namespace: NAMESPACE should create cluster role binding when RBAC enabled: 1: | apiVersion: rbac.authorization.k8s.io/v1 @@ -8,7 +95,7 @@ should create cluster role binding when RBAC enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io @@ -16,7 +103,7 @@ should create cluster role binding when RBAC enabled: name: RELEASE-NAME-manager-role subjects: - kind: ServiceAccount - name: RELEASE-NAME-kmcp-controller-manager + name: RELEASE-NAME-controller-manager namespace: NAMESPACE should create cluster role when RBAC enabled: 1: | @@ -28,7 +115,7 @@ should create cluster role when RBAC enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-manager-role rules: - apiGroups: @@ -36,6 +123,7 @@ should create cluster role when RBAC enabled: resources: - configmaps - services + - serviceaccounts verbs: - create - delete @@ -92,7 +180,7 @@ should create leader election role: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-leader-election-role namespace: NAMESPACE rules: @@ -137,7 +225,7 @@ should create leader election role binding: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-leader-election-rolebinding namespace: NAMESPACE roleRef: @@ -146,7 +234,7 @@ should create leader election role binding: name: RELEASE-NAME-leader-election-role subjects: - kind: ServiceAccount - name: RELEASE-NAME-kmcp-controller-manager + name: RELEASE-NAME-controller-manager namespace: NAMESPACE should create metrics auth cluster role: 1: | @@ -158,7 +246,7 @@ should create metrics auth cluster role: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-metrics-auth-role rules: - apiGroups: @@ -183,7 +271,7 @@ should create metrics auth cluster role binding: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-metrics-auth-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io @@ -191,7 +279,7 @@ should create metrics auth cluster role binding: name: RELEASE-NAME-metrics-auth-role subjects: - kind: ServiceAccount - name: RELEASE-NAME-kmcp-controller-manager + name: RELEASE-NAME-controller-manager namespace: NAMESPACE should create metrics reader cluster role: 1: | @@ -203,13 +291,144 @@ should create metrics reader cluster role: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-metrics-reader rules: - nonResourceURLs: - /metrics verbs: - get +should create multiple Roles when multiple namespaces are provided: + 1: | + apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: kmcp + control-plane: controller-manager + helm.sh/chart: kmcp-1.0.0 + name: RELEASE-NAME-manager-role + namespace: ns1 + rules: + - apiGroups: + - "" + resources: + - configmaps + - services + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kagent.dev + resources: + - mcpservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kagent.dev + resources: + - mcpservers/finalizers + verbs: + - update + - apiGroups: + - kagent.dev + resources: + - mcpservers/status + verbs: + - get + - patch + - update + 2: | + apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: kmcp + control-plane: controller-manager + helm.sh/chart: kmcp-1.0.0 + name: RELEASE-NAME-manager-role + namespace: ns2 + rules: + - apiGroups: + - "" + resources: + - configmaps + - services + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kagent.dev + resources: + - mcpservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - kagent.dev + resources: + - mcpservers/finalizers + verbs: + - update + - apiGroups: + - kagent.dev + resources: + - mcpservers/status + verbs: + - get + - patch + - update should create service account when enabled: 1: | apiVersion: v1 @@ -221,8 +440,8 @@ should create service account when enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 - name: RELEASE-NAME-kmcp-controller-manager + helm.sh/chart: kmcp-1.0.0 + name: RELEASE-NAME-controller-manager namespace: NAMESPACE should include mcpservers finalizers rules: 1: | @@ -234,7 +453,7 @@ should include mcpservers finalizers rules: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-manager-role rules: - apiGroups: @@ -242,6 +461,7 @@ should include mcpservers finalizers rules: resources: - configmaps - services + - serviceaccounts verbs: - create - delete @@ -298,7 +518,7 @@ should include mcpservers resource rules: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-manager-role rules: - apiGroups: @@ -306,6 +526,7 @@ should include mcpservers resource rules: resources: - configmaps - services + - serviceaccounts verbs: - create - delete @@ -362,7 +583,7 @@ should include mcpservers status rules: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-manager-role rules: - apiGroups: @@ -370,6 +591,7 @@ should include mcpservers status rules: resources: - configmaps - services + - serviceaccounts verbs: - create - delete @@ -429,6 +651,6 @@ should include service account annotations when specified: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 - name: RELEASE-NAME-kmcp-controller-manager + helm.sh/chart: kmcp-1.0.0 + name: RELEASE-NAME-controller-manager namespace: NAMESPACE diff --git a/helm/kmcp/tests/__snapshot__/service_test.yaml.snap b/helm/kmcp/tests/__snapshot__/service_test.yaml.snap index 0a077c5..e95f5cf 100644 --- a/helm/kmcp/tests/__snapshot__/service_test.yaml.snap +++ b/helm/kmcp/tests/__snapshot__/service_test.yaml.snap @@ -8,7 +8,7 @@ should create service when metrics enabled: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager-metrics-service namespace: NAMESPACE spec: @@ -32,7 +32,7 @@ should set custom service port: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager-metrics-service namespace: NAMESPACE spec: @@ -56,7 +56,7 @@ should set custom service type: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager-metrics-service namespace: NAMESPACE spec: @@ -80,7 +80,7 @@ should set custom target port: app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: kmcp control-plane: controller-manager - helm.sh/chart: kmcp-0.1.0 + helm.sh/chart: kmcp-1.0.0 name: RELEASE-NAME-controller-manager-metrics-service namespace: NAMESPACE spec: diff --git a/helm/kmcp/tests/deployment_test.yaml b/helm/kmcp/tests/deployment_test.yaml index 54e483c..5bb622e 100644 --- a/helm/kmcp/tests/deployment_test.yaml +++ b/helm/kmcp/tests/deployment_test.yaml @@ -153,4 +153,46 @@ tests: asserts: - hasDocuments: count: 1 - - matchSnapshot: {} \ No newline at end of file + - matchSnapshot: {} + + - it: should not include watch-namespaces arg when clusterScoped is true + template: deployment.yaml + set: + rbac.clusterScoped: true + image.repository: test-repo + image.tag: v1.0.0 + asserts: + - hasDocuments: + count: 1 + - notContains: + path: spec.template.spec.containers[0].args + content: --watch-namespaces=NAMESPACE + + - it: should include watch-namespaces arg when clusterScoped is false with default namespace + template: deployment.yaml + set: + rbac.clusterScoped: false + image.repository: test-repo + image.tag: v1.0.0 + asserts: + - hasDocuments: + count: 1 + - contains: + path: spec.template.spec.containers[0].args + content: --watch-namespaces=NAMESPACE + + - it: should include watch-namespaces arg with explicit namespaces when clusterScoped is false + template: deployment.yaml + set: + rbac.clusterScoped: false + rbac.namespaces: + - ns1 + - ns2 + image.repository: test-repo + image.tag: v1.0.0 + asserts: + - hasDocuments: + count: 1 + - contains: + path: spec.template.spec.containers[0].args + content: --watch-namespaces=ns1,ns2 \ No newline at end of file diff --git a/helm/kmcp/tests/rbac_test.yaml b/helm/kmcp/tests/rbac_test.yaml index 8c3a679..178a461 100644 --- a/helm/kmcp/tests/rbac_test.yaml +++ b/helm/kmcp/tests/rbac_test.yaml @@ -142,4 +142,57 @@ tests: asserts: - hasDocuments: count: 1 - - matchSnapshot: {} \ No newline at end of file + - matchSnapshot: {} + + - it: should create Role when clusterScoped is false + template: rbac/clusterrole.yaml + set: + rbac.create: true + rbac.clusterScoped: false + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: Role + apiVersion: rbac.authorization.k8s.io/v1 + - matchSnapshot: {} + + - it: should create RoleBinding when clusterScoped is false + template: rbac/clusterrolebinding.yaml + set: + rbac.create: true + rbac.clusterScoped: false + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: RoleBinding + apiVersion: rbac.authorization.k8s.io/v1 + - matchSnapshot: {} + + - it: should create multiple Roles when multiple namespaces are provided + template: rbac/clusterrole.yaml + set: + rbac.create: true + rbac.clusterScoped: false + rbac.namespaces: + - ns1 + - ns2 + asserts: + - hasDocuments: + count: 2 + - equal: + path: metadata.namespace + value: ns1 + documentIndex: 0 + - equal: + path: metadata.namespace + value: ns2 + documentIndex: 1 + - isKind: + of: Role + documentIndex: 0 + - isKind: + of: Role + documentIndex: 1 + - matchSnapshot: {} \ No newline at end of file diff --git a/helm/kmcp/values.yaml b/helm/kmcp/values.yaml index 4f20792..72a16fc 100644 --- a/helm/kmcp/values.yaml +++ b/helm/kmcp/values.yaml @@ -95,6 +95,14 @@ serviceAccount: rbac: # Specifies whether RBAC resources should be created create: true + + # -- If true, creates ClusterRole and ClusterRoleBinding resources. + # If false, creates Role and RoleBinding resources instead. + clusterScoped: true + + # -- When clusterScoped is false, specify additional namespaces to create Roles and RoleBindings in. + # If empty, defaults to the release namespace. + namespaces: [] # Service configuration for metrics service: diff --git a/pkg/app/app.go b/pkg/app/app.go index 015a7d5..eff9bdf 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -21,6 +21,7 @@ import ( "flag" "os" "path/filepath" + "strings" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -30,6 +31,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" @@ -70,10 +72,11 @@ type Config struct { CertName string CertKey string } - LeaderElection bool - ProbeAddr string - SecureMetrics bool - EnableHTTP2 bool + LeaderElection bool + ProbeAddr string + SecureMetrics bool + EnableHTTP2 bool + WatchNamespaces string } func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { @@ -109,6 +112,8 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.StringVar(&cfg.Webhook.CertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") commandLine.BoolVar(&cfg.EnableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") + commandLine.StringVar(&cfg.WatchNamespaces, "watch-namespaces", "", + "Comma-separated list of namespaces the controller watches. If empty, watches all namespaces.") } // PluginFactory creates a TranslatorPlugin when provided with the client and scheme. @@ -127,6 +132,31 @@ type ExtensionConfig struct { type GetExtensionConfig func() (*ExtensionConfig, error) +// filterValidNamespaces removes empty strings from a list of namespace names. +func filterValidNamespaces(namespaces []string) []string { + var result []string + for _, ns := range namespaces { + if ns = strings.TrimSpace(ns); ns != "" { + result = append(result, ns) + } + } + return result +} + +// configureNamespaceWatching returns a DefaultNamespaces map for cache.Options +// when a non-empty namespace list is provided, restricting the controller's +// watches to those namespaces. Returns nil (cluster-wide) when the list is empty. +func configureNamespaceWatching(namespaces []string) map[string]cache.Config { + if len(namespaces) == 0 { + return nil + } + nsMap := make(map[string]cache.Config, len(namespaces)) + for _, ns := range namespaces { + nsMap[ns] = cache.Config{} + } + return nsMap +} + // nolint:gocyclo func Start(getExtensionConfig GetExtensionConfig) { var cfg Config @@ -248,6 +278,13 @@ func Start(getExtensionConfig GetExtensionConfig) { } } + watchNamespacesList := filterValidNamespaces(strings.Split(cfg.WatchNamespaces, ",")) + if len(watchNamespacesList) > 0 { + setupLog.Info("watching specific namespaces", "namespaces", watchNamespacesList) + } else { + setupLog.Info("watching all namespaces") + } + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, Metrics: metricsServerOptions, @@ -255,6 +292,9 @@ func Start(getExtensionConfig GetExtensionConfig) { HealthProbeBindAddress: cfg.ProbeAddr, LeaderElection: cfg.LeaderElection, LeaderElectionID: "90217b08.kagent.dev", + Cache: cache.Options{ + DefaultNamespaces: configureNamespaceWatching(watchNamespacesList), + }, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go new file mode 100644 index 0000000..06e92e5 --- /dev/null +++ b/pkg/app/app_test.go @@ -0,0 +1,106 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "testing" +) + +func TestFilterValidNamespaces(t *testing.T) { + tests := []struct { + name string + input []string + expected []string + }{ + { + name: "empty list", + input: []string{""}, + expected: nil, + }, + { + name: "single valid namespace", + input: []string{"default"}, + expected: []string{"default"}, + }, + { + name: "multiple valid namespaces", + input: []string{"ns1", "ns2", "ns3"}, + expected: []string{"ns1", "ns2", "ns3"}, + }, + { + name: "filters out empty strings", + input: []string{"ns1", "", "ns2"}, + expected: []string{"ns1", "ns2"}, + }, + { + name: "filters out whitespace-only strings", + input: []string{"ns1", " ", "ns2"}, + expected: []string{"ns1", "ns2"}, + }, + { + name: "trims whitespace from valid namespaces", + input: []string{" ns1 ", "ns2"}, + expected: []string{"ns1", "ns2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := filterValidNamespaces(tt.input) + if len(result) != len(tt.expected) { + t.Errorf("got %v (len %d), want %v (len %d)", result, len(result), tt.expected, len(tt.expected)) + return + } + for i := range result { + if result[i] != tt.expected[i] { + t.Errorf("index %d: got %q, want %q", i, result[i], tt.expected[i]) + } + } + }) + } +} + +func TestConfigureNamespaceWatching(t *testing.T) { + t.Run("empty list returns nil (cluster-wide)", func(t *testing.T) { + result := configureNamespaceWatching([]string{}) + if result != nil { + t.Errorf("expected nil for cluster-wide watching, got %v", result) + } + }) + + t.Run("single namespace returns map with one entry", func(t *testing.T) { + result := configureNamespaceWatching([]string{"default"}) + if len(result) != 1 { + t.Errorf("expected 1 entry, got %d", len(result)) + } + if _, ok := result["default"]; !ok { + t.Errorf("expected key 'default' in map, got %v", result) + } + }) + + t.Run("multiple namespaces returns map with all entries", func(t *testing.T) { + result := configureNamespaceWatching([]string{"ns1", "ns2", "ns3"}) + if len(result) != 3 { + t.Errorf("expected 3 entries, got %d", len(result)) + } + for _, ns := range []string{"ns1", "ns2", "ns3"} { + if _, ok := result[ns]; !ok { + t.Errorf("expected key %q in map, got %v", ns, result) + } + } + }) +} diff --git a/pkg/controller/mcpserver_controller.go b/pkg/controller/mcpserver_controller.go index c33dc80..12fd8a6 100644 --- a/pkg/controller/mcpserver_controller.go +++ b/pkg/controller/mcpserver_controller.go @@ -50,6 +50,7 @@ type MCPServerReconciler struct { // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. From c72b9bf7cde0b97eb7145cfce5dad41b07121014 Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 15:23:16 -0400 Subject: [PATCH 2/8] attempt to improve e2e Signed-off-by: Jet Chiang --- .github/workflows/test-e2e.yml | 13 +++++++++++-- Makefile | 6 +++++- api/v1alpha1/zz_generated.deepcopy.go | 2 +- test/e2e/e2e_suite_test.go | 21 +-------------------- test/e2e/e2e_test.go | 3 ++- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 7d7dacc..35f0ace 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -21,8 +21,17 @@ jobs: - name: Create k8s Kind Cluster uses: helm/kind-action@v1 - - name: Running Test e2e + + - name: Build controller image + run: make docker-build CONTROLLER_IMG=ghcr.io/kagent-dev/kmcp/controller:e2e-test + timeout-minutes: 15 + + - name: Load image into Kind + run: make kind-load CONTROLLER_IMG=ghcr.io/kagent-dev/kmcp/controller:e2e-test + timeout-minutes: 5 + + - name: Run e2e tests run: | - kind create cluster go mod tidy make test-e2e + timeout-minutes: 20 diff --git a/Makefile b/Makefile index 261bdeb..2aaf8a7 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ HELM_REPO ?= oci://ghcr.io/kagent-dev BUILD_DATE := $(shell date -u '+%Y-%m-%d') GIT_COMMIT := $(shell git rev-parse --short HEAD || echo "unknown") -VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null | sed 's/-dirty//' | grep v || echo "v0.0.1+$(GIT_COMMIT)") +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null | sed 's/-dirty//' | grep v || echo "v0.0.1-$(GIT_COMMIT)") # Version information for the build @@ -243,6 +243,10 @@ docker-build: ## Build docker image with the manager. $(DOCKER_BUILDER) build $(DOCKER_BUILD_ARGS) -t ${CONTROLLER_IMG} . - $(DOCKER_BUILDER) rm $(BUILDX_BUILDER_NAME) +.PHONY: kind-load +kind-load: ## Load the controller image into the Kind cluster. + kind load docker-image ${CONTROLLER_IMG} + .PHONY: docker-tag-latest docker-tag-latest: ## Tag the built image as 'latest' and push it @echo "Tagging image as 'latest'..." diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ca9fa0d..b305b9d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -22,7 +22,7 @@ package v1alpha1 import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index c176ef1..4377960 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -18,20 +18,16 @@ package e2e import ( "fmt" - "os/exec" "testing" ginkgo "github.com/onsi/ginkgo/v2" gomega "github.com/onsi/gomega" - - "github.com/kagent-dev/kmcp/test/utils" ) var ( - // projectImage is the name of the image which will be build and loaded // with the code source changes to be tested. - projectImage = "ghcr.io/kagent-dev/kmcp/controller:v0.0.1" + projectImage = "ghcr.io/kagent-dev/kmcp/controller:e2e-test" ) // TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, @@ -43,18 +39,3 @@ func TestE2E(t *testing.T) { _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Starting kmcp integration test suite\n") ginkgo.RunSpecs(t, "e2e suite") } - -var _ = ginkgo.BeforeSuite(func() { - - ginkgo.By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("CONTROLLER_IMG=%s", projectImage)) - _, err := utils.Run(cmd) - gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred(), "Failed to build the manager(Operator) image") - - // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is - // built and available before running the tests. Also, remove the following block. - ginkgo.By("loading the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectImage) - gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred(), "Failed to load the manager(Operator) image into Kind") - -}) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index faedf29..1988c61 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -62,7 +62,8 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { "--namespace", namespace, "--wait", "--timeout=5m", "--set", fmt.Sprintf("image.repository=%s", getImageRepository(projectImage)), - "--set", fmt.Sprintf("image.tag=%s", getImageTag(projectImage))) + "--set", fmt.Sprintf("image.tag=%s", getImageTag(projectImage)), + "--set", "image.pullPolicy=Never") _, err = utils.Run(cmd) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to deploy the controller-manager using Helm") }) From e3eeab5ae5afb78739d68321789d306463d0ab2f Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 15:31:46 -0400 Subject: [PATCH 3/8] attempt to improve e2e Signed-off-by: Jet Chiang --- .github/workflows/test-e2e.yml | 2 ++ Makefile | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 35f0ace..404c29f 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -21,6 +21,8 @@ jobs: - name: Create k8s Kind Cluster uses: helm/kind-action@v1 + with: + cluster_name: kind-kmcp-test - name: Build controller image run: make docker-build CONTROLLER_IMG=ghcr.io/kagent-dev/kmcp/controller:e2e-test diff --git a/Makefile b/Makefile index 2aaf8a7..95e3f83 100644 --- a/Makefile +++ b/Makefile @@ -245,7 +245,7 @@ docker-build: ## Build docker image with the manager. .PHONY: kind-load kind-load: ## Load the controller image into the Kind cluster. - kind load docker-image ${CONTROLLER_IMG} + kind load docker-image ${CONTROLLER_IMG} --name kind-kmcp-test .PHONY: docker-tag-latest docker-tag-latest: ## Tag the built image as 'latest' and push it From 2317a1df0e65953417aa41979a275100521f48ea Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 15:44:30 -0400 Subject: [PATCH 4/8] ci Signed-off-by: Jet Chiang --- .github/workflows/test-e2e.yml | 6 +----- Makefile | 4 ---- test/e2e/e2e_suite_test.go | 8 ++++++++ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 404c29f..6e82874 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -22,16 +22,12 @@ jobs: - name: Create k8s Kind Cluster uses: helm/kind-action@v1 with: - cluster_name: kind-kmcp-test + cluster_name: kind - name: Build controller image run: make docker-build CONTROLLER_IMG=ghcr.io/kagent-dev/kmcp/controller:e2e-test timeout-minutes: 15 - - name: Load image into Kind - run: make kind-load CONTROLLER_IMG=ghcr.io/kagent-dev/kmcp/controller:e2e-test - timeout-minutes: 5 - - name: Run e2e tests run: | go mod tidy diff --git a/Makefile b/Makefile index 95e3f83..16d9224 100644 --- a/Makefile +++ b/Makefile @@ -243,10 +243,6 @@ docker-build: ## Build docker image with the manager. $(DOCKER_BUILDER) build $(DOCKER_BUILD_ARGS) -t ${CONTROLLER_IMG} . - $(DOCKER_BUILDER) rm $(BUILDX_BUILDER_NAME) -.PHONY: kind-load -kind-load: ## Load the controller image into the Kind cluster. - kind load docker-image ${CONTROLLER_IMG} --name kind-kmcp-test - .PHONY: docker-tag-latest docker-tag-latest: ## Tag the built image as 'latest' and push it @echo "Tagging image as 'latest'..." diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 4377960..db9300c 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -22,6 +22,8 @@ import ( ginkgo "github.com/onsi/ginkgo/v2" gomega "github.com/onsi/gomega" + + "github.com/kagent-dev/kmcp/test/utils" ) var ( @@ -39,3 +41,9 @@ func TestE2E(t *testing.T) { _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Starting kmcp integration test suite\n") ginkgo.RunSpecs(t, "e2e suite") } + +var _ = ginkgo.BeforeSuite(func() { + ginkgo.By("loading the manager(Operator) image on Kind") + err := utils.LoadImageToKindClusterWithName(projectImage) + gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred(), "Failed to load the manager(Operator) image into Kind") +}) From 6f8fcc24dba92d4f024428ba9f606b2993b4640e Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 16:06:50 -0400 Subject: [PATCH 5/8] attempt to fix tests again Signed-off-by: Jet Chiang --- Makefile | 2 +- test/e2e/e2e_test.go | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 16d9224..8768cd6 100644 --- a/Makefile +++ b/Makefile @@ -166,7 +166,7 @@ test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ exit 1; \ } - go test ./test/e2e/ -v + go test ./test/e2e/ -v -timeout 30m .PHONY: lint lint: golangci-lint ## Run golangci-lint linter diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 1988c61..3bf178a 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -303,9 +303,17 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { err = portForwardCmd.Start() gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to start port-forward") + ginkgo.DeferCleanup(func() { + if portForwardCmd != nil && portForwardCmd.Process != nil { + _ = portForwardCmd.Process.Kill() + _, _ = portForwardCmd.Process.Wait() + } + }) + // Wait for port-forward to be ready + httpClient := &http.Client{Timeout: 2 * time.Second} gomega.Eventually(func() error { - resp, err := http.Get(fmt.Sprintf("http://localhost:%d", localPort)) + resp, err := httpClient.Get(fmt.Sprintf("http://localhost:%d/mcp", localPort)) if err != nil { return err } @@ -317,7 +325,8 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { mcpClient, err := client.NewStreamableHttpClient(fmt.Sprintf("http://localhost:%d/mcp", localPort)) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to create MCP client") - ctx := context.Background() + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ginkgo.DeferCleanup(cancel) ginkgo.By("initializing the MCP client") initResponse, err := mcpClient.Initialize(ctx, mcp.InitializeRequest{ @@ -374,12 +383,6 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { g.Expect(currentPodName).NotTo(gomega.Equal(originalPodName), "Pod should have been restarted") }, 1*time.Minute, 5*time.Second).Should(gomega.Succeed()) - ginkgo.By("cleaning up port-forward") - if portForwardCmd != nil && portForwardCmd.Process != nil { - err = portForwardCmd.Process.Kill() - gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to kill port-forward process") - } - ginkgo.By("cleaning up the MCPServer") cmd = exec.Command("kubectl", "delete", "mcpserver", mcpServerName, "-n", namespace) _, err = utils.Run(cmd) From 492330a9667b52fa922baa1872e83261c7a26555 Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 17:09:49 -0400 Subject: [PATCH 6/8] Revert "attempt to fix tests again" This reverts commit 597cd531dff686ca32e1a0c411b8ee225607815b. Signed-off-by: Jet Chiang --- Makefile | 2 +- test/e2e/e2e_test.go | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 8768cd6..16d9224 100644 --- a/Makefile +++ b/Makefile @@ -166,7 +166,7 @@ test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ exit 1; \ } - go test ./test/e2e/ -v -timeout 30m + go test ./test/e2e/ -v .PHONY: lint lint: golangci-lint ## Run golangci-lint linter diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 3bf178a..1988c61 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -303,17 +303,9 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { err = portForwardCmd.Start() gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to start port-forward") - ginkgo.DeferCleanup(func() { - if portForwardCmd != nil && portForwardCmd.Process != nil { - _ = portForwardCmd.Process.Kill() - _, _ = portForwardCmd.Process.Wait() - } - }) - // Wait for port-forward to be ready - httpClient := &http.Client{Timeout: 2 * time.Second} gomega.Eventually(func() error { - resp, err := httpClient.Get(fmt.Sprintf("http://localhost:%d/mcp", localPort)) + resp, err := http.Get(fmt.Sprintf("http://localhost:%d", localPort)) if err != nil { return err } @@ -325,8 +317,7 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { mcpClient, err := client.NewStreamableHttpClient(fmt.Sprintf("http://localhost:%d/mcp", localPort)) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to create MCP client") - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - ginkgo.DeferCleanup(cancel) + ctx := context.Background() ginkgo.By("initializing the MCP client") initResponse, err := mcpClient.Initialize(ctx, mcp.InitializeRequest{ @@ -383,6 +374,12 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { g.Expect(currentPodName).NotTo(gomega.Equal(originalPodName), "Pod should have been restarted") }, 1*time.Minute, 5*time.Second).Should(gomega.Succeed()) + ginkgo.By("cleaning up port-forward") + if portForwardCmd != nil && portForwardCmd.Process != nil { + err = portForwardCmd.Process.Kill() + gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to kill port-forward process") + } + ginkgo.By("cleaning up the MCPServer") cmd = exec.Command("kubectl", "delete", "mcpserver", mcpServerName, "-n", namespace) _, err = utils.Run(cmd) From c60575153f39999f26a817cea8e1e1c90c6f033b Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 17:10:02 -0400 Subject: [PATCH 7/8] fix Signed-off-by: Jet Chiang --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 16d9224..8768cd6 100644 --- a/Makefile +++ b/Makefile @@ -166,7 +166,7 @@ test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ exit 1; \ } - go test ./test/e2e/ -v + go test ./test/e2e/ -v -timeout 30m .PHONY: lint lint: golangci-lint ## Run golangci-lint linter From c63526888645f7f725d67acd567901192c1427dd Mon Sep 17 00:00:00 2001 From: Jet Chiang Date: Fri, 27 Mar 2026 18:48:21 -0400 Subject: [PATCH 8/8] fix ci finally? Signed-off-by: Jet Chiang --- pkg/cli/internal/frameworks/python/templates/Dockerfile.tmpl | 4 ++-- test/e2e/e2e_test.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/cli/internal/frameworks/python/templates/Dockerfile.tmpl b/pkg/cli/internal/frameworks/python/templates/Dockerfile.tmpl index 6cf2c41..1b3ba09 100644 --- a/pkg/cli/internal/frameworks/python/templates/Dockerfile.tmpl +++ b/pkg/cli/internal/frameworks/python/templates/Dockerfile.tmpl @@ -1,5 +1,5 @@ # Multi-stage build for {{.ProjectName}} MCP server using uv -FROM python:3.11-slim as builder +FROM python:3.12-slim as builder # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv @@ -26,7 +26,7 @@ COPY src/ ./src/ COPY kmcp.yaml ./ # Production stage -FROM python:3.11-slim +FROM python:3.12-slim # Install uv in production COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 1988c61..753c608 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -317,7 +317,8 @@ var _ = ginkgo.Describe("Manager", ginkgo.Ordered, func() { mcpClient, err := client.NewStreamableHttpClient(fmt.Sprintf("http://localhost:%d/mcp", localPort)) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to create MCP client") - ctx := context.Background() + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() ginkgo.By("initializing the MCP client") initResponse, err := mcpClient.Initialize(ctx, mcp.InitializeRequest{