Skip to content

Commit

Permalink
Merge pull request #5860: Add pants-plugins/sample_conf to streamli…
Browse files Browse the repository at this point in the history
…ne regenerating `conf/st2.conf.sample`
  • Loading branch information
cognifloyd authored Jan 10, 2023
2 parents efa1b87 + a54e76e commit 5373574
Show file tree
Hide file tree
Showing 13 changed files with 387 additions and 3 deletions.
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

0 comments on commit 5373574

Please sign in to comment.