Class: Yobi::Repository
- Inherits:
-
Object
- Object
- Yobi::Repository
- Defined in:
- lib/yobi/repository.rb,
lib/yobi/repository/ls.rb,
lib/yobi/repository/cat.rb,
lib/yobi/repository/key.rb,
lib/yobi/repository/tag.rb,
lib/yobi/repository/copy.rb,
lib/yobi/repository/diff.rb,
lib/yobi/repository/dump.rb,
lib/yobi/repository/find.rb,
lib/yobi/repository/list.rb,
lib/yobi/repository/check.rb,
lib/yobi/repository/mount.rb,
lib/yobi/repository/prune.rb,
lib/yobi/repository/stats.rb,
lib/yobi/repository/backup.rb,
lib/yobi/repository/forget.rb,
lib/yobi/repository/repair.rb,
lib/yobi/repository/unlock.rb,
lib/yobi/repository/migrate.rb,
lib/yobi/repository/recover.rb,
lib/yobi/repository/restore.rb,
lib/yobi/repository/rewrite.rb,
lib/yobi/repository/snapshots.rb,
sig/yobi.rbs
Overview
One Restic repository. Every Restic subcommand that operates on a repository is a method here.
Constant Summary collapse
- ARCHIVE_TYPES =
Valid
archive:values for #dump, as both String and Symbol. Set.new(%w[tar zip].flat_map { [_1, _1.to_sym] }).freeze
- STATS_MODES =
Maps every valid
mode:value for #stats, as both String and Symbol, to the String Restic itself expects. %w[restore-size files-by-contents blobs-per-file raw-data].each_with_object({}) do |value, hash| hash[value] = value hash[value.tr("-", "_").to_sym] = value end.freeze
Instance Attribute Summary collapse
-
#backend_credentials ⇒ Hash, #call
readonly
The storage backend's own credentials, as given to #initialize.
-
#password ⇒ String, ...
readonly
The repository's encryption password, as given to #initialize.
-
#url ⇒ String
readonly
The repository location.
Instance Method Summary collapse
-
#backup(source:, excludes: [], exclude_files: [], exclude_if_present: [], exclude_larger_than: nil, files_from: [], files_from_raw: [], files_from_verbatim: [], iexcludes: [], iexclude_files: [], tags: [], dry_run: false, exclude_caches: false, exclude_cloud_files: false, force: false, group_by: nil, host: nil, ignore_ctime: false, ignore_inode: false, no_scan: false, one_file_system: false, parent: nil, read_concurrency: nil, skip_if_unchanged: false, time: nil, verbose: false, with_atime: false) {|message| ... } ⇒ Yobi::BackupOutcome
restic backup: creates a new snapshot from a source path. -
#cat_blob(id) {|io| ... } ⇒ Yobi::IOHandle
restic cat blob ID: one data blob's raw, decrypted bytes. -
#cat_index(id) ⇒ Hash
restic cat index ID: one index file's own raw contents. -
#cat_key(id) ⇒ Hash
restic cat key ID: one key's own raw stored record. -
#cat_masterkey_and_game_over_if_this_leaks ⇒ Hash
restic cat masterkey: this repository's own encryption/MAC key material. -
#cat_pack(id) {|io| ... } ⇒ Yobi::IOHandle
restic cat pack ID: one pack file's raw, still-encrypted bytes. -
#cat_snapshot(id) ⇒ Hash
restic cat snapshot ID: one snapshot's own raw stored record. -
#cat_tree(snapshot_id, subfolder: nil) ⇒ Hash
restic cat tree snapshot:subfolder: the raw tree object at a snapshot's root, or atsubfolderwithin it. -
#check(hosts: [], paths: [], read_data: false, read_data_subset: nil, tags: [], with_cache: false) ⇒ Yobi::CheckOutcome
restic check: tests the repository for errors. -
#config ⇒ Hash
restic cat config: the repository's own config document. -
#copy(from_repo:, from_password: nil, snapshot_ids: [], from_key_hint: nil, from_repository_file: nil, hosts: [], paths: [], tags: []) ⇒ true
restic copy: replicates snapshots from another repository into this one. -
#diff(from:, to:, metadata: false) ⇒ Yobi::DiffOutcome
restic diff: compares two snapshots. -
#dump(snapshot_id:, file:, target: nil, archive: nil, hosts: [], paths: [], tags: []) {|io| ... } ⇒ true, Yobi::IOHandle
restic dump: extracts a file/folder from a snapshot. - #env ⇒ Hash{String => String}
-
#find(patterns:, blob: false, pack: false, tree: false, hosts: [], human_readable: false, ignore_case: false, long: false, newest: nil, oldest: nil, paths: [], reverse: false, show_pack_id: false, snapshot_ids: [], tags: []) ⇒ Enumerable<Yobi::MatchesPerSnapshot>
restic find: searches for files/directories across snapshots by name pattern. -
#forget(keep_last: nil, keep_hourly: nil, keep_daily: nil, keep_weekly: nil, keep_monthly: nil, keep_yearly: nil, keep_within: nil, keep_within_hourly: nil, keep_within_daily: nil, keep_within_weekly: nil, keep_within_monthly: nil, keep_within_yearly: nil, keep_tags: [], hosts: [], tags: [], paths: [], compact: false, group_by: nil, dry_run: false, prune: false, unsafe_allow_remove_all: false, max_unused: nil, max_repack_size: nil, repack_cacheable_only: false, repack_uncompressed: false, repack_smaller_than: nil) ⇒ Enumerable<Yobi::ForgetGroup>
restic forget: applies a retention policy, removing snapshots that don't match anykeep_*rule. -
#init(copy_chunker_params: false, from_insecure_no_password: false, from_key_hint: nil, from_password_command: nil, from_password_file: nil, from_repo: nil, from_repository_file: nil, repository_version: nil) ⇒ Hash
restic init: creates the repository at #url. -
#initialize(url:, password: nil, backend_credentials: {}, restic: nil) ⇒ Repository
constructor
A new instance of Repository.
- #inspect ⇒ String
-
#key_add(host: nil, user: nil, new_insecure_no_password: false, new_password_file: nil) ⇒ true
(also: #add_key)
restic key add: creates a new key (password) for this repository. -
#key_list ⇒ Enumerable<Yobi::Key>
(also: #keys)
restic key list: lists every key (password) associated with this repository. -
#key_passwd(host: nil, user: nil, new_insecure_no_password: false, new_password_file: nil) ⇒ true
(also: #change_password)
restic key passwd: creates a new key for this repository and removes the key currently in use. -
#key_remove(id:) ⇒ true
(also: #remove_key)
restic key remove: removes the given key ID from this repository. -
#list(type) ⇒ Array<String>
restic list: lists every object ID of the given type in this repository. -
#ls(snapshot_id:, dirs: [], hosts: [], human_readable: false, long: false, paths: [], recursive: false, reverse: false, sort: nil, tags: []) ⇒ Yobi::LsOutcome
restic ls: lists a snapshot's files/directories. -
#migrate(names: [], force: false) ⇒ true
restic migrate: checks which migrations can be applied to this repository, or applies the given ones. -
#mount(mountpoint:, hosts: [], paths: [], tags: [], allow_other: false, no_default_permissions: false, owner_root: false, path_templates: [], time_template: nil, ready_timeout: 10) {|mount| ... } ⇒ Yobi::Mount, Object
restic mount: serves this repository as a read-only FUSE filesystem atmountpoint, which must already exist. -
#prune(dry_run: false, max_repack_size: nil, max_unused: nil, repack_cacheable_only: false, repack_smaller_than: nil, repack_uncompressed: false, unsafe_recover_no_free_space: nil) ⇒ true
restic prune: removes data no longer referenced by any snapshot. -
#recover ⇒ true
restic recover: builds a new snapshot from any data found in this repository that isn't referenced by an existing snapshot (e.g. after an accidental #forget). -
#repair_index(read_all_packs: false) ⇒ true
restic repair index: creates a new index based on the pack files present in this repository. -
#repair_packs(ids:) ⇒ true
restic repair packs: extracts intact blobs from the given pack files, rebuilds the index to drop the damaged packs, and removes them from the repository. -
#repair_snapshots(snapshot_ids: [], dry_run: false, forget: false, hosts: [], paths: [], tags: []) ⇒ true
restic repair snapshots: scans the given snapshots and generates new ones with damaged directories/file contents removed. -
#restore(snapshot_id:, target:, excludes: [], exclude_files: [], exclude_xattrs: [], hosts: [], iexcludes: [], iexclude_files: [], iincludes: [], iinclude_files: [], includes: [], include_files: [], include_xattrs: [], paths: [], tags: [], delete: false, dry_run: false, overwrite: nil, ownership_by_name: false, sparse: false, verbose: false, verify: false) {|message| ... } ⇒ Yobi::RestoreOutcome
restic restore: extracts a snapshot's contents to a target directory. -
#rewrite(snapshot_ids: [], hosts: [], tags: [], paths: [], excludes: [], exclude_files: [], iexcludes: [], iexclude_files: [], includes: [], include_files: [], iincludes: [], iinclude_files: [], dry_run: false, forget: false, new_host: nil, new_time: nil, snapshot_summary: false) ⇒ true
restic rewrite: creates new snapshots from existing ones with exclude/include filters applied, or metadata changed. -
#snapshots(tags: [], hosts: [], paths: [], compact: false, group_by: nil, latest: nil) ⇒ Enumerable<Yobi::Snapshot>
restic snapshots: lists snapshots, optionally filtered. -
#stats(snapshot_ids: [], hosts: [], mode: nil, paths: [], tags: []) ⇒ Hash
restic stats: accumulates statistics about the repository's data. -
#tag(snapshot_ids: [], add: [], remove: [], set: [], tags: [], hosts: [], paths: []) ⇒ Yobi::TagOutcome
restic tag: modifies tags on existing snapshots. -
#unlock(remove_all: false) ⇒ true
restic unlock: removes stale locks left by other Restic processes.
Constructor Details
#initialize(url:, password: nil, backend_credentials: {}, restic: nil) ⇒ Repository
Returns a new instance of Repository.
27 28 29 30 31 32 |
# File 'lib/yobi/repository.rb', line 27 def initialize(url:, password: nil, backend_credentials: {}, restic: nil) @url, @extracted_rest_credentials = extract_rest_credentials(url) @password = password @backend_credentials = backend_credentials @restic = initialize_restic(restic) end |
Instance Attribute Details
#backend_credentials ⇒ Hash, #call (readonly)
Returns the storage backend's own credentials, as given to #initialize.
15 16 17 |
# File 'lib/yobi/repository.rb', line 15 def backend_credentials @backend_credentials end |
#password ⇒ String, ... (readonly)
Returns the repository's encryption password, as given to #initialize.
13 14 15 |
# File 'lib/yobi/repository.rb', line 13 def password @password end |
#url ⇒ String (readonly)
Returns the repository location.
11 12 13 |
# File 'lib/yobi/repository.rb', line 11 def url @url end |
Instance Method Details
#backup(source:, excludes: [], exclude_files: [], exclude_if_present: [], exclude_larger_than: nil, files_from: [], files_from_raw: [], files_from_verbatim: [], iexcludes: [], iexclude_files: [], tags: [], dry_run: false, exclude_caches: false, exclude_cloud_files: false, force: false, group_by: nil, host: nil, ignore_ctime: false, ignore_inode: false, no_scan: false, one_file_system: false, parent: nil, read_concurrency: nil, skip_if_unchanged: false, time: nil, verbose: false, with_atime: false) {|message| ... } ⇒ Yobi::BackupOutcome
restic backup: creates a new snapshot from a source path.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/yobi/repository/backup.rb', line 45 def backup(source:, excludes: [], exclude_files: [], exclude_if_present: [], exclude_larger_than: nil, files_from: [], files_from_raw: [], files_from_verbatim: [], iexcludes: [], iexclude_files: [], tags: [], dry_run: false, exclude_caches: false, exclude_cloud_files: false, force: false, group_by: nil, host: nil, ignore_ctime: false, ignore_inode: false, no_scan: false, one_file_system: false, parent: nil, read_concurrency: nil, skip_if_unchanged: false, time: nil, verbose: false, with_atime: false, &block) argv = build_argv("backup") do |a| a.repeat_flag(:exclude, excludes) a.repeat_flag(:exclude_file, exclude_files) a.repeat_flag(:exclude_if_present, exclude_if_present) a.flag(:exclude_larger_than, exclude_larger_than) unless exclude_larger_than.nil? a.repeat_flag(:files_from, files_from) a.repeat_flag(:files_from_raw, files_from_raw) a.repeat_flag(:files_from_verbatim, files_from_verbatim) a.repeat_flag(:iexclude, iexcludes) a.repeat_flag(:iexclude_file, iexclude_files) a.repeat_flag(:tag, ) a.flag(:dry_run) if dry_run a.flag(:exclude_caches) if exclude_caches a.flag(:exclude_cloud_files) if exclude_cloud_files a.flag(:force) if force a.flag(:group_by, group_by) unless group_by.nil? a.flag(:host, host) unless host.nil? a.flag(:ignore_ctime) if ignore_ctime a.flag(:ignore_inode) if ignore_inode a.flag(:no_scan) if no_scan a.flag(:one_file_system) if one_file_system a.flag(:parent, parent) unless parent.nil? a.flag(:read_concurrency, read_concurrency) unless read_concurrency.nil? a.flag(:skip_if_unchanged) if skip_if_unchanged a.flag(:time, time) unless time.nil? a.short_flag(:v) if verbose a.flag(:with_atime) if with_atime case source in String => path a..append(path) in [:stdin_from_command, command] a.flag(:stdin_from_command) a..append(tokenize(command)) in [:stdin_from_command, command, String => filename] a.flag(:stdin_from_command) a.flag(:stdin_filename, filename) a..append(tokenize(command)) end end execution = if block run_restic(argv) { |raw| (raw, &block) } else run_restic(argv) end BackupOutcome.new(execution[:exit_code], execution[:output]) end |
#cat_blob(id) ⇒ Object #cat_blob(id) ⇒ IOHandle
71 72 73 |
# File 'lib/yobi/repository/cat.rb', line 71 def cat_blob(id, &block) run_restic_dump(build_argv("cat", "blob", id), &block) end |
#cat_index(id) ⇒ Hash
restic cat index ID: one index file's own raw contents. IDs come
from list(:index).
22 23 24 25 |
# File 'lib/yobi/repository/cat.rb', line 22 def cat_index(id) execution = run_restic(build_argv("cat", "index", id)) JSON.parse(execution[:output].to_s) end |
#cat_key(id) ⇒ Hash
restic cat key ID: one key's own raw stored record.
31 32 33 34 35 |
# File 'lib/yobi/repository/cat.rb', line 31 def cat_key(id) id = id.id if id.is_a?(Yobi::Key) execution = run_restic(build_argv("cat", "key", id)) JSON.parse(execution[:output].to_s) end |
#cat_masterkey_and_game_over_if_this_leaks ⇒ Hash
restic cat masterkey: this repository's own encryption/MAC key
material. Extremely sensitive: this is the actual key, not a
redacted reference to it, and there is no operation that rotates it;
every other key/password management method here only manages
different ways to unlock this same master key.
82 83 84 85 |
# File 'lib/yobi/repository/cat.rb', line 82 def cat_masterkey_and_game_over_if_this_leaks execution = run_restic(build_argv("cat", "masterkey")) JSON.parse(execution[:output].to_s) end |
#cat_pack(id) ⇒ Object #cat_pack(id) ⇒ IOHandle
restic cat pack ID: one pack file's raw, still-encrypted bytes.
IDs come from list(:packs). Same block/handle shape as #dump.
60 61 62 |
# File 'lib/yobi/repository/cat.rb', line 60 def cat_pack(id, &block) run_restic_dump(build_argv("cat", "pack", id), &block) end |
#cat_snapshot(id) ⇒ Hash
restic cat snapshot ID: one snapshot's own raw stored record.
11 12 13 14 15 |
# File 'lib/yobi/repository/cat.rb', line 11 def cat_snapshot(id) id = id.id if id.is_a?(Yobi::Snapshot) execution = run_restic(build_argv("cat", "snapshot", id)) JSON.parse(execution[:output].to_s) end |
#cat_tree(snapshot_id, subfolder: nil) ⇒ Hash
restic cat tree snapshot:subfolder: the raw tree object at a
snapshot's root, or at subfolder within it.
43 44 45 46 47 48 49 50 51 52 |
# File 'lib/yobi/repository/cat.rb', line 43 def cat_tree(snapshot_id, subfolder: nil) snapshot_id = snapshot_id.id if snapshot_id.is_a?(Yobi::Snapshot) target = if subfolder.nil? snapshot_id else "#{snapshot_id}:#{subfolder}" end execution = run_restic(build_argv("cat", "tree", target)) JSON.parse(execution[:output].to_s) end |
#check(hosts: [], paths: [], read_data: false, read_data_subset: nil, tags: [], with_cache: false) ⇒ Yobi::CheckOutcome
restic check: tests the repository for errors.
17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/yobi/repository/check.rb', line 17 def check(hosts: [], paths: [], read_data: false, read_data_subset: nil, tags: [], with_cache: false) argv = build_argv("check") do |a| a.repeat_flag(:host, hosts) a.repeat_flag(:path, paths) a.flag(:read_data) if read_data a.flag(:read_data_subset, read_data_subset) unless read_data_subset.nil? a.repeat_flag(:tag, ) a.flag(:with_cache) if with_cache end execution = run_restic(argv) CheckOutcome.new(execution[:exit_code], execution[:output]) end |
#config ⇒ Hash
restic cat config: the repository's own config document.
78 79 80 81 |
# File 'lib/yobi/repository.rb', line 78 def config execution = run_restic(build_argv("cat", "config")) JSON.parse(execution[:output].to_s) end |
#copy(from_repo:, from_password: nil, snapshot_ids: [], from_key_hint: nil, from_repository_file: nil, hosts: [], paths: [], tags: []) ⇒ true
restic copy: replicates snapshots from another repository into
this one. Already-copied snapshots are skipped automatically.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/yobi/repository/copy.rb', line 19 def copy(from_repo:, from_password: nil, snapshot_ids: [], from_key_hint: nil, from_repository_file: nil, hosts: [], paths: [], tags: []) if from_repo.is_a?(Repository) from_password = from_repo.password if from_password.nil? from_repo = from_repo.url end argv = build_argv("copy", snapshot_ids) do |a| a.flag(:from_repo, from_repo) a.flag(:from_insecure_no_password) if from_password == :insecure_no_password a.flag(:from_key_hint, from_key_hint) unless from_key_hint.nil? a.flag(:from_repository_file, from_repository_file) unless from_repository_file.nil? a.repeat_flag(:host, hosts) a.repeat_flag(:path, paths) a.repeat_flag(:tag, ) end run_restic(argv, extra_env: password_env(from_password, "RESTIC_FROM_PASSWORD")) true end |
#diff(from:, to:, metadata: false) ⇒ Yobi::DiffOutcome
restic diff: compares two snapshots.
14 15 16 17 18 19 20 |
# File 'lib/yobi/repository/diff.rb', line 14 def diff(from:, to:, metadata: false) argv = build_argv("diff", from, to) do |a| a.flag(:metadata) if end execution = run_restic(argv) DiffOutcome.new(execution[:exit_code], execution[:output]) end |
#dump(snapshot_id:, file:, target: nil, archive: nil, hosts: [], paths: [], tags: []) {|io| ... } ⇒ true, Yobi::IOHandle
restic dump: extracts a file/folder from a snapshot. A single file's
raw bytes are written as-is; a folder is written as a tar/zip archive.
Give at most one of target: or a block.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/yobi/repository/dump.rb', line 25 def dump(snapshot_id:, file:, target: nil, archive: nil, hosts: [], paths: [], tags: [], &block) raise ArgumentError, "give at most one of target: or a block, not both" if target && block argv = build_argv("dump", snapshot_id) do |a| a.flag(:archive, archive) if ARCHIVE_TYPES.include?(archive) a.repeat_flag(:host, hosts) a.repeat_flag(:path, paths) a.repeat_flag(:tag, ) a.flag(:target, target) unless target.nil? a..append(file) end if target run_restic(argv) true else run_restic_dump(argv, &block) end end |
#env ⇒ Hash{String => String}
35 36 37 38 39 |
# File 'lib/yobi/repository.rb', line 35 def env {"RESTIC_REPOSITORY" => url} .merge(resolved_password) .merge(resolved_backend_credentials) end |
#find(patterns:, blob: false, pack: false, tree: false, hosts: [], human_readable: false, ignore_case: false, long: false, newest: nil, oldest: nil, paths: [], reverse: false, show_pack_id: false, snapshot_ids: [], tags: []) ⇒ Enumerable<Yobi::MatchesPerSnapshot>
restic find: searches for files/directories across snapshots by
name pattern.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/yobi/repository/find.rb', line 28 def find(patterns:, blob: false, pack: false, tree: false, hosts: [], human_readable: false, ignore_case: false, long: false, newest: nil, oldest: nil, paths: [], reverse: false, show_pack_id: false, snapshot_ids: [], tags: []) argv = build_argv("find") do |a| a.flag(:blob) if blob a.flag(:pack) if pack a.flag(:tree) if tree a.repeat_flag(:host, hosts) a.flag(:human_readable) if human_readable a.flag(:ignore_case) if ignore_case a.flag(:long) if long a.flag(:newest, newest) unless newest.nil? a.flag(:oldest, oldest) unless oldest.nil? a.repeat_flag(:path, paths) a.flag(:reverse) if reverse a.flag(:show_pack_id) if show_pack_id a.repeat_flag(:snapshot, snapshot_ids) a.repeat_flag(:tag, ) a..append(patterns) end execution = run_restic(argv) FindMatches.new(execution[:output]) end |
#forget(keep_last: nil, keep_hourly: nil, keep_daily: nil, keep_weekly: nil, keep_monthly: nil, keep_yearly: nil, keep_within: nil, keep_within_hourly: nil, keep_within_daily: nil, keep_within_weekly: nil, keep_within_monthly: nil, keep_within_yearly: nil, keep_tags: [], hosts: [], tags: [], paths: [], compact: false, group_by: nil, dry_run: false, prune: false, unsafe_allow_remove_all: false, max_unused: nil, max_repack_size: nil, repack_cacheable_only: false, repack_uncompressed: false, repack_smaller_than: nil) ⇒ Enumerable<Yobi::ForgetGroup>
restic forget: applies a retention policy, removing snapshots
that don't match any keep_* rule.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/yobi/repository/forget.rb', line 38 def forget(keep_last: nil, keep_hourly: nil, keep_daily: nil, keep_weekly: nil, keep_monthly: nil, keep_yearly: nil, keep_within: nil, keep_within_hourly: nil, keep_within_daily: nil, keep_within_weekly: nil, keep_within_monthly: nil, keep_within_yearly: nil, keep_tags: [], hosts: [], tags: [], paths: [], compact: false, group_by: nil, dry_run: false, prune: false, unsafe_allow_remove_all: false, max_unused: nil, max_repack_size: nil, repack_cacheable_only: false, repack_uncompressed: false, repack_smaller_than: nil) argv = build_argv("forget") do |a| a.flag(:keep_last, keep_last) unless keep_last.nil? a.flag(:keep_hourly, keep_hourly) unless keep_hourly.nil? a.flag(:keep_daily, keep_daily) unless keep_daily.nil? a.flag(:keep_weekly, keep_weekly) unless keep_weekly.nil? a.flag(:keep_monthly, keep_monthly) unless keep_monthly.nil? a.flag(:keep_yearly, keep_yearly) unless keep_yearly.nil? a.flag(:keep_within, keep_within) unless keep_within.nil? a.flag(:keep_within_hourly, keep_within_hourly) unless keep_within_hourly.nil? a.flag(:keep_within_daily, keep_within_daily) unless keep_within_daily.nil? a.flag(:keep_within_weekly, keep_within_weekly) unless keep_within_weekly.nil? a.flag(:keep_within_monthly, keep_within_monthly) unless keep_within_monthly.nil? a.flag(:keep_within_yearly, keep_within_yearly) unless keep_within_yearly.nil? a.repeat_flag(:keep_tag, ) a.repeat_flag(:host, hosts) a.repeat_flag(:tag, ) a.repeat_flag(:path, paths) a.flag(:compact) if compact a.flag(:group_by, group_by) unless group_by.nil? a.flag(:dry_run) if dry_run a.flag(:prune) if prune a.flag(:unsafe_allow_remove_all) if unsafe_allow_remove_all a.flag(:max_unused, max_unused) unless max_unused.nil? a.flag(:max_repack_size, max_repack_size) unless max_repack_size.nil? a.flag(:repack_cacheable_only) if repack_cacheable_only a.flag(:repack_uncompressed) if repack_uncompressed a.flag(:repack_smaller_than, repack_smaller_than) unless repack_smaller_than.nil? end execution = run_restic(argv) ForgetGroups.new(execution[:exit_code], execution[:output]) end |
#init(copy_chunker_params: false, from_insecure_no_password: false, from_key_hint: nil, from_password_command: nil, from_password_file: nil, from_repo: nil, from_repository_file: nil, repository_version: nil) ⇒ Hash
restic init: creates the repository at #url.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/yobi/repository.rb', line 57 def init(copy_chunker_params: false, from_insecure_no_password: false, from_key_hint: nil, from_password_command: nil, from_password_file: nil, from_repo: nil, from_repository_file: nil, repository_version: nil) argv = build_argv("init") do |a| a.flag(:copy_chunker_params) if copy_chunker_params a.flag(:from_insecure_no_password) if from_insecure_no_password a.flag(:from_key_hint, from_key_hint) unless from_key_hint.nil? a.flag(:from_password_command, from_password_command) unless from_password_command.nil? a.flag(:from_password_file, from_password_file) unless from_password_file.nil? a.flag(:from_repo, from_repo) unless from_repo.nil? a.flag(:from_repository_file, from_repository_file) unless from_repository_file.nil? a.flag(:repository_version, repository_version) unless repository_version.nil? end execution = run_restic(argv) JSON.parse(execution[:output].to_s) end |
#inspect ⇒ String
42 43 44 |
# File 'lib/yobi/repository.rb', line 42 def inspect "#<#{self.class} url=#{url.inspect} password=#{redacted_password.inspect} backend_credentials=#{redacted_backend_credentials.inspect}>" end |
#key_add(host: nil, user: nil, new_insecure_no_password: false, new_password_file: nil) ⇒ true Also known as: add_key
restic key add: creates a new key (password) for this repository.
16 17 18 19 20 21 22 23 24 25 |
# File 'lib/yobi/repository/key.rb', line 16 def key_add(host: nil, user: nil, new_insecure_no_password: false, new_password_file: nil) argv = build_argv("key", "add") do |a| a.flag(:host, host) unless host.nil? a.flag(:user, user) unless user.nil? a.flag(:new_insecure_no_password) if new_insecure_no_password a.flag(:new_password_file, new_password_file) unless new_password_file.nil? end run_restic(argv) true end |
#key_list ⇒ Enumerable<Yobi::Key> Also known as: keys
restic key list: lists every key (password) associated with this repository.
31 32 33 34 |
# File 'lib/yobi/repository/key.rb', line 31 def key_list execution = run_restic(build_argv("key", "list")) Keys.new(execution[:output]) end |
#key_passwd(host: nil, user: nil, new_insecure_no_password: false, new_password_file: nil) ⇒ true Also known as: change_password
restic key passwd: creates a new key for this repository and
removes the key currently in use. Doesn't mutate this Repository
instance's own password:. Build a new Repository with the
rotated password to keep talking to this repository afterward.
47 48 49 50 51 52 53 54 55 56 |
# File 'lib/yobi/repository/key.rb', line 47 def key_passwd(host: nil, user: nil, new_insecure_no_password: false, new_password_file: nil) argv = build_argv("key", "passwd") do |a| a.flag(:host, host) unless host.nil? a.flag(:user, user) unless user.nil? a.flag(:new_insecure_no_password) if new_insecure_no_password a.flag(:new_password_file, new_password_file) unless new_password_file.nil? end run_restic(argv) true end |
#key_remove(id:) ⇒ true Also known as: remove_key
restic key remove: removes the given key ID from this repository.
Restic refuses to remove the key currently being used to access the
repository.
65 66 67 68 |
# File 'lib/yobi/repository/key.rb', line 65 def key_remove(id:) run_restic(build_argv("key", "remove", id)) true end |
#list(type) ⇒ Array<String>
restic list: lists every object ID of the given type in this
repository. Restic ignores --json for this command; each line is a
bare ID, except for :blobs, where each line is "data <id>" or "tree <id>".
11 12 13 14 15 |
# File 'lib/yobi/repository/list.rb', line 11 def list(type) argv = build_argv("list", type) execution = run_restic(argv) execution[:output].to_s.each_line.map(&:strip).reject(&:empty?) end |
#ls(snapshot_id:, dirs: [], hosts: [], human_readable: false, long: false, paths: [], recursive: false, reverse: false, sort: nil, tags: []) ⇒ Yobi::LsOutcome
restic ls: lists a snapshot's files/directories.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/yobi/repository/ls.rb', line 22 def ls(snapshot_id:, dirs: [], hosts: [], human_readable: false, long: false, paths: [], recursive: false, reverse: false, sort: nil, tags: []) argv = build_argv("ls", snapshot_id) do |a| a.repeat_flag(:host, hosts) a.flag(:human_readable) if human_readable a.flag(:long) if long a.repeat_flag(:path, paths) a.flag(:recursive) if recursive a.flag(:reverse) if reverse a.flag(:sort, sort) unless sort.nil? a.repeat_flag(:tag, ) a..append(dirs) end execution = run_restic(argv) LsOutcome.new(execution[:exit_code], execution[:output]) end |
#migrate(names: [], force: false) ⇒ true
restic migrate: checks which migrations can be applied to this
repository, or applies the given ones. Restic ignores --json for
this command, so there's no programmatic way to discover which
migrations are available. Run restic migrate directly for that.
13 14 15 16 17 18 19 |
# File 'lib/yobi/repository/migrate.rb', line 13 def migrate(names: [], force: false) argv = build_argv("migrate", names) do |a| a.flag(:force) if force end run_restic(argv) true end |
#mount(mountpoint:, hosts: [], paths: [], tags: [], allow_other: false, no_default_permissions: false, owner_root: false, path_templates: [], time_template: nil, ready_timeout: 10) {|mount| ... } ⇒ Yobi::Mount, Object
restic mount: serves this repository as a read-only FUSE
filesystem at mountpoint, which must already exist.
Without a block, returns a Mount once Restic reports itself
ready; call #stop yourself once done. With one, yields the Mount
and stops it automatically once the block returns or raises.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/yobi/repository/mount.rb', line 29 def mount(mountpoint:, hosts: [], paths: [], tags: [], allow_other: false, no_default_permissions: false, owner_root: false, path_templates: [], time_template: nil, ready_timeout: 10) argv = build_argv("mount") do |a| a.flag(:allow_other) if allow_other a.repeat_flag(:host, hosts) a.flag(:no_default_permissions) if a.flag(:owner_root) if owner_root a.repeat_flag(:path, paths) a.repeat_flag(:path_template, path_templates) a.repeat_flag(:tag, ) a.flag(:time_template, time_template) unless time_template.nil? a..append(mountpoint) end mount = run_restic_mount(argv, mountpoint: mountpoint.to_s, ready_timeout: ready_timeout) return mount unless block_given? begin yield mount ensure mount.stop end end |
#prune(dry_run: false, max_repack_size: nil, max_unused: nil, repack_cacheable_only: false, repack_smaller_than: nil, repack_uncompressed: false, unsafe_recover_no_free_space: nil) ⇒ true
restic prune: removes data no longer referenced by any snapshot.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/yobi/repository/prune.rb', line 15 def prune(dry_run: false, max_repack_size: nil, max_unused: nil, repack_cacheable_only: false, repack_smaller_than: nil, repack_uncompressed: false, unsafe_recover_no_free_space: nil) argv = build_argv("prune") do |a| a.flag(:dry_run) if dry_run a.flag(:max_repack_size, max_repack_size) unless max_repack_size.nil? a.flag(:max_unused, max_unused) unless max_unused.nil? a.flag(:repack_cacheable_only) if repack_cacheable_only a.flag(:repack_smaller_than, repack_smaller_than) unless repack_smaller_than.nil? a.flag(:repack_uncompressed) if repack_uncompressed a.flag(:unsafe_recover_no_free_space, unsafe_recover_no_free_space) unless unsafe_recover_no_free_space.nil? end run_restic(argv) true end |
#recover ⇒ true
restic recover: builds a new snapshot from any data found in this
repository that isn't referenced by an existing snapshot (e.g. after
an accidental #forget). Call #snapshots afterward to find the
recovered snapshot, if any was created.
11 12 13 14 |
# File 'lib/yobi/repository/recover.rb', line 11 def recover run_restic(build_argv("recover")) true end |
#repair_index(read_all_packs: false) ⇒ true
restic repair index: creates a new index based on the pack files
present in this repository. Successor to the deprecated
rebuild-index command.
11 12 13 14 15 16 17 |
# File 'lib/yobi/repository/repair.rb', line 11 def repair_index(read_all_packs: false) argv = build_argv("repair", "index") do |a| a.flag(:read_all_packs) if read_all_packs end run_restic(argv) true end |
#repair_packs(ids:) ⇒ true
restic repair packs: extracts intact blobs from the given pack
files, rebuilds the index to drop the damaged packs, and removes
them from the repository. Restic also writes a backup copy of each
given pack file (named pack-<id>) into the calling process's
current working directory before removing it. There's no flag to
disable this.
28 29 30 31 32 |
# File 'lib/yobi/repository/repair.rb', line 28 def repair_packs(ids:) argv = build_argv("repair", "packs", ids) run_restic(argv) true end |
#repair_snapshots(snapshot_ids: [], dry_run: false, forget: false, hosts: [], paths: [], tags: []) ⇒ true
restic repair snapshots: scans the given snapshots and generates
new ones with damaged directories/file contents removed. This
causes data loss for the content actually removed; prefer a fresh
#backup where the source data is still available.
Depends on a correct index. Call #repair_index first.
47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/yobi/repository/repair.rb', line 47 def repair_snapshots(snapshot_ids: [], dry_run: false, forget: false, hosts: [], paths: [], tags: []) argv = build_argv("repair", "snapshots", snapshot_ids) do |a| a.flag(:dry_run) if dry_run a.flag(:forget) if forget a.repeat_flag(:host, hosts) a.repeat_flag(:path, paths) a.repeat_flag(:tag, ) end run_restic(argv) true end |
#restore(snapshot_id:, target:, excludes: [], exclude_files: [], exclude_xattrs: [], hosts: [], iexcludes: [], iexclude_files: [], iincludes: [], iinclude_files: [], includes: [], include_files: [], include_xattrs: [], paths: [], tags: [], delete: false, dry_run: false, overwrite: nil, ownership_by_name: false, sparse: false, verbose: false, verify: false) {|message| ... } ⇒ Yobi::RestoreOutcome
restic restore: extracts a snapshot's contents to a target directory.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/yobi/repository/restore.rb', line 34 def restore(snapshot_id:, target:, excludes: [], exclude_files: [], exclude_xattrs: [], hosts: [], iexcludes: [], iexclude_files: [], iincludes: [], iinclude_files: [], includes: [], include_files: [], include_xattrs: [], paths: [], tags: [], delete: false, dry_run: false, overwrite: nil, ownership_by_name: false, sparse: false, verbose: false, verify: false, &block) argv = build_argv("restore", snapshot_id) do |a| a.flag(:target, target) a.repeat_flag(:exclude, excludes) a.repeat_flag(:exclude_file, exclude_files) a.repeat_flag(:exclude_xattr, exclude_xattrs) a.repeat_flag(:host, hosts) a.repeat_flag(:iexclude, iexcludes) a.repeat_flag(:iexclude_file, iexclude_files) a.repeat_flag(:iinclude, iincludes) a.repeat_flag(:iinclude_file, iinclude_files) a.repeat_flag(:include, includes) a.repeat_flag(:include_file, include_files) a.repeat_flag(:include_xattr, include_xattrs) a.repeat_flag(:path, paths) a.repeat_flag(:tag, ) a.flag(:delete) if delete a.flag(:dry_run) if dry_run a.flag(:overwrite, overwrite) unless overwrite.nil? a.flag(:ownership_by_name) if ownership_by_name a.flag(:sparse) if sparse a.short_flag(:vv) if verbose a.flag(:verify) if verify end execution = if block run_restic(argv) { |raw| (raw, &block) } else run_restic(argv) end RestoreOutcome.new(execution[:exit_code], execution[:output]) end |
#rewrite(snapshot_ids: [], hosts: [], tags: [], paths: [], excludes: [], exclude_files: [], iexcludes: [], iexclude_files: [], includes: [], include_files: [], iincludes: [], iinclude_files: [], dry_run: false, forget: false, new_host: nil, new_time: nil, snapshot_summary: false) ⇒ true
restic rewrite: creates new snapshots from existing ones with
exclude/include filters applied, or metadata changed. With
snapshot_ids: and the other filters all left at their defaults,
rewrites every snapshot in the repository.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/yobi/repository/rewrite.rb', line 28 def rewrite(snapshot_ids: [], hosts: [], tags: [], paths: [], excludes: [], exclude_files: [], iexcludes: [], iexclude_files: [], includes: [], include_files: [], iincludes: [], iinclude_files: [], dry_run: false, forget: false, new_host: nil, new_time: nil, snapshot_summary: false) argv = build_argv("rewrite", snapshot_ids) do |a| a.repeat_flag(:host, hosts) a.repeat_flag(:tag, ) a.repeat_flag(:path, paths) a.repeat_flag(:exclude, excludes) a.repeat_flag(:exclude_file, exclude_files) a.repeat_flag(:iexclude, iexcludes) a.repeat_flag(:iexclude_file, iexclude_files) a.repeat_flag(:include, includes) a.repeat_flag(:include_file, include_files) a.repeat_flag(:iinclude, iincludes) a.repeat_flag(:iinclude_file, iinclude_files) a.flag(:dry_run) if dry_run a.flag(:forget) if forget a.flag(:new_host, new_host) unless new_host.nil? a.flag(:new_time, new_time) unless new_time.nil? a.flag(:snapshot_summary) if snapshot_summary end run_restic(argv) true end |
#snapshots(tags: [], hosts: [], paths: [], compact: false, group_by: nil, latest: nil) ⇒ Enumerable<Yobi::Snapshot>
restic snapshots: lists snapshots, optionally filtered.
16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/yobi/repository/snapshots.rb', line 16 def snapshots(tags: [], hosts: [], paths: [], compact: false, group_by: nil, latest: nil) argv = build_argv("snapshots") do |a| a.repeat_flag(:tag, ) a.repeat_flag(:host, hosts) a.repeat_flag(:path, paths) a.flag(:compact) if compact a.flag(:group_by, group_by) unless group_by.nil? a.flag(:latest, latest) unless latest.nil? end execution = run_restic(argv) Snapshots.new(execution[:output]) end |
#stats(snapshot_ids: [], hosts: [], mode: nil, paths: [], tags: []) ⇒ Hash
restic stats: accumulates statistics about the repository's data.
23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/yobi/repository/stats.rb', line 23 def stats(snapshot_ids: [], hosts: [], mode: nil, paths: [], tags: []) argv = build_argv("stats", snapshot_ids) do |a| a.repeat_flag(:host, hosts) unless mode.nil? a.flag(:mode, STATS_MODES.fetch(mode) { raise ArgumentError, "invalid mode: #{mode.inspect}" }) end a.repeat_flag(:path, paths) a.repeat_flag(:tag, ) end execution = run_restic(argv) JSON.parse(execution[:output].to_s) end |
#tag(snapshot_ids: [], add: [], remove: [], set: [], tags: [], hosts: [], paths: []) ⇒ Yobi::TagOutcome
restic tag: modifies tags on existing snapshots. Tags are part of
a snapshot's content-addressed identity, so changing them produces a
new snapshot ID for each affected snapshot.
20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/yobi/repository/tag.rb', line 20 def tag(snapshot_ids: [], add: [], remove: [], set: [], tags: [], hosts: [], paths: []) argv = build_argv("tag", snapshot_ids) do |a| a.repeat_flag(:add, add) a.repeat_flag(:remove, remove) a.repeat_flag(:set, set) a.repeat_flag(:tag, ) a.repeat_flag(:host, hosts) a.repeat_flag(:path, paths) end execution = run_restic(argv) TagOutcome.new(execution[:exit_code], execution[:output]) end |
#unlock(remove_all: false) ⇒ true
restic unlock: removes stale locks left by other Restic processes.
9 10 11 12 13 14 15 |
# File 'lib/yobi/repository/unlock.rb', line 9 def unlock(remove_all: false) argv = build_argv("unlock") do |a| a.flag(:remove_all) if remove_all end run_restic(argv) true end |