-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
144 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# encoding: ascii-8bit | ||
|
||
# Copyright 2022 Ball Aerospace & Technologies Corp. | ||
# All Rights Reserved. | ||
# | ||
# This program is free software; you can modify and/or redistribute it | ||
# under the terms of the GNU Affero General Public License | ||
# as published by the Free Software Foundation; version 3 with | ||
# attribution addendums as found in the LICENSE.txt | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# This program may also be used under the terms of a commercial or | ||
# enterprise edition license of COSMOS if purchased from the | ||
# copyright holder | ||
|
||
require 'cosmos/topics/topic' | ||
|
||
module Cosmos | ||
class ConfigTopic < Topic | ||
PRIMARY_KEY = "__CONFIG" | ||
|
||
# Helper method to initialize the stream and ensure a consistent key | ||
def self.initialize_stream(scope) | ||
self.initialize_streams(["#{scope}#{PRIMARY_KEY}"]) | ||
end | ||
|
||
# Write a configuration change to the topic | ||
# @param config [Hash] Hash with required keys 'kind', 'name', 'type' | ||
def self.write(config, scope:) | ||
unless config.keys.include?(:kind) | ||
raise "ConfigTopic error, required key kind: not given" | ||
end | ||
unless ['created', 'deleted'].include?(config[:kind]) | ||
raise "ConfigTopic error unknown kind: #{config[:kind]}" | ||
end | ||
unless config.keys.include?(:name) | ||
raise "ConfigTopic error, required key name: not given" | ||
end | ||
unless config.keys.include?(:type) | ||
raise "ConfigTopic error, required key type: not given" | ||
end | ||
# Limit the configuration topics to 1000 entries | ||
Topic.write_topic("#{scope}#{PRIMARY_KEY}", config, '*', 1000) | ||
end | ||
|
||
def self.read(offset = nil, count: 100, scope:) | ||
topic = "#{scope}#{PRIMARY_KEY}" | ||
if offset | ||
result = Topic.read_topics([topic], [offset], nil, count) | ||
if result.empty? | ||
[] # We want to return an empty array rather than an empty hash | ||
else | ||
# result is a hash with the topic key followed by an array of results | ||
# This returns just the array of arrays [[offset, hash], [offset, hash], ...] | ||
result[topic] | ||
end | ||
else | ||
result = Topic.get_newest_message(topic) | ||
return [result] if result | ||
return [] | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# encoding: ascii-8bit | ||
|
||
# Copyright 2022 Ball Aerospace & Technologies Corp. | ||
# All Rights Reserved. | ||
# | ||
# This program is free software; you can modify and/or redistribute it | ||
# under the terms of the GNU Affero General Public License | ||
# as published by the Free Software Foundation; version 3 with | ||
# attribution addendums as found in the LICENSE.txt | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# This program may also be used under the terms of a commercial or | ||
# enterprise edition license of COSMOS if purchased from the | ||
# copyright holder | ||
|
||
require 'spec_helper' | ||
require 'cosmos/topics/config_topic' | ||
|
||
module Cosmos | ||
describe ConfigTopic do | ||
before(:each) do | ||
mock_redis() | ||
end | ||
|
||
describe "self.initialize_stream" do | ||
it "initializes the stream with the scope" do | ||
ConfigTopic.initialize_stream('DEFAULT') | ||
expect(EphemeralStore.scan_each(type: 'stream', count: 100).to_a).to include("DEFAULT__CONFIG") | ||
end | ||
end | ||
|
||
describe "self.write" do | ||
it "requires kind, type, name keys" do | ||
expect { ConfigTopic.write({ type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/) | ||
expect { ConfigTopic.write({ kind: 'created', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/) | ||
expect { ConfigTopic.write({ kind: 'created', type: 'target', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/) | ||
end | ||
|
||
it "requires kind to be 'created' or 'deleted'" do | ||
expect { ConfigTopic.write({ kind: 'unknown', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/) | ||
end | ||
end | ||
|
||
describe "self.read" do | ||
it "reads from an offset" do | ||
ConfigTopic.initialize_stream('DEFAULT') | ||
ConfigTopic.write({ kind: 'created', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') | ||
ConfigTopic.write({ kind: 'deleted', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') | ||
config = ConfigTopic.read(0, scope: 'DEFAULT') # read all | ||
expect(config[0][1]['kind']).to eql 'created' | ||
expect(config[0][1]['type']).to eql 'target' | ||
expect(config[0][1]['name']).to eql 'INST' | ||
expect(config[0][1]['plugin']).to eql 'PLUGIN' | ||
expect(config[1][1]['kind']).to eql 'deleted' | ||
expect(config[1][1]['type']).to eql 'target' | ||
expect(config[1][1]['name']).to eql 'INST' | ||
expect(config[1][1]['plugin']).to eql 'PLUGIN' | ||
end | ||
|
||
it "reads the latest" do | ||
ConfigTopic.initialize_stream('DEFAULT') | ||
ConfigTopic.write({ kind: 'created', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') | ||
ConfigTopic.write({ kind: 'deleted', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') | ||
config = ConfigTopic.read(scope: 'DEFAULT') # read latest | ||
expect(config[0][1]['kind']).to eql 'deleted' | ||
expect(config[0][1]['type']).to eql 'target' | ||
expect(config[0][1]['name']).to eql 'INST' | ||
expect(config[0][1]['plugin']).to eql 'PLUGIN' | ||
end | ||
end | ||
end | ||
end |