class Restic::Service::Conf

The overall service configuration

This is the API side of the service configuration. The configuration is usually stored on disk in YAML and

The YAML format is as follows:

# The path to the underlying tools
tools:
  restic: /opt/restic
  rclone: /opt/rclone

# The targets. The only generic parts of a target definition
# are the name and type. The rest is target-specific
targets:
  - name: a_restic_sftp_target
    type: restic-sftp

See the README.md for more details about available targets

Constants

BANDWIDTH_SCALES
TARGET_CLASS_FROM_TYPE
TOOLS

Attributes

bandwidth_limit[R]

The bandwidth limit in bytes/s

Default is nil (none)

@return [nil,Integer]

conf_path[R]

The configuration path

@return [Pathname]

period[R]

The polling period in seconds

Default is 1h (3600s)

@return [Integer]

Public Class Methods

default_conf() click to toggle source

The default (empty) configuration

# File lib/restic/service/conf.rb, line 29
def self.default_conf
    Hash['targets' => [],
         'period' => 3600,
         'bandwidth_limit' => nil,
         'tools' => Hash.new]
end
load(path) click to toggle source

Load a configuration file

@param [Pathname] @return [Conf] @raise (see normalize_yaml)

# File lib/restic/service/conf.rb, line 100
def self.load(path)
    if !path.file?
        return Conf.new(Pathname.new(""))
    end

    yaml = YAML.load(path.read) || Hash.new
    yaml = normalize_yaml(yaml)

    conf = Conf.new(path.dirname)
    conf.load_from_yaml(yaml)
    conf
end
new(conf_path) click to toggle source
# File lib/restic/service/conf.rb, line 132
def initialize(conf_path)
    @conf_path = conf_path
    @targets = Hash.new
    @period  = 3600
    @tools   = Hash.new
    TOOLS.each do |tool_name|
        @tools[tool_name] = find_in_path(tool_name)
    end
end
normalize_yaml(yaml) click to toggle source

Normalizes and validates a configuration hash, as stored in YAML

@raise [InvalidConfigurationFile]

# File lib/restic/service/conf.rb, line 59
def self.normalize_yaml(yaml)
    yaml = default_conf.merge(yaml)
    TOOLS.each do |tool_name|
        yaml['tools'][tool_name] ||= tool_name
    end

    yaml['auto_update'] ||= Array.new

    target_names = Array.new
    yaml['targets'] = yaml['targets'].map do |target|
        if !target['name']
            raise InvalidConfigurationFile, "missing 'name' field in target"
        elsif !target['type']
            raise InvalidConfigurationFile, "missing 'type' field in target"
        end

        target_class = target_class_from_type(target['type'])
        if !target_class
            raise InvalidConfigurationFile, "target type #{target['type']} does not exist, "\
                "available targets: #{TARGET_CLASS_FROM_TYPE.keys.sort.join(", ")}"
        end

        name = target['name'].to_s
        if target_names.include?(name)
            raise InvalidConfigurationFile, "duplicate target name '#{name}'"
        end

        target = target.dup
        target['name'] = name
        target = target_class.normalize_yaml(target)
        target_names << name
        target
    end
    yaml
end
parse_bandwidth_limit(limit) click to toggle source
# File lib/restic/service/conf.rb, line 148
def self.parse_bandwidth_limit(limit)
    if !limit.respond_to?(:to_str)
        return Integer(limit)
    else
        match = /^(\d+)\s*(k|m|g)?$/.match(limit.downcase)
        if match
            return Integer(match[1]) * BANDWIDTH_SCALES.fetch(match[2])
        else
            raise ArgumentError, "cannot interpret '#{limit}' as a valid bandwidth limit, give a plain number in bytes or use the k, M and G suffixes"
        end
    end
end
target_class_from_type(type) click to toggle source

Returns the target class that will handle the given target type

@param [String] type the type as represented in the YAML file

# File lib/restic/service/conf.rb, line 48
def self.target_class_from_type(type)
    if target_class = TARGET_CLASS_FROM_TYPE[type]
        return target_class
    else
        raise InvalidConfigurationFile, "target type #{type} does not exist, available targets: #{TARGET_CLASS_FROM_TYPE.keys.sort.join(", ")}"
    end
end

Public Instance Methods

auto_update_rclone?() click to toggle source
# File lib/restic/service/conf.rb, line 235
def auto_update_rclone?
    @auto_update_rclone
end
auto_update_restic?() click to toggle source
# File lib/restic/service/conf.rb, line 227
def auto_update_restic?
    @auto_update_restic
end
auto_update_restic_service?() click to toggle source
# File lib/restic/service/conf.rb, line 223
def auto_update_restic_service?
    @auto_update_restic_service
end
conf_keys_path_for(target) click to toggle source

The path to the key file for the given target

# File lib/restic/service/conf.rb, line 162
def conf_keys_path_for(target)
    conf_path.join("keys", "#{target.name}.keys")
end
each_target(&block) click to toggle source

Enumerates the targets

# File lib/restic/service/conf.rb, line 180
def each_target(&block)
    @targets.each_value(&block)
end
find_in_path(name) click to toggle source

@api private

Helper that resolves a binary in PATH

# File lib/restic/service/conf.rb, line 192
def find_in_path(name)
    ENV['PATH'].split(File::PATH_SEPARATOR).each do |p|
        candidate = Pathname.new(p).join(name)
        if candidate.file?
            return candidate
        end
    end
    nil
end
load_from_yaml(yaml) click to toggle source

Add the information stored in a YAML-like hash into this configuration

@param [Hash] the configuration, following the documented

configuration format (see {Conf})

@return [void]

# File lib/restic/service/conf.rb, line 249
def load_from_yaml(yaml)
    load_tools_from_yaml(yaml['tools'])
    @period = Integer(yaml['period'])
    @bandwidth_limit = if limit_yaml = yaml['bandwidth_limit']
                           Conf.parse_bandwidth_limit(limit_yaml)
                       end

    yaml['auto_update'].each do |update_target, do_update|
        if update_target == 'restic-service'
            @auto_update_restic_service = do_update
        elsif update_target == 'restic'
            @auto_update_restic = do_update
        elsif update_target == 'rclone'
            @auto_update_rclone = do_update
        end
    end

    yaml['targets'].each do |yaml_target|
        type = yaml_target['type']
        target_class = Conf.target_class_from_type(type)
        target = target_class.new(yaml_target['name'])
        target.setup_from_conf(self, yaml_target)
        register_target(target)
    end
end
load_tools_from_yaml(yaml) click to toggle source

@api private

Helper for {#load_from_yaml}

# File lib/restic/service/conf.rb, line 278
def load_tools_from_yaml(yaml)
    TOOLS.each do |tool_name|
        tool_path = Pathname.new(yaml[tool_name])
        if tool_path.relative?
            tool_path = find_in_path(tool_path)
        end

        exists = tool_path.file?
        STDERR.puts "#{tool_path} does not exist" unless exists
        @tools[tool_name] = [tool_path, exists]
    end
end
rclone_platform() click to toggle source
# File lib/restic/service/conf.rb, line 239
def rclone_platform
    @auto_update_rclone
end
register_target(target) click to toggle source

Registers a target

# File lib/restic/service/conf.rb, line 185
def register_target(target)
    @targets[target.name] = target
end
restic_platform() click to toggle source
# File lib/restic/service/conf.rb, line 231
def restic_platform
    @auto_update_restic
end
target_by_name(name) click to toggle source

Gets a target configuration

@param [String] name the target name @return [Target] @raise NoSuchTarget

# File lib/restic/service/conf.rb, line 171
def target_by_name(name)
    if target = @targets[name]
        target
    else
        raise NoSuchTarget, "no target named '#{name}'"
    end
end
tool_available?(tool_name) click to toggle source

Checks whether a given tool is available

@param [String] @return [Boolean]

# File lib/restic/service/conf.rb, line 206
def tool_available?(tool_name)
    _, available = @tools[tool_name]
    available
end
tool_path(tool_name, only_if_present: true) click to toggle source

The full path of a given tool

@param [String] @return [Pathname]

# File lib/restic/service/conf.rb, line 215
def tool_path(tool_name, only_if_present: true)
    if tool = @tools[tool_name]
        tool[0] if tool[1] || !only_if_present
    else
        raise ArgumentError, "cound not find '#{tool_name}'"
    end
end