Request classic confinement for tesseract-ignition

I used your plug format and incremented the version, then cleaned and rebuilt and now everything is working the same way your example does when executing the command below. Though after launching the app I still cannot browse to /var/lib/snapd/hostfs/opt/ros. Whats next?

sudo snap run --shell tesseract-ignition.tesseract-setup-wizard -c 'ls -l /var/lib/snapd/hostfs/opt/ros`

output:

mkdir: cannot create directory ‘/run/user/0’: Permission denied
realpath: '': No such file or directory
realpath: '': No such file or directory
realpath: '': No such file or directory
realpath: '': No such file or directory
realpath: '': No such file or directory
realpath: '': No such file or directory
realpath: '': No such file or directory
realpath: '': No such file or directory
total 12
drwxr-xr-x 9 root root 4096 Mar 25  2019 crystal
drwxr-xr-x 9 root root 4096 Jun  9 10:38 eloquent
drwxr-xr-x 7 root root 4096 Jun 27 09:06 melodic

Your app probably doesn’t have permission to list the contents of intermediate directories, but can you navigate there directly? I thought tesseract used this information to discover available meshes and offer them for loading, not that it depended on the user to hunt around the filesystem for meshes. The UX of the latter might be rough indeed under confinement.

I am able to navigate them myself once I get in to the ros directory. If I try to tab my way it does not work, I must first cd /var/lib/snapd/hostfs/opt/ros then I can navigate anywhere, but in between I get permission denied message.

After I went through the exercise above it gave me an idea. I can now access the files in the browser if I first manually past the /var/lib/snapd/hostfs/opt/ros and open directory, then I can navigate.

image

That will be the case most of the time, but the user could want to start with an existing one under /opt/ros and then save it locally under a different name which would require them to navigate. After what was learned is there a way to solve the navigation issue?

Also I assume I need to leverage a layout to map this directory to /opt/ros so the environment variable mapped form the host are correct?

I’m not sure how this works in apparmor, @jdstrand or @alexmurray can you give some more insight into this?

That’s one option, but layouts from the hostfs get a bit ugly quickly and aren’t well-tested. Instead I suggest looking at a slightly more complex version of my simple reproducer. Note that it includes essentially the same snapcraft.yaml plus one more app and the use of the catkin plugin, but it also includes its own custom catkin plugin with the sole purpose of rewriting the host’s $ROS_PACKAGE_PATH to be in /var/lib/snapd/hostfs instead. What do you think?

Looks good to me. Would this be something that could be pushed upstream? Also since I am using a custom plugin now of catkin, how hard would it be to add the skip keys functionality?

I think that could reasonably become an option you could set on the catkin plugin, sure. Although given that this use-case is not typical, I’d like to see another snap need it as well before we do that.

Not too hard, but it involves overriding private API so heads up that it might be a little fragile. Since rosdep install would install things on the host, snapcraft only uses rosdep to figure out what the dependencies are. You can see that happening here. The easiest way to skip specific keys would probably be to add the following method to your catkin plugin (untested, but should work):

def _resolve_package_dependencies(
    catkin_packages, dependency, catkin, rosdep, resolved_dependencies
):
    # Only continue if we weren't requested to skip this dependency
    if dependency not in self._skips:
        super()._resolve_package_dependencies(catkin_packages, dependency, catkin, rosdep, resolved_dependencies)

You can either hard-code your skips, or add them as a legit option by adding them to the schema following the established patterns.

Well I tried the plugin, but it does not appear to pull the environment. I get an empty HOST_ROS_PACKAGE_PATH.

I did the following

sudo snap run --shell tesseract-ignition.tesseract-setup-wizard
env | grep ROS
ROS_ETC_DIR=/snap/tesseract-ignition/x22/opt/ros/melodic/etc/ros
ROS_MASTER_URI=http://localhost:11311
ROS_VERSION=1
ROS_PYTHON_VERSION=2
ROS_PACKAGE_PATH=/snap/tesseract-ignition/x22/opt/ros/melodic/share
ROS_DISTRO=melodic
HOST_ROS_PACKAGE_PATH=
ROS_HOME=/root/snap/tesseract-ignition/x22/ros

Did you try with the reproducer? As a sanity check, you sourced a workspace in /opt/ros/ before opening that shell? I tested that reproducer before I shared it, so we’re missing something here.

So it does work using the app just not the shell.

The new plugin needs to be slightly modified because it prepends all paths where it should only prepend paths beginning with /opt/ros.

I am looking at how to modify the plugin you provided, but not very familiar with python. Do you mind explaining what the line of code below is doing and why the use of textwrap?

        # At the end of the process, rewrite the host's ROS_PACKAGE_PATH
        # (if any) to point to the right place in the snap
        env[-1] = env[-1] + "\n\n" + textwrap.dedent("""\
            set -f; IFS=:
            for path in $HOST_ROS_PACKAGE_PATH; do
                REWRITTEN_HOST_ROS_PACKAGE_PATH="${REWRITTEN_HOST_ROS_PACKAGE_PATH:+$REWRITTEN_HOST_ROS_PACKAGE_PATH:}/var/lib/snapd/hostfs${path}"
            done
            set +f; unset IFS
            ROS_PACKAGE_PATH="${REWRITTEN_HOST_ROS_PACKAGE_PATH:+$REWRITTEN_HOST_ROS_PACKAGE_PATH:}$ROS_PACKAGE_PATH"
            """)

Sure thing! textwrap.dedent removes common leading whitespace from a given block of text. The reason it’s used here is because it ends up getting written to a shell script (you’ll find it in $SNAP/snap/command-chain/) and having indentation all over the place makes such scripts impossible to decipher and can lead to errors. For the change you’re looking to make, let’s reduce that snippet to just the shell code, and you can paste it into the dedent block. All you need is a quick check in the loop to see if the path starts with /opt/ros/. This is untested, but should work:

set -f; IFS=:
for path in $HOST_ROS_PACKAGE_PATH; do
    if [ "$path" != "${path#/opt/ros/}" ]; then
        REWRITTEN_HOST_ROS_PACKAGE_PATH="${REWRITTEN_HOST_ROS_PACKAGE_PATH:+$REWRITTEN_HOST_ROS_PACKAGE_PATH:}/var/lib/snapd/hostfs${path}"
    fi
done
set +f; unset IFS
ROS_PACKAGE_PATH="${REWRITTEN_HOST_ROS_PACKAGE_PATH:+$REWRITTEN_HOST_ROS_PACKAGE_PATH:}$ROS_PACKAGE_PATH"

Thank you for the explanation. I have everything working and I made one tweak to keep other packages in there local workspace.

env[-1] = env[-1] + "\n\n" + textwrap.dedent("""\
            set -f; IFS=:
            for path in $HOST_ROS_PACKAGE_PATH; do
                if [ "$path" != "${path#/opt/ros/}" ]; then
                    REWRITTEN_HOST_ROS_PACKAGE_PATH="${REWRITTEN_HOST_ROS_PACKAGE_PATH:+$REWRITTEN_HOST_ROS_PACKAGE_PATH:}/var/lib/snapd/hostfs${path}"
                else
                    REWRITTEN_HOST_ROS_PACKAGE_PATH="${REWRITTEN_HOST_ROS_PACKAGE_PATH:+$REWRITTEN_HOST_ROS_PACKAGE_PATH:}${path}"
                fi
            done
            set +f; unset IFS
            ROS_PACKAGE_PATH="${REWRITTEN_HOST_ROS_PACKAGE_PATH:+$REWRITTEN_HOST_ROS_PACKAGE_PATH:}$ROS_PACKAGE_PATH"
            """)
1 Like

Awesome! You should add the home and removable-media plugs so that you will be able to access workspaces in there, then.

As there seems to be good progress getting tesseract-ignition working with strict confinement, I am removing this request for classic confinement from our internal queue.

1 Like

So I tried to push the latest but it says I still need approval because I am using the interface system-files; should I create another issue?

Yes, use of system-files requires approval and so a subsequent forum post requesting this (within the store-requests category) should be created detailing this requested access.

(feel free to refer to this topic for extra background, though)

Thank you all for all of the help. I will create a new one an reference this one.

A new thread has been create here.