Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ All notable changes to this project will be documented in this file.
- Support objectOverrides using `.spec.objectOverrides`.
See [objectOverrides concepts page](https://docs.stackable.tech/home/nightly/concepts/overrides/#object-overrides) for details ([#93]).
- Enable the [restart-controller](https://docs.stackable.tech/home/nightly/commons-operator/restarter/), so that the Pods are automatically restarted on config changes ([#97]).
- Configure OpenSearch to publish the fully-qualified domain names of the nodes instead of the IP
addresses, so that TLS certificates can be verified ([#100]).

### Changed

Expand All @@ -23,6 +25,7 @@ All notable changes to this project will be documented in this file.
[#91]: https://github.com/stackabletech/opensearch-operator/pull/91
[#93]: https://github.com/stackabletech/opensearch-operator/pull/93
[#97]: https://github.com/stackabletech/opensearch-operator/pull/97
[#100]: https://github.com/stackabletech/opensearch-operator/pull/100

## [25.11.0] - 2025-11-07

Expand Down
23 changes: 18 additions & 5 deletions rust/operator-binary/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use build::build;
use snafu::{ResultExt, Snafu};
use stackable_operator::{
cluster_resources::ClusterResourceApplyStrategy,
commons::{affinity::StackableAffinity, product_image_selection::ResolvedProductImage},
commons::{
affinity::StackableAffinity, networking::DomainName,
product_image_selection::ResolvedProductImage,
},
crd::listener::v1alpha1::Listener,
k8s_openapi::api::{
apps::v1::StatefulSet,
Expand Down Expand Up @@ -56,6 +59,7 @@ pub struct ContextNames {
pub product_name: ProductName,
pub operator_name: OperatorName,
pub controller_name: ControllerName,
pub cluster_domain_name: DomainName,
}

/// The controller context
Expand All @@ -66,19 +70,22 @@ pub struct Context {

impl Context {
pub fn new(client: stackable_operator::client::Client, operator_name: OperatorName) -> Self {
let cluster_domain_name = client.kubernetes_cluster_info.cluster_domain.clone();

Context {
client,
names: Self::context_names(operator_name),
names: Self::context_names(operator_name, cluster_domain_name),
}
}

fn context_names(operator_name: OperatorName) -> ContextNames {
fn context_names(operator_name: OperatorName, cluster_domain_name: DomainName) -> ContextNames {
ContextNames {
product_name: ProductName::from_str("opensearch")
.expect("should be a valid product name"),
operator_name,
controller_name: ControllerName::from_str("opensearchcluster")
.expect("should be a valid controller name"),
cluster_domain_name,
}
}

Expand Down Expand Up @@ -379,7 +386,10 @@ mod tests {
};

use stackable_operator::{
commons::{affinity::StackableAffinity, product_image_selection::ResolvedProductImage},
commons::{
affinity::StackableAffinity, networking::DomainName,
product_image_selection::ResolvedProductImage,
},
k8s_openapi::api::core::v1::PodTemplateSpec,
kvp::LabelValue,
product_logging::spec::AutomaticContainerLogConfig,
Expand All @@ -406,7 +416,10 @@ mod tests {
#[test]
fn test_context_names() {
// Test that the function does not panic
Context::context_names(OperatorName::from_str_unsafe("my-operator"));
Context::context_names(
OperatorName::from_str_unsafe("my-operator"),
DomainName::from_str("cluster.local").expect("should be a valid domain name"),
);
}

#[test]
Expand Down
7 changes: 6 additions & 1 deletion rust/operator-binary/src/controller/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ mod tests {
};

use stackable_operator::{
commons::{affinity::StackableAffinity, product_image_selection::ResolvedProductImage},
commons::{
affinity::StackableAffinity, networking::DomainName,
product_image_selection::ResolvedProductImage,
},
k8s_openapi::api::core::v1::PodTemplateSpec,
kube::Resource,
kvp::LabelValue,
Expand Down Expand Up @@ -158,6 +161,8 @@ mod tests {
product_name: ProductName::from_str_unsafe("opensearch"),
operator_name: OperatorName::from_str_unsafe("opensearch.stackable.tech"),
controller_name: ControllerName::from_str_unsafe("opensearchcluster"),
cluster_domain_name: DomainName::from_str("cluster.local")
.expect("should be a valid domain name"),
}
}

Expand Down
66 changes: 65 additions & 1 deletion rust/operator-binary/src/controller/build/node_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
use std::str::FromStr;

use serde_json::{Value, json};
use stackable_operator::builder::pod::container::FieldPathEnvVar;
use stackable_operator::{
builder::pod::container::FieldPathEnvVar, commons::networking::DomainName,
};

use super::ValidatedCluster;
use crate::{
Expand Down Expand Up @@ -32,6 +34,11 @@ pub const CONFIG_OPTION_DISCOVERY_SEED_HOSTS: &str = "discovery.seed_hosts";
/// Type: string
pub const CONFIG_OPTION_DISCOVERY_TYPE: &str = "discovery.type";

/// Specifies an address or addresses that an OpenSearch node publishes to other nodes for HTTP
/// communication.
/// Type: (comma-separated) list of strings
pub const CONFIG_OPTION_HTTP_PUBLISH_HOST: &str = "http.publish_host";

/// A list of cluster-manager-eligible nodes used to bootstrap the cluster.
/// Type: (comma-separated) list of strings
pub const CONFIG_OPTION_INITIAL_CLUSTER_MANAGER_NODES: &str =
Expand All @@ -41,6 +48,11 @@ pub const CONFIG_OPTION_INITIAL_CLUSTER_MANAGER_NODES: &str =
/// Type: string
pub const CONFIG_OPTION_NETWORK_HOST: &str = "network.host";

/// Specifies an address or addresses that an OpenSearch node publishes to other nodes in the
/// cluster so that they can connect to it.
/// Type: (comma-separated) list of strings
pub const CONFIG_OPTION_NETWORK_PUBLISH_HOST: &str = "network.publish_host";

/// The custom node attribute "role-group"
/// Type: string
pub const CONFIG_OPTION_NODE_ATTR_ROLE_GROUP: &str = "node.attr.role-group";
Expand Down Expand Up @@ -97,6 +109,11 @@ pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH: &str =
pub const CONFIG_OPTION_PLUGINS_SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH: &str =
"plugins.security.ssl.transport.pemtrustedcas_filepath";

/// Specifies an address or addresses that an OpenSearch node publishes to other nodes for
/// transport communication.
/// Type: (comma-separated) list of strings
pub const CONFIG_OPTION_TRANSPORT_PUBLISH_HOST: &str = "transport.publish_host";

const DEFAULT_OPENSEARCH_HOME: &str = "/stackable/opensearch";

/// Configuration of an OpenSearch node based on the cluster and role-group configuration
Expand All @@ -105,6 +122,8 @@ pub struct NodeConfig {
role_group_name: RoleGroupName,
role_group_config: OpenSearchRoleGroupConfig,
pub discovery_service_name: ServiceName,
cluster_domain_name: DomainName,
headless_service_name: ServiceName,
}

// Most functions are public because their configuration values could also be used in environment
Expand All @@ -115,12 +134,16 @@ impl NodeConfig {
role_group_name: RoleGroupName,
role_group_config: OpenSearchRoleGroupConfig,
discovery_service_name: ServiceName,
cluster_domain_name: DomainName,
headless_service_name: ServiceName,
) -> Self {
Self {
cluster,
role_group_name,
role_group_config,
discovery_service_name,
cluster_domain_name,
headless_service_name,
}
}

Expand Down Expand Up @@ -258,13 +281,36 @@ impl NodeConfig {
/// The environment variables should only contain node-specific configuration options.
/// Cluster-wide options should be added to the configuration file.
pub fn environment_variables(&self) -> EnvVarSet {
let fqdn = format!(
"$(_POD_NAME).{}.{}.svc.{}",
self.headless_service_name, self.cluster.namespace, self.cluster_domain_name
);

EnvVarSet::new()
.with_field_path(
// Prefix with an underscore, so that it occurs before the other environment
// variables which depend on it.
&EnvVarName::from_str_unsafe("_POD_NAME"),
FieldPathEnvVar::Name,
)
// Set the OpenSearch node name to the Pod name.
// The node name is used e.g. for INITIAL_CLUSTER_MANAGER_NODES.
.with_field_path(
&EnvVarName::from_str_unsafe(CONFIG_OPTION_NODE_NAME),
FieldPathEnvVar::Name,
)
.with_value(
&EnvVarName::from_str_unsafe(CONFIG_OPTION_NETWORK_PUBLISH_HOST),
&fqdn,
)
.with_value(
&EnvVarName::from_str_unsafe(CONFIG_OPTION_TRANSPORT_PUBLISH_HOST),
&fqdn,
)
.with_value(
&EnvVarName::from_str_unsafe(CONFIG_OPTION_HTTP_PUBLISH_HOST),
&fqdn,
)
.with_value(
&EnvVarName::from_str_unsafe(CONFIG_OPTION_DISCOVERY_SEED_HOSTS),
&self.discovery_service_name,
Expand Down Expand Up @@ -510,6 +556,8 @@ mod tests {
role_group_name,
role_group_config,
ServiceName::from_str_unsafe("my-opensearch-cluster-manager"),
DomainName::from_str("cluster.local").expect("should be a valid domain name"),
ServiceName::from_str_unsafe("my-opensearch-cluster-default-headless"),
)
}

Expand Down Expand Up @@ -609,6 +657,10 @@ mod tests {
assert_eq!(
EnvVarSet::new()
.with_value(&EnvVarName::from_str_unsafe("TEST"), "value")
.with_field_path(
&EnvVarName::from_str_unsafe("_POD_NAME"),
FieldPathEnvVar::Name
)
.with_value(
&EnvVarName::from_str_unsafe("cluster.initial_cluster_manager_nodes"),
"my-opensearch-cluster-nodes-default-0,my-opensearch-cluster-nodes-default-1",
Expand All @@ -617,13 +669,25 @@ mod tests {
&EnvVarName::from_str_unsafe("discovery.seed_hosts"),
"my-opensearch-cluster-manager",
)
.with_value(
&EnvVarName::from_str_unsafe("http.publish_host"),
"$(_POD_NAME).my-opensearch-cluster-default-headless.default.svc.cluster.local",
)
.with_value(
&EnvVarName::from_str_unsafe("network.publish_host"),
"$(_POD_NAME).my-opensearch-cluster-default-headless.default.svc.cluster.local",
)
.with_field_path(
&EnvVarName::from_str_unsafe("node.name"),
FieldPathEnvVar::Name
)
.with_value(
&EnvVarName::from_str_unsafe("node.roles"),
"cluster_manager,data,ingest,remote_cluster_client"
)
.with_value(
&EnvVarName::from_str_unsafe("transport.publish_host"),
"$(_POD_NAME).my-opensearch-cluster-default-headless.default.svc.cluster.local",
),
node_config.environment_variables()
);
Expand Down
3 changes: 3 additions & 0 deletions rust/operator-binary/src/controller/build/role_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ mod tests {
use stackable_operator::{
commons::{
affinity::StackableAffinity,
networking::DomainName,
product_image_selection::{ProductImage, ResolvedProductImage},
resources::Resources,
},
Expand Down Expand Up @@ -259,6 +260,8 @@ mod tests {
product_name: ProductName::from_str_unsafe("opensearch"),
operator_name: OperatorName::from_str_unsafe("opensearch.stackable.tech"),
controller_name: ControllerName::from_str_unsafe("opensearchcluster"),
cluster_domain_name: DomainName::from_str("cluster.local")
.expect("should be a valid domain name"),
}
}

Expand Down
41 changes: 33 additions & 8 deletions rust/operator-binary/src/controller/build/role_group_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ impl<'a> RoleGroupBuilder<'a> {
context_names: &'a ContextNames,
discovery_service_name: ServiceName,
) -> RoleGroupBuilder<'a> {
let resource_names = ResourceNames {
cluster_name: cluster.name.clone(),
role_name: ValidatedCluster::role_name(),
role_group_name: role_group_name.clone(),
};
RoleGroupBuilder {
service_account_name,
cluster: cluster.clone(),
Expand All @@ -124,15 +129,13 @@ impl<'a> RoleGroupBuilder<'a> {
role_group_name.clone(),
role_group_config.clone(),
discovery_service_name,
context_names.cluster_domain_name.clone(),
resource_names.headless_service_name(),
),
role_group_name: role_group_name.clone(),
role_group_config,
context_names,
resource_names: ResourceNames {
cluster_name: cluster.name.clone(),
role_name: ValidatedCluster::role_name(),
role_group_name,
},
resource_names,
}
}

Expand Down Expand Up @@ -810,8 +813,8 @@ mod tests {
use serde_json::json;
use stackable_operator::{
commons::{
affinity::StackableAffinity, product_image_selection::ResolvedProductImage,
resources::Resources,
affinity::StackableAffinity, networking::DomainName,
product_image_selection::ResolvedProductImage, resources::Resources,
},
k8s_openapi::api::core::v1::PodTemplateSpec,
kvp::LabelValue,
Expand Down Expand Up @@ -864,6 +867,8 @@ mod tests {
product_name: ProductName::from_str_unsafe("opensearch"),
operator_name: OperatorName::from_str_unsafe("opensearch.stackable.tech"),
controller_name: ControllerName::from_str_unsafe("opensearchcluster"),
cluster_domain_name: DomainName::from_str("cluster.local")
.expect("should be a valid domain name"),
}
}

Expand Down Expand Up @@ -1132,6 +1137,14 @@ mod tests {
"-c"
],
"env": [
{
"name": "_POD_NAME",
"valueFrom": {
"fieldRef": {
"fieldPath": "metadata.name"
}
}
},
{
"name": "cluster.initial_cluster_manager_nodes",
"value": ""
Expand All @@ -1140,6 +1153,14 @@ mod tests {
"name": "discovery.seed_hosts",
"value": "my-opensearch-cluster"
},
{
"name": "http.publish_host",
"value": "$(_POD_NAME).my-opensearch-cluster-nodes-default-headless.default.svc.cluster.local"
},
{
"name": "network.publish_host",
"value": "$(_POD_NAME).my-opensearch-cluster-nodes-default-headless.default.svc.cluster.local"
},
{
"name": "node.name",
"valueFrom": {
Expand All @@ -1151,7 +1172,11 @@ mod tests {
{
"name": "node.roles",
"value": "cluster_manager,data,ingest,remote_cluster_client"
}
},
{
"name": "transport.publish_host",
"value": "$(_POD_NAME).my-opensearch-cluster-nodes-default-headless.default.svc.cluster.local"
},
],
"image": "oci.stackable.tech/sdp/opensearch:3.1.0-stackable0.0.0-dev",
"imagePullPolicy": "Always",
Expand Down
3 changes: 3 additions & 0 deletions rust/operator-binary/src/controller/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ mod tests {
commons::{
affinity::StackableAffinity,
cluster_operation::ClusterOperation,
networking::DomainName,
product_image_selection::ResolvedProductImage,
resources::{CpuLimits, MemoryLimits, PvcConfig, Resources},
},
Expand Down Expand Up @@ -689,6 +690,8 @@ mod tests {
product_name: ProductName::from_str_unsafe("opensearch"),
operator_name: OperatorName::from_str_unsafe("opensearch.stackable.tech"),
controller_name: ControllerName::from_str_unsafe("opensearchcluster"),
cluster_domain_name: DomainName::from_str("cluster.local")
.expect("should be a valid domain name"),
}
}

Expand Down
Loading