diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f0b5b1..0cafe3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,5 +16,7 @@ All notable changes to this project will be documented in this file. - Resources (CPU, memory, storage) - PodDisruptionBudgets - Replicas +- Add Listener support ([#17]). [#10]: https://github.com/stackabletech/opensearch-operator/pull/10 +[#17]: https://github.com/stackabletech/opensearch-operator/pull/17 diff --git a/deploy/helm/opensearch-operator/crds/crds.yaml b/deploy/helm/opensearch-operator/crds/crds.yaml index e67d288..7b9d514 100644 --- a/deploy/helm/opensearch-operator/crds/crds.yaml +++ b/deploy/helm/opensearch-operator/crds/crds.yaml @@ -133,6 +133,10 @@ spec: description: Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. nullable: true type: string + listenerClass: + description: This field controls which [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html) is used to expose the HTTP communication. + nullable: true + type: string nodeRoles: items: enum: @@ -325,6 +329,10 @@ spec: description: Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. nullable: true type: string + listenerClass: + description: This field controls which [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html) is used to expose the HTTP communication. + nullable: true + type: string nodeRoles: items: enum: diff --git a/deploy/helm/opensearch-operator/templates/roles.yaml b/deploy/helm/opensearch-operator/templates/roles.yaml index c74dd92..97ea65f 100644 --- a/deploy/helm/opensearch-operator/templates/roles.yaml +++ b/deploy/helm/opensearch-operator/templates/roles.yaml @@ -70,6 +70,17 @@ rules: - customresourcedefinitions verbs: - get + - apiGroups: + - listeners.stackable.tech + resources: + - listeners + verbs: + - get + - list + - watch + - patch + - create + - delete - apiGroups: - events.k8s.io resources: diff --git a/docs/modules/opensearch/pages/usage-guide/listenerclass.adoc b/docs/modules/opensearch/pages/usage-guide/listenerclass.adoc new file mode 100644 index 0000000..0d4312f --- /dev/null +++ b/docs/modules/opensearch/pages/usage-guide/listenerclass.adoc @@ -0,0 +1,17 @@ += Service exposition with ListenerClasses +:description: Configure OpenSearch service exposure with ListenerClasses: cluster-internal, external-unstable, or external-stable. + +The operator deploys a xref:listener-operator:listener.adoc[Listener] for OpenSearch role-groups. +The listener defaults to only being accessible from within the Kubernetes cluster, but this can be changed by setting `.spec.nodes.roleGroups.\{role-group-name}.config.listenerClass`: + +[source,yaml] +---- +spec: + nodes: + roleGroups: + cluster-manager: + config: + listenerClass: external-stable # <1> +---- +<1> Specify a ListenerClass, such as `external-stable`, `external-unstable`, or `cluster-internal` (the default setting is `cluster-internal`) at role-group level. +This can be set for all role-groups individually. diff --git a/docs/modules/opensearch/partials/nav.adoc b/docs/modules/opensearch/partials/nav.adoc index c21e7d5..3ddf9b3 100644 --- a/docs/modules/opensearch/partials/nav.adoc +++ b/docs/modules/opensearch/partials/nav.adoc @@ -3,6 +3,7 @@ ** xref:opensearch:getting_started/first_steps.adoc[] * xref:opensearch:usage-guide/index.adoc[] ** xref:opensearch:usage-guide/node-roles.adoc[] +** xref:opensearch:usage-guide/listenerclass.adoc[] ** xref:opensearch:usage-guide/storage-resource-configuration.adoc[] ** xref:opensearch:usage-guide/configuration-environment-overrides.adoc[] ** xref:opensearch:usage-guide/operations/index.adoc[] diff --git a/rust/operator-binary/src/controller.rs b/rust/operator-binary/src/controller.rs index 6b092b1..4e23072 100644 --- a/rust/operator-binary/src/controller.rs +++ b/rust/operator-binary/src/controller.rs @@ -6,6 +6,7 @@ use snafu::{ResultExt, Snafu}; use stackable_operator::{ cluster_resources::ClusterResourceApplyStrategy, commons::{affinity::StackableAffinity, product_image_selection::ProductImage}, + crd::listener::v1alpha1::Listener, k8s_openapi::api::{ apps::v1::StatefulSet, core::v1::{ConfigMap, Service, ServiceAccount}, @@ -111,6 +112,7 @@ pub struct ValidatedOpenSearchConfig { pub node_roles: NodeRoles, pub resources: stackable_operator::commons::resources::Resources, pub termination_grace_period_seconds: i64, + pub listener_class: String, } // validated and converted to validated and safe types @@ -275,6 +277,7 @@ struct Applied; struct KubernetesResources { stateful_sets: Vec, services: Vec, + listeners: Vec, config_maps: Vec, service_accounts: Vec, role_bindings: Vec, diff --git a/rust/operator-binary/src/controller/apply.rs b/rust/operator-binary/src/controller/apply.rs index 23b6f2e..f0bd893 100644 --- a/rust/operator-binary/src/controller/apply.rs +++ b/rust/operator-binary/src/controller/apply.rs @@ -62,6 +62,8 @@ impl<'a> Applier<'a> { let services = self.add_resources(resources.services).await?; + let listeners = self.add_resources(resources.listeners).await?; + let config_maps = self.add_resources(resources.config_maps).await?; let service_accounts = self.add_resources(resources.service_accounts).await?; @@ -78,6 +80,7 @@ impl<'a> Applier<'a> { Ok(KubernetesResources { stateful_sets, services, + listeners, config_maps, service_accounts, role_bindings, diff --git a/rust/operator-binary/src/controller/build.rs b/rust/operator-binary/src/controller/build.rs index 28aa9ad..db8e35e 100644 --- a/rust/operator-binary/src/controller/build.rs +++ b/rust/operator-binary/src/controller/build.rs @@ -12,6 +12,7 @@ pub fn build(names: &ContextNames, cluster: ValidatedCluster) -> KubernetesResou let mut config_maps = vec![]; let mut stateful_sets = vec![]; let mut services = vec![]; + let mut listeners = vec![]; let role_builder = RoleBuilder::new(cluster.clone(), names); @@ -19,6 +20,7 @@ pub fn build(names: &ContextNames, cluster: ValidatedCluster) -> KubernetesResou config_maps.push(role_group_builder.build_config_map()); stateful_sets.push(role_group_builder.build_stateful_set()); services.push(role_group_builder.build_headless_service()); + listeners.push(role_group_builder.build_listener()); } let cluster_manager_service = role_builder.build_cluster_manager_service(); @@ -33,6 +35,7 @@ pub fn build(names: &ContextNames, cluster: ValidatedCluster) -> KubernetesResou KubernetesResources { stateful_sets, services, + listeners, config_maps, service_accounts, role_bindings, diff --git a/rust/operator-binary/src/controller/build/node_config.rs b/rust/operator-binary/src/controller/build/node_config.rs index 49da561..6eb9ba6 100644 --- a/rust/operator-binary/src/controller/build/node_config.rs +++ b/rust/operator-binary/src/controller/build/node_config.rs @@ -286,6 +286,7 @@ mod tests { node_roles: NodeRoles::default(), resources: Resources::default(), termination_grace_period_seconds: 30, + listener_class: "cluster-internal".to_string(), }, config_overrides: HashMap::default(), env_overrides: [("TEST".to_owned(), "value".to_owned())].into(), diff --git a/rust/operator-binary/src/controller/build/role_group_builder.rs b/rust/operator-binary/src/controller/build/role_group_builder.rs index 6ca788a..503ae97 100644 --- a/rust/operator-binary/src/controller/build/role_group_builder.rs +++ b/rust/operator-binary/src/controller/build/role_group_builder.rs @@ -1,13 +1,14 @@ use stackable_operator::{ builder::{meta::ObjectMetaBuilder, pod::container::ContainerBuilder}, + crd::listener::{self}, k8s_openapi::{ DeepMerge, api::{ apps::v1::{StatefulSet, StatefulSetSpec}, core::v1::{ Affinity, ConfigMap, ConfigMapVolumeSource, Container, ContainerPort, - PodSecurityContext, PodSpec, PodTemplateSpec, Probe, Service, ServicePort, - ServiceSpec, TCPSocketAction, Volume, VolumeMount, + PersistentVolumeClaim, PodSecurityContext, PodSpec, PodTemplateSpec, Probe, + Service, ServicePort, ServiceSpec, TCPSocketAction, Volume, VolumeMount, }, }, apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString}, @@ -24,6 +25,7 @@ use crate::{ RoleGroupName, builder::meta::ownerreference_from_resource, kvp::label::{recommended_labels, role_group_selector, role_selector}, + listener::listener_pvc, role_group_utils::ResourceNames, }, }; @@ -36,8 +38,11 @@ pub const TRANSPORT_PORT: u16 = 9300; const CONFIG_VOLUME_NAME: &str = "config"; const DATA_VOLUME_NAME: &str = "data"; +const LISTENER_VOLUME_NAME: &str = "listener"; +const LISTENER_VOLUME_DIR: &str = "/stackable/listener"; + // Path in opensearchproject/opensearch:3.0.0 -const OPENSEARCH_BASE_PATH: &str = "/usr/share/opensearch"; +const OPENSEARCH_BASE_PATH: &str = "/stackable/opensearch"; pub struct RoleGroupBuilder<'a> { service_account_name: String, @@ -107,6 +112,24 @@ impl<'a> RoleGroupBuilder<'a> { .data .build_pvc(DATA_VOLUME_NAME, Some(vec!["ReadWriteOnce"])); + let listener_group_name = self.resource_names.listener_service_name(); + + // Listener endpoints for the all rolegroups will use persistent + // volumes so that load balancers can hard-code the target + // addresses. This will be the case even when no class is set (and + // the value defaults to cluster-internal) as the address should + // still be consistent. + let listener_volume_claim_template = listener_pvc( + listener_group_name, + &self.recommended_labels(), + LISTENER_VOLUME_NAME.to_string(), + ); + + let pvcs: Option> = Some(vec![ + data_volume_claim_template, + listener_volume_claim_template, + ]); + let spec = StatefulSetSpec { // Order does not matter for OpenSearch pod_management_policy: Some("Parallel".to_string()), @@ -117,7 +140,7 @@ impl<'a> RoleGroupBuilder<'a> { }, service_name: Some(self.resource_names.headless_service_name()), template, - volume_claim_templates: Some(vec![data_volume_claim_template]), + volume_claim_templates: pvcs, ..StatefulSetSpec::default() }; @@ -271,6 +294,11 @@ impl<'a> RoleGroupBuilder<'a> { name: DATA_VOLUME_NAME.to_owned(), ..VolumeMount::default() }, + VolumeMount { + mount_path: LISTENER_VOLUME_DIR.to_owned(), + name: LISTENER_VOLUME_NAME.to_owned(), + ..VolumeMount::default() + }, ]) .expect("The mount paths are statically defined and there should be no duplicates.") .add_container_ports(vec![ @@ -337,6 +365,33 @@ impl<'a> RoleGroupBuilder<'a> { } } + pub fn build_listener(&self) -> listener::v1alpha1::Listener { + let metadata = + self.common_metadata(self.resource_names.listener_service_name(), Labels::new()); + + let listener_class = self.role_group_config.config.listener_class.to_owned(); + + listener::v1alpha1::Listener { + metadata, + spec: listener::v1alpha1::ListenerSpec { + class_name: Some(listener_class), + ports: Some(self.listener_ports()), + ..listener::v1alpha1::ListenerSpec::default() + }, + status: None, + } + } + + /// We only use the http port here and intentionally omit + /// the metrics one. + fn listener_ports(&self) -> Vec { + vec![listener::v1alpha1::ListenerPort { + name: HTTP_PORT_NAME.to_string(), + port: HTTP_PORT.into(), + protocol: Some("TCP".to_string()), + }] + } + fn common_metadata( &self, resource_name: impl Into, diff --git a/rust/operator-binary/src/controller/validate.rs b/rust/operator-binary/src/controller/validate.rs index d171898..959ef80 100644 --- a/rust/operator-binary/src/controller/validate.rs +++ b/rust/operator-binary/src/controller/validate.rs @@ -122,6 +122,7 @@ fn validate_role_group_config( node_roles: merged_role_group.config.config.node_roles, resources: merged_role_group.config.config.resources, termination_grace_period_seconds, + listener_class: merged_role_group.config.config.listener_class, }; Ok(RoleGroupConfig { diff --git a/rust/operator-binary/src/crd/mod.rs b/rust/operator-binary/src/crd/mod.rs index ce1eabb..84a6efa 100644 --- a/rust/operator-binary/src/crd/mod.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -30,6 +30,8 @@ use crate::framework::{ role_utils::GenericProductSpecificCommonConfig, }; +const DEFAULT_LISTENER_CLASS: &str = "cluster-internal"; + #[versioned(version(name = "v1alpha1"))] pub mod versioned { @@ -130,6 +132,10 @@ pub mod versioned { #[fragment_attrs(serde(default))] pub resources: Resources, + + /// This field controls which [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html) is used to expose the HTTP communication. + #[fragment_attrs(serde(default))] + pub listener_class: String, } #[derive(Clone, Debug, Default, JsonSchema, PartialEq, Fragment)] @@ -232,6 +238,7 @@ impl v1alpha1::OpenSearchConfig { }, }, }, + listener_class: Some(DEFAULT_LISTENER_CLASS.to_string()), } } } diff --git a/rust/operator-binary/src/framework.rs b/rust/operator-binary/src/framework.rs index aad601d..07c5699 100644 --- a/rust/operator-binary/src/framework.rs +++ b/rust/operator-binary/src/framework.rs @@ -12,6 +12,7 @@ use strum::{EnumDiscriminants, IntoStaticStr}; pub mod builder; pub mod cluster_resources; pub mod kvp; +pub mod listener; pub mod role_group_utils; pub mod role_utils; diff --git a/rust/operator-binary/src/framework/listener.rs b/rust/operator-binary/src/framework/listener.rs new file mode 100644 index 0000000..5151aaa --- /dev/null +++ b/rust/operator-binary/src/framework/listener.rs @@ -0,0 +1,19 @@ +use stackable_operator::{ + builder::pod::volume::{ListenerOperatorVolumeSourceBuilder, ListenerReference}, + k8s_openapi::api::core::v1::PersistentVolumeClaim, + kvp::Labels, +}; + +pub fn listener_pvc( + listener_group_name: String, + labels: &Labels, + pvc_name: String, +) -> PersistentVolumeClaim { + ListenerOperatorVolumeSourceBuilder::new( + &ListenerReference::ListenerName(listener_group_name), + labels, + ) + .expect("should return Ok independent of the given parameters") + .build_pvc(pvc_name.to_string()) + .expect("should be a valid annotation") +} diff --git a/rust/operator-binary/src/framework/role_group_utils.rs b/rust/operator-binary/src/framework/role_group_utils.rs index e4b522a..7cd26df 100644 --- a/rust/operator-binary/src/framework/role_group_utils.rs +++ b/rust/operator-binary/src/framework/role_group_utils.rs @@ -65,6 +65,16 @@ impl ResourceNames { format!("{}{SUFFIX}", self.qualified_role_group_name()) } + + pub fn listener_service_name(&self) -> String { + // Compile-time check + const _: () = assert!( + ResourceNames::MAX_QUALIFIED_ROLE_GROUP_NAME_LENGTH <= MAX_OBJECT_NAME_LENGTH, + "The listener name `--` must not exceed 253 characters." + ); + + self.qualified_role_group_name() + } } #[cfg(test)] diff --git a/tests/templates/kuttl/external-access/00-patch-ns.yaml b/tests/templates/kuttl/external-access/00-patch-ns.yaml new file mode 100644 index 0000000..d4f91fa --- /dev/null +++ b/tests/templates/kuttl/external-access/00-patch-ns.yaml @@ -0,0 +1,15 @@ +# see https://github.com/stackabletech/issues/issues/566 +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + kubectl patch namespace $NAMESPACE --patch=' + { + "metadata": { + "labels": { + "pod-security.kubernetes.io/enforce": "privileged" + } + } + }' + timeout: 120 diff --git a/tests/templates/kuttl/external-access/01-rbac.yaml b/tests/templates/kuttl/external-access/01-rbac.yaml new file mode 100644 index 0000000..64eced8 --- /dev/null +++ b/tests/templates/kuttl/external-access/01-rbac.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-service-account +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: test-role +rules: + - apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - privileged + verbs: + - use +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: test-role-binding +subjects: + - kind: ServiceAccount + name: test-service-account +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: test-role diff --git a/tests/templates/kuttl/external-access/10-listener-classes.yaml b/tests/templates/kuttl/external-access/10-listener-classes.yaml new file mode 100644 index 0000000..893032c --- /dev/null +++ b/tests/templates/kuttl/external-access/10-listener-classes.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + envsubst < listener-classes.yaml | kubectl apply -n $NAMESPACE -f - diff --git a/tests/templates/kuttl/external-access/20-assert.yaml b/tests/templates/kuttl/external-access/20-assert.yaml new file mode 100644 index 0000000..6e7c514 --- /dev/null +++ b/tests/templates/kuttl/external-access/20-assert.yaml @@ -0,0 +1,62 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 600 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: opensearch-nodes-cluster-manager +spec: + replicas: 1 +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: opensearch-nodes-data1 +spec: + replicas: 1 +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: opensearch-nodes-data2 +spec: + replicas: 1 +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: v1 +kind: Service +metadata: + name: opensearch-nodes-cluster-manager +spec: + type: NodePort # external-stable +--- +apiVersion: v1 +kind: Service +metadata: + name: opensearch-nodes-data1 +spec: + type: NodePort # external-unstable +--- +apiVersion: v1 +kind: Service +metadata: + name: opensearch-nodes-data2 +spec: + type: ClusterIP # cluster-internal +--- +apiVersion: v1 +kind: Service +metadata: + name: opensearch +spec: + type: ClusterIP diff --git a/tests/templates/kuttl/external-access/20-install-opensearch.yaml b/tests/templates/kuttl/external-access/20-install-opensearch.yaml new file mode 100644 index 0000000..78a7a56 --- /dev/null +++ b/tests/templates/kuttl/external-access/20-install-opensearch.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +timeout: 600 +commands: + - script: > + envsubst < opensearch.yaml | + kubectl apply -n $NAMESPACE -f - diff --git a/tests/templates/kuttl/external-access/listener-classes.yaml b/tests/templates/kuttl/external-access/listener-classes.yaml new file mode 100644 index 0000000..4131526 --- /dev/null +++ b/tests/templates/kuttl/external-access/listener-classes.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: listeners.stackable.tech/v1alpha1 +kind: ListenerClass +metadata: + name: test-cluster-internal-$NAMESPACE +spec: + serviceType: ClusterIP +--- +apiVersion: listeners.stackable.tech/v1alpha1 +kind: ListenerClass +metadata: + name: test-external-stable-$NAMESPACE +spec: + serviceType: NodePort +--- +apiVersion: listeners.stackable.tech/v1alpha1 +kind: ListenerClass +metadata: + name: test-external-unstable-$NAMESPACE +spec: + serviceType: NodePort diff --git a/tests/templates/kuttl/external-access/opensearch.yaml.j2 b/tests/templates/kuttl/external-access/opensearch.yaml.j2 new file mode 100644 index 0000000..463cf67 --- /dev/null +++ b/tests/templates/kuttl/external-access/opensearch.yaml.j2 @@ -0,0 +1,202 @@ +--- +apiVersion: opensearch.stackable.tech/v1alpha1 +kind: OpenSearchCluster +metadata: + name: opensearch +spec: + image: + productVersion: 3.1.0 + pullPolicy: IfNotPresent + nodes: + roleGroups: + cluster-manager: + config: + nodeRoles: + - cluster_manager + listenerClass: test-external-stable-$NAMESPACE + replicas: 1 + podOverrides: + spec: + volumes: + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/scope: node,pod,service=opensearch,service=opensearch-nodes-cluster-manager-headless + data1: + config: + nodeRoles: + - data + listenerClass: test-external-unstable-$NAMESPACE + replicas: 1 + podOverrides: + spec: + volumes: + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/scope: node,pod,service=opensearch-nodes-data1-headless + data2: + config: + nodeRoles: + - data + listenerClass: test-cluster-internal-$NAMESPACE + replicas: 1 + podOverrides: + spec: + volumes: + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/scope: node,pod,service=opensearch-nodes-data2-headless + envOverrides: + # TODO Make these the defaults in the image + DISABLE_INSTALL_DEMO_CONFIG: "true" + configOverrides: + # TODO Add the required options to the operator + opensearch.yml: + # Disable memory mapping in this test; If memory mapping were activated, the kernel setting + # vm.max_map_count would have to be increased to 262144 on the node. + node.store.allow_mmap: "false" + # TODO Check that this is safe despite the warning in the documentation + plugins.security.allow_default_init_securityindex: "true" + plugins.security.ssl.transport.enabled: "true" + plugins.security.ssl.transport.pemcert_filepath: /stackable/opensearch/config/tls/tls.crt + plugins.security.ssl.transport.pemkey_filepath: /stackable/opensearch/config/tls/tls.key + plugins.security.ssl.transport.pemtrustedcas_filepath: /stackable/opensearch/config/tls/ca.crt + plugins.security.ssl.http.enabled: "true" + plugins.security.ssl.http.pemcert_filepath: /stackable/opensearch/config/tls/tls.crt + plugins.security.ssl.http.pemkey_filepath: /stackable/opensearch/config/tls/tls.key + plugins.security.ssl.http.pemtrustedcas_filepath: /stackable/opensearch/config/tls/ca.crt + plugins.security.authcz.admin_dn: "CN=generated certificate for pod" + podOverrides: + spec: + containers: + - name: opensearch + volumeMounts: + - name: security-config + mountPath: /stackable/opensearch/config/opensearch-security + readOnly: true + - name: tls + mountPath: /stackable/opensearch/config/tls + readOnly: true + securityContext: + fsGroup: 1000 + volumes: + - name: security-config + secret: + secretName: opensearch-security-config + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: tls + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" +--- +apiVersion: v1 +kind: Secret +metadata: + name: opensearch-security-config +stringData: + action_groups.yml: | + --- + _meta: + type: actiongroups + config_version: 2 + allowlist.yml: | + --- + _meta: + type: allowlist + config_version: 2 + + config: + enabled: false + audit.yml: | + --- + _meta: + type: audit + config_version: 2 + + config: + enabled: false + config.yml: | + --- + _meta: + type: config + config_version: 2 + + config: + dynamic: + authc: + basic_internal_auth_domain: + description: Authenticate via HTTP Basic against internal users database + http_enabled: true + transport_enabled: true + order: 1 + http_authenticator: + type: basic + challenge: true + authentication_backend: + type: intern + authz: {} + internal_users.yml: | + --- + # The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh + + _meta: + type: internalusers + config_version: 2 + + admin: + hash: $2y$10$xRtHZFJ9QhG9GcYhRpAGpufCZYsk//nxsuel5URh0GWEBgmiI4Q/e + reserved: true + backend_roles: + - admin + description: OpenSearch admin user + + kibanaserver: + hash: $2y$10$vPgQ/6ilKDM5utawBqxoR.7euhVQ0qeGl8mPTeKhmFT475WUDrfQS + reserved: true + description: OpenSearch Dashboards user + nodes_dn.yml: | + --- + _meta: + type: nodesdn + config_version: 2 + roles.yml: | + --- + _meta: + type: roles + config_version: 2 + roles_mapping.yml: | + --- + _meta: + type: rolesmapping + config_version: 2 + + all_access: + reserved: false + backend_roles: + - admin + + kibana_server: + reserved: true + users: + - kibanaserver + tenants.yml: | + --- + _meta: + type: tenants + config_version: 2 diff --git a/tests/templates/kuttl/smoke/10-assert.yaml b/tests/templates/kuttl/smoke/10-assert.yaml index 99038c7..7f33e9a 100644 --- a/tests/templates/kuttl/smoke/10-assert.yaml +++ b/tests/templates/kuttl/smoke/10-assert.yaml @@ -16,7 +16,7 @@ metadata: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: cluster-manager - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-nodes-cluster-manager ownerReferences: @@ -42,7 +42,7 @@ spec: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: cluster-manager - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/opensearch-role.cluster_manager: "true" stackable.tech/vendor: Stackable spec: @@ -59,7 +59,7 @@ spec: weight: 1 containers: - command: - - /usr/share/opensearch/opensearch-docker-entrypoint.sh + - /stackable/opensearch/opensearch-docker-entrypoint.sh env: - name: DISABLE_INSTALL_DEMO_CONFIG value: "true" @@ -74,8 +74,8 @@ spec: fieldPath: metadata.name - name: node.roles value: cluster_manager - image: opensearchproject/opensearch:3.0.0 - imagePullPolicy: Always + image: oci.stackable.tech/sdp/opensearch:3.1.0-stackable0.0.0-dev + imagePullPolicy: IfNotPresent name: opensearch ports: - containerPort: 9200 @@ -107,16 +107,18 @@ spec: port: http timeoutSeconds: 3 volumeMounts: - - mountPath: /usr/share/opensearch/config/opensearch.yml + - mountPath: /stackable/opensearch/config/opensearch.yml name: config readOnly: true subPath: opensearch.yml - - mountPath: /usr/share/opensearch/data + - mountPath: /stackable/opensearch/data name: data - - mountPath: /usr/share/opensearch/config/opensearch-security + - mountPath: /stackable/listener + name: listener + - mountPath: /stackable/opensearch/config/opensearch-security name: security-config readOnly: true - - mountPath: /usr/share/opensearch/config/tls + - mountPath: /stackable/opensearch/config/tls name: tls readOnly: true securityContext: @@ -134,8 +136,7 @@ spec: metadata: annotations: secrets.stackable.tech/class: tls - secrets.stackable.tech/scope: node,pod,service=opensearch-cluster-manager,service=opensearch-nodes-cluster-manager - creationTimestamp: null + secrets.stackable.tech/scope: node,pod,service=opensearch,service=opensearch-nodes-cluster-manager-headless spec: accessModes: - ReadWriteOnce @@ -163,6 +164,30 @@ spec: volumeMode: Filesystem status: phase: Pending + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + annotations: + listeners.stackable.tech/listener-name: opensearch-nodes-cluster-manager + labels: + app.kubernetes.io/component: nodes + app.kubernetes.io/instance: opensearch + app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster + app.kubernetes.io/name: opensearch + app.kubernetes.io/role-group: cluster-manager + app.kubernetes.io/version: 3.1.0 + stackable.tech/vendor: Stackable + name: listener + spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: "1" + storageClassName: listeners.stackable.tech + volumeMode: Filesystem + status: + phase: Pending status: readyReplicas: 3 replicas: 3 @@ -176,7 +201,7 @@ metadata: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: data - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-nodes-data ownerReferences: @@ -202,7 +227,7 @@ spec: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: data - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/opensearch-role.data: "true" stackable.tech/opensearch-role.ingest: "true" stackable.tech/opensearch-role.remote_cluster_client: "true" @@ -221,7 +246,7 @@ spec: weight: 1 containers: - command: - - /usr/share/opensearch/opensearch-docker-entrypoint.sh + - /stackable/opensearch/opensearch-docker-entrypoint.sh env: - name: DISABLE_INSTALL_DEMO_CONFIG value: "true" @@ -235,8 +260,8 @@ spec: fieldPath: metadata.name - name: node.roles value: ingest,data,remote_cluster_client - image: opensearchproject/opensearch:3.0.0 - imagePullPolicy: Always + image: oci.stackable.tech/sdp/opensearch:3.1.0-stackable0.0.0-dev + imagePullPolicy: IfNotPresent name: opensearch ports: - containerPort: 9200 @@ -268,16 +293,18 @@ spec: port: http timeoutSeconds: 3 volumeMounts: - - mountPath: /usr/share/opensearch/config/opensearch.yml + - mountPath: /stackable/opensearch/config/opensearch.yml name: config readOnly: true subPath: opensearch.yml - - mountPath: /usr/share/opensearch/data + - mountPath: /stackable/opensearch/data name: data - - mountPath: /usr/share/opensearch/config/opensearch-security + - mountPath: /stackable/listener + name: listener + - mountPath: /stackable/opensearch/config/opensearch-security name: security-config readOnly: true - - mountPath: /usr/share/opensearch/config/tls + - mountPath: /stackable/opensearch/config/tls name: tls readOnly: true securityContext: @@ -295,8 +322,7 @@ spec: metadata: annotations: secrets.stackable.tech/class: tls - secrets.stackable.tech/scope: node,pod,service=opensearch-nodes-data - creationTimestamp: null + secrets.stackable.tech/scope: node,pod,service=opensearch-nodes-data-headless spec: accessModes: - ReadWriteOnce @@ -324,6 +350,30 @@ spec: volumeMode: Filesystem status: phase: Pending + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + annotations: + listeners.stackable.tech/listener-name: opensearch-nodes-data + labels: + app.kubernetes.io/component: nodes + app.kubernetes.io/instance: opensearch + app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster + app.kubernetes.io/name: opensearch + app.kubernetes.io/role-group: data + app.kubernetes.io/version: 3.1.0 + stackable.tech/vendor: Stackable + name: listener + spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: "1" + storageClassName: listeners.stackable.tech + volumeMode: Filesystem + status: + phase: Pending status: readyReplicas: 2 replicas: 2 @@ -337,7 +387,7 @@ metadata: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: cluster-manager - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-nodes-cluster-manager ownerReferences: @@ -352,15 +402,16 @@ data: network.host: "0.0.0.0" node.store.allow_mmap: "false" plugins.security.allow_default_init_securityindex: "true" + plugins.security.authcz.admin_dn: "CN=generated certificate for pod" plugins.security.nodes_dn: ["CN=generated certificate for pod"] plugins.security.ssl.http.enabled: "true" - plugins.security.ssl.http.pemcert_filepath: "/usr/share/opensearch/config/tls/tls.crt" - plugins.security.ssl.http.pemkey_filepath: "/usr/share/opensearch/config/tls/tls.key" - plugins.security.ssl.http.pemtrustedcas_filepath: "/usr/share/opensearch/config/tls/ca.crt" + plugins.security.ssl.http.pemcert_filepath: "/stackable/opensearch/config/tls/tls.crt" + plugins.security.ssl.http.pemkey_filepath: "/stackable/opensearch/config/tls/tls.key" + plugins.security.ssl.http.pemtrustedcas_filepath: "/stackable/opensearch/config/tls/ca.crt" plugins.security.ssl.transport.enabled: "true" - plugins.security.ssl.transport.pemcert_filepath: "/usr/share/opensearch/config/tls/tls.crt" - plugins.security.ssl.transport.pemkey_filepath: "/usr/share/opensearch/config/tls/tls.key" - plugins.security.ssl.transport.pemtrustedcas_filepath: "/usr/share/opensearch/config/tls/ca.crt" + plugins.security.ssl.transport.pemcert_filepath: "/stackable/opensearch/config/tls/tls.crt" + plugins.security.ssl.transport.pemkey_filepath: "/stackable/opensearch/config/tls/tls.key" + plugins.security.ssl.transport.pemtrustedcas_filepath: "/stackable/opensearch/config/tls/ca.crt" --- apiVersion: v1 kind: ConfigMap @@ -371,7 +422,7 @@ metadata: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: data - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-nodes-data ownerReferences: @@ -386,15 +437,16 @@ data: network.host: "0.0.0.0" node.store.allow_mmap: "false" plugins.security.allow_default_init_securityindex: "true" + plugins.security.authcz.admin_dn: "CN=generated certificate for pod" plugins.security.nodes_dn: ["CN=generated certificate for pod"] plugins.security.ssl.http.enabled: "true" - plugins.security.ssl.http.pemcert_filepath: "/usr/share/opensearch/config/tls/tls.crt" - plugins.security.ssl.http.pemkey_filepath: "/usr/share/opensearch/config/tls/tls.key" - plugins.security.ssl.http.pemtrustedcas_filepath: "/usr/share/opensearch/config/tls/ca.crt" + plugins.security.ssl.http.pemcert_filepath: "/stackable/opensearch/config/tls/tls.crt" + plugins.security.ssl.http.pemkey_filepath: "/stackable/opensearch/config/tls/tls.key" + plugins.security.ssl.http.pemtrustedcas_filepath: "/stackable/opensearch/config/tls/ca.crt" plugins.security.ssl.transport.enabled: "true" - plugins.security.ssl.transport.pemcert_filepath: "/usr/share/opensearch/config/tls/tls.crt" - plugins.security.ssl.transport.pemkey_filepath: "/usr/share/opensearch/config/tls/tls.key" - plugins.security.ssl.transport.pemtrustedcas_filepath: "/usr/share/opensearch/config/tls/ca.crt" + plugins.security.ssl.transport.pemcert_filepath: "/stackable/opensearch/config/tls/tls.crt" + plugins.security.ssl.transport.pemkey_filepath: "/stackable/opensearch/config/tls/tls.key" + plugins.security.ssl.transport.pemtrustedcas_filepath: "/stackable/opensearch/config/tls/ca.crt" --- apiVersion: v1 kind: Service @@ -405,7 +457,7 @@ metadata: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: cluster-manager - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-nodes-cluster-manager-headless spec: @@ -435,7 +487,7 @@ metadata: app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch app.kubernetes.io/role-group: data - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-nodes-data-headless spec: @@ -464,7 +516,7 @@ metadata: app.kubernetes.io/instance: opensearch app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch ownerReferences: @@ -498,7 +550,7 @@ metadata: app.kubernetes.io/instance: opensearch app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-serviceaccount ownerReferences: @@ -515,7 +567,7 @@ metadata: app.kubernetes.io/instance: opensearch app.kubernetes.io/managed-by: opensearch.stackable.tech_opensearchcluster app.kubernetes.io/name: opensearch - app.kubernetes.io/version: 3.0.0 + app.kubernetes.io/version: 3.1.0 stackable.tech/vendor: Stackable name: opensearch-rolebinding ownerReferences: diff --git a/tests/templates/kuttl/smoke/10-install-opensearch.yaml b/tests/templates/kuttl/smoke/10-install-opensearch.yaml index d390b29..dbc0c88 100644 --- a/tests/templates/kuttl/smoke/10-install-opensearch.yaml +++ b/tests/templates/kuttl/smoke/10-install-opensearch.yaml @@ -5,8 +5,8 @@ metadata: name: opensearch spec: image: - custom: opensearchproject/opensearch:3.0.0 - productVersion: 3.0.0 + productVersion: 3.1.0 + pullPolicy: IfNotPresent nodes: roleGroups: cluster-manager: @@ -17,6 +17,7 @@ spec: storage: data: capacity: 100Mi + listenerClass: external-stable replicas: 3 podOverrides: spec: @@ -26,7 +27,7 @@ spec: volumeClaimTemplate: metadata: annotations: - secrets.stackable.tech/scope: node,pod,service=opensearch-cluster-manager,service=opensearch-nodes-cluster-manager + secrets.stackable.tech/scope: node,pod,service=opensearch,service=opensearch-nodes-cluster-manager-headless data: config: nodeRoles: @@ -37,6 +38,7 @@ spec: storage: data: capacity: 2Gi + listenerClass: cluster-internal replicas: 2 podOverrides: spec: @@ -46,7 +48,7 @@ spec: volumeClaimTemplate: metadata: annotations: - secrets.stackable.tech/scope: node,pod,service=opensearch-nodes-data + secrets.stackable.tech/scope: node,pod,service=opensearch-nodes-data-headless envOverrides: # TODO Make these the defaults in the image DISABLE_INSTALL_DEMO_CONFIG: "true" @@ -59,24 +61,24 @@ spec: # TODO Check that this is safe despite the warning in the documentation plugins.security.allow_default_init_securityindex: "true" plugins.security.ssl.transport.enabled: "true" - plugins.security.ssl.transport.pemcert_filepath: /usr/share/opensearch/config/tls/tls.crt - plugins.security.ssl.transport.pemkey_filepath: /usr/share/opensearch/config/tls/tls.key - plugins.security.ssl.transport.pemtrustedcas_filepath: /usr/share/opensearch/config/tls/ca.crt + plugins.security.ssl.transport.pemcert_filepath: /stackable/opensearch/config/tls/tls.crt + plugins.security.ssl.transport.pemkey_filepath: /stackable/opensearch/config/tls/tls.key + plugins.security.ssl.transport.pemtrustedcas_filepath: /stackable/opensearch/config/tls/ca.crt plugins.security.ssl.http.enabled: "true" - plugins.security.ssl.http.pemcert_filepath: /usr/share/opensearch/config/tls/tls.crt - plugins.security.ssl.http.pemkey_filepath: /usr/share/opensearch/config/tls/tls.key - plugins.security.ssl.http.pemtrustedcas_filepath: /usr/share/opensearch/config/tls/ca.crt + plugins.security.ssl.http.pemcert_filepath: /stackable/opensearch/config/tls/tls.crt + plugins.security.ssl.http.pemkey_filepath: /stackable/opensearch/config/tls/tls.key + plugins.security.ssl.http.pemtrustedcas_filepath: /stackable/opensearch/config/tls/ca.crt + plugins.security.authcz.admin_dn: "CN=generated certificate for pod" podOverrides: spec: containers: - name: opensearch volumeMounts: - name: security-config - mountPath: /usr/share/opensearch/config/opensearch-security + mountPath: /stackable/opensearch/config/opensearch-security readOnly: true - name: tls - # The Java policy allows reading from /usr/share/opensearch/config. - mountPath: /usr/share/opensearch/config/tls + mountPath: /stackable/opensearch/config/tls readOnly: true securityContext: fsGroup: 1000 diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index 505167a..4e66bf6 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -2,7 +2,7 @@ dimensions: - name: opensearch values: - - 3.0.0 + - 3.1.0 - name: openshift values: - "false" @@ -11,6 +11,10 @@ tests: dimensions: - opensearch - openshift + - name: external-access + dimensions: + - opensearch + - openshift suites: - name: nightly patch: