diff --git a/.circleci/config.yml b/.circleci/config.yml index 25da76898..66c86b3b9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,9 +2,9 @@ version: 2.1 orbs: - kubernetes: circleci/kubernetes@0.4.0 - aws-cli: circleci/aws-cli@0.1.22 - aws-eks: sandbox/aws-eks@0.1.2 + kubernetes: circleci/kubernetes@0.11.2 + aws-cli: circleci/aws-cli@1.3.0 + aws-eks: circleci/aws-eks@1.0.3 workflows: version: 2 diff --git a/README.md b/README.md index 8a87782c2..aabf90b9a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Splunk Operator for Kubernetes [![License](https://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) -[![GoDoc](https://godoc.org/github.com/splunk/splunk-operator?status.svg)](https://godoc.org/github.com/splunk/splunk-operator) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/splunk/splunk-operator)](https://pkg.go.dev/github.com/splunk/splunk-operator) [![Go Report Card](https://goreportcard.com/badge/github.com/splunk/splunk-operator)](https://goreportcard.com/report/github.com/splunk/splunk-operator) [![CircleCI](https://circleci.com/gh/splunk/splunk-operator/tree/master.svg?style=shield)](https://circleci.com/gh/splunk/splunk-operator/tree/master) [![Coverage Status](https://coveralls.io/repos/github/splunk/splunk-operator/badge.svg?branch=master)](https://coveralls.io/github/splunk/splunk-operator?branch=master) diff --git a/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml b/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml index 2abb01ba8..5b0f94156 100644 --- a/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml +++ b/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml @@ -2236,6 +2236,11 @@ spec: description: IndexerClusterStatus defines the observed state of a Splunk Enterprise indexer cluster properties: + IdxcPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose IDXC password has changed + type: object clusterMasterPhase: description: current phase of the cluster master enum: @@ -2320,6 +2325,10 @@ spec: description: Indicates whether the master is ready to begin servicing, based on whether it is initialized. type: boolean + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean type: object type: object version: v1beta1 diff --git a/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml b/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml index 1db73dcc0..77d75f9aa 100644 --- a/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml +++ b/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml @@ -2375,6 +2375,10 @@ spec: items: type: boolean type: array + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean type: object type: object version: v1beta1 diff --git a/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml b/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml index 2abb01ba8..4652896eb 100644 --- a/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml +++ b/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml @@ -2236,6 +2236,11 @@ spec: description: IndexerClusterStatus defines the observed state of a Splunk Enterprise indexer cluster properties: + IdxcPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose IDXC password has changed + type: object clusterMasterPhase: description: current phase of the cluster master enum: diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_clustermasters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_clustermasters_crd.yaml new file mode 100644 index 000000000..5c9b2adc7 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_clustermasters_crd.yaml @@ -0,0 +1,2463 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: clustermasters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of cluster master + name: Phase + type: string + group: enterprise.splunk.com + names: + kind: ClusterMaster + listKind: ClusterMasterList + plural: clustermasters + shortNames: + - cm-idxc + singular: clustermaster + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: ClusterMaster is the Schema for the clustermasters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterMasterSpec defines the desired state of ClusterMaster + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + 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 + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + sparkImage: + description: Image to use for Spark pod containers (overrides RELATED_IMAGE_SPLUNK_SPARK + environment variables). Also used on cluster master for init container + to setup the soft links from ../master-apps/splunk-operator/local/ + to /mnt/splunk-operator/local/ + type: string + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: 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 + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced ConfigMap will be projected into + the volume as a file whose name is the key and content is + the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the + ConfigMap, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path name + of the file to be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 encoded. The + first item of the relative path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. Directories within the + path are not affected by this setting. This might be in + conflict with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI data + to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience of + the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account token. + As the token approaches expiration, the kubelet + volume plugin will proactively rotate the service + account token. The kubelet will start trying to + rotate the token if the token is older than 80 + percent of its time to live or if the token is + older than 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to the mount + point of the file to project the token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: ClusterMasterStatus defines the observed state of ClusterMaster + properties: + bundlePushInfo: + description: Bundle push status tracker + properties: + lastCheckInterval: + format: int64 + type: integer + needToPushMasterApps: + type: boolean + type: object + phase: + description: current phase of the cluster master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + resourceRevMap: + additionalProperties: + type: string + description: Resource Revision tracker + type: object + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_indexerclusters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_indexerclusters_crd.yaml new file mode 100644 index 000000000..5b0f94156 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_indexerclusters_crd.yaml @@ -0,0 +1,2344 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: indexerclusters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of indexer cluster + name: Phase + type: string + - JSONPath: .status.clusterMasterPhase + description: Status of cluster master + name: Master + type: string + - JSONPath: .status.replicas + description: Desired number of indexer peers + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready indexer peers + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of indexer cluster + name: Age + type: date + group: enterprise.splunk.com + names: + kind: IndexerCluster + listKind: IndexerClusterList + plural: indexerclusters + shortNames: + - idc + - idxc + singular: indexercluster + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: IndexerCluster is the Schema for a Splunk Enterprise indexer cluster + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IndexerClusterSpec defines the desired state of a Splunk Enterprise + indexer cluster + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + 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 + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + replicas: + description: Number of search head pods; a search head cluster will + be created if > 1 + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: 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 + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced ConfigMap will be projected into + the volume as a file whose name is the key and content is + the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the + ConfigMap, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path name + of the file to be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 encoded. The + first item of the relative path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. Directories within the + path are not affected by this setting. This might be in + conflict with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI data + to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience of + the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account token. + As the token approaches expiration, the kubelet + volume plugin will proactively rotate the service + account token. The kubelet will start trying to + rotate the token if the token is older than 80 + percent of its time to live or if the token is + older than 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to the mount + point of the file to project the token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: IndexerClusterStatus defines the observed state of a Splunk + Enterprise indexer cluster + properties: + IdxcPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose IDXC password has changed + type: object + clusterMasterPhase: + description: current phase of the cluster master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + indexer_secret_changed_flag: + description: Indicates when the idxc_secret has been changed for a peer + items: + type: boolean + type: array + indexing_ready_flag: + description: Indicates if the cluster is ready for indexing. + type: boolean + initialized_flag: + description: Indicates if the cluster is initialized. + type: boolean + maintenance_mode: + description: Indicates if the cluster is in maintenance mode. + type: boolean + namespace_scoped_secret_resource_version: + description: Indicates resource version of namespace scoped secret + type: string + peers: + description: status of each indexer cluster peer + items: + description: IndexerClusterMemberStatus is used to track the status + of each indexer cluster peer. + properties: + active_bundle_id: + description: The ID of the configuration bundle currently being + used by the master. + type: string + bucket_count: + description: Count of the number of buckets on this peer, across + all indexes. + format: int64 + type: integer + guid: + description: Unique identifier or GUID for the peer + type: string + is_searchable: + description: Flag indicating if this peer belongs to the current + committed generation and is searchable. + type: boolean + name: + description: Name of the indexer cluster peer + type: string + status: + description: Status of the indexer cluster peer + type: string + type: object + type: array + phase: + description: current phase of the indexer cluster + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready indexer peers + format: int32 + type: integer + replicas: + description: desired number of indexer peers + format: int32 + type: integer + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + service_ready_flag: + description: Indicates whether the master is ready to begin servicing, + based on whether it is initialized. + type: boolean + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_licensemasters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_licensemasters_crd.yaml new file mode 100644 index 000000000..5b2239343 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_licensemasters_crd.yaml @@ -0,0 +1,2240 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: licensemasters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of license master + name: Phase + type: string + - JSONPath: .metadata.creationTimestamp + description: Age of license master + name: Age + type: date + group: enterprise.splunk.com + names: + kind: LicenseMaster + listKind: LicenseMasterList + plural: licensemasters + shortNames: + - lm + singular: licensemaster + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: LicenseMaster is the Schema for a Splunk Enterprise license master. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: LicenseMasterSpec defines the desired state of a Splunk Enterprise + license master. + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + 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 + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: 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 + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced ConfigMap will be projected into + the volume as a file whose name is the key and content is + the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the + ConfigMap, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path name + of the file to be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 encoded. The + first item of the relative path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. Directories within the + path are not affected by this setting. This might be in + conflict with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI data + to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience of + the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account token. + As the token approaches expiration, the kubelet + volume plugin will proactively rotate the service + account token. The kubelet will start trying to + rotate the token if the token is older than 80 + percent of its time to live or if the token is + older than 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to the mount + point of the file to project the token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: LicenseMasterStatus defines the observed state of a Splunk + Enterprise license master. + properties: + phase: + description: current phase of the license master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_searchheadclusters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_searchheadclusters_crd.yaml new file mode 100644 index 000000000..77d75f9aa --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_searchheadclusters_crd.yaml @@ -0,0 +1,2394 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: searchheadclusters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of search head cluster + name: Phase + type: string + - JSONPath: .status.deployerPhase + description: Status of the deployer + name: Deployer + type: string + - JSONPath: .status.replicas + description: Desired number of search head cluster members + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready search head cluster members + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of search head cluster + name: Age + type: date + group: enterprise.splunk.com + names: + kind: SearchHeadCluster + listKind: SearchHeadClusterList + plural: searchheadclusters + shortNames: + - shc + singular: searchheadcluster + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: SearchHeadCluster is the Schema for a Splunk Enterprise search + head cluster + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SearchHeadClusterSpec defines the desired state of a Splunk + Enterprise search head cluster + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + 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 + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + replicas: + description: Number of search head pods; a search head cluster will + be created if > 1 + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + sparkImage: + description: Image to use for Spark pod containers (overrides RELATED_IMAGE_SPLUNK_SPARK + environment variables) + type: string + sparkRef: + description: SparkRef refers to a Spark cluster managed by the operator + within Kubernetes When defined, Data Fabric Search (DFS) will be enabled + and configured to use the Spark cluster. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: 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 + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced ConfigMap will be projected into + the volume as a file whose name is the key and content is + the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the + ConfigMap, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path name + of the file to be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 encoded. The + first item of the relative path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. Directories within the + path are not affected by this setting. This might be in + conflict with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI data + to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience of + the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account token. + As the token approaches expiration, the kubelet + volume plugin will proactively rotate the service + account token. The kubelet will start trying to + rotate the token if the token is older than 80 + percent of its time to live or if the token is + older than 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to the mount + point of the file to project the token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: SearchHeadClusterStatus defines the observed state of a Splunk + Enterprise search head cluster + properties: + adminPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose admin password has changed + type: object + adminSecretChangedFlag: + description: Indicates when the admin password has been changed for + a peer + items: + type: boolean + type: array + captain: + description: name or label of the search head captain + type: string + captainReady: + description: true if the search head cluster's captain is ready to service + requests + type: boolean + deployerPhase: + description: current phase of the deployer + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + initialized: + description: true if the search head cluster has finished initialization + type: boolean + maintenanceMode: + description: true if the search head cluster is in maintenance mode + type: boolean + members: + description: status of each search head cluster member + items: + description: SearchHeadClusterMemberStatus is used to track the status + of each search head cluster member + properties: + active_historical_search_count: + description: Number of currently running historical searches. + type: integer + active_realtime_search_count: + description: Number of currently running realtime searches. + type: integer + adhoc_searchhead: + description: Flag that indicates if this member can run scheduled + searches. + type: boolean + is_registered: + description: Indicates if this member is registered with the searchhead + cluster captain. + type: boolean + name: + description: Name of the search head cluster member + type: string + status: + description: Indicates the status of the member. + type: string + type: object + type: array + minPeersJoined: + description: true if the minimum number of search head cluster members + have joined + type: boolean + namespace_scoped_secret_resource_version: + description: Indicates resource version of namespace scoped secret + type: string + phase: + description: current phase of the search head cluster + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready search head cluster members + format: int32 + type: integer + replicas: + description: desired number of search head cluster members + format: int32 + type: integer + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + shcSecretChangedFlag: + description: Indicates when the shc_secret has been changed for a peer + items: + type: boolean + type: array + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_sparks_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_sparks_crd.yaml new file mode 100644 index 000000000..063d16f26 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_sparks_crd.yaml @@ -0,0 +1,996 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: sparks.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of Spark workers + name: Phase + type: string + - JSONPath: .status.masterPhase + description: Status of Spark master + name: Master + type: string + - JSONPath: .status.replicas + description: Number of desired Spark workers + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready Spark workers + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of Spark cluster + name: Age + type: date + group: enterprise.splunk.com + names: + kind: Spark + listKind: SparkList + plural: sparks + singular: spark + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: Spark is the Schema for a Spark cluster + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SparkSpec defines the desired state of a Spark cluster + properties: + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + 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 + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + replicas: + description: Number of spark worker pods + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: 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 + type: array + type: object + status: + description: SparkStatus defines the observed state of a Spark cluster + properties: + masterPhase: + description: current phase of the spark master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + phase: + description: current phase of the spark workers + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready spark workers + format: int32 + type: integer + replicas: + description: number of desired spark workers + format: int32 + type: integer + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_standalones_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_standalones_crd.yaml new file mode 100644 index 000000000..044014cd5 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_standalones_crd.yaml @@ -0,0 +1,2518 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: standalones.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of standalone instances + name: Phase + type: string + - JSONPath: .status.replicas + description: Number of desired standalone instances + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready standalone instances + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of standalone resource + name: Age + type: date + group: enterprise.splunk.com + names: + kind: Standalone + listKind: StandaloneList + plural: standalones + singular: standalone + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: Standalone is the Schema for a Splunk Enterprise standalone instances. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: StandaloneSpec defines the desired state of a Splunk Enterprise + standalone instances. + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + 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 + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: 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. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + replicas: + description: Number of standalone pods + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + sparkImage: + description: Image to use for Spark pod containers (overrides RELATED_IMAGE_SPLUNK_SPARK + environment variables) + type: string + sparkRef: + description: SparkRef refers to a Spark cluster managed by the operator + within Kubernetes When defined, Data Fabric Search (DFS) will be enabled + and configured to use the Spark cluster. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: 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 + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced ConfigMap will be projected into + the volume as a file whose name is the key and content is + the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the + ConfigMap, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path name + of the file to be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 encoded. The + first item of the relative path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. Directories within the + path are not affected by this setting. This might be in + conflict with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI data + to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience of + the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account token. + As the token approaches expiration, the kubelet + volume plugin will proactively rotate the service + account token. The kubelet will start trying to + rotate the token if the token is older than 80 + percent of its time to live or if the token is + older than 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to the mount + point of the file to project the token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. If not specified, + the volume defaultMode will be used. This might be + in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: StandaloneStatus defines the observed state of a Splunk Enterprise + standalone instances. + properties: + phase: + description: current phase of the standalone instances + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready standalone instances + format: int32 + type: integer + replicas: + description: number of desired standalone instances + format: int32 + type: integer + resourceRevMap: + additionalProperties: + type: string + description: Resource Revision tracker + type: object + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/splunk.v0.2.1.clusterserviceversion.yaml b/deploy/olm-catalog/splunk/0.2.1/splunk.v0.2.1.clusterserviceversion.yaml new file mode 100644 index 000000000..1888cd640 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/splunk.v0.2.1.clusterserviceversion.yaml @@ -0,0 +1,261 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [{ + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "IndexerCluster", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": { + "replicas": 1 + } + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "LicenseMaster", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": {} + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "SearchHeadCluster", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": { + "replicas": 1 + } + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "Spark", + "metadata": { + "name": "example" + }, + "spec": { + "replicas": 1 + } + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "Standalone", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": {} + }] + capabilities: Basic Install + name: splunk.v0.2.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: ClusterMaster is the Schema for the clustermasters API + kind: ClusterMaster + name: clustermasters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: IndexerCluster + - description: IndexerCluster is the Schema for a Splunk Enterprise indexer cluster + kind: IndexerCluster + name: indexerclusters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: LicenseMaster + - description: LicenseMaster is the Schema for a Splunk Enterprise license master. + kind: LicenseMaster + name: licensemasters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: SearchHeadCluster + - description: SearchHeadCluster is the Schema for a Splunk Enterprise search + head cluster + kind: SearchHeadCluster + name: searchheadclusters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: Spark + - description: Spark is the Schema for a Spark cluster + kind: Spark + name: sparks.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: Standalone + - description: Standalone is the Schema for a Splunk Enterprise standalone instances. + kind: Standalone + name: standalones.enterprise.splunk.com + version: v1beta1 + description: Placeholder description + displayName: Splunk + install: + spec: + deployments: + - name: splunk-operator + spec: + replicas: 1 + selector: + matchLabels: + name: splunk-operator + strategy: {} + template: + metadata: + labels: + name: splunk-operator + spec: + containers: + - env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.annotations['olm.targetNamespaces'] + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: splunk-operator + - name: RELATED_IMAGE_SPLUNK_ENTERPRISE + value: docker.io/splunk/splunk:8.1.1 + - name: RELATED_IMAGE_SPLUNK_SPARK + value: docker.io/splunk/spark:0.0.2 + image: docker.io/splunk/splunk-operator:0.2.1 + imagePullPolicy: IfNotPresent + name: splunk-operator + resources: {} + serviceAccountName: splunk-operator + permissions: + - rules: + - apiGroups: + - "" + resources: + - services + - endpoints + - persistentvolumeclaims + - configmaps + - secrets + - pods + - pods/exec + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - enterprise.splunk.com + resources: + - '*' + verbs: + - '*' + serviceAccountName: splunk-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + maturity: alpha + provider: {} + replaces: splunk.v0.0.0 + version: 0.2.1 diff --git a/deploy/operator.yaml b/deploy/operator.yaml index 148e1db65..701e6ba53 100644 --- a/deploy/operator.yaml +++ b/deploy/operator.yaml @@ -30,6 +30,6 @@ spec: - name: OPERATOR_NAME value: "splunk-operator" - name: RELATED_IMAGE_SPLUNK_ENTERPRISE - value: "docker.io/splunk/splunk:8.1.0" + value: "docker.io/splunk/splunk:8.1.1" - name: RELATED_IMAGE_SPLUNK_SPARK value: "docker.io/splunk/spark:0.0.2" diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index ccc47a374..a9b41904c 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -1,5 +1,22 @@ # Splunk Operator for Kubernetes Change Log +## 0.2.1 Beta() +* This release depends upon changes made concurrently in the Splunk Enterprise container images. You must use the latest splunk/splunk:edge nightly image with it, or alternatively any release version 8.1.0 or later + +* CSPL-529 - Fixed incorrect deletion of Indexer PVCs upon deletion of ClusterMaster + +* CSPL 466 - Fixed infinite reconcile loop of the Operator when an Indexer Cluster is created with peers < SF, RF + +* CSPL-532 - Fixed a race condition where changing the idxc.secret on the global secret object could result in an infinite loop of container restarts + +* Increased code coverage + +* CSPL-534 - Fixed unnecessary pod recycles on scale up/down + +* CSPL-592 - Initiate a pod recycle on change of environment variables of containers + +* CSPL-658 - Fixed incorrect change of Indexer state from Configured to New in the Monitoring Console + ## 0.2.0 Beta (2020-10-15) * This release depends upon changes made concurrently in the Splunk Enterprise container images. You must use the latest splunk/splunk:edge nightly image with it, or alternatively any release version 8.1.0 or later. diff --git a/docs/CustomResources.md b/docs/CustomResources.md index d43b9332a..3506e9741 100644 --- a/docs/CustomResources.md +++ b/docs/CustomResources.md @@ -244,7 +244,8 @@ metadata: name: example spec: replicas: 3 - clusterMasterRef: example-cm + clusterMasterRef: + name: example-cm ``` Note: `clusterMasterRef` is required field in case of IndexerCluster resource since it will be used to connect the IndexerCluster to ClusterMaster resource. diff --git a/docs/Examples.md b/docs/Examples.md index 9463b82b3..604ebfff8 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -79,8 +79,11 @@ spec: EOF ``` -This will automatically configure a cluster master with a single indexer -peer. +This will automatically configure a cluster with RF(replication_factor) number of indexer peers. + +NOTE: Whenever we try to specify `replicas` on IndexerCluster CR less than RF(as set on ClusterMaster), +the operator will always scale the number of peers to either `replication_factor`(in case of single site indexer cluster) +or to `origin` count in `site_replication_factor`(in case of multi-site indexer cluster). ``` $ kubectl get pods @@ -88,6 +91,8 @@ NAME READY STATUS RESTARTS AGE splunk-cm-cluster-master-0 1/1 Running 0 29s splunk-default-monitoring-console-0 1/1 Running 0 15s splunk-example-indexer-0 1/1 Running 0 29s +splunk-example-indexer-1 1/1 Running 0 29s +splunk-example-indexer-2 1/1 Running 0 29s splunk-operator-7c5599546c-wt4xl 1/1 Running 0 14h ``` Notes: @@ -429,32 +434,12 @@ installation. defaultsUrl: "http://myco.com/splunk/generic.yml,/mnt/defaults/apps.yml" ``` -Suppose you want to just override the admin password for your deployment -(instead of using the automatically generated one), you can also specify -inline overrides using the `defaults` parameter: - -```yaml -apiVersion: enterprise.splunk.com/v1beta1 -kind: Standalone -metadata: - name: example - finalizers: - - enterprise.splunk.com/delete-pvc -spec: - volumes: - - name: defaults - configMap: - name: splunk-defaults - defaultsUrl: /mnt/defaults/default.yml - defaults: |- - splunk: - password: helloworld456 -``` - -*Setting passwords in your CRDs may be OK for testing, but it is discouraged.* - Inline `defaults` are always processed last, after any `defaultsUrl` files. +Any password management related configuration via `defaults` and `defaultsUrl` +has been disabled. Please review [`PasswordManagement.md`](PasswordManagement.md) +and [`Managing global kubernetes secret object`](#managing-global-kubernetes-secret-object) +for more details. ## Installing Splunk Apps @@ -638,7 +623,7 @@ pass4SymmKey for authentication. There are two ways to configure `pass4Symmkey` with an External LM: #### Approach 1 -- Setup the desired plain-text [`pass4Symmkey`](PasswordManagement.md#pass4symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). +- Setup the desired plain-text [`pass4Symmkey`](PasswordManagement.md#pass4Symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). - Setup the same plain-text `pass4SymmKey` in the `[general]` section of your LM's `server.conf` file. #### Approach 2 @@ -657,7 +642,7 @@ There are two ways to configure `pass4Symmkey` with an External LM: ``` $SPLUNK_HOME/bin/splunk show-decrypted --value '$7$Sw0A+wvJdTztMcA2Ge7u435XmpTzPqyaq49kUZqn0yfAgwFpwrArM2JjWJ3mUyf/FyHAnCZkE/U=' ``` -- Setup the above decrypted plain-text [`pass4Symmkey`](PasswordManagement.md#pass4symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) +- Setup the above decrypted plain-text [`pass4Symmkey`](PasswordManagement.md#pass4Symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) ### Configuring license_master_url: @@ -707,7 +692,7 @@ operator & the external indexer cluster, and configure the `splunk.cluster_maste There are two ways to configure `IDXC pass4Symmkey` with an External Indexer Cluster: #### Approach 1 -- Setup the desired plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). +- Setup the desired plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4Symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). - Setup the same plain-text `IDXC pass4SymmKey` in the `[clustering]` section of your cluster master's and indexers' `server.conf` file. #### Approach 2 @@ -726,7 +711,7 @@ There are two ways to configure `IDXC pass4Symmkey` with an External Indexer Clu ``` $SPLUNK_HOME/bin/splunk show-decrypted --value '$7$Sw0A+wvJdTztMcA2Ge7u435XmpTzPqyaq49kUZqn0yfAgwFpwrArM2JjWJ3mUyf/FyHAnCZkE/U=' ``` -- Setup the above decrypted plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) +- Setup the above decrypted plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4Symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) ### Configuring cluster_master_url: @@ -772,8 +757,8 @@ Use the kubectl command to create the global kubernetes secret object: 2. Gather the password values for the secret tokens you want to configure. To see all available secret tokens defined for the global kubernetes secret object, review [password management](PasswordManagement.md#splunk-secret-tokens-in-the-global-secret-object) 3. Create a kubernetes secret object referencing the namespace. Example: splunk-`-secret. -In the example below, we are creating the global kubernetes secret object, defining the default administrator and pass4symmkey tokens, and passing in the values. -`kubectl create secret generic splunk--secret --from-literal='password=' --from-literal='pass4symmkey='` +In the example below, we are creating the global kubernetes secret object, defining the default administrator and pass4Symmkey tokens, and passing in the values. +`kubectl create secret generic splunk--secret --from-literal='password=' --from-literal='pass4Symmkey='` ### Reading global kubernetes secret object diff --git a/docs/Install.md b/docs/Install.md index eb03fbbed..f3469c4b7 100644 --- a/docs/Install.md +++ b/docs/Install.md @@ -11,7 +11,7 @@ Splunk Operator (as described below), please download a local copy of the installation YAML and open it in your favorite editor. ``` -wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-install.yaml +wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-install.yaml ``` @@ -23,14 +23,14 @@ installed by regular users within their own namespaces. If you are not an administrator, you can have someone else create these objects for you by running ``` -kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-crds.yaml +kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-crds.yaml ``` You should then be able download and use the following YAML to install the operator within your own namespace: ``` -wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-noadmin.yaml +wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-noadmin.yaml kubectl config set-context --current --namespace= kubectl apply -f splunk-operator.yaml ``` @@ -43,7 +43,7 @@ objects for all the namespaces of your cluster, you can use the alternative cluster scope installation YAML: ``` -wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-cluster.yaml +wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-cluster.yaml ``` When running at cluster scope, you will need to bind the @@ -78,7 +78,7 @@ EOF ## Private Registries -*Note: The `splunk/splunk:8.1.0` image is rather large, so we strongly +*Note: The `splunk/splunk:8.1.0` image (or later) is rather large, so we strongly recommend copying this to a private registry or directly onto your Kubernetes workers as per the [Required Images Documentation](Images.md), and following these instructions before creating any large Splunk deployments.* @@ -92,7 +92,7 @@ it to a private registry, you will need to edit the image parameter in the image: splunk/splunk-operator ``` -If you are using a private registry for the `splunk/splunk:8.1.0` and +If you are using a private registry for the `splunk/splunk:8.1.0` (or later) and `splunk/spark` (used by DFS) images, you should modify the `RELATED_IMAGE_SPLUNK_ENTERPRISE` and `RELATED_IMAGE_SPLUNK_SPARK` environment variables in `splunk-operator.yaml` to point @@ -100,7 +100,7 @@ to the appropriate locations. ```yaml - name: RELATED_IMAGE_SPLUNK_ENTERPRISE - value: "splunk/splunk:8.1.0" + value: "splunk/splunk:8.1.0" (or later) - name: RELATED_IMAGE_SPLUNK_SPARK value: "splunk/spark" ``` diff --git a/docs/PasswordManagement.md b/docs/PasswordManagement.md index adde67004..7a64b638d 100644 --- a/docs/PasswordManagement.md +++ b/docs/PasswordManagement.md @@ -5,9 +5,9 @@ - [Splunk Secret Tokens in the global secret object](#splunk-secret-tokens-in-the-global-secret-object) - [HEC Token](#hec-token) - [Default administrator password](#default-administrator-password) - - [Pass4symmkey](#pass4symmkey) - - [IDXC Pass4symmkey](#idxc-pass4symmkey) - - [SHC Pass4symmkey](#shc-pass4symmkey) + - [pass4Symmkey](#pass4Symmkey) + - [IDXC pass4Symmkey](#idxc-pass4Symmkey) + - [SHC pass4Symmkey](#shc-pass4Symmkey) - [Information for Splunk Enterprise administrator](#information-for-splunk-enterprise-administrator) - [Secrets on Docker Splunk](#secrets-on-docker-splunk) @@ -42,15 +42,15 @@ The configurable Splunk Secret Tokens include: **Key name in global kubernetes secret object**: `password` **Description**: password refers to the default administrator password for Splunk. -#### Pass4symmkey -**Key name in global kubernetes secret object**: `pass4symmkey` -**Description**: pass4symmkey is an authentication token for inter-communication within Splunk Enterprise. +#### pass4Symmkey +**Key name in global kubernetes secret object**: `pass4Symmkey` +**Description**: pass4Symmkey is an authentication token for inter-communication within Splunk Enterprise. -#### IDXC Pass4symmkey +#### IDXC pass4Symmkey **Key name in global kubernetes secret object**: `idxc.secret` **Description**: idxc.secret is an authentication token for inter-communication specifically for indexer clustering in Splunk Enterprise. -#### SHC Pass4symmkey +#### SHC pass4Symmkey **Key name in global kubernetes secret object**: `shc.secret` **Description**: shc.secret is an authentication token for inter-communication specifically for search head clustering in Splunk Enterprise. diff --git a/docs/README.md b/docs/README.md index 4a3b331a0..61ad4df70 100644 --- a/docs/README.md +++ b/docs/README.md @@ -86,7 +86,7 @@ information. Most users can install and start the Splunk Operator by just running ``` -kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-install.yaml +kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-install.yaml ``` Users of Red Hat OpenShift should read the additional @@ -97,7 +97,7 @@ special considerations, including the use of private image registries, installation at cluster scope, and installing as a regular user (who is not a Kubernetes cluster administrator). -*Note: The `splunk/splunk:8.1.0` image is rather large, so we strongly +*Note: The `splunk/splunk:8.1.0` image (or later) is rather large, so we strongly recommend copying this to a private registry or directly onto your Kubernetes workers as per the [Required Images Documentation](Images.md), and following the [Advanced Installation Instructions](Install.md), @@ -120,7 +120,7 @@ kubectl delete searchheadclusters --all kubectl delete clustermasters --all kubectl delete indexerclusters --all kubectl delete spark --all -kubectl delete -f https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-install.yaml +kubectl delete -f https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-install.yaml ``` @@ -157,7 +157,7 @@ splunk-default-monitoring-console-0 1/1 Running 0 30s splunk-s1-standalone-0 1/1 Running 0 45s ``` -The passwords for the instance are generated automatically. To review the passwords, please refer to the [Reading global kubernetes secret object](#reading-global-kubernetes-secret-object) instructions. +The passwords for the instance are generated automatically. To review the passwords, please refer to the [Reading global kubernetes secret object](Examples.md#reading-global-kubernetes-secret-object) instructions. *Note: if your shell prints a `%` at the end, leave that out when you copy the output.* diff --git a/pkg/apis/enterprise/v1beta1/indexercluster_types.go b/pkg/apis/enterprise/v1beta1/indexercluster_types.go index 5969a8706..702af40f8 100644 --- a/pkg/apis/enterprise/v1beta1/indexercluster_types.go +++ b/pkg/apis/enterprise/v1beta1/indexercluster_types.go @@ -89,9 +89,15 @@ type IndexerClusterStatus struct { // Indicates resource version of namespace scoped secret NamespaceSecretResourceVersion string `json:"namespace_scoped_secret_resource_version"` + // Holds secrets whose IDXC password has changed + IdxcPasswordChangedSecrets map[string]bool `json:"IdxcPasswordChangedSecrets"` + // Indicates if the cluster is in maintenance mode. MaintenanceMode bool `json:"maintenance_mode"` + // Indicates if we need to recheck the revision update on pods + SkipRecheckUpdate bool `json:"skip_recheck_update"` + // status of each indexer cluster peer Peers []IndexerClusterMemberStatus `json:"peers"` } diff --git a/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go b/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go index ea3dd3f21..04dd455f9 100644 --- a/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go +++ b/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go @@ -109,6 +109,9 @@ type SearchHeadClusterStatus struct { // Indicates resource version of namespace scoped secret NamespaceSecretResourceVersion string `json:"namespace_scoped_secret_resource_version"` + // Indicates if we need to recheck the revision update on pods + SkipRecheckUpdate bool `json:"skip_recheck_update"` + // status of each search head cluster member Members []SearchHeadClusterMemberStatus `json:"members"` } diff --git a/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go b/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go index d17e1f7ca..643175b41 100644 --- a/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go @@ -341,6 +341,13 @@ func (in *IndexerClusterStatus) DeepCopyInto(out *IndexerClusterStatus) { *out = make([]bool, len(*in)) copy(*out, *in) } + if in.IdxcPasswordChangedSecrets != nil { + in, out := &in.IdxcPasswordChangedSecrets, &out.IdxcPasswordChangedSecrets + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Peers != nil { in, out := &in.Peers, &out.Peers *out = make([]IndexerClusterMemberStatus, len(*in)) diff --git a/pkg/splunk/client/enterprise.go b/pkg/splunk/client/enterprise.go index 29d72200f..a61e5e277 100644 --- a/pkg/splunk/client/enterprise.go +++ b/pkg/splunk/client/enterprise.go @@ -629,10 +629,7 @@ func (c *SplunkClient) DecommissionIndexerClusterPeer(enforceCounts bool) error } // BundlePush pushes the CM master apps bundle to all the indexer peers -func (c *SplunkClient) BundlePush(ignoreIdenticalBundle bool, mock bool) error { - if mock { - return nil - } +func (c *SplunkClient) BundlePush(ignoreIdenticalBundle bool) error { endpoint := fmt.Sprintf("%s/services/cluster/master/control/default/apply", c.ManagementURI) reqBody := fmt.Sprintf("&ignore_identical_bundle=%t", ignoreIdenticalBundle) @@ -894,7 +891,9 @@ func (c *SplunkClient) UpdateMonitoringConsoleApp() error { //ClusterInfo is the struct for checking ClusterInfo type ClusterInfo struct { - MultiSite string `json:"multisite"` + MultiSite string `json:"multisite"` + ReplicationFactor int32 `json:"replication_factor"` + SiteReplicationFactor string `json:"site_replication_factor,omitempty"` } // GetClusterInfo queries the cluster about multi-site or single-site. diff --git a/pkg/splunk/client/enterprise_test.go b/pkg/splunk/client/enterprise_test.go index 9f6fc8ef6..42903e887 100644 --- a/pkg/splunk/client/enterprise_test.go +++ b/pkg/splunk/client/enterprise_test.go @@ -179,7 +179,7 @@ func TestBundlePush(t *testing.T) { wantRequest, _ := http.NewRequest("POST", "https://localhost:8089/services/cluster/master/control/default/apply", body) test := func(c SplunkClient) error { - return c.BundlePush(true, false) + return c.BundlePush(true) } splunkClientTester(t, "TestBundlePush", 200, "", wantRequest, test) } diff --git a/pkg/splunk/common/messages.go b/pkg/splunk/common/messages.go index 86ab1d3cb..39df3be7e 100644 --- a/pkg/splunk/common/messages.go +++ b/pkg/splunk/common/messages.go @@ -21,6 +21,15 @@ const ( // PodNotFoundError indicates Pod is not found PodNotFoundError = "Couldn't find pod" + // PodSecretNotFoundError indicates that a mounted secret wasn't found on the Pod + PodSecretNotFoundError = "Couldn't find secret in Pod %s" + // SecretNotFoundError indicates Pod is not found SecretNotFoundError = "Couldn't find secret" + + // SecretTokenNotRetrievable indicates missing secret token in pod secret + SecretTokenNotRetrievable = "Couldn't retrieve %s from secret data" + + // EmptyClusterMasterRef indicates an empty cluster master reference + EmptyClusterMasterRef = "Empty cluster master reference" ) diff --git a/pkg/splunk/common/names.go b/pkg/splunk/common/names.go index e71621c5b..3865760bb 100644 --- a/pkg/splunk/common/names.go +++ b/pkg/splunk/common/names.go @@ -34,6 +34,9 @@ const ( // MinimumVersionedSecrets holds the minimum number of secrets to be held per version MinimumVersionedSecrets = 3 + + // IdxcSecret represents indexer cluster pass4Symmkey secret token + IdxcSecret = "idxc_secret" ) // GetVersionedSecretName returns a versioned secret name diff --git a/pkg/splunk/controller/configmap_test.go b/pkg/splunk/controller/configmap_test.go index 4cdaf981a..3385ca5e0 100644 --- a/pkg/splunk/controller/configmap_test.go +++ b/pkg/splunk/controller/configmap_test.go @@ -19,6 +19,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" spltest "github.com/splunk/splunk-operator/pkg/splunk/test" ) @@ -41,3 +42,62 @@ func TestApplyConfigMap(t *testing.T) { } spltest.ReconcileTester(t, "TestApplyConfigMap", ¤t, revised, createCalls, updateCalls, reconcile, false) } + +func TestGetConfigMap(t *testing.T) { + current := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "defaults", + Namespace: "test", + }, + } + + client := spltest.NewMockClient() + namespacedName := types.NamespacedName{Namespace: current.GetNamespace(), Name: current.GetName()} + + _, err := GetConfigMap(client, namespacedName) + if err == nil { + t.Errorf("Should return an error, when the configMap doesn't exist") + } + + _, err = ApplyConfigMap(client, ¤t) + if err != nil { + t.Errorf("Failed to create the configMap. Error: %s", err.Error()) + } + + _, err = GetConfigMap(client, namespacedName) + if err != nil { + t.Errorf("Should not return an error, when the configMap exists") + } +} + +func TestGetConfigMapResourceVersion(t *testing.T) { + current := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "defaults", + Namespace: "test", + }, + } + + client := spltest.NewMockClient() + namespacedName := types.NamespacedName{Namespace: current.GetNamespace(), Name: current.GetName()} + + _, err := GetConfigMap(client, namespacedName) + if err == nil { + t.Errorf("Should return an error, when the configMap doesn't exist") + } + + _, err = GetConfigMapResourceVersion(client, namespacedName) + if err == nil { + t.Errorf("Should return an error, when the configMap doesn't exist") + } + + _, err = ApplyConfigMap(client, ¤t) + if err != nil { + t.Errorf("Failed to create the configMap. Error: %s", err.Error()) + } + + _, err = GetConfigMapResourceVersion(client, namespacedName) + if err != nil { + t.Errorf("Should not return an error, when the configMap exists") + } +} diff --git a/pkg/splunk/controller/statefulset.go b/pkg/splunk/controller/statefulset.go index 721367ba8..9bdd5df17 100644 --- a/pkg/splunk/controller/statefulset.go +++ b/pkg/splunk/controller/statefulset.go @@ -34,8 +34,9 @@ type DefaultStatefulSetPodManager struct{} // Update for DefaultStatefulSetPodManager handles all updates for a statefulset of standard pods func (mgr *DefaultStatefulSetPodManager) Update(client splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, desiredReplicas int32) (splcommon.Phase, error) { phase, err := ApplyStatefulSet(client, statefulSet) + skipRecheckUpdate := false if err == nil && phase == splcommon.PhaseReady { - phase, err = UpdateStatefulSetPods(client, statefulSet, mgr, desiredReplicas) + phase, err = UpdateStatefulSetPods(client, statefulSet, mgr, desiredReplicas, &skipRecheckUpdate) } return phase, err } @@ -85,8 +86,105 @@ func ApplyStatefulSet(c splcommon.ControllerClient, revised *appsv1.StatefulSet) return splcommon.PhaseReady, nil } +// UpdatePodRevisionHash updates the controller-revision-hash label on pods. +func UpdatePodRevisionHash(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32) error { + scopedLog := log.WithName("updatePodRevisionHash").WithValues( + "name", statefulSet.GetObjectMeta().GetName(), + "namespace", statefulSet.GetObjectMeta().GetNamespace()) + + namespacedName := types.NamespacedName{Namespace: statefulSet.GetNamespace(), Name: statefulSet.GetName()} + var current appsv1.StatefulSet + + err := c.Get(context.TODO(), namespacedName, ¤t) + if err != nil { + scopedLog.Error(err, "Unable to Get statefulset", "statefulset", statefulSet.GetName()) + return err + } + + for n := readyReplicas - 1; n >= 0; n-- { + // get Pod + podName := fmt.Sprintf("%s-%d", current.GetName(), n) + namespacedName := types.NamespacedName{Namespace: current.GetNamespace(), Name: podName} + var pod corev1.Pod + err := c.Get(context.TODO(), namespacedName, &pod) + if err != nil { + scopedLog.Error(err, "Unable to find Pod", "podName", podName) + return err + } + if pod.Status.Phase != corev1.PodRunning || len(pod.Status.ContainerStatuses) == 0 || pod.Status.ContainerStatuses[0].Ready != true { + scopedLog.Error(err, "Waiting for Pod to become ready", "podName", podName) + return err + } + + labels := pod.GetLabels() + revisionHash := labels["controller-revision-hash"] + // update pod controller revision hash + if current.Status.UpdateRevision != "" && current.Status.UpdateRevision != revisionHash { + // update controller-revision-hash label for pod with statefulset update revision + labels["controller-revision-hash"] = current.Status.UpdateRevision + pod.SetLabels(labels) + err = splutil.UpdateResource(c, &pod) + if err != nil { + scopedLog.Error(err, "Unable to update controller-revision-hash label for pod", "podName", podName) + return err + } + scopedLog.Info("Updated controller-revision-hash label for pod", "podName", podName, "revision", current.Status.UpdateRevision) + } + } + + scopedLog.Info("Updated controller-revision-hash label for all pods") + return nil +} + +// isRevisionUpdateSuccessful checks if current revision is different from updated revision +func isRevisionUpdateSuccessful(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet) bool { + scopedLog := log.WithName("isRevisionUpdateSuccessful").WithValues( + "name", statefulSet.GetObjectMeta().GetName(), + "namespace", statefulSet.GetObjectMeta().GetNamespace()) + + namespacedName := types.NamespacedName{Namespace: statefulSet.GetNamespace(), Name: statefulSet.GetName()} + var current appsv1.StatefulSet + + err := c.Get(context.TODO(), namespacedName, ¤t) + if err != nil { + scopedLog.Error(err, "Unable to Get statefulset", "statefulset", statefulSet.GetName()) + return false + } + + // Check if current revision is different from update revision + if current.Status.CurrentRevision == current.Status.UpdateRevision { + scopedLog.Error(err, "Statefulset UpdateRevision not updated yet") + return false + } + + return true +} + +// checkAndUpdatePodRevision updates the pod revision hash labels on pods if statefulset update was successful +func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32, skipRecheckUpdate *bool) error { + scopedLog := log.WithName("checkAndUpdatePodRevision").WithValues( + "name", statefulSet.GetObjectMeta().GetName(), + "namespace", statefulSet.GetObjectMeta().GetNamespace()) + var err error + if !isRevisionUpdateSuccessful(c, statefulSet) { + scopedLog.Error(err, "Statefulset not updated yet") + *skipRecheckUpdate = false + return err + } + // update the controller-revision-hash label on pods to + // to avoid unnecessary recycle of pods + err = UpdatePodRevisionHash(c, statefulSet, readyReplicas) + if err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + *skipRecheckUpdate = false + return err + } + *skipRecheckUpdate = true + return nil +} + // UpdateStatefulSetPods manages scaling and config updates for StatefulSets -func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, mgr splcommon.StatefulSetPodManager, desiredReplicas int32) (splcommon.Phase, error) { +func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, mgr splcommon.StatefulSetPodManager, desiredReplicas int32, skipRecheckUpdate *bool) (splcommon.Phase, error) { scopedLog := log.WithName("UpdateStatefulSetPods").WithValues( "name", statefulSet.GetObjectMeta().GetName(), "namespace", statefulSet.GetObjectMeta().GetNamespace()) @@ -97,11 +195,25 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta if readyReplicas < replicas { scopedLog.Info("Waiting for pods to become ready") if readyReplicas > 0 { + if !*skipRecheckUpdate { + err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + } return splcommon.PhaseScalingUp, nil } return splcommon.PhasePending, nil } else if readyReplicas > replicas { scopedLog.Info("Waiting for scale down to complete") + if !*skipRecheckUpdate { + err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + } return splcommon.PhaseScalingDown, nil } @@ -112,7 +224,22 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta // scale up StatefulSet to match desiredReplicas scopedLog.Info("Scaling replicas up", "replicas", desiredReplicas) *statefulSet.Spec.Replicas = desiredReplicas - return splcommon.PhaseScalingUp, splutil.UpdateResource(c, statefulSet) + err := splutil.UpdateResource(c, statefulSet) + if err != nil { + scopedLog.Error(err, "Unable to update statefulset") + return splcommon.PhaseError, err + } + + // Check if the revision was updated successfully for Statefulset. + // It so can happen that it may take few seconds for the update to be + // reflected in the resource. In that case, just return from here and + // check the status back in the next reconcile loop. + err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + return splcommon.PhaseScalingUp, err } // check for scaling down @@ -139,6 +266,16 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta return splcommon.PhaseError, err } + // Check if the revision was updated successfully for Statefulset. + // It so can happen that it may take few seconds for the update to be + // reflected in the resource. In that case, just return from here and + // check the status back in the next reconcile loop. + err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + // delete PVCs used by the pod so that a future scale up will have clean state for _, vol := range statefulSet.Spec.VolumeClaimTemplates { namespacedName := types.NamespacedName{ diff --git a/pkg/splunk/controller/statefulset_test.go b/pkg/splunk/controller/statefulset_test.go index e1c0c29e6..f97a80820 100644 --- a/pkg/splunk/controller/statefulset_test.go +++ b/pkg/splunk/controller/statefulset_test.go @@ -18,13 +18,16 @@ import ( "testing" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" spltest "github.com/splunk/splunk-operator/pkg/splunk/test" splutil "github.com/splunk/splunk-operator/pkg/splunk/util" enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" + splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" ) func TestApplyStatefulSet(t *testing.T) { @@ -57,6 +60,181 @@ func TestDefaultStatefulSetPodManager(t *testing.T) { spltest.PodManagerTester(t, method, &mgr) } +func updateStatefulSetPodsTester(t *testing.T, mgr splcommon.StatefulSetPodManager, statefulSet *appsv1.StatefulSet, desiredReplicas int32, initObjects ...runtime.Object) (splcommon.Phase, error) { + // initialize client + c := spltest.NewMockClient() + c.AddObjects(initObjects) + skipRecheckUpdate := false + phase, err := UpdateStatefulSetPods(c, statefulSet, mgr, desiredReplicas, &skipRecheckUpdate) + return phase, err +} + +func TestUpdateStatefulSetPods(t *testing.T) { + mgr := DefaultStatefulSetPodManager{} + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + + var phase splcommon.Phase + phase, err := updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err != nil && phase != splcommon.PhaseUpdating { + t.Errorf("UpdateStatefulSetPods should not have returned error=%s with phase=%s", err, phase) + } + + // Check the scenario where UpdatePodRevisionHash should return error when Pod is not added to client. + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 2 /*desiredReplicas*/, statefulSet) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) + } + + replicas = 3 + statefulSet.Status.ReadyReplicas = 3 + statefulSet.Spec.Replicas = &replicas + // Check the scenario where UpdatePodRevisionHash should return error when Pod is not added to client. + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) + } + + // readyReplicas < replicas + replicas = 3 + statefulSet.Status.ReadyReplicas = 2 + statefulSet.Spec.Replicas = &replicas + // Check the scenario where UpdatePodRevisionHash should return error when readyReplicas < replicas and Pod is not found + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) + } + + // CurrentRevision = UpdateRevision + statefulSet.Status.CurrentRevision = "v1" + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) + } + + // readyReplicas > replicas + replicas = 2 + statefulSet.Status.ReadyReplicas = 3 + statefulSet.Spec.Replicas = &replicas + statefulSet.Status.CurrentRevision = "" + // Check the scenario where UpdatePodRevisionHash should return error when readyReplicas > replicas and Pod is not found + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) + } + + // CurrentRevision = UpdateRevision + statefulSet.Status.CurrentRevision = "v1" + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) + } + + // Check the scenario where UpdatePodRevisionHash should return error when statefulset is not added to client. + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 2 /*desiredReplicas*/, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) + } + +} + +func updatePodRevisionHashTester(t *testing.T, statefulSet *appsv1.StatefulSet, replicas int32, initObjects ...runtime.Object) error { + c := spltest.NewMockClient() + c.AddObjects(initObjects) + return UpdatePodRevisionHash(c, statefulSet, replicas) +} + +func TestUpdatePodRevisionHash(t *testing.T) { + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + + err := updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) + if err != nil { + t.Errorf("UpdatePodRevisionHash should not have returned error=%s", err) + } + + // Test the negative case where phase != corev1.PodRunning + pod.Status.Phase = corev1.PodPending + err = updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) + if err != nil { + t.Errorf("UpdatePodRevisionHash should not have returned error=%s", err) + } + + // Test invalid pod name splunk-stack1-1 + pod.Status.Phase = corev1.PodRunning + replicas = 2 + err = updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) + if err == nil { + t.Errorf("UpdatePodRevisionHash should have returned error") + } +} + func TestSetStatefulSetOwnerRef(t *testing.T) { cr := enterprisev1.Standalone{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/splunk/controller/util.go b/pkg/splunk/controller/util.go index 85db31534..03595e94f 100644 --- a/pkg/splunk/controller/util.go +++ b/pkg/splunk/controller/util.go @@ -16,6 +16,7 @@ package controller import ( "reflect" + "strings" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -166,6 +167,22 @@ func MergePodSpecUpdates(current *corev1.PodSpec, revised *corev1.PodSpec, name current.Containers[idx].Resources = revised.Containers[idx].Resources result = true } + + // check Env + // Skip this check for the Monitoring Console + // This is temporary until the MC has it's own CR to control the MC pod env. + skipForMC := false + if strings.Contains(name, "monitoring-console") { + scopedLog.Info("Ignoring Pod Container Envs differences for MC pods", "name", name) + skipForMC = true + } + if !skipForMC && splcommon.CompareEnvs(current.Containers[idx].Env, revised.Containers[idx].Env) { + scopedLog.Info("Pod Container Envs differ", + "current", current.Containers[idx].Env, + "revised", revised.Containers[idx].Env) + current.Containers[idx].Env = revised.Containers[idx].Env + result = true + } } } diff --git a/pkg/splunk/controller/util_test.go b/pkg/splunk/controller/util_test.go index 66b47fe06..a736818a3 100644 --- a/pkg/splunk/controller/util_test.go +++ b/pkg/splunk/controller/util_test.go @@ -121,6 +121,18 @@ func TestMergePodUpdates(t *testing.T) { matcher = func() bool { return reflect.DeepEqual(current.Spec.Containers, revised.Spec.Containers) } podUpdateTester("Container Resources") + // check pod env update + current.Spec.Containers[0].Env = append(current.Spec.Containers[0].Env, corev1.EnvVar{ + Name: "SPLUNK_DEFAULTS_URL", + Value: "defaults1.yaml", + }) + revised.Spec.Containers[0].Env = append(revised.Spec.Containers[0].Env, corev1.EnvVar{ + Name: "SPLUNK_DEFAULTS_URL", + Value: "defaults2.yaml", + }) + matcher = func() bool { return reflect.DeepEqual(current.Spec.Containers[0].Env, revised.Spec.Containers[0].Env) } + podUpdateTester("Pod Env changed") + // check container removed revised.Spec.Containers = []corev1.Container{} matcher = func() bool { return reflect.DeepEqual(current.Spec.Containers, revised.Spec.Containers) } diff --git a/pkg/splunk/enterprise/clustermaster.go b/pkg/splunk/enterprise/clustermaster.go index 9fea8bad7..3bc5199c0 100644 --- a/pkg/splunk/enterprise/clustermaster.go +++ b/pkg/splunk/enterprise/clustermaster.go @@ -58,9 +58,6 @@ func ApplyClusterMaster(client splcommon.ControllerClient, cr *enterprisev1.Clus if !reflect.DeepEqual(cr.Status.SmartStore, cr.Spec.SmartStore) || AreRemoteVolumeKeysChanged(client, cr, SplunkClusterMaster, &cr.Spec.SmartStore, cr.Status.ResourceRevMap, &err) { - if err != nil { - return result, err - } _, configMapDataChanged, err := ApplySmartstoreConfigMap(client, cr, &cr.Spec.SmartStore) if err != nil { return result, err @@ -75,6 +72,11 @@ func ApplyClusterMaster(client splcommon.ControllerClient, cr *enterprisev1.Clus cr.Status.SmartStore = cr.Spec.SmartStore } + // This is to take care of case where AreRemoteVolumeKeysChanged returns an error if it returns false. + if err != nil { + return result, err + } + defer func() { err = client.Status().Update(context.TODO(), cr) if err != nil { @@ -167,6 +169,9 @@ func getClusterMasterStatefulSet(client splcommon.ControllerClient, cr *enterpri var extraEnvVar []corev1.EnvVar ss, err := getSplunkStatefulSet(client, cr, &cr.Spec.CommonSplunkSpec, SplunkClusterMaster, 1, extraEnvVar) + if err != nil { + return ss, err + } _, exists := getSmartstoreConfigMap(client, cr, SplunkClusterMaster) if exists { @@ -186,9 +191,7 @@ func CheckIfsmartstoreConfigMapUpdatedToPod(c splcommon.ControllerClient, cr *en command := fmt.Sprintf("cat /mnt/splunk-operator/local/%s", configToken) stdOut, stdErr, err := splutil.PodExecCommand(c, cmPodName, cr.GetNamespace(), []string{"/bin/sh"}, command, false, false) if err != nil || stdErr != "" { - if cr.Spec.Mock == false { - return fmt.Errorf("Failed to check config token value on pod. stdout=%s, stderror=%s, error=%v", stdOut, stdErr, err) - } + return fmt.Errorf("Failed to check config token value on pod. stdout=%s, stderror=%s, error=%v", stdOut, stdErr, err) } configMap, exists := getSmartstoreConfigMap(c, cr, SplunkClusterMaster) @@ -259,7 +262,7 @@ func PushMasterAppsBundle(c splcommon.ControllerClient, cr *enterprisev1.Cluster return fmt.Errorf("Could not find admin password while trying to push the master apps bundle") } - scopedLog.Info("Issueing REST call to push master aps bundle") + scopedLog.Info("Issuing REST call to push master aps bundle") masterIdxcName := cr.GetName() fqdnName := splcommon.GetServiceFQDN(cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, masterIdxcName, false)) @@ -267,5 +270,5 @@ func PushMasterAppsBundle(c splcommon.ControllerClient, cr *enterprisev1.Cluster // Get a Splunk client to execute the REST call splunkClient := splclient.NewSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", string(adminPwd)) - return splunkClient.BundlePush(true, cr.Spec.Mock) + return splunkClient.BundlePush(true) } diff --git a/pkg/splunk/enterprise/clustermaster_test.go b/pkg/splunk/enterprise/clustermaster_test.go index 94a3259a1..2a8d377e7 100644 --- a/pkg/splunk/enterprise/clustermaster_test.go +++ b/pkg/splunk/enterprise/clustermaster_test.go @@ -16,11 +16,13 @@ package enterprise import ( "fmt" + "strings" "testing" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" @@ -139,6 +141,15 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { {MetaName: "*v1.ConfigMap-test-splunk-stack1-clustermaster-smartstore"}, {MetaName: "*v1.ConfigMap-test-splunk-stack1-clustermaster-smartstore"}, {MetaName: "*v1.StatefulSet-test-splunk-stack1-cluster-master"}, + {MetaName: "*v1.Pod-test-splunk-stack1-cluster-master-0"}, + {MetaName: "*v1.Secret-test-splunk-test-secret"}, + {MetaName: "*v1.Secret-test-splunk-test-monitoring-console-secret-v1"}, + {MetaName: "*v1.Service-test-splunk-test-monitoring-console-service"}, + {MetaName: "*v1.Service-test-splunk-test-monitoring-console-headless"}, + {MetaName: "*v1.ConfigMap-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.ConfigMap-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, } labels := map[string]string{ "app.kubernetes.io/component": "versionedSecrets", @@ -150,7 +161,7 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { } listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} - createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[6], funcCalls[7], funcCalls[9], funcCalls[12]}, "List": {listmockCall[0]}, "Update": {funcCalls[0], funcCalls[3]}} + createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[6], funcCalls[7], funcCalls[9], funcCalls[15], funcCalls[16], funcCalls[17], funcCalls[18], funcCalls[20]}, "List": {listmockCall[0], listmockCall[0], listmockCall[0]}, "Update": {funcCalls[0], funcCalls[3], funcCalls[20]}} updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[5], funcCalls[5], funcCalls[6], funcCalls[7], funcCalls[8], funcCalls[9], funcCalls[11], funcCalls[11], funcCalls[12]}, "Update": {funcCalls[10], funcCalls[12]}, "List": {listmockCall[0]}} current := enterprisev1.ClusterMaster{ @@ -188,10 +199,11 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { }, } client := spltest.NewMockClient() - // Without S3 keys, ApplyStandalone should fail + + // Without S3 keys, ApplyClusterMaster should fail _, err := ApplyClusterMaster(client, ¤t) if err == nil { - t.Errorf("ApplyIndexerCluster should fail without S3 secrets configured") + t.Errorf("ApplyClusterMaster should fail without S3 secrets configured") } // Create namespace scoped secret @@ -221,7 +233,59 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { _, err := ApplyClusterMaster(c, cr.(*enterprisev1.ClusterMaster)) return err } - spltest.ReconcileTesterWithoutRedundantCheck(t, "TestApplyClusterMasterWithSmartstore", ¤t, revised, createCalls, updateCalls, reconcile, true, secret, &smartstoreConfigMap) + + client.AddObject(&smartstoreConfigMap) + ss, _ := getClusterMasterStatefulSet(client, ¤t) + ss.Status.ReadyReplicas = 1 + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-cluster-master-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v1", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + + spltest.ReconcileTesterWithoutRedundantCheck(t, "TestApplyClusterMasterWithSmartstore-0", ¤t, revised, createCalls, updateCalls, reconcile, true, secret, &smartstoreConfigMap, ss, pod) + + current.Status.BundlePushTracker.NeedToPushMasterApps = true + if _, err = ApplyClusterMaster(client, ¤t); err != nil { + t.Errorf("ApplyClusterMaster() should not have returned error") + } + + current.Spec.CommonSplunkSpec.EtcStorage = "-abcd" + if _, err := ApplyClusterMaster(client, ¤t); err == nil { + t.Errorf("ApplyClusterMaster() should have returned error") + } + + var replicas int32 = 3 + current.Spec.CommonSplunkSpec.EtcStorage = "" + ss.Status.ReadyReplicas = 3 + ss.Spec.Replicas = &replicas + ss.Spec.Template.Spec.Containers[0].Image = "splunk/splunk" + client.AddObject(ss) + if result, err := ApplyClusterMaster(client, ¤t); err == nil && !result.Requeue { + t.Errorf("ApplyClusterMaster() should have returned error or result.requeue should have been false") + } + + ss.Status.ReadyReplicas = 1 + *ss.Spec.Replicas = ss.Status.ReadyReplicas + objects := []runtime.Object{ss, pod} + client.AddObjects(objects) + current.Spec.CommonSplunkSpec.Mock = false + + // This should fail at ApplyMonitoringConsole + if _, err := ApplyClusterMaster(client, ¤t); err == nil { + t.Errorf("ApplyClusterMaster() should have returned error") + } } func TestPerformCmBundlePush(t *testing.T) { @@ -243,6 +307,13 @@ func TestPerformCmBundlePush(t *testing.T) { client := spltest.NewMockClient() + // When the secret object is not present, should return an error + current.Status.BundlePushTracker.NeedToPushMasterApps = true + err := PerformCmBundlePush(client, ¤t) + if err == nil { + t.Errorf("Should return error, when the secret object is not present") + } + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") if err != nil { t.Errorf(err.Error()) @@ -261,23 +332,36 @@ func TestPerformCmBundlePush(t *testing.T) { Data: map[string]string{configToken: ""}, } + _, err = splctrl.ApplyConfigMap(client, &smartstoreConfigMap) + if err != nil { + t.Errorf(err.Error()) + } + current.Status.BundlePushTracker.NeedToPushMasterApps = true - current.Status.BundlePushTracker.LastCheckInterval = time.Now().Unix() - 100 - _, err = splctrl.ApplyConfigMap(client, &smartstoreConfigMap) + //Re-attempting to push the CM bundle in less than 5 seconds should return an error + current.Status.BundlePushTracker.LastCheckInterval = time.Now().Unix() - 1 + err = PerformCmBundlePush(client, ¤t) + if err == nil { + t.Errorf("Bundle Push Should fail, if attempted to push within 5 seconds interval") + } + //Re-attempting to push the CM bundle after 5 seconds passed, should not return an error + current.Status.BundlePushTracker.LastCheckInterval = time.Now().Unix() - 10 err = PerformCmBundlePush(client, ¤t) - if err != nil { - t.Errorf("Bundle Push failed") + if err != nil && strings.HasPrefix(err.Error(), "Will re-attempt to push the bundle after the 5 seconds") { + t.Errorf("Bundle Push Should not fail if reattempted after 5 seconds interval passed. Error: %s", err.Error()) } + // When the CM Bundle push is not pending, should not return an error + current.Status.BundlePushTracker.NeedToPushMasterApps = false err = PerformCmBundlePush(client, ¤t) if err != nil { - t.Errorf("Bundle Push failed") + t.Errorf("Should not return an error when the Bundle push is not required. Error: %s", err.Error()) } } -func TestPushMasterAppsBundlePush(t *testing.T) { +func TestPushMasterAppsBundle(t *testing.T) { current := enterprisev1.ClusterMaster{ TypeMeta: metav1.TypeMeta{ @@ -296,6 +380,12 @@ func TestPushMasterAppsBundlePush(t *testing.T) { client := spltest.NewMockClient() + //Without global secret object, should return an error + err := PushMasterAppsBundle(client, ¤t) + if err == nil { + t.Errorf("Bundle push should fail, when the secret object is not found") + } + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") if err != nil { t.Errorf(err.Error()) @@ -307,7 +397,14 @@ func TestPushMasterAppsBundlePush(t *testing.T) { } err = PushMasterAppsBundle(client, ¤t) - if err != nil { - t.Errorf("Bundle Push failed") + if err == nil { + t.Errorf("Bundle push should fail, when the password is not found") + } + + //Without password, should return an error + delete(secret.Data, "password") + err = PushMasterAppsBundle(client, ¤t) + if err == nil { + t.Errorf("Bundle push should fail, when the password is not found") } } diff --git a/pkg/splunk/enterprise/configuration.go b/pkg/splunk/enterprise/configuration.go index 368b822ed..48f2a4fce 100644 --- a/pkg/splunk/enterprise/configuration.go +++ b/pkg/splunk/enterprise/configuration.go @@ -659,23 +659,6 @@ func updateSplunkPodTemplateWithConfig(client splcommon.ControllerClient, podTem } } -// LogSmartStoreVolumes logs smartstore volumes -func LogSmartStoreVolumes(volumeList []enterprisev1.VolumeSpec) { - scopedLog := logC.WithName("LogSmartStoreVolumes") - //var temp string - for _, volume := range volumeList { - scopedLog.Info("Volume: ", "name: ", volume.Name, "endpoint: ", volume.Endpoint, "path: ", volume.Path) - } -} - -// LogSmartStoreIndexes logs smartstore indexes -func LogSmartStoreIndexes(indexList []enterprisev1.IndexSpec) { - scopedLog := logC.WithName("LogSmartStoreIndexes") - for _, index := range indexList { - scopedLog.Info("Index: ", "name: ", index.Name, "remotePath: ", index.RemotePath, "volumeName", index.VolName) - } -} - // isSmartstoreEnabled checks and returns true if smartstore is configured func isSmartstoreConfigured(smartstore *enterprisev1.SmartStoreSpec) bool { if smartstore == nil { diff --git a/pkg/splunk/enterprise/configuration_test.go b/pkg/splunk/enterprise/configuration_test.go index 9fc58b208..ec37dfb13 100644 --- a/pkg/splunk/enterprise/configuration_test.go +++ b/pkg/splunk/enterprise/configuration_test.go @@ -21,6 +21,9 @@ import ( enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" + splctrl "github.com/splunk/splunk-operator/pkg/splunk/controller" + spltest "github.com/splunk/splunk-operator/pkg/splunk/test" + splutil "github.com/splunk/splunk-operator/pkg/splunk/util" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -457,9 +460,199 @@ func TestValidateSplunkSmartstoreSpec(t *testing.T) { if err != nil { t.Errorf("Smartstore config is optional, should not cause an error. But, got the error: %v", err) } + + // Configuring indexes without volume config should return error + SmartStoreWithoutVolumes := enterprisev1.SmartStoreSpec{ + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreWithoutVolumes) + if err == nil { + t.Errorf("Smartstore config without volume details should return error") + } + + // Duplicate volume names should be rejected + SmartStoreWithDuplicateVolumes := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + {Name: "msos_s2s3_vol-2", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreWithDuplicateVolumes) + if err == nil { + t.Errorf("Duplicate volume configuration should return an error") + } + + // Defaults with invalid volume reference should return error + SmartStoreDefaultsWithNonExistingVolume := enterprisev1.SmartStoreSpec{ + Defaults: enterprisev1.IndexConfDefaultsSpec{ + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol-2"}, + }, + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreDefaultsWithNonExistingVolume) + if err == nil { + t.Errorf("Volume referred in the indexes defaults should be a valid volume") + } + + //Duplicate index names should return an error + SmartStoreWithDuplicateIndexes := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata1", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreWithDuplicateIndexes) + if err == nil { + t.Errorf("Duplicate index names should return an error") + } + + // If the default volume is not configured, then each index should be configured + // with an explicit volume info. If not, should return an error + SmartStoreVolumeMissingBothFromDefaultsAndIndex := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1"}, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreVolumeMissingBothFromDefaultsAndIndex) + if err == nil { + t.Errorf("If no default volume, index with missing volume info should return an error") + } + + // Volume referenced from an index must be a valid volume + SmartStoreIndexesWithInvalidVolumeName := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol-2"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreIndexesWithInvalidVolumeName) + if err == nil { + t.Errorf("Index with an invalid volume name should return error") + } } -func TestValidateSplunkSmartstoreCacheManagerSpec(t *testing.T) { +func TestGetSmartstoreIndexesConfig(t *testing.T) { + SmartStoreIndexes := enterprisev1.SmartStoreSpec{ + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + MaxGlobalDataSizeMB: 6000, + MaxGlobalRawDataSizeMB: 7000, + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", // Missing RemotePath should be filled with the default "$_index_name" + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + MaxGlobalDataSizeMB: 2000, + MaxGlobalRawDataSizeMB: 3000, + VolName: "msos_s2s3_vol"}, + IndexAndCacheManagerCommonSpec: enterprisev1.IndexAndCacheManagerCommonSpec{ + HotlistBloomFilterRecencyHours: 48, + HotlistRecencySecs: 48 * 60 * 60}, + }, + {Name: "salesdata4", // Missing RemotePath should be filled with the default "$_index_name" + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + MaxGlobalDataSizeMB: 4000, + MaxGlobalRawDataSizeMB: 5000, + VolName: "msos_s2s3_vol"}, + IndexAndCacheManagerCommonSpec: enterprisev1.IndexAndCacheManagerCommonSpec{ + HotlistBloomFilterRecencyHours: 24, + HotlistRecencySecs: 24 * 60 * 60}, + }, + }, + } + + expectedINIFormatString := fmt.Sprintf(` +[salesdata1] +remotePath = volume:msos_s2s3_vol/remotepath1 +maxGlobalDataSizeMB = 6000 +maxGlobalRawDataSizeMB = 7000 + +[salesdata2] +remotePath = volume:msos_s2s3_vol/remotepath2 + +[salesdata3] +remotePath = volume:msos_s2s3_vol/$_index_name +hotlist_bloom_filter_recency_hours = 48 +hotlist_recency_secs = 172800 +maxGlobalDataSizeMB = 2000 +maxGlobalRawDataSizeMB = 3000 + +[salesdata4] +remotePath = volume:msos_s2s3_vol/$_index_name +hotlist_bloom_filter_recency_hours = 24 +hotlist_recency_secs = 86400 +maxGlobalDataSizeMB = 4000 +maxGlobalRawDataSizeMB = 5000 +`) + + indexesConfIni := GetSmartstoreIndexesConfig(SmartStoreIndexes.IndexList) + if indexesConfIni != expectedINIFormatString { + t.Errorf("expected: %s, returned: %s", expectedINIFormatString, indexesConfIni) + } +} +func TestGetServerConfigEntries(t *testing.T) { SmartStoreCacheManager := enterprisev1.CacheManagerSpec{ IndexAndCacheManagerCommonSpec: enterprisev1.IndexAndCacheManagerCommonSpec{ @@ -489,9 +682,16 @@ max_concurrent_uploads = 6 if expectedIniContents != serverConfForCacheManager { t.Errorf("Expected: %s \n Received: %s", expectedIniContents, serverConfForCacheManager) } + + // Empty config should return empty string + serverConfForCacheManager = GetServerConfigEntries(nil) + if serverConfForCacheManager != "" { + t.Errorf("Expected empty string, but received: %s", serverConfForCacheManager) + } + } -func TestValidateSplunkSmartstoreDefaultsSpec(t *testing.T) { +func TestGetSmartstoreIndexesDefaults(t *testing.T) { SmartStoreDefaultsConf := enterprisev1.IndexConfDefaultsSpec{ IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ @@ -520,3 +720,128 @@ maxGlobalRawDataSizeMB = 61440 } } + +func TestCheckIfVolumeExists(t *testing.T) { + SmartStoreConfig := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + // Volume that doesn't should error out + _, err := checkIfVolumeExists(SmartStoreConfig.VolList, "random_volume_name") + + if err == nil { + t.Errorf("if the volume doesn't exists, error should be reported") + } + + // Volume that exists should not error out + index := len(SmartStoreConfig.VolList) - 1 + returnedIndex, err := checkIfVolumeExists(SmartStoreConfig.VolList, SmartStoreConfig.VolList[index].Name) + + if err != nil { + t.Errorf("existing volume should not error out. index id: %d, error: %s", index, err.Error()) + } else if index != returnedIndex { + t.Errorf("Expected index: %d, but returned index id: %d", index, returnedIndex) + } +} + +func TestAreRemoteVolumeKeysChanged(t *testing.T) { + cr := enterprisev1.ClusterMaster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "CM", + Namespace: "test", + }, + Spec: enterprisev1.ClusterMasterSpec{ + SmartStore: enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + }, + + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + }, + }, + } + + client := spltest.NewMockClient() + var err error + ResourceRev := map[string]string{ + "secret1": "12345", + "secret2": "67890", + } + + // Missing secret object should return an error + keysChanged := AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + if err == nil { + t.Errorf("Missing secret object should return an error. keyChangedFlag: %t", keysChanged) + } else if keysChanged { + t.Errorf("When the S3 secret object is not present, Key change should not be reported") + } + + // First time secret version reference should be updated + // Just to simplify the test, assume that the keys are stored as part of the splunk-test-scret + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") + if err != nil { + t.Errorf(err.Error()) + } + + _, err = splctrl.ApplySecret(client, secret) + if err != nil { + t.Errorf(err.Error()) + } + + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + + _, ok := ResourceRev["splunk-test-secret"] + if !ok { + t.Errorf("Failed to update the Resource Version for first time") + } + + // Change the Resource Version, and see if that is being detected + resourceVersion := "3434" + secret.SetResourceVersion(resourceVersion) + + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + resourceVersionUpdated, ok := ResourceRev["splunk-test-secret"] + if !keysChanged || resourceVersion != resourceVersionUpdated { + t.Errorf("Failed detect the secret object change. Key changed: %t, Expected resource version: %s, Updated resource version %s", keysChanged, resourceVersion, resourceVersionUpdated) + } + + // No change on the secret object should return false + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + resourceVersionUpdated, ok = ResourceRev["splunk-test-secret"] + if keysChanged { + t.Errorf("If there is no change on secret object, should return false") + } + + // Empty volume list should return false + cr.Spec.SmartStore.VolList = nil + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + if keysChanged { + t.Errorf("Empty volume should not report a key change") + } +} diff --git a/pkg/splunk/enterprise/finalizers.go b/pkg/splunk/enterprise/finalizers.go index 63fcf67bb..1034bb03f 100644 --- a/pkg/splunk/enterprise/finalizers.go +++ b/pkg/splunk/enterprise/finalizers.go @@ -31,27 +31,32 @@ func init() { // DeleteSplunkPvc removes all corresponding PersistentVolumeClaims that are associated with a custom resource. func DeleteSplunkPvc(cr splcommon.MetaObject, c splcommon.ControllerClient) error { - scopedLog := log.WithName("DeleteSplunkPvc").WithValues("kind", cr.GetObjectKind().GroupVersionKind().Kind, + var objectKind string + objectKind = cr.GetObjectKind().GroupVersionKind().Kind + + scopedLog := log.WithName("DeleteSplunkPvc").WithValues("kind", objectKind, "name", cr.GetName(), "namespace", cr.GetNamespace()) var component string - switch cr.GetObjectKind().GroupVersionKind().Kind { + switch objectKind { case "Standalone": component = "standalone" case "LicenseMaster": component = "license-master" case "SearchHeadCluster": component = "search-head" - case "IndexerCluster", "ClusterMaster": + case "IndexerCluster": component = "indexer" + case "ClusterMaster": + component = "cluster-master" default: scopedLog.Info("Skipping PVC removal") return nil } - // get list of PVCs for this cluster + // get list of PVCs associated with this CR labels := map[string]string{ - "app.kubernetes.io/part-of": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), + "app.kubernetes.io/instance": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), } listOpts := []client.ListOption{ client.InNamespace(cr.GetNamespace()), diff --git a/pkg/splunk/enterprise/finalizers_test.go b/pkg/splunk/enterprise/finalizers_test.go index 6951859d7..08aff67ab 100644 --- a/pkg/splunk/enterprise/finalizers_test.go +++ b/pkg/splunk/enterprise/finalizers_test.go @@ -40,8 +40,10 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl component = "license-master" case "SearchHeadCluster": component = "search-head" - case "IndexerCluster", "ClusterMaster": + case "IndexerCluster": component = "indexer" + case "ClusterMaster": + component = "cluster-master" } labelsA := map[string]string{ @@ -49,7 +51,7 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl "app.kubernetes.io/managed-by": "splunk-operator", } labelsB := map[string]string{ - "app.kubernetes.io/part-of": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), + "app.kubernetes.io/instance": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), } listOptsA := []client.ListOption{ client.InNamespace("test"), @@ -135,6 +137,7 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, + {MetaName: "*v1beta1.ClusterMaster-test-master1"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, } } @@ -169,12 +172,14 @@ func splunkPVCDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func( component = "license-master" case "SearchHeadCluster": component = "search-head" - case "IndexerCluster", "ClusterMaster": + case "IndexerCluster": component = "indexer" + case "ClusterMaster": + component = "cluster-master" } labels := map[string]string{ - "app.kubernetes.io/part-of": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), + "app.kubernetes.io/instance": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), } listOpts := []client.ListOption{ client.InNamespace(cr.GetNamespace()), diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 98cea1df8..a975661c0 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -18,6 +18,8 @@ import ( "context" "errors" "fmt" + "regexp" + "strconv" "time" appsv1 "k8s.io/api/apps/v1" @@ -60,6 +62,9 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind if cr.Status.IndexerSecretChanged == nil { cr.Status.IndexerSecretChanged = []bool{} } + if cr.Status.IdxcPasswordChangedSecrets == nil { + cr.Status.IdxcPasswordChangedSecrets = make(map[string]bool) + } defer func() { err = client.Status().Update(context.TODO(), cr) if err != nil { @@ -73,6 +78,26 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind return result, err } + namespacedName := types.NamespacedName{ + Namespace: cr.GetNamespace(), + Name: cr.Spec.ClusterMasterRef.Name, + } + masterIdxCluster := &enterprisev1.ClusterMaster{} + err = client.Get(context.TODO(), namespacedName, masterIdxCluster) + if err == nil { + cr.Status.ClusterMasterPhase = masterIdxCluster.Status.Phase + } else { + cr.Status.ClusterMasterPhase = splcommon.PhaseError + } + mgr := indexerClusterPodManager{log: scopedLog, cr: cr, secrets: namespaceScopedSecret, newSplunkClient: splclient.NewSplunkClient} + // Check if we have configured enough number(<= RF) of replicas + if mgr.cr.Status.ClusterMasterPhase == splcommon.PhaseReady { + err = mgr.verifyRFPeers(client) + if err != nil { + return result, err + } + } + // check if deletion has been requested if cr.ObjectMeta.DeletionTimestamp != nil { DeleteOwnerReferencesForResources(client, cr, nil) @@ -97,24 +122,12 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind return result, err } - namespacedName := types.NamespacedName{ - Namespace: cr.GetNamespace(), - Name: cr.Spec.ClusterMasterRef.Name, - } - masterIdxCluster := &enterprisev1.ClusterMaster{} - err = client.Get(context.TODO(), namespacedName, masterIdxCluster) - if err == nil { - cr.Status.ClusterMasterPhase = masterIdxCluster.Status.Phase - } else { - cr.Status.ClusterMasterPhase = splcommon.PhaseError - } - // create or update statefulset for the indexers statefulSet, err := getIndexerStatefulSet(client, cr) if err != nil { return result, err } - mgr := indexerClusterPodManager{log: scopedLog, cr: cr, secrets: namespaceScopedSecret, newSplunkClient: splclient.NewSplunkClient} + phase, err := mgr.Update(client, statefulSet, cr.Spec.Replicas) if err != nil { return result, err @@ -138,6 +151,7 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind // Reset idxc secret changed and namespace secret revision cr.Status.IndexerSecretChanged = []bool{} cr.Status.NamespaceSecretResourceVersion = namespaceScopedSecret.ObjectMeta.ResourceVersion + cr.Status.IdxcPasswordChangedSecrets = make(map[string]bool) result.Requeue = false } @@ -193,6 +207,7 @@ func SetClusterMaintenanceMode(c splcommon.ControllerClient, cr *enterprisev1.In // ApplyIdxcSecret checks if any of the indexer's have a different idxc_secret from namespace scoped secret and changes it func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) error { + var indIdxcSecret string // Get namespace scoped secret namespaceSecret, err := splutil.ApplyNamespaceScopedSecretObject(mgr.c, mgr.cr.GetNamespace()) if err != nil { @@ -214,17 +229,24 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e scopedLog.Info("Namespaced scoped secret revision has changed") // Retrieve idxc_secret password from secret data - nsIdxcSecret := string(namespaceSecret.Data["idxc_secret"]) + nsIdxcSecret := string(namespaceSecret.Data[splcommon.IdxcSecret]) // Loop over all indexer pods and get individual pod's idxc password for i := int32(0); i <= replicas-1; i++ { // Get Indexer's name indexerPodName := GetSplunkStatefulsetPodName(SplunkIndexer, mgr.cr.GetName(), i) - // Retrieve idxc_secret password from Pod - indIdxcSecret, err := splutil.GetSpecificSecretTokenFromPod(mgr.c, indexerPodName, mgr.cr.GetNamespace(), "idxc_secret") + // Retrieve secret from pod + podSecret, err := splutil.GetSecretFromPod(mgr.c, indexerPodName, mgr.cr.GetNamespace()) if err != nil { - return fmt.Errorf("Couldn't Retrieve idxc_secret from secret data %s", err.Error()) + return fmt.Errorf(fmt.Sprintf(splcommon.PodSecretNotFoundError, indexerPodName)) + } + + // Retrieve idxc_secret token + if indIdxcSecretByte, ok := podSecret.Data[splcommon.IdxcSecret]; ok { + indIdxcSecret = string(indIdxcSecretByte) + } else { + return fmt.Errorf(fmt.Sprintf(splcommon.SecretTokenNotRetrievable, splcommon.IdxcSecret)) } // If idxc secret is different from namespace scoped secret change it @@ -264,6 +286,9 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e } scopedLog.Info("Restarted splunk") + // Keep a track of all the secrets on pods to change their idxc secret below + mgr.cr.Status.IdxcPasswordChangedSecrets[podSecret.GetName()] = true + // Set the idxc_secret changed flag to true if i < int32(len(mgr.cr.Status.IndexerSecretChanged)) { mgr.cr.Status.IndexerSecretChanged[i] = true @@ -273,19 +298,63 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e } } + /* + During the recycle of indexer pods due to an idxc secret change, if there is a container + restart(for example if the splunkd process dies) before the operator + deletes the pod, the container restart fails due to mismatch of idxc password between Cluster + master and that particular indexer. + + Changing the idxc passwords on the secrets mounted on the indexer pods to avoid the above. + */ + if len(mgr.cr.Status.IdxcPasswordChangedSecrets) > 0 { + for podSecretName := range mgr.cr.Status.IdxcPasswordChangedSecrets { + if mgr.cr.Status.IdxcPasswordChangedSecrets[podSecretName] { + podSecret, err := splutil.GetSecretByName(mgr.c, mgr.cr, podSecretName) + if err != nil { + return fmt.Errorf("Could not read secret %s, reason - %v", podSecretName, err) + } + + // Retrieve namespaced scoped secret data in splunk readable format + splunkReadableData, err := splutil.GetSplunkReadableNamespaceScopedSecretData(mgr.c, mgr.cr.GetNamespace()) + if err != nil { + return err + } + + podSecret.Data[splcommon.IdxcSecret] = splunkReadableData[splcommon.IdxcSecret] + podSecret.Data["default.yml"] = splunkReadableData["default.yml"] + + _, err = splctrl.ApplySecret(mgr.c, podSecret) + if err != nil { + return err + } + scopedLog.Info("idxc password changed on the secret mounted on pod", "Secret on Pod:", podSecretName) + + // Set to false marking the idxc password change in the secret + mgr.cr.Status.IdxcPasswordChangedSecrets[podSecretName] = false + } + } + } + return nil } // Update for indexerClusterPodManager handles all updates for a statefulset of indexers func (mgr *indexerClusterPodManager) Update(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, desiredReplicas int32) (splcommon.Phase, error) { + + var err error + // Assign client if mgr.c == nil { mgr.c = c } // update statefulset, if necessary - _, err := splctrl.ApplyStatefulSet(mgr.c, statefulSet) - if err != nil { - return splcommon.PhaseError, err + if mgr.cr.Status.ClusterMasterPhase == splcommon.PhaseReady { + _, err = splctrl.ApplyStatefulSet(mgr.c, statefulSet) + if err != nil { + return splcommon.PhaseError, err + } + } else { + mgr.log.Error(err, "Cluster Master is not ready yet") } // Check if a recycle of idxc pods is necessary(due to idxc_secret mismatch with CM) @@ -302,7 +371,7 @@ func (mgr *indexerClusterPodManager) Update(c splcommon.ControllerClient, statef } // manage scaling and updates - return splctrl.UpdateStatefulSetPods(c, statefulSet, mgr, desiredReplicas) + return splctrl.UpdateStatefulSetPods(c, statefulSet, mgr, desiredReplicas, &mgr.cr.Status.SkipRecheckUpdate) } // PrepareScaleDown for indexerClusterPodManager prepares indexer pod to be removed via scale down event; it returns true when ready @@ -414,6 +483,40 @@ func (mgr *indexerClusterPodManager) getClusterMasterClient() *splclient.SplunkC return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd) } +// getSiteRepFactorOriginCount gets the origin count of the site_replication_factor +func getSiteRepFactorOriginCount(siteRepFactor string) int32 { + re := regexp.MustCompile(".*origin:(?P.*),.*") + match := re.FindStringSubmatch(siteRepFactor) + siteRF, _ := strconv.Atoi(match[1]) + return int32(siteRF) +} + +// verifyRFPeers verifies the number of peers specified in the replicas section +// of IndexerClsuster CR. If it is less than RF, than we set it to RF. +func (mgr *indexerClusterPodManager) verifyRFPeers(c splcommon.ControllerClient) error { + if mgr.c == nil { + mgr.c = c + } + cm := mgr.getClusterMasterClient() + clusterInfo, err := cm.GetClusterInfo(false) + if err != nil { + return fmt.Errorf("Could not get cluster info from cluster master") + } + var replicationFactor int32 + // if it is a multisite indexer cluster, check site_replication_factor + if clusterInfo.MultiSite == "true" { + replicationFactor = getSiteRepFactorOriginCount(clusterInfo.SiteReplicationFactor) + } else { // for single site, check replication factor + replicationFactor = clusterInfo.ReplicationFactor + } + + if mgr.cr.Spec.Replicas < replicationFactor { + mgr.log.Info("Changing number of replicas as it is less than RF number of peers", "replicas", mgr.cr.Spec.Replicas) + mgr.cr.Spec.Replicas = replicationFactor + } + return nil +} + // updateStatus for indexerClusterPodManager uses the REST API to update the status for an IndexerCluster custom resource func (mgr *indexerClusterPodManager) updateStatus(statefulSet *appsv1.StatefulSet) error { mgr.cr.Status.ReadyReplicas = statefulSet.Status.ReadyReplicas diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index b8547b9fc..770036790 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -37,12 +37,11 @@ func TestApplyIndexerCluster(t *testing.T) { funcCalls := []spltest.MockFuncCall{ {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, + {MetaName: "*v1beta1.ClusterMaster-test-master1"}, {MetaName: "*v1.Service-test-splunk-stack1-indexer-headless"}, {MetaName: "*v1.Service-test-splunk-stack1-indexer-service"}, - {MetaName: "*v1beta1.ClusterMaster-test-master1"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-stack1-indexer-secret-v1"}, - {MetaName: "*v1.StatefulSet-test-splunk-stack1-indexer"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, } labels := map[string]string{ @@ -55,8 +54,8 @@ func TestApplyIndexerCluster(t *testing.T) { } listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} - createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[2], funcCalls[3], funcCalls[6], funcCalls[7]}, "Update": {funcCalls[0]}, "List": {listmockCall[0]}} - updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6], funcCalls[7], funcCalls[8]}, "Update": {funcCalls[7]}, "List": {listmockCall[0]}} + createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[3], funcCalls[4], funcCalls[6]}, "Update": {funcCalls[0]}, "List": {listmockCall[0]}} + updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6], funcCalls[7]}, "List": {listmockCall[0]}} current := enterprisev1.IndexerCluster{ TypeMeta: metav1.TypeMeta{ @@ -76,6 +75,7 @@ func TestApplyIndexerCluster(t *testing.T) { }, }, } + current.Status.ClusterMasterPhase = splcommon.PhaseReady current.Status.IndexerSecretChanged = append(current.Status.IndexerSecretChanged, true) revised := current.DeepCopy() revised.Spec.Image = "splunk/test" @@ -96,11 +96,57 @@ func TestApplyIndexerCluster(t *testing.T) { splunkDeletionTester(t, revised, deleteFunc) } -func indexerClusterPodManagerTester(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, - desiredReplicas int32, wantPhase splcommon.Phase, statefulSet *appsv1.StatefulSet, - wantCalls map[string][]spltest.MockFuncCall, wantError error, initObjects ...runtime.Object) { +func TestGetClusterMasterClient(t *testing.T) { + scopedLog := log.WithName("TestGetClusterMasterClient") + cr := enterprisev1.IndexerCluster{ + TypeMeta: metav1.TypeMeta{ + Kind: "IndexerCluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1", + Namespace: "test", + }, + Spec: enterprisev1.IndexerClusterSpec{ + Replicas: 1, + CommonSplunkSpec: enterprisev1.CommonSplunkSpec{ + ClusterMasterRef: corev1.ObjectReference{ + Name: "", /* Empty ClusterMasterRef */ + }, + }, + }, + Status: enterprisev1.IndexerClusterStatus{ + ClusterMasterPhase: splcommon.PhaseReady, + }, + } + secrets := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-master1-indexer-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "password": {'1', '2', '3'}, + }, + } + mockSplunkClient := &spltest.MockHTTPClient{} + mgr := &indexerClusterPodManager{ + log: scopedLog, + cr: &cr, + secrets: secrets, + newSplunkClient: func(managementURI, username, password string) *splclient.SplunkClient { + c := splclient.NewSplunkClient(managementURI, username, password) + c.Client = mockSplunkClient + return c + }, + } + c := spltest.NewMockClient() + mgr.c = c + cm := mgr.getClusterMasterClient() + if cm.ManagementURI != "https://splunk--cluster-master-service.test.svc.cluster.local:8089" { + t.Errorf("getClusterMasterClient() should have returned incorrect mgmt URI") + } +} - // test for updating +func getIndexerClusterPodManager(method string, mockHandlers []spltest.MockHTTPHandler, mockSplunkClient *spltest.MockHTTPClient, replicas int32) *indexerClusterPodManager { scopedLog := log.WithName(method) cr := enterprisev1.IndexerCluster{ TypeMeta: metav1.TypeMeta{ @@ -111,7 +157,7 @@ func indexerClusterPodManagerTester(t *testing.T, method string, mockHandlers [] Namespace: "test", }, Spec: enterprisev1.IndexerClusterSpec{ - Replicas: 1, + Replicas: replicas, CommonSplunkSpec: enterprisev1.CommonSplunkSpec{ ClusterMasterRef: corev1.ObjectReference{ Name: "master1", @@ -133,8 +179,7 @@ func indexerClusterPodManagerTester(t *testing.T, method string, mockHandlers [] "password": {'1', '2', '3'}, }, } - mockSplunkClient := &spltest.MockHTTPClient{} - mockSplunkClient.AddHandlers(mockHandlers...) + mgr := &indexerClusterPodManager{ log: scopedLog, cr: &cr, @@ -145,6 +190,285 @@ func indexerClusterPodManagerTester(t *testing.T, method string, mockHandlers [] return c }, } + return mgr +} + +// indexerClusterpodManagerVerifyRFPeersTester is used to verify replicas against RF using a indexerClusterPodManager +func indexerClusterPodManagerVerifyRFPeersTester(t *testing.T, method string, mgr *indexerClusterPodManager, + desiredReplicas int32, wantPhase splcommon.Phase, wantCalls map[string][]spltest.MockFuncCall, wantError error) { + + // initialize client + c := spltest.NewMockClient() + + // test update + err := mgr.verifyRFPeers(c) + if (err == nil && wantError != nil) || + (err != nil && wantError == nil) || + (err != nil && wantError != nil && err.Error() != wantError.Error()) { + t.Errorf("%s returned error %v; want %v", method, err, wantError) + } + + if mgr.cr.Spec.Replicas != desiredReplicas { + t.Errorf("spec has replicas as %d ; want %d", mgr.cr.Spec.Replicas, desiredReplicas) + } + // check calls + c.CheckCalls(t, method, wantCalls) +} + +func indexerClusterPodManagerReplicasTester(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, + replicas int32, desiredReplicas int32, wantPhase splcommon.Phase, + wantCalls map[string][]spltest.MockFuncCall, wantError error) { + + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) + indexerClusterPodManagerVerifyRFPeersTester(t, method, mgr, desiredReplicas, wantPhase, wantCalls, wantError) + mockSplunkClient.CheckRequests(t, method) +} + +func TestVerifyRFPeers(t *testing.T) { + + funcCalls := []spltest.MockFuncCall{ + {MetaName: "*v1.Pod-test-splunk-master1-cluster-master-0"}, + } + + wantCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0]}} + + // test 1 ready pod + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/config?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{"_reload":"/services/cluster/config/_reload","_acl":"/services/cluster/config/_acl"},"origin":"https://localhost:8089/services/cluster/config","updated":"2020-10-28T21:37:07+00:00","generator":{"build":"152fb4b2bb96","version":"8.0.6"},"entry":[{"name":"config","id":"https://localhost:8089/services/cluster/config/config","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/config/config","list":"/services/cluster/config/config","_reload":"/services/cluster/config/config/_reload","edit":"/services/cluster/config/config","disable":"/services/cluster/config/config/disable"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"access_logging_for_heartbeats":false,"auto_rebalance_primaries":true,"buckets_to_summarize":"primaries","cluster_label":"idxc_label","cxn_timeout":60,"decommission_force_finish_idle_time":0,"decommission_force_timeout":180,"disabled":false,"eai:acl":null,"forwarderdata_rcv_port":0,"forwarderdata_use_ssl":false,"frozen_notifications_per_batch":10,"guid":"F643BA71-0D3C-4D63-A0BC-A1604AC928E3","heartbeat_period":18446744073709552000,"heartbeat_timeout":60,"master_uri":"https://127.0.0.1:8089","max_auto_service_interval":30,"max_fixup_time_ms":5000,"max_peer_build_load":2,"max_peer_rep_load":5,"max_peer_sum_rep_load":5,"max_peers_to_download_bundle":5,"max_primary_backups_per_service":10,"mode":"master","multisite":"false","notify_buckets_period":10,"notify_scan_min_period":10,"notify_scan_period":10,"percent_peers_to_restart":10,"ping_flag":true,"quiet_period":60,"rcv_timeout":60,"rebalance_primaries_execution_limit_ms":0,"rebalance_threshold":0.9,"register_forwarder_address":"","register_replication_address":"","register_search_address":"","remote_storage_upload_timeout":60,"rep_cxn_timeout":60,"rep_max_rcv_timeout":180,"rep_max_send_timeout":180,"rep_rcv_timeout":60,"rep_send_timeout":60,"replication_factor":3,"replication_port":null,"replication_use_ssl":false,"report_remote_storage_bucket_upload_to_targets":false,"reporting_delay_period":30,"restart_inactivity_timeout":600,"restart_timeout":60,"rolling_restart":"restart","search_factor":3,"search_files_retry_timeout":600,"secret":"********","send_timeout":60,"service_interval":0,"site":"default","site_by_site":true,"summary_replication":"false","use_batch_discard":"true","use_batch_mask_changes":"true","use_batch_remote_rep_changes":"false"}}],"paging":{"total":1,"perPage":10000000,"offset":0},"messages":[]}`, + }, + } + + method := "indexerClusterPodManager.verifyRFPeers(All pods ready)" + // test for singlesite i.e. with replication_factor=3(on ClusterMaster) and replicas=3(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 3 /*replicas*/, 3 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) + + // test for singlesite i.e. with replication_factor=3(on ClusterMaster) and replicas=1(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 1 /*replicas*/, 3 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) + + // Now test for multi-site too + mockHandlers[0].Body = `{"links":{"_reload":"/services/cluster/config/_reload","_acl":"/services/cluster/config/_acl"},"origin":"https://localhost:8089/services/cluster/config","updated":"2020-10-28T21:37:07+00:00","generator":{"build":"152fb4b2bb96","version":"8.0.6"},"entry":[{"name":"config","id":"https://localhost:8089/services/cluster/config/config","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/config/config","list":"/services/cluster/config/config","_reload":"/services/cluster/config/config/_reload","edit":"/services/cluster/config/config","disable":"/services/cluster/config/config/disable"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"access_logging_for_heartbeats":false,"auto_rebalance_primaries":true,"buckets_to_summarize":"primaries","cluster_label":"idxc_label","cxn_timeout":60,"decommission_force_finish_idle_time":0,"decommission_force_timeout":180,"disabled":false,"eai:acl":null,"forwarderdata_rcv_port":0,"forwarderdata_use_ssl":false,"frozen_notifications_per_batch":10,"guid":"F643BA71-0D3C-4D63-A0BC-A1604AC928E3","heartbeat_period":18446744073709552000,"heartbeat_timeout":60,"master_uri":"https://127.0.0.1:8089","max_auto_service_interval":30,"max_fixup_time_ms":5000,"max_peer_build_load":2,"max_peer_rep_load":5,"max_peer_sum_rep_load":5,"max_peers_to_download_bundle":5,"max_primary_backups_per_service":10,"mode":"master","multisite":"true","notify_buckets_period":10,"notify_scan_min_period":10,"notify_scan_period":10,"percent_peers_to_restart":10,"ping_flag":true,"quiet_period":60,"rcv_timeout":60,"rebalance_primaries_execution_limit_ms":0,"rebalance_threshold":0.9,"register_forwarder_address":"","register_replication_address":"","register_search_address":"","remote_storage_upload_timeout":60,"rep_cxn_timeout":60,"rep_max_rcv_timeout":180,"rep_max_send_timeout":180,"rep_rcv_timeout":60,"rep_send_timeout":60,"replication_factor":3,"replication_port":null,"replication_use_ssl":false,"report_remote_storage_bucket_upload_to_targets":false,"reporting_delay_period":30,"restart_inactivity_timeout":600,"restart_timeout":60,"rolling_restart":"restart","search_factor":3,"search_files_retry_timeout":600,"secret":"********","send_timeout":60,"service_interval":0,"site":"site1","site_by_site":true,"site_replication_factor":"{ origin:2, total:2 }","site_search_factor":"{ origin:2, total:2 }","summary_replication":"false","use_batch_discard":"true","use_batch_mask_changes":"true","use_batch_remote_rep_changes":"false"}}],"paging":{"total":1,"perPage":10000000,"offset":0},"messages":[]}` + + //test for multisite i.e. with site_replication_factor=origin:2,total:2(on ClusterMaster) and replicas=2(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 2 /*replicas*/, 2 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) + + //test for multisite i.e. with site_replication_factor=origin:2,total:2(on ClusterMaster) and replicas=1(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 1 /*replicas*/, 2 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) +} + +func checkResponseFromUpdateStatus(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, replicas int32, statefulSet *appsv1.StatefulSet, retry bool) error { + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) + + c := spltest.NewMockClient() + mgr.c = c + + err := mgr.updateStatus(statefulSet) + if retry == true { + err = mgr.updateStatus(statefulSet) + } + return err +} + +func TestUpdateStatusInvalidResponse(t *testing.T) { + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/info?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: ``, + }, + } + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + + method := "indexerClusterPodManager.UpdateStatus(Invalid response)" + err := checkResponseFromUpdateStatus(t, method, mockHandlers, 1, statefulSet, false) + if err == nil { + t.Errorf("mgr.updateStatus() should have returned an error here") + } + + mockHandlers[0].Body = `{"links":{},"origin":"https://localhost:8089/services/cluster/master/info","updated":"2020-03-18T01:04:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"master","id":"https://localhost:8089/services/cluster/master/info/master","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/info/master","list":"/services/cluster/master/info/master"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"apply_bundle_status":{"invalid_bundle":{"bundle_path":"","bundle_validation_errors_on_master":[],"checksum":"","timestamp":0},"reload_bundle_issued":false,"status":"None"},"backup_and_restore_primaries":false,"controlled_rolling_restart_flag":false,"eai:acl":null,"indexing_ready_flag":true,"initialized_flag":true,"label":"splunk-stack1-cluster-master-0","last_check_restart_bundle_result":false,"last_dry_run_bundle":{"bundle_path":"","checksum":"","timestamp":0},"last_validated_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/0af7c0e95f313f7be3b0cb1d878df9a1-1583948640.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","is_valid_bundle":true,"timestamp":1583948640},"latest_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"maintenance_mode":false,"multisite":false,"previous_active_bundle":{"bundle_path":"","checksum":"","timestamp":0},"primaries_backup_status":"No on-going (or) completed primaries backup yet. Check back again in few minutes if you expect a backup.","quiet_period_flag":false,"rolling_restart_flag":false,"rolling_restart_or_upgrade":false,"service_ready_flag":true,"start_time":1583948636,"summary_replication":"false"}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}` + + mockHandler := spltest.MockHTTPHandler{ + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/peers?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: ``, + } + + mockHandlers = append(mockHandlers, mockHandler) + err = checkResponseFromUpdateStatus(t, method, mockHandlers, 1, statefulSet, false) + if err == nil { + t.Errorf("mgr.updateStatus() should have returned an error here") + } + + mockHandlers[1].Body = `{"links":{"create":"/services/cluster/master/peers/_new"},"origin":"https://localhost:8089/services/cluster/master/peers","updated":"2020-03-18T01:08:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","id":"https://localhost:8089/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","list":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","edit":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","apply_bundle_status":{"invalid_bundle":{"bundle_validation_errors":[],"invalid_bundle_id":""},"reasons_for_restart":[],"restart_required_for_apply_bundle":false,"status":"None"},"base_generation_id":26,"bucket_count":73,"bucket_count_by_index":{"_audit":24,"_internal":45,"_telemetry":4},"buckets_rf_by_origin_site":{"default":73},"buckets_sf_by_origin_site":{"default":73},"delayed_buckets_to_discard":[],"eai:acl":null,"fixup_set":[],"heartbeat_started":true,"host_port_pair":"10.36.0.6:8089","indexing_disk_space":210707374080,"is_searchable":true,"is_valid_bundle":true,"label":"splunk-stack1-indexer-0","last_dry_run_bundle":"","last_heartbeat":1584493732,"last_validated_bundle":"14310A4AABD23E85BBD4559C4A3B59F8","latest_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","peer_registered_summaries":true,"pending_builds":[],"pending_job_count":0,"primary_count":73,"primary_count_remote":0,"register_search_address":"10.36.0.6:8089","replication_count":0,"replication_port":9887,"replication_use_ssl":false,"restart_required_for_applying_dry_run_bundle":false,"search_state_counter":{"PendingSearchable":0,"Searchable":73,"SearchablePendingMask":0,"Unsearchable":0},"site":"default","splunk_version":"8.0.2","status":"Up","status_counter":{"Complete":69,"NonStreamingTarget":0,"StreamingSource":4,"StreamingTarget":0},"summary_replication_count":0}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}` + + // We would like to call mgr.updateStatus() here twice just to mimic calling reconcile twice, + // so that the first call fill the field `mgr.cr.Status.Peers` and the next call can use that. + err = checkResponseFromUpdateStatus(t, method, mockHandlers, 1, statefulSet, true) + if err != nil { + t.Errorf("mgr.updateStatus() should not have returned an error here") + } +} + +func TestInvalidPeerStatusInScaleDown(t *testing.T) { + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + + // Create a mock handler that returns an invalid peer status as response + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/info?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{},"origin":"https://localhost:8089/services/cluster/master/info","updated":"2020-03-18T01:04:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"master","id":"https://localhost:8089/services/cluster/master/info/master","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/info/master","list":"/services/cluster/master/info/master"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"apply_bundle_status":{"invalid_bundle":{"bundle_path":"","bundle_validation_errors_on_master":[],"checksum":"","timestamp":0},"reload_bundle_issued":false,"status":"None"},"backup_and_restore_primaries":false,"controlled_rolling_restart_flag":false,"eai:acl":null,"indexing_ready_flag":true,"initialized_flag":true,"label":"splunk-stack1-cluster-master-0","last_check_restart_bundle_result":false,"last_dry_run_bundle":{"bundle_path":"","checksum":"","timestamp":0},"last_validated_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/0af7c0e95f313f7be3b0cb1d878df9a1-1583948640.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","is_valid_bundle":true,"timestamp":1583948640},"latest_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"maintenance_mode":false,"multisite":false,"previous_active_bundle":{"bundle_path":"","checksum":"","timestamp":0},"primaries_backup_status":"No on-going (or) completed primaries backup yet. Check back again in few minutes if you expect a backup.","quiet_period_flag":false,"rolling_restart_flag":false,"rolling_restart_or_upgrade":false,"service_ready_flag":true,"start_time":1583948636,"summary_replication":"false"}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/peers?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{"create":"/services/cluster/master/peers/_new"},"origin":"https://localhost:8089/services/cluster/master/peers","updated":"2020-03-18T01:08:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","id":"https://localhost:8089/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","list":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","edit":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","apply_bundle_status":{"invalid_bundle":{"bundle_validation_errors":[],"invalid_bundle_id":""},"reasons_for_restart":[],"restart_required_for_apply_bundle":false,"status":"None"},"base_generation_id":26,"bucket_count":73,"bucket_count_by_index":{"_audit":24,"_internal":45,"_telemetry":4},"buckets_rf_by_origin_site":{"default":73},"buckets_sf_by_origin_site":{"default":73},"delayed_buckets_to_discard":[],"eai:acl":null,"fixup_set":[],"heartbeat_started":true,"host_port_pair":"10.36.0.6:8089","indexing_disk_space":210707374080,"is_searchable":true,"is_valid_bundle":true,"label":"splunk-stack1-indexer-0","last_dry_run_bundle":"","last_heartbeat":1584493732,"last_validated_bundle":"14310A4AABD23E85BBD4559C4A3B59F8","latest_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","peer_registered_summaries":true,"pending_builds":[],"pending_job_count":0,"primary_count":73,"primary_count_remote":0,"register_search_address":"10.36.0.6:8089","replication_count":0,"replication_port":9887,"replication_use_ssl":false,"restart_required_for_applying_dry_run_bundle":false,"search_state_counter":{"PendingSearchable":0,"Searchable":73,"SearchablePendingMask":0,"Unsearchable":0},"site":"default","splunk_version":"8.0.2","status":"Invalid_Status","status_counter":{"Complete":69,"NonStreamingTarget":0,"StreamingSource":4,"StreamingTarget":0},"summary_replication_count":0}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + } + + method := "indexerClusterPodManager.decommission" + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) + + c := spltest.NewMockClient() + mgr.c = c + + err := mgr.updateStatus(statefulSet) + if err != nil { + t.Errorf("mgr.updateStatus() should not have returned an error here") + } + + _, err = mgr.PrepareScaleDown(0) + if err == nil { + t.Errorf("mgr.PrepareScaleDown() should have returned an error here") + } +} + +func TestInvalidPeerInFinishRecycle(t *testing.T) { + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/info?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{},"origin":"https://localhost:8089/services/cluster/master/info","updated":"2020-03-18T01:04:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"master","id":"https://localhost:8089/services/cluster/master/info/master","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/info/master","list":"/services/cluster/master/info/master"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"apply_bundle_status":{"invalid_bundle":{"bundle_path":"","bundle_validation_errors_on_master":[],"checksum":"","timestamp":0},"reload_bundle_issued":false,"status":"None"},"backup_and_restore_primaries":false,"controlled_rolling_restart_flag":false,"eai:acl":null,"indexing_ready_flag":true,"initialized_flag":true,"label":"splunk-stack1-cluster-master-0","last_check_restart_bundle_result":false,"last_dry_run_bundle":{"bundle_path":"","checksum":"","timestamp":0},"last_validated_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/0af7c0e95f313f7be3b0cb1d878df9a1-1583948640.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","is_valid_bundle":true,"timestamp":1583948640},"latest_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"maintenance_mode":false,"multisite":false,"previous_active_bundle":{"bundle_path":"","checksum":"","timestamp":0},"primaries_backup_status":"No on-going (or) completed primaries backup yet. Check back again in few minutes if you expect a backup.","quiet_period_flag":false,"rolling_restart_flag":false,"rolling_restart_or_upgrade":false,"service_ready_flag":true,"start_time":1583948636,"summary_replication":"false"}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/peers?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{"create":"/services/cluster/master/peers/_new"},"origin":"https://localhost:8089/services/cluster/master/peers","updated":"2020-03-18T01:08:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","id":"https://localhost:8089/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","list":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","edit":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","apply_bundle_status":{"invalid_bundle":{"bundle_validation_errors":[],"invalid_bundle_id":""},"reasons_for_restart":[],"restart_required_for_apply_bundle":false,"status":"None"},"base_generation_id":26,"bucket_count":73,"bucket_count_by_index":{"_audit":24,"_internal":45,"_telemetry":4},"buckets_rf_by_origin_site":{"default":73},"buckets_sf_by_origin_site":{"default":73},"delayed_buckets_to_discard":[],"eai:acl":null,"fixup_set":[],"heartbeat_started":true,"host_port_pair":"10.36.0.6:8089","indexing_disk_space":210707374080,"is_searchable":true,"is_valid_bundle":true,"label":"splunk-stack1-indexer-0","last_dry_run_bundle":"","last_heartbeat":1584493732,"last_validated_bundle":"14310A4AABD23E85BBD4559C4A3B59F8","latest_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","peer_registered_summaries":true,"pending_builds":[],"pending_job_count":0,"primary_count":73,"primary_count_remote":0,"register_search_address":"10.36.0.6:8089","replication_count":0,"replication_port":9887,"replication_use_ssl":false,"restart_required_for_applying_dry_run_bundle":false,"search_state_counter":{"PendingSearchable":0,"Searchable":73,"SearchablePendingMask":0,"Unsearchable":0},"site":"default","splunk_version":"8.0.2","status":"Up","status_counter":{"Complete":69,"NonStreamingTarget":0,"StreamingSource":4,"StreamingTarget":0},"summary_replication_count":0}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + } + + method := "indexerClusterPodManager.FinishRecycle" + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) + + c := spltest.NewMockClient() + mgr.c = c + + err := mgr.updateStatus(statefulSet) + if err != nil { + t.Errorf("mgr.updateStatus() should not have returned an error here") + } + + // Here we are trying to call FinishRecycle for a peer which is not in the list. + _, err = mgr.FinishRecycle(1) + if err == nil { + t.Errorf("mgr.FinishRecycle() should have returned an error here") + } +} + +func indexerClusterPodManagerUpdateTester(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, + desiredReplicas int32, wantPhase splcommon.Phase, statefulSet *appsv1.StatefulSet, + wantCalls map[string][]spltest.MockFuncCall, wantError error, initObjects ...runtime.Object) { + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + // get indexerClusterPodManager instance + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, 1) spltest.PodManagerUpdateTester(t, method, mgr, desiredReplicas, wantPhase, statefulSet, wantCalls, wantError, initObjects...) mockSplunkClient.CheckRequests(t, method) } @@ -223,7 +547,7 @@ func TestIndexerClusterPodManager(t *testing.T) { }, } method := "indexerClusterPodManager.Update(All pods ready)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseReady, statefulSet, wantCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseReady, statefulSet, wantCalls, nil, statefulSet, pod) // test pod needs update => decommission mockHandlers = append(mockHandlers, spltest.MockHTTPHandler{ @@ -236,26 +560,26 @@ func TestIndexerClusterPodManager(t *testing.T) { pod.ObjectMeta.Labels["controller-revision-hash"] = "v0" method = "indexerClusterPodManager.Update(Decommission Pod)" wantDecomPodCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4], funcCalls[2]}, "Create": {funcCalls[1]}} - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomPodCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomPodCalls, nil, statefulSet, pod) // test pod needs update => wait for decommission to complete mockHandlers = []spltest.MockHTTPHandler{mockHandlers[0], mockHandlers[1]} mockHandlers[1].Body = strings.Replace(mockHandlers[1].Body, `"status":"Up"`, `"status":"ReassigningPrimaries"`, 1) method = "indexerClusterPodManager.Update(ReassigningPrimaries)" wantReasCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4]}, "Create": {funcCalls[1]}} - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantReasCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantReasCalls, nil, statefulSet, pod) // test pod needs update => wait for decommission to complete mockHandlers[1].Body = strings.Replace(mockHandlers[1].Body, `"status":"ReassigningPrimaries"`, `"status":"Decommissioning"`, 1) method = "indexerClusterPodManager.Update(Decommissioning)" wantDecomCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4]}, "Create": {funcCalls[1]}} - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomCalls, nil, statefulSet, pod) // test pod needs update => delete pod wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4]}, "Create": {funcCalls[1]}, "Delete": {funcCalls[4]}} mockHandlers[1].Body = strings.Replace(mockHandlers[1].Body, `"status":"Decommissioning"`, `"status":"Down"`, 1) method = "indexerClusterPodManager.Update(Delete Pod)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantCalls, nil, statefulSet, pod) // test scale down => pod not found pod.ObjectMeta.Name = "splunk-stack1-2" @@ -265,7 +589,7 @@ func TestIndexerClusterPodManager(t *testing.T) { statefulSet.Status.UpdatedReplicas = 2 wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3]}, "Create": {funcCalls[1]}} method = "indexerClusterPodManager.Update(Pod Not Found)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod) // test scale down => decommission pod mockHandlers[1].Body = `{"entry":[{"name":"aa45bf46-7f46-47af-a760-590d5c606d10","content":{"status":"Up","label":"splunk-stack1-indexer-0"}},{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","content":{"status":"GracefulShutdown","label":"splunk-stack1-indexer-1"}}]}` @@ -280,15 +604,15 @@ func TestIndexerClusterPodManager(t *testing.T) { {MetaName: "*v1.PersistentVolumeClaim-test-pvc-etc-splunk-stack1-1"}, {MetaName: "*v1.PersistentVolumeClaim-test-pvc-var-splunk-stack1-1"}, } - //funcCalls[1] = spltest.MockFuncCall{MetaName: "*v1.Pod-test-splunk-stack1-0"} - wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[3]}, "Create": {funcCalls[1]}, "Delete": pvcCalls, "Update": {funcCalls[0]}} + wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[3], funcCalls[0], funcCalls[0], funcCalls[4]}, "Create": {funcCalls[1]}, "Delete": pvcCalls, "Update": {funcCalls[0], funcCalls[4]}} wantCalls["Get"] = append(wantCalls["Get"], pvcCalls...) pvcList := []*corev1.PersistentVolumeClaim{ {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc-splunk-stack1-1", Namespace: "test"}}, {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var-splunk-stack1-1", Namespace: "test"}}, } method = "indexerClusterPodManager.Update(Decommission)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod, pvcList[0], pvcList[1]) + pod.ObjectMeta.Name = "splunk-stack1-0" + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod, pvcList[0], pvcList[1]) } func TestSetClusterMaintenanceMode(t *testing.T) { @@ -386,6 +710,13 @@ func TestSetClusterMaintenanceMode(t *testing.T) { if err.Error() != splcommon.PodNotFoundError { t.Errorf("Couldn't enable cm maintenance mode %s", err.Error()) } + + // Empty clusterMaster reference + cr.Spec.ClusterMasterRef.Name = "" + err = SetClusterMaintenanceMode(c, &cr, false, true) + if err.Error() != splcommon.EmptyClusterMasterRef { + t.Errorf("Couldn't detect empty Cluster Master reference %s", err.Error()) + } } func TestApplyIdxcSecret(t *testing.T) { @@ -401,10 +732,11 @@ func TestApplyIdxcSecret(t *testing.T) { t.Errorf("Apply namespace scoped secret failed") } + podName := "splunk-stack1-indexer-0" // Create pod pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: "splunk-stack1-indexer-0", + Name: podName, Namespace: "test", Labels: map[string]string{ "controller-revision-hash": "v0", @@ -474,8 +806,8 @@ func TestApplyIdxcSecret(t *testing.T) { Namespace: "test", }, Data: map[string][]byte{ - "password": {'1', '2', '3'}, - "idxc_secret": {'a'}, + "password": {'1', '2', '3'}, + splcommon.IdxcSecret: {'a'}, }, } initObjectList = append(initObjectList, secrets) @@ -485,7 +817,7 @@ func TestApplyIdxcSecret(t *testing.T) { mockHandlers := []spltest.MockHTTPHandler{ { Method: "POST", - URL: fmt.Sprintf("https://splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local:8089/services/cluster/config/config?secret=%s", string(nsSecret.Data["idxc_secret"])), + URL: fmt.Sprintf("https://splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local:8089/services/cluster/config/config?secret=%s", string(nsSecret.Data[splcommon.IdxcSecret])), Status: 200, Err: nil, }, @@ -506,7 +838,7 @@ func TestApplyIdxcSecret(t *testing.T) { Namespace: "test", }, } - + cr.Status.IdxcPasswordChangedSecrets = make(map[string]bool) cr.Spec.ClusterMasterRef.Name = cr.GetName() mockSplunkClient := &spltest.MockHTTPClient{} mockSplunkClient.AddHandlers(mockHandlers...) @@ -537,17 +869,131 @@ func TestApplyIdxcSecret(t *testing.T) { mockSplunkClient.CheckRequests(t, method) // Don't set as it is set already + secrets.Data[splcommon.IdxcSecret] = []byte{'a'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } err = ApplyIdxcSecret(mgr, 1, true) if err != nil { t.Errorf("Couldn't apply idxc secret %s", err.Error()) } mgr.cr.Status.IndexerSecretChanged[0] = false + secrets.Data[splcommon.IdxcSecret] = []byte{'a'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } // Test set again err = ApplyIdxcSecret(mgr, 1, true) if err != nil { t.Errorf("Couldn't apply idxc secret %s", err.Error()) } + + // Test the setCmMode failure + secrets.Data[splcommon.IdxcSecret] = []byte{'a'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + mgr.cr.Status.NamespaceSecretResourceVersion = "2" + mgr.cr.Spec.ClusterMasterRef.Name = "" + mgr.cr.Status.MaintenanceMode = false + mgr.cr.Status.IndexerSecretChanged = []bool{} + err = ApplyIdxcSecret(mgr, 1, true) + if err.Error() != splcommon.EmptyClusterMasterRef { + t.Errorf("Couldn't apply idxc secret %s", err.Error()) + } + + // Remove idxc secret + secrets = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "password": {'1', '2', '3'}, + }, + } + + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyIdxcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, splcommon.IdxcSecret) { + t.Errorf("Couldn't recognize missing idxc secret %s", err.Error()) + } + + // Test scenario with same namespace secret and cr status resource version + nsSecret.ResourceVersion = "1" + mgr.cr.Status.NamespaceSecretResourceVersion = nsSecret.ResourceVersion + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyIdxcSecret(mgr, 1, true) + if err != nil { + t.Errorf("Couldn't apply idxc secret %s", err.Error()) + } + + // Test missing secret from pod + mgr.cr.Status.NamespaceSecretResourceVersion = "10" + err = splutil.DeleteResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyIdxcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.PodSecretNotFoundError, podName) { + t.Errorf("Couldn't recognize missing secret from Pod, error: %s", err.Error()) + } +} + +func TestInvalidIndexerClusterSpec(t *testing.T) { + + cr := enterprisev1.IndexerCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1", + Namespace: "test", + }, + } + + cm := enterprisev1.ClusterMaster{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterMaster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "master1", + Namespace: "test", + }, + } + + c := spltest.NewMockClient() + c.AddObject(&cm) + + cm.Status.Phase = splcommon.PhaseReady + // Empty ClusterMasterRef should return an error + cr.Spec.ClusterMasterRef.Name = "" + if _, err := ApplyIndexerCluster(c, &cr); err == nil { + t.Errorf("ApplyIndxerCluster() should have returned error") + } + + cr.Spec.ClusterMasterRef.Name = "master1" + // verifyRFPeers should return err here + if _, err := ApplyIndexerCluster(c, &cr); err == nil { + t.Errorf("ApplyIndxerCluster() should have returned error") + } + + cm.Status.Phase = splcommon.PhaseError + cr.Spec.CommonSplunkSpec.EtcStorage = "-abcd" + if _, err := ApplyIndexerCluster(c, &cr); err == nil { + t.Errorf("ApplyIndxerCluster() should have returned error") + } } func TestGetIndexerStatefulSet(t *testing.T) { @@ -575,6 +1021,9 @@ func TestGetIndexerStatefulSet(t *testing.T) { configTester(t, "getIndexerStatefulSet()", f, want) } + cr.Spec.Replicas = 0 + test(`{"kind":"StatefulSet","apiVersion":"apps/v1","metadata":{"name":"splunk-stack1-indexer","namespace":"test","creationTimestamp":null,"ownerReferences":[{"apiVersion":"","kind":"","name":"stack1","uid":"","controller":true}]},"spec":{"replicas":1,"selector":{"matchLabels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"},"annotations":{"traffic.sidecar.istio.io/excludeOutboundPorts":"8089,8191,9997,7777,9000,17000,17500,19000","traffic.sidecar.istio.io/includeInboundPorts":"8000,8088"}},"spec":{"volumes":[{"name":"mnt-splunk-secrets","secret":{"secretName":"splunk-stack1-indexer-secret-v1","defaultMode":420}}],"containers":[{"name":"splunk","image":"splunk/splunk","ports":[{"name":"splunkweb","containerPort":8000,"protocol":"TCP"},{"name":"hec","containerPort":8088,"protocol":"TCP"},{"name":"splunkd","containerPort":8089,"protocol":"TCP"},{"name":"s2s","containerPort":9997,"protocol":"TCP"}],"env":[{"name":"SPLUNK_HOME","value":"/opt/splunk"},{"name":"SPLUNK_START_ARGS","value":"--accept-license"},{"name":"SPLUNK_DEFAULTS_URL","value":"/mnt/splunk-secrets/default.yml"},{"name":"SPLUNK_HOME_OWNERSHIP_ENFORCEMENT","value":"false"},{"name":"SPLUNK_ROLE","value":"splunk_indexer"},{"name":"SPLUNK_DECLARATIVE_ADMIN_PASSWORD","value":"true"},{"name":"SPLUNK_INDEXER_URL","value":"splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local"},{"name":"SPLUNK_CLUSTER_MASTER_URL","value":"splunk-master1-cluster-master-service"}],"resources":{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"100m","memory":"512Mi"}},"volumeMounts":[{"name":"pvc-etc","mountPath":"/opt/splunk/etc"},{"name":"pvc-var","mountPath":"/opt/splunk/var"},{"name":"mnt-splunk-secrets","mountPath":"/mnt/splunk-secrets"}],"livenessProbe":{"exec":{"command":["/sbin/checkstate.sh"]},"initialDelaySeconds":300,"timeoutSeconds":30,"periodSeconds":30},"readinessProbe":{"exec":{"command":["/bin/grep","started","/opt/container_artifact/splunk-container.state"]},"initialDelaySeconds":10,"timeoutSeconds":5,"periodSeconds":5},"imagePullPolicy":"IfNotPresent"}],"securityContext":{"runAsUser":41812,"fsGroup":41812},"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"weight":100,"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/instance","operator":"In","values":["splunk-stack1-indexer"]}]},"topologyKey":"kubernetes.io/hostname"}}]}},"schedulerName":"default-scheduler"}},"volumeClaimTemplates":[{"metadata":{"name":"pvc-etc","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"10Gi"}}},"status":{}},{"metadata":{"name":"pvc-var","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"100Gi"}}},"status":{}}],"serviceName":"splunk-stack1-indexer-headless","podManagementPolicy":"Parallel","updateStrategy":{"type":"OnDelete"}},"status":{"replicas":0}}`) + cr.Spec.Replicas = 1 test(`{"kind":"StatefulSet","apiVersion":"apps/v1","metadata":{"name":"splunk-stack1-indexer","namespace":"test","creationTimestamp":null,"ownerReferences":[{"apiVersion":"","kind":"","name":"stack1","uid":"","controller":true}]},"spec":{"replicas":1,"selector":{"matchLabels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"},"annotations":{"traffic.sidecar.istio.io/excludeOutboundPorts":"8089,8191,9997,7777,9000,17000,17500,19000","traffic.sidecar.istio.io/includeInboundPorts":"8000,8088"}},"spec":{"volumes":[{"name":"mnt-splunk-secrets","secret":{"secretName":"splunk-stack1-indexer-secret-v1","defaultMode":420}}],"containers":[{"name":"splunk","image":"splunk/splunk","ports":[{"name":"splunkweb","containerPort":8000,"protocol":"TCP"},{"name":"hec","containerPort":8088,"protocol":"TCP"},{"name":"splunkd","containerPort":8089,"protocol":"TCP"},{"name":"s2s","containerPort":9997,"protocol":"TCP"}],"env":[{"name":"SPLUNK_HOME","value":"/opt/splunk"},{"name":"SPLUNK_START_ARGS","value":"--accept-license"},{"name":"SPLUNK_DEFAULTS_URL","value":"/mnt/splunk-secrets/default.yml"},{"name":"SPLUNK_HOME_OWNERSHIP_ENFORCEMENT","value":"false"},{"name":"SPLUNK_ROLE","value":"splunk_indexer"},{"name":"SPLUNK_DECLARATIVE_ADMIN_PASSWORD","value":"true"},{"name":"SPLUNK_INDEXER_URL","value":"splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local"},{"name":"SPLUNK_CLUSTER_MASTER_URL","value":"splunk-master1-cluster-master-service"}],"resources":{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"100m","memory":"512Mi"}},"volumeMounts":[{"name":"pvc-etc","mountPath":"/opt/splunk/etc"},{"name":"pvc-var","mountPath":"/opt/splunk/var"},{"name":"mnt-splunk-secrets","mountPath":"/mnt/splunk-secrets"}],"livenessProbe":{"exec":{"command":["/sbin/checkstate.sh"]},"initialDelaySeconds":300,"timeoutSeconds":30,"periodSeconds":30},"readinessProbe":{"exec":{"command":["/bin/grep","started","/opt/container_artifact/splunk-container.state"]},"initialDelaySeconds":10,"timeoutSeconds":5,"periodSeconds":5},"imagePullPolicy":"IfNotPresent"}],"securityContext":{"runAsUser":41812,"fsGroup":41812},"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"weight":100,"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/instance","operator":"In","values":["splunk-stack1-indexer"]}]},"topologyKey":"kubernetes.io/hostname"}}]}},"schedulerName":"default-scheduler"}},"volumeClaimTemplates":[{"metadata":{"name":"pvc-etc","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"10Gi"}}},"status":{}},{"metadata":{"name":"pvc-var","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"100Gi"}}},"status":{}}],"serviceName":"splunk-stack1-indexer-headless","podManagementPolicy":"Parallel","updateStrategy":{"type":"OnDelete"}},"status":{"replicas":0}}`) diff --git a/pkg/splunk/enterprise/names.go b/pkg/splunk/enterprise/names.go index b38b51ac9..4c298e83d 100644 --- a/pkg/splunk/enterprise/names.go +++ b/pkg/splunk/enterprise/names.go @@ -52,6 +52,7 @@ const ( //identifier for monitoring console configMap revision monitoringConsoleConfigRev = "monitoringConsoleConfigRev" + // identifier to track the smartstore config rev. on Pod smartStoreConfigRev = "SmartStoreConfigRev" @@ -70,9 +71,6 @@ const ( // command for init container on a CM commandForCMSmartstore = "mkdir -p /opt/splk/etc/master-apps/splunk-operator/local && ln -sfn /mnt/splunk-operator/local/indexes.conf /opt/splk/etc/master-apps/splunk-operator/local/indexes.conf && ln -sfn /mnt/splunk-operator/local/server.conf /opt/splk/etc/master-apps/splunk-operator/local/server.conf" - // Used to identify if the configMap Changed - //configMapUpdated = "configMapChanged" - //smartstoreconfigToken used to track if the config is reflecting on Pod or not configToken = "conftoken" ) diff --git a/pkg/splunk/enterprise/searchheadcluster.go b/pkg/splunk/enterprise/searchheadcluster.go index c7312a405..9cbca1408 100644 --- a/pkg/splunk/enterprise/searchheadcluster.go +++ b/pkg/splunk/enterprise/searchheadcluster.go @@ -203,13 +203,13 @@ func ApplyShcSecret(mgr *searchHeadClusterPodManager, replicas int32, mock bool) // Retrieve shc_secret password from Pod shcSecret, err := splutil.GetSpecificSecretTokenFromPod(mgr.c, shPodName, mgr.cr.GetNamespace(), "shc_secret") if err != nil { - return fmt.Errorf("Couldn't retrieve shc_secret from secret data %s", err.Error()) + return fmt.Errorf("Couldn't retrieve shc_secret from secret data") } // Retrieve admin password from Pod adminPwd, err := splutil.GetSpecificSecretTokenFromPod(mgr.c, shPodName, mgr.cr.GetNamespace(), "password") if err != nil { - return fmt.Errorf("Couldn't retrieve admin password from secret data %s", err.Error()) + return fmt.Errorf("Couldn't retrieve admin password from secret data") } // If shc secret is different from namespace scoped secret change it @@ -347,7 +347,7 @@ func (mgr *searchHeadClusterPodManager) Update(c splcommon.ControllerClient, sta } // manage scaling and updates - return splctrl.UpdateStatefulSetPods(mgr.c, statefulSet, mgr, desiredReplicas) + return splctrl.UpdateStatefulSetPods(mgr.c, statefulSet, mgr, desiredReplicas, &mgr.cr.Status.SkipRecheckUpdate) } // PrepareScaleDown for searchHeadClusterPodManager prepares search head pod to be removed via scale down event; it returns true when ready diff --git a/pkg/splunk/enterprise/searchheadcluster_test.go b/pkg/splunk/enterprise/searchheadcluster_test.go index 9eadb43e2..cf48380ad 100644 --- a/pkg/splunk/enterprise/searchheadcluster_test.go +++ b/pkg/splunk/enterprise/searchheadcluster_test.go @@ -294,8 +294,11 @@ func TestSearchHeadClusterPodManager(t *testing.T) { extraCalls := []spltest.MockFuncCall{ {MetaName: "*v1.Pod-test-splunk-stack1-search-head-1"}, {MetaName: "*v1.Pod-test-splunk-stack1-search-head-1"}, + {MetaName: "*v1.StatefulSet-test-splunk-stack1"}, + {MetaName: "*v1.StatefulSet-test-splunk-stack1"}, + {MetaName: "*v1.Pod-test-splunk-stack1-0"}, } - //funcCalls[1] = spltest.MockFuncCall{MetaName: "*v1.Pod-test-splunk-stack1-0"} + wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2]}, "Delete": pvcCalls, "Update": {funcCalls[0]}, "Create": {funcCalls[1]}} wantCalls["Get"] = append(wantCalls["Get"], extraCalls...) wantCalls["Get"] = append(wantCalls["Get"], pvcCalls...) @@ -303,7 +306,7 @@ func TestSearchHeadClusterPodManager(t *testing.T) { {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc-splunk-stack1-1", Namespace: "test"}}, {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var-splunk-stack1-1", Namespace: "test"}}, } - pod.ObjectMeta.Name = "splunk-stack1-2" + pod.ObjectMeta.Name = "splunk-stack1-0" replicas = 2 statefulSet.Status.Replicas = 2 statefulSet.Status.ReadyReplicas = 2 @@ -320,7 +323,7 @@ func TestApplyShcSecret(t *testing.T) { c := spltest.NewMockClient() // Get namespace scoped secret - _, err := splutil.ApplyNamespaceScopedSecretObject(c, "test") + nsSecret, err := splutil.ApplyNamespaceScopedSecretObject(c, "test") if err != nil { t.Errorf("Apply namespace scoped secret failed") } @@ -432,8 +435,84 @@ func TestApplyShcSecret(t *testing.T) { t.Errorf("Couldn't apply shc secret %s", err.Error()) } + // Update admin password in secret again to hit already set scenario + secrets.Data["password"] = []byte{'1'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + mgr.cr.Status.ShcSecretChanged[0] = false - // Test set again + // Test set again for shc_secret + err = ApplyShcSecret(mgr, 1, true) + if err != nil { + t.Errorf("Couldn't apply shc secret %s", err.Error()) + } + + // Update admin password in secret again to hit already set scenario + secrets.Data["password"] = []byte{'1'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + mgr.cr.Status.ShcSecretChanged[0] = false + mgr.cr.Status.AdminSecretChanged[0] = false + // Test set again for admin password + err = ApplyShcSecret(mgr, 1, true) + if err != nil { + t.Errorf("Couldn't apply shc secret %s", err.Error()) + } + + // Missing shc_secret scenario + secrets = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "password": {'1', '2', '3'}, + }, + } + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyShcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, "shc_secret") { + t.Errorf("Couldn't recognize missing shc_secret %s", err.Error()) + } + + // Missing admin password scenario + secrets = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "shc_secret": {'a'}, + }, + } + + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyShcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, "admin password") { + t.Errorf("Couldn't recognize missing admin password %s", err.Error()) + } + + // Make resource version of ns secret and cr the same + mgr.cr.Status.NamespaceSecretResourceVersion = "1" + nsSecret.ResourceVersion = mgr.cr.Status.NamespaceSecretResourceVersion + err = splutil.UpdateResource(c, nsSecret) + if err != nil { + t.Errorf("Couldn't update resource") + } + err = ApplyShcSecret(mgr, 1, true) if err != nil { t.Errorf("Couldn't apply shc secret %s", err.Error()) diff --git a/pkg/splunk/enterprise/util_test.go b/pkg/splunk/enterprise/util_test.go index d837dfc9e..c72ef8a33 100644 --- a/pkg/splunk/enterprise/util_test.go +++ b/pkg/splunk/enterprise/util_test.go @@ -19,7 +19,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" @@ -29,26 +28,6 @@ import ( ) func init() { - spltest.MockObjectCopiers = append(spltest.MockObjectCopiers, enterpriseObjectCopier) -} - -// enterpriseObjectCopier is used to copy enterprise runtime.Objects -func enterpriseObjectCopier(dst, src runtime.Object) bool { - switch src.(type) { - case *enterprisev1.IndexerCluster: - *dst.(*enterprisev1.IndexerCluster) = *src.(*enterprisev1.IndexerCluster) - case *enterprisev1.LicenseMaster: - *dst.(*enterprisev1.LicenseMaster) = *src.(*enterprisev1.LicenseMaster) - case *enterprisev1.SearchHeadCluster: - *dst.(*enterprisev1.SearchHeadCluster) = *src.(*enterprisev1.SearchHeadCluster) - case *enterprisev1.Spark: - *dst.(*enterprisev1.Spark) = *src.(*enterprisev1.Spark) - case *enterprisev1.Standalone: - *dst.(*enterprisev1.Standalone) = *src.(*enterprisev1.Standalone) - default: - return false - } - return true } func TestApplySplunkConfig(t *testing.T) { @@ -202,6 +181,13 @@ func TestApplySmartstoreConfigMap(t *testing.T) { } test(client, &cr, &cr.Spec.SmartStore, `{"metadata":{"name":"splunk-idxCluster--smartstore","namespace":"test","creationTimestamp":null},"data":{"conftoken":"1601945361","indexes.conf":"[default]\nrepFactor = auto\nmaxDataSize = auto\nhomePath = $SPLUNK_DB/$_index_name/db\ncoldPath = $SPLUNK_DB/$_index_name/colddb\nthawedPath = $SPLUNK_DB/$_index_name/thaweddb\n \n[volume:msos_s2s3_vol]\nstorageType = remote\npath = s3://testbucket-rs-london\nremote.s3.access_key = abcdJDckRkxhMEdmSk5FekFRRzBFOXV6bGNldzJSWE9IenhVUy80aa\nremote.s3.secret_key = g4NVp0a29PTzlPdGczWk1vekVUcVBSa0o4NkhBWWMvR1NadDV4YVEy\nremote.s3.endpoint = https://s3-eu-west-2.amazonaws.com\n \n[salesdata1]\nremotePath = volume:msos_s2s3_vol/remotepath1\n\n[salesdata2]\nremotePath = volume:msos_s2s3_vol/remotepath2\n\n[salesdata3]\nremotePath = volume:msos_s2s3_vol/remotepath3\n","server.conf":""}}`) + + // Missing Volume config should return an error + cr.Spec.SmartStore.VolList = nil + _, _, err = ApplySmartstoreConfigMap(client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Configuring Indexes without volumes should return an error") + } } func TestRemoveOwenerReferencesForSecretObjectsReferredBySmartstoreVolumes(t *testing.T) { @@ -214,6 +200,9 @@ func TestRemoveOwenerReferencesForSecretObjectsReferredBySmartstoreVolumes(t *te SmartStore: enterprisev1.SmartStoreSpec{ VolList: []enterprisev1.VolumeSpec{ {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + {Name: "msos_s2s3_vol_2", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + {Name: "msos_s2s3_vol_3", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + {Name: "msos_s2s3_vol_4", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, }, IndexList: []enterprisev1.IndexSpec{ @@ -260,4 +249,85 @@ func TestRemoveOwenerReferencesForSecretObjectsReferredBySmartstoreVolumes(t *te if err != nil { t.Errorf("Couldn't Remove S3 Secret object references %v", err) } + + // If the secret object doesn't exist, should return an error + // Here in the volume references, secrets splunk-test-sec_1, to splunk-test-sec_4 doesn't exist + cr = enterprisev1.ClusterMaster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "idxCluster", + Namespace: "testWithNoSecret", + }, + Spec: enterprisev1.ClusterMasterSpec{ + SmartStore: enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_1"}, + {Name: "msos_s2s3_vol_2", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_2"}, + {Name: "msos_s2s3_vol_3", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_3"}, + {Name: "msos_s2s3_vol_4", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_4"}, + }, + }, + }, + } + + // S3 secret owner reference removal, with non-existing secret objects + err = DeleteOwnerReferencesForS3SecretObjects(client, secret, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Should report an error, when the secret object referenced in the volume config doesn't exist") + } + + // Smartstore volume config with non-existing secret objects + err = DeleteOwnerReferencesForResources(client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Should report an error, when the secret objects doesn't exist") + } +} + +func TestGetSmartstoreRemoteVolumeSecrets(t *testing.T) { + cr := enterprisev1.ClusterMaster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "CM", + Namespace: "test", + }, + Spec: enterprisev1.ClusterMasterSpec{ + SmartStore: enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + }, + }, + }, + } + + client := spltest.NewMockClient() + + // Just to simplify the test, assume that the keys are stored as part of the splunk-test-secret object, hence create that secret object + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") + if err != nil { + t.Errorf(err.Error()) + } + + _, err = splctrl.ApplySecret(client, secret) + if err != nil { + t.Errorf(err.Error()) + } + + // Missing S3 access key should return error + _, _, _, err = GetSmartstoreRemoteVolumeSecrets(cr.Spec.SmartStore.VolList[0], client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Missing S3 access key should return an error") + } + + secret.Data[s3AccessKey] = []byte("abcdJDckRkxhMEdmSk5FekFRRzBFOXV6bGNldzJSWE9IenhVUy80aa") + + // Missing S3 secret key should return error + _, _, _, err = GetSmartstoreRemoteVolumeSecrets(cr.Spec.SmartStore.VolList[0], client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Missing S3 secret key should return an error") + } + + // When access key and secret keys are present, returned keys should not be empty. Also, should not return an error + secret.Data[s3SecretKey] = []byte("g4NVp0a29PTzlPdGczWk1vekVUcVBSa0o4NkhBWWMvR1NadDV4YVEy") + accessKey, secretKey, _, err := GetSmartstoreRemoteVolumeSecrets(cr.Spec.SmartStore.VolList[0], client, &cr, &cr.Spec.SmartStore) + if accessKey == "" || secretKey == "" || err != nil { + t.Errorf("Missing S3 Keys / Error not expected, when the Secret object with the S3 specific keys are present") + } } diff --git a/pkg/splunk/spark/spark_test.go b/pkg/splunk/spark/spark_test.go index 0aef6172e..7a9fb8711 100644 --- a/pkg/splunk/spark/spark_test.go +++ b/pkg/splunk/spark/spark_test.go @@ -34,10 +34,12 @@ func init() { } // sparkObjectCopier is used to copy enterprisev1.Spark runtime.Objects -func sparkObjectCopier(dst, src runtime.Object) bool { - switch src.(type) { +func sparkObjectCopier(dst, src *runtime.Object) bool { + dstP := *dst + srcP := *src + switch srcP.(type) { case *enterprisev1.Spark: - *dst.(*enterprisev1.Spark) = *src.(*enterprisev1.Spark) + *dstP.(*enterprisev1.Spark) = *srcP.(*enterprisev1.Spark) default: return false } diff --git a/pkg/splunk/test/controller.go b/pkg/splunk/test/controller.go index da420800e..099177361 100644 --- a/pkg/splunk/test/controller.go +++ b/pkg/splunk/test/controller.go @@ -21,6 +21,7 @@ import ( "reflect" "testing" + enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,7 +32,7 @@ import ( ) func init() { - MockObjectCopiers = append(MockObjectCopiers, coreObjectCopier, appsObjectCopier) + MockObjectCopiers = append(MockObjectCopiers, coreObjectCopier, appsObjectCopier, enterpriseObjCopier) } // MockObjectCopiers is a slice of MockObjectCopier methods that MockClient uses to copy runtime.Objects @@ -39,26 +40,51 @@ var MockObjectCopiers []MockObjectCopier // MockObjectCopier is a method used to perform the typed copy of a runtime.Object from src to dst. // It returns true if the runtime.Object was copied, or false if the type is unknown. -type MockObjectCopier func(dst, src runtime.Object) bool +type MockObjectCopier func(dst, src *runtime.Object) bool + +// enterpriseObjCopier is used to copy enterprise runtime.Objects +func enterpriseObjCopier(dst, src *runtime.Object) bool { + dstP := *dst + srcP := *src + switch srcP.(type) { + case *enterprisev1.ClusterMaster: + *dstP.(*enterprisev1.ClusterMaster) = *srcP.(*enterprisev1.ClusterMaster) + case *enterprisev1.IndexerCluster: + *dstP.(*enterprisev1.IndexerCluster) = *srcP.(*enterprisev1.IndexerCluster) + case *enterprisev1.LicenseMaster: + *dstP.(*enterprisev1.LicenseMaster) = *srcP.(*enterprisev1.LicenseMaster) + case *enterprisev1.SearchHeadCluster: + *dstP.(*enterprisev1.SearchHeadCluster) = *srcP.(*enterprisev1.SearchHeadCluster) + case *enterprisev1.Spark: + *dstP.(*enterprisev1.Spark) = *srcP.(*enterprisev1.Spark) + case *enterprisev1.Standalone: + *dstP.(*enterprisev1.Standalone) = *srcP.(*enterprisev1.Standalone) + default: + return false + } + return true +} // coreObjectCopier is used to copy corev1 runtime.Objects -func coreObjectCopier(dst, src runtime.Object) bool { - if reflect.TypeOf(dst).String() == reflect.TypeOf(src.(runtime.Object)).String() { - switch src.(type) { +func coreObjectCopier(dst, src *runtime.Object) bool { + dstP := *dst + srcP := *src + if reflect.TypeOf(dstP).String() == reflect.TypeOf((*src).(runtime.Object)).String() { + switch (srcP).(type) { case *corev1.ConfigMap: - *dst.(*corev1.ConfigMap) = *src.(*corev1.ConfigMap) + *dstP.(*corev1.ConfigMap) = *srcP.(*corev1.ConfigMap) case *corev1.Secret: - *dst.(*corev1.Secret) = *src.(*corev1.Secret) + *dstP.(*corev1.Secret) = *srcP.(*corev1.Secret) case *corev1.SecretList: - *dst.(*corev1.SecretList) = *src.(*corev1.SecretList) + *dstP.(*corev1.SecretList) = *srcP.(*corev1.SecretList) case *corev1.PersistentVolumeClaim: - *dst.(*corev1.PersistentVolumeClaim) = *src.(*corev1.PersistentVolumeClaim) + *dstP.(*corev1.PersistentVolumeClaim) = *srcP.(*corev1.PersistentVolumeClaim) case *corev1.PersistentVolumeClaimList: - *dst.(*corev1.PersistentVolumeClaimList) = *src.(*corev1.PersistentVolumeClaimList) + *dstP.(*corev1.PersistentVolumeClaimList) = *srcP.(*corev1.PersistentVolumeClaimList) case *corev1.Service: - *dst.(*corev1.Service) = *src.(*corev1.Service) + *dstP.(*corev1.Service) = *srcP.(*corev1.Service) case *corev1.Pod: - *dst.(*corev1.Pod) = *src.(*corev1.Pod) + *dstP.(*corev1.Pod) = *srcP.(*corev1.Pod) default: return false } @@ -67,12 +93,14 @@ func coreObjectCopier(dst, src runtime.Object) bool { } // appsObjectCopier is used to copy appsv1 runtime.Objects -func appsObjectCopier(dst, src runtime.Object) bool { - switch src.(type) { +func appsObjectCopier(dst, src *runtime.Object) bool { + srcP := *src + dstP := *dst + switch srcP.(type) { case *appsv1.Deployment: - *dst.(*appsv1.Deployment) = *src.(*appsv1.Deployment) + *dstP.(*appsv1.Deployment) = *srcP.(*appsv1.Deployment) case *appsv1.StatefulSet: - *dst.(*appsv1.StatefulSet) = *src.(*appsv1.StatefulSet) + *dstP.(*appsv1.StatefulSet) = *srcP.(*appsv1.StatefulSet) default: return false } @@ -80,15 +108,15 @@ func appsObjectCopier(dst, src runtime.Object) bool { } // copyMockObject uses the global MockObjectCopiers to perform the typed copy of a runtime.Object from src to dst -func copyMockObject(dst, src runtime.Object) { +func copyMockObject(dst, src *runtime.Object) { for n := range MockObjectCopiers { if MockObjectCopiers[n](dst, src) { return } } - + srcP := *src // default if no types match - dst = src.DeepCopyObject() + *dst = srcP.DeepCopyObject() } // MockFuncCall is used to record a function call to MockClient methods @@ -156,8 +184,10 @@ func (c MockClient) Get(ctx context.Context, key client.ObjectKey, obj runtime.O Obj: obj, }) getObj := c.State[getStateKeyWithKey(key, obj)] + if getObj != nil { - copyMockObject(obj, getObj.(runtime.Object)) + srcObj := getObj.(runtime.Object) + copyMockObject(&obj, &srcObj) return nil } return c.NotFoundError @@ -172,7 +202,8 @@ func (c MockClient) List(ctx context.Context, obj runtime.Object, opts ...client }) listObj := c.ListObj if listObj != nil { - copyMockObject(obj, listObj.(runtime.Object)) + srcObj := listObj.(runtime.Object) + copyMockObject(&obj, &srcObj) return nil } return c.NotFoundError @@ -420,8 +451,11 @@ func PodManagerUpdateTester(t *testing.T, method string, mgr splcommon.StatefulS // PodManagerTester is used to test StatefulSetPodManager reconcile operations func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodManager) { // test create - funcCalls := []MockFuncCall{{MetaName: "*v1.StatefulSet-test-splunk-stack1"}} - createCalls := map[string][]MockFuncCall{"Get": funcCalls, "Create": funcCalls} + funcCalls := []MockFuncCall{ + {MetaName: "*v1.StatefulSet-test-splunk-stack1"}, + {MetaName: "*v1.Pod-test-splunk-stack1-0"}, + } + createCalls := map[string][]MockFuncCall{"Get": {funcCalls[0]}, "Create": {funcCalls[0]}} var replicas int32 = 1 current := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ @@ -442,19 +476,35 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM UpdateRevision: "v1", }, } + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + PodManagerUpdateTester(t, method, mgr, 1, splcommon.PhasePending, current, createCalls, nil) // test update revised := current.DeepCopy() revised.Spec.Template.ObjectMeta.Labels = map[string]string{"one": "two"} - updateCalls := map[string][]MockFuncCall{"Get": funcCalls, "Update": funcCalls} + updateCalls := map[string][]MockFuncCall{"Get": {funcCalls[0]}, "Update": {funcCalls[0]}} methodPlus := fmt.Sprintf("%s(%s)", method, "Update StatefulSet") PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseUpdating, revised, updateCalls, nil, current) // test scale up (zero ready so far; wait for ready) revised = current.DeepCopy() current.Status.ReadyReplicas = 0 - scaleUpCalls := map[string][]MockFuncCall{"Get": funcCalls} + scaleUpCalls := map[string][]MockFuncCall{"Get": {funcCalls[0]}} methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingUp, 0 ready") PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhasePending, revised, scaleUpCalls, nil, current) @@ -462,22 +512,26 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM replicas = 2 current.Status.Replicas = 2 current.Status.ReadyReplicas = 1 + scaleUpCalls["Get"] = append(scaleUpCalls["Get"], funcCalls[0], funcCalls[0], funcCalls[1]) + scaleUpCalls["Update"] = append(scaleUpCalls["Update"], funcCalls[1]) methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingUp, 1/2 ready") - PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, scaleUpCalls, nil, current) + PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, scaleUpCalls, nil, current, pod) // test scale up (1 ready scaling to 2) replicas = 1 current.Status.Replicas = 1 current.Status.ReadyReplicas = 1 + updateCalls["Get"] = append(updateCalls["Get"], funcCalls[0], funcCalls[0], funcCalls[1]) methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingUp, Update Replicas 1=>2") - PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, updateCalls, nil, current) + PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, updateCalls, nil, current, pod) // test scale down (2 ready, 1 desired) replicas = 1 current.Status.Replicas = 1 current.Status.ReadyReplicas = 2 + delete(scaleUpCalls, "Update") methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingDown, Ready > Replicas") - PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleUpCalls, nil, current) + PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleUpCalls, nil, current, pod) // test scale down (2 ready scaling down to 1) pvcCalls := []MockFuncCall{ @@ -485,7 +539,7 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM {MetaName: "*v1.PersistentVolumeClaim-test-pvc-var-splunk-stack1-1"}, } scaleDownCalls := map[string][]MockFuncCall{ - "Get": {funcCalls[0], pvcCalls[0], pvcCalls[1]}, + "Get": {funcCalls[0], funcCalls[0], funcCalls[0], funcCalls[1], pvcCalls[0], pvcCalls[1]}, "Update": {funcCalls[0]}, "Delete": pvcCalls, } @@ -497,7 +551,7 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM current.Status.Replicas = 2 current.Status.ReadyReplicas = 2 methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingDown, Update Replicas 2=>1") - PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleDownCalls, nil, current, pvcList[0], pvcList[1]) + PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleDownCalls, nil, current, pod, pvcList[0], pvcList[1]) // test pod not found replicas = 1 @@ -508,30 +562,6 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM methodPlus = fmt.Sprintf("%s(%s)", method, "Pod not found") PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseError, revised, getPodCalls, errors.New("NotFound"), current) - // test pod updated - pod := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "splunk-stack1-0", - Namespace: "test", - Labels: map[string]string{ - "controller-revision-hash": "v0", - }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - ContainerStatuses: []corev1.ContainerStatus{ - {Ready: true}, - }, - }, - } - updatePodCalls := map[string][]MockFuncCall{"Get": podCalls, "Delete": {podCalls[1]}} - methodPlus = fmt.Sprintf("%s(%s)", method, "Recycle pod") - PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseUpdating, revised, updatePodCalls, nil, current, pod) - - // test all pods ready - pod.ObjectMeta.Labels["controller-revision-hash"] = "v1" - methodPlus = fmt.Sprintf("%s(%s)", method, "All pods ready") - labels := map[string]string{ "app.kubernetes.io/component": "versionedSecrets", "app.kubernetes.io/managed-by": "splunk-operator", @@ -543,6 +573,14 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM listmockCall := []MockFuncCall{ {ListOpts: listOpts}} + updatePodCalls := map[string][]MockFuncCall{"Get": podCalls, "Delete": {}, "List": {listmockCall[0]}} + methodPlus = fmt.Sprintf("%s(%s)", method, "Recycle pod") + PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseReady, revised, updatePodCalls, nil, current, pod) + + // test all pods ready + pod.ObjectMeta.Labels["controller-revision-hash"] = "v1" + methodPlus = fmt.Sprintf("%s(%s)", method, "All pods ready") + getPodCalls["List"] = []MockFuncCall{listmockCall[0]} PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseReady, revised, getPodCalls, nil, current, pod) diff --git a/pkg/splunk/util/messages.go b/pkg/splunk/util/messages.go index 36c5b67f5..74b9dd75d 100644 --- a/pkg/splunk/util/messages.go +++ b/pkg/splunk/util/messages.go @@ -32,4 +32,10 @@ const ( // emptySecretTokenError indicates empty secret token emptySecretTokenError = "Empty secret token" + + // nonExistingSecret indicates a non-existing secret + emptyPodSpecVolumes = "Empty pod spec volumes" + + // emptySecretVolumeSource indicates an empty + emptySecretVolumeSource = "Didn't find secret volume source in any pod volume" ) diff --git a/pkg/splunk/util/secrets.go b/pkg/splunk/util/secrets.go index 538130da0..de9901f35 100644 --- a/pkg/splunk/util/secrets.go +++ b/pkg/splunk/util/secrets.go @@ -74,7 +74,7 @@ func GetSecretFromPod(c splcommon.ControllerClient, PodName string, namespace st var found bool = false for i := range podSpecVolumes { - if podSpecVolumes[i].Name == "mnt-splunk-secrets" { + if podSpecVolumes[i].Name == "mnt-splunk-secrets" && podSpecVolumes[i].VolumeSource.Size() > 0 { secretName = podSpecVolumes[i].VolumeSource.Secret.SecretName if len(secretName) > 0 { found = true @@ -85,7 +85,7 @@ func GetSecretFromPod(c splcommon.ControllerClient, PodName string, namespace st // Check if we find the secret if !found { - return nil, errors.New("Didn't find secret in any pod volume") + return nil, errors.New("Didn't find secret volume source in any pod volume") } // Retrieve the secret @@ -175,13 +175,7 @@ func RemoveUnwantedSecrets(c splcommon.ControllerClient, versionedSecretIdentifi } // Atleast one exists - for _, secret := range list { - // Get version from name - version, err := GetVersionedSecretVersion(secret.GetName(), versionedSecretIdentifier) - if err != nil { - return err - } - + for version, secret := range list { if (latestVersion - version) >= splcommon.MinimumVersionedSecrets { // Delete secret err := DeleteResource(c, &secret) @@ -236,7 +230,7 @@ func GetVersionedSecretVersion(secretName string, versionedSecretIdentifier stri } // GetExistingLatestVersionedSecret retrieves latest EXISTING versionedSecretIdentifier based secret existing currently in the namespace -func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace string, versionedSecretIdentifier string, list bool) (*corev1.Secret, int, []corev1.Secret) { +func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace string, versionedSecretIdentifier string, list bool) (*corev1.Secret, int, map[int]corev1.Secret) { scopedLog := log.WithName("GetExistingLatestVersionedSecret").WithValues( "versionedSecretIdentifier", versionedSecretIdentifier, "namespace", namespace) @@ -265,8 +259,8 @@ func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace st // existingLatestVersionedSecret holds the latest versionedSecretIdentifier based secret, if atleast one exists var existingLatestVersionedSecret corev1.Secret - // List of versionedSecretIdentifier based secrets - var secretListRetr []corev1.Secret + // map of versionedSecretIdentifier based secrets + secretListRetr := make(map[int]corev1.Secret) // Loop through all secrets in K8S cluster for _, secret := range secretList.Items { @@ -279,7 +273,7 @@ func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace st // Append to list of secrets if required if list { - secretListRetr = append(secretListRetr, secret) + secretListRetr[version] = secret } // Version extracted successfully, checking for latest version diff --git a/pkg/splunk/util/secrets_test.go b/pkg/splunk/util/secrets_test.go index 9604b6a51..2a4f57ea3 100644 --- a/pkg/splunk/util/secrets_test.go +++ b/pkg/splunk/util/secrets_test.go @@ -114,11 +114,35 @@ func TestGetSpecificSecretTokenFromPod(t *testing.T) { if err != nil { t.Errorf("Couldn't update secret %s", current.GetName()) } + // Retrieve secret data from non-existing pod gotData, err = GetSpecificSecretTokenFromPod(c, pod.GetName(), "test", "key1") if err.Error() != invalidSecretDataError { t.Errorf("Didn't recognize nil secret data") } + + // Update pod to remove pod spec volumes + pod = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + } + + // Update pod spec to remove the volume mount + err = UpdateResource(c, pod) + if err != nil { + t.Errorf("Failed to update pod %s", pod.GetName()) + } + + // Retrieve secret data from non-existing pod + gotData, err = GetSpecificSecretTokenFromPod(c, pod.GetName(), "test", "key1") + if err.Error() != emptyPodSpecVolumes { + t.Errorf("Didn't recognize empty pod spec volumes") + } } func TestGetSecretFromPod(t *testing.T) { @@ -204,6 +228,70 @@ func TestGetSecretFromPod(t *testing.T) { if err.Error() != splcommon.SecretNotFoundError { t.Errorf("Didn't recognize non-existing secret %s", "test") } + + // Update pod to remove pod spec volumes + pod = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + } + + // Update pod spec to remove the volume mount + err = UpdateResource(c, pod) + if err != nil { + t.Errorf("Failed to update pod %s", pod.GetName()) + } + + // Retrieve secret data from Pod + gotSecret, err = GetSecretFromPod(c, pod.GetName(), "test") + if err.Error() != emptyPodSpecVolumes { + t.Errorf("Couldn't recognize empty pod spec volumes") + } + + // Update pod spec to add volume mount but not a secret + pod = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/mnt/splunk-secrets", + Name: "mnt-splunk-secrets", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "mnt-splunk-secrets", + }, + }, + }, + } + + // Update pod spec to remove the volume mount + err = UpdateResource(c, pod) + if err != nil { + t.Errorf("Failed to update pod %s", pod.GetName()) + } + + // Retrieve secret data from Pod + gotSecret, err = GetSecretFromPod(c, pod.GetName(), "test") + if err.Error() != emptySecretVolumeSource { + t.Errorf("Couldn't recognize empty pod spec volumes") + } + } func TestGetSecretLabels(t *testing.T) { @@ -249,15 +337,18 @@ func TestSetSecretOwnerRef(t *testing.T) { t.Errorf("Failed to create secret %s", current.GetName()) } - // Reset secret owner reference - // current.OwnerReferences = nil - - // Test existing secret + // Test adding secret owner reference err = SetSecretOwnerRef(c, current.GetName(), &cr) if err != nil { t.Errorf("Couldn't set owner ref for secret %s", current.GetName()) } + // Test existing secret owner reference + err = SetSecretOwnerRef(c, current.GetName(), &cr) + if err != nil { + t.Errorf("Couldn't bail out once owner ref for secret %s is already set", current.GetName()) + } + removedReferralCount, err := RemoveSecretOwnerRef(c, current.GetName(), &cr) if removedReferralCount == 0 || err != nil { @@ -265,6 +356,21 @@ func TestSetSecretOwnerRef(t *testing.T) { } } +func TestRemoveSecretOwnerRef(t *testing.T) { + c := spltest.NewMockClient() + cr := TestResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1", + Namespace: "test", + }, + } + // Test secret not found error condition + _, err := RemoveSecretOwnerRef(c, "testSecretName", &cr) + if err.Error() != "NotFound" { + t.Errorf("Couldn't find secret not found error") + } + +} func TestRemoveUnwantedSecrets(t *testing.T) { var current corev1.Secret var err error @@ -327,7 +433,7 @@ func TestRemoveUnwantedSecrets(t *testing.T) { // Add an invalid secret to test error leg current = corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "random", + Name: splcommon.GetVersionedSecretName(versionedSecretIdentifier, "-1"), Namespace: "test", }, Data: make(map[string][]byte), @@ -428,7 +534,7 @@ func TestGetVersionedSecretVersion(t *testing.T) { // Negative testing with non-integer version for testVersion := 0; testVersion < 10; testVersion++ { - testSecretName = splcommon.GetVersionedSecretName(versionedSecretIdentifier, string('A'-1+testVersion)) + testSecretName = splcommon.GetVersionedSecretName(versionedSecretIdentifier, string(rune('A'-1+testVersion))) _, err := GetVersionedSecretVersion(testSecretName, versionedSecretIdentifier) if err.Error() != nonIntegerVersionError { t.Errorf("Failed to detect incorrect versioning") diff --git a/test/deploy-eks-cluster.sh b/test/deploy-eks-cluster.sh index c379a5653..6d8a3abb3 100755 --- a/test/deploy-eks-cluster.sh +++ b/test/deploy-eks-cluster.sh @@ -20,7 +20,7 @@ function createCluster() { found=$(eksctl get cluster --name "${CLUSTER_NAME}") if [ -z "${found}" ]; then - eksctl create cluster --name=${CLUSTER_NAME} --nodes=${NUM_WORKERS} + eksctl create cluster --name=${CLUSTER_NAME} --nodes=${NUM_WORKERS} --vpc-public-subnets=${VPC_PUBLIC_SUBNET_STRING} --vpc-private-subnets=${VPC_PRIVATE_SUBNET_STRING} if [ $? -ne 0 ]; then echo "Unable to create cluster - ${CLUSTER_NAME}" return 1 diff --git a/test/env.sh b/test/env.sh index 12967ca8a..a9c092238 100644 --- a/test/env.sh +++ b/test/env.sh @@ -8,6 +8,8 @@ : "${NUM_NODES:=2}" : "${COMMIT_HASH:=}" : "${ECR_REGISTRY:=}" +: "${VPC_PUBLIC_SUBNET_STRING:=}" +: "${VPC_PRIVATE_SUBNET_STRING:=}" # Docker registry to use to push the test images to and pull from in the cluster if [ -z "${PRIVATE_REGISTRY}" ]; then diff --git a/test/ingest_search/ingest_search_suite_test.go b/test/ingest_search/ingest_search_suite_test.go new file mode 100644 index 000000000..ff71d88e6 --- /dev/null +++ b/test/ingest_search/ingest_search_suite_test.go @@ -0,0 +1,47 @@ +package ingestsearchtest + +import ( + "testing" + "time" + + . "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/reporters" + . "github.com/onsi/gomega" + + "github.com/splunk/splunk-operator/test/testenv" +) + +const ( + // PollInterval specifies the polling interval + PollInterval = 5 * time.Second + + // ConsistentPollInterval is the interval to use to consistently check a state is stable + ConsistentPollInterval = 200 * time.Millisecond + ConsistentDuration = 2000 * time.Millisecond +) + +var ( + testenvInstance *testenv.TestEnv + testSuiteName = "mc-" + testenv.RandomDNSName(2) +) + +// TestBasic is the main entry point +func TestBasic(t *testing.T) { + + RegisterFailHandler(Fail) + + junitReporter := reporters.NewJUnitReporter(testSuiteName + "_junit.xml") + RunSpecsWithDefaultAndCustomReporters(t, "Running "+testSuiteName, []Reporter{junitReporter}) +} + +var _ = BeforeSuite(func() { + var err error + testenvInstance, err = testenv.NewDefaultTestEnv(testSuiteName) + Expect(err).ToNot(HaveOccurred()) +}) + +var _ = AfterSuite(func() { + if testenvInstance != nil { + Expect(testenvInstance.Teardown()).ToNot(HaveOccurred()) + } +}) diff --git a/test/ingest_search/ingest_search_test.go b/test/ingest_search/ingest_search_test.go new file mode 100644 index 000000000..ae1492372 --- /dev/null +++ b/test/ingest_search/ingest_search_test.go @@ -0,0 +1,236 @@ +package ingestsearchtest + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + "strings" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" + "github.com/splunk/splunk-operator/test/testenv" +) + +var _ = Describe("Ingest and Search Test", func() { + + var deployment *testenv.Deployment + var firstLine string + + BeforeEach(func() { + var err error + deployment, err = testenvInstance.NewDeployment(testenv.RandomDNSName(3)) + Expect(err).To(Succeed(), "Unable to create deployment") + }) + + AfterEach(func() { + // When a test spec failed, skip the teardown so we can troubleshoot. + if CurrentGinkgoTestDescription().Failed { + testenvInstance.SkipTeardown = true + } + if deployment != nil { + deployment.Teardown() + } + }) + + Context("Standalone deployment (S1)", func() { + It("can search internal logs for standalone instance", func() { + + standalone, err := deployment.DeployStandalone(deployment.GetName()) + Expect(err).To(Succeed(), "Unable to deploy standalone instance ") + + // Wait for standalone to be in READY Status + testenv.StandaloneReady(deployment, deployment.GetName(), standalone, testenvInstance) + + Eventually(func() splcommon.Phase { + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + + searchString := "index=_internal | stats count by host" + searchResultsResp, err := testenv.PerformSearchSync(podName, searchString, deployment) + if err != nil { + testenvInstance.Log.Error(err, "Failed to execute search on pod", "pod", podName, "searchString", searchString) + return splcommon.PhaseError + } + testenvInstance.Log.Info("Performed a search", "searchString", searchString) + + var searchResults map[string]interface{} + unmarshalErr := json.Unmarshal([]byte(searchResultsResp), &searchResults) + if unmarshalErr != nil { + testenvInstance.Log.Error(unmarshalErr, "Failed to unmarshal JSON response") + } + + prettyResults, jsonErr := json.MarshalIndent(searchResults, "", " ") + if jsonErr != nil { + testenvInstance.Log.Error(jsonErr, "Failed to generate pretty json") + } else { + testenvInstance.Log.Info("Sync Search results:", "prettyResults", string(prettyResults)) + } + + return standalone.Status.Phase + }, deployment.GetTimeout(), PollInterval).Should(Equal(splcommon.PhaseReady)) + + Eventually(func() splcommon.Phase { + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + searchString := "index=_internal GUID component=ServerConfig" + + // Perform a simple search + sid, reqErr := testenv.PerformSearchReq(podName, searchString, deployment) + if reqErr != nil { + testenvInstance.Log.Error(reqErr, "Failed to execute search on pod", "pod", podName, "searchString", searchString) + return splcommon.PhaseError + } + testenvInstance.Log.Info("Got a search with sid", "sid", sid) + + // Check SID status until done + searchStatusResult, statusErr := testenv.GetSearchStatus(podName, sid, deployment) + if statusErr != nil { + testenvInstance.Log.Error(statusErr, "Failed to get search status on pod", "pod", podName, "sid", sid) + return splcommon.PhaseError + } + testenvInstance.Log.Info("Search status:", "searchStatusResult", searchStatusResult) + + // Get SID results + searchResultsResp, resErr := testenv.GetSearchResults(podName, sid, deployment) + if resErr != nil { + testenvInstance.Log.Error(resErr, "Failed to get search results on pod", "pod", podName, "sid", sid) + return splcommon.PhaseError + } + + // Display results for debug purposes + prettyResults, jsonErr := json.MarshalIndent(searchResultsResp, "", " ") + if jsonErr != nil { + testenvInstance.Log.Error(jsonErr, "Failed to generate pretty json") + } else { + testenvInstance.Log.Info("Search results:", "prettyResults", string(prettyResults)) + } + + return standalone.Status.Phase + }, deployment.GetTimeout(), PollInterval).Should(Equal(splcommon.PhaseReady)) + }) + }) + + Context("Standalone deployment (S1)", func() { + It("can ingest custom data to new index and search", func() { + + standalone, err := deployment.DeployStandalone(deployment.GetName()) + Expect(err).To(Succeed(), "Unable to deploy standalone instance ") + + // Wait for standalone to be in READY Status + testenv.StandaloneReady(deployment, deployment.GetName(), standalone, testenvInstance) + + // Verify splunk status is up + Eventually(func() splcommon.Phase { + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + + splunkBin := "/opt/splunk/bin/splunk" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + splunkCmd := "status" + + statusCmd := fmt.Sprintf("%s %s -auth %s:%s", splunkBin, splunkCmd, username, password) + command := []string{"/bin/bash"} + statusCmdResp, stderr, err := deployment.PodExecCommand(podName, command, statusCmd, false) + if err != nil { + testenvInstance.Log.Error(err, "Failed to execute command on pod", "pod", podName, "statusCmd", statusCmd, "statusCmdResp", statusCmdResp, "stderr", stderr) + return splcommon.PhaseError + } + + if !strings.Contains(strings.ToLower(statusCmdResp), strings.ToLower("splunkd is running")) { + testenvInstance.Log.Error(err, "Failed to find splunkd running", "pod", podName, "statusCmdResp", statusCmdResp) + return splcommon.PhaseError + } + + testenvInstance.Log.Info("Waiting for standalone splunkd status to be ready", "instance", standalone.ObjectMeta.Name, "Phase", standalone.Status.Phase) + return standalone.Status.Phase + }, deployment.GetTimeout(), PollInterval).Should(Equal(splcommon.PhaseReady)) + + // Create an index + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + indexName := "myTestIndex" + + // Create an index on a standalone instance + err = testenv.CreateAnIndexStandalone(indexName, podName, deployment) + Expect(err).To(Succeed(), "Failed response to add index to splunk") + + // Create a mock logfile to ingest + logFile := "/tmp/test.log" + err = testenv.CreateMockLogfile(logFile, 1) + Expect(err).To(Succeed(), "Failed response to add index to splunk logfile %s", logFile) + + // Copy log file and ingest it + err = testenv.IngestFileViaOneshot(logFile, indexName, podName, deployment) + Expect(err).To(Succeed(), "Failed to ingest logfile %s on pod %s", logFile, podName) + + // Read first line to find a search token + var file, openErr = os.Open(logFile) + Expect(openErr).To(Succeed(), "Failed to open newly created logfile %s on pod %s", logFile, podName) + + reader := bufio.NewReader(file) + var readErr error + firstLine, readErr = reader.ReadString('\n') + Expect(readErr).Should(Or(BeNil(), Equal(io.EOF)), "Failed to read first line of logfile %s on pod ", logFile, podName) + + tokens := strings.Fields(firstLine) + Expect(len(tokens)).To(BeNumerically(">=", 2), "Incorrect tokens (%s) in first logline %s for logfile %s", tokens, firstLine, logFile) + + searchToken := tokens[len(tokens)-1] + testenvInstance.Log.Info("Got search token successfully", "logFile", logFile, "searchToken", searchToken) + + searchString := fmt.Sprintf("index=%s | stats count by host", indexName) + + // Wait for ingestion lag prior to searching + time.Sleep(2 * time.Second) + searchResultsResp, err := testenv.PerformSearchSync(podName, searchString, deployment) + Expect(err).To(Succeed(), "Failed to execute search '%s' on pod %s", podName, searchString) + + // Verify result. Should get count 1. result:{count:1} + var searchResults map[string]interface{} + jsonErr := json.Unmarshal([]byte(searchResultsResp), &searchResults) + Expect(jsonErr).To(Succeed(), "Failed to unmarshal JSON Search Results from response '%s'", searchResultsResp) + + testenvInstance.Log.Info("Search results :", "searchResults", searchResults["result"]) + Expect(searchResults["result"]).ShouldNot(BeNil(), "No results in search response '%s' on pod %s", searchResults, podName) + + hostCount := searchResults["result"].(map[string]interface{}) + testenvInstance.Log.Info("Sync Search results host count:", "count", hostCount["count"].(string), "host", hostCount["host"].(string)) + testHostCnt := strings.Compare(hostCount["count"].(string), "1") + testHostname := strings.Compare(hostCount["host"].(string), podName) + Expect(testHostCnt).To(Equal(0), "Incorrect search results for count. Expect: 1 Got: %d", hostCount["count"].(string)) + Expect(testHostname).To(Equal(0), "Incorrect search result hostname. Expect: %s Got: %s", podName, hostCount["host"].(string)) + + searchString2 := fmt.Sprintf("index=%s %s", indexName, searchToken) + sid, reqErr := testenv.PerformSearchReq(podName, searchString2, deployment) + Expect(reqErr).To(Succeed(), "Failed to execute search '%s' on pod %s", searchString, podName) + testenvInstance.Log.Info("Got a search with sid", "sid", sid) + + // Check SID status until done + searchStatusResult, statusErr := testenv.GetSearchStatus(podName, sid, deployment) + Expect(statusErr).To(Succeed(), "Failed to get search status on pod %s for sid %s", podName, sid) + testenvInstance.Log.Info("Search status:", "searchStatusResult", searchStatusResult) + + // Get SID results + searchResultsResp, resErr := testenv.GetSearchResults(podName, sid, deployment) + Expect(resErr).To(Succeed(), "Failed to get search results on pod %s for sid %s", podName, sid) + + testenvInstance.Log.Info("Raw Search results:", "searchResultsResp", searchResultsResp) + var searchResults2 testenv.SearchJobResultsResponse + jsonErr = json.Unmarshal([]byte(searchResultsResp), &searchResults2) + Expect(jsonErr).To(Succeed(), "Failed to unmarshal JSON Search Results from response '%s'", searchResultsResp) + + found := false + for key, elem := range searchResults2.Results { + testenvInstance.Log.Info("Search results _raw and host:", "_raw", elem.Raw, "host", elem.SplunkServer, "firstLine", firstLine) + trimFirstLine := strings.TrimSuffix(firstLine, "\n") + if strings.Compare(elem.Raw, trimFirstLine) == 0 { + testenvInstance.Log.Info("Found search results in _raw and splunk_server", "key", key, "podName", podName, "elem", elem) + found = true + } + } + Expect(found).To(Equal(true), "Incorrect search results %s", searchResults) + }) + }) +}) diff --git a/test/monitoring_console/monitoring_console_test.go b/test/monitoring_console/monitoring_console_test.go index fc08eb78c..3da166084 100644 --- a/test/monitoring_console/monitoring_console_test.go +++ b/test/monitoring_console/monitoring_console_test.go @@ -13,7 +13,7 @@ import ( "github.com/splunk/splunk-operator/test/testenv" ) -var _ = Describe("Montioring Console test", func() { +var _ = Describe("Monitoring Console test", func() { var deployment *testenv.Deployment diff --git a/test/smoke/smoke_test.go b/test/smoke/smoke_test.go index d425443ce..f1f75b132 100644 --- a/test/smoke/smoke_test.go +++ b/test/smoke/smoke_test.go @@ -135,6 +135,9 @@ var _ = Describe("Smoke test", func() { // Verify MC Pod is Ready testenv.MCPodReady(testenvInstance.GetName(), deployment) + + // Verify RF SF is met + testenv.VerifyRFSFMet(deployment, testenvInstance) }) }) @@ -232,6 +235,9 @@ var _ = Describe("Smoke test", func() { // Verify MC Pod is Ready testenv.MCPodReady(testenvInstance.GetName(), deployment) + + // Verify RF SF is met + testenv.VerifyRFSFMet(deployment, testenvInstance) }) }) @@ -309,6 +315,11 @@ var _ = Describe("Smoke test", func() { return siteIndexerStatus }, deployment.GetTimeout(), PollInterval).Should(Equal(siteIndexerMap)) + // Verify MC Pod is Ready + testenv.MCPodReady(testenvInstance.GetName(), deployment) + + // Verify RF SF is met + testenv.VerifyRFSFMet(deployment, testenvInstance) }) }) }) diff --git a/test/testenv/cmutil.go b/test/testenv/cmutil.go new file mode 100644 index 000000000..d1b4fa568 --- /dev/null +++ b/test/testenv/cmutil.go @@ -0,0 +1,60 @@ +package testenv + +import ( + "encoding/json" + "fmt" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// ClusterMasterHealthResponse is a representation of the health response by a Splunk cluster-master +// Endpoint: /services/cluster/master/health +type ClusterMasterHealthResponse struct { + Entries []ClusterMasterHealthEntry `json:"entry"` +} + +// ClusterMasterHealthEntry represents a site of an indexer cluster with its metadata +type ClusterMasterHealthEntry struct { + Name string `json:"name"` + Content ClusterMasterHealthContent `json:"content"` +} + +// ClusterMasterHealthContent represents detailed information about a site +type ClusterMasterHealthContent struct { + AllDataIsSearchable string `json:"all_data_is_searchable"` + AllPeersAreUp string `json:"all_peers_are_up"` + Multisite string `json:"multisite"` + NoFixupTasksInProgress string `json:"no_fixup_tasks_in_progress"` + ReplicationFactorMet string `json:"replication_factor_met"` + SearchFactorMet string `json:"search_factor_met"` + SiteReplicationFactorMet string `json:"site_replication_factor_met"` + SiteSearchFactorMet string `json:"site_search_factor_met"` +} + +// CheckRFSF check if cluster has met replication factor and search factor +func CheckRFSF(deployment *Deployment) bool { + //code to execute + podName := fmt.Sprintf("splunk-%s-cluster-master-0", deployment.GetName()) + stdin := "curl -ks -u admin:$(cat /mnt/splunk-secrets/password) https://localhost:8089/services/cluster/master/health?output_mode=json" + command := []string{"/bin/sh"} + stdout, stderr, err := deployment.PodExecCommand(podName, command, stdin, false) + if err != nil { + logf.Log.Error(err, "Failed to execute command on pod", "pod", podName, "command", command) + return false + } + logf.Log.Info("Command executed on pod", "pod", podName, "command", command, "stdin", stdin, "stdout", stdout, "stderr", stderr) + restResponse := ClusterMasterHealthResponse{} + err = json.Unmarshal([]byte(stdout), &restResponse) + if err != nil { + logf.Log.Error(err, "Failed to parse health status") + return false + } + sfMet := restResponse.Entries[0].Content.SearchFactorMet == "1" + if sfMet == false { + logf.Log.Info("Search Factor not met") + } + rfMet := restResponse.Entries[0].Content.ReplicationFactorMet == "1" + if rfMet == false { + logf.Log.Info("Replicaton Factor not met") + } + return rfMet && sfMet +} diff --git a/test/testenv/ingest_utils.go b/test/testenv/ingest_utils.go new file mode 100644 index 000000000..5d7227b08 --- /dev/null +++ b/test/testenv/ingest_utils.go @@ -0,0 +1,207 @@ +package testenv + +import ( + "bytes" + "errors" + "fmt" + "io" + "math/rand" + "net/http" + "os" + "path" + "strings" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/remotecommand" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/client/config" + logf "sigs.k8s.io/controller-runtime/pkg/log" + // Used to move files between pods + _ "k8s.io/kubernetes/pkg/kubectl/cmd/cp" + _ "unsafe" +) + +// CreateMockLogfile creates a mock logfile with n entries to be ingested. +// - Each logline has a random number that can be searched after ingest. +// - Timestamps for the logs are set to a line a second ending with the current time +func CreateMockLogfile(logFile string, totalLines int) error { + // Create data log + var file, err = os.Create(logFile) + if err != nil { + logf.Log.Error(err, "Failed File Created", "logFile", logFile) + return err + } + logf.Log.Info("File Created Successfully", "logFile", logFile) + + // Write some text line-by-line to file. + var logLine strings.Builder + level := "DEBUG" + component := "GenericComponent" + msg := "This log line is special!" + timestamp := time.Now() + + // Simulate a log every second. This could be adjusted however for this simple case its probably sufficient + timestamp = timestamp.Add(time.Second * time.Duration(-totalLines)) + rand.Seed(time.Now().UnixNano()) + + // Write each line to the file + for i := 0; i < totalLines; i++ { + fmt.Fprintf(&logLine, "%s %s %s %s randomNumber=%d\n", timestamp.Format("01-02-2006 15:04:05.000"), level, component, msg, rand.Int63()) + _, err = file.WriteString(logLine.String()) + if err != nil { + logf.Log.Error(err, "Failed File Write", "logFile", logFile, "logLine", logLine.String()) + return err + } + timestamp = timestamp.Add(time.Second) + } + + // Save logFile + err = file.Sync() + if err != nil { + logf.Log.Error(err, "Failed File Save", "logFile", logFile, "logLine", logLine) + return err + } + logf.Log.Info("File Updated Successfully", "logFile", logFile) + + return nil +} + +// CreateAnIndexStandalone creates an index on a standalone instance using the CLI +func CreateAnIndexStandalone(indexName string, podName string, deployment *Deployment) error { + + var addIndexCmd strings.Builder + splunkBin := "/opt/splunk/bin/splunk" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password) " + splunkCmd := "add index" + + fmt.Fprintf(&addIndexCmd, "%s %s %s -auth %s:%s", splunkBin, splunkCmd, indexName, username, password) + command := []string{"/bin/bash"} + stdin := addIndexCmd.String() + addIndexResp, stderr, err := deployment.PodExecCommand(podName, command, stdin, false) + if err != nil { + logf.Log.Error(err, "Failed to execute command on pod", "pod", podName, "stdin", stdin, "addIndexResp", addIndexResp, "stderr", stderr) + return err + } + + // Validate the response of the CLI command + var expectedResp strings.Builder + fmt.Fprintf(&expectedResp, "Index \"%s\" added.", indexName) + if strings.Compare(addIndexResp, expectedResp.String()) == 0 { + logf.Log.Error(err, "Failed response to add index to splunk", "pod", podName, "addIndexResp", addIndexResp) + return errors.New("Failed response to add index to splunk") + } + + logf.Log.Info("Added index to Splunk", "podName", podName, "addIndexResp", addIndexResp) + return nil +} + +// IngestFileViaOneshot ingests a file into an instance using the oneshot CLI +func IngestFileViaOneshot(logFile string, indexName string, podName string, deployment *Deployment) error { + + // Send it to the instance + resp, stderr, cpErr := CopyFileToPod(podName, logFile, logFile, deployment) + if cpErr != nil { + logf.Log.Error(cpErr, "Failed File Copy to pod", "logFile", logFile, "podName", podName, "stderr", stderr) + return cpErr + } + logf.Log.Info("File Copied Successfully", "logFile", logFile, "resp", resp) + + // oneshot log into specified index + var addOneshotCmd strings.Builder + splunkBin := "/opt/splunk/bin/splunk" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password) " + splunkCmd := "add oneshot" + + fmt.Fprintf(&addOneshotCmd, "%s %s %s -index %s -auth %s:%s", splunkBin, splunkCmd, logFile, indexName, username, password) + command := []string{"/bin/bash"} + stdin := addOneshotCmd.String() + addOneshotResp, stderr, err := deployment.PodExecCommand(podName, command, stdin, false) + if err != nil { + logf.Log.Error(err, "Failed to execute command on pod", "pod", podName, "stdin", stdin, "addOneshotResp", addOneshotResp, "stderr", stderr) + return err + } + + // Validate the expected CLI response + var expectedResp strings.Builder + fmt.Fprintf(&expectedResp, "Oneshot '%s' added", indexName) + if strings.Compare(addOneshotResp, expectedResp.String()) == 0 { + logf.Log.Error(err, "Failed response to add oneshot to splunk", "pod", podName, "addOneshotResp", addOneshotResp) + return err + } + logf.Log.Info("File Ingested via add oneshot Successfully", "logFile", logFile, "addOneshotResp", addOneshotResp) + return nil +} + +// CopyFileToPod copies a file locally from srcPath to the destPath on the pod specified in podName +func CopyFileToPod(podName string, srcPath string, destPath string, deployment *Deployment) (string, string, error) { + // Create tar file stream + reader, writer := io.Pipe() + if destPath != "/" && strings.HasSuffix(string(destPath[len(destPath)-1]), "/") { + destPath = destPath[:len(destPath)-1] + } + go func() { + defer writer.Close() + err := cpMakeTar(srcPath, destPath, writer) + if err != nil { + return + } + }() + var cmdArr []string + + cmdArr = []string{"tar", "-xf", "-"} + destDir := path.Dir(destPath) + if len(destDir) > 0 { + cmdArr = append(cmdArr, "-C", destDir) + } + + // Setup exec command for pod + pod := &corev1.Pod{} + deployment.GetInstance(podName, pod) + gvk, _ := apiutil.GVKForObject(pod, scheme.Scheme) + restConfig, err := config.GetConfig() + if err != nil { + return "", "", err + } + restClient, err := apiutil.RESTClientForGVK(gvk, restConfig, serializer.NewCodecFactory(scheme.Scheme)) + if err != nil { + return "", "", err + } + + execReq := restClient.Post().Resource("pods").Name(podName).Namespace(deployment.testenv.namespace).SubResource("exec") + option := &corev1.PodExecOptions{ + Command: cmdArr, + Stdin: true, + Stdout: true, + Stderr: true, + TTY: false, + } + + execReq.VersionedParams( + option, + scheme.ParameterCodec, + ) + exec, err := remotecommand.NewSPDYExecutor(restConfig, http.MethodPost, execReq.URL()) + if err != nil { + return "", "", err + } + + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + err = exec.Stream(remotecommand.StreamOptions{ + Stdin: reader, + Stdout: stdout, + Stderr: stderr, + }) + if err != nil { + return "", "", err + } + return stdout.String(), stderr.String(), nil +} + +//go:linkname cpMakeTar k8s.io/kubernetes/pkg/kubectl/cmd/cp.makeTar +func cpMakeTar(srcPath, destPath string, writer io.Writer) error diff --git a/test/testenv/mcutil.go b/test/testenv/mcutil.go index abbfd8c51..2542314ca 100644 --- a/test/testenv/mcutil.go +++ b/test/testenv/mcutil.go @@ -116,6 +116,14 @@ func MCPodReady(ns string, deployment *Deployment) { DumpGetPods(ns) return check }, deployment.GetTimeout(), PollInterval).Should(gomega.Equal(true)) + + // Verify MC Pod Stays in ready state + gomega.Consistently(func() bool { + logf.Log.Info("Checking status of Monitoring Console Pod") + check := CheckMCPodReady(ns) + DumpGetPods(ns) + return check + }, ConsistentDuration, ConsistentPollInterval).Should(gomega.Equal(true)) } // GetSearchHeadPeersOnMC GET Search head configured on MC diff --git a/test/testenv/search_utils.go b/test/testenv/search_utils.go new file mode 100644 index 000000000..9fa3b887a --- /dev/null +++ b/test/testenv/search_utils.go @@ -0,0 +1,146 @@ +package testenv + +import ( + "encoding/json" + "fmt" + + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// SearchJobStatusResponse represents the search status returned by splunk for +// endpoint: https://localhost:8089/services/search/jobs/ +type SearchJobStatusResponse struct { + Entries []SearchJobStatusEntry `json:"entry"` +} + +// SearchJobStatusEntry represents the metadata for a given sid returned as part of the search status +type SearchJobStatusEntry struct { + Name string + ID string + Content SearchJobStatusContent +} + +// SearchJobStatusContent represents the search metadata returned as part of the search status +type SearchJobStatusContent struct { + IsDone bool +} + +// SearchJobResultsResponse represents the search results on non-transforming searches +type SearchJobResultsResponse struct { + Fields []SearchJobResponseFields `json:"fields"` + Results []SearchJobResponseResults `json:"results"` +} + +// SearchJobResponseFields represents the fields in results from non-transforming searches +type SearchJobResponseFields struct { + Name string +} + +// SearchJobResponseResults represents the results from non-transforming searches +type SearchJobResponseResults struct { + Raw string `json:"_raw"` + Source string `json:"source"` + Sourcetype string `json:"sourcetype"` + SplunkServer string `json:"splunk_server"` +} + +// PerformSearchSync performs a syncronous search within splunk and returns the search results +func PerformSearchSync(podName string, search string, deployment *Deployment) (string, error) { + // Build the search curl command and send it to an instance + curlCmd := "curl -ks -u" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + url := "https://localhost:8089/services/search/jobs/export" + + searchReq := fmt.Sprintf("%s %s:%s %s -d output_mode=json -d search=\"search %s\"", curlCmd, username, password, url, search) + command := []string{"/bin/sh"} + searchReqResp, stderr, err := deployment.PodExecCommand(podName, command, searchReq, false) + _ = stderr + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command) + return "", err + } + + // Since results can have multiple formats depending on the search SPL, leave this response as a string + return searchReqResp, err +} + +// PerformSearchReq makes a search request for a search to be performed. Returns a sid to be used to check for status and results +func PerformSearchReq(podName string, search string, deployment *Deployment) (string, error) { + // Build the search curl command + curlCmd := "curl -ks -u" + url := "https://localhost:8089/services/search/jobs" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + + searchReq := fmt.Sprintf("%s %s:%s %s -d output_mode=json -d search=\"search %s\"", curlCmd, username, password, url, search) + + // Send search request to instance + command := []string{"/bin/sh"} + stdout, stderr, err := deployment.PodExecCommand(podName, command, searchReq, false) + _ = stderr + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command) + return "", err + } + + // Get SID + var searchReqResult map[string]interface{} + jsonErr := json.Unmarshal([]byte(stdout), &searchReqResult) + if jsonErr != nil { + logf.Log.Error(jsonErr, "Failed to unmarshal JSON Search Request Response to get SID") + return "", jsonErr + } + sid := searchReqResult["sid"].(string) + return sid, err +} + +// GetSearchStatus checks the search status for a given +func GetSearchStatus(podName string, sid string, deployment *Deployment) (*SearchJobStatusResponse, error) { + // Build search status request curl command + curlCmd := "curl -ks -u" + url := "https://localhost:8089/services/search/jobs" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + + searchStatusReq := fmt.Sprintf("%s %s:%s %s/%s -d output_mode=json", curlCmd, username, password, url, sid) + + // Send search status request to instance + command := []string{"/bin/sh"} + searchStatusResp, stderr, err := deployment.PodExecCommand(podName, command, searchStatusReq, false) + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command, "stderr", stderr) + return nil, err + } + + // Parse resulting JSON + searchStatusResult := SearchJobStatusResponse{} + jsonErr := json.Unmarshal([]byte(searchStatusResp), &searchStatusResult) + if jsonErr != nil { + logf.Log.Error(jsonErr, "Failed to unmarshal JSON Search Status Response to get SID") + return nil, jsonErr + } + return &searchStatusResult, err +} + +// GetSearchResults retrieve the results for a given once the search status isDone == true +func GetSearchResults(podName string, sid string, deployment *Deployment) (string, error) { + // Build search results request curl command + curlCmd := "curl -ks -u" + url := "https://localhost:8089/services/search/jobs" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + + searchResultsReq := fmt.Sprintf("%s %s:%s %s/%s/results/ --get -d output_mode=json", curlCmd, username, password, url, sid) + + // Send search results request to instance + command := []string{"/bin/sh"} + searchResultsResp, stderr, err := deployment.PodExecCommand(podName, command, searchResultsReq, false) + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command, "stderr", stderr) + return "", err + } + + // Since results can have multiple formats depending on the search SPL (transforming vs. non-transforming, etc.), leave this response as a string + return searchResultsResp, err +} diff --git a/test/testenv/testenv.go b/test/testenv/testenv.go index a13e2376f..f65173b5d 100644 --- a/test/testenv/testenv.go +++ b/test/testenv/testenv.go @@ -31,7 +31,7 @@ const ( defaultSparkImage = "splunk/spark" // defaultTestTimeout is the max timeout in seconds before async test failed. - defaultTestTimeout = 900 + defaultTestTimeout = 1500 // PollInterval specifies the polling interval PollInterval = 5 * time.Second diff --git a/test/testenv/verificationutils.go b/test/testenv/verificationutils.go index 4c34abc6f..327b3f86d 100644 --- a/test/testenv/verificationutils.go +++ b/test/testenv/verificationutils.go @@ -45,3 +45,12 @@ func SearchHeadClusterReady(deployment *Deployment, testenvInstance *TestEnv) { return shc.Status.Phase }, ConsistentDuration, ConsistentPollInterval).Should(gomega.Equal(splcommon.PhaseReady)) } + +// VerifyRFSFMet verify RF SF is met on cluster masterr +func VerifyRFSFMet(deployment *Deployment, testenvInstance *TestEnv) { + gomega.Eventually(func() bool { + rfSfStatus := CheckRFSF(deployment) + testenvInstance.Log.Info("Verifying RF SF is met", "Status", rfSfStatus) + return rfSfStatus + }, deployment.GetTimeout(), PollInterval).Should(gomega.Equal(true)) +} diff --git a/version/version.go b/version/version.go index 14c9d1830..36ec83b1d 100644 --- a/version/version.go +++ b/version/version.go @@ -16,5 +16,5 @@ package version var ( // Version of splunk-operator - Version = "0.2.0" + Version = "0.2.1" )