Projects
home:rottame:rubygems
rubygem-ygg_provisioner
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 8
View file
rubygem-ygg_provisioner.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Mon Dec 1 16:51:03 UTC 2025 - Angelo Grossini <rottame@intercom.it> + +- update to 1.5.0 + +------------------------------------------------------------------- Tue Sep 12 09:04:44 UTC 2023 - Angelo Grossini <rottame@intercom.it> - revert test
View file
rubygem-ygg_provisioner.spec
Changed
@@ -1,16 +1,20 @@ %define mod_name ygg_provisioner %define mod_full_name %{mod_name}-%{version} Name: rubygem-ygg_provisioner -Version: 1.4.1 +Version: 1.5.0 Release: 0 Summary: Yggdra base provisioner License: Apache-2.0 Group: Development/Languages/Ruby URL: https://intercom.it Source: https://gems.intercom.it/gems/%{mod_full_name}.gem +Source1: gem2rpm.yml BuildRequires: %{ruby >= 1.9} BuildRequires: %{rubygem gem2rpm} BuildRequires: ruby-macros >= 5 +Suggests: %{rubygem sqlite3} +Suggests: %{rubygem kyotocabinet} +Suggests: %{rubygem lmdb} %description Yggdra base provisioner @@ -21,6 +25,8 @@ %install %gem_install \ + --symlink-binaries \ + --no-ri --no-rdoc \ --doc-files="README" \ -f
View file
gem2rpm.yml
Added
@@ -0,0 +1,4 @@ +--- +:disable_docs: true +:patches: +:sources: \ No newline at end of file
View file
ygg_provisioner-1.4.1.gem/data/.gitignore
Deleted
@@ -1,1 +0,0 @@ -pkg/
View file
ygg_provisioner-1.4.1.gem/data/.gitlab-ci.yml
Deleted
@@ -1,19 +0,0 @@ -stages: - - build-gem - -build: - stage: build-gem - #rules: - # - if: '$CI_COMMIT_BRANCH == "deploy"' - # when: always - # - if: '$CI_COMMIT_BRANCH == "staging"' - # when: always - # - when: never - image: ruby:2.4 - script: | - mkdir -p $HOME/.gem - touch $HOME/.gem/credentials - chmod 0600 $HOME/.gem/credentials - printf -- "---\n:rubygems_api_key: Bearer ${GEMINABOX_API_KEY}\n" > $HOME/.gem/credentials - gem build *.gemspec --output=release.gem - gem push release.gem --host https://gems.intercom.it
View file
ygg_provisioner-1.4.1.gem/data/lib/ygg/provisioner/colon_delimited_file.rb
Deleted
@@ -1,67 +0,0 @@ -# -# Copyright (C) 2013-2013, Intercom Srl, Daniele Orlandi -# -# Author:: Daniele Orlandi <daniele@orlandi.com> -# Lele Forzani <lele@windmill.it> -# Alfredo Cerutti <acerutti@intercom.it> -# -# License:: You can redistribute it and/or modify it under the terms of the LICENSE file. -# - -require 'ygg/provisioner/file_switcher' - -module Ygg -module Provisioner - -class ColonDelimitedFile - include FileSwitcher - - def initialize(filename, opts = {}) - @entries = {} - @tmp_filename = opts:tmpname || filename + '.new' - - File::open(filename, 'r') do |f| - f.each_line do |line| - line.chomp! - entry = line.split(':', opts:limit || -1) - - @entriesentry0 = entry - end - end rescue Errno::ENOENT - - yield self - - File::open(@tmp_filename, 'w') do |f| - @entries.each do |entrykey, entry| - f << entry.join(':') + "\n" - end - end - - uid = opts:uid - gid = opts:gid - begin - uid = File.stat(filename).uid - gid = File.stat(filename).gid - rescue Errno::ENOENT - end - - FileUtils::chown(uid, gid, @tmp_filename) - - switch_files(@tmp_filename, filename) - end - - def set(data) - @entriesdata0 = data - end - - def (key) - @entrieskey - end - - def delete(key) - @entries.delete(key) - end -end - -end -end
View file
ygg_provisioner-1.4.1.gem/data/lib/ygg/provisioner/file_switcher.rb
Deleted
@@ -1,23 +0,0 @@ -# -# Copyright (C) 2013-2013, Intercom Srl, Daniele Orlandi -# -# Author:: Daniele Orlandi <daniele@orlandi.com> -# Lele Forzani <lele@windmill.it> -# Alfredo Cerutti <acerutti@intercom.it> -# -# License:: You can redistribute it and/or modify it under the terms of the LICENSE file. -# - -module Ygg -module Provisioner - -module FileSwitcher - def switch_files(f1, f2) - begin ; File.unlink(f2 + '.old') rescue Errno::ENOENT ; end - begin ; File.link(f2, f2 + '.old') rescue Errno::ENOENT ; end - File.rename(f1, f2) - end -end - -end -end
View file
ygg_provisioner-1.4.1.gem/data/lib/ygg/provisioner/tools.rb
Deleted
@@ -1,179 +0,0 @@ -# -# Copyright (C) 2013-2013, Intercom Srl, Daniele Orlandi -# -# Author:: Daniele Orlandi <daniele@orlandi.com> -# Lele Forzani <lele@windmill.it> -# Alfredo Cerutti <acerutti@intercom.it> -# -# License:: You can redistribute it and/or modify it under the terms of the LICENSE file. -# - -require 'fiber' -require 'publisher' -require 'eventmachine' - - - - - - - - -module EventMachine - def self.popen3(*args) - new_stderr = $stderr.dup - rd, wr = IO::pipe - $stderr.reopen wr - connection = EM.popen(*args) - $stderr.reopen new_stderr - EM.attach rd, Popen3StderrHandler, connection - connection - end - - class Popen3StderrHandler < EventMachine::Connection - def initialize(connection) - @connection = connection - end - - def receive_data(data) - @connection.receive_stderr(data) - end - end -end - - -module Ygg -module Provisioner - -module Tools - def switch_files(f1, f2) - begin ; File.unlink(f2 + '.old') rescue Errno::ENOENT ; end - begin ; File.link(f2, f2 + '.old') rescue Errno::ENOENT ; end - File.rename(f1, f2) - end - - class CommandError < StandardError - attr_accessor :status - - def initialize(msg, status) - super msg - self.status = status - end - end - - class CommandRunner < EventMachine::Connection - extend Publisher - - can_fire :success, :failure, :receive_data, :receive_stderr - - # @private - def initialize(collect_data = true) - super - @out = '' - @collect_data = collect_data - end - - def self.open(cmd, collect_data = true) - EventMachine.popen3(cmd, self, collect_data) - end - - def receive_data(data) - @out += data if @collect_data - fire :receive_data, data - end - - def receive_stderr(data) - fire :receive_stderr, data - end - - def unbind - if get_status.success? - fire :success, @out, get_status - else - fire :failure, @out, get_status - end - end - end - - def run_command(cmd, opts = {}, &block) - if block - run_command_async(cmd, opts, &block) - - nil - else - fiber = Fiber.current - - run_command(cmd, opts) do |out, status| - fiber.resume(out, status) - end - - (out, status) = Fiber.yield - - if opts:with_status - out, status - else - raise CommandError.new('Error running command', status) if !status.success? - out - end - end - end - - #private - def run_command_async(cmd, opts = {}, &block) - out = '' - - plog = respond_to?(:plog) ? self.plog : opts:plog - - old_euid = Process::UID.eid - old_egid = Process::GID.eid - - if opts:uid - # This isn't the proper way to drop the privileges for the child process!!! FIXME TODO - Process::UID.grant_privilege(opts:uid) - end - - if opts:env - # This isn't the proper way to set the environment for the child process!!! FIXME TODO - opts:env.each { |k,v| ENVk = v } - end - - plog.debug " = running command #{cmd.inspect}" if plog - - c = CommandRunner.open(cmd, !opts:progress) - - if opts:send_data - c.send_data(opts:send_data) - end - - if opts:progress - c.on(:receive_data) do |out| - yield out, nil if block_given? - end - - c.on(:receive_stderr) do |out| - opts:stderr_handler.call(out) if opts:stderr_handler - end - end - - c.on(:success) do |out, result| - out.each_line { |l| plog.info l } if plog - - yield out, result if block_given? - end - - c.on(:failure) do |out, result| - out.each_line { |l| plog.error l } if plog - - yield out, result if block_given? - end - - Process::UID.eid = old_euid - Process::GID.eid = old_egid - - nil - end - -end - -end -end
View file
ygg_provisioner-1.4.1.gem/checksums.yaml.gz -> ygg_provisioner-1.5.0.gem/checksums.yaml.gz
Changed
@@ -1,7 +1,7 @@ --- SHA256: - metadata.gz: d9f2873d1c242bb177ab78e0ce269baad4451ef98fefae6a376c2be69e90aded - data.tar.gz: 7d90b7f4e3b078a59f097980858b8fce13022c44b461725a347169f1673d4585 + metadata.gz: 5848ee3c07f250b2417e0f0e7b759eb1fbaea2fe0da60c7c2342f47144a0014b + data.tar.gz: 342c05c1df38b2aa6c5f47677c0dd1fdf471e77f2b726ddd1b088396ba4fb1fd SHA512: - metadata.gz: ed016c953e3f2f9a5fcf5ff4dd2e0dc5fb5e1c3a19f8e62236fed518914fa0935cedbf3c797ac05f0298958f5953de3e078fdfdcc083573088eb318a38dce61a - data.tar.gz: 04b47f1fbb486eb8cbcd5adf21166308e8607b347de242a8ae70f256796e304bba8b15c9547b4db221d7bc8c4ae739850197541860b5373fccb7fafac8a9cd15 + metadata.gz: 17926a8885236290a164fee4d4febc8e68471a716af68df57e18842740a4e50c32ec0948b1c06b0da58811e9bf18ce9259313aebde7999eeeefb0b6add5fb93d + data.tar.gz: 5e5027fda497785ee720a6e591d4d88eca6e59dacb2ddce45da9f64d5b8a70374aae8a14d9b791a95d4de99b58cab60b5a91bdd6f05e478ca713f6b3725e456c
View file
ygg_provisioner-1.4.1.gem/data/Gemfile -> ygg_provisioner-1.5.0.gem/data/Gemfile
Changed
@@ -4,3 +4,6 @@ # Specify your gem's dependencies in a.gemspec gemspec +gem 'kyotocabinet' +gem 'lmdb' +gem 'sqlite3'
View file
ygg_provisioner-1.5.0.gem/data/Gemfile.lock
Added
@@ -0,0 +1,104 @@ +PATH + remote: . + specs: + ygg_provisioner (1.5.0) + eventmachine (~> 1.2.3) + publisher + tomte-agents + +GEM + remote: http://rubygems.org/ + remote: http://gems.intercom.it/ + specs: + activesupport (6.0.6.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + amq-protocol (2.3.0) + amqp (1.8.0) + amq-protocol (>= 2.2.0) + eventmachine + awesome_print (1.9.2) + bson (1.12.5) + bson_ext (1.12.5) + bson (~> 1.12.5) + byebug (11.1.3) + case (0.5.2.1) + coderay (1.1.3) + concurrent-ruby (1.3.5) + diff-lcs (1.3) + eventmachine (1.2.5) + i18n (1.14.7) + concurrent-ruby (~> 1.0) + json (2.7.6) + kyotocabinet (1.33) + lmdb (0.6.1) + method_source (1.1.0) + mime-types (3.3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + minitest (5.15.0) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + publisher (1.1.2) + rake (12.3.3) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-support (3.10.2) + sqlite3 (1.3.9) + thor (0.20.3) + thread_safe (0.3.6) + tomte-agents (1.1.1) + tomte-core (>= 1.2.0) + tomte-protocol (>= 1.1.1) + tomte-core (1.2.2) + activesupport (>= 5.0.0) + bson (>= 1.12.5) + bson_ext (>= 1.12.5) + case (>= 0.5.2) + json (>= 2.2.0) + mime-types + pry (>= 0.12.2) + rake + thor (>= 0.20.3) + uuidtools (>= 2.1.5) + wml-compat (>= 0.8.6) + tomte-protocol (1.2.3) + amqp (>= 1.8.0) + tomte-core (>= 1.2.0) + tzinfo (1.2.7) + thread_safe (~> 0.1) + uuidtools (3.0.0) + wml-compat (0.8.13) + awesome_print (>= 1.1.0) + uuidtools (>= 2.1.0) + zeitwerk (2.2.2) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + byebug + kyotocabinet + lmdb + pry + rake + rspec + sqlite3 + ygg_provisioner! + +BUNDLED WITH + 2.2.34
View file
ygg_provisioner-1.5.0.gem/data/bin/ygg_provisioner_convert_store
Added
@@ -0,0 +1,47 @@ +#!/usr/bin/ruby + +require 'rubygems' + +require 'active_support/core_ext' +require 'ygg/provisioner/model_store' + +module Ygg +module Provisioner + + # Simple CLI utility to migrate all records from one store to another. + # + # Usage: + # ygg_provisioner_convert_store SOURCE_PATH DESTINATION_PATH + # + # Both SOURCE_PATH and DESTINATION_PATH are opaque specifications understood + # by {Ygg::Provisioner::ModelStore.instantiate}, typically filesystem paths + # such as: + # - /path/to/db.kch + # - /path/to/db.sqlite3 + class StoreConverter + def self.run(argv) + unless argv.size == 2 + $stderr.puts "Usage: #{$0} SOURCE DESTINATION" + exit 1 + end + + source_spec, dest_spec = argv + + source = ModelStore.instantiate(source_spec) + dest = ModelStore.instantiate(dest_spec) + + migrated = 0 + + source.each_pair do |key, value| + dest.put(key, value) + migrated += 1 + end + + $stdout.puts "Migrated #{migrated} records from #{source_spec.inspect} to #{dest_spec.inspect}" + end + end + +end +end + +Ygg::Provisioner::StoreConverter.run(ARGV)
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/internals/colon_delimited_file.rb
Added
@@ -0,0 +1,69 @@ +# +# Copyright (C) 2013-2013, Intercom Srl, Daniele Orlandi +# +# Author:: Daniele Orlandi <daniele@orlandi.com> +# Lele Forzani <lele@windmill.it> +# Alfredo Cerutti <acerutti@intercom.it> +# +# License:: You can redistribute it and/or modify it under the terms of the LICENSE file. +# + +require 'ygg/provisioner/internals/file_switcher' + +module Ygg +module Provisioner +module Internals + +class ColonDelimitedFile + include FileSwitcher + + def initialize(filename, opts = {}) + @entries = {} + @tmp_filename = opts:tmpname || filename + '.new' + + File::open(filename, 'r') do |f| + f.each_line do |line| + line.chomp! + entry = line.split(':', opts:limit || -1) + + @entriesentry0 = entry + end + end rescue Errno::ENOENT + + yield self + + File::open(@tmp_filename, 'w') do |f| + @entries.each do |entrykey, entry| + f << entry.join(':') + "\n" + end + end + + uid = opts:uid + gid = opts:gid + begin + uid = File.stat(filename).uid + gid = File.stat(filename).gid + rescue Errno::ENOENT + end + + FileUtils::chown(uid, gid, @tmp_filename) + + switch_files(@tmp_filename, filename) + end + + def set(data) + @entriesdata0 = data + end + + def (key) + @entrieskey + end + + def delete(key) + @entries.delete(key) + end +end + +end +end +end
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/internals/file_switcher.rb
Added
@@ -0,0 +1,25 @@ +# +# Copyright (C) 2013-2013, Intercom Srl, Daniele Orlandi +# +# Author:: Daniele Orlandi <daniele@orlandi.com> +# Lele Forzani <lele@windmill.it> +# Alfredo Cerutti <acerutti@intercom.it> +# +# License:: You can redistribute it and/or modify it under the terms of the LICENSE file. +# + +module Ygg +module Provisioner +module Internals + +module FileSwitcher + def switch_files(f1, f2) + begin ; File.unlink(f2 + '.old') rescue Errno::ENOENT ; end + begin ; File.link(f2, f2 + '.old') rescue Errno::ENOENT ; end + File.rename(f1, f2) + end +end + +end +end +end
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/internals/tools.rb
Added
@@ -0,0 +1,181 @@ +# +# Copyright (C) 2013-2013, Intercom Srl, Daniele Orlandi +# +# Author:: Daniele Orlandi <daniele@orlandi.com> +# Lele Forzani <lele@windmill.it> +# Alfredo Cerutti <acerutti@intercom.it> +# +# License:: You can redistribute it and/or modify it under the terms of the LICENSE file. +# + +require 'fiber' +require 'publisher' +require 'eventmachine' + + + + + + + + +module EventMachine + def self.popen3(*args) + new_stderr = $stderr.dup + rd, wr = IO::pipe + $stderr.reopen wr + connection = EM.popen(*args) + $stderr.reopen new_stderr + EM.attach rd, Popen3StderrHandler, connection + connection + end + + class Popen3StderrHandler < EventMachine::Connection + def initialize(connection) + @connection = connection + end + + def receive_data(data) + @connection.receive_stderr(data) + end + end +end + + +module Ygg +module Provisioner +module Internals + +module Tools + def switch_files(f1, f2) + begin ; File.unlink(f2 + '.old') rescue Errno::ENOENT ; end + begin ; File.link(f2, f2 + '.old') rescue Errno::ENOENT ; end + File.rename(f1, f2) + end + + class CommandError < StandardError + attr_accessor :status + + def initialize(msg, status) + super msg + self.status = status + end + end + + class CommandRunner < EventMachine::Connection + extend Publisher + + can_fire :success, :failure, :receive_data, :receive_stderr + + # @private + def initialize(collect_data = true) + super + @out = '' + @collect_data = collect_data + end + + def self.open(cmd, collect_data = true) + EventMachine.popen3(cmd, self, collect_data) + end + + def receive_data(data) + @out += data if @collect_data + fire :receive_data, data + end + + def receive_stderr(data) + fire :receive_stderr, data + end + + def unbind + if get_status.success? + fire :success, @out, get_status + else + fire :failure, @out, get_status + end + end + end + + def run_command(cmd, opts = {}, &block) + if block + run_command_async(cmd, opts, &block) + + nil + else + fiber = Fiber.current + + run_command(cmd, opts) do |out, status| + fiber.resume(out, status) + end + + (out, status) = Fiber.yield + + if opts:with_status + out, status + else + raise CommandError.new('Error running command', status) if !status.success? + out + end + end + end + + #private + def run_command_async(cmd, opts = {}, &block) + out = '' + + plog = respond_to?(:plog) ? self.plog : opts:plog + + old_euid = Process::UID.eid + old_egid = Process::GID.eid + + if opts:uid + # This isn't the proper way to drop the privileges for the child process!!! FIXME TODO + Process::UID.grant_privilege(opts:uid) + end + + if opts:env + # This isn't the proper way to set the environment for the child process!!! FIXME TODO + opts:env.each { |k,v| ENVk = v } + end + + plog.debug " = running command #{cmd.inspect}" if plog + + c = CommandRunner.open(cmd, !opts:progress) + + if opts:send_data + c.send_data(opts:send_data) + end + + if opts:progress + c.on(:receive_data) do |out| + yield out, nil if block_given? + end + + c.on(:receive_stderr) do |out| + opts:stderr_handler.call(out) if opts:stderr_handler + end + end + + c.on(:success) do |out, result| + out.each_line { |l| plog.info l } if plog + + yield out, result if block_given? + end + + c.on(:failure) do |out, result| + out.each_line { |l| plog.error l } if plog + + yield out, result if block_given? + end + + Process::UID.eid = old_euid + Process::GID.eid = old_egid + + nil + end + +end + +end +end +end
View file
ygg_provisioner-1.4.1.gem/data/lib/ygg/provisioner/model.rb -> ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/model.rb
Changed
@@ -8,188 +8,247 @@ # License:: You can redistribute it and/or modify it under the terms of the LICENSE file. # -require 'kyotocabinet' -require 'ygg/provisioner/tools' -require 'ygg/provisioner/file_switcher' +require 'ostruct' +require 'active_support/core_ext' +require 'ygg/provisioner/internals/tools' +require 'ygg/provisioner/internals/file_switcher' +require 'ygg/provisioner/model_store' module Ygg module Provisioner +# +# == Ygg::Provisioner::Model +# +# Base class for provisioner models persisted in a database. +# It builds on top of +OpenStruct+ and provides: +# * a split between configuration (+cfg+) and runtime state (+state+) +# * persistence primitives backed by store +# * a small error hierarchy and database–checking utilities +# +# Subclasses are expected to: +# * set {.db_path} to the database path +# * configure {.state_persistant_keys} with the list of keys that belong +# to the local state +# * add whatever behaviour they need on top of the persistence layer +# +# A minimal example: +# class MyModel < Ygg::Provisioner::Model +# self.state_persistant_keys = :status +# end +# +# m = MyModel.new(:id => "my-id", :name => "example") +# m.update_state(:status => "pending") +# m.save! +# +# MyModel.find("my-id").status # => "pending" +# class Model < OpenStruct + # Generic superclass for all model‑related errors. class Error < StandardError ; end + + # Error raised when the data contained in the model are not valid + # from a domain point of view. class DataError < Error ; end + + # Error raised when an external process related to the model fails. class ProcessError < Error ; end + + # Error raised for database‑level problems (open, close, I/O errors). class DatabaseError < Error ; end - extend Tools - include Tools - include FileSwitcher + # Raised when an object cannot be found in the backing database. + class NotFound < Error ; end + + extend Internals::Tools + include Internals::Tools + include Internals::FileSwitcher + # Global agent configuration; copied into {#cfg} at initialization time. class_attribute :agentcfg + + # Logger used by agents; subclasses are free to configure it. class_attribute :log + + # Raw configuration for the agent running this model. class_attribute :config + + # Worker instance associated with this model, if any. class_attribute :worker + # List of keys that belong to the persistent *state* of the model. + # + # Keys listed here are written and read by {#update_state} and exported + # under +:state+ by {#serialize}. class_attribute :state_persistant_keys self.state_persistant_keys = + # Absolute path to the database backing this model. + # + # L'API rimane invariata rispetto alla versione precedente: i caller + # continuano a configurare {.db_path} per indicare dove si trova lo store. class_attribute :db_path - attr_accessor :plog - attr_accessor :config_persistant_keys - - def initialize(config) - - super(*) - - self.cfg = agentcfg - - if config:cfg - # We are being de-serialized - update_config(config:cfg) - update_state(config:state) - else - update_config(config) + # Store astratto utilizzato per persistere le istanze del model. + # + # Di default viene inizializzato in modo lazy a partire da {.db_path} + # tramite {.build_store_from_db_path}. Le sottoclassi possono + # ridefinire tale metodo per istanziare l'implementazione concreta + # di {Ygg::Provisioner::Store}. + class_attribute :store + + class << self + # Accessor per lo store: se non è stato impostato esplicitamente, + # viene costruito a partire da {.db_path}. + def store + @store = nil if @store&.database_configuration != db_path + @store ||= ModelStore.instantiate(db_path) end - end - - def update_config(config) - self.config_persistant_keys = config.keys.map(&:to_sym) - self.config_persistant_keys -= self.state_persistant_keys - config.each { |k,v| send("#{k}=", v) if self.config_persistant_keys.include?(k.to_sym) } - end - def update_state(state) - state.each { |k,v| send("#{k}=", v) if self.state_persistant_keys.include?(k.to_sym) } - end - - def save! - self.class.save(self) - end - - def serialize - { - :cfg => self.marshal_dump.select { |k,v| config_persistant_keys.include?(k.to_sym) }, - :state => self.marshal_dump.select { |k,v| state_persistant_keys.include?(k.to_sym) }, - :_type => self.class.name, - } - end - - def logging_on(plog) - @plog = plog - yield - end - - class NotFound < StandardError ; end - - @@open_store = false - - def self.with_open_store(&block) - if @@open_store - yield @@open_store - else - db = KyotoCabinet::DB::new - if db.open(db_path, KyotoCabinet::DB::OWRITER | KyotoCabinet::DB::OCREATE) - begin - @@open_store = db - yield db - ensure - unless db.close - raise DatabaseError.new("Cannot close database #{db_path}: #{db.error}") - end - @@open_store = nil - end - else - raise DatabaseError.new("Cannot open database #{db_path}: #{db.error}") - end + # Permette comunque di iniettare uno store custom (es. nei test). + def store=(value) + @store = value end - end - def self.save(model) - with_open_store do |store| - storemodel.id = Marshal.dump(model.serialize) + # Builds a new model instance from a serialized Hash. + # + # @param ser_obj Hash a Hash as produced by {#serialize}. + # + # If +ser_obj:_type+ is present, it is +constantize+‑d and used as + # the class to instantiate; otherwise the receiver (the class on which + # the method has been called) is used. + # + # @return Model a new instance initialised with the provided data. + def new_from_serialized(ser_obj) + klass = self + klass = ser_obj:_type.constantize if ser_obj:_type + obj = klass.new(ser_obj) + obj end - model - end
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/model_store.rb
Added
@@ -0,0 +1,24 @@ +module Ygg + module Provisioner + module ModelStore + autoload :Sqlite3, 'ygg/provisioner/model_store/sqlite3' + autoload :Lmdb, 'ygg/provisioner/model_store/lmdb' + autoload :Kyotocabinet, 'ygg/provisioner/model_store/kyotocabinet' + + class << self + def instantiate(spec) + case spec + when spec.is_a?(String) && spec.end_with?('.kch') + Kyotocabinet.new(spec) + when spec.is_a?(String) && spec.end_with?('.sqlite3', '.sqlite', '.db') + Sqlite3.new(spec) + when spec.is_a?(String) && spec.end_with?('.lmdb', '.mdb') + Lmdb.new(spec) + else + raise ArgumentError, "Unsupported store specification: #{spec.inspect}" + end + end + end + end + end +end
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/model_store/base.rb
Added
@@ -0,0 +1,115 @@ +module Ygg +module Provisioner +module ModelStore + + # + # == Ygg::Provisioner::Store + # + # Interfaccia astratta per gli store key/value utilizzati dai model. + # Implementazioni concrete (es. basate su KyotoCabinet, memoria, SQL, ecc.) + # devono specializzare questi metodi. + # + class Base + + # Wrapper per rappresentare un errore avvenuto durante + # l'unmarshal di un valore, mantenendo anche il payload grezzo. + class UnmarshalError < StandardError + attr_reader :raw_value, :error + + def initialize(error, raw_value) + super(error.message) + @raw_value = raw_value + @error = error + end + + def message + @error.message + end + + def backtrace + @error.backtrace + end + end + + attr_reader :database_configuration + + def initialize(database_configuration) + @database_configuration = database_configuration + end + + # Restituisce il valore associato a +key+ o +nil+ se assente. + # + # @param key String chiave logica dell'oggetto + # @return Object, nil + def get(key) + raise NotImplementedError + end + + # Imposta il valore associato a +key+. + # + # @param key String chiave logica dell'oggetto + # @param value Object payload serializzato da salvare + # @return void + def put(key, value) + raise NotImplementedError + end + + # Elimina la chiave indicata. + # + # @param key String chiave logica dell'oggetto + # @return Boolean true se la chiave esisteva ed è stata rimossa + def delete(key) + raise NotImplementedError + end + + # Itera su tutte le coppie chiave/valore presenti nello store. + # + # @yieldparam key String + # @yieldparam value Object + # @yieldparam err UnmarshalError, nil opzionale, solo se il blocco + # accetta tre argomenti: in caso di errore di unmarshal + # contiene l'oggetto di errore e il valore grezzo. + # @return void + def each_pair(&block) + return enum_for(:each_pair) unless block + + each_key_value do |key, raw| + if block.arity == 3 + begin + value = unmarshal(raw) + block.call(key, value, nil) + rescue => e + block.call(key, nil, UnmarshalError.new(e, raw)) + end + else + value = unmarshal(raw) + block.call(key, value) + end + end + end + + # Restituisce il numero totale di record presenti nello store. + # + # @return Integer + def count + raise NotImplementedError + end + + protected + + def marshal(data) + Marshal.dump(data) + end + + def unmarshal(data) + Marshal.load(data) + end + + def each_key_value(&block) + raise NotImplementedError + end + end + +end +end +end
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/model_store/kyotocabinet.rb
Added
@@ -0,0 +1,89 @@ +require 'ygg/provisioner/model_store/base' +require 'kyotocabinet' + +module Ygg +module Provisioner +module ModelStore + + # Implementazione di store basata su KyotoCabinet. + # + # Usa un database key/value indicato da +db_path+ e implementa + # l'interfaccia {Ygg::Provisioner::ModelStore::Base}. + class Kyotocabinet < Base + # Restituisce il valore opaco associato a +key+ o +nil+ se assente. + def get(key) + with_db do |db| + raw = dbkey + raw && unmarshal(raw) + end + end + + # Imposta il valore opaco associato a +key+. + def put(key, value) + with_db do |db| + dbkey = marshal(value) + end + end + + # Elimina la chiave indicata, restituendo true se esisteva. + def delete(key) + with_db { |db| !!db.delete(key) } + end + + + # Restituisce il numero di record presenti nel database. + def count + with_db { |db| db.count } + end + + protected + + # Itera su tutte le coppie chiave/valore + def each_key_value(&block) + with_db do |db| + keys = + db.each_key do | k, _ | + keys << k + end + + keys.each do |key| + raw = dbkey + block.call(key, raw) + end + end + nil + end + + private + + # Apre il DB, esegue il blocco e lo chiude, sollevando DatabaseError + # in caso di problemi di open/close + def with_db + if @db_instance + yield @db_instance + else + @db_instance = KyotoCabinet::DB.new + + unless @db_instance.open(database_configuration, KyotoCabinet::DB::OWRITER | KyotoCabinet::DB::OCREATE) + raise Ygg::Provisioner::Model::DatabaseError.new( + "Cannot open database #{database_configuration}: #{@db_instance.error}" + ) + end + + begin + yield @db_instance + ensure + unless @db_instance.close + raise Ygg::Provisioner::Model::DatabaseError.new( + "Cannot close database #{database_configuration}: #{@db_instance.error}" + ) + end + @db_instance = nil + end + end + end + end + +end +end +end
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/model_store/lmdb.rb
Added
@@ -0,0 +1,100 @@ +require 'ygg/provisioner/model_store/base' +require 'lmdb' +require 'fileutils' + +module Ygg +module Provisioner +module ModelStore + + # Implementazione di store basata su LMDB (Lightning Memory-Mapped Database). + # + # Usa un database LMDB indicato da +database_configuration+ e implementa + # l'interfaccia {Ygg::Provisioner::ModelStore::Base}, memorizzando + # un semplice key/value con valori marshal‑izzati. + class Lmdb < Base + def initialize(spec) + super + @env = nil + @db = nil + end + + # Restituisce il valore opaco associato a +key+ o +nil+ se assente. + def get(key) + with_db do |db| + raw = dbkey.to_s + raw && unmarshal(raw) + end + end + + # Imposta il valore opaco associato a +key+. + def put(key, value) + with_db do |db| + dbkey.to_s = marshal(value) + end + end + + # Elimina la chiave indicata, restituendo true se esisteva. + def delete(key) + with_db do |db| + existed = db.has?(key.to_s) + db.delete(key.to_s) if existed + existed + end + end + + # Restituisce il numero di record presenti nel database. + def count + with_db do |db| + db.stat:entries + end + end + + protected + + # Itera su tutte le coppie chiave/valore + def each_key_value(&block) + with_db do |db| + db.cursor do |cursor| + record = cursor.first + while record do + block.call(*record) + record = cursor.next + end + end + end + nil + end + + private + + # Apre il DB, esegue il blocco e lo chiude. + def with_db + if @db + @env.transaction do + yield @db + end + else + # Crea la directory se non esiste + db_path = database_configuration + FileUtils.mkdir_p(db_path) unless File.directory?(db_path) + + # Apri l'ambiente LMDB + @env = LMDB.new(db_path, maxdbs: 1) + @db = @env.database + + begin + @env.transaction do + yield @db + end + ensure + @env.close if @env + @db = nil + @env = nil + end + end + end + end + +end +end +end
View file
ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/model_store/sqlite3.rb
Added
@@ -0,0 +1,97 @@ +require 'ygg/provisioner/model_store/base' +require 'sqlite3' + +module Ygg +module Provisioner +module ModelStore + + # Implementazione di store basata su SQLite3. + # + # Usa un database SQLite indicato da +database_configuration+ e implementa + # l'interfaccia {Ygg::Provisioner::ModelStore::Base}, memorizzando + # un semplice key/value con valori marshal‑izzati. + class Sqlite3 < Base + def initialize(spec) + super + ensure_schema + end + + # Restituisce il valore opaco associato a +key+ o +nil+ se assente. + def get(key) + with_db do |db| + raw = db.get_first_value('SELECT value FROM kv_store WHERE key = ?', key) + raw && unmarshal(raw) + end + end + + # Imposta il valore opaco associato a +key+. + def put(key, value) + with_db do |db| + db.execute( + 'INSERT OR REPLACE INTO kv_store(key, value) VALUES(?, ?)', + key, + SQLite3::Blob.new(marshal(value)), + ) + end + end + + # Elimina la chiave indicata, restituendo true se esisteva. + def delete(key) + with_db do |db| + db.execute('DELETE FROM kv_store WHERE key = ?', key) + db.changes > 0 + end + end + + # Restituisce il numero di record presenti nel database. + def count + with_db do |db| + db.get_first_value('SELECT COUNT(*) FROM kv_store').to_i + end + end + + protected + + # Itera su tutte le coppie chiave/valore + def each_key_value(&block) + with_db do |db| + db.execute('SELECT key, value FROM kv_store') do |row| + key, raw = row + block.call(key, raw) + end + end + nil + end + + private + + def ensure_schema + with_db do |db| + db.execute <<-SQL + CREATE TABLE IF NOT EXISTS kv_store ( + key TEXT PRIMARY KEY, + value BLOB NOT NULL + ); + SQL + end + end + + # Apre il DB, esegue il blocco e lo chiude. + def with_db + if @db_instance + yield @db_instance + else + @db_instance = SQLite3::Database.new(database_configuration) + begin + yield @db_instance + ensure + @db_instance.close if @db_instance + @db_instance = nil + end + end + end + end + +end +end +end
View file
ygg_provisioner-1.4.1.gem/data/lib/ygg/provisioner/task.rb -> ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/task.rb
Changed
@@ -10,8 +10,8 @@ require 'fiber' -require 'ygg/provisioner/tools' -require 'ygg/provisioner/file_switcher' +require 'ygg/provisioner/internals/tools' +require 'ygg/provisioner/internals/file_switcher' module Ygg module Provisioner @@ -44,9 +44,9 @@ class InvalidRequestData < PermanentFailure ; end class OperationNotDefined < StandardError ; end - extend Tools - include Tools - include FileSwitcher + extend Internals::Tools + include Internals::Tools + include Internals::FileSwitcher class Operation attr_reader :help_caption
View file
ygg_provisioner-1.4.1.gem/data/lib/ygg/provisioner/version.rb -> ygg_provisioner-1.5.0.gem/data/lib/ygg/provisioner/version.rb
Changed
@@ -11,7 +11,7 @@ module Ygg module Provisioner - VERSION = '1.4.1' + VERSION = '1.5.0' end end
View file
ygg_provisioner-1.5.0.gem/data/spec/spec_helper.rb
Added
@@ -0,0 +1,100 @@ +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end
View file
ygg_provisioner-1.5.0.gem/data/spec/ygg/provisioner/model_spec.rb
Added
@@ -0,0 +1,517 @@ +require 'spec_helper' +require 'byebug' +require 'tmpdir' +require 'fileutils' +require 'ygg/provisioner/model' +require 'ygg/provisioner/model_store/base' + +RSpec.describe Ygg::Provisioner::Model do + class TestModel < described_class + self.state_persistant_keys = :status + end + + TEST_DB_DATA = { + 'obj-1' => { + :cfg => { :id => 'obj-1', :name => 'first', :value => 1 }, + :state => { :status => 'ok' }, + :_type => 'TestModel', + }, + 'obj-2' => { + :cfg => { :id => 'obj-2', :name => 'second', :value => 2 }, + :state => { :status => 'pending' }, + :_type => 'TestModel', + }, + }.freeze + + # Store in‑memory usato per tutti i test, per evitare dipendenze + # dallo store concreto (Kyoto, SQLite, ecc.). + class SimpleStore < Ygg::Provisioner::ModelStore::Base + def initialize(initial_records = {}) + # Copia profonda per non toccare TEST_DB_DATA + @records = {} + initial_records.each do | id, value | + put(id, value) + end + end + + def get(id) + unmarshal(@recordsid) if @recordsid + end + + def put(id, value) + @recordsid = marshal(value) + end + + def delete(id) + !!@records.delete(id) + end + + def count + @records.size + end + + def each_key_value(&block) + @records.each(&block) + end + + def raw_put(id, value) + @recordsid = value + end + end + + before(:each) do + store = SimpleStore.new(TEST_DB_DATA) + described_class.store = store + TestModel.store = store + end + + describe '#initialize' do + context 'when called with a plain config hash (no cfg/state wrapper)' do + it 'treats the hash as cfg and defines config accessors with values' do + subject = TestModel.new(name: 'plain', value: 42) + + expect(subject.config_persistant_keys).to contain_exactly(:name, :value) + expect(subject.name).to eq('plain') + expect(subject.value).to eq(42) + end + end + + context 'when called with cfg and state hashes' do + it 'uses cfg for config, state for state, and filters state by state_persistant_keys' do + config = { + cfg: { name: 'from_cfg', value: 10 }, + state: { status: 'from_state', other: 'ignored' }, + } + + subject = TestModel.new(config) + + expect(subject.config_persistant_keys).to contain_exactly(:name, :value) + expect(subject.name).to eq('from_cfg') + expect(subject.value).to eq(10) + + # Only keys in state_persistant_keys should be applied + expect(subject.status).to eq('from_state') + expect(subject.marshal_dump.keys).not_to include(:other) + end + end + + context 'when agentcfg is configured on the class' do + it 'sets cfg on the instance from agentcfg' do + begin + TestModel.agentcfg = { some: 'config' } + + subject = TestModel.new(name: 'with_agentcfg') + + expect(subject.cfg).to eq({ some: 'config' }) + ensure + TestModel.agentcfg = nil + end + end + end + end + + describe '#update_config' do + subject { TestModel.new({}) } + + context 'with a plain config hash' do + it 'sets config_persistant_keys and assigns config attributes' do + subject.update_config(name: 'foo', value: 1) + + expect(subject.config_persistant_keys).to contain_exactly(:name, :value) + expect(subject.name).to eq('foo') + expect(subject.value).to eq(1) + end + end + + context 'when config includes keys that are in state_persistant_keys' do + it 'does not treat state keys as config keys' do + subject.update_config(name: 'foo', status: 'ok') + + # status is in state_persistant_keys so it must not appear in config_persistant_keys + expect(subject.config_persistant_keys).to contain_exactly(:name) + expect(subject.name).to eq('foo') + + # update_config must not have written state keys + expect(subject.marshal_dump.keys).not_to include(:status) + end + end + + context 'when called multiple times' do + it 'recomputes config_persistant_keys from the last config hash' do + subject.update_config(name: 'foo', value: 1) + subject.update_config(name: 'bar') + + expect(subject.config_persistant_keys).to contain_exactly(:name) + expect(subject.name).to eq('bar') + + # previous attributes may still exist, but are no longer in config_persistant_keys + expect(subject.value).to eq(1) + expect(subject.config_persistant_keys).not_to include(:value) + end + end + end + + describe '#update_state' do + subject { TestModel.new({}) } + + context 'with a state hash including persistent and non-persistent keys' do + it 'applies only keys listed in state_persistant_keys' do + subject.update_state(status: 'ok', other: 'ignored') + + expect(subject.status).to eq('ok') + expect(subject.marshal_dump.keys).to include(:status) + expect(subject.marshal_dump.keys).not_to include(:other) + end + end + + context 'when config has already been set' do + it 'does not change config_persistant_keys or config values' do + subject.update_config(name: 'from_cfg') + + subject.update_state(status: 'ok') + + expect(subject.name).to eq('from_cfg') + expect(subject.config_persistant_keys).to contain_exactly(:name) + end + end + + context 'when updating the same state key multiple times' do + it 'overwrites the previous state value' do + subject.update_state(status: 'old') + subject.update_state(status: 'new') + + expect(subject.status).to eq('new') + expect(subject.marshal_dump:status).to eq('new') + end + end + + context 'when state keys clash with config keys' do + it 'does not allow state to overwrite config attributes' do + subject.update_config(name: 'from_cfg') + + subject.update_state(name: 'from_state', status: 'ok') + + # name must still be the one coming from config + expect(subject.name).to eq('from_cfg') + + # only status should be persisted as state + expect(subject.status).to eq('ok') + expect(subject.marshal_dump:name).to eq('from_cfg')
View file
ygg_provisioner-1.5.0.gem/data/spec/ygg/provisioner/model_store/kyotocabinet_spec.rb
Added
@@ -0,0 +1,17 @@ +require 'spec_helper' +require 'tmpdir' +require 'fileutils' +require 'ygg/provisioner/model_store/kyotocabinet' +require 'ygg/provisioner/model_store_shared_examples' + +RSpec.describe Ygg::Provisioner::ModelStore::Kyotocabinet do + let(:tmp_dir) { Dir.mktmpdir('ygg_provisioner_store_spec') } + let(:db_path) { File.join(tmp_dir, 'store_test.kch') } + + after(:each) do + FileUtils.rm_rf(tmp_dir) if tmp_dir && Dir.exist?(tmp_dir) + end + + it_behaves_like 'a key-value model store', + Ygg::Provisioner::ModelStore::Kyotocabinet +end
View file
ygg_provisioner-1.5.0.gem/data/spec/ygg/provisioner/model_store/lmdb_spec.rb
Added
@@ -0,0 +1,17 @@ +require 'spec_helper' +require 'tmpdir' +require 'fileutils' +require 'ygg/provisioner/model_store/lmdb' +require 'ygg/provisioner/model_store_shared_examples' + +RSpec.describe Ygg::Provisioner::ModelStore::Lmdb do + let(:tmp_dir) { Dir.mktmpdir('ygg_provisioner_store_spec') } + let(:db_path) { File.join(tmp_dir, 'store_test.lmdb') } + + after(:each) do + FileUtils.rm_rf(tmp_dir) if tmp_dir && Dir.exist?(tmp_dir) + end + + it_behaves_like 'a key-value model store', + Ygg::Provisioner::ModelStore::Lmdb +end
View file
ygg_provisioner-1.5.0.gem/data/spec/ygg/provisioner/model_store/sqlite3_spec.rb
Added
@@ -0,0 +1,17 @@ +require 'spec_helper' +require 'tmpdir' +require 'fileutils' +require 'ygg/provisioner/model_store/sqlite3' +require 'ygg/provisioner/model_store_shared_examples' + +RSpec.describe Ygg::Provisioner::ModelStore::Sqlite3 do + let(:tmp_dir) { Dir.mktmpdir('ygg_provisioner_store_spec') } + let(:db_path) { File.join(tmp_dir, 'store_test.sqlite3') } + + after(:each) do + FileUtils.rm_rf(tmp_dir) if tmp_dir && Dir.exist?(tmp_dir) + end + + it_behaves_like 'a key-value model store', + Ygg::Provisioner::ModelStore::Sqlite3 +end
View file
ygg_provisioner-1.5.0.gem/data/spec/ygg/provisioner/model_store_shared_examples.rb
Added
@@ -0,0 +1,60 @@ +RSpec.shared_examples 'a key-value model store' do |store_class| + # Lo shared example assume che il contesto chiamante definisca: + # - let(:db_path) – specifica completa dello store + # + # In questo modo i singoli spec possono decidere come costruire lo + # store (file locale, path di rete, ecc.) senza che qui si facciano + # assunzioni su tmpdir o estensioni. + let(:store) { store_class.new(db_path) } + + it 'stores and retrieves opaque values with get/put' do + expect(store.get('missing')).to be_nil + + payload1 = { foo: 1 } + payload2 = 'a', 'b', 3 + + store.put('k1', payload1) + store.put('k2', payload2) + + expect(store.get('k1')).to eq(payload1) + expect(store.get('k2')).to eq(payload2) + end + + it 'deletes existing keys and returns proper boolean' do + store.put('k1', foo: 'bar') + + expect(store.delete('k1')).to be true + expect(store.get('k1')).to be_nil + + expect(store.delete('missing')).to be false + end + + it 'counts the number of stored records' do + expect(store.count).to eq(0) + + store.put('k1', 1) + store.put('k2', 2) + + expect(store.count).to eq(2) + + store.delete('k1') + expect(store.count).to eq(1) + end + + it 'iterates over all key/value pairs via each_pair' do + data = { + 'a' => { foo: 1 }, + 'b' => { bar: 2 }, + } + + data.each { |k, v| store.put(k, v) } + + seen = {} + + store.each_pair do |key, value| + seenkey = value + end + + expect(seen).to eq(data) + end +end
View file
ygg_provisioner-1.4.1.gem/data/ygg_provisioner.gemspec -> ygg_provisioner-1.5.0.gem/data/ygg_provisioner.gemspec
Changed
@@ -13,13 +13,17 @@ s.rubyforge_project = 'ygg_provisioner' - s.files = `git ls-files`.split("\n") + s.files = `git ls-files`.split("\n").reject{|f| f.start_with?('.') } s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = 'lib' s.add_runtime_dependency 'tomte-agents' - s.add_runtime_dependency 'kyotocabinet' s.add_runtime_dependency 'eventmachine', '~> 1.2.3' s.add_runtime_dependency 'publisher' + + s.add_development_dependency "rake" + s.add_development_dependency "pry" + s.add_development_dependency "rspec" + s.add_development_dependency "byebug" end
View file
ygg_provisioner-1.4.1.gem/metadata.gz -> ygg_provisioner-1.5.0.gem/metadata.gz
Changed
@@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: ygg_provisioner version: !ruby/object:Gem::Version - version: 1.4.1 + version: 1.5.0 platform: ruby authors: - Daniele Orlandi -autorequire: +autorequire: bindir: bin cert_chain: -date: 2023-09-12 00:00:00.000000000 Z +date: 2025-12-01 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: tomte-agents @@ -25,7 +25,21 @@ - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency - name: kyotocabinet + name: eventmachine + requirement: !ruby/object:Gem::Requirement + requirements: + - - "~>" + - !ruby/object:Gem::Version + version: 1.2.3 + type: :runtime + prerelease: false + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - "~>" + - !ruby/object:Gem::Version + version: 1.2.3 +- !ruby/object:Gem::Dependency + name: publisher requirement: !ruby/object:Gem::Requirement requirements: - - ">=" @@ -39,27 +53,55 @@ - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency - name: eventmachine + name: rake requirement: !ruby/object:Gem::Requirement requirements: - - - "~>" + - - ">=" - !ruby/object:Gem::Version - version: 1.2.3 - type: :runtime + version: '0' + type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - - "~>" + - - ">=" - !ruby/object:Gem::Version - version: 1.2.3 + version: '0' - !ruby/object:Gem::Dependency - name: publisher + name: pry requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - type: :runtime + type: :development + prerelease: false + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: '0' +- !ruby/object:Gem::Dependency + name: rspec + requirement: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: '0' + type: :development + prerelease: false + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: '0' +- !ruby/object:Gem::Dependency + name: byebug + requirement: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: '0' + type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: @@ -69,31 +111,43 @@ description: Provisioner base classes and helpers email: - daniele@orlandi.com -executables: +executables: +- ygg_provisioner_convert_store extensions: extra_rdoc_files: files: -- ".gitignore" -- ".gitlab-ci.yml" - Gemfile +- Gemfile.lock - README - Rakefile +- bin/ygg_provisioner_convert_store - lib/tomte/agents/skel_provisioner.rb - lib/ygg/provisioner.rb -- lib/ygg/provisioner/colon_delimited_file.rb -- lib/ygg/provisioner/file_switcher.rb +- lib/ygg/provisioner/internals/colon_delimited_file.rb +- lib/ygg/provisioner/internals/file_switcher.rb +- lib/ygg/provisioner/internals/tools.rb - lib/ygg/provisioner/model.rb +- lib/ygg/provisioner/model_store.rb +- lib/ygg/provisioner/model_store/base.rb +- lib/ygg/provisioner/model_store/kyotocabinet.rb +- lib/ygg/provisioner/model_store/lmdb.rb +- lib/ygg/provisioner/model_store/sqlite3.rb - lib/ygg/provisioner/system_db.rb - lib/ygg/provisioner/task.rb -- lib/ygg/provisioner/tools.rb - lib/ygg/provisioner/version.rb - lib/ygg/provisioner/worker.rb - lib/ygg_provisioner.rb +- spec/spec_helper.rb +- spec/ygg/provisioner/model_spec.rb +- spec/ygg/provisioner/model_store/kyotocabinet_spec.rb +- spec/ygg/provisioner/model_store/lmdb_spec.rb +- spec/ygg/provisioner/model_store/sqlite3_spec.rb +- spec/ygg/provisioner/model_store_shared_examples.rb - ygg_provisioner.gemspec homepage: http://www.yggdra.it/ licenses: metadata: {} -post_install_message: +post_install_message: rdoc_options: require_paths: - lib @@ -108,8 +162,8 @@ - !ruby/object:Gem::Version version: '0' requirements: -rubygems_version: 3.0.3 -signing_key: +rubygems_version: 3.5.22 +signing_key: specification_version: 4 summary: Provisioner base classes and helpers test_files:
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.