compose icon indicating copy to clipboard operation
compose copied to clipboard

Add GO templating capabilities to images and list commands

Open leoperegrino opened this issue 6 months ago • 11 comments

What I did Implemented GO templates for images and list commands. I tried to align the changes with already existing commits, such as 1054792b4778b9fa9f8e0d24594882733aa4ed28, even though a new formatter file in cmd/formatter/<command>.go was not created.

This PR changes the output of the json format. This is the default behavior for the commands that have template support.

# behavior for other commands with templating
docker images --format json
# {"Containers":"N/A","CreatedAt":"2014-07-19 04:02:32 -0300 -03","CreatedSince":"10 years ago","Digest":"\u003cnone\u003e","ID":"350b164e7ae1","Repository":"gcr.io/google-containers/pause","SharedSize":"N/A","Size":"240kB","Tag":"latest","UniqueSize":"N/A","VirtualSize":"239.8kB"}
# {"Containers":"N/A","CreatedAt":"1979-12-31 21:00:00 -0300 -03","CreatedSince":"45 years ago","Digest":"\u003cnone\u003e","ID":"f576b2d0af25","Repository":"test","SharedSize":"N/A","Size":"2.06GB","Tag":"latest","UniqueSize":"N/A","VirtualSize":"2.056GB"}

# images before this PR
docker compose images --format json
# [{"ID":"sha256:350b164e7ae1dcddeffadd65c76226c9b6dc5553f5179153fb0e36b78f2a5e06","ContainerName":"test_1-compose_test-1","Repository":"gcr.io/google-containers/pause","Tag":"latest","Size":239840,"LastTagTime":"0001-01-01T00:00:00Z"},{"ID":"sha256:350b164e7ae1dcddeffadd65c76226c9b6dc5553f5179153fb0e36b78f2a5e06","ContainerName":"test_1-compose_test_2-1","Repository":"gcr.io/google-containers/pause","Tag":"latest","Size":239840,"LastTagTime":"0001-01-01T00:00:00Z"}]

# images after this PR
docker compose images --format json
# {"ContainerName":"test_1-compose_test-1","Created":"292 years ago","ID":"350b164e7ae1","LastTagTime":"0001-01-01 00:00:00 +0000 UTC","Platform":"linux/amd64","Repository":"gcr.io/google-containers/pause","Size":"240kB","Tag":"latest"}
# {"ContainerName":"test_1-compose_test_2-1","Created":"292 years ago","ID":"350b164e7ae1","LastTagTime":"0001-01-01 00:00:00 +0000 UTC","Platform":"linux/amd64","Repository":"gcr.io/google-containers/pause","Size":"240kB","Tag":"latest"}

For the images command, a new --no-trunc flag was added to control the truncate behavior of the image ID.

Related issue

This closes #12948.

(not mandatory) A picture of a cute animal, if possible in relation to what you did

leoperegrino avatar Jun 16 '25 14:06 leoperegrino

Hello @leoperegrino for legal reasons all contributors must sign-off commits so we can accept them in this repository please amend your PR thanks

glours avatar Jun 16 '25 14:06 glours

LGTM regarding the code approach

  1. for legal reasons, you have to sign-off your commits so we can accept contributions
  2. regarding compatibility break, cli/formatter applies slice formatting per entry, not as an array. I wonder we need to find a workaround; As your example illustrate, attribute formatting also changed, typically using a "human friendly" format which IMHO doesn't make any sense for json output.

Assuming the final goal is to align with docker/cli, format should offer the same data as https://github.com/docker/cli/blob/master/cli/command/formatter/image.go#L198-L208 and reuse existing public types

I'm not convince alignment with docker/cli is the best option here, and go templating format support is even desirable (with json output, you can pipe to a dedicated formatting engine, typically jq) but won't block this PR is others see value.

ndeloof avatar Jun 16 '25 14:06 ndeloof

Sorry about the signoff, I thought it was done automatically but already push forced amended commits.

The use case for this feature was expanded a bit more in the issue #12948. It consists in avoiding the use of a external tool such as jq to process a machine readable format, as most distributions don't come with jq pre installed and you may not have the permission to install it.

I don't think it needs to be a 1-1 mapping to docker/cli, I just tried to not deviate from the current codebase. But if a specific data structure format is required, I could change it when agreed upon.

Besides the slice/array difference, I think the only change in the attributes it's the ID. Now it's going to be truncated unless the --no-trunc flag is passed, as it is with docker compose ps.

leoperegrino avatar Jun 16 '25 15:06 leoperegrino

feel free to change "api" so that it returns image.Summary and you can use more of the existing public types from https://github.com/docker/cli/blob/master/cli/command/image/list.go

ndeloof avatar Jun 16 '25 15:06 ndeloof

The api.Stack struct holds attributes (ID and Reason) which doesn't seem to be used, maybe left from the ecs integration.

When formatting as json should they appear? Because right now, they don't.

docker compose ls --format json
# [{"Name":"test_1","Status":"running(2)","ConfigFiles":"..."},{"Name":"test_2","Status":"running(1)","ConfigFiles":"..."}]

leoperegrino avatar Jun 16 '25 16:06 leoperegrino

maybe left from the ecs integration indeed 🥹 - lot's of legacy here, feel free to remove those (can do in a separate PR if you prefer to keep this one focussed)

ndeloof avatar Jun 16 '25 16:06 ndeloof

I've made the ImageContext and StackContext aware of the cliformatter.Context by adding a new cliFormat field to their structs:

https://github.com/docker/compose/blob/5a0ca602e99c269b9bb05c299ba911ee865960e6/cmd/compose/images.go#L128-L133

https://github.com/docker/compose/blob/5a0ca602e99c269b9bb05c299ba911ee865960e6/cmd/compose/list.go#L129-L133

Now, it's possible to access the --format passed in the cli in the *Context methods and have different logic paths if cliFormat.isJSON():

https://github.com/docker/compose/blob/5a0ca602e99c269b9bb05c299ba911ee865960e6/cmd/compose/images.go#L181-L186

https://github.com/docker/compose/blob/5a0ca602e99c269b9bb05c299ba911ee865960e6/cmd/compose/images.go#L154-L159

docker compose images --format json

# {"ContainerName":"test_1-compose_test-1","Created":"292 years ago","ID":"sha256:350b164e7ae1dcddeffadd65c76226c9b6dc5553f5179153fb0e36b78f2a5e06","LastTagTime":"0001-01-01 00:00:00 +0000 UTC","Platform":"linux/amd64","Repository":"gcr.io/google-containers/pause","Size":"239840","Tag":"latest"}
# {"ContainerName":"test_1-compose_test_2-1","Created":"292 years ago","ID":"sha256:350b164e7ae1dcddeffadd65c76226c9b6dc5553f5179153fb0e36b78f2a5e06","LastTagTime":"0001-01-01 00:00:00 +0000 UTC","Platform":"linux/amd64","Repository":"gcr.io/google-containers/pause","Size":"239840","Tag":"latest"}

docker compose images --format table

# CONTAINER                 REPOSITORY                       TAG       PLATFORM      IMAGE ID       SIZE      CREATED
# test_1-compose_test_2-1   gcr.io/google-containers/pause   latest    linux/amd64   350b164e7ae1   240kB     292 years ago
# test_1-compose_test-1     gcr.io/google-containers/pause   latest    linux/amd64   350b164e7ae1   240kB     292 years ago

docker-compose ls --format json

# {"ConfigFiles":"...","Name":"test_1","Status":"running(2)"}
# {"ConfigFiles":"...","Name":"test_2","Status":"running(1)"}

docker compose ls --format table

# NAME      STATUS       CONFIG FILES
# test_1    running(2)   ...
# test_2    running(1)   ...

I believe this should be enough but tell me what do you think.

leoperegrino avatar Jun 16 '25 19:06 leoperegrino

for JSON support, sounds way simpler to just bypass all the formater logic and rely on json.Marshall, or did I missed something ?

ndeloof avatar Jun 16 '25 19:06 ndeloof

for JSON support, sounds way simpler to just bypass all the formater logic and rely on json.Marshall, or did I missed something ?

Could definitely be, I tried to mimic already inplace behavior and that's why I used the Context approach. So, do you think I should drop the Contexts and use Marshal directly in their run functions?

leoperegrino avatar Jun 16 '25 19:06 leoperegrino

yes, adopt docker/cli approach with the minimal amount of code (feel free to make a few changes so you can directly use the same structs/functions) + include a dedicated if block to manage JSON output for backward compatibility

ndeloof avatar Jun 16 '25 19:06 ndeloof

Ok but to maintain the templating capability we would also need all the Context code, right? So if that code is already going to be there, why not reuse for the JSON marshal? This is how it's done by the ps command where it simply calls the ContainerWrite regardless if it was passed a JSON format or not: https://github.com/docker/compose/blob/4f491ffa9869869579b7b5f5a3b3d94bc8395377/cmd/compose/ps.go#L154-L160

To me this makes more sense than to have another logical path in the run command just for JSON

leoperegrino avatar Jun 16 '25 21:06 leoperegrino