Skip to content

Commit

Permalink
Collect uptime data in daily heartbeat from connect-ng
Browse files Browse the repository at this point in the history
  • Loading branch information
cjainsuse committed Feb 13, 2024
1 parent 3c686d4 commit b9700ed
Show file tree
Hide file tree
Showing 16 changed files with 319 additions and 150 deletions.
16 changes: 16 additions & 0 deletions app/controllers/api/connect/v3/systems/systems_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ class Api::Connect::V3::Systems::SystemsController < Api::Connect::BaseControlle
before_action :authenticate_system

def update
if params[:online_at].present?
params[:online_at].each do |online_at|
dthours = online_at.split(':')
if dthours.count == 2
begin
@system_uptime = SystemUptime.create!(system_id: @system.id, online_at_day: dthours[0], online_at_hours: dthours[1])
logger.debug(N_("Added uptime information for system '%s'") % @system.id)
rescue ActiveRecord::RecordNotUnique
logger.debug(N_("Uptime information existing for system '%s'") % @system.id)
end
else
logger.error(N_("Uptime data is malformed '%s'") % online_at)
end
end
end

@system.hostname = params[:hostname]

# Since the payload is handled by rails all values are converted to string
Expand Down
1 change: 1 addition & 0 deletions app/models/system.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class System < ApplicationRecord
has_many :services, through: :activations
has_many :repositories, -> { distinct }, through: :services
has_many :products, -> { distinct }, through: :services
has_many :system_uptimes, dependent: :destroy

validates :system_token, uniqueness: { scope: %i[login password], case_sensitive: false }

Expand Down
7 changes: 7 additions & 0 deletions app/models/system_uptime.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class SystemUptime < ApplicationRecord
belongs_to :system

validates :system_id, presence: true
validates :online_at_day, presence: true
validates :online_at_hours, presence: true
end
18 changes: 18 additions & 0 deletions db/migrate/20240111200053_create_system_uptimes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class CreateSystemUptimes < ActiveRecord::Migration[6.1]
def change
safety_assured do
create_table :system_uptimes, id: :serial, force: :cascade do |t|
t.bigint :system_id, null: false
t.date :online_at_day, null: false
t.column :online_at_hours, 'binary(24)', null: false
t.timestamps
end

commit_db_transaction

add_index :system_uptimes, %i[system_id online_at_day], unique: true, name: 'id_online_day'

add_foreign_key :system_uptimes, :systems, column: :system_id, validate: false
end
end
end
295 changes: 152 additions & 143 deletions db/schema.rb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/rmt.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module RMT
VERSION ||= '2.15'.freeze
VERSION ||= '2.16'.freeze

DEFAULT_USER = '_rmt'.freeze
DEFAULT_GROUP = 'nginx'.freeze
Expand Down
16 changes: 16 additions & 0 deletions lib/suse/connect/system_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class SUSE::Connect::SystemSerializer < ActiveModel::Serializer
attribute :hostname, if: :needs_full_update?
attribute :hwinfo, if: :has_hwinfo_and_needs_full_update?
attribute :products, if: :needs_full_update?
attribute :systemuptimes, if: :has_systemuptime?

# We send the internal system id as system_token if the system (in RMT) is
# duplicated (therefore using the system_token mechanism).
Expand Down Expand Up @@ -47,6 +48,17 @@ def products
end
end

def systemuptimes
object.system_uptimes.map do |systemuptime|
payload = {
system_id: systemuptime.system_id,
online_at_day: systemuptime.online_at_day,
online_at_hours: systemuptime.online_at_hours
}
payload
end
end

def hwinfo
JSON.parse(object.system_information).symbolize_keys
end
Expand All @@ -59,6 +71,10 @@ def has_system_token?
object.system_token.present?
end

def has_systemuptime?
systemuptimes.any?
end

def needs_full_update?
!object.scc_synced_at
end
Expand Down
8 changes: 8 additions & 0 deletions lib/tasks/system_uptimes.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace :db do
namespace :maintenance do
desc 'Delete all uptime tracking data which are older than 90 days'
task cleanup_uptime_tracking: :environment do
SystemUptime.where("online_at_day < '#{2.days.ago}'").delete_all
end
end
end
12 changes: 12 additions & 0 deletions package/files/systemd/rmt-uptime-cleanup.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Unit]
Description=RMT uptime cleanup service
Requires=mysql.service
Wants=rmt-uptime-cleanup.timer

[Service]
Type=oneshot
WorkingDirectory=/usr/share/rmt/bin
ExecStart=bundle exec rake db:maintenance:cleanup_uptime_tracking RAILS_ENV=production

[Install]
WantedBy=multi-user.target
10 changes: 10 additions & 0 deletions package/files/systemd/rmt-uptime-cleanup.timer
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=RMT Uptime Data cleanup timer

[Timer]
# Run this timer every day at a randomized 24h delay.
OnCalendar=daily
RandomizedDelaySec=24h

[Install]
WantedBy=timers.target
18 changes: 12 additions & 6 deletions package/obs/rmt-server.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#
# spec file for package rmt-server
#
# Copyright (c) 2023 SUSE LLC
# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
Expand Down Expand Up @@ -30,7 +30,7 @@
%define ruby_version %{rb_default_ruby_suffix}

Name: rmt-server
Version: 2.15
Version: 2.16
Release: 0
Summary: Repository mirroring tool and registration proxy for SCC
License: GPL-2.0-or-later
Expand Down Expand Up @@ -144,13 +144,15 @@ mkdir -p %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server-mirror.timer %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server-sync.timer %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server-systems-scc-sync.timer %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-uptime-cleanup.timer %{buildroot}%{_unitdir}

install -m 444 package/files/systemd/rmt-server-mirror.service %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server-sync.service %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server-systems-scc-sync.service %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server.service %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server.target %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-server-migration.service %{buildroot}%{_unitdir}
install -m 444 package/files/systemd/rmt-uptime-cleanup.service %{buildroot}%{_unitdir}

install -m 444 engines/registration_sharing/package/rmt-server-regsharing.service %{buildroot}%{_unitdir}
install -m 444 engines/registration_sharing/package/rmt-server-regsharing.timer %{buildroot}%{_unitdir}
Expand All @@ -163,6 +165,7 @@ ln -fs %{_sbindir}/service %{buildroot}%{_sbindir}/rcrmt-server-migration
ln -fs %{_sbindir}/service %{buildroot}%{_sbindir}/rcrmt-server-mirror
ln -fs %{_sbindir}/service %{buildroot}%{_sbindir}/rcrmt-server-sync
ln -fs %{_sbindir}/service %{buildroot}%{_sbindir}/rcrmt-server-systems-scc-sync
ln -fs %{_sbindir}/service %{buildroot}%{_sbindir}/rcrmt-uptime-cleanup

ln -fs %{_sbindir}/service %{buildroot}%{_sbindir}/rcrmt-server-regsharing
ln -fs %{_sbindir}/service %{buildroot}%{_sbindir}/rcrmt-server-trim-cache
Expand Down Expand Up @@ -269,6 +272,7 @@ chrpath -d %{buildroot}%{lib_dir}/vendor/bundle/ruby/*/extensions/*/*/mysql2-*/m
%{_sbindir}/rcrmt-server-sync
%{_sbindir}/rcrmt-server-mirror
%{_sbindir}/rcrmt-server-systems-scc-sync
%{_sbindir}/rcrmt-uptime-cleanup
%{_unitdir}/rmt-server.target
%{_unitdir}/rmt-server.service
%{_unitdir}/rmt-server-migration.service
Expand All @@ -278,6 +282,8 @@ chrpath -d %{buildroot}%{lib_dir}/vendor/bundle/ruby/*/extensions/*/*/mysql2-*/m
%{_unitdir}/rmt-server-sync.timer
%{_unitdir}/rmt-server-systems-scc-sync.service
%{_unitdir}/rmt-server-systems-scc-sync.timer
%{_unitdir}/rmt-uptime-cleanup.service
%{_unitdir}/rmt-uptime-cleanup.timer
%dir %{_datadir}/bash-completion/
%dir %{_datadir}/bash-completion/completions/
%{_datadir}/bash-completion/completions/rmt-cli
Expand Down Expand Up @@ -318,10 +324,10 @@ getent group %{rmt_group} >/dev/null || %{_sbindir}/groupadd -r %{rmt_group}
getent passwd %{rmt_user} >/dev/null || \
%{_sbindir}/useradd -g %{rmt_group} -s /bin/false -r \
-c "user for RMT" %{rmt_user}
%service_add_pre rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service
%service_add_pre rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service rmt-uptime-cleanup.service

%post
%service_add_post rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service
%service_add_post rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service rmt-uptime-cleanup.service

# Run only on install
if [ $1 -eq 1 ]; then
Expand Down Expand Up @@ -354,10 +360,10 @@ if [ ! -e %{_datadir}/rmt/public/suma ]; then
fi

%preun
%service_del_preun rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service
%service_del_preun rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service rmt-uptime-cleanup.service

%postun
%service_del_postun rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service
%service_del_postun rmt-server.target rmt-server.service rmt-server-migration.service rmt-server-mirror.service rmt-server-sync.service rmt-server-systems-scc-sync.service rmt-uptime-cleanup.service

%posttrans config
# Don't fail if either systemd or nginx are not running
Expand Down
7 changes: 7 additions & 0 deletions spec/factories/system_uptimes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryBot.define do
factory :system_uptime do
association :system
sequence(:online_at_day) { Time.zone.now }
online_at_hours { '111111111111111111111111' }
end
end
6 changes: 6 additions & 0 deletions spec/factories/systems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,11 @@
trait :with_system_token do
sequence(:system_token) { |n| "00000000-0000-4000-9000-#{n.to_s.rjust(12, '0')}" }
end

trait :with_system_uptimes do
after :create do |system, _|
create(:system_uptime, system: system)
end
end
end
end
9 changes: 9 additions & 0 deletions spec/lib/suse/connect/system_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,13 @@
expect(serializer.key? :system_token).to eq(false)
end
end

context 'system with systemuptime' do
let(:system) { create :system, :with_system_uptimes }

it 'match systemuptime data' do
expect((serializer[:systemuptimes][0][:online_at_day]).to_date).to eq(Time.zone.now.to_date)
expect((serializer[:systemuptimes][0][:online_at_hours]).to_s).to eq('111111111111111111111111')
end
end
end
7 changes: 7 additions & 0 deletions spec/models/system_uptime_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'rails_helper'

RSpec.describe SystemUptime, type: :model do
it { is_expected.to validate_presence_of(:system_id) }
it { is_expected.to validate_presence_of(:online_at_day) }
it { is_expected.to validate_presence_of(:online_at_hours) }
end
37 changes: 37 additions & 0 deletions spec/requests/api/connect/v3/systems/systems_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
}
end
let(:payload) { { hostname: 'test', hwinfo: hwinfo } }
let(:systemuptime) { system.system_uptimes.first }
let(:online_hours) { ':111111111111111111111111' }

describe '#update' do
subject(:update_action) { put url, params: payload, headers: headers }
Expand Down Expand Up @@ -45,6 +47,41 @@
expect(information[:cpus]).to eq('16')
end
end

context 'when uptime data provided' do
let(:payload) { { hostname: 'test', hwinfo: hwinfo, online_at: [1.day.ago.to_date.to_s << online_hours] } }

it 'inserts the uptime data in system_uptimes table' do
update_action

expect(systemuptime.system_id).to eq(system.reload.id)
expect(systemuptime.online_at_day.to_date).to eq(1.day.ago.to_date)
expect(systemuptime.online_at_hours.to_s).to eq('111111111111111111111111')
end
end

context 'when same uptime data added twice' do
let(:payload) { { hostname: 'test', hwinfo: hwinfo, online_at: [1.day.ago.to_date.to_s << online_hours, 1.day.ago.to_date.to_s << online_hours] } }

it 'avoids duplication if multiple records have same data' do
update_action

expect(system.system_uptimes.count).to eq(1)
expect(systemuptime.system_id).to eq(system.reload.id)
expect(systemuptime.online_at_day.to_date).to eq(1.day.ago.to_date)
expect(systemuptime.online_at_hours.to_s).to eq('111111111111111111111111')
end
end

context 'when uptime data is malformed' do
let(:payload) { { hostname: 'test', hwinfo: hwinfo, online_at: [1.day.ago.to_date.to_s] } }

it 'record is not inserted' do
update_action

expect(system.system_uptimes.count).to eq(0)
end
end
end

context 'when hostname is not provided' do
Expand Down

0 comments on commit b9700ed

Please sign in to comment.