-
-
Notifications
You must be signed in to change notification settings - Fork 4
feat: HBase Listener integration #639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 52 commits
05e5c16
776e273
d824b50
967239d
4eb4e35
96593c7
ae1654a
53ea8bc
f8cc1c7
7a2756b
2affa65
652999e
c8a2644
dd793c0
bd1db17
80adf71
aee4fc2
bf969a0
90efe2b
0030bfb
8313995
465d0f1
fb8efde
5ddbff2
6146593
8acadff
68ab9cc
9a4231a
61c79a5
7b742c0
fde3c8a
550ea47
71015a5
c555448
8b3e650
39740fa
00ef5c9
74bfc29
6c616a1
4309b9d
51b2837
a51003c
4ceb588
4122baf
fda8ca3
2c41938
16e7c45
88d7458
42148aa
67d6e54
474bdfe
e8469ab
1aa786a
9177c2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,31 @@ | ||
= Service exposition with ListenerClasses | ||
:description: Configure HBase service exposure using ListenerClasses to control internal and external access for all roles. | ||
|
||
Apache HBase offers an API. | ||
The operator deploys a service called `<name>` (where `<name>` is the name of the HbaseCluster) through which HBase can be reached. | ||
|
||
This service can have either the `cluster-internal` or `external-unstable` type. | ||
`external-stable` is not supported for HBase at the moment. | ||
Read more about the types in the xref:concepts:service-exposition.adoc[service exposition] documentation at platform level. | ||
|
||
This is how the listener class is configured: | ||
The operator deploys a xref:listener-operator:listener.adoc[Listener] for each Master, Regionserver and Restserver pod. | ||
They all default to only being accessible from within the Kubernetes cluster, but this can be changed by setting `.spec.{masters,regionServers,restServers}.config.listenerClass`: | ||
|
||
[source,yaml] | ||
---- | ||
spec: | ||
clusterConfig: | ||
listenerClass: cluster-internal # <1> | ||
masters: | ||
config: | ||
listenerClass: external-unstable # <1> | ||
regionServers: | ||
config: | ||
listenerClass: external-unstable | ||
restServers: | ||
config: | ||
listenerClass: external-unstable | ||
---- | ||
<1> Specify one of `external-stable`, `external-unstable`, `cluster-internal` (the default setting is `cluster-internal`). | ||
This can be set separately for all roles. | ||
|
||
Listener endpoints are written to `hbase-site.xml` like this: | ||
|
||
[source,xml] | ||
---- | ||
<property> | ||
<name>hbase.listener.endpoint</name> | ||
<value>172.19.0.3:32445</value> | ||
</property> | ||
---- | ||
<1> The default `cluster-internal` setting. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,8 +59,6 @@ pub const SSL_CLIENT_XML: &str = "ssl-client.xml"; | |
|
||
pub const HBASE_CLUSTER_DISTRIBUTED: &str = "hbase.cluster.distributed"; | ||
pub const HBASE_ROOTDIR: &str = "hbase.rootdir"; | ||
pub const HBASE_UNSAFE_REGIONSERVER_HOSTNAME_DISABLE_MASTER_REVERSEDNS: &str = | ||
"hbase.unsafe.regionserver.hostname.disable.master.reversedns"; | ||
|
||
pub const HBASE_UI_PORT_NAME_HTTP: &str = "ui-http"; | ||
pub const HBASE_UI_PORT_NAME_HTTPS: &str = "ui-https"; | ||
|
@@ -76,10 +74,14 @@ pub const HBASE_REGIONSERVER_PORT: u16 = 16020; | |
pub const HBASE_REGIONSERVER_UI_PORT: u16 = 16030; | ||
pub const HBASE_REST_PORT: u16 = 8080; | ||
pub const HBASE_REST_UI_PORT: u16 = 8085; | ||
pub const LISTENER_VOLUME_NAME: &str = "listener"; | ||
pub const LISTENER_VOLUME_DIR: &str = "/stackable/listener"; | ||
|
||
const DEFAULT_REGION_MOVER_TIMEOUT: Duration = Duration::from_minutes_unchecked(59); | ||
const DEFAULT_REGION_MOVER_DELTA_TO_SHUTDOWN: Duration = Duration::from_minutes_unchecked(1); | ||
|
||
const DEFAULT_LISTENER_CLASS: &str = "cluster-internal"; | ||
|
||
#[derive(Snafu, Debug)] | ||
pub enum Error { | ||
#[snafu(display("the role [{role}] is invalid and does not exist in HBase"))] | ||
|
@@ -102,6 +104,12 @@ pub enum Error { | |
|
||
#[snafu(display("incompatible merge types"))] | ||
IncompatibleMergeTypes, | ||
|
||
#[snafu(display("role-group is not valid"))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This error message says nothing; why is it not valid? |
||
NoRoleGroup, | ||
|
||
#[snafu(display("role-group not found by name"))] | ||
RoleGroupNotFound, | ||
} | ||
|
||
#[versioned(version(name = "v1alpha1"))] | ||
|
@@ -171,18 +179,6 @@ pub mod versioned { | |
/// for a ZooKeeper cluster. | ||
pub zookeeper_config_map_name: String, | ||
|
||
/// This field controls which type of Service the Operator creates for this HbaseCluster: | ||
/// | ||
/// * cluster-internal: Use a ClusterIP service | ||
/// | ||
/// * external-unstable: Use a NodePort service | ||
/// | ||
/// This is a temporary solution with the goal to keep yaml manifests forward compatible. | ||
/// In the future, this setting will control which [ListenerClass](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listenerclass.html) | ||
/// will be used to expose the service, and ListenerClass names will stay the same, allowing for a non-breaking change. | ||
#[serde(default)] | ||
pub listener_class: CurrentlySupportedListenerClasses, | ||
|
||
/// Settings related to user [authentication](DOCS_BASE_URL_PLACEHOLDER/usage-guide/security). | ||
pub authentication: Option<AuthenticationConfig>, | ||
|
||
|
@@ -212,6 +208,11 @@ impl v1alpha1::HbaseCluster { | |
let defaults = | ||
AnyConfigFragment::default_for(role, &self.name_any(), hdfs_discovery_cm_name); | ||
|
||
// Trivial values for role-groups are not allowed | ||
if role_group.is_empty() { | ||
return Err(Error::NoRoleGroup); | ||
} | ||
Comment on lines
+211
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validating this feels like a broader question across the SDP, not something to sneak in here. |
||
|
||
let (mut role_config, mut role_group_config) = match role { | ||
HbaseRole::RegionServer => { | ||
let role = self | ||
|
@@ -227,7 +228,7 @@ impl v1alpha1::HbaseCluster { | |
.role_groups | ||
.get(role_group) | ||
.map(|rg| rg.config.config.clone()) | ||
.unwrap_or_default(); | ||
.context(RoleGroupNotFoundSnafu)?; | ||
|
||
( | ||
AnyConfigFragment::RegionServer(role_config), | ||
|
@@ -249,7 +250,7 @@ impl v1alpha1::HbaseCluster { | |
.role_groups | ||
.get(role_group) | ||
.map(|rg| rg.config.config.clone()) | ||
.unwrap_or_default(); | ||
.context(RoleGroupNotFoundSnafu)?; | ||
|
||
// Retrieve role resource config | ||
( | ||
|
@@ -269,7 +270,7 @@ impl v1alpha1::HbaseCluster { | |
.role_groups | ||
.get(role_group) | ||
.map(|rg| rg.config.config.clone()) | ||
.unwrap_or_default(); | ||
.context(RoleGroupNotFoundSnafu)?; | ||
|
||
// Retrieve role resource config | ||
( | ||
|
@@ -526,7 +527,7 @@ impl v1alpha1::HbaseCluster { | |
} | ||
|
||
/// Name of the port used by the Web UI, which depends on HTTPS usage | ||
fn ui_port_name(&self) -> String { | ||
pub fn ui_port_name(&self) -> String { | ||
if self.has_https_enabled() { | ||
HBASE_UI_PORT_NAME_HTTPS | ||
} else { | ||
|
@@ -552,27 +553,6 @@ pub fn merged_env(rolegroup_config: Option<&BTreeMap<String, String>>) -> Vec<En | |
merged_env | ||
} | ||
|
||
// TODO: Temporary solution until listener-operator is finished | ||
#[derive(Clone, Debug, Default, Display, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] | ||
#[serde(rename_all = "PascalCase")] | ||
pub enum CurrentlySupportedListenerClasses { | ||
#[default] | ||
#[serde(rename = "cluster-internal")] | ||
ClusterInternal, | ||
|
||
#[serde(rename = "external-unstable")] | ||
ExternalUnstable, | ||
} | ||
|
||
impl CurrentlySupportedListenerClasses { | ||
pub fn k8s_service_type(&self) -> String { | ||
match self { | ||
CurrentlySupportedListenerClasses::ClusterInternal => "ClusterIP".to_string(), | ||
CurrentlySupportedListenerClasses::ExternalUnstable => "NodePort".to_string(), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, Deserialize, Eq, Hash, JsonSchema, PartialEq, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct KerberosConfig { | ||
|
@@ -696,6 +676,7 @@ impl HbaseRole { | |
affinity: get_affinity(cluster_name, self, hdfs_discovery_cm_name), | ||
graceful_shutdown_timeout: Some(graceful_shutdown_timeout), | ||
requested_secret_lifetime: Some(requested_secret_lifetime), | ||
listener_class: Some(DEFAULT_LISTENER_CLASS.to_string()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these go under There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HBase uses listener-per-pod, so rolegroup should be fine. |
||
} | ||
} | ||
|
||
|
@@ -796,6 +777,7 @@ impl AnyConfigFragment { | |
cli_opts: None, | ||
}, | ||
requested_secret_lifetime: Some(HbaseRole::DEFAULT_REGION_SECRET_LIFETIME), | ||
listener_class: Some(DEFAULT_LISTENER_CLASS.to_string()), | ||
}) | ||
} | ||
HbaseRole::RestServer => AnyConfigFragment::RestServer(HbaseConfigFragment { | ||
|
@@ -807,6 +789,7 @@ impl AnyConfigFragment { | |
HbaseRole::DEFAULT_REST_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT, | ||
), | ||
requested_secret_lifetime: Some(HbaseRole::DEFAULT_REST_SECRET_LIFETIME), | ||
listener_class: Some(DEFAULT_LISTENER_CLASS.to_string()), | ||
}), | ||
HbaseRole::Master => AnyConfigFragment::Master(HbaseConfigFragment { | ||
hbase_rootdir: None, | ||
|
@@ -817,6 +800,7 @@ impl AnyConfigFragment { | |
HbaseRole::DEFAULT_MASTER_GRACEFUL_SHUTDOWN_TIMEOUT, | ||
), | ||
requested_secret_lifetime: Some(HbaseRole::DEFAULT_MASTER_SECRET_LIFETIME), | ||
listener_class: Some(DEFAULT_LISTENER_CLASS.to_string()), | ||
}), | ||
} | ||
} | ||
|
@@ -894,6 +878,9 @@ pub struct HbaseConfig { | |
/// Please note that this can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate. | ||
#[fragment_attrs(serde(default))] | ||
pub requested_secret_lifetime: Option<Duration>, | ||
|
||
/// This field controls which [ListenerClass](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listenerclass.html) is used to expose this rolegroup. | ||
pub listener_class: String, | ||
} | ||
|
||
impl Configuration for HbaseConfigFragment { | ||
|
@@ -952,10 +939,6 @@ impl Configuration for HbaseConfigFragment { | |
HBASE_CLUSTER_DISTRIBUTED.to_string(), | ||
Some("true".to_string()), | ||
); | ||
result.insert( | ||
HBASE_UNSAFE_REGIONSERVER_HOSTNAME_DISABLE_MASTER_REVERSEDNS.to_string(), | ||
Some("true".to_string()), | ||
); | ||
result.insert(HBASE_ROOTDIR.to_string(), self.hbase_rootdir.clone()); | ||
} | ||
_ => {} | ||
|
@@ -1047,6 +1030,9 @@ pub struct RegionServerConfig { | |
/// The operator will compute a timeout period for the region move that will not exceed the graceful shutdown timeout. | ||
#[fragment_attrs(serde(default))] | ||
pub region_mover: RegionMover, | ||
|
||
/// This field controls which [ListenerClass](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listenerclass.html) is used to expose this rolegroup. | ||
pub listener_class: String, | ||
} | ||
|
||
impl Configuration for RegionServerConfigFragment { | ||
|
@@ -1103,10 +1089,6 @@ impl Configuration for RegionServerConfigFragment { | |
HBASE_CLUSTER_DISTRIBUTED.to_string(), | ||
Some("true".to_string()), | ||
); | ||
result.insert( | ||
HBASE_UNSAFE_REGIONSERVER_HOSTNAME_DISABLE_MASTER_REVERSEDNS.to_string(), | ||
Some("true".to_string()), | ||
); | ||
result.insert(HBASE_ROOTDIR.to_string(), self.hbase_rootdir.clone()); | ||
} | ||
_ => {} | ||
|
@@ -1172,6 +1154,14 @@ impl AnyServiceConfig { | |
} | ||
} | ||
|
||
pub fn listener_class(&self) -> String { | ||
match self { | ||
AnyServiceConfig::Master(config) => config.listener_class.clone(), | ||
AnyServiceConfig::RegionServer(config) => config.listener_class.clone(), | ||
AnyServiceConfig::RestServer(config) => config.listener_class.clone(), | ||
} | ||
} | ||
|
||
/// Returns command line arguments to pass on to the region mover tool. | ||
/// The following arguments are excluded because they are already part of the | ||
/// hbase-entrypoint.sh script. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.