How to remote URL POST in remove hook?

Is there a good tutorial out there for making a POST in a remove hook? Or does anyone have any suggestions on how to go about this?

I’ve tried using curl and wget, but I keep getting the message that they are both “not found,” even though the hook code runs fine on the machine when called directly through the terminal.

You likely need to adjust the $PATH in your hook, as depending how you’re building your snap, snapcraft may not be generating a command-chain for your hook, so it doesn’t have the $PATH setup that snapcraft does for the snap apps.

I’m fairly new to Linux and very new to Snapcraft, and I’m having difficulty understanding exactly what you’re saying or how to implement it, or even what exactly I would Google to find out more.

How would I make sure that my hook has the same $PATH setup as the app? Or do I even want it to be the same?

Or, what kind of code do I need to run in my bash remove hook to get wget or curl to actually work?

Can you show your snapcraft.yaml?

Sure. Thanks for looking at it with me!

Fair warning, I don’t know what all of the stuff in my YAML file really does, so it might be a bit of a mess. I got it to a point where the app was working as I like, and once that happened I decided not to mess with much else in here, even if I didn’t know what it was, with the intent of possibly cleaning it up later if I can figure out what isn’t needed.

I’m still definitely having trouble with the hooks though. The only hook I really want is the remove hook, but I have an install hook as well right now because it seems easier to troubleshoot that than the remove hook.

YAML:

name: myapp
base: core18
version: '0.0.1'
title: 'MyApp'
summary: 'MyApp does stuff.'
icon: 'myapp.svg'
grade: devel
confinement: strict

plugs:
    network:
    network-bind:
    desktop:
    desktop-legacy:
    x11:
    unity7:
    pulseaudio:
    home:
    gsettings:

hooks:
    install:
        plugs: [network, network-bind, home]
    remove:
        plugs: [network, network-bind, home]

apps:
    myapp:
        command: desktop-launch python3 $SNAP/main.py
        plugs:
            - network
            - network-bind
            - desktop
            - desktop-legacy
            - x11
            - unity7
            - pulseaudio
            - home
            - gsettings
        desktop: usr/share/applications/myapp.desktop
        autostart: myapp.desktop

parts:
    desktop-gtk3:
        build-packages:
            - build-essential
            - libgtk-3-dev
        make-parameters:
            - FLAVOR=gtk3
        plugin: make
        source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
        source-subdir: gtk
        stage-packages:
            - libxkbcommon0
            - ttf-ubuntu-font-family
            - dmz-cursor-theme
            - light-themes
            - adwaita-icon-theme
            - gnome-themes-standard
            - shared-mime-info
            - libgtk-3-0
            - libgdk-pixbuf2.0-0
            - libglib2.0-bin
            - libgtk-3-bin
            - unity-gtk3-module
            - libappindicator3-1
            - locales-all
            - xdg-user-dirs
            - ibus-gtk3
            - libibus-1.0-5
            - fcitx-frontend-gtk3
            - x11-utils
            - libcanberra-gtk-module
            - libcanberra-gtk3-module
    copy-stuff:
        plugin: dump
        source: .
    copy-desktop-icon-etc:
        plugin: dump
        source: snap/gui
        organize:
            myapp.png: usr/share/applications/myapp.png
            myapp.desktop: usr/share/applications/myapp.desktop
    myapp:
        plugin: python
        python-version: python3
        source: .
        build-packages:
            - curl
        stage-packages:
            - libssl-dev
            - libjpeg-dev
            - libtiff-dev
            - libsdl1.2-dev
            - libnotify-dev
            - freeglut3
            - ibus-gtk3
            - libwebkitgtk-3.0-0
            - zlib1g
            - libsm6
            - libpulse0
            - libslang2
            - libsdl1.2debian
            - wmctrl
            - gir1.2-gtk-3.0
            - gir1.2-appindicator3-0.1
            - python3-gi
            - gir1.2-wnck-3.0
            - libcanberra-gtk-module
            - libcanberra-gtk3-module
            - ttf-ubuntu-font-family
            - x11-utils
        requirements:
            - /root/project/requirements.txt

Also, I checked the $PATH variable while my install hook is running and this is what I got:
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

That means curl will only be available when building, you need to stage-packages it instead.

1 Like

I added curl to stage packages for both of the parts that have stage packages. That made no difference. I’m still getting:
curl: not found

I also already had curl in the requirements.txt. I think the stage/build packages and requirements.txt are only relevant for the parts they’re listed under, not for the hooks. But I may be mistaken.

stage-packages are simply putting stuff inside your snap, parts are only relevant during build but not for the resulting binary or hooks, if some stage-packages entry pulled curl into your snap it should be available in general …

note though that you probably want to simply call it as $SNAP/usr/bin/curl from your hook to avoid having to hack around with PATH variables …

I kept curl in stage packages as well as build packages as well as requirements.txt and switched the curl call over to:
$SNAP/usr/bin/curl -X POST -d "mydata" "https://myapp.url/mypath"

I am still seeing this when the install hook runs:
/snap/myapp/x1/meta/hooks/install: curl: not found

I also created a whole new snap based on this tutorial:

I then added a bash install and remove hook with one curl command in each, added curl to both stage and build packages of the gnu-hello part in the YAML, and got the exact same error on Manjaro, where I have been testing my installs:
/snap/hello/x1/meta/hooks/install: curl: not found

There are two ways to get hooks into the snap:

  1. Put them in snap/hooks/ and run snapcraft
  2. Install them from a part into $SNAPCRAFT_PART_INSTALL/snap/hooks/

If you use (1), you don’t get any help from snapcraft (no $PATH, no $LD_LIBRARY_PATH, etc.). If you use (2), you do. I don’t know for sure, but given your snapcraft.yaml and the issues you’re hitting, I suspect you’re using (1). You might want to consider (2).

How do I use (2)? Is there a thorough tutorial somewhere or plain documentation for us beginners on this?

Thank you for the suggestion!

I don’t know of one off the top of my head, but here’s a trivial example of a part that installs the remove hook in a way that you get an environment from snapcraft:

# ...
parts:
  my-part:
    plugin: nil
    override-build: |
      cat > remove << EOF
      #!/bin/sh
      echo "I'm the remove hook"
      EOF
      install -D remove $SNAPCRAFT_PART_INSTALL/snap/hooks/remove

Using this tutorial for a simple, basic snap:

…I set up an install and a remove hook per your instructions @kyrofa , with one curl in each of them. When I install the snap via the terminal, it appears as though the hooks are running, but I am not seeing any of the echo commands displayed in my terminal, and the curl calls do not appear to be accomplishing anything on the remote server like they should. The hooks are also no longer producing error messages like, at least the install hook, used to.

Is there a good method for troubleshooting hooks? Shouldn’t echos print to the terminal when the hook runs?

Here is my YAML, much simpler than my original YAML, and the YAML is really the entire snap this time. Did I do everything correctly? Did I miss anything?

name: hello
base: core18
version: '0.1'
summary: Single-line elevator pitch for your amazing snap
description: |
  This is my-snap's description. You have a paragraph or two to tell the
  most important story about your snap. Keep it under 100 words though,
  we live in tweetspace and your description wants to look good in the snap
  store.

grade: devel
confinement: strict

hooks:
    install:
        plugs: [network, network-bind, home]
    remove:
        plugs: [network, network-bind, home]

parts:
    gnu-hello:
        source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
        plugin: autotools
        stage-packages:
            - curl
        build-packages:
            - curl
    install-hook:
        plugin: nil
        build-packages:
            - curl
        stage-packages:
            - curl
        override-build: |
            cat > myfile << EOF
            #!/bin/sh
            echo "I'm the install hook (A)"
            curl -X POST -H 'Content-Type: text/xml' -d "<soap:Envelope [stuff here] soap:Envelope>" "https://webservice.url/blahblah"
            echo "I'm the install hook (B)"
            EOF
            install -D myfile $SNAPCRAFT_PART_INSTALL/snap/hooks/install
    remove-hook:
        plugin: nil
        build-packages:
            - curl
        stage-packages:
            - curl
        override-build: |
            cat > myfile << EOF
            #!/bin/sh
            echo "I'm the remove hook (A)"
            curl -X POST -H 'Content-Type: text/xml' -d "<soap:Envelope [stuff here] soap:Envelope>" "https://webservice.url/blahblah"
            echo "I'm the remove hook (B)"
            EOF
            install -D myfile $SNAPCRAFT_PART_INSTALL/snap/hooks/remove

What you’re seeing (or rather, the fact that you see nothing) is normal. Hooks that don’t exit with errors typically have their output hidden. However, once the snap is installed you can run the hook manually like so:

$ snap run --hook <hook name> <snap name>

Then you’ll see your output.

In case that doesn’t work for some reason (e.g. you’re trying to debug an install hook that’s failing and thus won’t let the snap install), I suggest making it an app temporarily (by either adding an app with command: meta/hooks/<hook name> or moving it elsewhere so it’s not a hook at all) with appropriate plugs so you can run it manually.

2 Likes

Thank you @kyrofa! That worked well. Looks like my problem now is with a variable that I am trying to pass into the curl call… and no longer with sending the curl itself, so my original problem has been solved!

Having said that, any tips on passing variables around in a bash install-hook or remove-hook that’s created via the process you outlined above? It does seem to work fine in the original bash file run in the terminal, just not when run in the snap-hook context. The variable is just totally empty when used in the curl (or wget) call.

parts:
    gnu-hello:
        source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
        plugin: autotools
        stage-packages:
            - curl
        build-packages:
            - curl
    install-hook:
        plugin: nil
        build-packages:
            - wget
        stage-packages:
            - wget
        override-build: |
            cat > myfile << EOF
            
            #!/bin/sh
            
            echo "I'm the install hook (A)"
            
            myvariable="70000997"
            
            echo "I'm the install hook (B)"
            
            wget --method=POST --body-data="<soap:Envelope...${myvariable}...soap:Envelope>" --header="Content-Type: text/xml; charset=utf-8" "https://webservice.url/blahblah"
            
            echo "I'm the install hook (C)"
            
            EOF
            
            install -D myfile $SNAPCRAFT_PART_INSTALL/snap/hooks/install

Ah, you’re being bit by good old shell programming stuff now. Check this out:

$ cat << EOF | /bin/sh
location="world"
echo "hello $location"
EOF
hello

Huh? The problem here is when variables are expanded. As-written, your variable is expanded when creating the heredoc. To clarify, check this out:

$ location="mars"
$ cat << EOF | /bin/sh
location="world"
echo "hello $location"
EOF
hello mars

To avoid expanding that soon, you either need to escape the $location with \$location, or need to quote the heredoc word, like so:

$ cat << 'EOF' | /bin/sh
location="world"
echo "hello $location"
EOF
hello world

However, you might want to consider just writing your hook in a file that you can check into your VCS instead of using the quick hacky heredoc that I gave you. Then you can run shellcheck on it as well, for example. For reference, look how we do it in the Nextcloud snap. All you need to do is get the hooks in the right spot and snapcraft will do the rest. How you get them there is up to you.

2 Likes

Also, in case I’m coming off as dismissive, I want to be clear that, in an ideal world, hooks would be far more declarative in snapcraft than relying on some “put the files here someplace” convention. This feature is hard to document and even harder to understand. Thanks for sticking with it!

2 Likes

That worked! Woohoo!

FWIW, you did not come off as dismissive. You’ve been very helpful!

Whenever I ask a question on any forum (particularly those that particularly involve Linux or open source languages for some reason) I always expect people to respond with RTFM. Which is never helpful.

To everyone here’s credit, I have not seen any responses like that – to me or anyone else.

1 Like

How did I not know this?! I always escaped the $ with a \ which gets somewhat messy when you’re doing it inside another heredoc!

1 Like

Yeah all the info is in the docs. I often use the leading hyphen as well so I can indent my heredocs, thereby satisfying my OCD and proving that tabs are really the superior method of indentation. As if that needed proving.