Skip to content

Commit 2252fc2

Browse files
Axel von Bertoldipdecat
andcommitted
Merge branch 'community-fork/feat/docker-logging-options' into 'main'
feat: allow passing `env` and `labels` options to `json-file` Docker logging driver See merge request https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/5638 Merged-by: Axel von Bertoldi <avonbertoldi@gitlab.com> Approved-by: Roshni Sarangadharan <rsarangadharan@gitlab.com> Approved-by: Hannes Hörl <2185075-hoegaarden@users.noreply.gitlab.com> Approved-by: Axel von Bertoldi <avonbertoldi@gitlab.com> Reviewed-by: Hannes Hörl <2185075-hoegaarden@users.noreply.gitlab.com> Reviewed-by: GitLab Duo <gitlab-duo@gitlab.com> Reviewed-by: Roshni Sarangadharan <rsarangadharan@gitlab.com> Co-authored-by: Patrick Decat <pdecat@gmail.com>
2 parents ca2c4c7 + 5926aaf commit 2252fc2

6 files changed

Lines changed: 463 additions & 18 deletions

File tree

common/config.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"sigs.k8s.io/yaml"
2424

2525
"github.com/BurntSushi/toml"
26+
"github.com/docker/docker/api/types/container"
2627
"github.com/docker/go-units"
2728
"github.com/minio/minio-go/v7/pkg/s3utils"
2829
"github.com/sirupsen/logrus"
@@ -310,6 +311,7 @@ type DockerConfig struct {
310311
EnableIPv6 bool `toml:"enable_ipv6,omitempty" json:"enable_ipv6" long:"enable-ipv6" description:"Enable IPv6 for automatically created networks. This is only takes affect when the feature flag FF_NETWORK_PER_BUILD is enabled."`
311312
Ulimit map[string]string `toml:"ulimit,omitempty" json:"ulimit,omitempty" long:"ulimit" env:"DOCKER_ULIMIT" description:"Ulimit options for container"`
312313
NetworkMTU int `toml:"network_mtu,omitempty" json:"network_mtu" long:"network-mtu" description:"MTU of the Docker network created for the job IFF the FF_NETWORK_PER_BUILD feature-flag was specified."`
314+
LogOptions map[string]string `toml:"log_options,omitempty" json:"log_options,omitempty" long:"log-options" env:"DOCKER_LOG_OPTIONS" description:"Log driver options for json-file logging"`
313315
}
314316

315317
type InstanceConfig struct {
@@ -1684,6 +1686,36 @@ func (c *DockerConfig) GetServicesLimit() int {
16841686
return *c.ServicesLimit
16851687
}
16861688

1689+
// GetLogConfig returns the LogConfig for build containers
1690+
func (c *DockerConfig) GetLogConfig() (container.LogConfig, error) {
1691+
logConfig := container.LogConfig{
1692+
Type: "json-file",
1693+
}
1694+
1695+
if c == nil || len(c.LogOptions) == 0 {
1696+
return logConfig, nil
1697+
}
1698+
1699+
var invalidKeys []string
1700+
var allowedKeys = []string{"env", "labels"}
1701+
1702+
for key := range c.LogOptions {
1703+
if !slices.Contains(allowedKeys, key) {
1704+
invalidKeys = append(invalidKeys, key)
1705+
}
1706+
}
1707+
1708+
slices.Sort(invalidKeys) // to get stable error outputs
1709+
1710+
if len(invalidKeys) > 0 {
1711+
return logConfig, fmt.Errorf("invalid log options: only %q are allowed, but found: %q", allowedKeys, invalidKeys)
1712+
}
1713+
1714+
logConfig.Config = c.LogOptions
1715+
1716+
return logConfig, nil
1717+
}
1718+
16871719
func (c *KubernetesConfig) GetPollTimeout() int {
16881720
if c.PollTimeout <= 0 {
16891721
c.PollTimeout = KubernetesPollTimeout

common/config_log_options_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//go:build !integration
2+
3+
package common
4+
5+
import (
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestDockerConfig_ValidateLogOptions(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
logOptions map[string]string
15+
expectedErrMsg string
16+
}{
17+
{
18+
name: "nil config",
19+
},
20+
{
21+
name: "empty log options",
22+
logOptions: map[string]string{},
23+
},
24+
{
25+
name: "valid env option",
26+
logOptions: map[string]string{
27+
"env": "GITLAB_CI_JOB_ID,GITLAB_CI_JOB_NAME",
28+
},
29+
},
30+
{
31+
name: "valid labels option",
32+
logOptions: map[string]string{
33+
"labels": "com.gitlab.gitlab-runner.type",
34+
},
35+
},
36+
{
37+
name: "valid env and labels options",
38+
logOptions: map[string]string{
39+
"env": "GITLAB_CI_JOB_ID,GITLAB_CI_JOB_NAME",
40+
"labels": "com.gitlab.gitlab-runner.type",
41+
},
42+
},
43+
{
44+
name: "invalid single option",
45+
logOptions: map[string]string{
46+
"max-size": "10m",
47+
},
48+
expectedErrMsg: `invalid log options: only ["env" "labels"] are allowed, but found: ["max-size"]`,
49+
},
50+
{
51+
name: "invalid multiple options",
52+
logOptions: map[string]string{
53+
"max-size": "10m",
54+
"max-file": "3",
55+
},
56+
expectedErrMsg: `invalid log options: only ["env" "labels"] are allowed, but found: ["max-file" "max-size"]`,
57+
},
58+
{
59+
name: "mixed valid and invalid options",
60+
logOptions: map[string]string{
61+
"env": "CI_JOB_ID",
62+
"max-size": "10m",
63+
"labels": "job_name",
64+
},
65+
expectedErrMsg: `invalid log options: only ["env" "labels"] are allowed, but found: ["max-size"]`,
66+
},
67+
{
68+
name: "unknown option",
69+
logOptions: map[string]string{
70+
"unknown-option": "value",
71+
},
72+
expectedErrMsg: `invalid log options: only ["env" "labels"] are allowed, but found: ["unknown-option"]`,
73+
},
74+
}
75+
76+
for _, tt := range tests {
77+
t.Run(tt.name, func(t *testing.T) {
78+
dockerConfig := &DockerConfig{
79+
LogOptions: tt.logOptions,
80+
}
81+
82+
logConfig, err := dockerConfig.GetLogConfig()
83+
84+
if tt.expectedErrMsg != "" {
85+
assert.Error(t, err)
86+
assert.Contains(t, err.Error(), tt.expectedErrMsg)
87+
} else {
88+
assert.NoError(t, err)
89+
assertMapMatches(t, tt.logOptions, logConfig.Config)
90+
}
91+
})
92+
}
93+
}
94+
95+
func assertMapMatches(t *testing.T, expected, actual map[string]string) {
96+
t.Helper()
97+
if len(expected) == 0 {
98+
assert.Len(t, actual, 0)
99+
return
100+
}
101+
assert.Equal(t, expected, actual)
102+
}

docs/configuration/advanced-configuration.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ The following settings define the Docker container parameters. These settings ar
559559
| `hostname` | | Custom hostname for the Docker container. |
560560
| `image` | `"ruby:3.3"` | The image to run jobs with. |
561561
| `links` | `["mysql_container:mysql"]` | Containers that should be linked with container that runs the job. |
562+
| `log_options` | `{"env": "GITLAB_CI_JOB_ID,GITLAB_CI_JOB_NAME", "labels": "com.gitlab.gitlab-runner.type"}` | Log driver options for Docker containers that use the `json-file` log driver. Only `env` and `labels` options are allowed. For more information, see [Docker log options](#docker-log-options). |
562563
| `memory` | `"128m"` | The memory limit. A string. |
563564
| `memory_swap` | `"256m"` | The total memory limit. A string. |
564565
| `memory_reservation` | `"64m"` | The memory soft limit. A string. |
@@ -645,6 +646,7 @@ Example:
645646
links = ["mysql_container:mysql"]
646647
allowed_images = ["ruby:*", "python:*", "php:*"]
647648
allowed_services = ["postgres:9", "redis:*", "mysql:*"]
649+
log_options = { env = "GITLAB_CI_JOB_ID,GITLAB_CI_JOB_NAME", labels = "com.gitlab.gitlab-runner.type" }
648650
[runners.docker.ulimit]
649651
"rtprio" = "99"
650652
[[runners.docker.services]]
@@ -711,6 +713,50 @@ GitLab Runner 11.11 and later [mount the host directory](https://gitlab.com/gitl
711713
for the defined [services](https://docs.gitlab.com/ci/services/) as
712714
well.
713715

716+
### Docker log options
717+
718+
The `log_options` parameter allows you to configure Docker container log options for the `json-file` log driver.
719+
For security and compatibility reasons, only the `env` and `labels` options are supported.
720+
721+
#### Supported log options
722+
723+
- `env`: Comma-separated list of environment variable names to include in log entries
724+
- `labels`: Comma-separated list of container label names to include in log entries
725+
726+
#### Configuration examples
727+
728+
The following are some configuration examples:
729+
730+
```toml
731+
[[runners]]
732+
[runners.docker]
733+
# Include specific environment variables in logs
734+
log_options = { env = "GITLAB_CI_JOB_ID,GITLAB_CI_JOB_NAME,CI_PIPELINE_ID" }
735+
```
736+
737+
```toml
738+
[[runners]]
739+
[runners.docker]
740+
# Include container labels in logs
741+
log_options = { labels = "com.gitlab.gitlab-runner.type" }
742+
```
743+
744+
```toml
745+
[[runners]]
746+
[runners.docker]
747+
# Include both environment variables and labels
748+
log_options = { env = "GITLAB_CI_JOB_ID,GITLAB_CI_JOB_NAME", labels = "com.gitlab.gitlab-runner.type" }
749+
```
750+
751+
#### Validation and error handling
752+
753+
GitLab Runner validates log options during executor preparation. If you specify unsupported options
754+
such as `max-size`, `max-file`, or `compress`, the job fails immediately with a configuration error.
755+
756+
The log options apply to the main job container and any service containers defined in your CI/CD configuration.
757+
758+
For more information about Docker logging, see the [Docker `json-file` log driver documentation](https://docs.docker.com/config/containers/logging/json-file/).
759+
714760
### Use a private container registry
715761

716762
To use private registries as a source of images for your jobs, configure authorization

executors/docker/docker.go

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,19 @@ type executor struct {
115115

116116
projectUniqRandomizedName string
117117

118-
dockerConn *dockerConnection
118+
dockerConn *dockerConnection
119+
dockerConnector dockerConnector
120+
121+
logConfig container.LogConfig
122+
}
123+
124+
type dockerConnector func(ctx context.Context, options common.ExecutorPrepareOptions, executor *executor) error
125+
126+
func (dc dockerConnector) Connect(ctx context.Context, options common.ExecutorPrepareOptions, executor *executor) error {
127+
if dc == nil {
128+
dc = connectDocker
129+
}
130+
return dc(ctx, options, executor)
119131
}
120132

121133
type dockerTunnel struct {
@@ -559,10 +571,8 @@ func (e *executor) createHostConfigForService(imageIsPrivileged bool, devices []
559571
Binds: e.volumesManager.Binds(),
560572
ShmSize: e.Config.Docker.ShmSize,
561573
Tmpfs: e.Config.Docker.ServicesTmpfs,
562-
LogConfig: container.LogConfig{
563-
Type: "json-file",
564-
},
565-
Init: useInit,
574+
LogConfig: e.logConfig,
575+
Init: useInit,
566576
}, nil
567577
}
568578

@@ -992,12 +1002,10 @@ func (e *executor) createHostConfig(isBuildContainer, imageIsPrivileged bool) (*
9921002
Isolation: isolation,
9931003
VolumeDriver: e.Config.Docker.VolumeDriver,
9941004
VolumesFrom: e.Config.Docker.VolumesFrom,
995-
LogConfig: container.LogConfig{
996-
Type: "json-file",
997-
},
998-
Tmpfs: e.Config.Docker.Tmpfs,
999-
Sysctls: e.Config.Docker.SysCtls,
1000-
Init: useInit,
1005+
LogConfig: e.logConfig,
1006+
Tmpfs: e.Config.Docker.Tmpfs,
1007+
Sysctls: e.Config.Docker.SysCtls,
1008+
Init: useInit,
10011009
}, nil
10021010
}
10031011

@@ -1142,7 +1150,7 @@ func (e *executor) overwriteEntrypoint(image *spec.Image) []string {
11421150
return nil
11431151
}
11441152

1145-
func (e *executor) connectDocker(ctx context.Context, options common.ExecutorPrepareOptions) error {
1153+
func connectDocker(ctx context.Context, options common.ExecutorPrepareOptions, e *executor) error {
11461154
_ = e.dockerConn.Close()
11471155

11481156
dockerConnection, err := createDockerConnection(ctx, options, e)
@@ -1341,7 +1349,16 @@ func (e *executor) Prepare(options common.ExecutorPrepareOptions) error {
13411349

13421350
e.AbstractExecutor.PrepareConfiguration(options)
13431351

1344-
err := e.connectDocker(e.Context, options)
1352+
var err error
1353+
e.logConfig, err = options.Config.Docker.GetLogConfig()
1354+
if err != nil {
1355+
return &common.BuildError{
1356+
Inner: fmt.Errorf("creating docker log configuration: %w", err),
1357+
FailureReason: common.RunnerSystemFailure,
1358+
}
1359+
}
1360+
1361+
err = e.dockerConnector.Connect(e.Context, options, e)
13451362
if err != nil {
13461363
return err
13471364
}
@@ -1607,9 +1624,7 @@ func (e *executor) createHostConfigForServiceHealthCheck(service *serviceInfo) *
16071624
RestartPolicy: neverRestartPolicy,
16081625
ExtraHosts: extraHosts,
16091626
NetworkMode: e.networkMode,
1610-
LogConfig: container.LogConfig{
1611-
Type: "json-file",
1612-
},
1627+
LogConfig: e.logConfig,
16131628
}
16141629
}
16151630

0 commit comments

Comments
 (0)