Skip to content

Commit 21a1aae

Browse files
authored
feat!: Support configuring JVM arguments (#572)
* feat: Support configuring JVM arguments * changelog * fmt * fix: HADOOP_HEAPSIZE has no suffix * fixup, part 2 * clippy * Fix rustdoc * sneaky sneaky little bug * fix: Remove the hive-env.sh * Fix test * trigger CI
1 parent 8a9c5b9 commit 21a1aae

File tree

12 files changed

+361
-162
lines changed

12 files changed

+361
-162
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,25 @@ All notable changes to this project will be documented in this file.
88

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

1213
### Changed
1314

1415
- Default to OCI for image metadata and product image selection ([#561]).
1516

17+
### Fixed
18+
19+
- BREAKING: Remove the `hive-env.sh` config file, as e.g. setting `HADOOP_OPTS` in there had absolutely no effect.
20+
This is considered a fix, as users expected the envs to be used, but they haven't.
21+
Users should use `envOverrides` instead, which are actually working ([#572]).
22+
- BREAKING: The env variable `HADOOP_HEAPSIZE` was previously put in `hive-env.sh` and very likely had no effect.
23+
It is now passed as env variable, thus working.
24+
This might impact your stacklet as the heap size setting now actually has an effect ([#572]).
25+
1626
[#554]: https://github.com/stackabletech/hive-operator/pull/554
1727
[#560]: https://github.com/stackabletech/hive-operator/pull/560
1828
[#561]: https://github.com/stackabletech/hive-operator/pull/561
29+
[#572]: https://github.com/stackabletech/hive-operator/pull/572
1930

2031
## [24.11.1] - 2025-01-10
2132

deploy/helm/hive-operator/crds/crds.yaml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,32 @@ spec:
507507
default: {}
508508
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.'
509509
type: object
510+
jvmArgumentOverrides:
511+
default:
512+
add: []
513+
remove: []
514+
removeRegex: []
515+
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.
516+
properties:
517+
add:
518+
default: []
519+
description: JVM arguments to be added
520+
items:
521+
type: string
522+
type: array
523+
remove:
524+
default: []
525+
description: JVM arguments to be removed by exact match
526+
items:
527+
type: string
528+
type: array
529+
removeRegex:
530+
default: []
531+
description: JVM arguments matching any of this regexes will be removed
532+
items:
533+
type: string
534+
type: array
535+
type: object
510536
podOverrides:
511537
default: {}
512538
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.
@@ -775,6 +801,32 @@ spec:
775801
default: {}
776802
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.'
777803
type: object
804+
jvmArgumentOverrides:
805+
default:
806+
add: []
807+
remove: []
808+
removeRegex: []
809+
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.
810+
properties:
811+
add:
812+
default: []
813+
description: JVM arguments to be added
814+
items:
815+
type: string
816+
type: array
817+
remove:
818+
default: []
819+
description: JVM arguments to be removed by exact match
820+
items:
821+
type: string
822+
type: array
823+
removeRegex:
824+
default: []
825+
description: JVM arguments matching any of this regexes will be removed
826+
items:
827+
type: string
828+
type: array
829+
type: object
778830
podOverrides:
779831
default: {}
780832
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.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,11 @@ metastore:
101101

102102
The Hive operator also supports Pod overrides, allowing you to override any property that you can set on a Kubernetes Pod.
103103
Read the xref:concepts:overrides.adoc#pod-overrides[Pod overrides documentation] to learn more about this feature.
104+
105+
== JVM argument overrides
106+
107+
Stackable operators automatically determine the set of needed JVM arguments, such as memory settings or trust- and keystores.
108+
Using JVM argument overrides you can configure the JVM arguments xref:concepts:overrides.adoc#jvm-argument-overrides[according to the concepts page].
109+
110+
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`.
111+
`HADOOP_HEAPSIZE` can *not* have a unit suffix, it will always be an integer representing the number of megabytes heap available.

docs/modules/hive/partials/nav.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
** xref:hive:usage-guide/monitoring.adoc[]
1212
** xref:hive:usage-guide/resources.adoc[]
1313
** xref:hive:usage-guide/security.adoc[]
14-
** xref:hive:usage-guide/configuration-environment-overrides.adoc[]
14+
** xref:hive:usage-guide/overrides.adoc[]
1515
** xref:hive:usage-guide/operations/index.adoc[]
1616
*** xref:hive:usage-guide/operations/cluster-operations.adoc[]
1717
*** xref:hive:usage-guide/operations/pod-placement.adoc[]
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
use snafu::{OptionExt, ResultExt, Snafu};
2+
use stackable_operator::{
3+
memory::{BinaryMultiple, MemoryQuantity},
4+
role_utils::{self, GenericRoleConfig, JavaCommonConfig, JvmArgumentOverrides, Role},
5+
};
6+
7+
use crate::crd::{
8+
v1alpha1::HiveCluster, MetaStoreConfig, MetaStoreConfigFragment, JVM_SECURITY_PROPERTIES_FILE,
9+
METRICS_PORT, STACKABLE_CONFIG_DIR, STACKABLE_TRUST_STORE, STACKABLE_TRUST_STORE_PASSWORD,
10+
};
11+
12+
const JAVA_HEAP_FACTOR: f32 = 0.8;
13+
14+
#[derive(Snafu, Debug)]
15+
pub enum Error {
16+
#[snafu(display("invalid memory resource configuration - missing default or value in crd?"))]
17+
MissingMemoryResourceConfig,
18+
19+
#[snafu(display("invalid memory config"))]
20+
InvalidMemoryConfig {
21+
source: stackable_operator::memory::Error,
22+
},
23+
24+
#[snafu(display("failed to merge jvm argument overrides"))]
25+
MergeJvmArgumentOverrides { source: role_utils::Error },
26+
}
27+
28+
/// All JVM arguments.
29+
fn construct_jvm_args(
30+
hive: &HiveCluster,
31+
role: &Role<MetaStoreConfigFragment, GenericRoleConfig, JavaCommonConfig>,
32+
role_group: &str,
33+
) -> Result<Vec<String>, Error> {
34+
let mut jvm_args = vec![
35+
format!("-Djava.security.properties={STACKABLE_CONFIG_DIR}/{JVM_SECURITY_PROPERTIES_FILE}"),
36+
format!("-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar={METRICS_PORT}:/stackable/jmx/jmx_hive_config.yaml"),
37+
format!("-Djavax.net.ssl.trustStore={STACKABLE_TRUST_STORE}"),
38+
format!("-Djavax.net.ssl.trustStorePassword={STACKABLE_TRUST_STORE_PASSWORD}"),
39+
format!("-Djavax.net.ssl.trustStoreType=pkcs12"),
40+
];
41+
42+
if hive.has_kerberos_enabled() {
43+
jvm_args.push("-Djava.security.krb5.conf=/stackable/kerberos/krb5.conf".to_owned());
44+
}
45+
46+
let operator_generated = JvmArgumentOverrides::new_with_only_additions(jvm_args);
47+
let merged = role
48+
.get_merged_jvm_argument_overrides(role_group, &operator_generated)
49+
.context(MergeJvmArgumentOverridesSnafu)?;
50+
Ok(merged
51+
.effective_jvm_config_after_merging()
52+
// Sorry for the clone, that's how operator-rs is currently modelled :P
53+
.clone())
54+
}
55+
56+
/// Arguments that go into `HADOOP_OPTS`, so *not* the heap settings (which you can get using
57+
/// [`construct_hadoop_heapsize_env`]).
58+
pub fn construct_non_heap_jvm_args(
59+
hive: &HiveCluster,
60+
role: &Role<MetaStoreConfigFragment, GenericRoleConfig, JavaCommonConfig>,
61+
role_group: &str,
62+
) -> Result<String, Error> {
63+
let mut jvm_args = construct_jvm_args(hive, role, role_group)?;
64+
jvm_args.retain(|arg| !is_heap_jvm_argument(arg));
65+
66+
Ok(jvm_args.join(" "))
67+
}
68+
69+
/// This will be put into `HADOOP_HEAPSIZE`, which is just the heap size in megabytes (*without* the `m`
70+
/// unit prepended).
71+
pub fn construct_hadoop_heapsize_env(merged_config: &MetaStoreConfig) -> Result<String, Error> {
72+
let heap_size_in_mb = (MemoryQuantity::try_from(
73+
merged_config
74+
.resources
75+
.memory
76+
.limit
77+
.as_ref()
78+
.context(MissingMemoryResourceConfigSnafu)?,
79+
)
80+
.context(InvalidMemoryConfigSnafu)?
81+
* JAVA_HEAP_FACTOR)
82+
.scale_to(BinaryMultiple::Mebi);
83+
84+
Ok((heap_size_in_mb.value.floor() as u32).to_string())
85+
}
86+
87+
fn is_heap_jvm_argument(jvm_argument: &str) -> bool {
88+
let lowercase = jvm_argument.to_lowercase();
89+
90+
lowercase.starts_with("-xms") || lowercase.starts_with("-xmx")
91+
}
92+
93+
#[cfg(test)]
94+
mod tests {
95+
use super::*;
96+
use crate::crd::HiveRole;
97+
98+
#[test]
99+
fn test_construct_jvm_arguments_defaults() {
100+
let input = r#"
101+
apiVersion: hive.stackable.tech/v1alpha1
102+
kind: HiveCluster
103+
metadata:
104+
name: simple-hive
105+
spec:
106+
image:
107+
productVersion: 4.0.0
108+
clusterConfig:
109+
database:
110+
connString: jdbc:derby:;databaseName=/tmp/hive;create=true
111+
dbType: derby
112+
credentialsSecret: mySecret
113+
metastore:
114+
roleGroups:
115+
default:
116+
replicas: 1
117+
"#;
118+
let (hive, merged_config, role, rolegroup) = construct_boilerplate(input);
119+
let non_heap_jvm_args = construct_non_heap_jvm_args(&hive, &role, &rolegroup).unwrap();
120+
let hadoop_heapsize_env = construct_hadoop_heapsize_env(&merged_config).unwrap();
121+
122+
assert_eq!(
123+
non_heap_jvm_args,
124+
"-Djava.security.properties=/stackable/config/security.properties \
125+
-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar=9084:/stackable/jmx/jmx_hive_config.yaml \
126+
-Djavax.net.ssl.trustStore=/stackable/truststore.p12 \
127+
-Djavax.net.ssl.trustStorePassword=changeit \
128+
-Djavax.net.ssl.trustStoreType=pkcs12"
129+
);
130+
assert_eq!(hadoop_heapsize_env, "409");
131+
}
132+
133+
#[test]
134+
fn test_construct_jvm_argument_overrides() {
135+
let input = r#"
136+
apiVersion: hive.stackable.tech/v1alpha1
137+
kind: HiveCluster
138+
metadata:
139+
name: simple-hive
140+
spec:
141+
image:
142+
productVersion: 4.0.0
143+
clusterConfig:
144+
database:
145+
connString: jdbc:derby:;databaseName=/tmp/hive;create=true
146+
dbType: derby
147+
credentialsSecret: mySecret
148+
metastore:
149+
config:
150+
resources:
151+
memory:
152+
limit: 42Gi
153+
jvmArgumentOverrides:
154+
add:
155+
- -Dhttps.proxyHost=proxy.my.corp
156+
- -Dhttps.proxyPort=8080
157+
- -Djava.net.preferIPv4Stack=true
158+
roleGroups:
159+
default:
160+
replicas: 1
161+
jvmArgumentOverrides:
162+
# We need more memory!
163+
removeRegex:
164+
- -Xmx.*
165+
- -Dhttps.proxyPort=.*
166+
add:
167+
- -Xmx40000m
168+
- -Dhttps.proxyPort=1234
169+
"#;
170+
let (hive, merged_config, role, rolegroup) = construct_boilerplate(input);
171+
let non_heap_jvm_args = construct_non_heap_jvm_args(&hive, &role, &rolegroup).unwrap();
172+
let hadoop_heapsize_env = construct_hadoop_heapsize_env(&merged_config).unwrap();
173+
174+
assert_eq!(
175+
non_heap_jvm_args,
176+
"-Djava.security.properties=/stackable/config/security.properties \
177+
-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar=9084:/stackable/jmx/jmx_hive_config.yaml \
178+
-Djavax.net.ssl.trustStore=/stackable/truststore.p12 \
179+
-Djavax.net.ssl.trustStorePassword=changeit \
180+
-Djavax.net.ssl.trustStoreType=pkcs12 \
181+
-Dhttps.proxyHost=proxy.my.corp \
182+
-Djava.net.preferIPv4Stack=true \
183+
-Dhttps.proxyPort=1234"
184+
);
185+
assert_eq!(hadoop_heapsize_env, "34406");
186+
}
187+
188+
fn construct_boilerplate(
189+
hive_cluster: &str,
190+
) -> (
191+
HiveCluster,
192+
MetaStoreConfig,
193+
Role<MetaStoreConfigFragment, GenericRoleConfig, JavaCommonConfig>,
194+
String,
195+
) {
196+
let hive: HiveCluster = serde_yaml::from_str(hive_cluster).expect("illegal test input");
197+
198+
let hive_role = HiveRole::MetaStore;
199+
let rolegroup_ref = hive.metastore_rolegroup_ref("default");
200+
let merged_config = hive.merged_config(&hive_role, &rolegroup_ref).unwrap();
201+
let role = hive.spec.metastore.clone().unwrap();
202+
203+
(hive, merged_config, role, "default".to_owned())
204+
}
205+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod jvm;

0 commit comments

Comments
 (0)