Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pants-plugins/sample_conf to streamline regenerating conf/st2.conf.sample #5860

Merged
merged 16 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,23 @@ python_requirements(
"//:reqs#zake",
]
},
# make sure anything that uses st2-auth-ldap gets the st2auth constant
"st2-auth-ldap": {
"dependencies": [
"st2auth/st2auth/backends/constants.py",
]
},
},
)

target(
name="auth_backends",
dependencies=[
"//:reqs#st2-auth-backend-flat-file",
"//:reqs#st2-auth-ldap",
],
)

python_test_utils(
name="test_utils",
skip_pylint=True,
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Added
working on StackStorm, improve our security posture, and improve CI reliability thanks in part
to pants' use of PEX lockfiles. This is not a user-facing addition.
#5778 #5789 #5817 #5795 #5830 #5833 #5834 #5841 #5840 #5838 #5842 #5837 #5849 #5850
#5846 #5853 #5848 #5847 #5858 #5857
#5846 #5853 #5848 #5847 #5858 #5857 #5860
Contributed by @cognifloyd

* Added a joint index to solve the problem of slow mongo queries for scheduled executions. #5805
Expand Down
5 changes: 4 additions & 1 deletion conf/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ file(
source="st2rc.sample.ini",
)

file(
sample_conf( # defined in pants-plugins/sample_conf
name="st2.conf.sample",
source="st2.conf.sample",
dependencies=[
"tools/config_gen.py",
],
)

file(
Expand Down
10 changes: 10 additions & 0 deletions pants-plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ To see available goals, do "./pants help goals" and "./pants help $goal".

These StackStorm-specific plugins are probably only useful for the st2 repo.
- `api_spec`
- `sample_conf`
- `schemas`

### `api_spec` plugin
Expand All @@ -25,6 +26,15 @@ This plugin also wires up pants so that the `lint` goal runs additional
api spec validation on `st2common/st2common/openapi.yaml` with something
like `./pants lint st2common/st2common/openapi.yaml`.

### `sample_conf` plugin

This plugin wires up pants to make sure `conf/st2.conf.sample` gets
regenerated whenever the source files change. Now, whenever someone runs
the `fmt` goal (eg `./pants fmt conf/st2.conf.sample`), the sample will
be regenerated if any of the files used to generate it have changed.
Also, running the `lint` goal will fail if the sample needs to be
regenerated.

### `schemas` plugin

This plugin wires up pants to make sure `contrib/schemas/*.json` gets
Expand Down
5 changes: 5 additions & 0 deletions pants-plugins/sample_conf/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
python_sources()

python_tests(
name="tests",
)
Empty file.
24 changes: 24 additions & 0 deletions pants-plugins/sample_conf/register.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2023 The StackStorm Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from sample_conf.rules import rules as sample_conf_rules
from sample_conf.target_types import SampleConf


def rules():
return [*sample_conf_rules()]


def target_types():
return [SampleConf]
113 changes: 113 additions & 0 deletions pants-plugins/sample_conf/rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright 2023 The StackStorm Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from dataclasses import dataclass

from pants.backend.python.target_types import EntryPoint
from pants.backend.python.util_rules import pex, pex_from_targets
from pants.backend.python.util_rules.pex import (
VenvPex,
VenvPexProcess,
)
from pants.backend.python.util_rules.pex_from_targets import PexFromTargetsRequest
from pants.core.goals.fmt import FmtResult, FmtTargetsRequest
from pants.engine.addresses import Address
from pants.engine.fs import (
CreateDigest,
Digest,
FileContent,
Snapshot,
)
from pants.engine.process import FallibleProcessResult
from pants.engine.rules import Get, collect_rules, rule
from pants.engine.target import FieldSet
from pants.engine.unions import UnionRule
from pants.util.logging import LogLevel

from sample_conf.target_types import SampleConfSourceField


# these constants are also used in the tests.
SCRIPT_DIR = "tools"
SCRIPT = "config_gen"


@dataclass(frozen=True)
class GenerateSampleConfFieldSet(FieldSet):
required_fields = (SampleConfSourceField,)

source: SampleConfSourceField


class GenerateSampleConfViaFmtTargetsRequest(FmtTargetsRequest):
field_set_type = GenerateSampleConfFieldSet
name = SCRIPT


@rule(
desc=f"Update conf/st2.conf.sample with {SCRIPT_DIR}/{SCRIPT}.py",
level=LogLevel.DEBUG,
)
async def generate_sample_conf_via_fmt(
request: GenerateSampleConfViaFmtTargetsRequest,
) -> FmtResult:
# There will only be one target+field_set, but we iterate
# to satisfy how fmt expects that there could be more than one.
# If there is more than one, they will all get the same contents.

# actually generate it with an external script.
# Generation cannot be inlined here because it needs to import the st2 code.
pex = await Get(
VenvPex,
PexFromTargetsRequest(
[
Address(
SCRIPT_DIR,
target_name=SCRIPT_DIR,
relative_file_path=f"{SCRIPT}.py",
)
],
output_filename=f"{SCRIPT}.pex",
internal_only=True,
main=EntryPoint(SCRIPT),
),
)

result = await Get(
FallibleProcessResult,
VenvPexProcess(
pex,
description="Regenerating st2.conf.sample",
),
)

contents = [
FileContent(
f"{field_set.address.spec_path}/{field_set.source.value}",
result.stdout,
)
for field_set in request.field_sets
]

output_digest = await Get(Digest, CreateDigest(contents))
output_snapshot = await Get(Snapshot, Digest, output_digest)
return FmtResult.create(request, result, output_snapshot, strip_chroot_path=True)


def rules():
return [
*collect_rules(),
UnionRule(FmtTargetsRequest, GenerateSampleConfViaFmtTargetsRequest),
*pex.rules(),
*pex_from_targets.rules(),
]
158 changes: 158 additions & 0 deletions pants-plugins/sample_conf/rules_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Copyright 2023 The StackStorm Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations

import pytest

from pants.backend.python import target_types_rules
from pants.backend.python.target_types import PythonSourcesGeneratorTarget

from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
from pants.engine.addresses import Address
from pants.engine.fs import CreateDigest, Digest, FileContent, Snapshot
from pants.engine.target import Target
from pants.core.goals.fmt import FmtResult
from pants.testutil.rule_runner import QueryRule, RuleRunner

from .rules import (
SCRIPT,
SCRIPT_DIR,
GenerateSampleConfFieldSet,
GenerateSampleConfViaFmtTargetsRequest,
rules as sample_conf_rules,
)
from .target_types import SampleConf


@pytest.fixture
def rule_runner() -> RuleRunner:
return RuleRunner(
rules=[
*sample_conf_rules(),
*target_types_rules.rules(),
QueryRule(FmtResult, (GenerateSampleConfViaFmtTargetsRequest,)),
QueryRule(SourceFiles, (SourceFilesRequest,)),
],
target_types=[SampleConf, PythonSourcesGeneratorTarget],
)


def run_st2_generate_sample_conf(
rule_runner: RuleRunner,
targets: list[Target],
*,
extra_args: list[str] | None = None,
) -> FmtResult:
rule_runner.set_options(
[
"--backend-packages=sample_conf",
f"--source-root-patterns=/{SCRIPT_DIR}",
*(extra_args or ()),
],
env_inherit={"PATH", "PYENV_ROOT", "HOME"},
)
field_sets = [GenerateSampleConfFieldSet.create(tgt) for tgt in targets]
input_sources = rule_runner.request(
SourceFiles,
[
SourceFilesRequest(field_set.source for field_set in field_sets),
],
)
fmt_result = rule_runner.request(
FmtResult,
[
GenerateSampleConfViaFmtTargetsRequest(
field_sets, snapshot=input_sources.snapshot
),
],
)
return fmt_result


# copied from pantsbuild/pants.git/src/python/pants/backend/python/lint/black/rules_integration_test.py
def get_snapshot(rule_runner: RuleRunner, source_files: dict[str, str]) -> Snapshot:
files = [
FileContent(path, content.encode()) for path, content in source_files.items()
]
digest = rule_runner.request(Digest, [CreateDigest(files)])
return rule_runner.request(Snapshot, [digest])


# add dummy script at tools/config_gen.py that the test can load.
SCRIPT_PY = """
def main():
sample_conf_text = "{sample_conf_text}"
print(sample_conf_text)


if __name__ == "__main__":
main()
"""


def write_files(
sample_conf_dir: str,
sample_conf_file: str,
before: str,
after: str,
rule_runner: RuleRunner,
) -> None:
files = {
f"{sample_conf_dir}/{sample_conf_file}": before,
f"{sample_conf_dir}/BUILD": f"sample_conf(name='t', source='{sample_conf_file}')",
# add in the target that's hard-coded in the generate_sample_conf_via_fmt rue
f"{SCRIPT_DIR}/{SCRIPT}.py": SCRIPT_PY.format(sample_conf_text=after),
f"{SCRIPT_DIR}/__init__.py": "",
f"{SCRIPT_DIR}/BUILD": "python_sources()",
}

rule_runner.write_files(files)


def test_changed(rule_runner: RuleRunner) -> None:
write_files(
sample_conf_dir="my_dir",
sample_conf_file="dummy.conf",
before="BEFORE",
after="AFTER",
rule_runner=rule_runner,
)

tgt = rule_runner.get_target(
Address("my_dir", target_name="t", relative_file_path="dummy.conf")
)
fmt_result = run_st2_generate_sample_conf(rule_runner, [tgt])
assert fmt_result.output == get_snapshot(
rule_runner, {"my_dir/dummy.conf": "AFTER\n"}
)
assert fmt_result.did_change is True


def test_unchanged(rule_runner: RuleRunner) -> None:
write_files(
sample_conf_dir="my_dir",
sample_conf_file="dummy.conf",
before="AFTER\n",
after="AFTER", # print() adds a newline
rule_runner=rule_runner,
)

tgt = rule_runner.get_target(
Address("my_dir", target_name="t", relative_file_path="dummy.conf")
)
fmt_result = run_st2_generate_sample_conf(rule_runner, [tgt])
assert fmt_result.output == get_snapshot(
rule_runner, {"my_dir/dummy.conf": "AFTER\n"}
)
assert fmt_result.did_change is False
Loading