# HG changeset patch
# User Balasankar 'Balu' C <balasankar@gitlab.com>
# Date 1729609996 0
#      Tue Oct 22 15:13:16 2024 +0000
# Node ID fc398646988475b44317cd2773f675b169161f3a
# Parent  0439e0b748681a20210143154a6be2b2227334d8
Add Gitaly role

Changelog: added
Signed-off-by: Balasankar "Balu" C <balasankar@gitlab.com>

diff --git a/doc/roles/index.md b/doc/roles/index.md
--- a/doc/roles/index.md
+++ b/doc/roles/index.md
@@ -188,3 +188,14 @@
   Configures the server with spamcheck and spam-classifier services.
 
   *By default, enables no other services.*
+
+### Gitaly Roles
+
+Gitaly roles are used to set up and configure Gitaly services. For additional
+information, see the [Gitaly documentation](https://docs.gitlab.com/ee/administration/gitaly/)
+
+- **gitaly_role** (`gitlab-ce`/`gitlab-ee`)
+
+  Configures the server with Gitaly service.
+
+  *By default, enables no other services.*
diff --git a/files/gitlab-config-template/gitlab.rb.template b/files/gitlab-config-template/gitlab.rb.template
--- a/files/gitlab-config-template/gitlab.rb.template
+++ b/files/gitlab-config-template/gitlab.rb.template
@@ -34,8 +34,21 @@
 ## Roles for multi-instance GitLab
 ##! The default is to have no roles enabled, which results in GitLab running as an all-in-one instance.
 ##! Options:
-##!   redis_sentinel_role redis_master_role redis_replica_role geo_primary_role geo_secondary_role
-##!   postgres_role consul_role application_role monitoring_role
+##!   application_role
+##!   redis_sentinel_role
+##!   redis_master_role
+##!   redis_replica_role
+##!   monitoring_role
+##!   geo_primary_role
+##!   geo_secondary_role
+##!   postgres_role
+##!   patroni_role
+##!   consul_role
+##!   pgbouncer_role
+##!   pages_role
+##!   sidekiq_role
+##!   spamcheck_role
+##!   gitaly_role
 ##! For more details on each role, see:
 ##! https://docs.gitlab.com/omnibus/roles/index.html#roles
 ##!
diff --git a/files/gitlab-cookbooks/package/libraries/config/gitlab.rb b/files/gitlab-cookbooks/package/libraries/config/gitlab.rb
--- a/files/gitlab-cookbooks/package/libraries/config/gitlab.rb
+++ b/files/gitlab-cookbooks/package/libraries/config/gitlab.rb
@@ -44,6 +44,7 @@
   role('consul').use { ConsulRole }
   role('pages').use { PagesRole }
   role('spamcheck').use { SpamcheckRole }
+  role('gitaly').use { GitalyRole }
 
   ## Attributes directly on the node
   attribute('package').use { Package }
diff --git a/files/gitlab-cookbooks/package/libraries/config/roles/gitaly.rb b/files/gitlab-cookbooks/package/libraries/config/roles/gitaly.rb
new file mode 100644
--- /dev/null
+++ b/files/gitlab-cookbooks/package/libraries/config/roles/gitaly.rb
@@ -0,0 +1,10 @@
+module GitalyRole
+  def self.load_role
+    return unless Gitlab['gitaly_role']['enable']
+
+    # Turning off GitLab Rails unless explicitly enabled.
+    Gitlab['gitlab_rails']['enable'] ||= false
+
+    Services.enable_group('gitaly_role')
+  end
+end
diff --git a/files/gitlab-cookbooks/package/libraries/config/services.rb b/files/gitlab-cookbooks/package/libraries/config/services.rb
--- a/files/gitlab-cookbooks/package/libraries/config/services.rb
+++ b/files/gitlab-cookbooks/package/libraries/config/services.rb
@@ -26,7 +26,7 @@
     service 'sidekiq',            groups: [DEFAULT_GROUP, 'rails', 'sidekiq', 'sidekiq_role']
     service 'gitlab_exporter',    groups: [DEFAULT_GROUP, 'rails', 'monitoring']
     service 'gitlab_workhorse',   groups: [DEFAULT_GROUP, 'rails']
-    service 'gitaly',             groups: [DEFAULT_GROUP, 'rails']
+    service 'gitaly',             groups: [DEFAULT_GROUP, 'rails', 'gitaly_role']
     service 'redis',              groups: [DEFAULT_GROUP, 'redis', 'redis_node']
     service 'redis_exporter',     groups: [DEFAULT_GROUP, 'redis', 'redis_node', 'monitoring']
     service 'postgresql',         groups: [DEFAULT_GROUP, 'postgres', 'postgres_role', 'patroni_role']
diff --git a/spec/chef/cookbooks/package/libraries/config/roles_spec.rb b/spec/chef/cookbooks/package/libraries/config/roles_spec.rb
--- a/spec/chef/cookbooks/package/libraries/config/roles_spec.rb
+++ b/spec/chef/cookbooks/package/libraries/config/roles_spec.rb
@@ -122,4 +122,16 @@
       expect(Services).to have_received(:enable_group).with('monitoring_role').once
     end
   end
+
+  include_context 'recipes'
+
+  describe 'GitalyRole' do
+    context 'by default' do
+      include_examples 'enable only default service recipes'
+    end
+
+    context 'when enabled' do
+      include_examples 'enable recipes required for the service only', ['gitaly_role'], ['gitaly::enable', 'gitaly::git_data_dirs'], ['gitaly::disable']
+    end
+  end
 end
diff --git a/spec/chef/support/shared_context/recipes_shared_context.rb b/spec/chef/support/shared_context/recipes_shared_context.rb
new file mode 100644
--- /dev/null
+++ b/spec/chef/support/shared_context/recipes_shared_context.rb
@@ -0,0 +1,126 @@
+require 'chef_helper'
+
+RSpec.shared_context 'recipes' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
+
+  let(:mandatory_recipes) do
+    recipes =
+      [
+        "gitlab-ee::default",
+        "pgbouncer::user",
+        "gitlab::default",
+        "gitlab::config",
+        "postgresql::directory_locations",
+        "gitlab::web-server",
+        "gitlab::users",
+        "gitlab::selinux",
+        "gitlab::add_trusted_certs",
+        "package::runit",
+        "package::sysctl",
+        "logrotate::enable",
+        "logrotate::folders_and_configs",
+        "postgresql::bin",
+        "gitlab::bootstrap",
+        "monitoring::default",
+        "monitoring::node-exporter",
+        "monitoring::user"
+      ]
+    runit_recipe = if File.directory?('/run/systemd/system')
+                     ["package::runit_systemd"]
+                   else
+                     []
+                   end
+    recipes + runit_recipe
+  end
+
+  let(:default_service_enable_recipes) do
+    ["gitlab::gitlab-rails",
+     "gitaly::git_data_dirs",
+     "gitlab::rails_pages_shared_path",
+     "gitlab::gitlab-shell",
+     "redis::enable",
+     "gitaly::enable",
+     "postgresql::enable",
+     "postgresql::user",
+     "postgresql::sysctl",
+     "postgresql::standalone",
+     "gitlab-kas::enable",
+     "gitlab::database_migrations",
+     "gitlab::puma",
+     "gitlab::sidekiq",
+     "gitlab::gitlab-workhorse",
+     "gitlab::nginx",
+     "nginx::enable",
+     "gitlab::gitlab-healthcheck",
+     "monitoring::gitlab-exporter",
+     "monitoring::redis-exporter",
+     "monitoring::prometheus",
+     "monitoring::alertmanager",
+     "monitoring::postgres-exporter",
+     "gitlab-ee::suggested_reviewers"]
+  end
+
+  let(:default_service_disable_recipes) do
+    [
+      "redis::disable",
+      "gitaly::disable",
+      "postgresql::disable",
+      "gitlab-kas::disable",
+      "gitlab::puma_disable",
+      "gitlab::sidekiq_disable",
+      "gitlab::gitlab-workhorse_disable",
+      "gitlab::nginx_disable",
+      "monitoring::gitlab-exporter_disable",
+      "monitoring::redis-exporter_disable",
+      "monitoring::prometheus_disable",
+      "monitoring::alertmanager_disable",
+      "monitoring::postgres-exporter_disable",
+    ]
+  end
+
+  let(:extra_disable_recipes) do
+    [
+      "spamcheck::disable",
+      "praefect::disable",
+      "crond::disable",
+      "gitlab::mailroom_disable",
+      "gitlab::remote-syslog_disable",
+      "gitlab::storage-check_disable",
+      "gitlab-pages::disable",
+      "registry::disable",
+      "mattermost::disable",
+      "letsencrypt::disable",
+      "monitoring::pgbouncer-exporter_disable",
+      "gitlab::gitlab-backup-cli_disable",
+      "gitlab::database_reindexing_disable",
+      "gitlab-ee::sentinel_disable",
+      "gitlab-ee::geo-postgresql_disable",
+      "gitlab-ee::geo-logcursor_disable",
+      "consul::disable",
+      "consul::disable_daemon",
+      "pgbouncer::disable",
+      "patroni::disable",
+      "gitlab-ee::geo-secondary_disable",
+    ]
+  end
+
+  shared_examples 'enable only default service recipes' do
+    it 'only default service recipes are run' do
+      expected_recipes = mandatory_recipes + default_service_enable_recipes + extra_disable_recipes
+      expect(chef_run.run_context.loaded_recipes).to match_array(expected_recipes)
+    end
+  end
+
+  shared_examples 'enable recipes required for the service only' do |roles, include_recipes, exclude_recipes|
+    before do
+      stub_gitlab_rb(
+        roles: roles
+      )
+    end
+
+    it 'only recipes required for the service are run' do
+      expected_recipes = mandatory_recipes + default_service_disable_recipes + extra_disable_recipes + include_recipes - exclude_recipes
+      expect(chef_run.run_context.loaded_recipes).to match_array(expected_recipes)
+    end
+  end
+end