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(terraform): add plugin commands for terraform apply and plan #1641

Merged
merged 3 commits into from
Mar 10, 2020

Conversation

edvald
Copy link
Collaborator

@edvald edvald commented Feb 25, 2020

What this PR does / why we need it:

This makes the terraform provider a little easier to work with
day-to-day. We also explicitly discourage use of autoApply now, except
for private environments. The status check is now also faster, because
we don't refresh the stack state every time.

This involved a few refactors, mainly to improve the plugin command
framework. I also needed to modify the provider resolution flow so that
individual providers can be resolved (otherwise it wouldn't be possible
to run the apply/plan commands if another provider depended on the
outputs).

Special notes for your reviewer:

Please try using this with a real project, see if it matches your expectations,
and the interface+docs feel clear and sensible.

@edvald edvald force-pushed the terraform-commands branch 2 times, most recently from 84e95d6 to 6d5d97f Compare February 26, 2020 17:30
@eysi09
Copy link
Collaborator

eysi09 commented Mar 2, 2020

@solomonope, would be great to get your eyes on this as well,

garden-service/src/commands/plugins.ts Show resolved Hide resolved
@@ -34,6 +36,10 @@ export class PluginsCommand extends Command<Args> {
name = "plugins"
help = "Plugin-specific commands."

// FIXME: We need this while we're still resolving providers in the AnalyticsHandler
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you know how we would fix this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

By not doing that, basically. We'd add metadata as it becomes available, and not force/rush the init.


garden ${chalk.yellow("[global options]")} ${chalk.blueBright("<command>")} ${chalk.white("[args ...]")}

${chalk.white.bold("PLUGIN COMMANDS")}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could extract this and the stuff below to a helper function since we're doing basically the same in https://github.com/garden-io/garden/tree/master/garden-service/src/commands/options.ts

Copy link
Collaborator

Choose a reason for hiding this comment

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

I notice that the table config is duplicated as well, although not part of this PR per se.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually on second glance there isn't that much common with the code, seems like I'd be going out of my way there a bit. The table generation there could use the new helper function though, I guess.

}

// As an optimization, we return immediately if all requested providers are already resolved
const alreadyResolved = names.map((name) => this.resolvedProviders[name]).filter(Boolean)
Copy link
Collaborator

@eysi09 eysi09 Mar 4, 2020

Choose a reason for hiding this comment

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

A nitpick but the variable name sounds like a boolean, perhaps alreadyResolvedProviders. Makes the providers = alreadyResolved line below easier to read.

@@ -54,7 +54,7 @@ export class Logger extends LogNode {

static initialize(config: LoggerConfig): Logger {
if (Logger.instance) {
throw new InternalError("Logger already initialized", {})
return Logger.instance
Copy link
Collaborator

Choose a reason for hiding this comment

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

Trying to think why we didn't just do this in the first place.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, same. I couldn't think of a good reason.

} else if (plan.exitCode === 2) {
// No error but stack is not up-to-date
logEntry.setWarn({ msg: "Not up-to-date" })
if (autoApply) {
// Trigger the prepareEnvironment handler
return { ready: false, outputs: {} }
} else {
logEntry.warn(noAutoApplyMsg)
logEntry.warn({ symbol: "warning", msg: noAutoApplyMsg(applyCommand) })
const outputs = await getTfOutputs(log, tfVersion, root)
return { ready: true, outputs }
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you have autoApply set to false, and have a Terraform module that's not been applied, e.g. because you've never "deployed" it before, Garden will print Already deployed to the terminal but fail when other modules reference its runtime output.

Not sure how to workaround this tbh.

A similar thing would happen at the provider level but at least there you don't get the Already deployed message which is a little misleading.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We may need to special-case the module behavior (as opposed to the provider behavior). I can look into it.

@@ -850,11 +850,15 @@ export class TaskNodeBatch {
*/
taskCached(result: TaskResult, depResults: TaskResult[]): boolean {
const key = result.key
this.results[key] = result
if (this.remainingResultKeys.has(key)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are we changing the task graph? And could @thsig lend a pair of eyes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah yes, I forgot to mention this in the commit. There was basically an error in the TaskGraph that caused old/unrelated task results to be lumped in the TaskResults object, that prompted an issue while I was testing all this. Could split that out into a separate commit.

@@ -104,6 +104,12 @@ export class ResolveProviderTask extends BaseTask {
async process(dependencyResults: TaskResults) {
const resolvedProviders: Provider[] = Object.values(dependencyResults).map((result) => result && result.output)

// Return immediately if the provider has been previously resolved
const alreadyResolved = this.garden["resolvedProviders"][this.config.name]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as my comment above, looks like a boolean. Admittedly I'm not looking at this in VSCode with the type hints.

@eysi09
Copy link
Collaborator

eysi09 commented Mar 8, 2020

There are a few console log statements in there, also needs to be squashed.

This makes the terraform provider a little easier to work with
day-to-day. We also explicitly discourage use of `autoApply` now, except
for private environments. The status check is now also faster, because
we don't refresh the stack state every time.

This involved a few refactors, mainly to improve the plugin command
framework. I also needed to modify the provider resolution flow so that
individual providers can be resolved (otherwise it wouldn't be possible
to run the apply/plan commands if another provider depended on the
outputs).
@edvald edvald merged commit b4283dd into master Mar 10, 2020
@edvald edvald deleted the terraform-commands branch March 10, 2020 16:11
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.

None yet

2 participants