diff --git a/lib/datadog/core/remote/configuration/content.rb b/lib/datadog/core/remote/configuration/content.rb index 3b827a3c744..f5b788a105f 100644 --- a/lib/datadog/core/remote/configuration/content.rb +++ b/lib/datadog/core/remote/configuration/content.rb @@ -19,11 +19,13 @@ def parse(hash) end attr_reader :path, :data, :hashes + attr_accessor :version def initialize(path:, data:) @path = path @data = data @hashes = {} + @version = 0 end def hexdigest(type) diff --git a/lib/datadog/core/remote/configuration/repository.rb b/lib/datadog/core/remote/configuration/repository.rb index fba4a189887..6b367d0bc30 100644 --- a/lib/datadog/core/remote/configuration/repository.rb +++ b/lib/datadog/core/remote/configuration/repository.rb @@ -78,7 +78,7 @@ def initialize(repository) @repository = repository @root_version = repository.root_version @targets_version = repository.targets_version - @config_states = [] + @config_states = contents_to_config_states(repository.contents) @has_error = false @error = '' @opaque_backend_state = repository.opaque_backend_state @@ -87,6 +87,18 @@ def initialize(repository) private + def contents_to_config_states(contents) + return [] if contents.empty? + + contents.map do |content| + { + id: content.path.config_id, + version: content.version, + product: content.path.product + } + end + end + def contents_to_cached_target_files(contents) return [] if contents.empty? @@ -164,6 +176,7 @@ def initialize(path, target, content) def apply(repository) return unless repository[@path].nil? + @content.version = @target.version repository.contents << @content @path @@ -184,6 +197,7 @@ def initialize(path, target, content) def apply(repository) return if repository[@path].nil? + @content.version = @target.version repository.contents[@path] = @content @path diff --git a/lib/datadog/core/remote/configuration/target.rb b/lib/datadog/core/remote/configuration/target.rb index f091fc12e7e..6b1ba6e5130 100644 --- a/lib/datadog/core/remote/configuration/target.rb +++ b/lib/datadog/core/remote/configuration/target.rb @@ -48,16 +48,18 @@ class << self def parse(hash) length = Integer(hash['length']) digests = Configuration::DigestList.parse(hash['hashes']) + version = Integer(hash['custom']['v']) - new(digests: digests, length: length) + new(digests: digests, length: length, version: version) end end - attr_reader :length, :digests + attr_reader :length, :digests, :version - def initialize(digests:, length:) + def initialize(digests:, length:, version:) @digests = digests @length = length + @version = version end private_class_method :new diff --git a/sig/datadog/core/remote/configuration/content.rbs b/sig/datadog/core/remote/configuration/content.rbs index f7bc149ef30..167915e7869 100644 --- a/sig/datadog/core/remote/configuration/content.rbs +++ b/sig/datadog/core/remote/configuration/content.rbs @@ -11,6 +11,8 @@ module Datadog attr_reader hashes: Hash[Symbol, String] + attr_accessor version: Integer + @length: Integer def initialize: (path: Configuration::Path, data: StringIO) -> void diff --git a/sig/datadog/core/remote/configuration/repository.rbs b/sig/datadog/core/remote/configuration/repository.rbs index 4bb6b5657f7..37dcf685355 100644 --- a/sig/datadog/core/remote/configuration/repository.rbs +++ b/sig/datadog/core/remote/configuration/repository.rbs @@ -34,7 +34,7 @@ module Datadog attr_reader repository: Repository - attr_reader config_states: Array[untyped] + attr_reader config_states: Array[Hash[Symbol, untyped]] attr_reader cached_target_files: Array[Hash[Symbol, untyped]] @@ -49,6 +49,8 @@ module Datadog private def contents_to_cached_target_files: (ContentList contents) -> Array[Hash[Symbol, untyped]] + + def contents_to_config_states: (ContentList contents) -> Array[Hash[Symbol, untyped]] end class Transaction diff --git a/sig/datadog/core/remote/configuration/target.rbs b/sig/datadog/core/remote/configuration/target.rbs index e3dc799dda7..a304be9c88b 100644 --- a/sig/datadog/core/remote/configuration/target.rbs +++ b/sig/datadog/core/remote/configuration/target.rbs @@ -18,9 +18,11 @@ module Datadog attr_reader digests: DigestList + attr_reader version: Integer + def self.parse: (::Hash[::String, untyped] hash) -> Target - def initialize: (digests: DigestList, length: ::Integer) -> void + def initialize: (digests: DigestList, length: ::Integer, version: Integer) -> void def check: (untyped content) -> bool end diff --git a/spec/datadog/appsec/remote_spec.rb b/spec/datadog/appsec/remote_spec.rb index dd66e9ea3d8..b93a76af907 100644 --- a/spec/datadog/appsec/remote_spec.rb +++ b/spec/datadog/appsec/remote_spec.rb @@ -106,15 +106,28 @@ }.to_json end let(:receiver) { described_class.receivers[0] } + let(:target) do + Datadog::Core::Remote::Configuration::Target.parse( + { + 'custom' => { + 'v' => 1, + }, + 'hashes' => { 'sha256' => Digest::SHA256.hexdigest(rules_data.to_json) }, + 'length' => rules_data.to_s.length + } + ) + end + let(:content) do + Datadog::Core::Remote::Configuration::Content.parse( + { + path: 'datadog/603646/ASM_DD/latest/config', + content: StringIO.new(rules_data) + } + ) + end let(:transaction) do repository.transaction do |_repository, transaction| - content = Datadog::Core::Remote::Configuration::Content.parse( - { - path: 'datadog/603646/ASM_DD/latest/config', - content: StringIO.new(rules_data) - } - ) - transaction.insert(content.path, nil, content) + transaction.insert(content.path, target, content) end end let(:repository) { Datadog::Core::Remote::Configuration::Repository.new } diff --git a/spec/datadog/core/remote/configuration/respository_spec.rb b/spec/datadog/core/remote/configuration/respository_spec.rb index 6fc461258a7..ba7e6926eab 100644 --- a/spec/datadog/core/remote/configuration/respository_spec.rb +++ b/spec/datadog/core/remote/configuration/respository_spec.rb @@ -8,6 +8,9 @@ subject(:repository) { described_class.new } let(:raw_target) do { + 'custom' => { + 'v' => 1, + }, 'hashes' => { 'sha256' => Digest::SHA256.hexdigest(raw.to_json) }, 'length' => 645 } @@ -103,6 +106,12 @@ ) end + let(:new_target) do + updated_raw_target = raw_target.dup + updated_raw_target['custom']['v'] += 1 + Datadog::Core::Remote::Configuration::Target.parse(updated_raw_target) + end + describe '#transaction' do it 'yields self and a new Repository::Transaction instance' do expect do |b| @@ -350,5 +359,68 @@ end end end + + describe '#config_states' do + context 'without changes' do + it 'return empty array' do + expect(repository.state.config_states).to eq([]) + end + end + + context 'with changes' do + let(:expected_config_states) do + [ + { :id => path.config_id, :product => path.product, :version => 1 } + ] + end + + context 'insert' do + it 'return config_states' do + repository.transaction do |_repository, transaction| + transaction.insert(path, target, content) + end + + expect(repository.state.config_states).to eq(expected_config_states) + end + end + + context 'update' do + it 'return config_states' do + repository.transaction do |_repository, transaction| + transaction.insert(path, target, content) + end + + expect(repository.state.config_states).to eq(expected_config_states) + + repository.transaction do |_repository, transaction| + transaction.update(path, new_target, new_content) + end + + expected_updated_config_states = [ + { :id => path.config_id, :product => path.product, :version => 2 } + ] + + expect(repository.state.config_states).to_not eq(expected_config_states) + expect(repository.state.config_states).to eq(expected_updated_config_states) + end + end + + context 'delete' do + it 'return config_states' do + repository.transaction do |_repository, transaction| + transaction.insert(path, target, content) + end + + expect(repository.state.config_states).to eq(expected_config_states) + + repository.transaction do |_repository, transaction| + transaction.delete(path) + end + + expect(repository.state.config_states).to eq([]) + end + end + end + end end end diff --git a/spec/datadog/core/remote/configuration/target_spec.rb b/spec/datadog/core/remote/configuration/target_spec.rb index 53c1ecca63c..23dabd003d6 100644 --- a/spec/datadog/core/remote/configuration/target_spec.rb +++ b/spec/datadog/core/remote/configuration/target_spec.rb @@ -170,6 +170,9 @@ describe Datadog::Core::Remote::Configuration::Target do let(:raw_target) do { + 'custom' => { + 'v' => 1, + }, 'hashes' => { 'sha256' => Digest::SHA256.hexdigest(raw.to_json) }, 'length' => 645 }