Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions zebra/lib/zebra/workers/job_request_factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule Zebra.Workers.JobRequestFactory do
Artifacthub,
Cache,
CallbackToken,
GitCheckout,
JobRequest,
Loghub2,
Machine,
Expand Down Expand Up @@ -102,6 +103,7 @@ defmodule Zebra.Workers.JobRequestFactory do
cache_env_vars ++
ToolboxInstall.env_vars(job) ++
TestResults.env_vars(org_id) ++
GitCheckout.env_vars(job, org_id) ++
open_id_token_env_vars ++
repo_env_vars ++
Enum.flat_map(all_secrets.job_secrets, & &1.env_vars) ++
Expand Down
42 changes: 42 additions & 0 deletions zebra/lib/zebra/workers/job_request_factory/git_checkout.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule Zebra.Workers.JobRequestFactory.GitCheckout do
@moduledoc """
Handles resilient git checkout configuration for jobs.

When the :git_clone_slow_retry feature is enabled for an organization,
this module adds the SEMAPHORE_GIT_CLONE_SLOW_RETRY environment variable,
which opts the toolbox `checkout` into slow-clone detection and resilient
retry (speed monitoring, retries, and alternative-endpoint fallback).

Only injected on cloud agents. The resilient behaviour (GeoDNS-based
alternative-endpoint fallback, DoH lookups) targets GitHub.com reachability
from Semaphore's cloud egress; on self-hosted agents the network is the
customer's own, so injecting it there is inappropriate (and the DoH
endpoint may well be blocked).

The toolbox keeps sensible defaults for the tuning knobs
(threshold/timeout/grace/retries), so only the on/off switch is injected
here; the feature is a no-op in the toolbox when this var is absent.
"""

alias Zebra.Models.Job
alias Zebra.Workers.JobRequestFactory.JobRequest

@doc """
Returns environment variables for resilient git checkout.

Adds SEMAPHORE_GIT_CLONE_SLOW_RETRY=true when the job runs on a cloud agent
and the :git_clone_slow_retry feature is enabled for the organization.
"""
def env_vars(job, org_id) do
if inject?(job, org_id) do
[JobRequest.env_var("SEMAPHORE_GIT_CLONE_SLOW_RETRY", "true")]
Comment thread
dexyk marked this conversation as resolved.
else
[]
end
end

defp inject?(job, org_id) do
not Job.self_hosted?(job.machine_type) and
FeatureProvider.feature_enabled?(:git_clone_slow_retry, param: org_id)
end
end
6 changes: 6 additions & 0 deletions zebra/test/support/stubbed_provider.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Support.StubbedProvider do
@exclude_from_brownouts_org_id "org-exclude-from-brownouts-enabled"

@test_results_no_trim_org_id "org-test-results-no-trim-enabled"
@git_clone_slow_retry_org_id "org-git-clone-slow-retry-enabled"

@impl FeatureProvider.Provider
def provide_features(org_id \\ nil, _opts \\ []) do
Expand All @@ -18,11 +19,13 @@ defmodule Support.StubbedProvider do
feature("e1_to_f1_migration", e1_to_f1_traits(org_id)),
feature("e2_to_f1_migration", e2_to_f1_traits(org_id)),
feature("test_results_no_trim", test_results_no_trim_traits(org_id)),
feature("git_clone_slow_retry", git_clone_slow_retry_traits(org_id)),
feature("exclude_from_brownouts", exclude_from_brownouts_traits(org_id))
]}
end

def test_results_no_trim_org_id, do: @test_results_no_trim_org_id
def git_clone_slow_retry_org_id, do: @git_clone_slow_retry_org_id
def exclude_from_brownouts_org_id, do: @exclude_from_brownouts_org_id

def e1_to_f1_org_id, do: @e1_to_f1_org_id
Expand Down Expand Up @@ -57,6 +60,9 @@ defmodule Support.StubbedProvider do
defp test_results_no_trim_traits(@test_results_no_trim_org_id), do: [:enabled]
defp test_results_no_trim_traits(_org_id), do: [:hidden]

defp git_clone_slow_retry_traits(@git_clone_slow_retry_org_id), do: [:enabled]
defp git_clone_slow_retry_traits(_org_id), do: [:hidden]

defp exclude_from_brownouts_traits(@exclude_from_brownouts_org_id), do: [:enabled]
defp exclude_from_brownouts_traits(_org_id), do: [:hidden]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ defmodule Zebra.Workers.FeatureProviderInvalidatorWorkerTest do
Worker.features_changed(callback_message)

{:ok, features} = FeatureProvider.list_features()
assert length(features) == 8
assert length(features) == 9
end

test "when the organization feature state changes, organization feature caches are invalidated" do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule Zebra.Workers.JobRequestFactory.GitCheckoutTest do
use Zebra.DataCase

alias Zebra.Models.Job
alias Zebra.Workers.JobRequestFactory.GitCheckout

@cloud_job %Job{machine_type: "e1-standard-2"}
@self_hosted_job %Job{machine_type: "s1-local"}

describe "env_vars/2" do
test "returns empty list when feature is disabled" do
org_id = Ecto.UUID.generate()

assert GitCheckout.env_vars(@cloud_job, org_id) == []
end

test "returns SEMAPHORE_GIT_CLONE_SLOW_RETRY env var on a cloud agent when feature is enabled" do
org_id = Support.StubbedProvider.git_clone_slow_retry_org_id()

env_vars = GitCheckout.env_vars(@cloud_job, org_id)

assert length(env_vars) == 1
[env_var] = env_vars
assert env_var["name"] == "SEMAPHORE_GIT_CLONE_SLOW_RETRY"
assert env_var["value"] == Base.encode64("true")
end

test "returns empty list on a self-hosted agent even when feature is enabled" do
org_id = Support.StubbedProvider.git_clone_slow_retry_org_id()

assert GitCheckout.env_vars(@self_hosted_job, org_id) == []
end
end
end