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

Lifecycle Hooks #682

Open
skorfmann opened this issue May 4, 2021 · 7 comments
Open

Lifecycle Hooks #682

skorfmann opened this issue May 4, 2021 · 7 comments
Labels
enhancement New feature or request feature/programmatic-api priority/important-longterm Medium priority, to be worked on within the following 1-2 business quarters. size/medium estimated < 1 week ux/integration

Comments

@skorfmann
Copy link
Contributor

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Description

It'd be great if we were able to use hooks for the various lifecycles of the cdktf cli. I'm thinking about something like

  • [before|after]Synth
  • [before|after]Diff
  • [before|after]Deploy
  • [before|after]Destroy

I could see this becoming useful, if we'd want to build certain features in a pluggable way and at the same time enabling users to build their own hooks / plugins.

One slightly adapted example from this issue:

const app = new App()
const stack = new MyStack(app, "automate-everything")

stack.on('afterDiff', (plan, executor) => {
  if (plan.changes && plan.changes.module.custom.fruits > 4) {
    executor.addTarget('module.custom.fruits')
  }
})

Or when thinking about creating Terraform Cloud workspaces, perhaps something like this in more global fashion could work:

const allBackends(TerraformBackend): TerraformBackend[] {
  const backends: TerraformBackend[] = [];

  const visit = (node: IConstruct) => {
    if (node instanceof TerraformBackend) {
      backends.push(node)
    }

    for (const child of Node.of(node).children) {
      visit(child);
    }
  }

  visit(app)

  return backends
}

export beforeSynth = (app) => {
  const backends = allBackends(app)

  for (const backend of backends) {
    // make sure backend exists
  }
}

All of this above is pseudo code and are just rough ideas. It would only work in the context of the cdktf cli being used. However, I think this could be quite helpful for us and our users.

References

@skorfmann skorfmann added enhancement New feature or request new Un-triaged issue labels May 4, 2021
@jsteinich
Copy link
Collaborator

It would only work in the context of the cdktf cli being used

I understand the simplicity of routing through the cli; however, pieces that only work through the cli do add friction; particularly in languages where it is expected to just be able to run from your ide without setting up anything special.

@skorfmann
Copy link
Contributor Author

It would only work in the context of the cdktf cli being used

I understand the simplicity of routing through the cli; however, pieces that only work through the cli do add friction; particularly in languages where it is expected to just be able to run from your ide without setting up anything special.

I don't think we can make this work as part of the Terraform CLI, so I think depending on the cdktf cli is likely the only option here - or do you have another idea?

@jsteinich
Copy link
Collaborator

I don't think we can make this work as part of the Terraform CLI, so I think depending on the cdktf cli is likely the only option here - or do you have another idea?

Apologies. That was more of a user reaction rather than thinking it through. #408 really covers the only part that makes sense in the current context. #237 is also interesting, but that would probably exist on a layer above / side of the cli.

@matschaffer
Copy link

I think if I needed to run the hook directly I'd opt to move the operations into a library and keep the hook as just something like:

import * as hooks from './hooks';
stack.on('afterDiff', hooks.afterDiff);

That way I could use jest or something to run/validate hook assumptions outside of the cdktf cli.

@rirze
Copy link
Contributor

rirze commented Feb 4, 2022

I'd like to bring up another benefit of this approach-- this could enable dynamic importing of Terraform resources from code. It would be similar to how Pulumi works, where you could specify a import parameter into the resource and the first apply will generate the import statements necessary.

However, it would involve removing the import parameter from the code after one successful run.

I imagine this function running pre-deploy.

@DanielMSchmidt DanielMSchmidt added priority/important-longterm Medium priority, to be worked on within the following 1-2 business quarters. and removed needs-priority Issue has not yet been prioritized; this will prompt team review labels Feb 10, 2022
@eahrend
Copy link

eahrend commented Nov 2, 2022

Hey,

So, this would actually be super useful at the construct level. For example, we want to spin up a new K8S cluster and put a vendor's pods on said cluster. Unfortunately the vendor doesn't have a terraform provider, and we need to communicate with the API to generate a secret in order to deploy those pods, however the vendor keeps a record of this secret and we'd like to revoke that secret after we destroy said cluster and remove the vendor's pods from our cluster.

For example, I'd like to be able to do this:

package main

// copying from an example in the CDKTF tutorial page.

import (
    kubernetes "github.com/hashicorp/terraform-cdk/examples/go/documentation/generated/hashicorp/kubernetes/provider"
    // psuedo-code for this, not sure where this would go
    helm "github.com/hashicorp/terraform-cdk/examples/go/documentation/generated/hashicorp/kubernetes/helm"
    "github.com/hashicorp/terraform-cdk/examples/go/documentation/myconstructs"
    "github.com/aws/constructs-go/constructs/v10"
    "github.com/aws/jsii-runtime-go"
    "github.com/hashicorp/terraform-cdk-go/cdktf"

    // placeholder for library used to interact with the vendor's API
    "github.com/eahrend/vendor"

    "os"
    "path"
)

func NewExampleCdktfDocumentationStack(scope constructs.Construct, name string) cdktf.TerraformStack {
    stack := cdktf.NewTerraformStack(scope, &name)

    cwd, _ := os.Getwd()

    kubernetes.NewKubernetesProvider(stack, jsii.String("kind"), &kubernetes.KubernetesProviderConfig{
        ConfigPath: jsii.String(path.Join(cwd, "kubeconfig.yaml")),
    })
    // instantiating a new vendor client to get the token
    vendorClient, _ := vendor.NewClient("some configuration options")
    vendorToken, _ := vendorClient.GetToken()
    myconstructs.NewKubernetesWebAppDeployment(stack, "deployment", map[string]interface{}{
        "image":       jsii.String("nginx:latest"),
        "replicas":    jsii.Number(2),
        "app":         jsii.String("myapp"),
        "component":   jsii.String("frontend"),
        "environment": jsii.String("dev"),
        "token": jsii.String(vendorToken),
    })
    myconstructs.OnDelete(revokeToken(vendorToken))

    return stack
}


// here we're revoking the token from the vendor's API
func revokeToken(vendorToken string) error {
    vendorClient, _ := vendor.NewClient("some configuration options")
    vendorClient.RevokeToken(vendorToken)
}

func main() {
    app := cdktf.NewApp(nil)

    NewExampleCdktfDocumentationStack(app, "demo")

    app.Synth()
}

@ntrp
Copy link

ntrp commented Mar 8, 2023

This would be useful also in case we need to open a tunnel to access the resources, the tunnel init code could be in the before hook and the tunnel shutdown code in the after hook.

One example: Open a SSM port forwarding to RDS before refreshing or applying postgresql resources via the postgres provider

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feature/programmatic-api priority/important-longterm Medium priority, to be worked on within the following 1-2 business quarters. size/medium estimated < 1 week ux/integration
Projects
None yet
Development

No branches or pull requests

9 participants