actions / runner-container-hooks

Runner Container Hooks for GitHub Actions
MIT License
76 stars 46 forks source link

Usage details with Docker - How to use JSON definitions #43

Closed jcantosz closed 1 year ago

jcantosz commented 1 year ago

If someone could point me in the right direction for how to actually make use of the hook definitions with Docker containers. I seem to be missing some detail. I am trying to understand how/when run_container_step.json gets used.

For context: I would like to mount a volume (with internal/self-signed certs) to any container that action spins. I have a client with an internal CA and they don’t want to rebuild every image they use

I saw these container customization commands as a path forward but its not clear to me how I can pass in the .json customizations to the runner-container-hook index: https://docs.github.com/en/actions/hosting-your-own-runners/customizing-the-containers-used-by-jobs#generating-the-customization-script Which brought me here.

I have set ACTIONS_RUNNER_CONTAINER_HOOKS and I see this debug line

.env:

ACTIONS_RUNNER_CONTAINER_HOOKS=/home/azureuser/runner-container-hooks/packages/docker/dist/index.js

Snippet from run:

##[debug]/home/azureuser/actions-runner/externals/node16/bin/node /home/azureuser/runner-container-hooks/packages/docker/dist/index.js

But it doesn’t appear that it is picking up the run-container-step.json file I created, and I could not find an example of using those json hook definitions. Is there a specific flag or directory I need to be using? I tried these at my runner root and at the root of where I cloned the runner-container-hooks project and rebuilt after updating the location. Peeking at the code did not make a path obvious how these get used

For reference here is my run-container-step.json

{
    "command": "run_container_step",
    "responseFile": "out.json",
    "systemMountVolumes": [
        {
            "sourceVolumePath": "/etc/ssl/certs/ca-certificates.crt",
            "targetVolumePath": "/etc/ssl/certs/ca-certificates.crt",
            "readOnly": true

        },
        {
          "sourceVolumePath": "/Users/thomas/git/runner/_layout/_work",
          "targetVolumePath": "/__w",
          "readOnly": false
        },
        {
          "sourceVolumePath": "/Users/thomas/git/runner/_layout/externals",
          "targetVolumePath": "/__e",
          "readOnly": true
        },
        {
          "sourceVolumePath": "/Users/thomas/git/runner/_layout/_work/_temp",
          "targetVolumePath": "/__w/_temp",
          "readOnly": false
        },
        {
          "sourceVolumePath": "/Users/thomas/git/runner/_layout/_work/_actions",
          "targetVolumePath": "/__w/_actions",
          "readOnly": false
        },
        {
          "sourceVolumePath": "/Users/thomas/git/runner/_layout/_work/_tool",
          "targetVolumePath": "/__w/_tool",
          "readOnly": false
        },
        {
          "sourceVolumePath": "//Users/thomas/git/runner/_layout/_work/_temp/_github_home",
          "targetVolumePath": "/github/home",
          "readOnly": false
        },
        {
          "sourceVolumePath": "/Users/thomas/git/runner/_layout/_work/_temp/_github_workflow",
          "targetVolumePath": "/github/workflow",
          "readOnly": false
        }
    ]
}

Really appreciate you taking the time to look this longwinded description 😄

nikola-jokic commented 1 year ago

Hey @jcantosz,

I will try my best to explain it :relaxed:. The run_container_step is being used when you have a container action. So if your workflow has uses, and the step is actually the container action, this hook will get invoked. So to customize this, the best way would be to configure your hook to mount a volume with other volumes that will get passed from the runner. I assume you are using the docker hook, so I will explain it in terms of what you need to do there. So basically, here, you can add a line that will augment the args.userVolumeMounts like the following

const yourMount = new Mount{sourceVolumePath: "...", targetVolumePath: "...", readOnly: false} // you choose attributes based on your use-case
if (args.userVolumeMounts?.length) {
    args.userVolumeMounts.push(yourMount)
} else {
    args.userVolumeMounts = [yourMount]
}

After that, the containerRun is going to get called, and it will build volume mounts in the following here. The way you choose to customize your container hook really depends on you, but this is basically one of the ways you can do it. You can choose to have a configuration file to read it from the disk, ... It really depends on your implementation. But this is one of the ways you can customize it :relaxed:

I hope this helps!

jcantosz commented 1 year ago

Thanks @nikola-jokic ! I believe that clarifies things I think I read too much into this line form the docs:

When the resulting index.js is triggered by GitHub Actions, it will run the customization commands defined in the JSON files

If I understand correctly, I need to take this project, update the typescript with the properties I want to add on startup, retranspile and point ACTIONS_RUNNER_CONTAINER_HOOKS to that generated index.js. Then the JSON of those example files is there to represent the data format that the interface(s) expect.

Is that correct?

nikola-jokic commented 1 year ago

Exactly!

The JSON is just an API that is expected from the hook. The data is populated by the runner, but it is entirely up to the hook to decide how to react on that data.

So yes, if the behaviour should be different, you should change the hook implementation and re-build it :relaxed:.

The JSON there is just to define an interface between the runner and the hook, so the can pass the data to the hook, which the hook can use. If you want to bind a volume that is not passed by the runner, you can either hard-code it or have some file on the disk from which you can read and use it to build the command itself!

jcantosz commented 1 year ago

Got it! Really appreciate you walking me through that @nikola-jokic ! Many thanks!