Skip to content

Commit dc3c371

Browse files
chore: Fix TODOs and add unit tests
1 parent 610d8dd commit dc3c371

10 files changed

Lines changed: 528 additions & 127 deletions

File tree

rust/operator-binary/src/controller.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ type OpenSearchRoleGroupConfig =
124124
type OpenSearchNodeResources =
125125
stackable_operator::commons::resources::Resources<v1alpha1::StorageConfig>;
126126

127-
/// The validated [`v1alpha1::OpenSearchConfig`]
127+
/// Validated [`v1alpha1::OpenSearchConfig`]
128128
#[derive(Clone, Debug, PartialEq)]
129129
pub struct ValidatedOpenSearchConfig {
130130
pub affinity: StackableAffinity,
@@ -135,6 +135,7 @@ pub struct ValidatedOpenSearchConfig {
135135
pub termination_grace_period_seconds: i64,
136136
}
137137

138+
/// Validated log configuration per container
138139
#[derive(Clone, Debug, PartialEq)]
139140
pub struct ValidatedLogging {
140141
pub opensearch_container: ValidatedContainerLogConfigChoice,

rust/operator-binary/src/controller/build/node_config.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,6 @@ mod tests {
493493
..TestConfig::default()
494494
});
495495

496-
println!(
497-
"node_config_single_node: {:?}",
498-
node_config_single_node.role_group_config.config
499-
);
500-
501496
let node_config_multiple_nodes = node_config(TestConfig {
502497
replicas: 2,
503498
..TestConfig::default()

rust/operator-binary/src/controller/build/product_logging/config.rs

Lines changed: 91 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
use std::cmp;
1+
//! OpenSearch specific log configuration
2+
3+
use std::{cmp, collections::BTreeMap};
24

35
use stackable_operator::{
46
memory::{BinaryMultiple, MemoryQuantity},
5-
product_logging::spec::AutomaticContainerLogConfig,
7+
product_logging::spec::{AppenderConfig, AutomaticContainerLogConfig, LogLevel, LoggerConfig},
68
};
79

8-
use crate::{crd::v1alpha1, framework::product_logging::framework::STACKABLE_LOG_DIR};
10+
use crate::{
11+
crd::v1alpha1::{self},
12+
framework::{
13+
builder::pod::container::{EnvVarName, EnvVarSet},
14+
product_logging::framework::STACKABLE_LOG_DIR,
15+
},
16+
};
917

10-
/// The log configuration file
18+
/// OpenSearch log configuration file
1119
pub const CONFIGURATION_FILE_LOG4J2_PROPERTIES: &str = "log4j2.properties";
1220

1321
const OPENSEARCH_SERVER_LOG_FILE: &str = "opensearch_server.json";
@@ -17,24 +25,39 @@ pub const MAX_OPENSEARCH_SERVER_LOG_FILES_SIZE: MemoryQuantity = MemoryQuantity
1725
unit: BinaryMultiple::Mebi,
1826
};
1927

28+
/// Create a log4j2 configuration from the given automatic log configuration
2029
pub fn create_log4j2_config(config: &AutomaticContainerLogConfig) -> String {
21-
let log_path = format!(
22-
"{STACKABLE_LOG_DIR}/{container}/{OPENSEARCH_SERVER_LOG_FILE}",
23-
container = v1alpha1::Container::OpenSearch.to_container_name()
24-
);
30+
[
31+
log4j2_root_logger_config(&config.root_log_level()),
32+
log4j2_loggers_config(&config.loggers),
33+
log4j2_console_appender_config(&config.console),
34+
log4j2_file_appender_config(&config.file),
35+
]
36+
.iter()
37+
.flatten()
38+
.map(|(key, value)| format!("{key} = {value}\n"))
39+
.collect()
40+
}
2541

26-
let number_of_archived_log_files = 1;
27-
let max_log_files_size_in_mib = MAX_OPENSEARCH_SERVER_LOG_FILES_SIZE
28-
.scale_to(BinaryMultiple::Mebi)
29-
.floor()
30-
.value as u32;
31-
let max_log_file_size_in_mib = cmp::max(
32-
1,
33-
max_log_files_size_in_mib / (1 + number_of_archived_log_files),
34-
);
42+
fn log4j2_root_logger_config(root_log_level: &LogLevel) -> Vec<(String, String)> {
43+
vec![
44+
(
45+
"rootLogger.level".to_owned(),
46+
root_log_level.to_log4j2_literal(),
47+
),
48+
(
49+
"rootLogger.appenderRef.CONSOLE.ref".to_owned(),
50+
"CONSOLE".to_owned(),
51+
),
52+
(
53+
"rootLogger.appenderRef.FILE.ref".to_owned(),
54+
"FILE".to_owned(),
55+
),
56+
]
57+
}
3558

36-
let loggers = config
37-
.loggers
59+
fn log4j2_loggers_config(loggers_config: &BTreeMap<String, LoggerConfig>) -> Vec<(String, String)> {
60+
loggers_config
3861
.iter()
3962
.filter(|(name, _)| name.as_str() != AutomaticContainerLogConfig::ROOT_LOGGER)
4063
.enumerate()
@@ -50,24 +73,13 @@ pub fn create_log4j2_config(config: &AutomaticContainerLogConfig) -> String {
5073
),
5174
]
5275
})
53-
.collect::<Vec<_>>();
54-
55-
let root_logger = vec![
56-
(
57-
"rootLogger.level".to_owned(),
58-
config.root_log_level().to_log4j2_literal(),
59-
),
60-
(
61-
"rootLogger.appenderRef.CONSOLE.ref".to_owned(),
62-
"CONSOLE".to_owned(),
63-
),
64-
(
65-
"rootLogger.appenderRef.FILE.ref".to_owned(),
66-
"FILE".to_owned(),
67-
),
68-
];
76+
.collect::<Vec<_>>()
77+
}
6978

70-
let console_appender = vec![
79+
fn log4j2_console_appender_config(
80+
console_appender_config: &Option<AppenderConfig>,
81+
) -> Vec<(String, String)> {
82+
vec![
7183
("appender.CONSOLE.type".to_owned(), "Console".to_owned()),
7284
("appender.CONSOLE.name".to_owned(), "CONSOLE".to_owned()),
7385
(
@@ -90,16 +102,34 @@ pub fn create_log4j2_config(config: &AutomaticContainerLogConfig) -> String {
90102
),
91103
(
92104
"appender.CONSOLE.filter.threshold.level".to_owned(),
93-
config
94-
.console
105+
console_appender_config
95106
.as_ref()
96107
.and_then(|console| console.level)
97108
.unwrap_or_default()
98109
.to_log4j2_literal(),
99110
),
100-
];
111+
]
112+
}
113+
114+
fn log4j2_file_appender_config(
115+
file_appender_config: &Option<AppenderConfig>,
116+
) -> Vec<(String, String)> {
117+
let log_path = format!(
118+
"{STACKABLE_LOG_DIR}/{container}/{OPENSEARCH_SERVER_LOG_FILE}",
119+
container = v1alpha1::Container::OpenSearch.to_container_name()
120+
);
121+
122+
let number_of_archived_log_files = 1;
123+
let max_log_files_size_in_mib = MAX_OPENSEARCH_SERVER_LOG_FILES_SIZE
124+
.scale_to(BinaryMultiple::Mebi)
125+
.floor()
126+
.value as u32;
127+
let max_log_file_size_in_mib = cmp::max(
128+
1,
129+
max_log_files_size_in_mib / (1 + number_of_archived_log_files),
130+
);
101131

102-
let file_appender = vec![
132+
vec![
103133
("appender.FILE.type".to_owned(), "RollingFile".to_owned()),
104134
("appender.FILE.name".to_owned(), "FILE".to_owned()),
105135
("appender.FILE.fileName".to_owned(), log_path.to_owned()),
@@ -141,33 +171,38 @@ pub fn create_log4j2_config(config: &AutomaticContainerLogConfig) -> String {
141171
),
142172
(
143173
"appender.FILE.filter.threshold.level".to_owned(),
144-
config
145-
.file
174+
file_appender_config
146175
.as_ref()
147176
.and_then(|file| file.level)
148177
.unwrap_or_default()
149178
.to_log4j2_literal(),
150179
),
151-
];
152-
153-
[root_logger, loggers, console_appender, file_appender]
154-
.iter()
155-
.flatten()
156-
.map(|(key, value)| format!("{key} = {value}\n"))
157-
.collect()
180+
]
158181
}
159182

183+
/// Returns the Vector configuration file content as YAML
160184
pub fn vector_config_file_content() -> String {
161185
include_str!("vector.yaml").to_owned()
162186
}
163187

188+
/// Returns the OpenSearch specific environment variables used in the Vector configuration file
189+
///
190+
/// The common environment variables are already set in
191+
/// [`crate::framework::product_logging::framework::vector_container`].
192+
pub fn vector_config_file_extra_env_vars() -> EnvVarSet {
193+
EnvVarSet::new().with_value(
194+
&EnvVarName::from_str_unsafe("OPENSEARCH_SERVER_LOG_FILE"),
195+
"opensearch_server.json",
196+
)
197+
}
198+
164199
#[cfg(test)]
165200
mod tests {
166201
use stackable_operator::product_logging::spec::{
167202
AppenderConfig, AutomaticContainerLogConfig, LogLevel, LoggerConfig,
168203
};
169204

170-
use super::create_log4j2_config;
205+
use super::{create_log4j2_config, vector_config_file_extra_env_vars};
171206

172207
#[test]
173208
pub fn test_create_log4j2_config() {
@@ -227,4 +262,10 @@ mod tests {
227262

228263
assert_eq!(expected_config, log4j2_config);
229264
}
265+
266+
#[test]
267+
pub fn test_vector_config_file_extra_env_vars() {
268+
// Test that the function does not panic
269+
vector_config_file_extra_env_vars();
270+
}
230271
}

rust/operator-binary/src/controller/build/role_group_builder.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use stackable_operator::{
1818
},
1919
apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString},
2020
},
21-
kvp::{Annotations, Label, Labels},
21+
kvp::{Annotation, Annotations, Label, Labels},
2222
product_logging::framework::{
2323
VECTOR_CONFIG_FILE, calculate_log_volume_size_limit, create_vector_shutdown_file_command,
2424
remove_vector_shutdown_file_command,
@@ -36,7 +36,9 @@ use crate::{
3636
constant,
3737
controller::{
3838
ContextNames, OpenSearchRoleGroupConfig, ValidatedCluster,
39-
build::product_logging::config::MAX_OPENSEARCH_SERVER_LOG_FILES_SIZE,
39+
build::product_logging::config::{
40+
MAX_OPENSEARCH_SERVER_LOG_FILES_SIZE, vector_config_file_extra_env_vars,
41+
},
4042
},
4143
crd::v1alpha1,
4244
framework::{
@@ -217,6 +219,13 @@ impl<'a> RoleGroupBuilder<'a> {
217219
let metadata = ObjectMetaBuilder::new()
218220
.with_labels(self.recommended_labels())
219221
.with_labels(node_role_labels)
222+
.with_annotation(
223+
Annotation::try_from((
224+
"kubectl.kubernetes.io/default-container".to_owned(),
225+
v1alpha1::Container::OpenSearch.to_container_name(),
226+
))
227+
.expect("should be a valid annotation"),
228+
)
220229
.build();
221230

222231
let opensearch_container = self.build_opensearch_container();
@@ -229,13 +238,12 @@ impl<'a> RoleGroupBuilder<'a> {
229238
.map(|vector_container_log_config| {
230239
vector_container(
231240
&v1alpha1::Container::Vector.to_container_name(),
232-
vector_container_log_config,
233-
&self.resource_names.cluster_name,
234-
&self.resource_names.role_name,
235-
&self.resource_names.role_group_name,
236241
&self.cluster.image,
242+
vector_container_log_config,
243+
&self.resource_names,
237244
&CONFIG_VOLUME_NAME,
238245
&LOG_VOLUME_NAME,
246+
vector_config_file_extra_env_vars(),
239247
)
240248
});
241249

@@ -294,7 +302,6 @@ impl<'a> RoleGroupBuilder<'a> {
294302
.pod_anti_affinity
295303
.clone(),
296304
}),
297-
// TODO Add annotation that the opensearch container is the main one
298305
containers: [Some(opensearch_container), vector_container]
299306
.into_iter()
300307
.flatten()
@@ -768,7 +775,7 @@ mod tests {
768775
.expect("should be serializable");
769776

770777
// The content of log4j2.properties is already tested in the
771-
// `conrtoller::build::product_logging::config` module.
778+
// `controller::build::product_logging::config` module.
772779
config_map["data"]["log4j2.properties"].take();
773780
// The content of opensearch.yml is already tested in the `controller::build::node_config`
774781
// module.
@@ -860,6 +867,9 @@ mod tests {
860867
"serviceName": "my-opensearch-cluster-nodes-default-headless",
861868
"template": {
862869
"metadata": {
870+
"annotations": {
871+
"kubectl.kubernetes.io/default-container": "opensearch",
872+
},
863873
"labels": {
864874
"app.kubernetes.io/component": "nodes",
865875
"app.kubernetes.io/instance": "my-opensearch-cluster",
@@ -912,8 +922,12 @@ mod tests {
912922
"\n",
913923
"rm -f /stackable/log/_vector/shutdown\n",
914924
"prepare_signal_handlers\n",
925+
"if command --search containerdebug >/dev/null 2>&1; then\n",
915926
"containerdebug --output=/stackable/log/containerdebug-state.json --loop &\n",
916-
"/stackable/opensearch/opensearch-docker-entrypoint.sh &\n",
927+
"else\n",
928+
"echo >&2 \"containerdebug not installed; Proceed without it.\"\n",
929+
"fi\n",
930+
"./opensearch-docker-entrypoint.sh &\n",
917931
"wait_for_termination $!\n",
918932
"mkdir -p /stackable/log/_vector && touch /stackable/log/_vector/shutdown"
919933
)
@@ -1092,7 +1106,7 @@ mod tests {
10921106
"volumeMounts": [
10931107
{
10941108
"mountPath": "/stackable/config/vector.yaml",
1095-
"name": "log-config",
1109+
"name": "config",
10961110
"readOnly": true,
10971111
"subPath": "vector.yaml",
10981112
},

rust/operator-binary/src/crd/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,13 @@ pub mod versioned {
152152
#[fragment_attrs(serde(default))]
153153
pub graceful_shutdown_timeout: Duration,
154154

155-
/// This field controls which [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html) is used to expose the HTTP communication.
155+
/// This field controls which
156+
/// [ListenerClass](https://docs.stackable.tech/home/nightly/listener-operator/listenerclass.html)
157+
/// is used to expose the HTTP communication.
156158
#[fragment_attrs(serde(default))]
157159
pub listener_class: ListenerClassName,
158160

161+
/// Logging configuration
159162
#[fragment_attrs(serde(default))]
160163
pub logging: Logging<Container>,
161164

@@ -169,7 +172,6 @@ pub mod versioned {
169172
pub resources: Resources<StorageConfig>,
170173
}
171174

172-
// TODO All derives required?
173175
#[derive(
174176
Clone,
175177
Debug,
@@ -258,10 +260,10 @@ impl v1alpha1::OpenSearchConfig {
258260
graceful_shutdown_timeout: Some(
259261
Duration::from_str("2m").expect("should be a valid duration"),
260262
),
261-
// Defaults taken from the Helm chart, see
262-
// https://github.com/opensearch-project/helm-charts/blob/opensearch-3.0.0/charts/opensearch/values.yaml#L16-L20
263263
listener_class: Some(DEFAULT_LISTENER_CLASS.to_owned()),
264264
logging: product_logging::spec::default_logging(),
265+
// Defaults taken from the Helm chart, see
266+
// https://github.com/opensearch-project/helm-charts/blob/opensearch-3.0.0/charts/opensearch/values.yaml#L16-L20
265267
node_roles: Some(NodeRoles(vec![
266268
v1alpha1::NodeRole::ClusterManager,
267269
v1alpha1::NodeRole::Ingest,

0 commit comments

Comments
 (0)