-
-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added abstract helper interface for all storage backends #135
Added abstract helper interface for all storage backends #135
Conversation
Thanks for working on this. As it's a pretty big changeset and also a really important step forward, review might take a while and I just wanted to let you know that it's much appreciated and I'm looking forward to get this merged once it's ready. Thanks 🎩 |
No worries! It's non-functional so there is no hurry. |
Maybe having the helpers in a wrapper would be beneficial. One would only have to call helpers.prune() (as an example). |
Just saw your comment in #94, didn't know you already implemented something like that on a different branch. I'll have a look, maybe it can be combined or extended :) |
I wasn't super happy with that (otherwise I would have merged it), so feel free to stray away from that path when it makes sense. |
To do (@me):
|
Changes:
All changes are just architectural and structural. I tried to not change any functional flows. Feel free to have a look as you find time @m90 and share your thoughts - no hurry here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this, it will be very nice to have this cleaned up.
I left a couple of comments from a first read inline, and I'd also like to discuss one thing in detail here.
I think it would be great if we could change the package layout here a little bit to make it more flexible, a bit less arbitrary and also more idiomatic Go. The package layout could also guide us towards a minimal interface definition for a storage backend.
How I would envision this to look like right now would be:
internal/
└── storage
├── local
│ └── local.go // exports a func NewLocalStorage(...params) storage.Backend
├── s3
│ └── s3.go // exports a func NewS3Storage(...params) storage.Backend
├── ssh
│ └── ssh.go // exports a func NewSSHStorage(...params) storage.Backend
├── storage.go // exports a Backend interface type
└── webdav
└── webdav.go // exports a func NewWebDAVStorage(...params) storage.Backend
cmd
└── backup
├── archive.go
└── .... and so on
Like this we'd achieve two things:
-
The script logic and control flow stays in its place without creating arbitrary boundaries and creating hard-to-answer questions about who is responsible for doing what.
-
Logic for talking to a single storage backend is encapsulated in its own package and hidden from the script.
I'll also check out your branch now and poke around in the code a little, trying to run examples and such. Let me know if there's anything you'd like to be clarified or discussed further.
Thank you for taking the time to review the proposed changes and describing your thoughts on them! I totally agree, that the storages should be in their internal directory/package. You placed each backend, which is a single file, in their own subdirectory. Would you also make it a separate (internal) package? It looks odd to me to have directories for single files, but on the other hand, it cleanly separates it from I will wait with any changes in regards to your comments so we don't have conflicting changes, since you are having a look right now. |
The idea is that package |
I won't be making any changes on your branch in any case so feel free to continue whenever it works for you. Don't forget to have a weekend too. Thank you! |
Did some work regarding the structure like discussed:
Utilities/util.go could not be moved back to main, as e.g. the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice progress. How do you feel about the changed package layout now? I left a couple of comments inline.
In addition to that I think the next important step would be to get to the following point: Ideally, package storage just exports a single interface type and all of the constructor functions for a storage backend export a single function that returns this very interface type. In addition, the constructor functions could take non-custom types or types defined in package storage only, removing the need for the types
package.
Let me know if have any questions. Thanks!
Looks good to me!
Since the storage backends don't use the Config type anymore, it can surely be moved back to main now. For the |
If we already do it for copy, I think it's ok to do the same for prune, no? It's not super ugly, and if you feel it could need a bit more structure, you could also have some sort of intermediate wrapper type defined in package storage. The |
Did you mean with this and this comment what I implemented in c42b188 and 164a32b (last two commits)? Examples: time="2022-07-25T19:31:05+02:00" level=info msg="[S3] Uploaded a copy of backup time="2022-07-25T19:32:36+02:00" level=error msg="Fatal error running backup: [S3] Copy: Error uploading backup to remote storage! The specified bucket does not exist: Testbucket-VPS-invalid" |
Yes, that's pretty much what I meant, thank you. |
@MaxJa4 Just to avoid any misunderstandings, can you ping me when you think this is ready to have another look at? There's no need to hurry or anything, I just wanted to make sure you're not waiting on feedback when I'm thinking you're all set. Thanks. |
Sure, I will ping you! |
I doubt there is anything you can lose here, but you can also keep it a PR proper, I won't merge it by accident :) Thanks! |
@m90 I think I'm done with everything I wanted to do. If there is anything left, let me know. |
@MaxJa4 Are you still planning on doing refinements on this or are you waiting for a review of mine? Both would be fine, I just don't want this to go stale. Thanks. |
@m90 I have nothing left from my side. If we wanna optimize some small things here and there in the future, we still can with separate PRs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a last round of comments, all of them very nitpicky, but probably worth it so things are kept consistent. Everything else looks pretty good now.
There's a bigger topic left in there still, but I'd like to think about this more and then possibly implement it myself: Even if it's better than passing down the top level logger, I'm not super fond of passing down that logger function, it's currently the only sideeffecfful thing being passed down and I'm not sure if we need to do this at all. Maybe we can somehow have a channel on the storage implementations that emit log messages while things are happening and then script can decide what to do about these. In any case, don't worry about this, I just wanted to ḿention that I'm still thinking about how to solve this.
Rebased with GitHub UI, let's see if it worked correctly. History looked good though. No conflicts anymore. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This turned out really well, thanks for all the effort put into this. I still have to make up my mind about how to get this released (so I won't merge before that), but in case you want to add other features on top of that already, let me know and we can have a develop
branch or something similar.
Thank you very much.
There's now a @MaxJa4 just so that I can plan how to move ahead, are you planning to add some things on top (e.g. #138 or #101) soon? Both is fine, just so that I know. |
Ok, great, no need to hurry in any way. I'll let you know when this hits the main branch so you know where to start. |
* Added abstract helper interface and implemented it for all storage backends * Moved storage client initializations also to helper classes * Fixed ssh init issue * Moved script parameter to helper struct to simplify script init. * Created sub modules. Enhanced abstract implementation. * Fixed config issue * Fixed declaration issues. Added config to interface. * Added StorageProviders to unify all backends. * Cleanup, optimizations, comments. * Applied discussed changes. See description. Moved modules to internal packages. Replaced StoragePool with slice. Moved conditional for init of storage backends back to script. * Fix docker build issue * Fixed accidentally removed local copy condition. * Delete .gitignore * Renaming/changes according to review Renamed Init functions and interface. Replaced config object with specific config values. Init func returns interface instead of struct. Removed custom import names where possible. * Fixed auto-complete error. * Combined copy instructions into one layer. * Added logging func for storages. * Introduced logging func for errors too. * Missed an error message * Moved config back to main. Optimized prune stats handling. * Move stats back to main package * Code doc stuff * Apply changes from #136 * Replace name field with function. * Changed receiver names from stg to b. * Renamed LogFuncDef to Log * Removed redundant package name. * Renamed storagePool to storages. * Simplified creation of new storage backend. * Added initialization for storage stats map. * Invert .dockerignore patterns. * Fix package typo
* Added abstract helper interface and implemented it for all storage backends * Moved storage client initializations also to helper classes * Fixed ssh init issue * Moved script parameter to helper struct to simplify script init. * Created sub modules. Enhanced abstract implementation. * Fixed config issue * Fixed declaration issues. Added config to interface. * Added StorageProviders to unify all backends. * Cleanup, optimizations, comments. * Applied discussed changes. See description. Moved modules to internal packages. Replaced StoragePool with slice. Moved conditional for init of storage backends back to script. * Fix docker build issue * Fixed accidentally removed local copy condition. * Delete .gitignore * Renaming/changes according to review Renamed Init functions and interface. Replaced config object with specific config values. Init func returns interface instead of struct. Removed custom import names where possible. * Fixed auto-complete error. * Combined copy instructions into one layer. * Added logging func for storages. * Introduced logging func for errors too. * Missed an error message * Moved config back to main. Optimized prune stats handling. * Move stats back to main package * Code doc stuff * Apply changes from #136 * Replace name field with function. * Changed receiver names from stg to b. * Renamed LogFuncDef to Log * Removed redundant package name. * Renamed storagePool to storages. * Simplified creation of new storage backend. * Added initialization for storage stats map. * Invert .dockerignore patterns. * Fix package typo
This is on |
No worries. I like the improvements you made afterwards too, looks cleaner that way. |
This is now released in v2.22.0. Thanks for your contribution. |
Implements #113
I created an abstract helper interface and implemented it for all storage backends (Minio/S3, WebDav, SSH, local).
The obvious candidates for similar operations were copyArchive and pruneBackups as well as the initialization of the different clients.
Also doPrune has been moved to the abstract helper.
As I'm no Go expert, feel free to adjust these changes to your liking or suggest optimizations.
Maybe the helpers could be moved in their own module - idk how well that would work with Go though while avoiding import cycles since the helpers need "script" and the script.go needs the helpers...