Skip to content

Commit fefe7e5

Browse files
authored
pants-plugins/uses_services: add support for uses=st2cluster in BUILD metadata (#6279)
2 parents f39c755 + b0767b9 commit fefe7e5

File tree

8 files changed

+374
-4
lines changed

8 files changed

+374
-4
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Added
6969
working on StackStorm, improve our security posture, and improve CI reliability thanks in part
7070
to pants' use of PEX lockfiles. This is not a user-facing addition.
7171
#6118 #6141 #6133 #6120 #6181 #6183 #6200 #6237 #6229 #6240 #6241 #6244 #6251 #6253
72-
#6254 #6258 #6259 #6260 #6269 #6275
72+
#6254 #6258 #6259 #6260 #6269 #6275 #6279
7373
Contributed by @cognifloyd
7474
* Build of ST2 EL9 packages #6153
7575
Contributed by @amanda11

contrib/runners/orquesta_runner/tests/integration/BUILD

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ __defaults__(
55

66
python_tests(
77
name="tests",
8-
uses=["mongo", "rabbitmq", "redis", "system_user"],
8+
uses=["mongo", "rabbitmq", "redis", "st2cluster", "system_user"],
9+
tags=["integration", "st2cluster"],
910
)

pants-plugins/uses_services/register.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
platform_rules,
2222
rabbitmq_rules,
2323
redis_rules,
24+
st2cluster_rules,
2425
system_user_rules,
2526
)
2627
from uses_services.target_types import UsesServicesField
@@ -36,5 +37,6 @@ def rules():
3637
*mongo_rules.rules(),
3738
*rabbitmq_rules.rules(),
3839
*redis_rules.rules(),
40+
*st2cluster_rules.rules(),
3941
*system_user_rules.rules(),
4042
]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2024 The StackStorm Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from __future__ import annotations
15+
16+
import socket
17+
import sys
18+
19+
from contextlib import closing
20+
21+
22+
def _is_st2cluster_running(endpoints: list[tuple[str, str]]) -> bool:
23+
"""Check for listening ports of st2auth, st2api, and st2stream services.
24+
25+
This should not import the st2 code as it should be self-contained.
26+
"""
27+
# TODO: Once each service gains a reliable health check endpoint, use that.
28+
# https://github.com/StackStorm/st2/issues/4020
29+
for host, port in endpoints:
30+
# based on https://stackoverflow.com/a/35370008/1134951
31+
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
32+
# errno=0 means the connection succeeded
33+
if sock.connect_ex((host, int(port))) != 0:
34+
# failed to create a connection to the port.
35+
return False
36+
return True
37+
38+
39+
if __name__ == "__main__":
40+
args_iter = iter(sys.argv[1:])
41+
# Turn the list into 2 tuples (zip with query the same iterator twice for each entry)
42+
endpoints = list(zip(args_iter, args_iter))
43+
if not endpoints:
44+
endpoints = [
45+
("127.0.0.1", "9100"),
46+
("127.0.0.1", "9101"),
47+
("127.0.0.1", "9102"),
48+
]
49+
50+
is_running = _is_st2cluster_running(endpoints)
51+
exit_code = 0 if is_running else 1
52+
sys.exit(exit_code)
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# Copyright 2024 The StackStorm Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from __future__ import annotations
15+
16+
from dataclasses import dataclass
17+
from textwrap import dedent
18+
19+
from pants.backend.python.goals.pytest_runner import (
20+
PytestPluginSetupRequest,
21+
PytestPluginSetup,
22+
)
23+
from pants.backend.python.target_types import Executable
24+
from pants.backend.python.util_rules.pex import (
25+
PexRequest,
26+
VenvPex,
27+
VenvPexProcess,
28+
rules as pex_rules,
29+
)
30+
from pants.engine.fs import CreateDigest, Digest, FileContent
31+
from pants.engine.rules import collect_rules, Get, rule
32+
from pants.engine.process import FallibleProcessResult, ProcessCacheScope
33+
from pants.engine.target import Target
34+
from pants.engine.unions import UnionRule
35+
from pants.util.logging import LogLevel
36+
37+
from uses_services.exceptions import ServiceMissingError
38+
from uses_services.platform_rules import Platform
39+
from uses_services.scripts.is_st2cluster_running import (
40+
__file__ as is_st2cluster_running_full_path,
41+
)
42+
from uses_services.target_types import UsesServicesField
43+
44+
45+
@dataclass(frozen=True)
46+
class UsesSt2ClusterRequest:
47+
"""One or more targets need a running st2 cluster with all st2* services."""
48+
49+
auth_host: str = "127.0.0.1"
50+
auth_port: int = 9100
51+
api_host: str = "127.0.0.1"
52+
api_port: int = 9101
53+
stream_host: str = "127.0.0.1"
54+
stream_port: int = 9102
55+
56+
@property
57+
def endpoints(self) -> tuple[tuple[str, str], ...]:
58+
return (
59+
(self.auth_host, str(self.auth_port)),
60+
(self.api_host, str(self.api_port)),
61+
(self.stream_host, str(self.stream_port)),
62+
)
63+
64+
# @classmethod
65+
# def from_env(cls, env: EnvironmentVars) -> UsesSt2ClusterRequest:
66+
# return cls()
67+
# TODO: consider adding a from_env method using one or both of client => server vars:
68+
# ST2_CONFIG_FILE => ST2_CONFIG_PATH (used by many tests, so not safe) or
69+
# ST2_CONF (only in launchdev.sh and st2ctl)
70+
# ST2_BASE_URL => ST2_WEBUI__WEBUI_BASE_URL
71+
# ST2_API_URL => ST2_AUTH__API_URL or
72+
# http{'s' if ST2_API__USE_SSL else ''}://{ST2_API__HOST}:{ST2_API__PORT}
73+
# ST2_AUTH_URL => http://{ST2_AUTH__HOST}:{ST2_AUTH__PORT}
74+
# ST2_STREAM_URL => http://{ST2_STREAM__HOST}:{ST2_STREAM__PORT}
75+
# ST2_CACERT (might be needed if using a self-signed cert) => n/a
76+
# These st2client env vars are irrelevant for the connectivity check:
77+
# ST2_AUTH_TOKEN or ST2_API_KEY
78+
# ST2_API_VERSION (always "v1" since we don't have anything else)
79+
80+
81+
@dataclass(frozen=True)
82+
class St2ClusterIsRunning:
83+
pass
84+
85+
86+
class PytestUsesSt2ClusterRequest(PytestPluginSetupRequest):
87+
@classmethod
88+
def is_applicable(cls, target: Target) -> bool:
89+
if not target.has_field(UsesServicesField):
90+
return False
91+
uses = target.get(UsesServicesField).value
92+
return uses is not None and "st2cluster" in uses
93+
94+
95+
@rule(
96+
desc="Ensure ST2 Cluster is running and accessible before running tests.",
97+
level=LogLevel.DEBUG,
98+
)
99+
async def st2cluster_is_running_for_pytest(
100+
request: PytestUsesSt2ClusterRequest,
101+
) -> PytestPluginSetup:
102+
# this will raise an error if st2cluster is not running
103+
_ = await Get(St2ClusterIsRunning, UsesSt2ClusterRequest())
104+
105+
return PytestPluginSetup()
106+
107+
108+
@rule(
109+
desc="Test to see if ST2 Cluster is running and accessible.",
110+
level=LogLevel.DEBUG,
111+
)
112+
async def st2cluster_is_running(
113+
request: UsesSt2ClusterRequest, platform: Platform
114+
) -> St2ClusterIsRunning:
115+
script_path = "./is_st2cluster_running.py"
116+
117+
# pants is already watching this directory as it is under a source root.
118+
# So, we don't need to double watch with PathGlobs, just open it.
119+
with open(is_st2cluster_running_full_path, "rb") as script_file:
120+
script_contents = script_file.read()
121+
122+
script_digest = await Get(
123+
Digest, CreateDigest([FileContent(script_path, script_contents)])
124+
)
125+
script_pex = await Get(
126+
VenvPex,
127+
PexRequest(
128+
output_filename="script.pex",
129+
internal_only=True,
130+
sources=script_digest,
131+
main=Executable(script_path),
132+
),
133+
)
134+
135+
result = await Get(
136+
FallibleProcessResult,
137+
VenvPexProcess(
138+
script_pex,
139+
argv=[
140+
host_or_port
141+
for endpoint in request.endpoints
142+
for host_or_port in endpoint
143+
],
144+
input_digest=script_digest,
145+
description="Checking to see if ST2 Cluster is up and accessible.",
146+
# this can change from run to run, so don't cache results.
147+
cache_scope=ProcessCacheScope.PER_SESSION,
148+
level=LogLevel.DEBUG,
149+
),
150+
)
151+
is_running = result.exit_code == 0
152+
153+
if is_running:
154+
return St2ClusterIsRunning()
155+
156+
# st2cluster is not running, so raise an error with instructions.
157+
instructions = dedent(
158+
"""\
159+
A full StackStorm cluster is required to run some integration tests.
160+
To start the dev StackStorm cluster, run this from the repo root
161+
(probably in a new terminal/window, as the output is quite verbose):
162+
163+
tools/launchdev.sh start -x
164+
165+
This runs each StackStorm microservice in a tmux session. You can
166+
inspect the logs for this service in the `logs/` directory.
167+
168+
If tmux is not installed, please install it with a package manager,
169+
or use vagrant for local development with something like:
170+
171+
vagrant init stackstorm/st2
172+
vagrant up
173+
vagrant ssh
174+
175+
Please see: https://docs.stackstorm.com/install/vagrant.html
176+
"""
177+
)
178+
raise ServiceMissingError(
179+
service="st2cluster",
180+
platform=platform,
181+
instructions=instructions,
182+
msg=f"The dev StackStorm cluster seems to be down.\n{instructions}",
183+
)
184+
185+
186+
def rules():
187+
return [
188+
*collect_rules(),
189+
UnionRule(PytestPluginSetupRequest, PytestUsesSt2ClusterRequest),
190+
*pex_rules(),
191+
]

0 commit comments

Comments
 (0)