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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Support floating tags for product images via the new `spec.image.stackableVersionPolicy` field
([#809]).
- Add `/ready` endpoint to the operator Deployment, which reports the CRD installation status ([#812]).
- Add support for 2.6.7 and 3.0.0 (experimental) ([#815]).
- Masters and region servers now have a default affinity to the OPA Pods when OPA authorization is configured ([#816]).

### Changed
Expand Down Expand Up @@ -58,6 +59,7 @@
[#803]: https://github.com/stackabletech/hbase-operator/pull/803
[#809]: https://github.com/stackabletech/hbase-operator/pull/809
[#812]: https://github.com/stackabletech/hbase-operator/pull/812
[#815]: https://github.com/stackabletech/hbase-operator/pull/815
[#816]: https://github.com/stackabletech/hbase-operator/pull/816
[#821]: https://github.com/stackabletech/hbase-operator/pull/821

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/hbase/examples/getting_started/hbase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ metadata:
name: simple-hbase
spec:
image:
productVersion: 2.6.6
productVersion: 2.6.7
clusterConfig:
hdfsConfigMapName: simple-hdfs
zookeeperConfigMapName: simple-hbase-znode
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/hbase/examples/getting_started/hbase.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ metadata:
name: simple-hbase
spec:
image:
productVersion: 2.6.6
productVersion: 2.6.7
clusterConfig:
hdfsConfigMapName: simple-hdfs
zookeeperConfigMapName: simple-hbase-znode
Expand Down
2 changes: 2 additions & 0 deletions docs/modules/hbase/pages/reference/discovery.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ The ConfigMap data values are formatted as Hadoop XML files which allows simple

`hbase-site.xml`::
Contains the needed information to connect to Zookeeper and use that to establish a connection to HBase.
It also lists the master Pods in `hbase.client.bootstrap.servers`, which HBase 3.x clients use by default to connect (RPC-based connection registry).
These addresses are cluster-internal DNS names, so they only resolve for clients running inside the Kubernetes cluster.

=== Kerberos
In case Kerberos is enabled according to the xref:usage-guide/security.adoc[security documentation], the discovery ConfigMap also includes the information that clients must authenticate themselves using Kerberos.
Expand Down
6 changes: 4 additions & 2 deletions docs/modules/hbase/partials/supported-versions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
// This is a separate file, since it is used by both the direct HBase-Operator documentation, and the overarching
// Stackable Platform documentation.

- 2.6.4 (DEPRECATED)
- 2.6.6 (LTS)
* 3.0.0 (experimental)
** Phoenix does not yet support HBase 3.x and has been omitted from the product image.
* 2.6.7 (LTS)
* 2.6.6 (deprecated)
162 changes: 159 additions & 3 deletions rust/operator-binary/src/controller/build/properties/hbase_site.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@

use std::collections::BTreeMap;

use stackable_operator::v2::config_overrides::KeyValueConfigOverrides;
use stackable_operator::{
utils::cluster_info::KubernetesClusterInfo, v2::config_overrides::KeyValueConfigOverrides,
};

use crate::{
controller::build::{opa::HbaseOpaConfig, properties::build_xml_config},
controller::{
ValidatedCluster,
build::{opa::HbaseOpaConfig, properties::build_xml_config},
},
crd::{
AnyServiceConfig, HBASE_CLUSTER_DISTRIBUTED, HBASE_MASTER_PORT, HBASE_MASTER_UI_PORT,
HBASE_REGIONSERVER_PORT, HBASE_REGIONSERVER_UI_PORT, HBASE_ROOTDIR, HbaseRole,
Expand Down Expand Up @@ -38,20 +43,61 @@ const ENV_HBASE_SERVICE_HOST: &str = "${env:HBASE_SERVICE_HOST}";
const ENV_HBASE_SERVICE_PORT: &str = "${env:HBASE_SERVICE_PORT}";
const ENV_HBASE_INFO_PORT: &str = "${env:HBASE_INFO_PORT}";

/// Bootstrap nodes for the RPC-based connection registry, the client default since HBase 3.0.0.
/// HBase 2.x defaults to the ZooKeeper-based registry and does not read this key.
pub const HBASE_CLIENT_BOOTSTRAP_SERVERS: &str = "hbase.client.bootstrap.servers";

/// Lists every master Pod as `<sts>-<n>.<headless>.<ns>.svc.<domain>:<port>`.
///
/// A role group's headless Service name is not sufficient: the client resolves each entry to a
/// single address and only fails over between entries, and the headless Service also publishes
/// not-ready Pods.
pub fn client_bootstrap_servers(
cluster: &ValidatedCluster,
cluster_info: &KubernetesClusterInfo,
) -> String {
let namespace = cluster.namespace.as_ref();
let cluster_domain = &cluster_info.cluster_domain;
cluster
.role_group_configs
.get(&HbaseRole::Master)
.into_iter()
.flatten()
.flat_map(|(role_group_name, role_group)| {
let resource_names =
cluster.role_group_resource_names(&HbaseRole::Master, role_group_name);
let stateful_set = resource_names.stateful_set_name();
let headless_service = resource_names.headless_service_name();
// `None` leaves `replicas` unset on the StatefulSet, which Kubernetes defaults to 1
(0..role_group.replicas.unwrap_or(1)).map(move |ordinal| {
format!(
"{stateful_set}-{ordinal}.{headless_service}.{namespace}.svc.{cluster_domain}:{HBASE_MASTER_PORT}"
)
})
})
.collect::<Vec<_>>()
.join(",")
}

/// Renders `hbase-site.xml`.
pub fn build(
role: &HbaseRole,
merged_config: &AnyServiceConfig,
zookeeper_config: BTreeMap<String, String>,
kerberos_config: BTreeMap<String, String>,
opa_config: Option<&HbaseOpaConfig>,
client_bootstrap_servers: String,
overrides: KeyValueConfigOverrides,
) -> String {
let mut config: BTreeMap<String, String> = BTreeMap::new();

// Defaults
config.insert(HBASE_CLUSTER_DISTRIBUTED.to_string(), "true".to_string());
config.insert(HBASE_ROOTDIR.to_string(), merged_config.hbase_rootdir());
config.insert(
HBASE_CLIENT_BOOTSTRAP_SERVERS.to_string(),
client_bootstrap_servers,
);

config.extend(zookeeper_config);
config.extend(kerberos_config);
Expand Down Expand Up @@ -140,7 +186,71 @@ mod tests {
use indoc::indoc;

use super::*;
use crate::test_utils::{merged_config, validated_cluster};
use crate::test_utils::{
cluster_info, hbase_from_yaml, merged_config, validated_cluster, validated_cluster_from,
};

fn bootstrap_servers_for(masters_yaml: &str) -> String {
let hbase = hbase_from_yaml(&format!(
r#"
---
apiVersion: hbase.stackable.tech/v1alpha1
kind: HbaseCluster
metadata:
name: hbase
namespace: default
uid: c2c8c5c0-0b5a-4b1e-9f3e-1a2b3c4d5e6f
spec:
image:
productVersion: 2.6.3
clusterConfig:
hdfsConfigMapName: simple-hdfs
zookeeperConfigMapName: simple-znode
masters:
roleGroups:
{masters_yaml}
regionServers:
roleGroups:
default:
replicas: 1
restServers:
roleGroups:
default:
replicas: 1
"#
));
client_bootstrap_servers(&validated_cluster_from(&hbase), &cluster_info())
}

// The role-group lines are spliced under `roleGroups:` and must be 6 spaces deep, so they are
// written with explicit indentation (`indoc!` would strip it).
#[test]
fn bootstrap_servers_list_every_master_pod() {
let servers = bootstrap_servers_for(
" default:\n replicas: 2\n other:\n replicas: 1",
);
assert_eq!(
servers,
"hbase-master-default-0.hbase-master-default-headless.default.svc.cluster.local:16000,\
hbase-master-default-1.hbase-master-default-headless.default.svc.cluster.local:16000,\
hbase-master-other-0.hbase-master-other-headless.default.svc.cluster.local:16000"
);
}

#[test]
fn bootstrap_servers_default_to_one_replica() {
let servers = bootstrap_servers_for(" default: {}");
assert_eq!(
servers,
"hbase-master-default-0.hbase-master-default-headless.default.svc.cluster.local:16000"
);
}

#[test]
fn bootstrap_servers_skip_scaled_down_role_groups() {
let servers = bootstrap_servers_for(" default:\n replicas: 0");
assert_eq!(servers, "");
}

#[test]
fn renders_operator_defaults() {
Expand All @@ -152,6 +262,7 @@ mod tests {
BTreeMap::new(),
BTreeMap::new(),
None,
String::new(),
KeyValueConfigOverrides::default(),
);
assert!(
Expand All @@ -178,6 +289,7 @@ mod tests {
BTreeMap::new(),
BTreeMap::new(),
None,
String::new(),
KeyValueConfigOverrides::default(),
);
assert!(
Expand All @@ -204,6 +316,7 @@ mod tests {
BTreeMap::new(),
BTreeMap::new(),
None,
String::new(),
KeyValueConfigOverrides::default(),
);
assert!(
Expand All @@ -214,6 +327,48 @@ mod tests {
);
}

#[test]
fn renders_client_bootstrap_servers() {
let validated_cluster = validated_cluster();
let merged = merged_config(&validated_cluster, &HbaseRole::RestServer);
let xml = build(
&HbaseRole::RestServer,
merged,
BTreeMap::new(),
BTreeMap::new(),
None,
"m-0.m-headless.ns.svc.cluster.local:16000".to_string(),
KeyValueConfigOverrides::default(),
);
assert!(
xml.contains(indoc! {"
<name>hbase.client.bootstrap.servers</name>
<value>m-0.m-headless.ns.svc.cluster.local:16000</value>"}),
"{xml}"
);
}

#[test]
fn user_override_wins_for_client_bootstrap_servers() {
let validated_cluster = validated_cluster();
let merged = merged_config(&validated_cluster, &HbaseRole::RestServer);
let xml = build(
&HbaseRole::RestServer,
merged,
BTreeMap::new(),
BTreeMap::new(),
None,
"m-0.m-headless.ns.svc.cluster.local:16000".to_string(),
[("hbase.client.bootstrap.servers", "custom:16000")].into(),
);
assert!(
xml.contains(indoc! {"
<name>hbase.client.bootstrap.servers</name>
<value>custom:16000</value>"}),
"{xml}"
);
}

#[test]
fn user_override_wins() {
let validated_cluster = validated_cluster();
Expand All @@ -224,6 +379,7 @@ mod tests {
BTreeMap::new(),
BTreeMap::new(),
None,
String::new(),
[("hbase.cluster.distributed", "false")].into(),
);
assert!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub fn build_rolegroup_config_map(
.as_hbase_settings(),
kerberos::hbase_site_kerberos_config(cluster, cluster_info),
cluster_config.hbase_opa_config.as_ref(),
hbase_site::client_bootstrap_servers(cluster, cluster_info),
overrides.hbase_site_xml.clone(),
);

Expand Down
Loading
Loading