Skip to content

Instantly share code, notes, and snippets.

@jonjohnsonjr
Last active December 4, 2021 02:18
Show Gist options
  • Save jonjohnsonjr/66c3b5bc75a2bf69e0524d823e3f7228 to your computer and use it in GitHub Desktop.
Save jonjohnsonjr/66c3b5bc75a2bf69e0524d823e3f7228 to your computer and use it in GitHub Desktop.
run

run

Outputs the equivalent argv of what you would get with a docker run command.

This considers the given image's CMD, ENTRYPOINT, and docker run's --entrypoint flag.

$ run hello-world@sha256:96ebeec770e1af26469c98913277e1c3b40933202ca398cefc16177c3f26cc75
["cmd","/C","type C:\\hello.txt"]

$ run --entrypoint /crane hello-world@sha256:96ebeec770e1af26469c98913277e1c3b40933202ca398cefc16177c3f26cc75
["/crane"]

$ run hello-world@sha256:96ebeec770e1af26469c98913277e1c3b40933202ca398cefc16177c3f26cc75 crane
["crane"]

$ run gcr.io/go-containerregistry/crane version
["/ko-app/crane","version"]

$ run --entrypoint '["crane", "ls"]' gcr.io/go-containerregistry/crane ubuntu
["crane","ls","ubuntu"]
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"github.com/google/go-containerregistry/pkg/crane"
flag "github.com/spf13/pflag"
)
var entrypoint string
func init() {
flag.StringVar(&entrypoint, "entrypoint", "", "Overwrite the default ENTRYPOINT of the image")
flag.Parse()
}
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
args := flag.Args()
if len(args) < 1 {
return errors.New("no args")
}
img, err := crane.Pull(args[0])
if err != nil {
return err
}
cf, err := img.ConfigFile()
if err != nil {
return err
}
exec := []string{}
if entrypoint != "" {
b := []byte(entrypoint)
p := make([]string, 0, 1)
if err := json.Unmarshal(b, &p); err == nil {
cf.Config.Entrypoint = p
} else {
cf.Config.Entrypoint = []string{entrypoint}
}
cf.Config.Cmd = nil
}
exec = append(exec, cf.Config.Entrypoint...)
if len(args) > 1 {
exec = append(exec, args[1:]...)
} else if len(cf.Config.Cmd) > 0 {
exec = append(exec, cf.Config.Cmd...)
}
b, err := json.Marshal(exec)
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}
@subfuzion
Copy link

Very cool! I think I'll use your tool next time I demo (with attribution, of course)!

If anyone's interested in a bit of explanation around CMD and ENTRYPOINT and using them together, this section in a little guide I wrote up for a Docker presentation might help: https://dockerbyexample.dev/basics/time/

@subfuzion
Copy link

Here's an alternative way of dissecting the behavior that might also be helpful for others:

printargs

#!/bin/sh
echo "$@"

Dockerfile

FROM alpine
WORKDIR /usr/local/bin
COPY printargs .
RUN chmod +x printargs
ENTRYPOINT ["printargs", "1", "2", "3"]
CMD ["4", "5", "6"]

Build an image called printargs:

docker build -t printargs .

Then run variations of it...

  • Run ENTRYPOINT (printargs plus its options and append options from CMD)
$ docker run --rm printargs
1 2 3 4 5 6
  • Run ENTRYPOINT but replace CMD options with arguments on the command line:
$ docker run --rm printargs 7 8 9
1 2 3 7 8 9
  • Replace ENTRYPOINT and use options passed as arguments on the command line:
$ docker run --rm --entrypoint printargs printargs 7 8 9
7 8 9

But here's the one that might surprise you! 🤯

  • Replace ENTRYPOINT and expect to use default CMD options
$ docker run --rm --entrypoint printargs printargs

It's not 4 5 6 as you might have expected. Once you override the entrypoint, it's likely the rationale is that you're expected to supply optional arguments as well (it makes sense that once you replace the entrypoint, the default CMD options are probably no longer appropriate to use, so they're ignored).

@jonjohnsonjr
Copy link
Author

But here's the one that might surprise you! 🤯

Yes! This is what kept tripping me up. As was explained to me, the CMD depends on the ENTRYPOINT, because we're just supplying args to the entrypoint binary. If we replace the entrypoint, those args should be invalidated.

Your summary is perfect -- I couldn't understand why you would use one approach over the other, but it comes down to whether you treat a container as a process or an environment in which to run processes.

@subfuzion
Copy link

...it comes down to whether you treat a container as a process or an environment in which to run processes.

Nice! I think you condensed everything into the ideal one-line summary. I'm going to use that! 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment