diff --git a/app/models/cloud_connector/service_access_token.rb b/app/models/cloud_connector/service_access_token.rb index e026b10ec0c037f90876f28704df8ae4914e9dde..eeaf946b774fe2eb43f653e936e4d8525a8938ee 100644 --- a/app/models/cloud_connector/service_access_token.rb +++ b/app/models/cloud_connector/service_access_token.rb @@ -15,5 +15,9 @@ class ServiceAccessToken < ApplicationRecord encode_iv: false validates :token, :expires_at, presence: true + + def expired? + expires_at.past? + end end end diff --git a/ee/app/models/cloud_connector/access.rb b/ee/app/models/cloud_connector/access.rb index 8b2656c31eef9d5d78fdc23b7b7bf4bdca18fa24..370671ab1fbe9c6c35909aa272c4038de6033b1d 100644 --- a/ee/app/models/cloud_connector/access.rb +++ b/ee/app/models/cloud_connector/access.rb @@ -2,6 +2,10 @@ module CloudConnector class Access < ApplicationRecord + # Technically, access data has no expiration date, but we know that tokens + # are good for at most 3 days currently, so this is a good estimate. + STALE_PERIOD = 3.days + self.table_name = 'cloud_connector_access' validates :data, json_schema: { filename: "cloud_connector_access" } validates :data, presence: true diff --git a/ee/app/services/cloud_connector/status_checks/probes/access_probe.rb b/ee/app/services/cloud_connector/status_checks/probes/access_probe.rb new file mode 100644 index 0000000000000000000000000000000000000000..4bfb8713796a8f8e6d1f9743d52e2c015b978df5 --- /dev/null +++ b/ee/app/services/cloud_connector/status_checks/probes/access_probe.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module CloudConnector + module StatusChecks + module Probes + class AccessProbe < BaseProbe + def execute(*) + access_record = CloudConnector::Access.last + return failure("Access data is missing") unless access_record + + is_stale = (Time.current - access_record.updated_at) > CloudConnector::Access::STALE_PERIOD + return failure("Access data is stale") if is_stale + + last_token = CloudConnector::ServiceAccessToken.last + return failure("Access token is missing") unless last_token + return failure("Access token has expired") if last_token.expired? + + success("Access data is valid") + end + end + end + end +end diff --git a/ee/app/services/cloud_connector/status_checks/status_service.rb b/ee/app/services/cloud_connector/status_checks/status_service.rb index d2b5842a21443293721ee11b377dfc7b2ebdccff..96c61906e4df1f53fe342196a705229eff7b806f 100644 --- a/ee/app/services/cloud_connector/status_checks/status_service.rb +++ b/ee/app/services/cloud_connector/status_checks/status_service.rb @@ -12,6 +12,7 @@ class StatusService CloudConnector::StatusChecks::Probes::LicenseProbe.new, CloudConnector::StatusChecks::Probes::HostProbe.new(CUSTOMERS_DOT_HOST, 443), CloudConnector::StatusChecks::Probes::HostProbe.new(CLOUD_CONNECTOR_HOST, 443), + CloudConnector::StatusChecks::Probes::AccessProbe.new, CloudConnector::StatusChecks::Probes::EndToEndProbe.new ].freeze diff --git a/ee/spec/factories/cloud_connector/access.rb b/ee/spec/factories/cloud_connector/access.rb index 6d13f4b0cf69463d787669f645dc65b2e0993fcd..66d97aac6f714e5483f88d260b4ea913301b5d2d 100644 --- a/ee/spec/factories/cloud_connector/access.rb +++ b/ee/spec/factories/cloud_connector/access.rb @@ -18,5 +18,13 @@ ] } end + + trait :current do + updated_at { Time.current } + end + + trait :stale do + updated_at { Time.current - ::CloudConnector::Access::STALE_PERIOD - 1.minute } + end end end diff --git a/ee/spec/services/cloud_connector/status_checks/probes/access_probe_spec.rb b/ee/spec/services/cloud_connector/status_checks/probes/access_probe_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..63ff61e5b36e82988276f548e2f907b2e36cb365 --- /dev/null +++ b/ee/spec/services/cloud_connector/status_checks/probes/access_probe_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe CloudConnector::StatusChecks::Probes::AccessProbe, :freeze_time, feature_category: :cloud_connector do + describe '#execute' do + using RSpec::Parameterized::TableSyntax + + subject(:probe) { described_class.new } + + let(:now) { Time.current } + + # nil trait means record is missing + where(:access_trait, :token_trait, :success?, :message) do + :current | :active | true | 'Access data is valid' + nil | :active | false | 'Access data is missing' + :stale | :active | false | 'Access data is stale' + :current | nil | false | 'Access token is missing' + :current | :expired | false | 'Access token has expired' + end + + with_them do + it 'returns the expected result' do + create(:cloud_connector_access, access_trait) if access_trait + create(:service_access_token, token_trait) if token_trait + + result = probe.execute + + expect(result).to be_a(CloudConnector::StatusChecks::Probes::ProbeResult) + expect(result.success?).to be success? + expect(result.message).to eq(message) + end + end + end +end diff --git a/ee/spec/services/cloud_connector/status_checks/status_service_spec.rb b/ee/spec/services/cloud_connector/status_checks/status_service_spec.rb index 7138b86509fc23688fe1722b3962180f63d6bee5..0008a08b5048889ea67886e43b63fa6f11949320 100644 --- a/ee/spec/services/cloud_connector/status_checks/status_service_spec.rb +++ b/ee/spec/services/cloud_connector/status_checks/status_service_spec.rb @@ -20,6 +20,7 @@ an_instance_of(CloudConnector::StatusChecks::Probes::HostProbe).and( have_attributes(host: 'customers.staging.gitlab.com', port: 443) ), + an_instance_of(CloudConnector::StatusChecks::Probes::AccessProbe), an_instance_of(CloudConnector::StatusChecks::Probes::EndToEndProbe) ]) end diff --git a/spec/models/cloud_connector/service_access_token_spec.rb b/spec/models/cloud_connector/service_access_token_spec.rb index 4239ec486a50584b54b3843375c94240bd40100d..462a20d858f413a884a9d283735cfa53c848c4e8 100644 --- a/spec/models/cloud_connector/service_access_token_spec.rb +++ b/spec/models/cloud_connector/service_access_token_spec.rb @@ -3,19 +3,16 @@ require 'spec_helper' RSpec.describe CloudConnector::ServiceAccessToken, type: :model, feature_category: :cloud_connector do - describe '.expired', :freeze_time do - let_it_be(:expired_token) { create(:service_access_token, :expired) } - let_it_be(:active_token) { create(:service_access_token, :active) } + let_it_be(:expired_token) { create(:service_access_token, :expired) } + let_it_be(:active_token) { create(:service_access_token, :active) } + describe '.expired', :freeze_time do it 'selects all expired tokens' do expect(described_class.expired).to match_array([expired_token]) end end describe '.active', :freeze_time do - let_it_be(:expired_token) { create(:service_access_token, :expired) } - let_it_be(:active_token) { create(:service_access_token, :active) } - it 'selects all active tokens' do expect(described_class.active).to match_array([active_token]) end @@ -42,4 +39,14 @@ it { is_expected.to validate_presence_of(:expires_at) } end end + + describe '#expired?' do + it 'returns false for active token' do + expect(active_token).not_to be_expired + end + + it 'returns true for expired token' do + expect(expired_token).to be_expired + end + end end