-
Notifications
You must be signed in to change notification settings - Fork 1.8k
build: Add build secrets and authenticate GitHub clones #8939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9f0bad8
6265395
cd39e03
2587ac2
b0ee78a
f189c1a
e02d412
172ff1a
133660f
68a6e59
dd2f388
fca8a41
c2a03db
c419b56
4045b10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -263,10 +263,10 @@ | |
|
|
||
| def cmd(self, clist, check_exitcode=False): | ||
| if isinstance(clist, str): | ||
| self._file.write(f"{clist}\n") | ||
Check failureCode scanning / CodeQL Clear-text storage of sensitive information High
This expression stores
sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading |
||
| else: | ||
| for c in clist: | ||
| self._file.write(f"{c} ") | ||
Check failureCode scanning / CodeQL Clear-text storage of sensitive information High
This expression stores
sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading |
||
| self.blankln() | ||
|
|
||
| def cwd(self, path): | ||
|
|
@@ -878,6 +878,122 @@ | |
| ) | ||
|
|
||
|
|
||
| # Keys that 'docker build --secret' accepts. A spec may also carry keys that | ||
| # only the Dockerfile side understands ('target', 'required', 'mode', ...); | ||
| # passing those to the build command is an error, so they are filtered out. | ||
| DOCKER_BUILD_SECRET_KEYS = ("id", "src", "source", "env", "type") | ||
|
|
||
| # Id whose secret is mounted over the apt source list, and where it lands when | ||
| # the spec does not say. | ||
| APT_SOURCES_SECRET_ID = "apt_sources" | ||
| APT_SOURCES_DEFAULT_TARGET = "/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list" | ||
|
|
||
| # Variable holding a git config, and where the build container writes it out. | ||
| # The container is handed the contents rather than the file because docker | ||
| # resolves a bind mount source on the daemon, which need not share a filesystem | ||
| # with this script, and silently mounts a directory when it finds nothing there. | ||
| GIT_CONFIG_CONTENT_ENV = "TRITON_GITCONFIG" | ||
| GIT_CONFIG_CONTAINER_PATH = "/tmp/gitconfig" | ||
|
|
||
|
|
||
| def parse_secret_spec(spec): | ||
| """Split a Docker secret spec into ordered (key, value) pairs. | ||
|
|
||
| Docker does not require any particular field order, so callers look up | ||
| keys by name rather than by position. | ||
| """ | ||
| fields = [] | ||
| for field in spec.split(","): | ||
| key, _, value = field.partition("=") | ||
| key = key.strip() | ||
| if key: | ||
| fields.append((key, value.strip())) | ||
| return fields | ||
|
|
||
|
|
||
| def secret_spec_value(spec, name): | ||
| """Return the value of one field of a secret spec, or "" when unset.""" | ||
| for key, value in parse_secret_spec(spec): | ||
| if key == name: | ||
| return value | ||
| return "" | ||
|
|
||
|
|
||
| def secret_spec_id(spec): | ||
| """Return the 'id' of a Docker secret spec, or "" when it has none.""" | ||
| return secret_spec_value(spec, "id") | ||
|
|
||
|
|
||
| def declared_secret_specs(): | ||
| """Every --docker-build-secret spec, in the order it was given.""" | ||
| return [spec for spec in getattr(FLAGS, "docker_build_secret", None) or [] if spec] | ||
|
|
||
|
|
||
| def declared_secret_ids(): | ||
| """Ids of every secret supplied with --docker-build-secret.""" | ||
| return {i for i in (secret_spec_id(s) for s in declared_secret_specs()) if i} | ||
|
|
||
|
|
||
| def secret_build_args(): | ||
| """Return the '--secret' arguments to forward to 'docker build'. | ||
|
|
||
| Only the keys the build command understands are forwarded. Keys that | ||
| belong to the Dockerfile mount are dropped here and applied where the | ||
| Dockerfile is generated instead. | ||
| """ | ||
| args = [] | ||
| for spec in declared_secret_specs(): | ||
| fields = [ | ||
| "{}={}".format(key, value) | ||
| for key, value in parse_secret_spec(spec) | ||
| if key in DOCKER_BUILD_SECRET_KEYS | ||
| ] | ||
| if fields: | ||
| args.append("--secret {}".format(",".join(fields))) | ||
| return args | ||
|
|
||
|
|
||
| def apt_sources_secret_mount(): | ||
| """Return the RUN mount flag that overlays the apt source list, or an | ||
| empty string when no such secret was requested. | ||
|
|
||
| The mount point comes from the spec's 'target' when it has one, so | ||
| '--docker-build-secret id=apt_sources,src=<path>,target=<path>' controls | ||
| where the list lands. Without a 'target' it defaults to the NVIDIA | ||
| Artifactory source list. When the secret is absent this returns "" and the | ||
| generated Dockerfiles are byte-identical to a build without this feature. | ||
| """ | ||
| for spec in declared_secret_specs(): | ||
| if secret_spec_id(spec) != APT_SOURCES_SECRET_ID: | ||
| continue | ||
| target = secret_spec_value(spec, "target") or APT_SOURCES_DEFAULT_TARGET | ||
| return "--mount=type=secret,id={},target={},required=false ".format( | ||
| APT_SOURCES_SECRET_ID, target | ||
| ) | ||
| return "" | ||
|
|
||
|
|
||
| def mount_apt_sources_secret(df): | ||
| """Prefix every 'RUN apt-get update' in df with the apt source list mount. | ||
|
|
||
| A secret mount is scoped to a single RUN instruction, so every apt step | ||
| that should resolve through the mirror needs its own. | ||
|
|
||
| The first step of the build base is included even though it is the step | ||
| that installs ca-certificates. On a base image that already carries them | ||
| the step resolves through the mirror like any other. On one that does not, | ||
| apt reports the failed TLS handshake, still exits 0, and installs from the | ||
| distribution repositories, so the step behaves as it did before the mount | ||
| was added. | ||
| """ | ||
| mount = apt_sources_secret_mount() | ||
| if not mount: | ||
| return df | ||
|
|
||
| marker = "RUN apt-get update" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocking — the DCGM step never receives the mount, so a GPU build fails on a mirror-only network. This matches only the literal string The flag help and Suggest matching both spellings, e.g. a regex over |
||
| return df.replace(marker, "RUN " + mount + "apt-get update") | ||
|
|
||
|
|
||
| def create_dockerfile_buildbase_rhel(ddir, dockerfile_name, argmap): | ||
| df = """ | ||
| ARG TRITON_VERSION={} | ||
|
|
@@ -1131,8 +1247,10 @@ | |
| ENTRYPOINT [] | ||
| """ | ||
|
|
||
| df = mount_apt_sources_secret(df) | ||
|
|
||
| with open(os.path.join(ddir, dockerfile_name), "w") as dfile: | ||
| dfile.write(df) | ||
Check failureCode scanning / CodeQL Clear-text storage of sensitive information High
This expression stores
sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading |
||
|
|
||
|
|
||
| def create_dockerfile_cibase(ddir, dockerfile_name, argmap): | ||
|
|
@@ -1259,8 +1377,10 @@ | |
| ldconfig | ||
|
|
||
| """ | ||
| df = mount_apt_sources_secret(df) | ||
|
|
||
| with open(os.path.join(ddir, dockerfile_name), "w") as dfile: | ||
| dfile.write(df) | ||
Check failureCode scanning / CodeQL Clear-text storage of sensitive information High
This expression stores
sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading This expression stores sensitive data (secret) Error loading related location Loading |
||
|
|
||
|
|
||
| def dockerfile_prepare_container_linux(argmap, backends, enable_gpu, target_machine): | ||
|
|
@@ -1683,6 +1803,8 @@ | |
|
|
||
| baseargs += ["--cache-from={}".format(k) for k in cachefrommap] | ||
|
|
||
| baseargs += secret_build_args() | ||
|
|
||
| baseargs += ["."] | ||
|
|
||
| docker_script.cwd(THIS_SCRIPT_DIR) | ||
|
|
@@ -1729,6 +1851,19 @@ | |
| # or explicit --release-version). Dev / pre-release builds leave it | ||
| # unset so build_wheel.py reads the in-tree TRITON_VERSION file and | ||
| # takes the PEP 817 variant path. | ||
| # Authenticate the GitHub clones cmake_build performs in this container. | ||
| # It runs the build rather than an image build, so a build secret cannot | ||
| # reach it, and a bind mount cannot either: docker resolves the source | ||
| # path on the daemon, which need not share the filesystem this script | ||
| # runs on, and silently creates a directory when it does not find it. | ||
| # Hand the container the contents instead and let it write the file. | ||
| # | ||
| # Passed by name rather than as NAME=value, so the contents, which carry | ||
| # a credential, stay out of this script, which CI publishes as a build | ||
| # artifact. | ||
| if os.environ.get(GIT_CONFIG_CONTENT_ENV): | ||
| runargs += ["-e", GIT_CONFIG_CONTENT_ENV] | ||
|
|
||
| if "TRITON_RELEASE_VERSION" in os.environ: | ||
| runargs += [ | ||
| "-e", | ||
|
|
@@ -1745,7 +1880,23 @@ | |
|
|
||
| runargs += ["tritonserver_buildbase"] | ||
|
|
||
| runargs += ["./cmake_build"] | ||
| # Write the git config out inside the container, where the contents | ||
| # arrived as an environment variable, and point git at it for the build | ||
| # only. Single quoted so the outer shell leaves the expansion to the | ||
| # container, whose environment is the one holding the value. | ||
| if os.environ.get(GIT_CONFIG_CONTENT_ENV): | ||
| runargs += [ | ||
| "bash", | ||
| "-c", | ||
| '\'printf "%s" "${}" > {} && export GIT_CONFIG_GLOBAL={} && ' | ||
| "./cmake_build'".format( | ||
| GIT_CONFIG_CONTENT_ENV, | ||
| GIT_CONFIG_CONTAINER_PATH, | ||
| GIT_CONFIG_CONTAINER_PATH, | ||
| ), | ||
| ] | ||
| else: | ||
| runargs += ["./cmake_build"] | ||
|
|
||
| # Remove existing tritonserver_builder container... | ||
| docker_script._file.write( | ||
|
|
@@ -1785,14 +1936,7 @@ | |
| "docker", | ||
| "build", | ||
| ] | ||
| if secrets: | ||
| finalargs += [ | ||
| f"--secret id=req,src={requirements}", | ||
| "--secret id=VLLM_INDEX_URL", | ||
| "--secret id=PYTORCH_TRITON_URL", | ||
| "--secret id=NVPL_SLIM_URL", | ||
| f"--build-arg BUILD_PUBLIC_VLLM={build_public_vllm}", | ||
| ] | ||
| finalargs += secret_build_args() | ||
| finalargs += [ | ||
| "-t", | ||
| "tritonserver", | ||
|
|
@@ -2623,15 +2767,25 @@ | |
| help="This flag sets the Python version for RHEL platform of Triton Inference Server to be built. Default: the latest supported version.", | ||
| ) | ||
| parser.add_argument( | ||
| "--build-secret", | ||
| "--docker-build-secret", | ||
|
mc-nv marked this conversation as resolved.
|
||
| action="append", | ||
| required=False, | ||
| nargs=2, | ||
| metavar=("key", "value"), | ||
| help="Add build secrets in the form of <key> <value>. These secrets are used during the build process for vllm. The secrets are passed to the Docker build step as `--secret id=<key>`. The following keys are expected and their purposes are described below:\n\n" | ||
| " - 'req': A file containing a list of dependencies for pip (e.g., requirements.txt).\n" | ||
| " - 'build_public_vllm': A flag (default is 'true') indicating whether to build the public VLLM version.\n\n" | ||
| "Ensure that the required environment variables for these secrets are set before running the build.", | ||
| metavar="spec", | ||
| help="Pass a build secret to 'docker build' using Docker's own syntax. The spec is forwarded " | ||
| "unchanged, so every form 'docker build --secret' accepts is supported:\n\n" | ||
| " - 'id=<id>,src=<path>' read the secret from a file (source= is an accepted alias)\n" | ||
| " - 'id=<id>,env=<var>' read the secret from an environment variable\n" | ||
| " - 'id=<id>' read the environment variable of the same name\n\n" | ||
| "May be repeated. The secret is available to a Dockerfile step that mounts it with " | ||
| "'RUN --mount=type=secret,id=<id>', and never becomes part of an image layer.\n\n" | ||
| "A spec may additionally carry 'target=<path>'. Docker rejects that key on the command line " | ||
| "because it belongs to the Dockerfile mount, so it is stripped from the build command and " | ||
| "applied where the Dockerfile is generated.\n\n" | ||
| "The id 'apt_sources' carries extra meaning: it is also mounted for the duration of each apt step " | ||
| "in the generated Dockerfile and Dockerfile.buildbase, so package installs resolve through a " | ||
| "package mirror. It lands on 'target' when the spec sets one, and on " | ||
| "/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list otherwise. When the secret is omitted the " | ||
| "generated Dockerfiles are unchanged and apt uses the distribution repositories.", | ||
| ) | ||
| parser.add_argument( | ||
| "--triton-wheels-dependencies-group", | ||
|
|
@@ -2666,8 +2820,8 @@ | |
| FLAGS.override_backend_cmake_arg = [] | ||
| if FLAGS.extra_backend_cmake_arg is None: | ||
| FLAGS.extra_backend_cmake_arg = [] | ||
| if FLAGS.build_secret is None: | ||
| FLAGS.build_secret = [] | ||
| if FLAGS.docker_build_secret is None: | ||
| FLAGS.docker_build_secret = [] | ||
|
|
||
| FLAGS.boost_url = os.getenv( | ||
| "TRITON_BOOST_URL", | ||
|
|
@@ -2777,12 +2931,6 @@ | |
| ) | ||
| backends["python"] = backends["vllm"] | ||
|
|
||
| secrets = dict(getattr(FLAGS, "build_secret", [])) | ||
| if secrets: | ||
| requirements = secrets.get("req", "") | ||
| build_public_vllm = secrets.get("build_public_vllm", "true") | ||
| log('Build Arg for BUILD_PUBLIC_VLLM: "{}"'.format(build_public_vllm)) | ||
|
|
||
| # Initialize map of repo agents to build and repo-tag for each. | ||
| repoagents = {} | ||
| for be in FLAGS.repoagent: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocking for the SDK image — this is the only apt step that got the mount.
The
sdk_buildstage's apt step (line 64) and the DCGM install in this runtime stage (line 256) have noapt_sourcesmount. On a mirror-only networkdocker build -f Dockerfile.sdkfails on the first stage, before ever reaching this line.Same fix as
build.py'smount_apt_sources_secret: add--mount=type=secret,id=apt_sources,target=/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list,required=falseto lines 64 and 256.