Skip to content

Commit 05ba7b1

Browse files
committed
MINIFICPP-2683 Move C2 docker tests to modular docker tests
1 parent 544f99c commit 05ba7b1

File tree

13 files changed

+164
-227
lines changed

13 files changed

+164
-227
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
import jks
19+
import os
20+
from OpenSSL import crypto
21+
from cryptography.hazmat.primitives.serialization import pkcs12, BestAvailableEncryption, load_pem_private_key
22+
from cryptography import x509
23+
from pathlib import Path
24+
25+
from minifi_test_framework.containers.container import Container
26+
from minifi_test_framework.core.helpers import wait_for_condition
27+
from minifi_test_framework.core.minifi_test_context import MinifiTestContext
28+
from minifi_test_framework.core.ssl_utils import make_server_cert
29+
from minifi_test_framework.containers.file import File
30+
from minifi_test_framework.containers.host_file import HostFile
31+
32+
33+
class MinifiC2Server(Container):
34+
def __init__(self, test_context: MinifiTestContext, ssl: bool = False):
35+
super().__init__("apache/nifi-minifi-c2:1.27.0", f"minifi-c2-server-{test_context.scenario_id}", test_context.network)
36+
if ssl:
37+
c2_cert, c2_key = make_server_cert(f"minifi-c2-server-{test_context.scenario_id}", test_context.root_ca_cert, test_context.root_ca_key)
38+
pke = jks.PrivateKeyEntry.new('c2-server-cert', [crypto.dump_certificate(crypto.FILETYPE_ASN1, c2_cert)], crypto.dump_privatekey(crypto.FILETYPE_ASN1, c2_key), 'rsa_raw')
39+
server_keystore = jks.KeyStore.new('jks', [pke])
40+
server_keystore_content = server_keystore.saves('abcdefgh')
41+
self.files.append(File("/opt/minifi-c2/minifi-c2-current/certs/minifi-c2-server-keystore.jks", server_keystore_content, permissions=0o644))
42+
43+
private_key_pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, test_context.root_ca_key)
44+
private_key = load_pem_private_key(private_key_pem, password=None)
45+
certificate_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, test_context.root_ca_cert)
46+
certificate = x509.load_pem_x509_certificate(certificate_pem)
47+
pkcs12_data = pkcs12.serialize_key_and_certificates(
48+
name=None,
49+
key=private_key,
50+
cert=certificate,
51+
cas=None,
52+
encryption_algorithm=BestAvailableEncryption(b'abcdefgh')
53+
)
54+
self.files.append(File("/opt/minifi-c2/minifi-c2-current/certs/minifi-c2-server-truststore.p12", pkcs12_data, permissions=0o644))
55+
56+
authorities_file_content = """
57+
CN=minifi-primary-{scenario_id}:
58+
- CLASS_MINIFI_CPP
59+
""".format(scenario_id=test_context.scenario_id)
60+
self.files.append(File("/opt/minifi-c2/minifi-c2-current/conf/authorities.yaml", authorities_file_content, permissions=0o644))
61+
62+
resource_dir = Path(__file__).resolve().parent / "resources" / "minifi-c2-server"
63+
self.host_files.append(HostFile("/opt/minifi-c2/minifi-c2-current/files/minifi-test-class/config.text.yml.v1", os.path.join(resource_dir, "config.yml")))
64+
if ssl:
65+
self.host_files.append(HostFile("/opt/minifi-c2/minifi-c2-current/conf/authorizations.yaml", os.path.join(resource_dir, "authorizations.yaml")))
66+
self.host_files.append(HostFile("/opt/minifi-c2/minifi-c2-current/conf/c2.properties", os.path.join(resource_dir, "c2.properties")))
67+
68+
def deploy(self):
69+
super().deploy()
70+
finished_str = "Server Started"
71+
return wait_for_condition(
72+
condition=lambda: finished_str in self.get_logs(),
73+
timeout_seconds=60,
74+
bail_condition=lambda: self.exited,
75+
context=None
76+
)

behave_framework/src/minifi_test_framework/containers/minifi_container.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def __init__(self, container_name: str, test_context: MinifiTestContext):
3535
self.flow_definition = MinifiFlowDefinition()
3636
self.properties: dict[str, str] = {}
3737
self.log_properties: dict[str, str] = {}
38+
self.scenario_id = test_context.scenario_id
3839

3940
minifi_client_cert, minifi_client_key = make_cert_without_extended_usage(common_name=self.container_name, ca_cert=test_context.root_ca_cert, ca_key=test_context.root_ca_key)
4041
self.files.append(File("/usr/local/share/certs/ca-root-nss.crt", crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)))
@@ -96,6 +97,35 @@ def set_log_property(self, key: str, value: str):
9697
def enable_openssl_fips_mode(self):
9798
self.properties["nifi.openssl.fips.support.enable"] = "true"
9899

100+
def enable_c2(self):
101+
self.properties["nifi.c2.enable"] = "true"
102+
self.properties["nifi.c2.rest.url"] = f"http://minifi-c2-server-{self.scenario_id}:10090/c2/config/heartbeat"
103+
self.properties["nifi.c2.rest.url.ack"] = f"http://minifi-c2-server-{self.scenario_id}:10090/c2/config/acknowledge"
104+
self.properties["nifi.c2.flow.base.url"] = f"http://minifi-c2-server-{self.scenario_id}:10090/c2/config/"
105+
self.properties["nifi.c2.root.classes"] = "DeviceInfoNode,AgentInformation,FlowInformation,AssetInformation"
106+
self.properties["nifi.c2.full.heartbeat"] = "false"
107+
self.properties["nifi.c2.agent.class"] = "minifi-test-class"
108+
self.properties["nifi.c2.agent.identifier"] = "minifi-test-id"
109+
110+
def enable_c2_with_ssl(self):
111+
self.properties["nifi.c2.enable"] = "true"
112+
self.properties["nifi.c2.rest.url"] = f"https://minifi-c2-server-{self.scenario_id}:10090/c2/config/heartbeat"
113+
self.properties["nifi.c2.rest.url.ack"] = f"https://minifi-c2-server-{self.scenario_id}:10090/c2/config/acknowledge"
114+
self.properties["nifi.c2.flow.base.url"] = f"https://minifi-c2-server-{self.scenario_id}:10090/c2/config/"
115+
self.properties["nifi.c2.root.classes"] = "DeviceInfoNode,AgentInformation,FlowInformation,AssetInformation"
116+
self.properties["nifi.c2.full.heartbeat"] = "false"
117+
self.properties["nifi.c2.agent.class"] = "minifi-test-class"
118+
self.properties["nifi.c2.agent.identifier"] = "minifi-test-id"
119+
120+
def fetch_flow_config_from_flow_url(self):
121+
self.properties["nifi.c2.flow.url"] = f"http://minifi-c2-server-{self.scenario_id}:10090/c2/config?class=minifi-test-class"
122+
123+
def set_up_ssl_proprties(self):
124+
self.properties["nifi.remote.input.secure"] = "true"
125+
self.properties["nifi.security.client.certificate"] = "/tmp/resources/minifi_client.crt"
126+
self.properties["nifi.security.client.private.key"] = "/tmp/resources/minifi_client.key"
127+
self.properties["nifi.security.client.ca.certificate"] = "/tmp/resources/root_ca.crt"
128+
99129
def _fill_default_properties(self):
100130
if self.is_fhs:
101131
self.properties["nifi.flow.configuration.file"] = "/etc/nifi-minifi-cpp/config.yml"

docker/test/integration/resources/minifi-c2-server/authorizations.yaml renamed to behave_framework/src/minifi_test_framework/containers/resources/minifi-c2-server/authorizations.yaml

File renamed without changes.

docker/test/integration/resources/minifi-c2-server/c2.properties renamed to behave_framework/src/minifi_test_framework/containers/resources/minifi-c2-server/c2.properties

File renamed without changes.

docker/test/integration/resources/minifi-c2-server/config.yml renamed to behave_framework/src/minifi_test_framework/containers/resources/minifi-c2-server/config.yml

File renamed without changes.

behave_framework/src/minifi_test_framework/steps/checking_steps.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,15 @@ def step_impl(context: MinifiTestContext, content: str, directory: str, duration
196196

197197

198198
@then('after a wait of {duration}, at least {lower_bound:d} and at most {upper_bound:d} files are produced and placed in the "{directory}" directory')
199-
def step_impl(context, lower_bound, upper_bound, duration, directory):
199+
def step_impl(context: MinifiTestContext, lower_bound: int, upper_bound: int, duration: str, directory: str):
200200
duration_seconds = humanfriendly.parse_timespan(duration)
201201
assert check_condition_after_wait(condition=lambda: context.get_default_minifi_container().get_number_of_files(directory) >= lower_bound
202202
and context.get_default_minifi_container().get_number_of_files(directory) <= upper_bound,
203203
context=context, wait_time=duration_seconds)
204204

205205

206206
@then('exactly these files are in the "{directory}" directory in less than {duration}: "{contents}"')
207-
def step_impl(context, directory, duration, contents):
207+
def step_impl(context: MinifiTestContext, directory: str, duration: str, contents: str):
208208
if not contents:
209209
context.execute_steps(f'then no files are placed in the "{directory}" directory in {duration} of running time')
210210
return
@@ -221,15 +221,15 @@ def step_impl(context, directory, duration):
221221

222222

223223
@then("at least one empty file is placed in the \"{directory}\" directory in less than {duration}")
224-
def step_impl(context, directory, duration):
224+
def step_impl(context: MinifiTestContext, directory: str, duration: str):
225225
timeout_in_seconds = humanfriendly.parse_timespan(duration)
226226
assert wait_for_condition(
227227
condition=lambda: context.get_default_minifi_container().directory_contains_empty_file(directory),
228228
timeout_seconds=timeout_in_seconds, bail_condition=lambda: context.get_default_minifi_container().exited, context=context)
229229

230230

231231
@then("in the \"{container_name}\" container at least one empty file is placed in the \"{directory}\" directory in less than {duration}")
232-
def step_impl(context, container_name, directory, duration):
232+
def step_impl(context: MinifiTestContext, container_name: str, directory: str, duration: str):
233233
timeout_in_seconds = humanfriendly.parse_timespan(duration)
234234
if container_name == "nifi":
235235
assert wait_for_condition(
@@ -242,7 +242,7 @@ def step_impl(context, container_name, directory, duration):
242242

243243

244244
@then("in the \"{container_name}\" container at least one file with minimum size of \"{size}\" is placed in the \"{directory}\" directory in less than {duration}")
245-
def step_impl(context, container_name: str, directory: str, size: str, duration: str):
245+
def step_impl(context: MinifiTestContext, container_name: str, directory: str, size: str, duration: str):
246246
timeout_in_seconds = humanfriendly.parse_timespan(duration)
247247
size_in_bytes = humanfriendly.parse_size(size)
248248
assert wait_for_condition(
@@ -251,6 +251,14 @@ def step_impl(context, container_name: str, directory: str, size: str, duration:
251251

252252

253253
@then("at least one file with minimum size of \"{size}\" is placed in the \"{directory}\" directory in less than {duration}")
254-
def step_impl(context, directory: str, size: str, duration: str):
254+
def step_impl(context: MinifiTestContext, directory: str, size: str, duration: str):
255255
context.execute_steps(
256256
f'Then in the "{DEFAULT_MINIFI_CONTAINER_NAME}" container at least one file with minimum size of "{size}" is placed in the "{directory}" directory in less than {duration}')
257+
258+
259+
@then("the MiNiFi C2 server logs contain the following message: \"{log_message}\" in less than {duration}")
260+
def step_impl(context: MinifiTestContext, log_message: str, duration: str):
261+
duration_seconds = humanfriendly.parse_timespan(duration)
262+
assert wait_for_condition(condition=lambda: log_message in context.get_minifi_container("minifi-c2-server").get_logs(),
263+
timeout_seconds=duration_seconds, bail_condition=lambda: context.get_minifi_container("minifi-c2-server").exited,
264+
context=context)

behave_framework/src/minifi_test_framework/steps/core_steps.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from minifi_test_framework.containers.directory import Directory
3131
from minifi_test_framework.containers.file import File
3232
from minifi_test_framework.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
33+
from minifi_test_framework.containers.minifi_c2_server_container import MinifiC2Server
3334

3435

3536
@when("both instances start up")
@@ -100,15 +101,47 @@ def step_impl(context: MinifiTestContext):
100101

101102

102103
@given("OpenSSL FIPS mode is enabled in MiNiFi")
103-
def step_impl(context):
104+
def step_impl(context: MinifiTestContext):
104105
context.get_or_create_default_minifi_container().enable_openssl_fips_mode()
105106

106107

107108
@step("the http proxy server is set up")
108-
def step_impl(context):
109+
def step_impl(context: MinifiTestContext):
109110
context.containers["http-proxy"] = HttpProxy(context)
110111

111112

112113
@step("a NiFi container is set up")
113-
def step_impl(context):
114+
def step_impl(context: MinifiTestContext):
114115
context.containers["nifi"] = NifiContainer(context)
116+
117+
118+
@given("C2 is enabled in MiNiFi")
119+
def step_impl(context: MinifiTestContext):
120+
context.get_or_create_default_minifi_container().enable_c2()
121+
122+
123+
@given("flow configuration path is set up in flow url property")
124+
def step_impl(context: MinifiTestContext):
125+
context.get_or_create_default_minifi_container().fetch_flow_config_from_flow_url()
126+
127+
128+
@given("ssl properties are set up for MiNiFi C2 server")
129+
def step_impl(context: MinifiTestContext):
130+
context.get_or_create_default_minifi_container().enable_c2_with_ssl()
131+
context.get_or_create_default_minifi_container().set_up_ssl_proprties()
132+
133+
134+
@given("a MiNiFi C2 server is set up")
135+
def step_impl(context: MinifiTestContext):
136+
context.containers["minifi-c2-server"] = MinifiC2Server(context)
137+
138+
139+
@given("a MiNiFi C2 server is set up with SSL")
140+
def step_impl(context: MinifiTestContext):
141+
context.containers["minifi-c2-server"] = MinifiC2Server(context, ssl=True)
142+
143+
144+
@given("a MiNiFi C2 server is started")
145+
def step_impl(context: MinifiTestContext):
146+
context.containers["minifi-c2-server"] = MinifiC2Server(context)
147+
assert context.containers["minifi-c2-server"].deploy()

docker/test/integration/cluster/ContainerStore.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
from .containers.SyslogTcpClientContainer import SyslogTcpClientContainer
2727
from .containers.MinifiAsPodInKubernetesCluster import MinifiAsPodInKubernetesCluster
2828
from .containers.PrometheusContainer import PrometheusContainer
29-
from .containers.MinifiC2ServerContainer import MinifiC2ServerContainer
3029
from .FeatureContext import FeatureContext
3130

3231

@@ -172,24 +171,6 @@ def acquire_container(self, context, container_name: str, engine='minifi-cpp', c
172171
image_store=self.image_store,
173172
command=command,
174173
ssl=True))
175-
elif engine == "minifi-c2-server":
176-
return self.containers.setdefault(container_name,
177-
MinifiC2ServerContainer(feature_context=feature_context,
178-
name=container_name,
179-
vols=self.vols,
180-
network=self.network,
181-
image_store=self.image_store,
182-
command=command,
183-
ssl=False))
184-
elif engine == "minifi-c2-server-ssl":
185-
return self.containers.setdefault(container_name,
186-
MinifiC2ServerContainer(feature_context=feature_context,
187-
name=container_name,
188-
vols=self.vols,
189-
network=self.network,
190-
image_store=self.image_store,
191-
command=command,
192-
ssl=True))
193174
else:
194175
raise Exception('invalid flow engine: \'%s\'' % engine)
195176

@@ -231,15 +212,6 @@ def restart_container(self, container_name):
231212
def enable_provenance_repository_in_minifi(self):
232213
self.minifi_options.enable_provenance = True
233214

234-
def enable_c2_in_minifi(self):
235-
self.minifi_options.enable_c2 = True
236-
237-
def enable_c2_with_ssl_in_minifi(self):
238-
self.minifi_options.enable_c2_with_ssl = True
239-
240-
def fetch_flow_config_from_c2_url_in_minifi(self):
241-
self.minifi_options.use_flow_config_from_url = True
242-
243215
def set_ssl_context_properties_in_minifi(self):
244216
self.minifi_options.set_ssl_context_properties = True
245217

0 commit comments

Comments
 (0)