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

Adhoc Provider - Custom Resource / Data Source #3270

Open
skorfmann opened this issue Jul 5, 2023 · 4 comments
Open

Adhoc Provider - Custom Resource / Data Source #3270

skorfmann opened this issue Jul 5, 2023 · 4 comments
Labels
✨ enhancement New feature or request needs-discussion Further discussion is needed prior to impl roadmap 🎨 sdk SDK

Comments

@skorfmann
Copy link
Contributor

skorfmann commented Jul 5, 2023

Feature Spec

With Wing Custom Resources, it's now simple to interface with bespoke APIs and extend the Terraform inflight behaviour with custom code snippets.

bring resource

class GithubIssue impl resource.ICustomResource {
  create(title: str) {
    // ...
  }
  update(id: num, title: str) {
    // ...
  }
  read(id: num) {
    // ...
  }
  destroy(id: num) {
    // ...
  }
}

or for data fetching / manipulation something like this (inspired by this)

interface CustomDockerImageArgs {
  repositoryUrl: str;
  username: str;
  password: str;
}

class CustomDockerImage impl resource.ICustomDataSource {    
  sha256Digest: str?;  
  reposoitryUrl: str;
  username: str;
  password: str;

  init(config: CustomDockerImageArgs) {
    this.repositoryUrl = config.repositoryUrl;
    this.username = config.username;
    this.password = config.password;
  }

  onRead(): {    
    // For complex cases, this should be JS code. So maybe external?
    const drc = require('docker-registry-client')
    return new Promise((resolve, reject) => {
      var rar = drc.parseRepoAndRef(this.repositoryUrl);
      var client = drc.createClientV2({
        repo: rar,
        insecure: false,
        username: this.username,
        password: this.password,
        maxSchemaVersion: 2
      });
      var tagOrDigest = rar.tag || rar.digest;
      client.getManifest({ref: tagOrDigest}, function (err:any, _manifest:any, _res:any, manifestStr:any) {
        client.close();
        if (err) {
          reject(err)
        }
        this.sha256Digest = drc.digestFromManifestStr(manifestStr);
        resolve(this);
      });
    });
  }
}

Both, resources and data sources, are build ad-hoc as a Terraform provider when comping this with the Wing CLI transparent to to the user.

In a Wing application, this could be used like:

let image = new CustomDockerImage(...)

which would render down to a Terraform HCL (JSON) data source, something like (pseudo HCL code)

provider "wing-adhoc" {}

data "wing-adhoc_custom-docker-image" {
  repository_url = "https://abc"
  username = "hello"
  password = "world"
}

output "sha56" {
  value = data.wing-adhoc_custom-docker-image.sha56
}

Use Cases

  • build ad hoc resources to interface with APIs not covered by a Terraform provider
  • build ad hoc data sources, to fetch and / or manipulate data as needed
  • perform custom computations at Terraform run time
  • build test cases and bake them into Terraform
  • enable running any JS code at Terraform run time

Implementation Notes

There are two components to make this work at a high level:

  1. Build a solid implementation of the Provider Plugin Framework in Javascript or find a way to utilize parts the Go implementation under the hood.
  2. Build abstractions in Wing which can use this with minimal overhead

Component

Other

Community Notes

  • Please vote by adding a 👍 reaction to the issue to help us prioritize.
  • If you are interested to work on this issue, please leave a comment.
@skorfmann skorfmann added the ✨ enhancement New feature or request label Jul 5, 2023
@monadabot monadabot added this to Wing Jul 5, 2023
@github-project-automation github-project-automation bot moved this to 🆕 New - not properly defined in Wing Jul 5, 2023
@staycoolcall911 staycoolcall911 moved this from 🆕 New - not properly defined to 🤝 Backlog - handoff to owners in Wing Jul 6, 2023
@github-actions
Copy link

github-actions bot commented Sep 5, 2023

Hi,

This issue hasn't seen activity in 60 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days.
Feel free to re-open this issue when there's an update or relevant information to be added.
Thanks!

@github-actions github-actions bot added the Stale label Sep 5, 2023
@staycoolcall911 staycoolcall911 added the needs-discussion Further discussion is needed prior to impl label Oct 4, 2023
@skorfmann
Copy link
Contributor Author

Here's a use case for fly.io - while there's a Terraform provider, the readme states:

This project is not currently maintained, and is not a recommended method of deployment to Fly.io.

we want to do create a project via Terraform, right now this is shelling out via null_resource and local-exec. While this works for creation, it's pretty much a dead-end for destroying - see https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax#destroy-time-provisioners

   let resource = new nullProvider.resource.Resource(triggers: { "changed": util.nanoid() }) as "create"
    resource.addOverride("provisioner.local-exec.environment", {"FLY_APP_NAME": appName});
    resource.addOverride("provisioner.local-exec.command", "
flyctl status -a \$FLY_APP_NAME || flyctl launch --copy-config --no-deploy --name \$FLY_APP_NAME -o ${props.org} -r iad -y
    ");

  // FIXME: We'll have to find another way to do this. Can't reference other resources here. Also, there are drawbacks
    // see https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax#destroy-time-provisioners
  
   let destroy =  new nullProvider.resource.Resource() as "destroy";
    destroy.addOverride("provisioner.local-exec.when", "destroy");
    destroy.addOverride("provisioner.local-exec.environment", {"FLY_APP_NAME": appName});
    destroy.addOverride("provisioner.local-exec.command", "flyctl status -a \$FLY_APP_NAME && flyctl apps destroy \$FLY_APP_NAME -y");

so, if it was possible to do something like:

bring http;

struct FlyProjectProps {
  name: str;
  org: str;
  region: str;
}

class FlyProject extends CustomResource {
  var name: str;
  var region: str;
  var bucket: str;

  init(props: FlyProjectProps) {
    this.name = props.name;
    this.region = props.region;
    this.bucket = props.bucket;
  }

  // get invoked during terraform apply as normal Terraform resource
  pub create(): void {
    // Add code here to create the project via http
    http.post("https://api.fly.io/v2/projects", ...)
  }

  pub read(): void {
    // Add code here to read the resource
    http.get("https://api.fly.io/v2/projects", ...)
  }

  pub update(): void {
    // Add code here to update the resource
    http.put("https://api.fly.io/v2/projects", ...)
  }

  pub delete(): void {
    // Add code here to delete the resource
    http.delete("https://api.fly.io/v2/projects", ...)
  }
}

then this could become

bring fly

new fly.Project(
  name: "foo",
  region: "iad",
  org: "bar"
);

Copy link

Hi,

This issue hasn't seen activity in 90 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days.
Feel free to re-open this issue when there's an update or relevant information to be added.
Thanks!

Copy link

Hi,

This issue hasn't seen activity in 90 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days.
Feel free to re-open this issue when there's an update or relevant information to be added.
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ enhancement New feature or request needs-discussion Further discussion is needed prior to impl roadmap 🎨 sdk SDK
Projects
Status: 🤝 Backlog - handoff to owners
Development

No branches or pull requests

3 participants