Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.

- Run a `containerdebug` process in the background of each Hive container to collect debugging information ([#554]).
- Aggregate emitted Kubernetes events on the CustomResources ([#560]).
- Support configuring JVM arguments ([#572]).

### Changed

Expand All @@ -16,6 +17,7 @@ All notable changes to this project will be documented in this file.
[#554]: https://github.com/stackabletech/hive-operator/pull/554
[#560]: https://github.com/stackabletech/hive-operator/pull/560
[#561]: https://github.com/stackabletech/hive-operator/pull/561
[#572]: https://github.com/stackabletech/hive-operator/pull/572

## [24.11.1] - 2025-01-10

Expand Down
52 changes: 52 additions & 0 deletions deploy/helm/hive-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,32 @@ spec:
default: {}
description: '`envOverrides` configure environment variables to be set in the Pods. It is a map from strings to strings - environment variables and the value to set. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about the product specific environment variables that are available.'
type: object
jvmArgumentOverrides:
default:
add: []
remove: []
removeRegex: []
description: Allows overriding JVM arguments. Please read on the [JVM argument overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#jvm-argument-overrides) for details on the usage.
properties:
add:
default: []
description: JVM arguments to be added
items:
type: string
type: array
remove:
default: []
description: JVM arguments to be removed by exact match
items:
type: string
type: array
removeRegex:
default: []
description: JVM arguments matching any of this regexes will be removed
items:
type: string
type: array
type: object
podOverrides:
default: {}
description: In the `podOverrides` property you can define a [PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) to override any property that can be set on a Kubernetes Pod. Read the [Pod overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#pod-overrides) for more information.
Expand Down Expand Up @@ -775,6 +801,32 @@ spec:
default: {}
description: '`envOverrides` configure environment variables to be set in the Pods. It is a map from strings to strings - environment variables and the value to set. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about the product specific environment variables that are available.'
type: object
jvmArgumentOverrides:
default:
add: []
remove: []
removeRegex: []
description: Allows overriding JVM arguments. Please read on the [JVM argument overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#jvm-argument-overrides) for details on the usage.
properties:
add:
default: []
description: JVM arguments to be added
items:
type: string
type: array
remove:
default: []
description: JVM arguments to be removed by exact match
items:
type: string
type: array
removeRegex:
default: []
description: JVM arguments matching any of this regexes will be removed
items:
type: string
type: array
type: object
podOverrides:
default: {}
description: In the `podOverrides` property you can define a [PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) to override any property that can be set on a Kubernetes Pod. Read the [Pod overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#pod-overrides) for more information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,10 @@ metastore:

The Hive operator also supports Pod overrides, allowing you to override any property that you can set on a Kubernetes Pod.
Read the xref:concepts:overrides.adoc#pod-overrides[Pod overrides documentation] to learn more about this feature.

== JVM argument overrides

Stackable operators automatically determine the set of needed JVM arguments, such as memory settings or trust- and keystores.
Using JVM argument overrides you can configure the JVM arguments xref:concepts:overrides.adoc#jvm-argument-overrides[according to the concepts page].

One thing that is different for Hive metastores, is that all heap-related arguments will be passed in via the env variable `HADOOP_HEAPSIZE`, all the other ones via `HADOOP_OPTS`.
2 changes: 1 addition & 1 deletion docs/modules/hive/partials/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
** xref:hive:usage-guide/monitoring.adoc[]
** xref:hive:usage-guide/resources.adoc[]
** xref:hive:usage-guide/security.adoc[]
** xref:hive:usage-guide/configuration-environment-overrides.adoc[]
** xref:hive:usage-guide/overrides.adoc[]
** xref:hive:usage-guide/operations/index.adoc[]
*** xref:hive:usage-guide/operations/cluster-operations.adoc[]
*** xref:hive:usage-guide/operations/pod-placement.adoc[]
Expand Down
225 changes: 225 additions & 0 deletions rust/operator-binary/src/config/jvm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
use crate::crd::{
v1alpha1::HiveCluster, MetaStoreConfig, MetaStoreConfigFragment, JVM_SECURITY_PROPERTIES_FILE,
METRICS_PORT, STACKABLE_CONFIG_DIR, STACKABLE_TRUST_STORE, STACKABLE_TRUST_STORE_PASSWORD,
};
use snafu::{OptionExt, ResultExt, Snafu};
use stackable_operator::{
memory::{BinaryMultiple, MemoryQuantity},
role_utils::{self, GenericRoleConfig, JavaCommonConfig, JvmArgumentOverrides, Role},
};

const JAVA_HEAP_FACTOR: f32 = 0.8;

#[derive(Snafu, Debug)]
pub enum Error {
#[snafu(display("invalid memory resource configuration - missing default or value in crd?"))]
MissingMemoryResourceConfig,

#[snafu(display("invalid memory config"))]
InvalidMemoryConfig {
source: stackable_operator::memory::Error,
},

#[snafu(display("failed to merge jvm argument overrides"))]
MergeJvmArgumentOverrides { source: role_utils::Error },
}

/// All JVM arguments.
fn construct_jvm_args(
hive: &HiveCluster,
merged_config: &MetaStoreConfig,
role: &Role<MetaStoreConfigFragment, GenericRoleConfig, JavaCommonConfig>,
role_group: &str,
) -> Result<Vec<String>, Error> {
let heap_size = MemoryQuantity::try_from(
merged_config
.resources
.memory
.limit
.as_ref()
.context(MissingMemoryResourceConfigSnafu)?,
)
.context(InvalidMemoryConfigSnafu)?
.scale_to(BinaryMultiple::Mebi)
* JAVA_HEAP_FACTOR;
let java_heap = heap_size
.format_for_java()
.context(InvalidMemoryConfigSnafu)?;

let mut jvm_args = vec![
// Heap settings
format!("-Xmx{java_heap}"),
format!("-Xms{java_heap}"),
format!("-Djava.security.properties={STACKABLE_CONFIG_DIR}/{JVM_SECURITY_PROPERTIES_FILE}"),
format!("-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar={METRICS_PORT}:/stackable/jmx/jmx_hive_config.yaml"),
format!("-Djavax.net.ssl.trustStore={STACKABLE_TRUST_STORE}"),
format!("-Djavax.net.ssl.trustStorePassword={STACKABLE_TRUST_STORE_PASSWORD}"),
format!("-Djavax.net.ssl.trustStoreType=pkcs12"),
];

if hive.has_kerberos_enabled() {
jvm_args.push("-Djava.security.krb5.conf=/stackable/kerberos/krb5.conf".to_owned());
}

let operator_generated = JvmArgumentOverrides::new_with_only_additions(jvm_args);
let merged = role
.get_merged_jvm_argument_overrides(role_group, &operator_generated)
.context(MergeJvmArgumentOverridesSnafu)?;
Ok(merged
.effective_jvm_config_after_merging()
// Sorry for the clone, that's how operator-rs is currently modelled :P
.clone())
}

/// Arguments that go into `HADOOP_OPTS`, so *not* the heap settings (which you can get using
/// [`construct_heap_jvm_args`]).
pub fn construct_non_heap_jvm_args(
hive: &HiveCluster,
merged_config: &MetaStoreConfig,
role: &Role<MetaStoreConfigFragment, GenericRoleConfig, JavaCommonConfig>,
role_group: &str,
) -> Result<String, Error> {
let mut jvm_args = construct_jvm_args(hive, merged_config, role, role_group)?;
jvm_args.retain(|arg| !is_heap_jvm_argument(arg));

Ok(jvm_args.join(" "))
}

/// Arguments that go into `HADOOP_HEAPSIZE`.
/// You can get the normal JVM arguments using [`construct_non_heap_jvm_args`].
pub fn construct_heap_jvm_args(
hive: &HiveCluster,
merged_config: &MetaStoreConfig,
role: &Role<MetaStoreConfigFragment, GenericRoleConfig, JavaCommonConfig>,
role_group: &str,
) -> Result<String, Error> {
let mut jvm_args = construct_jvm_args(hive, merged_config, role, role_group)?;
jvm_args.retain(|arg| is_heap_jvm_argument(arg));

Ok(jvm_args.join(" "))
}

fn is_heap_jvm_argument(jvm_argument: &str) -> bool {
let lowercase = jvm_argument.to_lowercase();

lowercase.starts_with("-xms") || lowercase.starts_with("-xmx")
}

#[cfg(test)]
mod tests {
use crate::crd::HiveRole;

use super::*;

#[test]
fn test_construct_jvm_arguments_defaults() {
let input = r#"
apiVersion: hive.stackable.tech/v1alpha1
kind: HiveCluster
metadata:
name: simple-hive
spec:
image:
productVersion: 4.0.0
clusterConfig:
database:
connString: jdbc:derby:;databaseName=/tmp/hive;create=true
dbType: derby
credentialsSecret: mySecret
metastore:
roleGroups:
default:
replicas: 1
"#;
let (hive, hive_role, role, merged_config) = construct_boilerplate(input);
let non_heap_jvm_args =
construct_non_heap_jvm_args(&hive, &hive_role, &role, &merged_config).unwrap();
let heap_jvm_args =
construct_heap_jvm_args(&hive, &hive_role, &role, &merged_config).unwrap();

assert_eq!(
non_heap_jvm_args,
"-Djava.security.properties=/stackable/config/security.properties \
-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar=9084:/stackable/jmx/jmx_hive_config.yaml \
-Djavax.net.ssl.trustStore=/stackable/truststore.p12 \
-Djavax.net.ssl.trustStorePassword=changeit \
-Djavax.net.ssl.trustStoreType=pkcs12"
);
assert_eq!(heap_jvm_args, "-Xmx409m -Xms409m");
}

#[test]
fn test_construct_jvm_argument_overrides() {
let input = r#"
apiVersion: hive.stackable.tech/v1alpha1
kind: HiveCluster
metadata:
name: simple-hive
spec:
image:
productVersion: 4.0.0
clusterConfig:
database:
connString: jdbc:derby:;databaseName=/tmp/hive;create=true
dbType: derby
credentialsSecret: mySecret
metastore:
config:
resources:
memory:
limit: 42Gi
jvmArgumentOverrides:
add:
- -Dhttps.proxyHost=proxy.my.corp
- -Dhttps.proxyPort=8080
- -Djava.net.preferIPv4Stack=true
roleGroups:
default:
replicas: 1
jvmArgumentOverrides:
# We need more memory!
removeRegex:
- -Xmx.*
- -Dhttps.proxyPort=.*
add:
- -Xmx40000m
- -Dhttps.proxyPort=1234
"#;
let (hive, merged_config, role, role_group) = construct_boilerplate(input);
let non_heap_jvm_args =
construct_non_heap_jvm_args(&hive, &merged_config, &role, &role_group).unwrap();
let heap_jvm_args =
construct_heap_jvm_args(&hive, &merged_config, &role, &role_group).unwrap();

assert_eq!(
non_heap_jvm_args,
"-Djava.security.properties=/stackable/config/security.properties \
-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar=9084:/stackable/jmx/jmx_hive_config.yaml \
-Djavax.net.ssl.trustStore=/stackable/truststore.p12 \
-Djavax.net.ssl.trustStorePassword=changeit \
-Djavax.net.ssl.trustStoreType=pkcs12 \
-Dhttps.proxyHost=proxy.my.corp \
-Djava.net.preferIPv4Stack=true \
-Dhttps.proxyPort=1234"
);
assert_eq!(heap_jvm_args, "-Xms34406m -Xmx40000m");
}

fn construct_boilerplate(
hive_cluster: &str,
) -> (
HiveCluster,
MetaStoreConfig,
Role<MetaStoreConfigFragment, GenericRoleConfig, JavaCommonConfig>,
String,
) {
let hive: HiveCluster = serde_yaml::from_str(hive_cluster).expect("illegal test input");

let hive_role = HiveRole::MetaStore;
let rolegroup_ref = hive.metastore_rolegroup_ref("default");
let merged_config = hive.merged_config(&hive_role, &rolegroup_ref).unwrap();
let role = hive.spec.metastore.clone().unwrap();

(hive, merged_config, role, "default".to_owned())
}
}
1 change: 1 addition & 0 deletions rust/operator-binary/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod jvm;
Loading
Loading