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/api_spec to streamline regenerating openapi.yaml #5857

Merged
merged 18 commits into from
Jan 10, 2023

Conversation

cognifloyd
Copy link
Member

Background

This is another part of introducing pants, as discussed in various TSC meetings.

Related PRs can be found in:

Overview of this PR

This PR improves the DX (developer experience) by wiring up:

  • the fmt goal to generate st2common/st2common/openapi.yaml if needed (effectively runs st2-generate-api-spec), and
  • the lint goal to validate st2common/st2common/openapi.yaml to make sure the generated schema is valid (effectively runs st2-validate-api-spec)

This includes creating a api_spec plugin for pants that adds a api_spec target.

Developer Experience

Today, if someone changes one of the files/scripts used to generate our openapi spec, then CI might complain as part of the ci-checks Makefile target, with instructions to Please run "make generate-api-spec". This also gets regenerated any time someone runs the lint or tests Makefile targets, which might be surprising (tests generally shouldn't handle codegen).

In any case, this wires up pants to run st2-generate-api-spec and st2-validate-api-spec at times that are analogous to when the Makefile runs them.

With this PR, we add api spec generation into the fmt goal and validation to the lint goal. Anything that runs under fmt also runs under the lint goal, so we will get two signals about the api spec: (1) whether or not it needs to be regenerated, and (2) whether or not there are validation errors in the spec.

Our api spec is generated from a jinja template, which only does minimal interpolation (populating the values for some of the enums). There are actually quite a few inconsistencies between our models and the api spec, so we will have to refactor the spec, the spec generation, and possibly the models in the future. That refactoring work, however, is out-of-scope for this PR. After we get pants in, it would be excellent to revisit these issues.

In working on this plugin, I discovered that our api spec validation has been broken for awhile, which I fixed in #5709. Part of our custom validation is still disabled because there are so many things in the spec that are out of date. This PR also does not fix that, but it does leave a comment in the pants plugin on how to enable that later.

Fine grained results cache

After someone runs ./pants fmt :: once, they will benefit from the pants' results cache. For the api_spec plugin, here's what that means:

First, the api_spec target in st2common/st2common depends on the generate/validate binaries in st2common/bin:

api_spec(
name="openapi_spec",
source="openapi.yaml",
dependencies=[
":openapi_spec_template",
"st2common/st2common/cmd/generate_api_spec.py", # st2-generate-api-spec
"st2common/st2common/cmd/validate_api_spec.py", # st2-validate-api-spec
"//conf:st2_dev_conf", # used for both generate and validate
],
)

If either script (st2-generate-api-spec or st2-validate-api-spec) changes, then pants now knows that openapi.yaml need to be regenerated. Or, in other words, the st2-generate-api-spec file is part of the cache key for st2common/st2common/openapi.yaml. If that file changes, then pants will re-run the generator.

st2-generate-api-spec is a python script, and we use pants' python dependency inference. So pants (effectively) also adds the python files imported by st2-generate-api-spec to the cache key. Stepping through the code, we can see what pants would infer:

from st2common.cmd.generate_api_spec import main

from st2common import config
from st2common import log as logging
from st2common.util import spec_loader
from st2common.script_setup import setup as common_setup
from st2common.script_setup import teardown as common_teardown

import st2common.constants.pack
import st2common.constants.action
from st2common.rbac.types import PermissionType
from st2common.util import isotime
from st2common.util import yml as yaml_utils

Here we reach the few things that get interpolated into the Jinja Template:

ARGUMENTS = {
"DEFAULT_PACK_NAME": st2common.constants.pack.DEFAULT_PACK_NAME,
"LIVEACTION_STATUSES": st2common.constants.action.LIVEACTION_STATUSES,
"PERMISSION_TYPE": PermissionType,
"ISO8601_UTC_REGEX": isotime.ISO8601_UTC_REGEX,
}

Now, if anyone changes any of these files, or any of the python bits that get imported to help define things in them, then the next time someone runs the lint goal (or in CI) they will find out that they need to run the fmt goal.

Developing pants plugins

Pants has extensive documentation on the plugin API, including how to create targets, how to write rules, and there are even mini tutorials about how to add a linter or a formatter (adding formatters/linters is a very common thing plugins to do).

@cognifloyd cognifloyd added this to the pants milestone Jan 5, 2023
@cognifloyd cognifloyd requested review from winem, arm4b, nzlosh, rush-skills, amanda11 and a team January 5, 2023 04:50
@cognifloyd cognifloyd self-assigned this Jan 5, 2023
@pull-request-size pull-request-size bot added the size/XL PR that changes 500-999 lines. Consider splitting work into several ones that easier to review. label Jan 5, 2023
pants-plugins/api_spec/rules.py Outdated Show resolved Hide resolved
pants-plugins/api_spec/rules.py Outdated Show resolved Hide resolved
pants-plugins/api_spec/rules.py Outdated Show resolved Hide resolved
pants-plugins/api_spec/rules.py Show resolved Hide resolved
pants-plugins/api_spec/rules_test.py Outdated Show resolved Hide resolved
@cognifloyd
Copy link
Member Author

@amanda11 Thanks for the feedback. I think I've addressed your comments.

@cognifloyd cognifloyd requested review from amanda11 and a team January 5, 2023 17:12
Copy link
Contributor

@amanda11 amanda11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cognifloyd cognifloyd requested a review from a team January 5, 2023 17:47
],
output_filename=f"{GENERATE_CMD}.pex",
internal_only=True,
main=EntryPoint.parse(f"{CMD_MODULE}.{GENERATE_CMD}:main"),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using st2common.cmd.generate_api_spec:main directly instead of using the st2-generate-api-spec script because pants can't run python with a - in the name (because pants relies on importing from the target script, and python cannot import from files with a - in the name).

Eventually I'll get around to plumbing a change in pants to make that possible (it already works with the underlying pex, but pants hasn't learned about that arg yet). In the meantime, we just use the entrypoint itself instead of the script.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we in the wrong here for having a python script named st2-generate-api-spec in the first place?

Copy link
Member Author

@cognifloyd cognifloyd Jan 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a command with - is fairly common, so I don't think that's a problem.

Pex (used by pants) already had everything it needed to read files without importing them, but that didn't have a user facing API. So, when I asked about it, John (pex maintainer) quickly added an --executable option, so now pex supports it. But, pants hasn't learned about that pex option yet, so it doesn't use it.

I started adding the pants bits here, but haven't had time to dive farther than this: pantsbuild/pants@main...cognifloyd:pants:pex-exe-arg

],
output_filename=f"{VALIDATE_CMD}.pex",
internal_only=True,
main=EntryPoint.parse(f"{CMD_MODULE}.{VALIDATE_CMD}:main"),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same thing applies here. We are using st2common.cmd.validate_api_spec:main directly instead of using the st2-validate-api-spec script directly.

@cognifloyd cognifloyd enabled auto-merge January 5, 2023 18:09
Copy link
Member

@rush-skills rush-skills left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cognifloyd cognifloyd force-pushed the pants-plugins-api_spec branch from fd4f47d to 971c99d Compare January 10, 2023 22:16
@cognifloyd cognifloyd force-pushed the pants-plugins-api_spec branch from 971c99d to dade25a Compare January 10, 2023 22:43
@cognifloyd cognifloyd merged commit efa1b87 into master Jan 10, 2023
@cognifloyd cognifloyd deleted the pants-plugins-api_spec branch January 10, 2023 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pantsbuild size/XL PR that changes 500-999 lines. Consider splitting work into several ones that easier to review.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants