Backup на Яндекс.Диск

Для того чтобы сливать бэкапы на сторонний сервер или в локальную директорию уже давным давно можно не писать самопальные скрипты, а воспользоваться гемом Backup. И то, что это гем и написан он на Ruby, совершенно не означает, что организовывать бэкапы он может только для Ruby.

Backup много чего умеет делать, работает с двумя вагонами СУБД (MySQL, PostgreSQL, MongoDB итд), может закачивать сами бэкапы на триллионы различных площадок (Dropbox, CloudFiles, S3 итд) используя килотонны всевозможных протоколов (SFTP, FTP, SCP итд), подробнее об этом давно уже расписано на Хабре. Одна беда у гема: он не поддерживает протокол WebDAV и площадку Яндекс.Диск. Ну, а почему собственно необходим Яндекс.Диск и чем он лучше всех остальных можно узнать перейдя по моей партнерской ссылке (новый шедевр скрытой рекламы).

установка backup

Тут ничего нового, все тоже самое, что и в официальной инструкции.
Сама установка:

# gem install backup

Генерация конфига:

$ backup generate:model --trigger my_backup
Generated model file: '~/Backup/models/my_backup.rb'.

В результате этих манипуляций возникнет директория ~/Backup с какой-то лабудой в ~/Backup/config.rb и собственно с конфигом одной из задач в ~/Backup/models/my_backup.rb.

интерфейс для Яндекс.Диска

Управлять ЯДом можно как через API, так и напрямую через WebDAV. Для взаимодействия по WebDAV есть отдельные специальные гемы, но нормально закачать что-нибудь вряд ли удастся т.к. для авторизации по WebDAV придется использовать пару логин:пароль, в то время как ЯД позволяет загружать только при авторизации по ключу приложения, насколько я понял.

К счастью существует гем yandex-disk, через который и будут заливаться бэкапы.

После установки гема:

# gem install yandex-disk

чтобы иметь права не только на чтение, необходимо получить токен от Яндекса. Для этого надо:

  1. авторизоваться в Яндексе
  2. выбрать имеющееся приложение или создать новое
  3. получить токен пройдя по ссылке: https://oauth.yandex.ru/authorize?response_type=token&client_id=YOUR_APP_ID

настройка backup под Яндекс.Диск

Удивительно, но на странице гема yandex-disk в самом низу приведен конфиг модели под backup, лично для себя, я приведу указанную конфигурацию к mysql-евскому виду:

# encoding: utf-8
 
##
# Backup Generated: my_backup
# Once configured, you can run the backup with the following command:
#
# $ backup perform -t my_backup [-c ]
#
 
require 'yandex/disk/backup/storage'
 
Backup::Model.new(:my_backup, 'My backup DB to Yandex.Disk') do
  ##
  # Split [Splitter]
  #
  # Split the backup file in to chunks of 250 megabytes
  # if the backup file size exceeds 250 megabytes
  #
  split_into_chunks_of 500
 
  database MySQL do |db|
    db.name  = 'mydb'
    db.username = 'myuser'
    db.password = 'mypass'
  end
 
  compress_with Gzip
 
  store_with Yandex::Disk do |disk|
    disk.access_token = 'mysupersecretyandexapptoken'
    disk.path         = '/backups/'
    disk.keep = 5
  end
end

запуск

И вот казалось бы все готово, осталось только запустить backup:

$ backup perform -t my_backup
[2014/01/08 18:53:04][info] Performing Backup for 'My backup DB to Yandex.Disk (my_backup)'!
[2014/01/08 18:53:04][info] [ backup 3.4.0 : ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux] ]
[2014/01/08 18:53:04][info] Database::MySQL Started...
[2014/01/08 18:53:04][info] Using Compressor::Gzip for compression.
[2014/01/08 18:53:04][info]   Command: '/bin/gzip'
[2014/01/08 18:53:04][info]   Ext: '.gz'
[2014/01/08 18:53:06][info] Database::MySQL Finished!
[2014/01/08 18:53:06][info] Packaging the backup files...
[2014/01/08 18:53:06][info] Splitter configured with a chunk size of 500MB.
[2014/01/08 18:53:06][info] Packaging Complete!
[2014/01/08 18:53:06][info] Cleaning up the temporary files...
[2014/01/08 18:53:06][info] Yandex::Disk Started...
[2014/01/08 18:53:06][error] ModelError: Backup for Backup OA DB to Yandex.Disk (oabackup) Failed!
[2014/01/08 18:53:06][error]   An Error occured which has caused this Backup to abort before completion.
[2014/01/08 18:53:06][error]   Reason: NoMethodError
[2014/01/08 18:53:06][error]   undefined method `present?' for "mysupersecretyandexapptoken":String
[2014/01/08 18:53:06][error] 
[2014/01/08 18:53:06][error] Backtrace:
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/yandex-disk-0.0.5/lib/yandex/disk/client.rb:17:in `block in initialize'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/faraday-0.8.8/lib/faraday/connection.rb:65:in `initialize'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/faraday-0.8.8/lib/faraday.rb:11:in `new'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/faraday-0.8.8/lib/faraday.rb:11:in `new'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/yandex-disk-0.0.5/lib/yandex/disk/client.rb:16:in `initialize'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/yandex-disk-0.0.5/lib/yandex/disk/backup/storage.rb:18:in `new'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/yandex-disk-0.0.5/lib/yandex/disk/backup/storage.rb:18:in `connection'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/yandex-disk-0.0.5/lib/yandex/disk/backup/storage.rb:22:in `transfer!'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/storage/base.rb:34:in `perform!'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/model.rb:242:in `each'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/model.rb:242:in `block in perform!'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/model.rb:240:in `each'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/model.rb:240:in `perform!'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/cli.rb:163:in `block in perform'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/cli.rb:162:in `each'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/lib/backup/cli.rb:162:in `perform'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/thor-0.18.1/lib/thor/command.rb:27:in `run'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/thor-0.18.1/lib/thor/invocation.rb:120:in `invoke_command'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/thor-0.18.1/lib/thor.rb:363:in `dispatch'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/thor-0.18.1/lib/thor/base.rb:439:in `start'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/backup-3.4.0/bin/backup:5:in `<top (required)>'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/bin/backup:23:in `load'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/bin/backup:23:in `<main>'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/bin/ruby_executable_hooks:15:in `eval'
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/bin/ruby_executable_hooks:15:in `<main>'
[2014/01/08 18:53:06][warn] CleanerError: Cleanup Warning
[2014/01/08 18:53:06][warn]   The temporary backup folder '~/Backup/.tmp'
[2014/01/08 18:53:06][warn]   appears to contain the backup files which were to be stored:
[2014/01/08 18:53:06][warn]  ~/Backup/.tmp/my_backup.tar
[2014/01/08 18:53:06][warn]   
[2014/01/08 18:53:06][warn]   Make sure you check these files before the next scheduled backup for
[2014/01/08 18:53:06][warn]   'My backup DB to Yandex.Disk (my_backup)'
[2014/01/08 18:53:06][warn]   These files will be removed at that time!
[2014/01/08 18:53:06][info] ModelError: If you have other Backup jobs (triggers) configured to run,
[2014/01/08 18:53:06][info]   Backup will now attempt to continue...

И, как уже догадался проницательный читатель, бэкап не залился на ЯД, но не стоит расстраиваться. Пристально и с презрением посмотрите на строки:

[2014/01/08 18:53:06][error]   undefined method `present?' for "mysupersecretyandexapptoken":String
[2014/01/08 18:53:06][error] 
[2014/01/08 18:53:06][error] Backtrace:
[2014/01/08 18:53:06][error]   /usr/local/rvm/gems/ruby-2.0.0-p247/gems/yandex-disk-0.0.5/lib/yandex/disk/client.rb:17:in `block in initialize'

и откройте файл /usr/local/rvm/gems/ruby-2.0.0-p247/gems/yandex-disk-0.0.5/lib/yandex/disk/client.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# coding: utf-8
 
require 'base64'
require 'faraday'
require 'faraday_middleware'
 
Faraday::Connection::METHODS << :propfind
 
module Yandex
  module Disk
    class Client
      autoload :Request, 'yandex/disk/client/request'
 
      def initialize options={}
        @timeout = options[:timeout] || 300
        @http = Faraday.new(:url => 'https://webdav.yandex.ru') do |builder|
          if options[:access_token].present?
            builder.request :authorization, "OAuth", options[:access_token]
          else
            basic_token = Base64.encode64("#{options[:login]}:#{options[:password]}")
            builder.request :authorization, "Basic", basic_token
          end
 
          builder.response :follow_redirects
 
          if faraday_configurator = options[:faraday_configurator]
            faraday_configurator.call(builder)
          else
            builder.adapter :excon
          end
        end
      end
...

По неизвестным науке обстоятельствам ошибка возникает при вызове метода present? для String-элемента в хэше. Наивный читатель может предложить заменить

if options[:access_token].present?

на

unless options[:access_token].blank?

но спешу заметить, что такая рокировка вызовет аналогичную ошибку, поэтому в данном случае я предлагаю строку №17 привести к виду:

16
17
18
        @http = Faraday.new(:url => 'https://webdav.yandex.ru') do |builder|
          if options[:access_token]
            builder.request :authorization, "OAuth", options[:access_token]

После такой магии у меня все запустилось:

$ backup perform -t my_backup
[2014/01/08 19:09:03][info] Performing Backup for 'My backup DB to Yandex.Disk (my_backup)'!
[2014/01/08 19:09:03][info] [ backup 3.4.0 : ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux] ]
[2014/01/08 19:09:04][warn] CleanerError: Cleanup Warning
[2014/01/08 19:09:04][warn]   The temporary backup folder '~/Backup/.tmp'
[2014/01/08 19:09:04][warn]   appears to contain the package files from the previous backup!
[2014/01/08 19:09:04][warn]   ~/Backup/.tmp/my_backup.tar
[2014/01/08 19:09:04][warn]   These files will now be removed.
[2014/01/08 19:09:04][warn]   
[2014/01/08 19:09:04][warn]   Please check the log for messages and/or your notifications
[2014/01/08 19:09:04][warn]   concerning this backup: 'My backup DB to Yandex.Disk (my_backup)'
[2014/01/08 19:09:04][warn]   The temporary files which had to be removed should not have existed.
[2014/01/08 19:09:04][info] Database::MySQL Started...
[2014/01/08 19:09:04][info] Using Compressor::Gzip for compression.
[2014/01/08 19:09:04][info]   Command: '/bin/gzip'
[2014/01/08 19:09:04][info]   Ext: '.gz'
[2014/01/08 19:09:10][info] Database::MySQL Finished!
[2014/01/08 19:09:10][info] Packaging the backup files...
[2014/01/08 19:09:10][info] Splitter configured with a chunk size of 500MB.
[2014/01/08 19:09:10][info] Packaging Complete!
[2014/01/08 19:09:10][info] Cleaning up the temporary files...
[2014/01/08 19:09:10][info] Yandex::Disk Started...
[2014/01/08 19:09:12][info] Storing '/backups/my_backup/2014.01.08.19.09.03/my_backup.tar'...
[2014/01/08 19:10:13][info] Cycling Started...
[2014/01/08 19:10:13][info] Yandex::Disk Finished!
[2014/01/08 19:10:13][info] Cleaning up the package files...
[2014/01/08 19:10:13][warn] Backup for 'My backup DB to Yandex.Disk (my_backup)' Completed Successfully (with Warnings) in 00:01:10

2 Comments to “Backup на Яндекс.Диск”

  1. Dr.Samuil пишет:

    слишком пафосно написано…
    “триллионы”, “килотонны” и т.п…

Leave a Reply

(обязательно)

(обязательно)