Dealing with `deletefile` when writing a backend from scratch

I'm now writing Internet Archive support from the scratch, as adding it as S3 remote will cause rclone to be confused (and impression of degraded behavior).

Now, most of the functionalities including hash check work, except rclone deletefile.
Once I delete files using rclone delete or mount IA item (consider it "bucket" on S3) using rclone mount, files can be deleted, without any problem.

Here's the log when I tried removing test file uploaded at IA:

% ./rclone -vvP deletefile ia-test:lesmi-rclone-test/rclone
<7>DEBUG : rclone: Version "v1.59.0-beta.12.9c9c8d4.internetarchive" starting with parameters ["./rclone" "-vvP" "deletefile" "ia-test:lesmi-rclone-test/rclone"]
<7>DEBUG : rclone: systemd logging support activated
<7>DEBUG : Creating backend with remote "ia-test:lesmi-rclone-test/rclone"
<7>DEBUG : Using config file from "/home/lesmi/.config/rclone/rclone.conf"
2022-04-14 23:09:17 ERROR : Attempt 1/3 failed with 1 errors and: ia-test:lesmi-rclone-test/rclone is a directory or doesn't exist
2022-04-14 23:09:17 ERROR : Attempt 2/3 failed with 1 errors and: ia-test:lesmi-rclone-test/rclone is a directory or doesn't exist
2022-04-14 23:09:17 ERROR : Attempt 3/3 failed with 1 errors and: ia-test:lesmi-rclone-test/rclone is a directory or doesn't exist
Transferred:              0 B / 0 B, -, 0 B/s, ETA -
Errors:                 1 (retrying may help)
Elapsed time:         0.0s
<7>DEBUG : 2 go routines active
Failed to deletefile: ia-test:lesmi-rclone-test/rclone is a directory or doesn't exist

You can find the test file named rclone here: lesmi-rclone-test directory listing (downloading it is not recommended)

I'd like you to give me some advise to get it work.

Here's my development code: rclone/internetarchive.go at internetarchive · Lesmiscore/rclone · GitHub (I'll revert some of core modification, squash to set a valid commit message, and more before making a PR, please ignore for now)

hello and welcome to the forum,

IA would be a great addition to rclone backends.

have you seen?
https://forum.rclone.org/t/add-support-for-internet-archive/23738

I did, but it gives me nothing to write a backend from the scratch which is what I'm doing.

Nice! I noticed there were lots of interesting hash formats it supports.

I think your problem is probably to do with mis-handling of the fs.ErrorIsFile error returned from NewFs. It is the backends job to identify files and return that error along with the pointer to the directory above.

I recommend the procedure outlined here: rclone/CONTRIBUTING.md at master · rclone/rclone · GitHub for writing a new backend.

In particular this section

Unit tests

  • Create a config entry called TestRemote for the unit tests to use
  • Create a backend/remote/remote_test.go - copy and adjust your example remote
  • Make sure all tests pass with go test -v

If you keep chipping away at errors in go test -v. Fix the first error, then the next and before you know it you'll have a completely working backend! It sounds like you've got to the stage that the tests should mostly work now.

The tests have all the detail needed to make sure that you'll make a first class rclone backend and that all the rclone commands will work!

That is the way I make new backends anyway - let the tests guide you as to what needs fixing.

After you've got that working, your backend is nearly ready - the test_all will winkle out any last incompatibilities.

I gave the code a quick once over.

  • user agent shouldn't be necessary. lib/rest sets it for you to something sensible and there is a global flag --user-agent to override.
  • hashes you've specified MD5,SHA1,SHA256 here but the IAFile Object has CRC32, MD5, SHA1
  • PutStream is very useful if you can implement it as it enables uploads from rclone mount with --vfs-cache-mode off without having to buffer to disk

Looking great! Keeping working through the tests and let me know if there is a problem you can't fix.

Thank you for your response.

I think your problem is probably to do with mis-handling of the fs.ErrorIsFile error returned from NewFs. It is the backends job to identify files and return that error along with the pointer to the directory above.

I see the problem at NewFs code. That makes sense.

user agent shouldn't be necessary. lib/rest sets it for you to something sensible and there is a global flag --user-agent to override.

Removed User-Agent as IAS3 only mandates it, no requirement for its format.

hashes you've specified MD5,SHA1,SHA256 here but the IAFile Object has CRC32, MD5, SHA1

Oops, I didn't notice I misenumerated hashes. Keys from IAFile object is correct.

PutStream is very useful if you can implement it as it enables uploads from rclone mount with --vfs-cache-mode off without having to buffer to disk

Sadly it can't be done. (I pushed the code but will remove it in few hours)
IAS3 doesn't accept any requests without Content-Length header.
Their official IAS3 client dumps stdin to a temporary file to "stream" file to the server, for your reference.

I got an another question. The backend wrap nothing, and I'm not implementing Command feature. So is this okay?

$ go test -v backend/internetarchive/internetarchive_test.go
=== RUN   TestIntegration
    fstests.go:418: Using remote "TestIA:"
=== RUN   TestIntegration/FsCheckWrap
    fstests.go:459: Not a wrapping Fs
=== RUN   TestIntegration/FsCommand
    fstests.go:487: No commands in this remote
=== RUN   TestIntegration/FsRmdirNotFound
=== CONT  TestIntegration
    fstests.go:510:
                Error Trace:    fstests.go:510
                                                        internetarchive_test.go:13
                Error:          Received unexpected error:
                                optional feature not implemented
                Test:           TestIntegration
--- FAIL: TestIntegration (2.86s)
    --- SKIP: TestIntegration/FsCheckWrap (0.00s)
    --- SKIP: TestIntegration/FsCommand (0.00s)
    --- PASS: TestIntegration/FsRmdirNotFound (0.00s)
FAIL
FAIL    command-line-arguments  2.963s
FAIL

All good :slight_smile:

If it can't be done, it can't be done! Rclone can work around just fine so don't sweat it

This error isn't from the Wrapping test, it is the test trying to run Mkdir - check out fstests.go:510

That is because of this line

If it is anything like S3 you should just return nil rather than fs.ErrorNotImplemented - same with Rmdir.

Thank you. I fixed it.

1 Like

When you are ready for your PR review I'll want to know the results of all the integration tests.

I'll also want to stick it in the daily integration tester if possible - is it easy to get a set of credentials for testing purposes?

When you are ready for your PR review I'll want to know the results of all the integration tests.

There are few more problems on server-side:

  • Reflecting files to download endpoint always delays, because of how it processes files. It will confuse test cases even though files are properly uploaded.
  • Modified time is taken by server's choice, so there will always be discrepancy between local and IA backend. We can add custom metadata fields upon upload, but it will bloat up top page of the Item. ("top page" here means pages like this: lesmi-rclone-test : Free Download, Borrow, and Streaming : Internet Archive where lesmi-rclone-test is the name of this Item)

The former is clearly documented:

  • When a file is added to an Item, it is staged in temporary storage and ingested via the Archive’s content management system. This can take some time.

Latter may be workaround by using Item Metadata API: Write — Internet Archive item APIs 3.0.0 documentation which allows adding metadata to one file, but we need to wait for the upload process to finish to use this.

But totally I'm ready for review, except that it never passes tests :sweat_smile:

I'll also want to stick it in the daily integration tester if possible - is it easy to get a set of credentials for testing purposes?

Yes. Register at archive.org and get S3 credentials at Internet Archive S3-Like API Keys . You won't be charged on any operations, including upload, download, and listing.

How long is the delay?

The rclone test suite can deal with "eventual consistency" delays of a few 10s of seconds.

Experiment with the -listretries parameter to go test -v

Maybe it would be best to start without adding metadata? In that case you set the Precision returned by the backend to fs.ModTimeNotSupported then the test cases won't moan about the differing times.

So you can't add metadata to a file as it is being uploaded?

Apparently I've done that before! What is the etiquette for creating and uploading things like the rclone integration test directory?

Usually less than a second for smaller files used in tests, but it can take up to more than a day for very large files. (>800GB or so)

Yes, it can wait in this case. But fails with Modification time difference too big

Yes. IAS3 requires to amend it later.

You shouldn't let it make a lot of Items with tests, as cleanup is currently not implemented. (note to myself: make_dark)
Too high rate of upload requests could cause you banned.

P.S. attached logs from go test -v
tests.log (48.7 KB)

Fix that by returning from Precision the value fs.ModTimeNotSupported

Keep going with the test suite! It is very fussy but we've managed to make 40 backends pass it so I think you'll get there with internetarchive :slight_smile:

That's a weird workaround to me... anyway, I did it

Thank you

It means that the modification times from the backend shouldn't be used for syncing as they can't be set.

Lets get this working first and then we can think about whether we should be adding metadata to the objects or not to support the modified time. I think probably not as the internet archive isn't a general purpose backup but I might be wrong!

:white_flag:
tests.log (9.7 KB)

edit: part of the log when it was "working" (rescued from console log)

=== RUN   TestIntegration/FsMkdir/FsEncoding/trailing_HT
    fstests.go:674: testing "trailing HT␉"
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:204
                                                        fstests.go:212
                                                        fstests.go:217
                                                        fstests.go:680
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsEncoding/trailing_HT
=== RUN   TestIntegration/FsMkdir/FsEncoding/trailing_VT
    fstests.go:674: testing "trailing VT␋"
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:204
                                                        fstests.go:212
                                                        fstests.go:217
                                                        fstests.go:680
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsEncoding/trailing_VT
=== RUN   TestIntegration/FsMkdir/FsEncoding/trailing_dot
    fstests.go:674: testing "trailing dot."
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:204
                                                        fstests.go:212
                                                        fstests.go:217
                                                        fstests.go:680
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsEncoding/trailing_dot
=== RUN   TestIntegration/FsMkdir/FsEncoding/invalid_UTF-8
    fstests.go:674: testing "invalid utf-8\xfe"
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:204
                                                        fstests.go:212
                                                        fstests.go:217
                                                        fstests.go:680
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsEncoding/invalid_UTF-8
=== RUN   TestIntegration/FsMkdir/FsEncoding/URL_encoding
    fstests.go:674: testing "test%46.txt"
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:204
                                                        fstests.go:212
                                                        fstests.go:217
                                                        fstests.go:680
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsEncoding/URL_encoding
=== RUN   TestIntegration/FsMkdir/FsNewObjectNotFound
=== RUN   TestIntegration/FsMkdir/FsPutError
=== RUN   TestIntegration/FsMkdir/FsPutZeroLength
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:248
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsPutZeroLength
=== RUN   TestIntegration/FsMkdir/FsOpenWriterAt
    fstests.go:747: FS has no OpenWriterAt interface
=== RUN   TestIntegration/FsMkdir/FsChangeNotify
    fstests.go:783: FS has no ChangeNotify interface
=== RUN   TestIntegration/FsMkdir/FsPutFiles
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:204
                                                        fstests.go:212
                                                        fstests.go:217
                                                        fstests.go:868
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsPutFiles
=== RUN   TestIntegration/FsMkdir/FsPutChunked
    fstests.go:1807: *internetarchive.Fs does not implement SetUploadChunkSizer
=== RUN   TestIntegration/FsMkdir/FsUploadUnknownSize
=== RUN   TestIntegration/FsMkdir/FsUploadUnknownSize/FsPutUnknownSize
=== RUN   TestIntegration/FsMkdir/FsUploadUnknownSize/FsUpdateUnknownSize
    fstests.go:132: Sleeping for 1s for findObject eventual consistency: 1/3 (object not found)
    fstests.go:132: Sleeping for 1.5s for findObject eventual consistency: 2/3 (object not found)
    fstests.go:132: Sleeping for 2.25s for findObject eventual consistency: 3/3 (object not found)
    fstests.go:136: 
                Error Trace:    fstests.go:136
                                                        fstests.go:204
                                                        fstests.go:212
                                                        fstests.go:217
                                                        fstests.go:1935
                Error:          Received unexpected error:
                                object not found
                Test:           TestIntegration/FsMkdir/FsUploadUnknownSize/FsUpdateUnknownSize
=== RUN   TestIntegration/FsMkdir/FsRootCollapse
lesmi-rclone-test/rclone-test-zacahuj0gidisoy8xoqokoz3/ 0
=== RUN   TestIntegration/FsShutdown
    fstests.go:2003: Shutdown method not supported
--- FAIL: TestIntegration (193.58s)
    --- SKIP: TestIntegration/FsCheckWrap (0.00s)
    --- SKIP: TestIntegration/FsCommand (0.00s)
    --- SKIP: TestIntegration/FsRmdirNotFound (0.00s)
    --- PASS: TestIntegration/FsString (0.00s)
    --- PASS: TestIntegration/FsName (0.00s)
    --- PASS: TestIntegration/FsRoot (0.00s)
    --- PASS: TestIntegration/FsRmdirEmpty (0.00s)
    --- FAIL: TestIntegration/FsMkdir (192.87s)
        --- PASS: TestIntegration/FsMkdir/FsMkdirRmdirSubdir (0.48s)
        --- PASS: TestIntegration/FsMkdir/FsListEmpty (0.16s)
        --- PASS: TestIntegration/FsMkdir/FsListDirEmpty (0.19s)
        --- PASS: TestIntegration/FsMkdir/FsListRDirEmpty (0.15s)
        --- PASS: TestIntegration/FsMkdir/FsListDirNotFound (0.16s)
        --- PASS: TestIntegration/FsMkdir/FsListRDirNotFound (0.16s)
        --- FAIL: TestIntegration/FsMkdir/FsEncoding (161.93s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/control_chars (18.29s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/dot (7.27s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/dot_dot (7.67s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/punctuation (7.40s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/leading_space (8.51s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/leading_tilde (7.19s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/leading_CR (7.23s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/leading_LF (10.11s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/leading_HT (7.26s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/leading_VT (7.39s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/leading_dot (8.35s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/trailing_space (8.44s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/trailing_CR (7.64s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/trailing_LF (7.73s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/trailing_HT (8.99s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/trailing_VT (8.11s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/trailing_dot (8.76s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/invalid_UTF-8 (7.46s)
            --- FAIL: TestIntegration/FsMkdir/FsEncoding/URL_encoding (7.97s)
        --- PASS: TestIntegration/FsMkdir/FsNewObjectNotFound (1.38s)
        --- PASS: TestIntegration/FsMkdir/FsPutError (0.46s)
        --- FAIL: TestIntegration/FsMkdir/FsPutZeroLength (7.12s)
        --- SKIP: TestIntegration/FsMkdir/FsOpenWriterAt (0.00s)
        --- SKIP: TestIntegration/FsMkdir/FsChangeNotify (0.00s)
        --- FAIL: TestIntegration/FsMkdir/FsPutFiles (7.06s)
        --- SKIP: TestIntegration/FsMkdir/FsPutChunked (0.00s)
        --- FAIL: TestIntegration/FsMkdir/FsUploadUnknownSize (12.40s)
            --- PASS: TestIntegration/FsMkdir/FsUploadUnknownSize/FsPutUnknownSize (0.39s)
            --- FAIL: TestIntegration/FsMkdir/FsUploadUnknownSize/FsUpdateUnknownSize (12.00s)
        --- PASS: TestIntegration/FsMkdir/FsRootCollapse (0.41s)
    --- SKIP: TestIntegration/FsShutdown (0.00s)
FAIL
FAIL    command-line-arguments  193.591s
FAIL

This is rclone testing which file names are saved properly.

You need to adjust the encoding to make these tests pass - eg add RightCrLfHtVt to the encoding to escape trailing Vt and Ht

You should probably take all the things out of the encoding and add them in one by one to make the TestIntegration/FsMkdir/FsEncoding tests pass.

Back here from long silence to have a break.

I thought you're right for here, and I was digging around there. But one thing I noticed was the files are remaining, even though the test cases delete them. Since files are correctly created and displayed on website, I doubt there's a huge problem on encoding.

Am I doing wrong in my code? fixed

ref. lesmi-rclone-test directory listing (all files remain, probably URL encoding is not created)

Okay, fixed some inconsistencies in my code. Let me rerun tests again.

Another observation: semicolons aren't accepted for filenames, but directories. Currently rclone has no option to escape them.

https://archive.org/download/lesmi-rclone-test/rclone-test-gerupag6deqafon2yarurih6/!"#%24%%26'()*%2B%2C-./%3A%3B<%3D>?%40[\]^_`{|}~/