Commit 42616e40 authored by Steve Azzopardi's avatar Steve Azzopardi

Add feature flag to use old API for helper

With gitlab-org/gitlab-runner!1195 &
gitlab-org/gitlab-runner!1201 the bash
scripts were moved to Go so that they can be used for windows
containers.

It's safe to use these commands on new helper images, which is
automatically downloaded when the user does not specify a `helper_image`
in the config file. If the user has specified a helper image which does
not use the new API will be considered as a breaking change.

If the `helper_image` is specified and the newly introduced feature flag
`FF_DOCKER_HELPER_IMAGE_V2` is not set, use the old command when
starting the container.
parent 62745eed
......@@ -62,6 +62,10 @@ const (
BuildStageUploadOnFailureArtifacts BuildStage = "upload_artifacts_on_failure"
)
const (
FFDockerHelperImageV2 string = "FF_DOCKER_HELPER_IMAGE_V2"
)
type Build struct {
JobResponse `yaml:",inline"`
......
......@@ -29,5 +29,6 @@ change hidden behind the feature flag disabled a corresponding environment varia
| Feature flag | Default value | Deprecated | To be removed with | Description |
|--------------------------------------|---------------|------------|--------------------|-------------|
| `FF_K8S_USE_ENTRYPOINT_OVER_COMMAND` | `true` | ✓ | 12.0 | Enables [the fix][mr-1010] for entrypoint configuration when `kubernetes` executor is used. |
| `FF_DOCKER_HELPER_IMAGE_V2` | `false` | ✓ | 12.0 | Enable the helper image to use the new commands when [helper_image](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section) is specified. This will start using the new API that will be used in 12.0 and stop showing the warning message in the build log. |
[mr-1010]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1010
......@@ -380,17 +380,21 @@ func (e *executor) getLabels(containerType string, otherLabels ...string) map[st
// createCacheVolume returns the id of the created container, or an error
func (e *executor) createCacheVolume(containerName, containerPath string) (string, error) {
// get busybox image
cacheImage, err := e.getPrebuiltImage()
if err != nil {
return "", err
}
cmd := []string{"gitlab-runner-helper", "cache-init", containerPath}
// TODO: Remove in 12.0 to start using the command from `gitlab-runner-helper`
if e.checkOutdatedHelperImage() {
e.Debugln(common.FFDockerHelperImageV2, "is not set, falling back to old command")
cmd = []string{"gitlab-runner-cache", containerPath}
}
config := &container.Config{
Image: cacheImage.ID,
Cmd: []string{
"gitlab-runner-cache", containerPath,
},
Cmd: cmd,
Volumes: map[string]struct{}{
containerPath: {},
},
......@@ -1299,8 +1303,15 @@ func (e *executor) runServiceHealthCheckContainer(service *types.Container, time
containerName := service.Names[0] + "-wait-for-service"
cmd := []string{"gitlab-runner-helper", "health-check"}
// TODO: Remove in 12.0 to start using the command from `gitlab-runner-helper`
if e.checkOutdatedHelperImage() {
e.Debugln(common.FFDockerHelperImageV2, "is not set, falling back to old command")
cmd = []string{"gitlab-runner-service"}
}
config := &container.Config{
Cmd: []string{"gitlab-runner-service"},
Cmd: cmd,
Image: waitImage.ID,
Labels: e.getLabels("wait", "wait="+service.ID),
}
......@@ -1399,3 +1410,7 @@ func (e *executor) readContainerLogs(containerID string) string {
containerLog := containerBuffer.String()
return strings.TrimSpace(containerLog)
}
func (e *executor) checkOutdatedHelperImage() bool {
return !e.Build.IsFeatureFlagOn(common.FFDockerHelperImageV2) && e.Config.Docker.HelperImage != ""
}
......@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-runner/common"
"gitlab.com/gitlab-org/gitlab-runner/executors"
"gitlab.com/gitlab-org/gitlab-runner/helpers"
"gitlab.com/gitlab-org/gitlab-runner/helpers/docker"
)
......@@ -321,6 +322,11 @@ func TestHelperImageWithVariable(t *testing.T) {
Once()
e := executor{
AbstractExecutor: executors.AbstractExecutor{
Build: &common.Build{
JobResponse: common.JobResponse{},
},
},
client: c,
}
......@@ -1156,6 +1162,201 @@ func TestDockerSysctlsSetting(t *testing.T) {
testDockerConfigurationWithJobContainer(t, dockerConfig, cce)
}
// TODO: Remove in 12.0
func TestCreateCacheVolumeFeatureFlag(t *testing.T) {
cacheDir := "/cache"
cases := []struct {
name string
variables common.JobVariables
helperImage string
expectedCmd []string
}{
{
name: "Helper image is not specified",
variables: common.JobVariables{},
helperImage: "",
expectedCmd: []string{"gitlab-runner-helper", "cache-init", cacheDir},
},
{
name: "Helper image is not specified and FF still turned on",
variables: common.JobVariables{
common.JobVariable{Key: common.FFDockerHelperImageV2, Value: "true"},
},
helperImage: "",
expectedCmd: []string{"gitlab-runner-helper", "cache-init", cacheDir},
},
{
name: "Helper image is specified",
variables: common.JobVariables{},
helperImage: "gitlab/gitlab-runner-helper:x86_64-latest",
expectedCmd: []string{"gitlab-runner-cache", cacheDir},
},
{
name: "Helper image is specified & FF variable is set to true",
variables: common.JobVariables{
common.JobVariable{Key: common.FFDockerHelperImageV2, Value: "true"},
},
helperImage: "gitlab/gitlab-runner-helper:x86_64-latest",
expectedCmd: []string{"gitlab-runner-helper", "cache-init", cacheDir},
},
}
for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
helperImageID := fmt.Sprintf("%s-helperImage-%d", t.Name(), time.Now().Unix())
cacheContainerID := fmt.Sprintf("%s-cacheContainer-%d", t.Name(), time.Now().Unix())
containerName := fmt.Sprintf("%s-cacheContainerName-%d", t.Name(), time.Now().Unix())
mClient := docker_helpers.MockClient{}
defer mClient.AssertExpectations(t)
mClient.On("ImageInspectWithRaw", mock.Anything, mock.Anything).
Return(types.ImageInspect{ID: helperImageID}, nil, nil)
mClient.On("ContainerStart", mock.Anything, cacheContainerID, mock.Anything).Return(nil).Once()
mClient.On("ContainerInspect", mock.Anything, cacheContainerID).Return(types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
State: &types.ContainerState{
ExitCode: 0,
},
},
}, nil).Once()
if testCase.helperImage != "" {
mClient.On("ImagePullBlocking", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
}
executor := setUpExecutorForFeatureFlag(testCase.variables, testCase.helperImage, &mClient)
expectedConfig := &container.Config{
Image: helperImageID,
Cmd: testCase.expectedCmd,
Volumes: map[string]struct{}{
cacheDir: {},
},
Labels: executor.getLabels("cache", "cache.dir="+cacheDir),
}
mClient.On("ContainerCreate", mock.Anything, expectedConfig, mock.Anything, mock.Anything, containerName).
Return(container.ContainerCreateCreatedBody{ID: cacheContainerID}, nil).
Once()
_, err := executor.createCacheVolume(containerName, cacheDir)
assert.NoError(t, err)
})
}
}
// TODO: Remove in 12.0
func TestRunServiceHealthCheckContainerFeatureFlag(t *testing.T) {
var cases = []struct {
name string
variables common.JobVariables
helperImage string
expectedCmd []string
}{
{
name: "Helper image is not specified",
variables: common.JobVariables{},
helperImage: "",
expectedCmd: []string{"gitlab-runner-helper", "health-check"},
},
{
name: "Helper image is not specified and FF still turned on",
variables: common.JobVariables{
common.JobVariable{Key: common.FFDockerHelperImageV2, Value: "true"},
},
helperImage: "",
expectedCmd: []string{"gitlab-runner-helper", "health-check"},
},
{
name: "Helper image is specified",
variables: common.JobVariables{},
helperImage: "gitlab/gitlab-runner-helper:x86_64-latest",
expectedCmd: []string{"gitlab-runner-service"},
},
{
name: "Helper image is specified & FF variable is set to true",
variables: common.JobVariables{
common.JobVariable{Key: common.FFDockerHelperImageV2, Value: "true"},
},
helperImage: "gitlab/gitlab-runner-helper:x86_64-latest",
expectedCmd: []string{"gitlab-runner-helper", "health-check"},
},
}
for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
helperImageID := fmt.Sprintf("%s-helperImage-%d", t.Name(), time.Now().Unix())
containerID := fmt.Sprintf("%s-%d", t.Name(), time.Now().Unix())
serviceContainerID := fmt.Sprintf("%s-wait-for-service", containerID)
mClient := docker_helpers.MockClient{}
defer mClient.AssertExpectations(t)
mClient.On("ImageInspectWithRaw", mock.Anything, mock.Anything).
Return(types.ImageInspect{ID: helperImageID}, nil, nil)
mClient.On("ContainerStart", mock.Anything, serviceContainerID, mock.Anything).Return(nil).Once()
mClient.On("ContainerInspect", mock.Anything, serviceContainerID).Return(types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
State: &types.ContainerState{
ExitCode: 0,
},
},
}, nil).Once()
mClient.On("NetworkList", mock.Anything, mock.Anything).Return([]types.NetworkResource{}, nil).Once()
mClient.On("ContainerRemove", mock.Anything, serviceContainerID, mock.Anything).Return(nil).Once()
if testCase.helperImage != "" {
mClient.On("ImagePullBlocking", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
}
executor := setUpExecutorForFeatureFlag(testCase.variables, testCase.helperImage, &mClient)
service := &types.Container{
ID: containerID,
Names: []string{containerID},
}
expectedConfig := &container.Config{
Cmd: testCase.expectedCmd,
Image: helperImageID,
Labels: executor.getLabels("wait", "wait="+service.ID),
}
mClient.On("ContainerCreate", mock.Anything, expectedConfig, mock.Anything, mock.Anything, serviceContainerID).
Return(container.ContainerCreateCreatedBody{ID: serviceContainerID}, nil).
Once()
err := executor.runServiceHealthCheckContainer(service, time.Minute)
assert.NoError(t, err)
})
}
}
// TODO: Remove in 12.0
func setUpExecutorForFeatureFlag(variables common.JobVariables, helperImage string, client docker_helpers.Client) executor {
return executor{
AbstractExecutor: executors.AbstractExecutor{
Config: common.RunnerConfig{
RunnerSettings: common.RunnerSettings{
Docker: &common.DockerConfig{
HelperImage: helperImage,
},
},
},
Build: &common.Build{
JobResponse: common.JobResponse{
Variables: variables,
},
Runner: &common.RunnerConfig{
RunnerCredentials: common.RunnerCredentials{
Token: "xxxxx",
},
},
},
Context: context.Background(),
},
client: client,
}
}
func init() {
docker_helpers.HomeDirectory = ""
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment