Search code examples
node.jstypescriptconcourse

Replicating "Concourse CI" Node Call / Debugging process.stdin Net.socket


I've built a customer resource using node. The resource code looks okay, but once compiled and placed into Concourse, the "Check" in the resource is failing.

Concourse is not giving any useful information except "Unexpected end of JSON"

I'd like to replicate exactly how Concourse calls the build, but I don't know how to find out what it calls?

My assumption was /opt/resource/check which has #!/usr/bin/env node so just calling this should be sufficient, but I do not get the same behavior.

What I can determine is that it's hanging on my socket that fetches the params passed via stdIn, code below:

export async function retrieveRequestFromStdin<T extends any>(): Promise<T> {
    return new Promise<T>((resolve, reject) => {
        let inputRaw = "";
        process.stdin.on("data", (chunk) => {
            process.stdout.write(chunk);
            inputRaw += chunk;
        });
        process.stdout.write(inputRaw);
        process.stdin.on("end", async () => {
            try {
                const json = JSON.parse(inputRaw) as T;
                if (!json.source.server_url.endsWith("/")) {
                    // Forgive input errors and append missing slash if the user did not honor the docs.
                    json.source.server_url = `${json.source.server_url}/`;
                }
                resolve(json);
            } catch (e) {
                reject(e);
            }
        });
    });
}

This is the check code:

(async () => {
  try {
    const request: ICheckRequest = await retrieveRequestFromStdin<ICheckRequest>();
   // Removed unnecessary items
  } catch (e) {
    stderr.write(e);
    process.exit(1);
  }
})();

How do I call a NodeJS script the same way as Concourse, so I can find out exactly what the problem is?

Note, I'm compiling Javascript from Typescript


Solution

  • For a resource check, Concourse will run /opt/resource/check passing in the source information from the resource configuration in on stdin. For example, if you configured the pipeline with:

    resources:
    - name: resource-name
      type: your-new-node-resource-type
      source:
        server_url: https://someurl.com
    

    the first time check runs, your script would receive this on stdin:

    {"source": {"server_url": "https://someurl.com"}}
    

    Your script is then expected to return the "current" version of the resource on stdout. In the example below, I've named the key version-example, but you can name that anything you want. You can also add other keys too if you want. This allows you flexibility in uniquely identifying a version.

    [{"version-example": "46"}]
    

    Subsequent calls from Concourse to your check script will also include the latest version it knows about, so continuing the example, the next call will pass this to your script:

    {"source": {"server_url": "https://someurl.com"}, 
     "version": {"version-example": "46"}}
    

    Your check script should now return (on stdout) an array of any new versions that it finds in order:

    [{"version-example": "47"},
     {"version-example": "48"},
     {"version-example": "49"}]
    

    For more details, you can check out the official docs, which should also be useful when implementing the in and out scripts.


    Taking a quick look at your code, it seems that it's writing stdin twice to stdout, which results in the Unexpected end of JSON message. e.g.:

    {"source": {"server_url": "https://someurl.com"}}
    {"source": {"server_url": "https://someurl.com"}}