From edf3445d7e78f6b17ed78751b53299cf5445adfd Mon Sep 17 00:00:00 2001 From: Mikk Romulus Date: Fri, 15 Nov 2024 11:34:54 +0200 Subject: [PATCH] feat: Add actor_nr as substitution variable This is a long time coming, since we are now using actors instead of hardcoded teams. Old team_nr will stay working for now --- app/calculation/hostname_generator.rb | 4 ++-- app/calculation/liquid_range_substitution.rb | 6 ++++-- app/calculation/string_substituter.rb | 3 ++- app/forms/address_pool_form.rb | 2 +- app/models/address_pool.rb | 6 +++--- app/models/network.rb | 5 +++-- app/presenters/address_preview_presenter.rb | 3 ++- app/presenters/api/v3/instance_presenter.rb | 5 ++++- .../api/v3/network_instance_presenter.rb | 4 +++- app/views/docs/templating.html.md | 18 ++++++++++-------- .../_address_preview.html.haml | 8 ++++---- spec/calculations/address_values_spec.rb | 2 +- .../liquid_range_substitution_spec.rb | 18 ++++++++++++++++++ spec/models/address_pool_spec.rb | 2 +- spec/models/address_spec.rb | 4 ++-- spec/presenters/instance_presenter_spec.rb | 4 ++-- spec/requests/api_v3_network_spec.rb | 6 +++++- 17 files changed, 67 insertions(+), 33 deletions(-) diff --git a/app/calculation/hostname_generator.rb b/app/calculation/hostname_generator.rb index aa7ccb20..03b0ed09 100644 --- a/app/calculation/hostname_generator.rb +++ b/app/calculation/hostname_generator.rb @@ -5,14 +5,14 @@ class HostnameGenerator < Patterns::Calculation def result subject.virtual_machine = virtual_machine hostname_sequence_suffix = '{{ seq }}' if virtual_machine.clustered? - hostname_team_suffix = '{{ team_nr_str }}' if virtual_machine.numbered_actor && (!nic || !nic.network&.numbered?) + hostname_team_suffix = '{{ actor_nr_str }}' if virtual_machine.numbered_actor && (!nic || !nic.network&.numbered?) sequences = [ hostname_sequence_suffix, hostname_team_suffix ].compact hostname = "#{subject.hostname}#{sequences.join('-')}" - domain = nic&.network&.full_domain.to_s.gsub(/#+/, '{{ team_nr_str }}') + domain = nic&.network&.full_domain.to_s.gsub(/#+/, '{{ actor_nr_str }}') Struct.new(:hostname, :domain, :fqdn).new( hostname:, diff --git a/app/calculation/liquid_range_substitution.rb b/app/calculation/liquid_range_substitution.rb index f654f9b9..911b100c 100644 --- a/app/calculation/liquid_range_substitution.rb +++ b/app/calculation/liquid_range_substitution.rb @@ -52,9 +52,11 @@ def network_numbering_source(object) def replacement_ranges { - 'seq' => sequential_range.map { |a| a.to_s.rjust(2, '0') }, + 'seq' => sequential_range.map { _1.to_s.rjust(2, '0') }, 'team_nr' => numbering_range, - 'team_nr_str' => numbering_range&.map { |a| a.to_s.rjust(2, '0') } + 'team_nr_str' => numbering_range&.map { _1.to_s.rjust(2, '0') }, + 'actor_nr' => numbering_range, + 'actor_nr_str' => numbering_range&.map { _1.to_s.rjust(2, '0') } } end diff --git a/app/calculation/string_substituter.rb b/app/calculation/string_substituter.rb index 3406f305..206b8acb 100644 --- a/app/calculation/string_substituter.rb +++ b/app/calculation/string_substituter.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class StringSubstituter < Patterns::Calculation - VALID_LIQUID_KEYS = %i(seq team_nr) + VALID_LIQUID_KEYS = %i(seq team_nr actor_nr) private def result @@ -21,6 +21,7 @@ def substitution_keys options.slice(*VALID_LIQUID_KEYS).symbolize_keys.tap do |keys| keys[:seq] = keys[:seq].to_s.rjust(2, '0') if keys[:seq] keys[:team_nr_str] = keys[:team_nr].to_s.rjust(2, '0') if keys[:team_nr] + keys[:actor_nr_str] = keys[:actor_nr].to_s.rjust(2, '0') if keys[:actor_nr] end.stringify_keys end end diff --git a/app/forms/address_pool_form.rb b/app/forms/address_pool_form.rb index 75932824..595fa8b9 100644 --- a/app/forms/address_pool_form.rb +++ b/app/forms/address_pool_form.rb @@ -29,7 +29,7 @@ class AddressPoolForm < Patterns::Form if input == '' public_send("#{field.to_s.sub('_address', '')}=", nil) else - address = IPAddress::IPv4.new("#{StringSubstituter.result_for(input, { team_nr: 1 })}/#{resource.ip_network.prefix}") rescue nil + address = IPAddress::IPv4.new("#{StringSubstituter.result_for(input, { team_nr: 1, actor_nr: 1 })}/#{resource.ip_network.prefix}") rescue nil if address && resource.ip_network.include?(address) public_send("#{field.to_s.sub('_address', '')}=", address.u32 - resource.ip_network.network_u32 - 1) else diff --git a/app/models/address_pool.rb b/app/models/address_pool.rb index cb9de497..4fa87e64 100644 --- a/app/models/address_pool.rb +++ b/app/models/address_pool.rb @@ -37,7 +37,7 @@ def self.to_icon end def numbered? - network_address =~ /#|team_nr/ + network_address =~ /#|team_nr|actor_nr/ end def gateway_address_object @@ -59,12 +59,12 @@ def ip_network(team = nil, base = network_address.dup) return unless base if last_octet_is_dynamic? - first_net = IPAddress(StringSubstituter.result_for(base, { team_nr: 1 })).network + first_net = IPAddress(StringSubstituter.result_for(base, { team_nr: 1, actor_nr: 1 })).network IPAddress::IPv4.parse_u32( first_net.to_i + ((team || 1) - 1) * first_net.size, first_net.prefix ) else - templated = StringSubstituter.result_for(base, { team_nr: team || 1 }) + templated = StringSubstituter.result_for(base, { team_nr: team || 1, actor_nr: team || 1 }) IPAddress(templated).network end end diff --git a/app/models/network.rb b/app/models/network.rb index e54721a4..efa6cc5a 100644 --- a/app/models/network.rb +++ b/app/models/network.rb @@ -65,7 +65,7 @@ def gateway_hosts end def numbered? - cloud_id =~ /#|team_nr/ || address_pools.any?(&:numbered?) + cloud_id =~ /#|team_nr|actor_nr/ || address_pools.any?(&:numbered?) end def slug_candidates @@ -84,7 +84,8 @@ def ip_network(index, type) return unless public_send(type) raw_value = public_send(type).dup templated = StringSubstituter.result_for(raw_value, { - team_nr: index || 1 + team_nr: index || 1, + actor_nr: index || 1 }) IPAddress(templated).network end diff --git a/app/presenters/address_preview_presenter.rb b/app/presenters/address_preview_presenter.rb index cac045dd..eb8a45fe 100644 --- a/app/presenters/address_preview_presenter.rb +++ b/app/presenters/address_preview_presenter.rb @@ -33,7 +33,8 @@ def substitute(text) text, { seq: sequential_number, - team_nr: team_number + team_nr: team_number, + actor_nr: team_number } ) end diff --git a/app/presenters/api/v3/instance_presenter.rb b/app/presenters/api/v3/instance_presenter.rb index fa644787..6a3e5091 100644 --- a/app/presenters/api/v3/instance_presenter.rb +++ b/app/presenters/api/v3/instance_presenter.rb @@ -74,6 +74,7 @@ def substitute(text) text, { team_nr: team_number, + actor_nr: team_number, seq: sequential_number } ) @@ -84,6 +85,8 @@ def team_numbers { team_nr: team_number, team_nr_str: team_number.to_s.rjust(2, '0'), + actor_nr: team_number, + actor_nr_str: team_number.to_s.rjust(2, '0'), } end @@ -105,7 +108,7 @@ def hostname_sequence_suffix end def hostname_team_suffix - 't{{ team_nr_str }}' if vm.numbered_actor + 't{{ actor_nr_str }}' if vm.numbered_actor end def inventory_name diff --git a/app/presenters/api/v3/network_instance_presenter.rb b/app/presenters/api/v3/network_instance_presenter.rb index cf791963..5e6ad0ab 100644 --- a/app/presenters/api/v3/network_instance_presenter.rb +++ b/app/presenters/api/v3/network_instance_presenter.rb @@ -6,6 +6,7 @@ class NetworkInstancePresenter < Struct.new(:network, :team_number) def as_json { team_nr: team_number, + actor_nr: team_number, cloud_id: substitute(network.cloud_id), domains: [ substitute(network.full_domain) @@ -29,7 +30,8 @@ def substitute(text) StringSubstituter.result_for( text, { - team_nr: team_number + team_nr: team_number, + actor_nr: team_number } ) end diff --git a/app/views/docs/templating.html.md b/app/views/docs/templating.html.md index 7b6f42de..8ea01c58 100644 --- a/app/views/docs/templating.html.md +++ b/app/views/docs/templating.html.md @@ -6,20 +6,22 @@ Main use case is using dynamic variables in `Network` or `VirtualMachine` fields Currently, following variables can be used: -1. `team_nr` - the number of Blue team, as iterated over the environment maximum -1. `team_nr_str` - the number of Blue team, as iterated over the environment maximum, leftpadded with zeroes. +1. `actor_nr` - the number of Blue team, as iterated over the environment maximum +1. `actor_nr_str` - the number of Blue team, as iterated over the environment maximum, leftpadded with zeroes. +1. `team_nr` - legacy version of `actor_nr` +1. `team_nr_str` - legacy version of `actor_nr_str` 1. `seq` - sequential number, if a `VirtualMachine` is configured with a custom deploy count. ### Network -When `{{ team_nr }}` or `{{ team_nr_str }}` is added to compatible field (domain, cloud ID, network address), the network is switched to be in numbered mode. Any virtual machine attached to this network will be forced to deploy in "one per each actor entry" mode. +When `{{ actor_nr }}` or `{{ actor_nr_str }}` is added to compatible field (domain, cloud ID, network address), the network is switched to be in numbered mode. Any virtual machine attached to this network will be forced to deploy in "one per each actor entry" mode. ### Filters Commonly used filter patterns, when current actor entries count equals _5_: -| Pattern | Description | Resulting values | -| ------------------------------------------ | ---------------------------------------------------------------------------------- | ---------------------------- | -| 10.{{ team_nr }}.1.0/24 | basic substitution | 10. **[1-5]** .1.0/24 | -| a:b:c:d:12{{ team_nr_str }}::/80 | leftpad equivalent, makes the substitution always be 2 characters, with 0 in front | a:b:c:d:12 **[01-05]** ::/80 | -| 100.64.{{ team_nr | plus: 200 }}.0/24 | offset applied to addressing, allows for more efficient use of address space | 100.64. **[201-205]** .0/24 | +| Pattern | Description | Resulting values | +| ------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------- | +| 10.{{ actor_nr }}.1.0/24 | basic substitution | 10. **[1-5]** .1.0/24 | +| a:b:c:d:12{{ actor_nr_str }}::/80 | leftpad equivalent, makes the substitution always be 2 characters, with 0 in front | a:b:c:d:12 **[01-05]** ::/80 | +| 100.64.{{ actor_nr | plus: 200 }}.0/24 | offset applied to addressing, allows for more efficient use of address space | 100.64. **[201-205]** .0/24 | diff --git a/app/views/virtual_machines/_address_preview.html.haml b/app/views/virtual_machines/_address_preview.html.haml index dce40159..6509999a 100644 --- a/app/views/virtual_machines/_address_preview.html.haml +++ b/app/views/virtual_machines/_address_preview.html.haml @@ -1,11 +1,11 @@ - cache([@virtual_machine.cache_key_with_version, 'address_preview']) do = render TableComponent.new do |c| - - @virtual_machine.host_spec.deployable_instances(AddressPreviewPresenter).group_by(&:team_number).each do |team_nr, instances| + - @virtual_machine.host_spec.deployable_instances(AddressPreviewPresenter).group_by(&:team_number).each do |actor_nr, instances| - c.with_table_row do - %td.text-center.font-lg.font-bold.py-2{colspan: 100, class: actor_color_classes(team_nr ? @virtual_machine.numbered_actor : @virtual_machine.actor)} - - if team_nr + %td.text-center.font-lg.font-bold.py-2{colspan: 100, class: actor_color_classes(actor_nr ? @virtual_machine.numbered_actor : @virtual_machine.actor)} + - if actor_nr = @virtual_machine.numbered_actor.name - %strong= team_nr.to_s.rjust(2, "0") + %strong= actor_nr.to_s.rjust(2, "0") - else = @virtual_machine.actor.name diff --git a/spec/calculations/address_values_spec.rb b/spec/calculations/address_values_spec.rb index dafb48bf..6b631f2a 100644 --- a/spec/calculations/address_values_spec.rb +++ b/spec/calculations/address_values_spec.rb @@ -13,7 +13,7 @@ end context 'and dynamic last octet' do - let(:address_pool) { build(:address_pool, network_address: '1.0.0.{{ team_nr }}/27', network:) } + let(:address_pool) { build(:address_pool, network_address: '1.0.0.{{ actor_nr }}/27', network:) } it { is_expected.to be_nil } diff --git a/spec/calculations/liquid_range_substitution_spec.rb b/spec/calculations/liquid_range_substitution_spec.rb index e91f6835..fd1c56f7 100644 --- a/spec/calculations/liquid_range_substitution_spec.rb +++ b/spec/calculations/liquid_range_substitution_spec.rb @@ -9,6 +9,24 @@ context 'vm' do let(:source_object) { build(:virtual_machine) } + context 'with actor number node' do + let(:input_string) { '{{ actor_nr }}' } + + it { is_expected.to eq 'actor_nr: N/A' } + + context 'with numbered actor' do + let(:source_object) { build(:virtual_machine, actor: build(:actor, :numbered)) } + + it { is_expected.to eq "actor_nr: 1 - #{source_object.actor.number}" } + end + + context 'with numbering actor' do + let(:source_object) { build(:virtual_machine, numbered_by: build(:actor, :numbered)) } + + it { is_expected.to eq "actor_nr: 1 - #{source_object.numbered_by.number}" } + end + end + context 'with team number node' do let(:input_string) { '{{ team_nr }}' } diff --git a/spec/models/address_pool_spec.rb b/spec/models/address_pool_spec.rb index 5a2356e3..9a7d880e 100644 --- a/spec/models/address_pool_spec.rb +++ b/spec/models/address_pool_spec.rb @@ -8,7 +8,7 @@ context 'dynamic last octet' do let(:network) { build(:network, actor: build(:actor, :numbered)) } - subject { build(:address_pool, network_address: '1.0.0.{{ team_nr }}/28', network:) } + subject { build(:address_pool, network_address: '1.0.0.{{ actor_nr }}/28', network:) } it 'should generate correctly numbered network segments' do expect(subject.ip_network(1).to_string).to eq '1.0.0.0/28' diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb index 571c4734..156048bd 100644 --- a/spec/models/address_spec.rb +++ b/spec/models/address_spec.rb @@ -42,7 +42,7 @@ end context 'numbered net' do - let(:network_address) { '1.{{ team_nr }}.0.0/24' } + let(:network_address) { '1.{{ actor_nr }}.0.0/24' } it 'should return first address by default' do expect(subject.ip_object.to_s).to eq '1.1.0.1' @@ -112,7 +112,7 @@ end context 'numbered net' do - let(:network_address) { '1:a:{{ team_nr }}::/64' } + let(:network_address) { '1:a:{{ actor_nr }}::/64' } it 'should return first address by default' do expect(subject.ip_object.to_s).to eq '1:a:1::' diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb index b53c662f..e67c839c 100644 --- a/spec/presenters/instance_presenter_spec.rb +++ b/spec/presenters/instance_presenter_spec.rb @@ -6,11 +6,11 @@ let(:virtual_machine) { create(:virtual_machine) } let(:spec) { virtual_machine.host_spec } - let(:team_nr) { nil } + let(:actor_nr) { nil } let(:seq_nr) { nil } let(:opts) { nil } - subject { described_class.new(spec, team_nr, seq_nr, **opts) } + subject { described_class.new(spec, actor_nr, seq_nr, **opts) } before { Current.interfaces_cache ||= {} diff --git a/spec/requests/api_v3_network_spec.rb b/spec/requests/api_v3_network_spec.rb index 6f7e1918..69c3d9f7 100644 --- a/spec/requests/api_v3_network_spec.rb +++ b/spec/requests/api_v3_network_spec.rb @@ -25,6 +25,7 @@ actor: net.actor.abbreviation.downcase, instances: [{ team_nr: nil, + actor_nr: nil, cloud_id: '', domains: [], address_pools: [ @@ -40,7 +41,7 @@ context 'with numbered network' do let!(:numbered_actor) { create(:actor, :numbered, exercise:) } - let!(:networks) { [create(:network, exercise:, actor: numbered_actor, cloud_id: 'hello{{ team_nr }}')] } + let!(:networks) { [create(:network, exercise:, actor: numbered_actor, cloud_id: 'hello{{ actor_nr }}')] } it 'should list network with numbered instances' do expect(response).to be_successful @@ -53,6 +54,7 @@ actor: numbered_actor.abbreviation.downcase, instances: [{ team_nr: 1, + actor_nr: 1, cloud_id: 'hello1', domains: [], address_pools: [ @@ -62,6 +64,7 @@ config_map: nil }, { team_nr: 2, + actor_nr: 2, cloud_id: 'hello2', domains: [], address_pools: [ @@ -71,6 +74,7 @@ config_map: nil }, { team_nr: 3, + actor_nr: 3, cloud_id: 'hello3', domains: [], address_pools: [