# HG changeset patch
# User John Cai <jcai@gitlab.com>
# Date 1650004208 0
#      Fri Apr 15 06:30:08 2022 +0000
# Node ID f388612ee81421c1aaff33a26e5d288c87f346f7
# Parent  a3c52bdce5d607c3f925577b027ad2134960ed8c
Gitaly: Add rate_limiting section to config toml

https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4427 adds a new
section called rate_limiting to the gitaly config toml. This change
allows omnibus to change this config.

Changelog: added

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
@@ -2323,6 +2323,17 @@
 #     'max_per_repo' => 5
 #   }
 # ]
+# gitaly['rate_limiting'] = [
+#   {
+#     'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
+#     'interval' => '1m',
+#     'burst' => 10
+#   }, {
+#     'rpc' => "/gitaly.SSHService/SSHUploadPack",
+#     'interval' => '1m',
+#     'burst' => 5
+#   }
+# ]
 #
 # gitaly['daily_maintenance_start_hour'] = 22
 # gitaly['daily_maintenance_start_minute'] = 30
diff --git a/files/gitlab-cookbooks/gitaly/attributes/default.rb b/files/gitlab-cookbooks/gitaly/attributes/default.rb
--- a/files/gitlab-cookbooks/gitaly/attributes/default.rb
+++ b/files/gitlab-cookbooks/gitaly/attributes/default.rb
@@ -33,6 +33,7 @@
 default['gitaly']['ruby_num_workers'] = nil
 default['gitaly']['ruby_rugged_git_config_search_path'] = "/opt/gitlab/embedded/etc"
 default['gitaly']['concurrency'] = nil
+default['gitaly']['rate_limiting'] = nil
 default['gitaly']['daily_maintenance_start_hour'] = nil
 default['gitaly']['daily_maintenance_start_minute'] = nil
 default['gitaly']['daily_maintenance_duration'] = nil
diff --git a/files/gitlab-cookbooks/gitaly/templates/default/gitaly-config.toml.erb b/files/gitlab-cookbooks/gitaly/templates/default/gitaly-config.toml.erb
--- a/files/gitlab-cookbooks/gitaly/templates/default/gitaly-config.toml.erb
+++ b/files/gitlab-cookbooks/gitaly/templates/default/gitaly-config.toml.erb
@@ -160,6 +160,19 @@
   <% end %>
 <% end %>
 
+<% if @rate_limiting %>
+  <% @rate_limiting.each do |endpoint| %>
+    <% if (endpoint['interval'] && endpoint['burst']) && endpoint['burst'] > 0 %>
+
+[[rate_limiting]]
+rpc = "<%= endpoint['rpc'] %>"
+interval = "<%= endpoint['interval'] %>"
+burst = <%= endpoint['burst'] %>
+
+    <% end %>
+  <% end %>
+<% end %>
+
 [daily_maintenance]
 <% if @daily_maintenance_disabled %>
 disabled = <%= @daily_maintenance_disabled %>
diff --git a/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb b/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb
--- a/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb
+++ b/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb
@@ -549,6 +549,35 @@
     end
   end
 
+  context 'when using rate limiting configuration' do
+    before do
+      stub_gitlab_rb(
+        {
+          gitaly: {
+            rate_limiting: [
+              {
+                'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
+                'interval' => '1s',
+                'burst' => 100
+              }, {
+                'rpc' => "/gitaly.SSHService/SSHUploadPack",
+                'interval' => '1s',
+                'burst' => 200,
+              }
+            ]
+          }
+        }
+      )
+    end
+
+    it 'populates gitaly config.toml with custom concurrency configurations' do
+      expect(chef_run).to render_file(config_path)
+        .with_content(%r{\[\[rate_limiting\]\]\s+rpc = "/gitaly.SmartHTTPService/PostReceivePack"\s+interval = "1s"\s+burst = 100})
+      expect(chef_run).to render_file(config_path)
+        .with_content(%r{\[\[rate_limiting\]\]\s+rpc = "/gitaly.SSHService/SSHUploadPack"\s+interval = "1s"\s+burst = 200})
+    end
+  end
+
   shared_examples 'empty concurrency configuration' do
     it 'does not generate a gitaly concurrency configuration' do
       expect(chef_run).not_to render_file(config_path)
@@ -556,6 +585,13 @@
     end
   end
 
+  shared_examples 'empty rate limiting configuration' do
+    it 'does not generate a gitaly concurrency configuration' do
+      expect(chef_run).not_to render_file(config_path)
+        .with_content(%r{\[\[rate_limiting\]\]})
+    end
+  end
+
   context 'when not using concurrency configuration' do
     context 'when concurrency configuration is not set' do
       before do
@@ -568,6 +604,7 @@
       end
 
       it_behaves_like 'empty concurrency configuration'
+      it_behaves_like 'empty rate limiting configuration'
     end
 
     context 'when concurrency configuration is empty' do
@@ -584,6 +621,56 @@
       it_behaves_like 'empty concurrency configuration'
     end
 
+    context 'when rate limiting configuration is empty' do
+      before do
+        stub_gitlab_rb(
+          {
+            gitaly: {
+              rate_limiting: []
+            }
+          }
+        )
+      end
+
+      it_behaves_like 'empty rate limiting configuration'
+    end
+
+    context 'when rate limiting configuration is incomplete' do
+      context 'missing interval' do
+        before do
+          stub_gitlab_rb(
+            {
+              gitaly: {
+                rate_limiting: [{
+                  'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
+                  'burst' => 100
+                }]
+              }
+            }
+          )
+        end
+
+        it_behaves_like 'empty rate limiting configuration'
+      end
+
+      context 'missing burst' do
+        before do
+          stub_gitlab_rb(
+            {
+              gitaly: {
+                rate_limiting: [{
+                  'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
+                  'interval' => '1s',
+                }]
+              }
+            }
+          )
+        end
+
+        it_behaves_like 'empty rate limiting configuration'
+      end
+    end
+
     context 'when max_queue_size and max_queue_wait are empty' do
       before do
         stub_gitlab_rb(