goss-org / goss

Quick and Easy server testing/validation
https://goss.rocks
Apache License 2.0
5.61k stars 472 forks source link

Cannot write command tests without shell (sh) #870

Open berney opened 9 months ago

berney commented 9 months ago

Describe the bug

Goss command test will execute the command wrapped with a sh -c "<the command>". This assumes sh executable exists. When testing distroless containers with a single binary, goss cannot execute the binary.

Dockerfile

FROM scratch
ADD figlet /
ENTRYPOINT ["figlet"]

goss.yaml

Command: figlet: exit-status:
Error
    exec: "sh": executable file not found in $PATH

How To Reproduce

Create a single statically compiled binary, or a binary with the needed libraries. For me it is figlet.

command:
  figlet:
    exec: figlet test

Expected Behavior

Goss to directly execute (without a shell in the middle) figlet (arg 0) with single argument test (arg 1).

I think that goss giving a shell, should be configurable - and optional. Perhaps a new attribute shell that defaults to sh, and if explicitly set to nothing shell:, then goss will exec directly the command, rather than running it in a shell.

Actual Behavior

Goss attempts to execute sh, which doesn't exist, so fails.

Environment:

aelsabbahy commented 9 months ago

Not sure this is a bug to be honest.

The documentation should be clarified that Goss uses sh -c under the hood so things like pipes and redirects work properly. (E.g. grep xx < somefile | sort)

Perhaps there's a feature request in here to allow exec to workout without shell no-shell: true or something like that for situations like this.

berney commented 9 months ago

I'm going to raise a PR because this is blocking what I am trying to use goss for. Would you be open to that, and if so, which would you prefer:

1. Specify Shell

Allows specifying an alternative shell or specifying no shell.

Use Case 1: Straight Exec

command:
  figlet:
    exec: figlet test
    # no shell (straight exec)
    shell: ""

Use Case 2: Zsh

command:
  zshism:
    # Use zsh glob qualifier to show files modified in the last day
    exec: "echo *(.m-1)"
    shell: zsh

2. Can opt out of shell

In this case the command is either (by default) executed with sh -c , or can optionally be directly executed.

Use Case 1:

Use Case 1: Straight Exec

command:
  figlet:
    exec: figlet test
    # no shell (straight exec)
    # naming of this could be `no-shell`?
    shell: false

Use Case 2: Zsh

You can't override which shell is used, but you can just directly execute the shell.

command:
  zshism:
    shell: false
    # Use zsh glob qualifier to show files modified in the last day
    exec: |-
      zsh -c "echo *(.m-1)"

3. Only exec, if user wants a shell, they need to specify it

Use Case 1:

Use Case 1: Straight Exec

command:
  figlet:
    exec: figlet test

Use Case 2: Zsh

User wants to use a shell, so they specify it as part of the command to be executed.

command:
  zshism:
    # Use zsh glob qualifier to show files modified in the last day
    exec: |-
      zsh -c "echo *(.m-1)"

More thoughts

I like the simplicity of the third solution, but it is a breaking change.

The second solution is relatively simple and backwards compatible. Simple true/false for shell. If a user wants to use bash, or zsh etc, they can just write the full command like in the example I gave. This is simpler solution than first one because maybe some shells won't use -c for the command, and users might complain that some esoteric shell doesn't work as expected.

What do you think?

berney commented 9 months ago

I agree this is more a feature request. If it's possible to change the label?

aelsabbahy commented 9 months ago

I prefer option 2 for all the reasons you outlined above.

Also, since Goss has been around for ~8 years without this issue, opting out seems sensible, likely needed only by a few users.