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

feat: separate out language logic from moduleconfig #3016

Merged
merged 16 commits into from
Oct 8, 2024

Conversation

matt2e
Copy link
Contributor

@matt2e matt2e commented Oct 7, 2024

Why

Language support was previously tightly integrated with how a ModuleConfig is created. Without this defaulting, FTL does not know key info about a module like deploy directory or where to find the schema.

Another complication is that module defaults for jvm modules is done by looking for certain files to automatically choose defaults for gradle or maven, so we can't get the defaults once per language and use them for all modules of that language.

Creating a ModuleConfig

To create a ModuleConfig, the steps are:

// all error checking is skipped for clarity

// first load the toml file to get a UnvalidatedModuleConfig,
// which holds only the values that were in the toml file.
partialConfig, err := moduleconfig.LoadConfig(dir)

// from this we know the language, and can create a language plugin
plugin, err := languageplugin.New(ctx, uncheckedConfig.Language)

// we then ask the language plugin for the custom defaults
customDefaults, err := plugin.ModuleConfigDefaults(ctx, uncheckedConfig.Dir)

// we can now fill in the defaults and validate the config
config, err := uncheckedConfig.FillDefaultsAndValidate(customDefaults)

Other changes

  • Removed ModuleGoConfig , ModuleKotlinConfig, ModuleJavaConfig, replace with LanguageConfig: map[string]any
    • To do this, we unmarshal the toml into a map[string]any and then parse that into an UnvalidatedModuleConfig using mapstructure and then pull out the language config from the correct key (go, java, kotlin, etc)
  • buildengine caches the defaults per module so that each time it reads the toml it does not to ask for CustomDefaults from the plugin.

}
go func() {
// Loading a plugin can be expensive. Is there a better way?
plugin, err := languageplugin.New(ctx, m.Language)
Copy link
Contributor Author

@matt2e matt2e Oct 7, 2024

Choose a reason for hiding this comment

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

This change will eventually (once language plugins operate over gRPC) lead to a slowdown in this logic because now we need to create a language plugin per module just so we can figure out where the schema is saved.

I'm not sure of a good solution to this, other than having a central place FTL saves schemas (similar to the central /.ftl/ external modules) so that having a ModuleConfig is not readed to read the saved schema for a module.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not specifically about the slowdown, but the schema won't be saved right? It will be returned over gRPC?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Plugins don't save the schema, it gets sent over gRPC. What is unresolved at the moment though is how FTL deals with the schema after that. This admin logic is something that breaks if the schema doesn't get saved at all. For now FTL itself is saving the schema just so this and the current deploy logic works and I'll be coming back to this admin stuff a bit later. I've added a section on deployment to the things to do in this main issue here: #2452

@ftl-robot ftl-robot mentioned this pull request Oct 7, 2024

// UpdateDependencies finds the dependencies for a module and returns a
// Module with those dependencies populated.
func UpdateDependencies(ctx context.Context, module Module, plugin LanguagePlugin) (Module, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was unused after previous language plugin changes

"github.com/alecthomas/assert/v2"
)

func TestExtractModuleDepsGo(t *testing.T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

these have been moved to unit tests within languageplugin

"golang.org/x/mod/modfile"
)

func replacementWatches(moduleDir, deployDir string) ([]string, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

all this code was previously in moduleconfig but is all specific to the go plugin

GeneratedSchemaDir: "src/main/ftl-module-schema",
Deploy: []string{"launch", "quarkus-app"},
// Watch defaults to files related to maven and gradle
Watch: []string{"pom.xml", "src/**", "build/generated", "target/generated-sources"},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously watch was different based on maven/gradle, but I think it's fine to just use the same watch patterns for both

@@ -13,8 +13,8 @@ import (

// DiscoverModules recursively loads all modules under the given directories
// (or if none provided, the current working directory is used).
func DiscoverModules(ctx context.Context, moduleDirs []string) ([]moduleconfig.ModuleConfig, error) {
out := []moduleconfig.ModuleConfig{}
func DiscoverModules(ctx context.Context, moduleDirs []string) ([]moduleconfig.UnvalidatedModuleConfig, error) {
Copy link
Contributor Author

@matt2e matt2e Oct 7, 2024

Choose a reason for hiding this comment

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

Note: Discover handles UnvalidatedModuleConfigs now, instead of ModuleConfigs

@@ -14,162 +14,31 @@ func TestDiscoverModules(t *testing.T) {
ctx := log.ContextWithNewDefaultLogger(context.Background())
modules, err := DiscoverModules(ctx, []string{"testdata"})
assert.NoError(t, err)
expected := []moduleconfig.ModuleConfig{
expected := []moduleconfig.UnvalidatedModuleConfig{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously this test was testing discovery and defaulting.
Now it just tests discovery. Generic defaulting tests can now be found in moduleconfig tests, while language specific default testing can be found in languageplugin

@matt2e matt2e marked this pull request as ready for review October 7, 2024 03:59
@matt2e matt2e requested review from a team and alecthomas as code owners October 7, 2024 03:59
@matt2e matt2e requested review from wesbillman and removed request for a team October 7, 2024 03:59
@matt2e matt2e mentioned this pull request Oct 7, 2024
38 tasks
@matt2e matt2e merged commit 28ac330 into main Oct 8, 2024
95 checks passed
@matt2e matt2e deleted the matt2e/language-config branch October 8, 2024 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants