Skip to content

Commit 3b45b5a

Browse files
committed
Merge branch 'ash2k/job-router' into 'main'
Job Router See merge request https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/5945 Merged-by: Mikhail Mazurskiy <mmazurskiy@gitlab.com> Approved-by: Timo Furrer <tfurrer@gitlab.com> Approved-by: Roshni Sarangadharan <rsarangadharan@gitlab.com> Approved-by: Arran Walker <ajwalker@gitlab.com> Reviewed-by: Timo Furrer <tfurrer@gitlab.com>
2 parents 0b80939 + b0c1191 commit 3b45b5a

File tree

19 files changed

+2902
-24
lines changed

19 files changed

+2902
-24
lines changed

common/mocks.go

Lines changed: 78 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/network.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,11 @@ type ArtifactsOptions struct {
11991199
LogResponseDetails bool
12001200
}
12011201

1202+
type RouterDiscovery struct {
1203+
ServerURL string `json:"server_url"`
1204+
TLSData TLSData `json:"-"`
1205+
}
1206+
12021207
type FailuresCollector interface {
12031208
RecordFailure(reason JobFailureReason, runnerConfig RunnerConfig)
12041209
}

docs/configuration/feature-flags.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ The flags are defined in `./helpers/featureflags/flags.go` file.
9393
| `FF_USE_GITALY_CORRELATION_ID` | `true` | {{< icon name="dotted-circle" >}} No | | When enabled, the `X-Gitaly-Correlation-ID` header is added to all Git HTTP requests. When disabled, the Git operations execute without Gitaly Correlation ID headers. |
9494
| `FF_HASH_CACHE_KEYS` | `false` | {{< icon name="dotted-circle" >}} No | | When GitLab Runner creates or extracts caches, it hashes the cache keys (SHA256) before using them, both for local and distributed caches (for example, S3). For more information, see [cache key handling](advanced-configuration.md#cache-key-handling). |
9595
| `FF_ENABLE_JOB_INPUTS_INTERPOLATION` | `false` | {{< icon name="dotted-circle" >}} No | | When enabled, job inputs are interpolated. For more information, see [&17833](https://gitlab.com/groups/gitlab-org/-/epics/17833). |
96+
| `FF_USE_JOB_ROUTER` | `false` | {{< icon name="dotted-circle" >}} No | | Makes GitLab Runner fetch jobs by connecting to Job Router rather than GitLab directly. |
9697

9798
<!-- feature_flags_list_end -->
9899

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ require (
9090
golang.org/x/text v0.32.0
9191
google.golang.org/api v0.191.0
9292
google.golang.org/grpc v1.75.1
93+
google.golang.org/protobuf v1.36.9
9394
gopkg.in/yaml.v2 v2.4.0
9495
k8s.io/api v0.32.10
9596
k8s.io/apimachinery v0.32.10
@@ -267,7 +268,6 @@ require (
267268
google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect
268269
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
269270
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
270-
google.golang.org/protobuf v1.36.9 // indirect
271271
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
272272
gopkg.in/inf.v0 v0.9.1 // indirect
273273
gopkg.in/ini.v1 v1.67.0 // indirect

helpers/certificate/x509.go

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,83 @@
11
package certificate
22

33
import (
4+
"crypto/ecdsa"
5+
"crypto/elliptic"
46
"crypto/rand"
57
"crypto/rsa"
68
"crypto/tls"
79
"crypto/x509"
810
"crypto/x509/pkix"
911
"encoding/pem"
10-
"errors"
11-
"math/big"
12+
"fmt"
1213
"net"
1314
"time"
1415
)
1516

1617
const (
1718
x509CertificatePrivateKeyBits = 2048
1819
x509CertificateExpiryInYears = 2
19-
x509CertificateSerialNumber = 1
2020
x509CertificateOrganization = "GitLab Runner"
2121
)
2222

2323
type X509Generator struct{}
2424

25+
func (c X509Generator) GenerateCA() ([]byte, []byte, *x509.Certificate, *ecdsa.PrivateKey, error) {
26+
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
27+
if err != nil {
28+
return nil, nil, nil, nil, err
29+
}
30+
31+
publicKey := privateKey.Public()
32+
33+
tpl := &x509.Certificate{
34+
Subject: pkix.Name{
35+
Organization: []string{"GitLab test CA"},
36+
OrganizationalUnit: []string{"group::runner core"},
37+
CommonName: "test CA cert",
38+
},
39+
40+
NotAfter: time.Now().AddDate(x509CertificateExpiryInYears, 0, 0),
41+
NotBefore: time.Now(),
42+
43+
KeyUsage: x509.KeyUsageCertSign,
44+
45+
BasicConstraintsValid: true,
46+
IsCA: true,
47+
MaxPathLenZero: true,
48+
}
49+
caCert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, publicKey, privateKey)
50+
if err != nil {
51+
return nil, nil, nil, nil, err
52+
}
53+
certTyped, err := x509.ParseCertificate(caCert)
54+
if err != nil {
55+
return nil, nil, nil, nil, err
56+
}
57+
privateDER, err := x509.MarshalPKCS8PrivateKey(privateKey)
58+
if err != nil {
59+
return nil, nil, nil, nil, err
60+
}
61+
62+
caCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caCert})
63+
caKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDER})
64+
65+
return caCertPEM, caKeyPEM, certTyped, privateKey, nil
66+
}
67+
2568
func (c X509Generator) Generate(host string) (tls.Certificate, []byte, error) {
69+
return c.GenerateWithCA(host, nil, nil)
70+
}
71+
72+
func (c X509Generator) GenerateWithCA(host string, caCert *x509.Certificate, caPrivateKey any) (tls.Certificate, []byte, error) {
2673
priv, err := rsa.GenerateKey(rand.Reader, x509CertificatePrivateKeyBits)
2774
if err != nil {
28-
return tls.Certificate{}, []byte{}, err
75+
return tls.Certificate{}, nil, err
2976
}
3077

3178
template := x509.Certificate{
32-
SerialNumber: big.NewInt(x509CertificateSerialNumber),
33-
NotBefore: time.Now(),
34-
NotAfter: time.Now().AddDate(x509CertificateExpiryInYears, 0, 0),
79+
NotBefore: time.Now(),
80+
NotAfter: time.Now().AddDate(x509CertificateExpiryInYears, 0, 0),
3581
Subject: pkix.Name{
3682
Organization: []string{x509CertificateOrganization},
3783
},
@@ -48,17 +94,21 @@ func (c X509Generator) Generate(host string) (tls.Certificate, []byte, error) {
4894
template.DNSNames = append(template.DNSNames, host)
4995
}
5096

51-
publicKeyBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv)
97+
if caCert == nil {
98+
caCert = &template
99+
caPrivateKey = priv
100+
}
101+
publicKeyBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, priv.Public(), caPrivateKey)
52102
if err != nil {
53-
return tls.Certificate{}, []byte{}, errors.New("failed to create certificate")
103+
return tls.Certificate{}, nil, fmt.Errorf("failed to create certificate: %w", err)
54104
}
55105

56106
publicKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicKeyBytes})
57107
privateKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
58108

59109
parsedCertificate, err := tls.X509KeyPair(publicKeyPEM, privateKeyPEM)
60110
if err != nil {
61-
return tls.Certificate{}, []byte{}, err
111+
return tls.Certificate{}, nil, err
62112
}
63113

64114
return parsedCertificate, publicKeyPEM, nil

helpers/featureflags/flags.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const (
5252
UseGitalyCorrelationId string = "FF_USE_GITALY_CORRELATION_ID"
5353
HashCacheKeys string = "FF_HASH_CACHE_KEYS"
5454
EnableJobInputsInterpolation string = "FF_ENABLE_JOB_INPUTS_INTERPOLATION"
55+
UseJobRouter string = "FF_USE_JOB_ROUTER"
5556
)
5657

5758
type FeatureFlag struct {
@@ -429,6 +430,12 @@ var flags = []FeatureFlag{
429430
Deprecated: false,
430431
Description: "When enabled, job inputs are interpolated. For more information, see [&17833](https://gitlab.com/groups/gitlab-org/-/epics/17833).",
431432
},
433+
{
434+
Name: UseJobRouter,
435+
DefaultValue: false,
436+
Deprecated: false,
437+
Description: "Makes GitLab Runner fetch jobs by connecting to Job Router rather than GitLab directly.",
438+
},
432439
}
433440

434441
func GetAll() []FeatureFlag {

main.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import (
1515
"gitlab.com/gitlab-org/gitlab-runner/commands/steps"
1616
"gitlab.com/gitlab-org/gitlab-runner/common"
1717
cli_helpers "gitlab.com/gitlab-org/gitlab-runner/helpers/cli"
18+
"gitlab.com/gitlab-org/gitlab-runner/helpers/featureflags"
1819
"gitlab.com/gitlab-org/gitlab-runner/log"
1920
"gitlab.com/gitlab-org/gitlab-runner/network"
21+
"gitlab.com/gitlab-org/gitlab-runner/router"
2022
"gitlab.com/gitlab-org/labkit/fips"
2123

2224
_ "gitlab.com/gitlab-org/gitlab-runner/cache/azure"
@@ -59,6 +61,8 @@ func main() {
5961
}()
6062

6163
fips.Check()
64+
gitLabClient, clientShutdown, apiRequestsCollector := newClient()
65+
defer clientShutdown()
6266

6367
app := cli.NewApp()
6468
app.Name = filepath.Base(os.Args[0])
@@ -71,7 +75,7 @@ func main() {
7175
Email: "support@gitlab.com",
7276
},
7377
}
74-
app.Commands = newCommands()
78+
app.Commands = newCommands(gitLabClient, apiRequestsCollector)
7579
app.CommandNotFound = func(context *cli.Context, command string) {
7680
logrus.Fatalln("Command", command, "not found.")
7781
}
@@ -89,12 +93,7 @@ func main() {
8993
}
9094
}
9195

92-
func newCommands() []cli.Command {
93-
apiRequestsCollector := network.NewAPIRequestsCollector()
94-
n := network.NewGitLabClient(
95-
network.WithAPIRequestsCollector(apiRequestsCollector),
96-
network.WithCertificateDirectory(commands.GetDefaultCertificateDirectory()),
97-
)
96+
func newCommands(n common.Network, apiRequestsCollector *network.APIRequestsCollector) []cli.Command {
9897
cmds := []cli.Command{
9998
commands.NewListCommand(),
10099
commands.NewRegisterCommand(n),
@@ -118,3 +117,22 @@ func newCommands() []cli.Command {
118117
cmds = append(cmds, commands.NewServiceCommands()...)
119118
return cmds
120119
}
120+
121+
func newClient() (common.Network, func(), *network.APIRequestsCollector) {
122+
apiRequestsCollector := network.NewAPIRequestsCollector()
123+
certDir := commands.GetDefaultCertificateDirectory()
124+
125+
mainClient := network.NewGitLabClient(
126+
network.WithAPIRequestsCollector(apiRequestsCollector),
127+
network.WithCertificateDirectory(certDir),
128+
)
129+
if os.Getenv(featureflags.UseJobRouter) != "true" {
130+
return mainClient, func() {}, apiRequestsCollector
131+
}
132+
rc := router.NewClient(
133+
mainClient,
134+
certDir,
135+
common.AppVersion.UserAgent(),
136+
)
137+
return rc, rc.Shutdown, apiRequestsCollector
138+
}

network/api_requests_collector.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const (
1818
apiEndpointRequestJob apiEndpoint = "request_job"
1919
apiEndpointUpdateJob apiEndpoint = "update_job"
2020
apiEndpointPatchTrace apiEndpoint = "patch_trace"
21+
apiEndpointDiscovery apiEndpoint = "discovery"
2122
)
2223

2324
var (

0 commit comments

Comments
 (0)