require 'pathname' require 'yaml/store' require 'auto_network/pool' module AutoNetwork # This is a specialized subclass of `YAML::Store` that manages data # persistence for {AutoNetwork::PoolManager} instances. # # In addition to managing serialization, the `YAML::Store` parent class also # provides facilities for synchronizing state across multiple Vagrant # processes. This subclass adds functionality for handling upgrades when the # AutoNetwork serialization format changes. # # Format history: # # - **Version 1:** A single instance of {AutoNetwork::Pool} instance # serialized to YAML. Never formalized with a version number. # # - **Version 2:** A hash containing a `poolfile_version` set to `2` and a # `pools` sub-hash with keys created from Vagrant provider names and # values of a single {AutoNetwork::Pool} instance serialized to YAML. # # @example Version 1 # --- !ruby/object:AutoNetwork::Pool # # Single serialized AutoNetwork::Pool # # @example Version 2 # --- # poolfile_version: 2 # pools: # some_vagrant_provider_name: !ruby/object:AutoNetwork::Pool # # Single serialized AutoNetwork::Pool # # @since 1.0.0 class PoolStorage < YAML::Store # An integer indicating the current AutoNetwork serialization format. POOLFILE_VERSION = 2 # The data structure that {AutoNetwork::PoolManager} instances # expect to be available in all pool files. POOLFILE_SKELETON = { 'poolfile_version' => POOLFILE_VERSION, 'pools' => {}, } # Creates a new pool file at a target location and fills it with default # data. # # @see POOLFILE_SKELETON # # @param path [String, Pathname] the location of the new pool file. # @return [void] def self.init(path) path = Pathname.new(path) dir = path.dirname dir.mkpath unless dir.exist? File.write(path, POOLFILE_SKELETON.to_yaml) end private # Override the method inherited from `YAML::Store`. All `PStore` instances # expect `load` to strictly return a `Hash`. This override allows us to # perform on-the-fly upgrading of data loaded from old pool files and # ensure the right structure is returned. # # @api private # # @param content [String] serialized YAML read from the pool file. # @return [Hash] def load(content) data = super(content) if data.is_a? AutoNetwork::Pool upgrade_from_version_1! data else data end end # The loosely defined "version 1" of the pool file just serialized a single # {AutoNetwork::Pool} object. All AutoNetwork releases that used Version 1 # files only supported the Vagrant VirtualBox provider, so we return a new # Hash-based data structure that assigns the old Pool to the `virtualbox` # provider. # # @api private # # @param data [AutoNetwork::Pool] the old pool object. # @return [Hash] a hash conforming to the current {POOLFILE_VERSION}. def upgrade_from_version_1!(data) { 'poolfile_version' => POOLFILE_VERSION, 'pools' => { 'virtualbox' => data, }, } end end end