From 838171b900a8dd95677320e4ee8912fac43b4b63 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Mon, 5 May 2025 20:15:04 +1000 Subject: [PATCH 01/16] Add internal event and metrics for workspace creation --- .../workspace_operations/create.rb | 4 ++ ee/config/events/create_workspace_result.yml | 21 +++++++ .../count_total_failed_workspaces_created.yml | 24 ++++++++ ...count_total_succeed_workspaces_created.yml | 24 ++++++++ .../create/creation_event_tracker.rb | 38 +++++++++++++ .../workspace_operations/create/main.rb | 3 + .../create/creation_event_tracker_spec.rb | 55 +++++++++++++++++++ 7 files changed, 169 insertions(+) create mode 100644 ee/config/events/create_workspace_result.yml create mode 100644 ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml create mode 100644 ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml create mode 100644 ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb create mode 100644 ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb diff --git a/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb b/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb index 03ba3fdd483663..da4f664307fb2e 100644 --- a/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb +++ b/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb @@ -8,6 +8,8 @@ class Create < BaseMutation include Gitlab::Utils::UsageData + # include Gitlab::InternalEventsTracking + authorize :create_workspace field :workspace, @@ -116,6 +118,8 @@ def resolve(args) # noinspection RubyNilAnalysis - This is because the superclass #current_user uses #[], which can return nil track_usage_event(:users_creating_workspaces, current_user.id) + # track_internal_event('create_workspace_result', user: current_user, + # additional_properties: { label: "failed" }) params = args.merge( agent: agent, diff --git a/ee/config/events/create_workspace_result.yml b/ee/config/events/create_workspace_result.yml new file mode 100644 index 00000000000000..c06022a4795f60 --- /dev/null +++ b/ee/config/events/create_workspace_result.yml @@ -0,0 +1,21 @@ +--- +description: GitLab Workspace creation result and error message +internal_events: true +action: create_workspace_result +identifiers: +- project +- namespace +- user +additional_properties: + label: + description: workspace succeed or fail + property: + description: error message if failed +product_group: remote_development +product_categories: +- workspaces +milestone: '18.0' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +tiers: +- premium +- ultimate diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml new file mode 100644 index 00000000000000..55a35539933310 --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml @@ -0,0 +1,24 @@ +--- +key_path: counts.count_total_failed_workspaces_created +description: Count of Total Failed Workspaces Created +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.0' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: failed diff --git a/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml new file mode 100644 index 00000000000000..74cff4daafd050 --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml @@ -0,0 +1,24 @@ +--- +key_path: counts.count_total_succeed_workspaces_created +description: Count of Total Succeed Workspaces Created +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.0' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: succeed diff --git a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb new file mode 100644 index 00000000000000..5ce54e56ca5c54 --- /dev/null +++ b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module RemoteDevelopment + module WorkspaceOperations + module Create + class CreationEventTracker + include Gitlab::InternalEventsTracking + include Messages + extend Gitlab::Fp::MessageSupport + + def self.track(context, result) + context => { + user: user, + params: { + project: project, + } + } + + # puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + # puts project.inspect + # puts user.inspect + # puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + + tracker = new + + case result + in { err: message } + tracker.track_internal_event('create_workspace_result', project: project, user: user, + additional_properties: { label: "failed", property: message.class }) + in { ok: message } + tracker.track_internal_event('create_workspace_result', project: project, user: user, + additional_properties: { label: "succeed" }) + end + end + end + end + end +end diff --git a/ee/lib/remote_development/workspace_operations/create/main.rb b/ee/lib/remote_development/workspace_operations/create/main.rb index 461e4acf1a6896..b3a10a8093e3df 100644 --- a/ee/lib/remote_development/workspace_operations/create/main.rb +++ b/ee/lib/remote_development/workspace_operations/create/main.rb @@ -29,6 +29,9 @@ def self.main(context) .map(VolumeComponentInserter.method(:insert)) .and_then(Creator.method(:create)) + # track workspace creation via internal events + CreationEventTracker.track(context, result) + # rubocop:disable Lint/DuplicateBranch -- Rubocop doesn't know the branches are different due to destructuring case result in { err: WorkspaceCreateParamsValidationFailed => message } diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb new file mode 100644 index 00000000000000..b3a2bd6e70c4f3 --- /dev/null +++ b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "fast_spec_helper" +require_relative "../../../../support/fast_spec/remote_development/fast_spec_helper_support" + +RSpec.describe RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker, feature_category: :workspaces do + let(:user) { instance_double(:user, id: 1) } + let(:project) { instance_double(:project, id: 1) } + let(:context) do + { + user: user, + params: { + project: project + } + } + end + + let(:tracker_instance) { instance_double(described_class) } + + before do + allow(described_class).to receive(:new).and_return(tracker_instance) + end + + context 'when result contains an error' do + let(:error_message) { StandardError.new('Something went wrong') } + let(:result) { { err: error_message } } + + it 'tracks a failed event with the error class' do + expect(tracker_instance).to receive(:track_internal_event).with( + 'create_workspace_result', + project: project, + user: user, + additional_properties: { label: 'failed', property: error_message.class } + ) + + described_class.track(context, result) + end + end + + context 'when result contains a success' do + let(:success_message) { 'Success' } + let(:result) { { ok: success_message } } + + it 'tracks a succeeded event' do + expect(tracker_instance).to receive(:track_internal_event).with( + 'create_workspace_result', + project: project, + user: user, + additional_properties: { label: 'succeed' } + ) + + described_class.track(context, result) + end + end +end -- GitLab From 3167411a69abd5ac3017f3327f197a6d5afeae35 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Tue, 6 May 2025 10:41:59 +1000 Subject: [PATCH 02/16] fix rspec --- .../remote_development/workspace_operations/create/main_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb index 265def41ebf98c..de6427388e92f5 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb @@ -31,6 +31,7 @@ end it "returns expected response" do + expect(::RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker).to receive(:track).once # noinspection RubyResolve - https://handbook.gitlab.com/handbook/tools-and-tips/editors-and-ides/jetbrains-ides/tracked-jetbrains-issues/#ruby-31542 expect do described_class.main(context_passed_along_steps) @@ -54,6 +55,7 @@ shared_examples "rop invocation with error response" do it "returns expected response" do + expect(::RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker).to receive(:track).once # noinspection RubyResolve - https://handbook.gitlab.com/handbook/tools-and-tips/editors-and-ides/jetbrains-ides/tracked-jetbrains-issues/#ruby-31542 expect do described_class.main(context_passed_along_steps) -- GitLab From eb0d6dc8915fbc6c32440fc95b2c48cfd46e5793 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Wed, 7 May 2025 10:53:33 +1000 Subject: [PATCH 03/16] Fix rspec --- .../create/creation_event_tracker_spec.rb | 4 ++-- .../workspace_operations/create/main_integration_spec.rb | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb index b3a2bd6e70c4f3..468ac79b0a70fa 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb @@ -4,8 +4,8 @@ require_relative "../../../../support/fast_spec/remote_development/fast_spec_helper_support" RSpec.describe RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker, feature_category: :workspaces do - let(:user) { instance_double(:user, id: 1) } - let(:project) { instance_double(:project, id: 1) } + let(:user) { instance_double("user", id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper + let(:project) { instance_double("Project", id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper let(:context) do { user: user, diff --git a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb index a14c9a1fb43d50..17d2fe0e21cf32 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb @@ -93,6 +93,10 @@ described_class.main(context) end + before do + allow(::RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker).to receive(:track) + end + context 'when params are valid' do before do allow(project.repository).to receive_message_chain(:blob_at_branch, :data) { devfile_yaml } -- GitLab From 20f3711ca6268fa877e70473bcf8a13d917391ef Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Wed, 7 May 2025 11:44:06 +1000 Subject: [PATCH 04/16] Fix rspec --- .../create/creation_event_tracker_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb index 468ac79b0a70fa..e65c65b86c2d76 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb @@ -4,8 +4,8 @@ require_relative "../../../../support/fast_spec/remote_development/fast_spec_helper_support" RSpec.describe RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker, feature_category: :workspaces do - let(:user) { instance_double("user", id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper - let(:project) { instance_double("Project", id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper + let(:user) { instance_double('User', id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper + let(:project) { instance_double('Project', id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper let(:context) do { user: user, -- GitLab From 191b10604fa91f857dfd98d99ccc965748c7a8e0 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Wed, 7 May 2025 20:22:04 +1000 Subject: [PATCH 05/16] Update event and metrics --- ...total_failed_reason_workspaces_created.yml | 45 +++++++++++++++++++ .../count_total_failed_workspaces_created.yml | 2 +- ...count_total_succeed_workspaces_created.yml | 2 +- .../create/creation_event_tracker.rb | 2 +- 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml diff --git a/ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml new file mode 100644 index 00000000000000..2f938b60a12157 --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml @@ -0,0 +1,45 @@ +--- +key_path: counts.count_total_failed_reason_workspaces_created +description: Count of Total Failed Reason Workspaces Created +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.0' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result_failed_params_validation + filter: + label: failed + property: WorkspaceCreateParamsValidationFailed +- name: create_workspace_result_failed_devfile_yaml_parse + filter: + label: failed + property: WorkspaceCreateDevfileYamlParseFailed +- name: create_workspace_result_failed_devfile_yaml_load + filter: + label: failed + property: WorkspaceCreateDevfileLoadFailed +- name: create_workspace_result_failed_devfile_validation + filter: + label: failed + property: WorkspaceCreateDevfileValidationFailed +- name: create_workspace_result_failed_devfile_flattern + filter: + label: failed + property: WorkspaceCreateDevfileFlattenFailed +- name: create_workspace_result_failed_creation_failed + filter: + label: failed + property: WorkspaceCreateFailed diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml index 55a35539933310..bdc65706a044fd 100644 --- a/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml @@ -19,6 +19,6 @@ tiers: - premium - ultimate events: -- name: create_workspace_result +- name: create_workspace_result_failed filter: label: failed diff --git a/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml index 74cff4daafd050..9b045a9d07da76 100644 --- a/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml +++ b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml @@ -19,6 +19,6 @@ tiers: - premium - ultimate events: -- name: create_workspace_result +- name: create_workspace_result_succeed filter: label: succeed diff --git a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb index 5ce54e56ca5c54..e883448539df46 100644 --- a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb +++ b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb @@ -26,7 +26,7 @@ def self.track(context, result) case result in { err: message } tracker.track_internal_event('create_workspace_result', project: project, user: user, - additional_properties: { label: "failed", property: message.class }) + additional_properties: { label: "failed", property: message.class.to_s.split('::').last }) in { ok: message } tracker.track_internal_event('create_workspace_result', project: project, user: user, additional_properties: { label: "succeed" }) -- GitLab From a1460c59579582e79c5bec9969d2abfc6e82967f Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Wed, 7 May 2025 20:23:10 +1000 Subject: [PATCH 06/16] Remove comments --- .../remote_development/workspace_operations/create.rb | 4 ---- .../workspace_operations/create/creation_event_tracker.rb | 5 ----- 2 files changed, 9 deletions(-) diff --git a/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb b/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb index da4f664307fb2e..03ba3fdd483663 100644 --- a/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb +++ b/ee/app/graphql/mutations/remote_development/workspace_operations/create.rb @@ -8,8 +8,6 @@ class Create < BaseMutation include Gitlab::Utils::UsageData - # include Gitlab::InternalEventsTracking - authorize :create_workspace field :workspace, @@ -118,8 +116,6 @@ def resolve(args) # noinspection RubyNilAnalysis - This is because the superclass #current_user uses #[], which can return nil track_usage_event(:users_creating_workspaces, current_user.id) - # track_internal_event('create_workspace_result', user: current_user, - # additional_properties: { label: "failed" }) params = args.merge( agent: agent, diff --git a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb index e883448539df46..81776349b57e5c 100644 --- a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb +++ b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb @@ -16,11 +16,6 @@ def self.track(context, result) } } - # puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - # puts project.inspect - # puts user.inspect - # puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - tracker = new case result -- GitLab From 099ef11fb55b981b6078047e38dc21153baf8f67 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Thu, 8 May 2025 11:28:29 +1000 Subject: [PATCH 07/16] Fix failing rspec --- .../workspace_operations/create/creation_event_tracker_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb index e65c65b86c2d76..266ef957808d9e 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb @@ -30,7 +30,7 @@ 'create_workspace_result', project: project, user: user, - additional_properties: { label: 'failed', property: error_message.class } + additional_properties: { label: 'failed', property: error_message.class.to_s } ) described_class.track(context, result) -- GitLab From 7d839692e9b7341b287f6a545478d820dccb8e81 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Thu, 15 May 2025 11:27:12 +1000 Subject: [PATCH 08/16] Address comments --- ...total_failed_reason_workspaces_created.yml | 45 ------------------- .../count_total_failed_workspaces_created.yml | 2 +- ...count_total_succeed_workspaces_created.yml | 2 +- .../create/creation_event_tracker.rb | 3 ++ .../workspace_operations/create/main.rb | 1 - .../create/creation_event_tracker_spec.rb | 40 ++++++----------- 6 files changed, 19 insertions(+), 74 deletions(-) delete mode 100644 ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml diff --git a/ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml deleted file mode 100644 index 2f938b60a12157..00000000000000 --- a/ee/config/metrics/counts_all/count_total_failed_reason_workspaces_created.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- -key_path: counts.count_total_failed_reason_workspaces_created -description: Count of Total Failed Reason Workspaces Created -product_group: remote_development -product_categories: -- workspaces -performance_indicator_type: [] -value_type: number -status: active -milestone: '18.0' -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 -time_frame: -- 28d -- 7d -- all -data_source: internal_events -data_category: optional -tiers: -- premium -- ultimate -events: -- name: create_workspace_result_failed_params_validation - filter: - label: failed - property: WorkspaceCreateParamsValidationFailed -- name: create_workspace_result_failed_devfile_yaml_parse - filter: - label: failed - property: WorkspaceCreateDevfileYamlParseFailed -- name: create_workspace_result_failed_devfile_yaml_load - filter: - label: failed - property: WorkspaceCreateDevfileLoadFailed -- name: create_workspace_result_failed_devfile_validation - filter: - label: failed - property: WorkspaceCreateDevfileValidationFailed -- name: create_workspace_result_failed_devfile_flattern - filter: - label: failed - property: WorkspaceCreateDevfileFlattenFailed -- name: create_workspace_result_failed_creation_failed - filter: - label: failed - property: WorkspaceCreateFailed diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml index bdc65706a044fd..55a35539933310 100644 --- a/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml @@ -19,6 +19,6 @@ tiers: - premium - ultimate events: -- name: create_workspace_result_failed +- name: create_workspace_result filter: label: failed diff --git a/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml index 9b045a9d07da76..74cff4daafd050 100644 --- a/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml +++ b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml @@ -19,6 +19,6 @@ tiers: - premium - ultimate events: -- name: create_workspace_result_succeed +- name: create_workspace_result filter: label: succeed diff --git a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb index 81776349b57e5c..207aa04843b4af 100644 --- a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb +++ b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb @@ -8,6 +8,9 @@ class CreationEventTracker include Messages extend Gitlab::Fp::MessageSupport + # @param [Hash] context + # @param [Gitlab::Fp::Result] result + # @return [void] def self.track(context, result) context => { user: user, diff --git a/ee/lib/remote_development/workspace_operations/create/main.rb b/ee/lib/remote_development/workspace_operations/create/main.rb index b3a10a8093e3df..7c9cf5762c90ca 100644 --- a/ee/lib/remote_development/workspace_operations/create/main.rb +++ b/ee/lib/remote_development/workspace_operations/create/main.rb @@ -29,7 +29,6 @@ def self.main(context) .map(VolumeComponentInserter.method(:insert)) .and_then(Creator.method(:create)) - # track workspace creation via internal events CreationEventTracker.track(context, result) # rubocop:disable Lint/DuplicateBranch -- Rubocop doesn't know the branches are different due to destructuring diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb index 266ef957808d9e..fb883dec2999db 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true -require "fast_spec_helper" -require_relative "../../../../support/fast_spec/remote_development/fast_spec_helper_support" +require 'spec_helper' RSpec.describe RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker, feature_category: :workspaces do - let(:user) { instance_double('User', id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper - let(:project) { instance_double('Project', id: 1) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper + let_it_be(:user) { create(:user) } + let_it_be(:namespace) { create(:group, developers: user) } + let_it_be(:project) { create(:project, namespace: namespace) } + let(:subject_track) { described_class.track(context, result) } let(:context) do { user: user, @@ -15,25 +16,16 @@ } end - let(:tracker_instance) { instance_double(described_class) } - - before do - allow(described_class).to receive(:new).and_return(tracker_instance) - end - context 'when result contains an error' do let(:error_message) { StandardError.new('Something went wrong') } let(:result) { { err: error_message } } it 'tracks a failed event with the error class' do - expect(tracker_instance).to receive(:track_internal_event).with( - 'create_workspace_result', - project: project, - user: user, - additional_properties: { label: 'failed', property: error_message.class.to_s } - ) - - described_class.track(context, result) + expect { subject_track } + .to trigger_internal_events('create_workspace_result') + .with(user: user, project: project, additional_properties: { label: 'failed', + property: error_message.class.to_s }) + .and increment_usage_metrics('counts.count_total_failed_workspaces_created') end end @@ -42,14 +34,10 @@ let(:result) { { ok: success_message } } it 'tracks a succeeded event' do - expect(tracker_instance).to receive(:track_internal_event).with( - 'create_workspace_result', - project: project, - user: user, - additional_properties: { label: 'succeed' } - ) - - described_class.track(context, result) + expect { subject_track } + .to trigger_internal_events('create_workspace_result') + .with(user: user, project: project, additional_properties: { label: 'succeed' }) + .and increment_usage_metrics('counts.count_total_succeed_workspaces_created') end end end -- GitLab From 6599bbce5377ebca523e6f73816b6bb7553eed44 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Thu, 15 May 2025 14:46:36 +1000 Subject: [PATCH 09/16] Add failed metrics by reason --- ee/config/events/create_workspace_result.yml | 2 +- .../count_total_failed_workspaces_created.yml | 2 +- ..._workspaces_created_by_creation_failed.yml | 25 +++++++++++++++++++ ...ces_created_by_failed_devfile_flattern.yml | 25 +++++++++++++++++++ ...s_created_by_failed_devfile_validation.yml | 25 +++++++++++++++++++ ...es_created_by_failed_devfile_yaml_load.yml | 25 +++++++++++++++++++ ...s_created_by_failed_devfile_yaml_parse.yml | 25 +++++++++++++++++++ ...es_created_by_failed_params_validation.yml | 25 +++++++++++++++++++ ...count_total_succeed_workspaces_created.yml | 2 +- 9 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_creation_failed.yml create mode 100644 ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_flattern.yml create mode 100644 ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_validation.yml create mode 100644 ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_load.yml create mode 100644 ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_parse.yml create mode 100644 ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_params_validation.yml diff --git a/ee/config/events/create_workspace_result.yml b/ee/config/events/create_workspace_result.yml index c06022a4795f60..9959b5d127f089 100644 --- a/ee/config/events/create_workspace_result.yml +++ b/ee/config/events/create_workspace_result.yml @@ -14,7 +14,7 @@ additional_properties: product_group: remote_development product_categories: - workspaces -milestone: '18.0' +milestone: '18.1' introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 tiers: - premium diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml index 55a35539933310..e947e6a6d479b6 100644 --- a/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created.yml @@ -7,7 +7,7 @@ product_categories: performance_indicator_type: [] value_type: number status: active -milestone: '18.0' +milestone: '18.1' introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 time_frame: - 28d diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_creation_failed.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_creation_failed.yml new file mode 100644 index 00000000000000..8c4f26a178cb51 --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_creation_failed.yml @@ -0,0 +1,25 @@ +--- +key_path: counts.count_total_failed_workspaces_created_by_creation_failed +description: Count of Total Failed Workspaces Created By Creation Failed +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.1' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: failed + property: WorkspaceCreateFailed diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_flattern.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_flattern.yml new file mode 100644 index 00000000000000..f67c6396d5ba59 --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_flattern.yml @@ -0,0 +1,25 @@ +--- +key_path: counts.count_total_failed_workspaces_created_by_failed_devfile_flattern +description: Count of Total Failed Workspaces Created By Failed Devfile Flattern +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.1' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: failed + property: WorkspaceCreateDevfileFlattenFailed diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_validation.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_validation.yml new file mode 100644 index 00000000000000..1b4026cd22d9fe --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_validation.yml @@ -0,0 +1,25 @@ +--- +key_path: counts.count_total_failed_workspaces_created_by_failed_devfile_validation +description: Count of Total Failed Workspaces Created By Failed Devfile Validation +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.1' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: failed + property: WorkspaceCreateDevfileValidationFailed diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_load.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_load.yml new file mode 100644 index 00000000000000..a23bf2ecd7ca49 --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_load.yml @@ -0,0 +1,25 @@ +--- +key_path: counts.count_total_failed_workspaces_created_by_failed_devfile_yaml_load +description: Count of Total Failed Workspaces Created By Failed Devfile Yaml Load +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.1' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: failed + property: WorkspaceCreateDevfileLoadFailed diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_parse.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_parse.yml new file mode 100644 index 00000000000000..469bae11c7b0c8 --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_devfile_yaml_parse.yml @@ -0,0 +1,25 @@ +--- +key_path: counts.count_total_failed_workspaces_created_by_failed_devfile_yaml_parse +description: Count of Total Failed Workspaces Created By Failed Devfile Yaml Parse +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.1' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: failed + property: WorkspaceCreateDevfileYamlParseFailed diff --git a/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_params_validation.yml b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_params_validation.yml new file mode 100644 index 00000000000000..9f69577f5302cb --- /dev/null +++ b/ee/config/metrics/counts_all/count_total_failed_workspaces_created_by_failed_params_validation.yml @@ -0,0 +1,25 @@ +--- +key_path: counts.count_total_failed_workspaces_created_by_failed_params_validation +description: Count of Total Failed Workspaces Created By Failed Params Validation +product_group: remote_development +product_categories: +- workspaces +performance_indicator_type: [] +value_type: number +status: active +milestone: '18.0' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 +time_frame: +- 28d +- 7d +- all +data_source: internal_events +data_category: optional +tiers: +- premium +- ultimate +events: +- name: create_workspace_result + filter: + label: failed + property: WorkspaceCreateParamsValidationFailed diff --git a/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml index 74cff4daafd050..9bf8ba4997e226 100644 --- a/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml +++ b/ee/config/metrics/counts_all/count_total_succeed_workspaces_created.yml @@ -7,7 +7,7 @@ product_categories: performance_indicator_type: [] value_type: number status: active -milestone: '18.0' +milestone: '18.1' introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/188226 time_frame: - 28d -- GitLab From 8a5d963f2f3cc07c9b6c98d674c1076378423bf1 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Fri, 16 May 2025 16:39:56 +1000 Subject: [PATCH 10/16] Update with rop chain --- .../create/agent_validator.rb | 6 +- .../create/creation_event_tracker.rb | 29 +++++---- .../workspace_operations/create/creator.rb | 6 +- .../create/devfile_fetcher.rb | 14 +++-- .../create/devfile_flattener.rb | 2 +- .../create/devfile_validator.rb | 62 ++++++++++++------- .../workspace_operations/create/main.rb | 19 +++++- .../create/personal_access_token_creator.rb | 4 +- .../create/workspace_creator.rb | 2 +- .../create/workspace_variables_creator.rb | 2 +- .../create/creation_event_tracker_spec.rb | 34 +++++----- .../create/main_integration_spec.rb | 49 +++++++++++++-- .../workspace_operations/create/main_spec.rb | 2 - spec/support/matchers/invoke_rop_steps.rb | 2 +- 14 files changed, 162 insertions(+), 71 deletions(-) diff --git a/ee/lib/remote_development/workspace_operations/create/agent_validator.rb b/ee/lib/remote_development/workspace_operations/create/agent_validator.rb index c69e33b678638a..5c30fc7ee1ff02 100644 --- a/ee/lib/remote_development/workspace_operations/create/agent_validator.rb +++ b/ee/lib/remote_development/workspace_operations/create/agent_validator.rb @@ -34,7 +34,8 @@ def self.validate(context) details: "Cannot use agent '#{agent.name}' as an organization mapped agent, the provided agent " \ "is not mapped in organization '#{project.organization.name}'. It also cannot be used as a " \ "namespace mapped agent, it is not mapped to an ancestor namespace of the workspaces' project." - ) + ), + context: context ) end @@ -52,7 +53,8 @@ def self.validate(context) "namespace mapped agent, #{relevant_namespace_mappings.size} mapping(s) exist between the " \ "provided agent and the ancestor namespaces of the workspaces' project, but the agent does not " \ "reside within the hierarchy of any of the mapped ancestor namespaces." - ) + ), + context: context ) end diff --git a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb index 207aa04843b4af..55d8097bf7f8df 100644 --- a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb +++ b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb @@ -9,9 +9,8 @@ class CreationEventTracker extend Gitlab::Fp::MessageSupport # @param [Hash] context - # @param [Gitlab::Fp::Result] result # @return [void] - def self.track(context, result) + def self.track_ok(context) context => { user: user, params: { @@ -19,16 +18,24 @@ def self.track(context, result) } } - tracker = new + new.track_internal_event('create_workspace_result', project: project, user: user, + additional_properties: { label: "succeed" }) + end + + # @param [RemoteDevelopment::Message] message + # @return [void] + def self.track_err(message) + message.content => { + context: { + user: user, + params: { + project: project, + }, + } + } - case result - in { err: message } - tracker.track_internal_event('create_workspace_result', project: project, user: user, - additional_properties: { label: "failed", property: message.class.to_s.split('::').last }) - in { ok: message } - tracker.track_internal_event('create_workspace_result', project: project, user: user, - additional_properties: { label: "succeed" }) - end + new.track_internal_event('create_workspace_result', project: project, user: user, + additional_properties: { label: "failed", property: message.class.to_s.split('::').last }) end end end diff --git a/ee/lib/remote_development/workspace_operations/create/creator.rb b/ee/lib/remote_development/workspace_operations/create/creator.rb index a785bda6a1dcf3..8e30c2d43c10a5 100644 --- a/ee/lib/remote_development/workspace_operations/create/creator.rb +++ b/ee/lib/remote_development/workspace_operations/create/creator.rb @@ -36,9 +36,11 @@ def self.create(context) end end - return Gitlab::Fp::Result.err(WorkspaceCreateFailed.new({ errors: model_errors })) if model_errors.present? + if model_errors.present? + return Gitlab::Fp::Result.err(WorkspaceCreateFailed.new({ errors: model_errors, context: context })) + end - Gitlab::Fp::Result.ok(WorkspaceCreateSuccessful.new(updated_value)) + Gitlab::Fp::Result.ok(updated_value) end end end diff --git a/ee/lib/remote_development/workspace_operations/create/devfile_fetcher.rb b/ee/lib/remote_development/workspace_operations/create/devfile_fetcher.rb index f85ea15bddf40f..779441920a0c95 100644 --- a/ee/lib/remote_development/workspace_operations/create/devfile_fetcher.rb +++ b/ee/lib/remote_development/workspace_operations/create/devfile_fetcher.rb @@ -30,7 +30,8 @@ def self.validate_agent_config_exists(context) unless agent.unversioned_latest_workspaces_agent_config return Gitlab::Fp::Result.err(WorkspaceCreateParamsValidationFailed.new( - details: "No WorkspacesAgentConfig found for agent '#{agent.name}'" + details: "No WorkspacesAgentConfig found for agent '#{agent.name}'", + context: context )) end @@ -73,13 +74,17 @@ def self.read_devfile_yaml_from_repo_if_devfile_path_is_provided(context) unless devfile_blob return Gitlab::Fp::Result.err(WorkspaceCreateDevfileLoadFailed.new( details: "Devfile path '#{devfile_path}' at ref '#{project_ref}' " \ - "does not exist in the project repository" + "does not exist in the project repository", + context: context )) end unless devfile_blob.data.present? return Gitlab::Fp::Result.err( - WorkspaceCreateDevfileLoadFailed.new(details: "Devfile could not be loaded from project") + WorkspaceCreateDevfileLoadFailed.new( + details: "Devfile could not be loaded from project", + context: context + ) ) end @@ -102,7 +107,8 @@ def self.parse_devfile_yaml(context) devfile = devfile_to_json_and_back_to_yaml.to_h.deep_symbolize_keys rescue RuntimeError, JSON::GeneratorError => e return Gitlab::Fp::Result.err(WorkspaceCreateDevfileYamlParseFailed.new( - details: "Devfile YAML could not be parsed: #{e.message}" + details: "Devfile YAML could not be parsed: #{e.message}", + context: context )) end diff --git a/ee/lib/remote_development/workspace_operations/create/devfile_flattener.rb b/ee/lib/remote_development/workspace_operations/create/devfile_flattener.rb index fd967b7f909d82..9f64a8fb8ae4d0 100644 --- a/ee/lib/remote_development/workspace_operations/create/devfile_flattener.rb +++ b/ee/lib/remote_development/workspace_operations/create/devfile_flattener.rb @@ -15,7 +15,7 @@ def self.flatten(context) devfile_yaml = YAML.dump(devfile.deep_stringify_keys) flattened_devfile_yaml = Devfile::Parser.flatten(devfile_yaml) rescue Devfile::CliError => e - return Gitlab::Fp::Result.err(WorkspaceCreateDevfileFlattenFailed.new(details: e.message)) + return Gitlab::Fp::Result.err(WorkspaceCreateDevfileFlattenFailed.new(details: e.message, context: context)) end # NOTE: We do not attempt to rescue any exceptions from YAML.safe_load here because we assume that the diff --git a/ee/lib/remote_development/workspace_operations/create/devfile_validator.rb b/ee/lib/remote_development/workspace_operations/create/devfile_validator.rb index 31436c9d639888..ebdb8bd1166472 100644 --- a/ee/lib/remote_development/workspace_operations/create/devfile_validator.rb +++ b/ee/lib/remote_development/workspace_operations/create/devfile_validator.rb @@ -60,7 +60,8 @@ def self.validate_schema_version(context) devfile_schema_version = Gem::Version.new(devfile_schema_version_string) rescue ArgumentError return err( - format(_("Invalid 'schemaVersion' '%{schema_version}'"), schema_version: devfile_schema_version_string) + format(_("Invalid 'schemaVersion' '%{schema_version}'"), schema_version: devfile_schema_version_string), + context ) end @@ -71,7 +72,8 @@ def self.validate_schema_version(context) _("'schemaVersion' '%{given_version}' is not supported, it must be '%{required_version}'"), given_version: devfile_schema_version_string, required_version: REQUIRED_DEVFILE_SCHEMA_VERSION - ) + ), + context ) end @@ -83,7 +85,7 @@ def self.validate_schema_version(context) def self.validate_parent(context) devfile = devfile_to_validate(context) - return err(format(_("Inheriting from 'parent' is not yet supported"))) if devfile[:parent] + return err(format(_("Inheriting from 'parent' is not yet supported")), context) if devfile[:parent] Gitlab::Fp::Result.ok(context) end @@ -93,8 +95,8 @@ def self.validate_parent(context) def self.validate_projects(context) devfile = devfile_to_validate(context) - return err(_("'starterProjects' is not yet supported")) if devfile[:starterProjects] - return err(_("'projects' is not yet supported")) if devfile[:projects] + return err(_("'starterProjects' is not yet supported"), context) if devfile[:starterProjects] + return err(_("'projects' is not yet supported"), context) if devfile[:projects] Gitlab::Fp::Result.ok(context) end @@ -104,7 +106,7 @@ def self.validate_projects(context) def self.validate_root_attributes(context) devfile = devfile_to_validate(context) - return err(_("Attribute 'pod-overrides' is not yet supported")) if devfile.dig(:attributes, + return err(_("Attribute 'pod-overrides' is not yet supported"), context) if devfile.dig(:attributes, :"pod-overrides") Gitlab::Fp::Result.ok(context) @@ -117,7 +119,7 @@ def self.validate_components(context) components = devfile[:components] - return err(_("No components present in devfile")) if components.blank? + return err(_("No components present in devfile"), context) if components.blank? injected_main_components = components.select do |component| component.dig(:attributes, MAIN_COMPONENT_INDICATOR_ATTRIBUTE.to_sym) @@ -125,7 +127,8 @@ def self.validate_components(context) if injected_main_components.empty? return err( - format(_("No component has '%{attribute}' attribute"), attribute: MAIN_COMPONENT_INDICATOR_ATTRIBUTE) + format(_("No component has '%{attribute}' attribute"), attribute: MAIN_COMPONENT_INDICATOR_ATTRIBUTE), + context ) end @@ -135,12 +138,13 @@ def self.validate_components(context) _("Multiple components '%{name}' have '%{attribute}' attribute"), name: injected_main_components.pluck(:name), # rubocop:disable CodeReuse/ActiveRecord -- this pluck isn't from ActiveRecord, it's from ActiveSupport attribute: MAIN_COMPONENT_INDICATOR_ATTRIBUTE - ) + ), + context ) end components_all_have_names = components.all? { |component| component[:name].present? } - return err(_("Components must have a 'name'")) unless components_all_have_names + return err(_("Components must have a 'name'"), context) unless components_all_have_names components.each do |component| component_name = component.fetch(:name) @@ -150,19 +154,22 @@ def self.validate_components(context) _("Component name '%{component}' must not start with '%{prefix}'"), component: component_name, prefix: RESTRICTED_PREFIX - )) + ), context) end UNSUPPORTED_COMPONENT_TYPES.each do |unsupported_component_type| if component[unsupported_component_type] - return err(format(_("Component type '%{type}' is not yet supported"), type: unsupported_component_type)) + return err( + format(_("Component type '%{type}' is not yet supported"), type: unsupported_component_type), + context + ) end end - return err(_("Attribute 'container-overrides' is not yet supported")) if component.dig( + return err(_("Attribute 'container-overrides' is not yet supported"), context) if component.dig( :attributes, :"container-overrides") - return err(_("Attribute 'pod-overrides' is not yet supported")) if component.dig(:attributes, + return err(_("Attribute 'pod-overrides' is not yet supported"), context) if component.dig(:attributes, :"pod-overrides") end @@ -185,7 +192,8 @@ def self.validate_containers(context) format( _("Property 'dedicatedPod' of component '%{name}' is not yet supported"), name: component.fetch(:name) - ) + ), + context ) end end @@ -217,7 +225,8 @@ def self.validate_endpoints(context) endpoint: endpoint_name, component: component.fetch(:name), prefix: RESTRICTED_PREFIX - ) + ), + context ) end end @@ -241,7 +250,8 @@ def self.validate_commands(context) _("Command id '%{command}' must not start with '%{prefix}'"), command: command_id, prefix: RESTRICTED_PREFIX - ) + ), + context ) end @@ -259,7 +269,8 @@ def self.validate_commands(context) component: component_name, command: command_id, prefix: RESTRICTED_PREFIX - ) + ), + context ) end end @@ -278,7 +289,7 @@ def self.validate_events(context) if SUPPORTED_EVENTS.exclude?(event_type) && event_type_events.present? err_msg = format(_("Event type '%{type}' is not yet supported"), type: event_type) # The entries for unsupported events may be defined, but they must be blank. - return err(err_msg) + return err(err_msg, context) end # Ensure no event starts with restricted_prefix @@ -291,7 +302,8 @@ def self.validate_events(context) event: event, event_type: event_type, prefix: RESTRICTED_PREFIX - ) + ), + context ) end end @@ -316,7 +328,8 @@ def self.validate_variables(context) _("Variable name '%{variable}' must not start with '%{prefix}'"), variable: variable, prefix: prefix - ) + ), + context ) end end @@ -325,9 +338,12 @@ def self.validate_variables(context) end # @param [String] details + # @param [Hash] context # @return [Gitlab::Fp::Result] - def self.err(details) - Gitlab::Fp::Result.err(WorkspaceCreateDevfileValidationFailed.new({ details: details })) + def self.err(details, context) + Gitlab::Fp::Result.err(WorkspaceCreateDevfileValidationFailed.new( + { details: details, context: context } + )) end private_class_method :devfile_to_validate, :validate_schema_version, :validate_parent, :validate_projects, :validate_components, :validate_containers, diff --git a/ee/lib/remote_development/workspace_operations/create/main.rb b/ee/lib/remote_development/workspace_operations/create/main.rb index 7c9cf5762c90ca..aba8bf901e67e4 100644 --- a/ee/lib/remote_development/workspace_operations/create/main.rb +++ b/ee/lib/remote_development/workspace_operations/create/main.rb @@ -28,8 +28,23 @@ def self.main(context) .map(ProjectClonerComponentInserter.method(:insert)) .map(VolumeComponentInserter.method(:insert)) .and_then(Creator.method(:create)) - - CreationEventTracker.track(context, result) + .inspect_ok( + ->(context) do + CreationEventTracker.track_ok(context) + nil + end + ) + .inspect_err( + ->(message) do + CreationEventTracker.track_err(message) + nil + end + ) + .map( + ->(context) do + WorkspaceCreateSuccessful.new(context) + end + ) # rubocop:disable Lint/DuplicateBranch -- Rubocop doesn't know the branches are different due to destructuring case result diff --git a/ee/lib/remote_development/workspace_operations/create/personal_access_token_creator.rb b/ee/lib/remote_development/workspace_operations/create/personal_access_token_creator.rb index 592eb7c9ae85e3..dee4c18d98bd7a 100644 --- a/ee/lib/remote_development/workspace_operations/create/personal_access_token_creator.rb +++ b/ee/lib/remote_development/workspace_operations/create/personal_access_token_creator.rb @@ -30,7 +30,9 @@ def self.create(context) if personal_access_token.errors.present? return Gitlab::Fp::Result.err( - PersonalAccessTokenModelCreateFailed.new({ errors: personal_access_token.errors }) + PersonalAccessTokenModelCreateFailed.new( + { errors: personal_access_token.errors, context: context } + ) ) end diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb b/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb index 17beea2a8de5e0..3120b89058a172 100644 --- a/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb +++ b/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb @@ -61,7 +61,7 @@ def self.create(context) if workspace.errors.present? return Gitlab::Fp::Result.err( - WorkspaceModelCreateFailed.new({ errors: workspace.errors }) + WorkspaceModelCreateFailed.new({ errors: workspace.errors, context: context }) ) end diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb b/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb index d3580e28d514fb..bf2069fe6c706e 100644 --- a/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb +++ b/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb @@ -39,7 +39,7 @@ def self.create(context) if workspace_variable.errors.present? return Gitlab::Fp::Result.err( - WorkspaceVariablesModelCreateFailed.new({ errors: workspace_variable.errors }) + WorkspaceVariablesModelCreateFailed.new({ errors: workspace_variable.errors, context: context }) ) end end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb index fb883dec2999db..00e61f406dea6c 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb @@ -6,7 +6,6 @@ let_it_be(:user) { create(:user) } let_it_be(:namespace) { create(:group, developers: user) } let_it_be(:project) { create(:project, namespace: namespace) } - let(:subject_track) { described_class.track(context, result) } let(:context) do { user: user, @@ -16,28 +15,31 @@ } end - context 'when result contains an error' do - let(:error_message) { StandardError.new('Something went wrong') } - let(:result) { { err: error_message } } + let(:message) do + RemoteDevelopment::Messages::WorkspaceCreateDevfileLoadFailed.new( + details: "Devfile could not be loaded from project", + context: context + ) + end - it 'tracks a failed event with the error class' do - expect { subject_track } + describe '.track_ok' do + it 'tracks a succeeded event and increases succeed metric' do + expect { described_class.track_ok(context) } .to trigger_internal_events('create_workspace_result') - .with(user: user, project: project, additional_properties: { label: 'failed', - property: error_message.class.to_s }) - .and increment_usage_metrics('counts.count_total_failed_workspaces_created') + .with(user: user, project: project, additional_properties: { label: 'succeed' }) + .and increment_usage_metrics('counts.count_total_succeed_workspaces_created') end end - context 'when result contains a success' do - let(:success_message) { 'Success' } - let(:result) { { ok: success_message } } + describe '.track_err' do + let(:err_message) { message.class.to_s.split('::').last } - it 'tracks a succeeded event' do - expect { subject_track } + it 'tracks a failed event with the error class and increases failure metric' do + expect { described_class.track_err(message) } .to trigger_internal_events('create_workspace_result') - .with(user: user, project: project, additional_properties: { label: 'succeed' }) - .and increment_usage_metrics('counts.count_total_succeed_workspaces_created') + .with(user: user, project: project, additional_properties: { label: 'failed', + property: err_message }) + .and increment_usage_metrics('counts.count_total_failed_workspaces_created') end end end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb index 17d2fe0e21cf32..ebc83598df97ad 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb @@ -2,6 +2,35 @@ require 'spec_helper' +RSpec.shared_examples 'tracks successful workspace creation event' do + it "tracks creation event" do + expect { response } + .to trigger_internal_events('create_workspace_result') + .with( + category: 'RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker', + user: user, + project: project, + additional_properties: { label: 'succeed' } + ) + end +end + +RSpec.shared_examples 'tracks failed workspace creation event' do |error_message| + it "tracks failed creation event with proper error details" do + expect { response } + .to trigger_internal_events('create_workspace_result') + .with( + category: 'RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker', + user: user, + project: project, + additional_properties: { + label: 'failed', + property: error_message + } + ) + end +end + # NOTE: This spec cannot use let_it_be because, because that doesn't work when using the `custom_repo` trait of # the project factory and subsequently modifying it, because it's a real on-disk repo at `tmp/tests/gitlab-test/`, # and any changes made to it are not reverted by let it be (even with reload). This means we also cannot use @@ -93,10 +122,6 @@ described_class.main(context) end - before do - allow(::RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker).to receive(:track) - end - context 'when params are valid' do before do allow(project.repository).to receive_message_chain(:blob_at_branch, :data) { devfile_yaml } @@ -157,6 +182,8 @@ end end + it_behaves_like 'tracks successful workspace creation event' + context 'with versioned workspaces_agent_configs behavior' do before do agent.unversioned_latest_workspaces_agent_config.touch @@ -168,6 +195,8 @@ workspace = response.fetch(:payload).fetch(:workspace) expect(workspace.workspaces_agent_config_version).to eq(expected_workspaces_agent_config_version) end + + it_behaves_like 'tracks successful workspace creation event' end context "with shared namespace" do @@ -183,6 +212,8 @@ workspace = response.fetch(:payload).fetch(:workspace) expect(workspace.namespace).to eq("default") end + + it_behaves_like 'tracks successful workspace creation event' end end @@ -194,6 +225,8 @@ expect(workspace.devfile).to eq(default_devfile_yaml) end + + it_behaves_like 'tracks successful workspace creation event' end context 'when devfile is not valid', :aggregate_failures do @@ -208,6 +241,8 @@ reason: :bad_request }) end + + it_behaves_like 'tracks failed workspace creation event', 'WorkspaceCreateDevfileValidationFailed' end end @@ -229,6 +264,8 @@ reason: :bad_request }) end + + it_behaves_like 'tracks failed workspace creation event', 'WorkspaceCreateDevfileLoadFailed' end context 'when agent has no associated config' do @@ -255,6 +292,8 @@ reason: :bad_request }) end + + it_behaves_like 'tracks failed workspace creation event', 'WorkspaceCreateParamsValidationFailed' end end @@ -273,5 +312,7 @@ .dig(:container, :image) expect(image_from_processed_devfile).to eq(tools_injector_image_from_settings) end + + it_behaves_like 'tracks successful workspace creation event' end end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb index de6427388e92f5..265def41ebf98c 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb @@ -31,7 +31,6 @@ end it "returns expected response" do - expect(::RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker).to receive(:track).once # noinspection RubyResolve - https://handbook.gitlab.com/handbook/tools-and-tips/editors-and-ides/jetbrains-ides/tracked-jetbrains-issues/#ruby-31542 expect do described_class.main(context_passed_along_steps) @@ -55,7 +54,6 @@ shared_examples "rop invocation with error response" do it "returns expected response" do - expect(::RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker).to receive(:track).once # noinspection RubyResolve - https://handbook.gitlab.com/handbook/tools-and-tips/editors-and-ides/jetbrains-ides/tracked-jetbrains-issues/#ruby-31542 expect do described_class.main(context_passed_along_steps) diff --git a/spec/support/matchers/invoke_rop_steps.rb b/spec/support/matchers/invoke_rop_steps.rb index d8b9830dd472f7..1de72c3ba9038e 100644 --- a/spec/support/matchers/invoke_rop_steps.rb +++ b/spec/support/matchers/invoke_rop_steps.rb @@ -53,7 +53,7 @@ def validate_rop_steps(rop_steps) end step_class = expected_rop_step[0] - unless step_class.is_a?(Class) + unless step_class.is_a?(Class) || step_class.is_a?(Proc) raise "'invoke_rop_steps' argument array entry first element '#{step_class}' must be a Class " \ "representing a step class, but was a #{step_class.class}" end -- GitLab From 44188bafa78ccb02f0a8c31d995f42c41835d44b Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Fri, 16 May 2025 17:49:27 +1000 Subject: [PATCH 11/16] Fix some rspecs --- .../workspace_operations/create/agent_validator.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ee/lib/remote_development/workspace_operations/create/agent_validator.rb b/ee/lib/remote_development/workspace_operations/create/agent_validator.rb index 5c30fc7ee1ff02..5f3bb5f846a2ef 100644 --- a/ee/lib/remote_development/workspace_operations/create/agent_validator.rb +++ b/ee/lib/remote_development/workspace_operations/create/agent_validator.rb @@ -33,9 +33,9 @@ def self.validate(context) WorkspaceCreateParamsValidationFailed.new( details: "Cannot use agent '#{agent.name}' as an organization mapped agent, the provided agent " \ "is not mapped in organization '#{project.organization.name}'. It also cannot be used as a " \ - "namespace mapped agent, it is not mapped to an ancestor namespace of the workspaces' project." - ), - context: context + "namespace mapped agent, it is not mapped to an ancestor namespace of the workspaces' project.", + context: context + ) ) end @@ -52,9 +52,9 @@ def self.validate(context) "is not mapped in organization '#{project.organization.name}'. It also cannot be used as a " \ "namespace mapped agent, #{relevant_namespace_mappings.size} mapping(s) exist between the " \ "provided agent and the ancestor namespaces of the workspaces' project, but the agent does not " \ - "reside within the hierarchy of any of the mapped ancestor namespaces." - ), - context: context + "reside within the hierarchy of any of the mapped ancestor namespaces.", + context: context + ) ) end -- GitLab From 9d469127b9bc37f02b236005bf1e30d756237616 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Mon, 19 May 2025 09:59:57 +1000 Subject: [PATCH 12/16] Address comment --- .../remote_development/common_service.rb | 4 +- .../workspace_operations/create/main.rb | 20 ++-------- .../create/workspace_errors_observer.rb | 31 ++++++++++++++++ .../create/workspace_observer.rb | 29 +++++++++++++++ .../workspace_successful_response_builder.rb | 13 +++++++ .../workspace_operations/create/main_spec.rb | 10 ++++- .../create/workspace_errors_observer_spec.rb | 37 +++++++++++++++++++ .../create/workspace_observer_spec.rb | 27 ++++++++++++++ spec/support/matchers/invoke_rop_steps.rb | 13 ++++--- 9 files changed, 158 insertions(+), 26 deletions(-) create mode 100644 ee/lib/remote_development/workspace_operations/create/workspace_errors_observer.rb create mode 100644 ee/lib/remote_development/workspace_operations/create/workspace_observer.rb create mode 100644 ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb create mode 100644 ee/spec/lib/remote_development/workspace_operations/create/workspace_errors_observer_spec.rb create mode 100644 ee/spec/lib/remote_development/workspace_operations/create/workspace_observer_spec.rb diff --git a/ee/app/services/remote_development/common_service.rb b/ee/app/services/remote_development/common_service.rb index d3450ee57193ae..7e5bfc8848c34c 100644 --- a/ee/app/services/remote_development/common_service.rb +++ b/ee/app/services/remote_development/common_service.rb @@ -6,6 +6,7 @@ module RemoteDevelopment class CommonService extend Gitlab::Fp::RopHelpers extend ServiceResponseFactory + include Gitlab::InternalEvents # NOTE: This service intentionally does not follow the conventions for object-based service classes as documented in # https://docs.gitlab.com/ee/development/reusing_abstractions.html#service-classes. @@ -28,9 +29,10 @@ def self.execute(domain_main_class:, domain_main_class_args:) settings = RemoteDevelopment::Settings.get(RemoteDevelopment::Settings::DefaultSettings.default_settings.keys) logger = RemoteDevelopment::Logger.build + internal_events_class = Gitlab::InternalEvents response_hash = domain_main_class.singleton_method(main_class_method).call( - **domain_main_class_args.merge(settings: settings, logger: logger) + **domain_main_class_args.merge(settings: settings, logger: logger, internal_events_class: internal_events_class) ) create_service_response(response_hash) diff --git a/ee/lib/remote_development/workspace_operations/create/main.rb b/ee/lib/remote_development/workspace_operations/create/main.rb index aba8bf901e67e4..2710ee4d29759f 100644 --- a/ee/lib/remote_development/workspace_operations/create/main.rb +++ b/ee/lib/remote_development/workspace_operations/create/main.rb @@ -28,23 +28,9 @@ def self.main(context) .map(ProjectClonerComponentInserter.method(:insert)) .map(VolumeComponentInserter.method(:insert)) .and_then(Creator.method(:create)) - .inspect_ok( - ->(context) do - CreationEventTracker.track_ok(context) - nil - end - ) - .inspect_err( - ->(message) do - CreationEventTracker.track_err(message) - nil - end - ) - .map( - ->(context) do - WorkspaceCreateSuccessful.new(context) - end - ) + .inspect_ok(WorkspaceObserver.method(:observe)) + .inspect_err(WorkspaceErrorsObserver.method(:observe)) + .and_then(WorkspaceSuccessfulResponseBuilder.method(:build)) # rubocop:disable Lint/DuplicateBranch -- Rubocop doesn't know the branches are different due to destructuring case result diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_errors_observer.rb b/ee/lib/remote_development/workspace_operations/create/workspace_errors_observer.rb new file mode 100644 index 00000000000000..bcc58bce92837e --- /dev/null +++ b/ee/lib/remote_development/workspace_operations/create/workspace_errors_observer.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module RemoteDevelopment + module WorkspaceOperations + module Create + class WorkspaceErrorsObserver + include Messages + extend Gitlab::Fp::MessageSupport + + # @param [RemoteDevelopment::Message] message + # @return [void] + def self.observe(message) + message.content => { + context: { + user: user, + internal_events_class: internal_events_class, + params: { + project: project, + }, + } + } + + internal_events_class.track_event('create_workspace_result', category: name, project: project, user: user, + additional_properties: { label: "failed", property: message.class.name.demodulize }) + + nil + end + end + end + end +end diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_observer.rb b/ee/lib/remote_development/workspace_operations/create/workspace_observer.rb new file mode 100644 index 00000000000000..01ac2a770ef80a --- /dev/null +++ b/ee/lib/remote_development/workspace_operations/create/workspace_observer.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module RemoteDevelopment + module WorkspaceOperations + module Create + class WorkspaceObserver + include Messages + extend Gitlab::Fp::MessageSupport + + # @param [Hash] context + # @return [void] + def self.observe(context) + context => { + user: user, + internal_events_class: internal_events_class, + params: { + project: project, + } + } + + internal_events_class.track_event('create_workspace_result', category: name, project: project, user: user, + additional_properties: { label: "succeed" }) + + nil + end + end + end + end +end diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb b/ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb new file mode 100644 index 00000000000000..5b15d44e54fd2b --- /dev/null +++ b/ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module RemoteDevelopment + module WorkspaceOperations + module Create + class WorkspaceSuccessfulResponseBuilder + def self.build(context) + Gitlab::Fp::Result.ok(WorkspaceCreateSuccessful.new(context)) + end + end + end + end +end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb index 265def41ebf98c..8155c33ba55474 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/main_spec.rb @@ -16,12 +16,16 @@ [RemoteDevelopment::WorkspaceOperations::Create::MainComponentUpdater, :map], [RemoteDevelopment::WorkspaceOperations::Create::ProjectClonerComponentInserter, :map], [RemoteDevelopment::WorkspaceOperations::Create::VolumeComponentInserter, :map], - [RemoteDevelopment::WorkspaceOperations::Create::Creator, :and_then] + [RemoteDevelopment::WorkspaceOperations::Create::Creator, :and_then], + [observer_class, observer_method], + [RemoteDevelopment::WorkspaceOperations::Create::WorkspaceSuccessfulResponseBuilder, :and_then] ] end describe "happy path" do let(:ok_message_content) { { ok_details: "Everything is OK!" } } + let(:observer_class) { RemoteDevelopment::WorkspaceOperations::Create::WorkspaceObserver } + let(:observer_method) { :inspect_ok } let(:expected_response) do { @@ -40,7 +44,7 @@ .with_context_passed_along_steps(context_passed_along_steps) .with_ok_result_for_step( { - step_class: RemoteDevelopment::WorkspaceOperations::Create::Creator, + step_class: RemoteDevelopment::WorkspaceOperations::Create::WorkspaceSuccessfulResponseBuilder, returned_message: RemoteDevelopment::Messages::WorkspaceCreateSuccessful.new(ok_message_content) } ) @@ -51,6 +55,8 @@ describe "error cases" do let(:error_details) { "some error details" } let(:err_message_content) { { details: error_details } } + let(:observer_class) { RemoteDevelopment::WorkspaceOperations::Create::WorkspaceErrorsObserver } + let(:observer_method) { :inspect_err } shared_examples "rop invocation with error response" do it "returns expected response" do diff --git a/ee/spec/lib/remote_development/workspace_operations/create/workspace_errors_observer_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/workspace_errors_observer_spec.rb new file mode 100644 index 00000000000000..77bad4424c7c03 --- /dev/null +++ b/ee/spec/lib/remote_development/workspace_operations/create/workspace_errors_observer_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RemoteDevelopment::WorkspaceOperations::Create::WorkspaceErrorsObserver, feature_category: :workspaces do + let_it_be(:user) { create(:user) } + let_it_be(:namespace) { create(:group, developers: user) } + let_it_be(:project) { create(:project, namespace: namespace) } + let(:context) do + { + user: user, + internal_events_class: Gitlab::InternalEvents, + params: { + project: project + } + } + end + + let(:message) do + RemoteDevelopment::Messages::WorkspaceCreateDevfileLoadFailed.new( + details: "Devfile could not be loaded from project", + context: context + ) + end + + describe '.observe' do + let(:err_message) { message.class.name.demodulize } + + it 'tracks a failed event with the error class and increases failure metric' do + expect { described_class.observe(message) } + .to trigger_internal_events('create_workspace_result') + .with(user: user, project: project, additional_properties: { label: 'failed', + property: err_message }) + .and increment_usage_metrics('counts.count_total_failed_workspaces_created') + end + end +end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/workspace_observer_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/workspace_observer_spec.rb new file mode 100644 index 00000000000000..fa664fa8788244 --- /dev/null +++ b/ee/spec/lib/remote_development/workspace_operations/create/workspace_observer_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RemoteDevelopment::WorkspaceOperations::Create::WorkspaceObserver, feature_category: :workspaces do + let_it_be(:user) { create(:user) } + let_it_be(:namespace) { create(:group, developers: user) } + let_it_be(:project) { create(:project, namespace: namespace) } + let(:context) do + { + user: user, + internal_events_class: Gitlab::InternalEvents, + params: { + project: project + } + } + end + + describe '.observe' do + it 'tracks a succeeded event and increases succeed metric' do + expect { described_class.observe(context) } + .to trigger_internal_events('create_workspace_result') + .with(user: user, project: project, additional_properties: { label: 'succeed' }) + .and increment_usage_metrics('counts.count_total_succeed_workspaces_created') + end + end +end diff --git a/spec/support/matchers/invoke_rop_steps.rb b/spec/support/matchers/invoke_rop_steps.rb index 1de72c3ba9038e..fb03bc62e19ace 100644 --- a/spec/support/matchers/invoke_rop_steps.rb +++ b/spec/support/matchers/invoke_rop_steps.rb @@ -111,6 +111,7 @@ def build_expected_rop_steps( context_passed_along_steps: ) expected_rop_steps = [] + skip_unless_inspect = false rop_steps.each do |rop_step| step_class = rop_step[0] @@ -121,13 +122,11 @@ def build_expected_rop_steps( step_action: step_action } + next if skip_unless_inspect && [:inspect_ok, :inspect_err].freeze.exclude?(step_action) + if err_results_for_steps.key?(step_class) expected_rop_step[:returned_object] = err_results_for_steps[step_class] - - # Currently, only a single error step is supported, so we assign expected_rop_step as the last entry - # in expected_rop_steps, break out of the loop early, and do not add any more steps - expected_rop_steps << expected_rop_step - break + skip_unless_inspect = true elsif ok_results_for_steps.key?(step_class) expected_rop_step[:returned_object] = ok_results_for_steps[step_class] elsif step_action == :and_then @@ -169,7 +168,9 @@ def set_up_step_class_expectation( context_passed_along_steps:, returned_object: ) - expect(step_class).to receive(step_class_method).with(context_passed_along_steps).ordered do + expectation = expect(step_class).to receive(step_class_method) + expectation = expectation.with(context_passed_along_steps) if step_class_method != :observe + expectation.ordered do returned_object end end -- GitLab From 2a7f7b2493b0f9164868d95dd3cd1d3fe8355eb0 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Mon, 19 May 2025 10:00:49 +1000 Subject: [PATCH 13/16] Remove unused files --- .../create/creation_event_tracker.rb | 43 ------------------ .../create/creation_event_tracker_spec.rb | 45 ------------------- spec/support/matchers/invoke_rop_steps.rb | 2 +- 3 files changed, 1 insertion(+), 89 deletions(-) delete mode 100644 ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb delete mode 100644 ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb diff --git a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb b/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb deleted file mode 100644 index 55d8097bf7f8df..00000000000000 --- a/ee/lib/remote_development/workspace_operations/create/creation_event_tracker.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module RemoteDevelopment - module WorkspaceOperations - module Create - class CreationEventTracker - include Gitlab::InternalEventsTracking - include Messages - extend Gitlab::Fp::MessageSupport - - # @param [Hash] context - # @return [void] - def self.track_ok(context) - context => { - user: user, - params: { - project: project, - } - } - - new.track_internal_event('create_workspace_result', project: project, user: user, - additional_properties: { label: "succeed" }) - end - - # @param [RemoteDevelopment::Message] message - # @return [void] - def self.track_err(message) - message.content => { - context: { - user: user, - params: { - project: project, - }, - } - } - - new.track_internal_event('create_workspace_result', project: project, user: user, - additional_properties: { label: "failed", property: message.class.to_s.split('::').last }) - end - end - end - end -end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb deleted file mode 100644 index 00e61f406dea6c..00000000000000 --- a/ee/spec/lib/remote_development/workspace_operations/create/creation_event_tracker_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker, feature_category: :workspaces do - let_it_be(:user) { create(:user) } - let_it_be(:namespace) { create(:group, developers: user) } - let_it_be(:project) { create(:project, namespace: namespace) } - let(:context) do - { - user: user, - params: { - project: project - } - } - end - - let(:message) do - RemoteDevelopment::Messages::WorkspaceCreateDevfileLoadFailed.new( - details: "Devfile could not be loaded from project", - context: context - ) - end - - describe '.track_ok' do - it 'tracks a succeeded event and increases succeed metric' do - expect { described_class.track_ok(context) } - .to trigger_internal_events('create_workspace_result') - .with(user: user, project: project, additional_properties: { label: 'succeed' }) - .and increment_usage_metrics('counts.count_total_succeed_workspaces_created') - end - end - - describe '.track_err' do - let(:err_message) { message.class.to_s.split('::').last } - - it 'tracks a failed event with the error class and increases failure metric' do - expect { described_class.track_err(message) } - .to trigger_internal_events('create_workspace_result') - .with(user: user, project: project, additional_properties: { label: 'failed', - property: err_message }) - .and increment_usage_metrics('counts.count_total_failed_workspaces_created') - end - end -end diff --git a/spec/support/matchers/invoke_rop_steps.rb b/spec/support/matchers/invoke_rop_steps.rb index fb03bc62e19ace..1270abe59f1b72 100644 --- a/spec/support/matchers/invoke_rop_steps.rb +++ b/spec/support/matchers/invoke_rop_steps.rb @@ -53,7 +53,7 @@ def validate_rop_steps(rop_steps) end step_class = expected_rop_step[0] - unless step_class.is_a?(Class) || step_class.is_a?(Proc) + unless step_class.is_a?(Class) raise "'invoke_rop_steps' argument array entry first element '#{step_class}' must be a Class " \ "representing a step class, but was a #{step_class.class}" end -- GitLab From bacb8777c4a718d7b57aea69c8085525f3284f38 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Mon, 19 May 2025 10:58:11 +1000 Subject: [PATCH 14/16] Fix Rspec --- .../create/workspace_successful_response_builder.rb | 2 ++ .../workspace_operations/create/main_integration_spec.rb | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb b/ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb index 5b15d44e54fd2b..221e57f2a59b28 100644 --- a/ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb +++ b/ee/lib/remote_development/workspace_operations/create/workspace_successful_response_builder.rb @@ -4,6 +4,8 @@ module RemoteDevelopment module WorkspaceOperations module Create class WorkspaceSuccessfulResponseBuilder + include Messages + def self.build(context) Gitlab::Fp::Result.ok(WorkspaceCreateSuccessful.new(context)) end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb index ebc83598df97ad..11a32c45f44dae 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb @@ -7,7 +7,7 @@ expect { response } .to trigger_internal_events('create_workspace_result') .with( - category: 'RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker', + category: 'RemoteDevelopment::WorkspaceOperations::Create::WorkspaceObserver', user: user, project: project, additional_properties: { label: 'succeed' } @@ -20,7 +20,7 @@ expect { response } .to trigger_internal_events('create_workspace_result') .with( - category: 'RemoteDevelopment::WorkspaceOperations::Create::CreationEventTracker', + category: 'RemoteDevelopment::WorkspaceOperations::Create::WorkspaceErrorsObserver', user: user, project: project, additional_properties: { @@ -112,6 +112,7 @@ { user: user, params: params, + internal_events_class: Gitlab::InternalEvents, settings: settings, vscode_extension_marketplace: vscode_extension_marketplace, vscode_extension_marketplace_metadata: { enabled: vscode_extension_marketplace_metadata_enabled } -- GitLab From 0f2582ed9cd8c30e2d082eaddc12c8975514233e Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Mon, 19 May 2025 15:58:25 +1000 Subject: [PATCH 15/16] Fix Rspec --- .../workspace_operations/create/creator_spec.rb | 4 ++-- .../workspace_operations/create/devfile_flattener_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ee/spec/lib/remote_development/workspace_operations/create/creator_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/creator_spec.rb index 5faefba1d3a64b..10013abd19819e 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/creator_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/creator_spec.rb @@ -18,7 +18,7 @@ describe "happy path" do let(:expected_value) do - Gitlab::Fp::Result.ok(Messages::WorkspaceCreateSuccessful.new(context_passed_along_steps)) + Gitlab::Fp::Result.ok(context_passed_along_steps) end it "returns expected response" do @@ -35,7 +35,7 @@ describe "error cases" do let(:error_details) { "some error details" } - let(:err_message_content) { { errors: error_details } } + let(:err_message_content) { { errors: error_details, context: context_passed_along_steps } } shared_examples "rop invocation with error response" do it "returns expected response" do diff --git a/ee/spec/lib/remote_development/workspace_operations/create/devfile_flattener_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/devfile_flattener_spec.rb index a71ee92641fae5..3b33ea1dc70a81 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/devfile_flattener_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/devfile_flattener_spec.rb @@ -52,7 +52,7 @@ "- (root): Additional property random is not allowed\n" message = result.unwrap_err expect(message).to be_a(RemoteDevelopment::Messages::WorkspaceCreateDevfileFlattenFailed) - expect(message.content).to eq(details: expected_error_message) + expect(message.content).to eq({ details: expected_error_message, context: context }) end end end -- GitLab From 6f71a4683d4090367a1445b9000b9c29ad5953b3 Mon Sep 17 00:00:00 2001 From: Zhaochen Li Date: Wed, 21 May 2025 09:17:59 +1000 Subject: [PATCH 16/16] Fix Rspec --- .../workspace_operations/reconcile/main_spec.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ee/spec/lib/remote_development/workspace_operations/reconcile/main_spec.rb b/ee/spec/lib/remote_development/workspace_operations/reconcile/main_spec.rb index 532a67f982920f..3c9a5f640aeaf2 100644 --- a/ee/spec/lib/remote_development/workspace_operations/reconcile/main_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/reconcile/main_spec.rb @@ -56,7 +56,19 @@ describe "error cases" do let(:error_details) { "some error details" } - let(:err_message_content) { { details: error_details } } + let(:err_message_content) { { details: error_details, context: context_passed_along_steps } } + let(:rop_steps) do + [ + [RemoteDevelopment::WorkspaceOperations::Reconcile::Input::ParamsValidator, :and_then], + [RemoteDevelopment::WorkspaceOperations::Reconcile::Input::ParamsExtractor, :map], + [RemoteDevelopment::WorkspaceOperations::Reconcile::Input::ParamsToInfosConverter, :map], + [RemoteDevelopment::WorkspaceOperations::Reconcile::Persistence::WorkspacesFromAgentInfosUpdater, :map], + [RemoteDevelopment::WorkspaceOperations::Reconcile::Persistence::WorkspacesLifecycleManager, :map], + [RemoteDevelopment::WorkspaceOperations::Reconcile::Persistence::WorkspacesToBeReturnedFinder, :map], + [RemoteDevelopment::WorkspaceOperations::Reconcile::Output::ResponsePayloadBuilder, :map], + [RemoteDevelopment::WorkspaceOperations::Reconcile::Persistence::WorkspacesToBeReturnedUpdater, :map] + ] + end shared_examples "rop invocation with error response" do it "returns expected response" do -- GitLab