Strict confinement with completer that runs the app (Go Cobra)

Hello!

I’m trying to add completions to my Go CLI application packaged as a Snap. The completer fails, because it cannot access the application itself. But it needs to run the application, because the completion is implemented by calling “<app> __complete”, so most of the completion logic just lives in the main application itself.

I’m using Cobra for the CLI framework (https://github.com/spf13/cobra), which is just implemented this way.

I’ve followed the guide Debugging tab completion, and found that it’s failing on the following:

$ snap run --command=complete klock.kubectl-klock 9 9 20 1 ' ' 'klock.kubectl-klock ' klock.kubectl-klock ''
# /...snip.../
+ __klock.kubectl-klock_debug '+++ klock.kubectl-klock __complete '\'''\''
/snap/klock/x1/bin/klock.kubectl-klock.completion.bash: line 58: klock.kubectl-klock: command not found'
+ [[ -n /dev/stderr ]]
+ echo '+++ klock.kubectl-klock __complete '\'''\''
/snap/klock/x1/bin/klock.kubectl-klock.completion.bash: line 58: klock.kubectl-klock: command not found'
+++ klock.kubectl-klock __complete ''
/snap/klock/x1/bin/klock.kubectl-klock.completion.bash: line 58: klock.kubectl-klock: command not found

Now, just switching the Snap’s confinement to classic solves this issue. But would be nice to not need this.

The snap.yaml file looks like so:

name: klock
version: 0.4.0-SNAPSHOT-f573ee2
summary: Kubectl plugin that watches resources
description: |
  Kubectl plugin that watches resources and prints them in a human-readable manner.
  Very similar to `kubectl get`, but with live updates.
base: core22
license: GPL-3.0-or-later
grade: stable
confinement: strict
architectures:
  - amd64
apps:
  kubectl-complete:
    command: kubectl_complete-klock
    aliases:
      - kubectl_complete-klock
  kubectl-klock:
    command: kubectl-klock
    aliases:
      - kubectl-klock
    completer: bin/klock.kubectl-klock.completion.bash
    plugs:
      - home
      - network

Any ideas?

My first guess would be that the command inside the snap is kubectl-klock and not klock.kubectl-klock, so the completion script probably needs a tweak. Failing that, I’d try running a shell inside the snap and trying to get the completion script to work, and then follow the pipeline out checking that each successive stage is getting the info it needs and passing on the info the next stage needs.

Right on the money! Well, almost at least.

The binary sure was just called kubectl-klock inside the snap, so I had to use that.

That path was not in $PATH though, but that was easy enough to just use the $SNAP variable.

I was reluctant to edit this completion script though, as it is generated by Cobra, the CLI framework I’m using. But I’ll just bite the bullet and go with that approach.

If anyone else finds this thread looking for help specifically with Cobra, this is the changeset I had to make:

--- a/bin/klock.kubectl-klock.completion.bash
+++ b/bin/klock.kubectl-klock.completion.bash
@@ -25,6 +25,16 @@ __klock.kubectl-klock_get_completion_results() {
     args=("${words[@]:1}")
     requestComp="${words[0]} __complete ${args[*]}"

+    ##########################
+    ## Custom fix for snap  ##
+    ##########################
+    if [[ -x "$SNAP/kubectl-klock" ]]; then
+        requestComp="$SNAP/kubectl-klock __complete ${args[*]}"
+    fi
+    ##########################
+    ##  End of custom fix   ##
+    ##########################
+
     lastParam=${words[$((${#words[@]}-1))]}
     lastChar=${lastParam:$((${#lastParam}-1)):1}
     __klock.kubectl-klock_debug "lastParam ${lastParam}, lastChar ${lastChar}"

And now completion works! :smiley:

Thanks for the help @chipaca!