diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json index 5fcf547a4ed7eb4da9d1eb2629bba6964046ab4a..d755a113851df2180a026891a208a1c5e38e5791 100644 --- a/app/assets/javascripts/editor/schema/ci.json +++ b/app/assets/javascripts/editor/schema/ci.json @@ -518,6 +518,12 @@ "type": "string", "minLength": 1, "description": "Image architecture to pull." + }, + "user": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "description": "Username or UID to use for the container." } } }, @@ -593,6 +599,12 @@ "type": "string", "minLength": 1, "description": "Image architecture to pull." + }, + "user": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "description": "Username or UID to use for the container." } } }, diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 10b603d4d92f3947c9b23c9296d4e257f896a111..47a3db38a7877e2e8529caf1b75767d0a12dc410 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -2444,7 +2444,8 @@ image: #### `image:docker` -> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27919) in GitLab 16.7. Requires GitLab Runner 16.7 or later. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27919) in GitLab 16.7. Requires GitLab Runner 16.7 or later. +> - `user` input option [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137907) in GitLab 16.8. Use `image:docker` to pass options to the Docker executor of a GitLab Runner. @@ -2457,6 +2458,7 @@ A hash of options for the Docker executor, which can include: - `platform`: Selects the architecture of the image to pull. When not specified, the default is the same platform as the host runner. +- `user`: Specify the username or UID to use when running the container. **Example of `image:docker`**: @@ -2467,11 +2469,13 @@ arm-sql-job: name: super/sql:experimental docker: platform: arm64/v8 + user: dave ``` **Additional details**: - `image:docker:platform` maps to the [`docker pull --platform` option](https://docs.docker.com/engine/reference/commandline/pull/#options). +- `image:docker:user` maps to the [`docker run --user` option](https://docs.docker.com/engine/reference/commandline/run/#options). #### `image:pull_policy` @@ -4291,7 +4295,8 @@ In this example, GitLab launches two containers for the job: #### `services:docker` -> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27919) in GitLab 16.7. Requires GitLab Runner 16.7 or later. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27919) in GitLab 16.7. Requires GitLab Runner 16.7 or later. +> - `user` input option [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137907) in GitLab 16.8. Use `services:docker` to pass options to the Docker executor of a GitLab Runner. @@ -4304,6 +4309,7 @@ A hash of options for the Docker executor, which can include: - `platform`: Selects the architecture of the image to pull. When not specified, the default is the same platform as the host runner. +- `user`: Specify the username or UID to use when running the container. **Example of `services:docker`**: @@ -4315,11 +4321,13 @@ arm-sql-job: - name: super/sql:experimental docker: platform: arm64/v8 + user: dave ``` **Additional details**: - `services:docker:platform` maps to the [`docker pull --platform` option](https://docs.docker.com/engine/reference/commandline/pull/#options). +- `services:docker:user` maps to the [`docker run --user` option](https://docs.docker.com/engine/reference/commandline/run/#options). #### `services:pull_policy` diff --git a/lib/gitlab/ci/config/entry/schemas/imageable/executor_opts.json b/lib/gitlab/ci/config/entry/schemas/imageable/executor_opts.json index a31374650e6eb7779d96901d075b2318bc464a33..1098da0111a166531e96bb2834006931e1c3e2b9 100644 --- a/lib/gitlab/ci/config/entry/schemas/imageable/executor_opts.json +++ b/lib/gitlab/ci/config/entry/schemas/imageable/executor_opts.json @@ -10,6 +10,11 @@ "type": "string", "minLength": 1, "maxLength": 64 + }, + "user": { + "type": "string", + "minLength": 1, + "maxLength": 255 } }, "additionalProperties": false diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/image.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/image.yml index ad37cd6c3bac8c6c6b9b56a90661ec4397744e09..d6bc3cccf41ea0ecf1e8f4db665c9c5aae9f1e5f 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/image.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/image.yml @@ -26,6 +26,17 @@ invalid_image_platform: docker: platform: ["arm64"] # The expected value is a string, not an array +invalid_image_user: + image: + name: alpine:latest + docker: + user: ["dave"] # The expected value is a string, not an array + +empty_image_user: + image: + name: alpine:latest + docker: + user: "" invalid_image_executor_opts: image: name: alpine:latest diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml index e14ac9ca86ec6cda20f13799b6f9af692de36089..fd05d2606e5adc4fd310ad444d3641e29edcaef0 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml @@ -50,3 +50,17 @@ invalid_service_platform: - name: mysql:5.7 docker: platform: ["arm64"] # The expected value is a string, not an array + +invalid_service_user: + script: echo "Specifying user." + services: + - name: mysql:5.7 + docker: + user: ["dave"] # The expected value is a string, not an array + +empty_service_user: + script: echo "Specifying user" + services: + - name: alpine:latest + docker: + user: "" diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/image.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/image.yml index 4c2559d0800e0063ab511b0755951d5ed0dff506..020cce80fd32d621e00a186624151c1b4ba3ad2e 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/image.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/image.yml @@ -30,6 +30,19 @@ valid_image_with_docker: docker: platform: linux/amd64 +valid_image_with_docker_user: + image: + name: ubuntu:latest + docker: + user: ubuntu + +valid_image_with_docker_multiple_options: + image: + name: ubuntu:latest + docker: + platform: linux/arm64 + user: ubuntu + valid_image_full: image: name: alpine:latest diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml index 1d19ee52cc3b03cf0ac416c09991edaca17ab8a0..0f45b075f537569b288c249595ff6309ac083801 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml @@ -36,3 +36,18 @@ services_platform_string: - name: mysql:5.7 docker: platform: arm64 + +services_with_docker_user: + script: echo "Specifying platform." + services: + - name: mysql:5.7 + docker: + user: ubuntu + +services_with_docker_multiple_options: + script: echo "Specifying platform." + services: + - name: mysql:5.7 + docker: + platform: linux/arm64 + user: ubuntu diff --git a/spec/lib/gitlab/ci/build/image_spec.rb b/spec/lib/gitlab/ci/build/image_spec.rb index f8c0d69be2e1f44aab3ec24134c659ef57dc3d94..3854437483d238b2d3b619fb4f5b5114723bc240 100644 --- a/spec/lib/gitlab/ci/build/image_spec.rb +++ b/spec/lib/gitlab/ci/build/image_spec.rb @@ -29,7 +29,7 @@ context 'when image is defined as hash' do let(:entrypoint) { '/bin/sh' } let(:pull_policy) { %w[always if-not-present] } - let(:executor_opts) { { docker: { platform: 'arm64' } } } + let(:executor_opts) { { docker: { platform: 'arm64', user: 'dave' } } } let(:job) do create(:ci_build, options: { image: { name: image_name, @@ -101,7 +101,7 @@ let(:service_entrypoint) { '/bin/sh' } let(:service_alias) { 'db' } let(:service_command) { 'sleep 30' } - let(:executor_opts) { { docker: { platform: 'amd64' } } } + let(:executor_opts) { { docker: { platform: 'amd64', user: 'dave' } } } let(:pull_policy) { %w[always if-not-present] } let(:job) do create(:ci_build, options: { services: [{ name: service_image_name, entrypoint: service_entrypoint, diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index 99a6e25b3138632a1816714cc0534ca058432abc..0a82010c20c273f7d3ded572b8b06fd916e70d2f 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -112,7 +112,7 @@ end end - context "when docker specifies an option" do + context "when docker specifies platform" do let(:config) { { name: 'image:1.0', docker: { platform: 'amd64' } } } it 'is valid' do @@ -129,15 +129,73 @@ ) end end + + context "when invalid data type is specified for platform option" do + let(:config) { { name: 'image:1.0', docker: { platform: 1 } } } + + it 'raises an error' do + expect(entry).not_to be_valid + expect(entry.errors.first) + .to match %r{image executor opts '/docker/platform' must be a valid 'string'} + end + end + end + + context "when docker specifies user" do + let(:config) { { name: 'image:1.0', docker: { user: 'dave' } } } + + it 'is valid' do + expect(entry).to be_valid + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'image:1.0', + executor_opts: { + docker: { user: 'dave' } + } + ) + end + end + + context "when user is a UID" do + let(:config) { { name: 'image:1.0', docker: { user: '1001' } } } + + it 'is valid' do + expect(entry).to be_valid + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'image:1.0', + executor_opts: { + docker: { user: '1001' } + } + ) + end + end + end + + context "when invalid data type is specified for user option" do + let(:config) { { name: 'image:1.0', docker: { user: 1 } } } + + it 'raises an error' do + expect(entry).not_to be_valid + expect(entry.errors.first) + .to match %r{image executor opts '/docker/user' must be a valid 'string'} + end + end end context "when docker specifies an invalid option" do - let(:config) { { name: 'image:1.0', docker: { platform: 1 } } } + let(:config) { { name: 'image:1.0', docker: { unknown_key: 'foo' } } } it 'is not valid' do expect(entry).not_to be_valid expect(entry.errors.first) - .to match %r{image executor opts '/docker/platform' must be a valid 'string'} + .to match %r{image executor opts '/docker/unknown_key' must be a valid 'schema'} end end end diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb index 82747e7b521e01e4ee80ce65be8157603934722a..8ce0f890b46ee090c70feeff2a4f34fe4ea38394 100644 --- a/spec/lib/gitlab/ci/config/entry/service_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb @@ -154,22 +154,45 @@ end context 'when configuration has docker options' do - let(:config) { { name: 'postgresql:9.5', docker: { platform: 'amd64' } } } + context "with platform option" do + let(:config) { { name: 'postgresql:9.5', docker: { platform: 'amd64' } } } - describe '#valid?' do - it 'is valid' do - expect(entry).to be_valid + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'postgresql:9.5', + executor_opts: { + docker: { platform: 'amd64' } + } + ) + end end end - describe '#value' do - it "returns value" do - expect(entry.value).to eq( - name: 'postgresql:9.5', - executor_opts: { - docker: { platform: 'amd64' } - } - ) + context "with user option" do + let(:config) { { name: 'postgresql:9.5', docker: { user: 'dave' } } } + + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'postgresql:9.5', + executor_opts: { + docker: { user: 'dave' } + } + ) + end end end end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index e44e01b2ffa23e885d69748ab735d99b5d1c93a9..aab7aa2180a79bd18bbbdc0a220872d7beb0da5f 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -1323,10 +1323,12 @@ module Ci name: ruby:2.7 docker: platform: linux/amd64 + user: dave services: - name: postgres:11.9 docker: platform: linux/amd64 + user: john YAML end @@ -1341,9 +1343,9 @@ module Ci options: { script: ["exit 0"], image: { name: "ruby:2.7", - executor_opts: { docker: { platform: 'linux/amd64' } } }, + executor_opts: { docker: { platform: 'linux/amd64', user: 'dave' } } }, services: [{ name: "postgres:11.9", - executor_opts: { docker: { platform: 'linux/amd64' } } }] + executor_opts: { docker: { platform: 'linux/amd64', user: 'john' } } }] }, allow_failure: false, when: "on_success", diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb index 3d6d86335eb812632991b22f8c0b05871a02e0c2..e118ef9a38409194544f72507b03f8a1b6775380 100644 --- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb +++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb @@ -932,7 +932,8 @@ name: 'ruby', executor_opts: { docker: { - platform: 'amd64' + platform: 'amd64', + user: 'dave' } } } @@ -948,7 +949,8 @@ 'image' => { 'name' => 'ruby', 'executor_opts' => { 'docker' => { - 'platform' => 'amd64' + 'platform' => 'amd64', + 'user' => 'dave' } }, 'pull_policy' => nil, diff --git a/spec/requests/api/ci/runner/yamls/image-executor_opts-user.yml b/spec/requests/api/ci/runner/yamls/image-executor_opts-user.yml new file mode 100644 index 0000000000000000000000000000000000000000..9fb56b941c9694c33119c6163f0f414584a2bb36 --- /dev/null +++ b/spec/requests/api/ci/runner/yamls/image-executor_opts-user.yml @@ -0,0 +1,25 @@ +gitlab_ci: + rspec: + image: + name: alpine:latest + docker: + user: dave + script: echo Hello World + +request_response: + image: + name: alpine:latest + entrypoint: null + executor_opts: + docker: + user: dave + ports: [] + pull_policy: null + steps: + - name: script + script: ["echo Hello World"] + timeout: 3600 + when: on_success + allow_failure: false + services: [] + diff --git a/spec/requests/api/ci/runner/yamls/service-executor_opts-user.yml b/spec/requests/api/ci/runner/yamls/service-executor_opts-user.yml new file mode 100644 index 0000000000000000000000000000000000000000..ea824110e637365dda54c758ee17988355eb227d --- /dev/null +++ b/spec/requests/api/ci/runner/yamls/service-executor_opts-user.yml @@ -0,0 +1,27 @@ +gitlab_ci: + rspec: + services: + - name: docker:dind + docker: + user: john + script: echo Hello World + +request_response: + image: null + steps: + - name: script + script: ["echo Hello World"] + timeout: 3600 + when: on_success + allow_failure: false + services: + - name: docker:dind + alias: null + command: null + entrypoint: null + executor_opts: + docker: + user: john + ports: [] + pull_policy: null + variables: []