Exploitez la puissance de Rust
Je voulais apprendre Rust et essayer AWS Cloud Development Kit (CDK) – et me protéger du cas peu probable où mon compte Google serait verrouillé et perdrait 500 Go de photos et de vidéos.
Donc, dans ce billet de blog, je décris mon parcours et le système que j’ai développé pour cela – un système qui sauvegarderait régulièrement toutes mes photos et vidéos dans Amazon S3. Je suis sûr qu’il existe des solutions prêtes à l’emploi pour cela, mais ce serait beaucoup moins amusant. Ceci est la première partie d’une série de blogs en deux parties. Alors allons-y.
Mais attendezsi vous souhaitez ignorer l’histoire ici et simplement mettre en place la même solution que je décris ici, n’hésitez pas à vous diriger directement vers et suivez les instructions. Ce n’est pas une configuration sans configuration et cela nécessitera des connaissances techniques pour la configuration, mais c’est faisable.
Maintenant, allons-y. L’idée était simple : utilisez l’API Google Photos Library pour analyser régulièrement l’intégralité de la bibliothèque Google Photos et copier les données photo et vidéo dans Archives profondes S3 Glacier d’Amazon.
À un prix de 0,00099 $ par Go, cela coûterait 0,50 $ par mois pour l’ensemble de ma bibliothèque Photos. Semblait raisonnable.
Tout d’abord, j’avais besoin d’un accès programmatique à ma bibliothèque Google Photos. Alors je me suis dirigé vers https://console.cloud.google.com/a créé un nouveau projet « Photos Backup » et a activé le API de la bibliothèque de photos pour ce projet.
Google autorise uniquement l’accès OAuth 2.0 pour cette API, pas d’accès au compte de service Google, donc je devais également créer un écran de consentement OAuth:
La partie importante ici est de spécifier la portée d’autorisation correcte comme …/auth/photoslibrary.readonly
comme expliqué dans le Documentation.
Puisque nous voulons seulement Télécharger données de la bibliothèque, readonly
c’est bien. Néanmoins, puisque cela permet à une application d’accéder aux photos privées d’un utilisateur, cela entre dans la catégorie des portées sensibles, et si je voulais rendre cette application accessible à tous, je devrais passer par le processus de vérification approprié de Google. Heureusement, ce n’est que pour un usage privé, ou « à des fins de test ».
Ensuite, nous pouvons télécharger notre ID client et notre secret client en cliquant sur l’icône de téléchargement à droite :
Pour faire une demande réelle contre l’API de la bibliothèque de photos, nous avons besoin d’un jeton d’accès. Nous pouvons l’obtenir en suivant le Processus d’autorisation OAuth 2.0. Obtenir ce droit est une corvée. Les messages d’erreur de Google, en cas d’erreur, ne sont pas utiles. Finalement, j’ai bien compris. Pour couper court, le résultat est dans ce binaire Rust.
Maintenant que nous avons le jeton d’accès, nous pouvons enfin commencer.
Alors j’ai pensé « créons un nouveau projet Rust et faisons-le ». Mais comme tout le monde le sait, qui a commencé à apprendre Rust, ce n’est pas si facile. Et les premières heures, je me suis battu plus avec le vérificateur d’emprunt que j’ai fait quoi que ce soit pour télécharger des données d’image à partir de Google Photos.
Finalement, cependant, j’ai piraté ensemble un premier prototype pour télécharger une photo de Google Photos. j’ai utilisé le google-photoslibrary1
crate et le code ressemblait à peu près à ceci :
Super content du résultat, j’ai ouvert la photo téléchargée dans une visionneuse d’images en local. Tout était là, sauf que la photo manquait les données Exif de localisation GPS. Pas une tragédie absolue dans un cas de catastrophe improbable, mais c’était une gêne tenace, et donc ça ne me satisfaisait pas tout à fait. Je devais trouver une meilleure solution qui me donnerait mes données photo et vidéo telles quelles.
Il s’avère que Google Takeout, le service fourni par Google aux utilisateurs pour exporter leurs données personnelles, conserve les données d’origine. Mais Google Takeout ne fournit pas d’API. Ce qu’il offre cependant, c’est la possibilité d’effectuer régulièrement des plats à emporter tous les 2 mois pendant un an. Il propose également de déposer les données dans Google Drive :
Je pourrais donc simplement configurer une tâche qui s’exécute tous les 2 mois et copie la dernière sortie de Google Drive dans S3. Ce n’est pas une solution 100% mains libres, car je dois désormais penser à déclencher le takeout une fois par an. Mais étant donné que c’est seulement une fois par an et cela prend environ 5 minutes, c’est un compromis que je suis prêt à faire.
Retour à la case départ : activons l’API Google Drive dans notre projet de sauvegarde de photos dans Google Cloud. Modifions l’écran de consentement OAuth et utilisons une portée d’autorisation différente, à savoir …/auth/drive.readonly
:
Maintenant, rassemblons le code pour télécharger les données de Google Drive. Cette fois, j’ai utilisé le google-drive3
crate et les pièces pour créer son « drive hub » et pour télécharger les données réelles ressemblent à ceci :
La hyper::Response<Body>
l’objet peut être utilisé directement pour le télécharger sur S3, en utilisant le client S3 à partir du aws-sdk-s3
Caisse:
Ce que nous pouvons également voir ici, c’est que nous pouvons utiliser la structure de fichier de l’API Google Drive md5_checksum
propriété (si elle est disponible) pour la spécifier dans la demande de téléchargement S3. De cette façon, S3 peut vérifier automatiquement que le fichier téléchargé sur S3 est identique à celui téléchargé depuis Google Drive.
Bien sûr, les choses doivent être raisonnablement rapides. Bien que cela importe un peu moins quand tout se passe dans les nuages de toute façon, c’est quand même plutôt agréable quand le travail de copie se termine rapidement. De plus, toutes ces bibliothèques sont déjà implémentées en tant que async
bibliothèques. Donc, le saut pour en profiter ne devrait pas être trop difficile. Droit? Encore une fois, il ne s’est pas avéré aussi simple de le faire dans Rust. Mais le livre Programmation asynchrone en Rust est une bouée de sauvetage.
J’ai réalisé, sans rendre les choses trop complexes, que je pouvais simplement obtenir d’abord la liste de tous les objets de l’API Google File dans le Takeout
dossier (dans mon cas, c’est environ 500 fichiers de 1 Go), créez un futures
diffusez-le et parcourez tous les fichiers en copiant quatre fichiers simultanément sur S3.
Un canal illimité est utilisé pour suivre les résultats de la copie asynchrone. L’appel à rx.close()
s’assurera automatiquement que tous les fichiers ont été copiés avant de continuer.
Il ressemble à ceci :
La partie critique se passe à la ligne 7 où nous utilisons for_each_concurrent(4, ...)
ce qui provoque les 4 copies simultanées. La ligne 11 effectue alors la copie asynchrone proprement dite.
La gestion des erreurs dans cette implémentation est encore un peu grossière, et je devrai en parler lors d’une future refactorisation. Cependant, ce qui est important, c’est que le travail échouera, si au moins une copie de fichier échoue, et toutes les erreurs seront consignées.
Pour rendre ce binaire un peu plus facile à utiliser, j’ai décidé d’utiliser une bibliothèque d’analyseur d’arguments de ligne de commande. Dans ce cas, clap
ressemblait exactement à ce dont j’avais besoin. Avec lui, je pourrais simplement définir une structure qui définit mes arguments de ligne de commande :
Qui produit :
$ back-up-drive-folder --helpUsage: back-up-drive-folder [OPTIONS] <SOURCE> <DESTINATION>Arguments:
<SOURCE> The Google Drive folder to back up
<DESTINATION> Where to copy the files. This must be in the format s3://bucket-name/some/folder Can also use {date} which will get substituted by the current dateOptions:
-s, --s3-storage-class <S3_STORAGE_CLASS>
Storage class to use when storing objects in S3. Possible values: DEEP_ARCHIVE, GLACIER, GLACIER_IR, INTELLIGENT_TIERING, ONEZONE_IA, OUTPOSTS, REDUCED_REDUNDANCY, STANDARD, STANDARD_IA [default: STANDARD]
-h, --help
Print help information
-V, --version
Print version information
Avec cela, j’avais un binaire de travail qui pouvait être utilisé pour copier le contenu d’un dossier Google Drive dans Amazon S3 via :
$ back-up-drive-folder --s3-storage-class DEEP_ARCHIVE \
Takeout \
s3://my-backup-bucket/some/folder
Ceci conclut la première partie de cette série de blogs en deux parties. Dans la partie II, j’écrirai sur l’automatisation que j’ai mise en place, en utilisant CDK, Docker et GitHub Actions, pour déployer ce système.
Si vous voulez voir le code source complet de ce projet, rendez-vous sur et n’hésitez pas à contribuer ou à laisser des commentaires.