Skip to content

Commit 4b201a1

Browse files
committed
Discovery configMap is filled properly
1 parent 8781225 commit 4b201a1

17 files changed

Lines changed: 362 additions & 71 deletions

File tree

rust/operator-binary/src/controller.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ pub async fn reconcile_hive(
449449
.context(ApplyRoleBindingSnafu)?;
450450

451451
let mut ss_cond_builder = StatefulSetConditionBuilder::default();
452-
let mut listeners = Vec::<Listener>::new();
453-
// for each rg a service needs to be build for hive
452+
// Collecting listener objects with corresponding rolegroup to fill the discovery configMap later on
453+
let mut listener_refs = BTreeMap::<&String, Listener>::new();
454454
for (rolegroup_name, rolegroup_config) in metastore_config.iter() {
455455
let rolegroup = hive.metastore_rolegroup_ref(rolegroup_name);
456456

@@ -486,7 +486,7 @@ pub async fn reconcile_hive(
486486
&rolegroup,
487487
config.listener_class,
488488
)?;
489-
// TODO: Listener name might be wrong
489+
490490
let listener = cluster_resources
491491
.add(client, rg_group_listener)
492492
.await
@@ -501,7 +501,7 @@ pub async fn reconcile_hive(
501501
rolegroup: rolegroup.clone(),
502502
})?;
503503

504-
listeners.push(listener);
504+
listener_refs.insert(rolegroup_name, listener);
505505

506506
cluster_resources
507507
.add(client, rg_configmap)
@@ -534,10 +534,15 @@ pub async fn reconcile_hive(
534534
// We don't /need/ stability, but it's still nice to avoid spurious changes where possible.
535535
let mut discovery_hash = FnvHasher::with_key(0);
536536

537-
for discovery_cm in
538-
discovery::build_discovery_configmaps(hive, hive, &resolved_product_image, None, &listeners)
539-
.await
540-
.context(BuildDiscoveryConfigSnafu)?
537+
for discovery_cm in discovery::build_discovery_configmaps(
538+
hive,
539+
hive,
540+
&resolved_product_image,
541+
None,
542+
listener_refs,
543+
)
544+
.await
545+
.context(BuildDiscoveryConfigSnafu)?
541546
{
542547
let discovery_cm = cluster_resources
543548
.add(client, discovery_cm)
Lines changed: 51 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use std::num::TryFromIntError;
1+
use std::{collections::BTreeMap, num::TryFromIntError};
22

33
use snafu::{OptionExt, ResultExt, Snafu};
44
use stackable_operator::{
55
builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder},
66
commons::product_image_selection::ResolvedProductImage,
7-
crd::listener,
7+
crd::listener::v1alpha1::Listener,
88
k8s_openapi::api::core::v1::{ConfigMap, Service},
99
kube::{Resource, runtime::reflector::ObjectRef},
1010
};
@@ -56,6 +56,8 @@ pub enum Error {
5656
MetadataBuild {
5757
source: stackable_operator::builder::meta::Error,
5858
},
59+
#[snafu(display("failed to apply group listener for {rolegroup}"))]
60+
RoleGroupListenerHasNoAddress { rolegroup: String },
5961
}
6062

6163
/// Builds discovery [`ConfigMap`]s for connecting to a [`v1alpha1::HiveCluster`] for all expected
@@ -65,7 +67,7 @@ pub async fn build_discovery_configmaps(
6567
hive: &v1alpha1::HiveCluster,
6668
resolved_product_image: &ResolvedProductImage,
6769
chroot: Option<&str>,
68-
listeners: &[listener::v1alpha1::Listener],
70+
listener_refs: BTreeMap<&String, Listener>,
6971
) -> Result<Vec<ConfigMap>, Error> {
7072
let name = owner
7173
.meta()
@@ -79,7 +81,7 @@ pub async fn build_discovery_configmaps(
7981
hive,
8082
resolved_product_image,
8183
chroot,
82-
listener_hosts(listeners, HIVE_PORT_NAME)?,
84+
listener_refs,
8385
)?];
8486

8587
Ok(discovery_configmaps)
@@ -95,67 +97,56 @@ fn build_discovery_configmap(
9597
hive: &v1alpha1::HiveCluster,
9698
resolved_product_image: &ResolvedProductImage,
9799
chroot: Option<&str>,
98-
hosts: impl IntoIterator<Item = (impl Into<String>, u16)>,
100+
listener_refs: BTreeMap<&String, Listener>,
99101
) -> Result<ConfigMap, Error> {
100-
let mut conn_str = hosts
101-
.into_iter()
102-
.map(|(host, port)| format!("thrift://{}:{}", host.into(), port))
103-
.collect::<Vec<_>>()
104-
.join("\n");
105-
if let Some(chroot) = chroot {
106-
if !chroot.starts_with('/') {
107-
return RelativeChrootSnafu { chroot }.fail();
102+
let mut discovery_configmap = ConfigMapBuilder::new();
103+
104+
discovery_configmap.metadata(
105+
ObjectMetaBuilder::new()
106+
.name_and_namespace(hive)
107+
.name(name)
108+
.ownerreference_from_resource(owner, None, Some(true))
109+
.with_context(|_| ObjectMissingMetadataForOwnerRefSnafu {
110+
hive: ObjectRef::from_obj(hive),
111+
})?
112+
.with_recommended_labels(build_recommended_labels(
113+
hive,
114+
&resolved_product_image.app_version_label,
115+
&HiveRole::MetaStore.to_string(),
116+
"discovery",
117+
))
118+
.context(MetadataBuildSnafu)?
119+
.build(),
120+
);
121+
122+
for (rolegroup, listener_ref) in listener_refs {
123+
let listener_address = listener_ref
124+
.status
125+
.and_then(|s| s.ingress_addresses?.into_iter().next())
126+
.context(RoleGroupListenerHasNoAddressSnafu { rolegroup })?;
127+
let mut conn_str = format!(
128+
"thrift://{}:{}",
129+
listener_address.address,
130+
listener_address
131+
.ports
132+
.get(HIVE_PORT_NAME)
133+
.copied()
134+
.context(NoServicePortSnafu {
135+
port_name: HIVE_PORT_NAME
136+
})?
137+
);
138+
if let Some(chroot) = chroot {
139+
if !chroot.starts_with('/') {
140+
return RelativeChrootSnafu { chroot }.fail();
141+
}
142+
conn_str.push_str(chroot);
108143
}
109-
conn_str.push_str(chroot);
144+
discovery_configmap.add_data(rolegroup, conn_str);
110145
}
111-
ConfigMapBuilder::new()
112-
.metadata(
113-
ObjectMetaBuilder::new()
114-
.name_and_namespace(hive)
115-
.name(name)
116-
.ownerreference_from_resource(owner, None, Some(true))
117-
.with_context(|_| ObjectMissingMetadataForOwnerRefSnafu {
118-
hive: ObjectRef::from_obj(hive),
119-
})?
120-
.with_recommended_labels(build_recommended_labels(
121-
hive,
122-
&resolved_product_image.app_version_label,
123-
&HiveRole::MetaStore.to_string(),
124-
"discovery",
125-
))
126-
.context(MetadataBuildSnafu)?
127-
.build(),
128-
)
129-
.add_data("HIVE", conn_str)
146+
147+
discovery_configmap
130148
.build()
131149
.with_context(|_| DiscoveryConfigMapSnafu {
132150
obj_ref: ObjectRef::from_obj(hive),
133151
})
134152
}
135-
136-
fn listener_hosts(
137-
listeners: &[listener::v1alpha1::Listener],
138-
port_name: &str,
139-
) -> Result<impl IntoIterator<Item = (String, u16)>, Error> {
140-
listeners
141-
.iter()
142-
.flat_map(|listener| {
143-
listener
144-
.status
145-
.as_ref()
146-
.and_then(|s| s.ingress_addresses.as_deref())
147-
})
148-
.flatten()
149-
.map(|addr| {
150-
Ok((
151-
addr.address.clone(),
152-
addr.ports
153-
.get(port_name)
154-
.copied()
155-
.context(NoServicePortSnafu { port_name })?
156-
.try_into()
157-
.context(InvalidNodePortSnafu)?,
158-
))
159-
})
160-
.collect::<Result<Vec<_>, _>>()
161-
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
## [Unreleased]
6+
7+
### Changed
8+
9+
- Generated kuttl tests prepend the original test name to the generated kuttl test folders ([#1])
10+
11+
[#1]: https://github.com/stackabletech/expand-tests/pull/1
12+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Role Name
2+
=========
3+
4+
A brief description of the role goes here.
5+
6+
Requirements
7+
------------
8+
9+
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
10+
11+
Role Variables
12+
--------------
13+
14+
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
15+
16+
Dependencies
17+
------------
18+
19+
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
20+
21+
Example Playbook
22+
----------------
23+
24+
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
25+
26+
- hosts: servers
27+
roles:
28+
- { role: username.rolename, x: 42 }
29+
30+
License
31+
-------
32+
33+
BSD
34+
35+
Author Information
36+
------------------
37+
38+
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
work_dir: "../test-work"
3+
test_dir: "../tests"
4+
template_dir: "{{ test_dir }}/templates"
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
from argparse import Namespace
5+
from dataclasses import dataclass, field, asdict
6+
import itertools
7+
import logging
8+
import sys
9+
from typing import Dict
10+
import yaml
11+
from itertools import chain
12+
13+
14+
@dataclass
15+
class TestCase:
16+
testcase: str
17+
values: Dict[str, str]
18+
name: str = field(init=False)
19+
20+
def __post_init__(self):
21+
self.name = "_".join(
22+
chain(
23+
[self.testcase],
24+
["-".join([x, self.values.get(x)]) for x in self.values.keys()],
25+
)
26+
)
27+
28+
29+
def check_args() -> Namespace:
30+
parser = argparse.ArgumentParser(
31+
description="This tool is used by the Stackable integration tests to create the final matrix of tests to run "
32+
"based on test dimensions defined for the test suite it is running in."
33+
)
34+
parser.add_argument(
35+
"--input",
36+
"-i",
37+
required=True,
38+
help="The input file (yaml format) which specifies the dimensions and their values",
39+
)
40+
parser.add_argument(
41+
"--output",
42+
"-o",
43+
required=True,
44+
help="A yaml file to be read by Ansible which contains the test cases",
45+
)
46+
parser.add_argument(
47+
"--debug",
48+
"-d",
49+
action="store_true",
50+
required=False,
51+
help="Will print additional debug statements (e.g. output from all run commands)",
52+
)
53+
args = parser.parse_args()
54+
55+
log_level = "DEBUG" if args.debug else "INFO"
56+
logging.basicConfig(
57+
level=log_level,
58+
format="%(asctime)s %(levelname)s: %(message)s",
59+
stream=sys.stdout,
60+
)
61+
return args
62+
63+
64+
def main() -> int:
65+
args = check_args()
66+
result = []
67+
with open(args.input, encoding="utf8") as stream:
68+
input_dimensions = yaml.safe_load(stream)
69+
70+
for test_case in input_dimensions["tests"]:
71+
dimensions = test_case["dimensions"]
72+
used_dimensions = [
73+
v for v in input_dimensions["dimensions"] if v["name"] in dimensions
74+
]
75+
tmp = []
76+
for value in used_dimensions:
77+
tmp.append([(value["name"], x) for x in value["values"]])
78+
79+
for materialized_case in itertools.product(*tmp):
80+
result.append(
81+
TestCase(testcase=test_case["name"], values=dict(materialized_case))
82+
)
83+
84+
with open(args.output, "w", encoding="utf8") as outstream:
85+
outputstruct = {"tests": [asdict(r) for r in result]}
86+
logging.debug(f"Got the following output: {outputstruct}")
87+
yaml.dump(outputstruct, outstream)
88+
89+
90+
if __name__ == "__main__":
91+
sys.exit(main())
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
# handlers file for expand-tests
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
install_date: Wed Jul 13 08:18:32 2022
2+
version: ''

0 commit comments

Comments
 (0)