How to sideload a snap with snapd REST API


#1

On the page of snapd REST API, it shows how to sideload a snap by sending request.

However, I am not clear what is the value for the key of the filename. The example shows as the following:
Content-Disposition: form-data; name=“snap”; filename=“hello-world_27.snap”

<20480 bytes of snap file data>
–foo

I am assuming I need to read the snap file as a byte stream and decode it as a string. Then use this string to replace <20480 bytes of snap file data>.

I tried to decode it as “utf-8”, I got the following error:
UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xf8 in position 9: invalid start byte

Can anyone tell me I am on the right path and how to do it?

Thanks in advance!


#2

that error is in your own code, which apparently is in python, so without knowing exactly what you’re doing it’s nigh impossible to tell what you’re doing wrong.

What you need to be doing is POSTing a multipart form. For example,


#3

Thanks for quick response.

Here is my python code:
#!/usr/bin/env python3

import socket
import sys
import time
import os
import base64

def installsnaps():
    clientsocket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    clientsocket.connect("/run/snapd.socket")

    # open the snap files
    print("install gotop")
    snap_file_name = "gotop_0.1_amd64.snap"
    
    with open(os.environ["SNAP_COMMON"] + "/so/" + snap_file_name,'rb') as f:
        file_data = f.read()
    bytes_string = file_data.decode("utf-8")
    print("The size of the data: " + len(bytes_string))
    clientsocket.sendall(
"""POST http://localhost/v2/snaps HTTP/1.1
Host: localhost
User-Agent: agent
Content-Type: multipart/form-data; boundary=foo
Accept: */*
Content-Length: {length}

--foo
Content-Disposition: form-data; name="dangerous"

true
--foo
Content-Disposition: form-data; name="snap"; filename="gotop_0.1_amd64.snap"

{file_data}
--foo
""".format(length=str(len(bytes_string)), file_data=bytes_string).encode("utf-8")
    )
   time.sleep(10)

   return clientsocket.recv(8192)

if __name__ == '__main__':
    installsnaps()

#4

I’d suggest that, unless you know what you’re doing, you don’t try to write it by hand. Use libraries like requests (and requests-unixsocket) to do the work for you.


#5

Thanks for the suggestion. I try it now.


#6

Thanks. Your suggestion works.