diff --git a/.github/workflows/dockerless.yaml b/.github/workflows/dockerless.yaml new file mode 100644 index 000000000..696c541cd --- /dev/null +++ b/.github/workflows/dockerless.yaml @@ -0,0 +1,88 @@ +name: dockerless + +# dockerless is the image DevPod builds dev containers with on Kubernetes, without a docker +# daemon (see dockerless/). It is its own Go module: checked on pull requests that change it, +# and published as ghcr.io/curityio/dockerless: when a dockerless-v tag is +# pushed. pkg/devcontainer/single.go pins the version DevPod uses. +on: + pull_request: + branches: + - main + paths: + - .github/workflows/dockerless.yaml + - dockerless/** + push: + tags: + - dockerless-v* + workflow_dispatch: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +defaults: + run: + working-directory: dockerless + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v4 + with: + go-version-file: dockerless/go.mod + cache-dependency-path: dockerless/go.sum + + - name: Build, vet and test + run: | + go build ./... + go vet ./... + go test ./... + + - uses: golangci/golangci-lint-action@v9 + with: + version: v2.15.0 + working-directory: dockerless + + - name: Check licenses + run: | + go install github.com/google/go-licenses/v2@v2.0.1 + go-licenses check ./... --ignore github.com/loft-sh/devpod + + image: + needs: check + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + # dockerless-v0.3.0 is published as 0.3.0. Anything else is only built, for both platforms. + - name: Get the version + id: version + run: | + if [[ "$GITHUB_REF" == refs/tags/dockerless-v* ]]; then + echo "version=${GITHUB_REF_NAME#dockerless-v}" >> "$GITHUB_OUTPUT" + fi + + - uses: docker/setup-qemu-action@v4 + + - uses: docker/setup-buildx-action@v4 + + - name: Log in to ghcr.io + if: steps.version.outputs.version != '' + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/build-push-action@v7 + with: + context: dockerless + platforms: linux/amd64,linux/arm64 + push: ${{ steps.version.outputs.version != '' }} + tags: ghcr.io/curityio/dockerless:${{ steps.version.outputs.version || 'test' }} diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml index ce98b6d76..6e09d5e60 100644 --- a/.github/workflows/e2e-tests.yaml +++ b/.github/workflows/e2e-tests.yaml @@ -14,6 +14,7 @@ on: - "!**_test.go" # exclude test files to ignore unit test changes - "e2e/**_test.go" # include test files in e2e again - ".github/workflows/e2e-tests.yaml" + - "dockerless/**" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -69,6 +70,14 @@ jobs: echo "kubectl config current-context:" $(kubectl config current-context) echo "KUBECONFIG env var:" ${KUBECONFIG} + # The kubernetes tests build with dockerless. Build its image from this checkout, under the + # name DevPod uses by default, so that they test this version rather than a published one. + - name: Build the dockerless image and load it into kind + run: | + image=$(sed -n 's/^var dockerlessImage = "\(.*\)"$/\1/p' pkg/devcontainer/single.go) + docker build -t "$image" dockerless + kind load docker-image "$image" + - name: Build binary and copy to the E2E directory working-directory: ./e2e run: | diff --git a/AGENTS.md b/AGENTS.md index 75a386296..c87c944a0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,6 +15,7 @@ VMs). [CHANGELOG.md](CHANGELOG.md) lists what the fork changed and [TODO.md](TOD | `providers/` | The built-in `docker` and `kubernetes` provider definitions, embedded into the binary. Other providers live in separate repos. | | `desktop/` | The desktop app: Tauri 2 (Rust, `desktop/src-tauri`) with a React/TypeScript UI (`desktop/src`). It runs the `devpod` CLI as a sidecar. | | `docs/` | Docusaurus documentation site, deployed to [devpod.curityio.net](https://devpod.curityio.net/) (GitHub Pages with a custom domain, so `baseUrl` is `/`) by `.github/workflows/docs.yaml` on pushes to `main`. | +| `dockerless/` | The image the kubernetes driver builds dev containers with, without a docker daemon (kaniko). A separate Go module; see its `Readme.md`. | | `e2e/` | Ginkgo end-to-end tests that run the real CLI against Docker, Podman and a kind cluster. | | `hack/` | Build and test scripts. | | `examples/` | Sample devcontainer projects, also used by the e2e tests. | @@ -85,6 +86,10 @@ cd src-tauri && cargo +stable check `pkg/log/table`, `pkg/log/survey`), and the SSH server that runs in the container is `pkg/ssh/sshd`, a copy of `gliderlabs/ssh` with unix socket forwarding added. Check that a new dependency is maintained before adding it. +- **`dockerless/` is a separate Go module** (`github.com/loft-sh/devpod/dockerless`), with its own `go.mod`, no + `vendor/`, a newer Go and golangci-lint v2 (its `.golangci.yaml`). Commands run at the root don't see it: run + them in `dockerless/`, with `GOOS=linux`. Its image is published from a `dockerless-v` tag; DevPod pins + the version in `pkg/devcontainer/single.go`, so a change to `dockerless/` needs a new tag and that pin bumped. - **gRPC tunnel code is generated.** After editing `pkg/agent/tunnel/tunnel.proto`, regenerate with `hack/build-grpc.sh` (needs `protoc`; existing code uses protoc-gen-go v1.36.4 and protoc-gen-go-grpc v1.5.1). - **CI quirks:** diff --git a/CHANGELOG.md b/CHANGELOG.md index a4219bfe7..781e62063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,20 @@ Add new entries to the top of the "Unreleased" section. ### Changed +- Kubernetes workspaces build their dev container with `ghcr.io/curityio/dockerless:0.3.0` instead of + `ghcr.io/loft-sh/dockerless:0.2.0`. Its source is now in this repository (`dockerless/`), built on the maintained + [osscontainertools/kaniko](https://github.com/osscontainertools/kaniko) v1.28 instead of loft-sh's patched copy + of Google's archived kaniko 1.9.2. The `DOCKERLESS_IMAGE` option of the kubernetes provider still overrides it. + It also fixes these dockerless problems: + - It checks registries' TLS certificates. It used to accept any certificate, and plain HTTP, for base images + and the registry cache; the kubernetes provider's new `DOCKERLESS_INSECURE` option allows that again. + - Workspace users can no longer run commands as root: `/.dockerless` was writable by everyone and on `PATH`, + where DevPod's commands, which run as root, looked for `sh` and `su`. + - A Dockerfile's `USER` can be a uid without a passwd entry, or have a group (`1000:1000`); the pod used to stop. + - The container's processes get the variables the pod was started with, such as `containerEnv`, as with docker. + - The build no longer leaves a copy of the files it changed on the pod's disk, no longer looks for cached + layers on Docker Hub when no registry cache is set, and writes the image config so that the pod can't + read it half-written. - `devpod upgrade` only upgrades to a newer stable release: it compares versions instead of installing the latest release whenever it differs from the running one, which could downgrade the CLI, and it never picks a pre-release or a tag that isn't a plain `major.minor.patch` version. It checks that it can replace the binary diff --git a/TODO.md b/TODO.md index 5cfd5b4d2..caef44168 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,15 @@ For each provider we keep: fork it to `curityio`, then update its references in (provider sources, `providers/*/provider.yaml`, docs, desktop app and e2e tests). Providers we don't keep should be removed from the code and docs. +## Publish the dockerless image + +`pkg/devcontainer/single.go` defaults to `ghcr.io/curityio/dockerless:0.3.0`, which +`.github/workflows/dockerless.yaml` publishes: + +- [ ] Push the `dockerless-v0.3.0` tag, so the workflow publishes the image, before the next release +- [ ] Make the `dockerless` package public in the `curityio` organization's package settings (GHCR makes it + private), so that clusters can pull it without credentials + ## Fix links to content that only exists upstream These links still point to `loft-sh/devpod` because the issues and releases they reference don't exist diff --git a/cmd/agent/container/setup.go b/cmd/agent/container/setup.go index db062f4e0..e7228a59c 100644 --- a/cmd/agent/container/setup.go +++ b/cmd/agent/container/setup.go @@ -308,6 +308,9 @@ func dockerlessBuild( log.Debug("Appending registry cache to dockerless build arguments ", dockerlessOptions.RegistryCache) args = append(args, "--registry-cache", dockerlessOptions.RegistryCache) } + if dockerlessOptions.Insecure == "true" { + args = append(args, "--insecure") + } // ignore mounts args = append(args, "--ignore-path", setupInfo.SubstitutionContext.ContainerWorkspaceFolder) diff --git a/dockerless/.dockerignore b/dockerless/.dockerignore new file mode 100644 index 000000000..0b7f7283e --- /dev/null +++ b/dockerless/.dockerignore @@ -0,0 +1,6 @@ +* +!go.mod +!go.sum +!main.go +!cmd/ +!files/ diff --git a/dockerless/.gitignore b/dockerless/.gitignore new file mode 100644 index 000000000..8d46ce65f --- /dev/null +++ b/dockerless/.gitignore @@ -0,0 +1,5 @@ +/.idea +/test +/tools +/dist +/dockerless diff --git a/dockerless/.golangci.yaml b/dockerless/.golangci.yaml new file mode 100644 index 000000000..209ce6309 --- /dev/null +++ b/dockerless/.golangci.yaml @@ -0,0 +1,3 @@ +# dockerless is its own module, linted with golangci-lint v2 and its standard linters; without +# this file, golangci-lint would read the repository root's v1 configuration. +version: "2" diff --git a/dockerless/Dockerfile b/dockerless/Dockerfile new file mode 100644 index 000000000..401f51f52 --- /dev/null +++ b/dockerless/Dockerfile @@ -0,0 +1,49 @@ +# Built and published as ghcr.io/curityio/dockerless by .github/workflows/dockerless.yaml. +FROM --platform=$BUILDPLATFORM golang:1.27.2-alpine AS build +ARG TARGETOS +ARG TARGETARCH +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -trimpath -ldflags "-s -w" -o /dockerless . + +FROM alpine:3.23 AS certs +RUN apk add --no-cache ca-certificates + +# use musl busybox since it's staticly compiled on all platforms +FROM busybox:1.37.0-musl AS busybox + +# now build from scratch +FROM scratch + +# Links the package to this repository when it is first published to ghcr.io, so that it gets the +# repository's access permissions (not its visibility, which is set in the package's settings). +LABEL org.opencontainers.image.source=https://github.com/curityio/devpod + +# Only root may write to the kaniko directory: dockerless and DevPod's agent run as root, and the +# container's other users must not be able to put files where root runs them. +RUN --mount=from=busybox,dst=/usr/ ["busybox", "sh", "-c", "mkdir -p /.dockerless && chmod 755 /.dockerless"] + +COPY --from=certs /etc/ssl/certs/ca-certificates.crt /.dockerless/ssl/certs/ +COPY files/nsswitch.conf /etc/nsswitch.conf + +# Keep these in step with dockerlessEnv in cmd/start.go. +ENV HOME=/root +ENV USER=root +ENV KANIKO_DIR=/.dockerless +# DevPod runs commands in the container through `sh` and `su`, as root, so PATH holds no directory +# a user could write to. dockerless itself is always called by its full path. +ENV PATH=/usr/local/bin:/.dockerless/bin +ENV SSL_CERT_DIR=/.dockerless/ssl/certs + +COPY --from=build /dockerless /.dockerless/dockerless +COPY --from=busybox /bin /.dockerless/bin + +RUN ["/.dockerless/bin/sh", "-c", "echo 'root:x:0:0:root:/root:/.dockerless/bin/sh' > /etc/passwd && chmod 666 /etc/passwd"] + +WORKDIR / + +ENTRYPOINT ["/.dockerless/bin/sh", "-c"] + +CMD ["sleep infinity"] diff --git a/dockerless/LICENSE b/dockerless/LICENSE new file mode 100644 index 000000000..a612ad981 --- /dev/null +++ b/dockerless/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/dockerless/Readme.md b/dockerless/Readme.md new file mode 100644 index 000000000..cbd44269b --- /dev/null +++ b/dockerless/Readme.md @@ -0,0 +1,37 @@ +# dockerless + +Dockerless builds and runs a dev container without a docker daemon. DevPod uses its image, +`ghcr.io/curityio/dockerless`, to build dev containers on Kubernetes: the workspace pod starts +from it, DevPod's agent runs `dockerless build`, which builds the Dockerfile with +[kaniko](https://github.com/osscontainertools/kaniko) straight into the pod's own file system, +and `dockerless start` then runs the container's entrypoint as its user. + +It comes from [loft-sh/dockerless](https://github.com/loft-sh/dockerless) v0.2.0, which used +loft-sh's patched copy of Google's kaniko 1.9.2. It now uses the maintained osscontainertools fork, +whose `Materialize` option does what loft-sh's patch did. + +WARN: running dockerless on your host can delete key files and destroy your working environment. +It only runs in its own image. + +## Development + +It is its own Go module, separate from DevPod's, so that kaniko's dependencies stay out of the CLI +and the agent. Its code is Linux only. + +```bash +GOOS=linux go build ./... +GOOS=linux golangci-lint run ./... # golangci-lint v2 +docker build -t dockerless:dev . +``` + +To try a build in it: + +```bash +docker run --rm -v "$PWD/example:/workspaces/test" dockerless:dev \ + "/.dockerless/dockerless build --context dir:///workspaces/test --dockerfile /workspaces/test/Dockerfile && cat /.dockerless/image.json" +``` + +DevPod uses the image that `pkg/devcontainer/single.go` names, unless the kubernetes provider's +`DOCKERLESS_IMAGE` option names another. The e2e tests build this directory's image under that name +and load it into kind. `.github/workflows/dockerless.yaml` checks it on pull requests, and publishes +it when a `dockerless-v` tag is pushed; then update the version in `single.go`. diff --git a/dockerless/cmd/build.go b/dockerless/cmd/build.go new file mode 100644 index 000000000..c4ddeff12 --- /dev/null +++ b/dockerless/cmd/build.go @@ -0,0 +1,236 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + "time" + + "github.com/containerd/platforms" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/osscontainertools/kaniko/pkg/config" + "github.com/osscontainertools/kaniko/pkg/executor" + "github.com/osscontainertools/kaniko/pkg/util" + "github.com/spf13/cobra" +) + +const defaultCacheDir = "/.dockerless/cache" + +var ImageConfigOutput = "/.dockerless/image.json" + +type BuildCmd struct { + Dockerfile string + Context string + Target string + RegistryCache string + BuildArgs []string + IgnorePaths []string + Insecure bool + ExportCache bool +} + +// NewBuildCmd returns a new build command +func NewBuildCmd() *cobra.Command { + cmd := &BuildCmd{} + cobraCmd := &cobra.Command{ + Use: "build", + Short: "Build the container", + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cobraCmd *cobra.Command, args []string) error { + return cmd.Run() + }, + } + + cobraCmd.Flags().StringVar(&cmd.Dockerfile, "dockerfile", "", "Dockerfile to build from.") + cobraCmd.Flags().StringVar(&cmd.Target, "target", "", "The docker target stage to build.") + cobraCmd.Flags().StringVar(&cmd.Context, "context", "", "Context to build from.") + cobraCmd.Flags().StringArrayVar(&cmd.BuildArgs, "build-arg", []string{}, "Docker build args.") + cobraCmd.Flags().StringArrayVar(&cmd.IgnorePaths, "ignore-path", []string{}, "Extra paths to exclude from deletion.") + cobraCmd.Flags().BoolVar(&cmd.Insecure, "insecure", false, "If true, registries may use plain HTTP or certificates that aren't trusted.") + cobraCmd.Flags().StringVar(&cmd.RegistryCache, "registry-cache", "", "Registry to use as remote cache.") + cobraCmd.Flags().BoolVar(&cmd.ExportCache, "export-cache", false, "If true kanoiko build push cache to registry.") + return cobraCmd +} + +func (cmd *BuildCmd) Run() error { + // check if we already have built the image + _, err := os.Stat(ImageConfigOutput) + if err == nil { + fmt.Println("skip building, because image is already built") + return nil + } + + // fill parameters through env + if cmd.Dockerfile == "" { + cmd.Dockerfile = os.Getenv("DOCKERLESS_DOCKERFILE") + if cmd.Dockerfile == "" { + return fmt.Errorf("--dockerfile is missing") + } + } + if cmd.Context == "" { + cmd.Context = os.Getenv("DOCKERLESS_CONTEXT") + if cmd.Context == "" { + return fmt.Errorf("--context is missing") + } + } + if cmd.Target == "" { + cmd.Target = os.Getenv("DOCKERLESS_TARGET") + } + + // parse extra build args + buildArgs := os.Getenv("DOCKERLESS_BUILD_ARGS") + if buildArgs != "" { + extraBuildArgs := []string{} + _ = json.Unmarshal([]byte(buildArgs), &extraBuildArgs) + cmd.BuildArgs = append(cmd.BuildArgs, extraBuildArgs...) + } + + // start actual build + image, err := cmd.build() + if err != nil { + return err + } + + // write config file to file + configFile, err := image.ConfigFile() + if err != nil { + return fmt.Errorf("get image config: %w", err) + } + + out, err := json.MarshalIndent(configFile, "", " ") + if err != nil { + return fmt.Errorf("marshal image config: %w", err) + } + + // kaniko keeps a copy of every file the build changed, which would double the container's + // disk use. The image config was read from it above, so it can go now. + err = config.CleanupBuild() + if err != nil { + fmt.Fprintf(os.Stderr, "warning: remove build files: %v\n", err) + } + + // `start --wait` reads this file as soon as it exists, so it must appear complete. + tmp := ImageConfigOutput + ".tmp" + err = os.WriteFile(tmp, out, 0666) + if err != nil { + return fmt.Errorf("write image config: %w", err) + } + err = os.Rename(tmp, ImageConfigOutput) + if err != nil { + return fmt.Errorf("write image config: %w", err) + } + + return nil +} + +func (cmd *BuildCmd) build() (v1.Image, error) { + // add ignore paths + buildIgnorePaths(cmd.IgnorePaths) + + // make sure we detect the correct ignore list + err := util.InitIgnoreList() + if err != nil { + return nil, fmt.Errorf("init ignore list: %w", err) + } + + // make sure to delete previous contents + err = util.DeleteFilesystem() + if err != nil { + return nil, fmt.Errorf("delete filesystem: %w", err) + } + + // change dir before building + err = os.Chdir("/") + if err != nil { + return nil, fmt.Errorf("change dir: %w", err) + } + + var targets []string + if cmd.Target != "" { + targets = []string{cmd.Target} + } + + opts := &config.KanikoOptions{ + Destinations: []string{"local"}, + // The workspace runs in this container, so the final stage's file system must be + // unpacked here even when every layer comes from the cache. + Materialize: true, + BuildArgs: cmd.BuildArgs, + DockerfilePath: cmd.Dockerfile, + RegistryOptions: config.RegistryOptions{ + Insecure: cmd.Insecure, + InsecurePull: cmd.Insecure, + SkipTLSVerify: cmd.Insecure, + }, + SrcContext: cmd.Context, + Target: targets, + CustomPlatform: platforms.Format(platforms.Normalize(platforms.DefaultSpec())), + SnapshotMode: "redo", + RunV2: true, + NoPush: true, + KanikoDir: "/.dockerless", + CacheRunLayers: true, + CacheCopyLayers: true, + CompressedCaching: true, + ImageFSExtractRetry: 3, + NoPushCache: !cmd.ExportCache, + Compression: config.ZStd, + CompressionLevel: 3, + CompressionLevelSet: true, + CacheOptions: config.CacheOptions{ + CacheTTL: time.Hour * 24 * 7, + }, + } + // Layers are only cached in a registry. Without one, kaniko would look them up in a + // repository named after the "local" destination, on Docker Hub. + if cmd.RegistryCache != "" { + opts.Cache = true + opts.CacheRepo = cmd.RegistryCache + } else { + opts.CacheDir = defaultCacheDir + } + if !cmd.ExportCache { + opts.SingleSnapshot = true + } + + // let's build! + image, err := executor.DoBuild(opts) + if err != nil { + // add a passwd as other we won't be able to exec into this container + if addPwdErr := addPasswd(); addPwdErr != nil { + return nil, fmt.Errorf("build and add passwd error occurred: %w --- %w", err, addPwdErr) + } + + return nil, fmt.Errorf("build error: %w", err) + } + + return image, nil +} + +func addPasswd() error { + err := os.WriteFile("/etc/passwd", []byte("root:x:0:0:root:/root:/.dockerless/bin/sh"), 0666) + if err != nil { + return fmt.Errorf("write passwd: %w", err) + } + + return nil +} + +func buildIgnorePaths(extraPaths []string) { + // we need to add a couple of extra ignore paths for kaniko + ignorePaths := append([]string{ + "/.dockerless", + "/workspaces", + "/etc/envfile.json", + "/etc/resolv.conf", + "/var/run", + "/product_uuid", + }, extraPaths...) + for _, ignorePath := range ignorePaths { + util.AddToDefaultIgnoreList(util.IgnoreListEntry{ + Path: ignorePath, + PrefixMatchOnly: false, + }) + } +} diff --git a/dockerless/cmd/root.go b/dockerless/cmd/root.go new file mode 100644 index 000000000..11fe5db73 --- /dev/null +++ b/dockerless/cmd/root.go @@ -0,0 +1,17 @@ +package cmd + +import "github.com/spf13/cobra" + +// NewRootCmd returns a new root command +func NewRootCmd() *cobra.Command { + rootCmd := &cobra.Command{ + Use: "dockerless", + Short: "Dockerless", + SilenceUsage: true, + SilenceErrors: true, + } + + rootCmd.AddCommand(NewBuildCmd()) + rootCmd.AddCommand(NewStartCmd()) + return rootCmd +} diff --git a/dockerless/cmd/start.go b/dockerless/cmd/start.go new file mode 100644 index 000000000..058e9a8a2 --- /dev/null +++ b/dockerless/cmd/start.go @@ -0,0 +1,365 @@ +package cmd + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "os/exec" + "os/user" + "strconv" + "strings" + "syscall" + "time" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/spf13/cobra" +) + +var ( + ErrContainerAlreadyRunning = errors.New("container is already running") + ErrNoEntrypoint = errors.New("no entrypoint specified") +) + +var ContainerConfigOutput = "/.dockerless/container.json" + +var ContainerPID = "/.dockerless/pid" + +type StartCmd struct { + Entrypoint []string + Cmd []string + + User string + Env []string + Labels []string + + Wait bool +} + +// NewStartCmd returns a new start command +func NewStartCmd() *cobra.Command { + cmd := &StartCmd{} + cobraCmd := &cobra.Command{ + Use: "start", + Short: "Starts the container", + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cobraCmd *cobra.Command, args []string) error { + return cmd.Run() + }, + } + + cobraCmd.Flags().BoolVar(&cmd.Wait, "wait", false, "If true, will wait until the container is built.") + cobraCmd.Flags().StringVar(&cmd.User, "user", "", "The container user to run the entrypoint with.") + cobraCmd.Flags().StringArrayVar(&cmd.Entrypoint, "entrypoint", []string{}, "The entrypoint to use.") + cobraCmd.Flags().StringArrayVar(&cmd.Cmd, "cmd", []string{}, "The cmds to use.") + cobraCmd.Flags().StringArrayVar(&cmd.Env, "env", []string{}, "Extra environment variables to start the container with.") + cobraCmd.Flags().StringArrayVar(&cmd.Labels, "labels", []string{}, "Labels to add to the container.") + return cobraCmd +} + +func (cmd *StartCmd) Run() error { + // check if we already have built the image + if isContainerRunning() { + return ErrContainerAlreadyRunning + } + + // get the built image config + var ( + configFile *v1.ConfigFile + err error + ) + for { + configFile, err = readImageConfig() + if err != nil { + if !cmd.Wait { + return err + } + + time.Sleep(time.Second) + continue + } + + break + } + + // entrypoint + if len(cmd.Entrypoint) > 0 { + configFile.Config.Entrypoint = cmd.Entrypoint + } + + // cmd + if len(cmd.Cmd) > 0 { + configFile.Config.Cmd = cmd.Cmd + } + + // add labels + if len(cmd.Labels) > 0 { + if configFile.Config.Labels == nil { + configFile.Config.Labels = map[string]string{} + } + + for _, label := range cmd.Labels { + splitted := strings.SplitN(label, "=", 2) + if len(splitted) == 2 { + configFile.Config.Labels[splitted[0]] = splitted[1] + } + } + } + + // add environment variables + configFile.Config.Env = append(configFile.Config.Env, cmd.Env...) + + // user + if cmd.User != "" { + configFile.Config.User = cmd.User + } + + // set to root if not specified + if configFile.Config.WorkingDir == "" { + configFile.Config.WorkingDir = "/" + } else { + // create working dir + err = os.MkdirAll(configFile.Config.WorkingDir, 0777) + if err != nil { + return fmt.Errorf("create workspace folder: %w", err) + } + } + + // get user info + homeDir, credential, err := getUserInfo(configFile.Config.User) + if err != nil { + return fmt.Errorf("get user: %w", err) + } + + // prepare the command to run + entrypoint := []string{} + entrypoint = append(entrypoint, configFile.Config.Entrypoint...) + entrypoint = append(entrypoint, configFile.Config.Cmd...) + if len(entrypoint) == 0 { + return ErrNoEntrypoint + } + + // build the container env + containerEnv := getContainerEnv(homeDir, configFile.Config.Env, os.Environ()) + + // write container config + out, err := json.MarshalIndent(configFile, "", " ") + if err != nil { + return fmt.Errorf("marshal config file: %w", err) + } + + err = os.WriteFile(ContainerConfigOutput, out, 0666) + if err != nil { + return fmt.Errorf("write container config: %w", err) + } + + err = os.WriteFile(ContainerPID, []byte(strconv.Itoa(os.Getpid())), 0666) + if err != nil { + return fmt.Errorf("write container pid: %w", err) + } + + // set container env before execution so we find executables + for _, env := range containerEnv { + splitted := strings.SplitN(env, "=", 2) + if len(splitted) != 2 { + continue + } + + _ = os.Setenv(splitted[0], splitted[1]) + } + + // start command + containerCmd := exec.Command(entrypoint[0], entrypoint[1:]...) + containerCmd.SysProcAttr = &syscall.SysProcAttr{} + containerCmd.SysProcAttr.Credential = credential + containerCmd.Stdout = os.Stdout + containerCmd.Stderr = os.Stderr + containerCmd.Stdin = os.Stdin + containerCmd.Dir = configFile.Config.WorkingDir + containerCmd.Env = containerEnv + + err = containerCmd.Run() + if err != nil { + return fmt.Errorf("run container: %w", err) + } + + return nil +} + +func readImageConfig() (*v1.ConfigFile, error) { + out, err := os.ReadFile(ImageConfigOutput) + if err != nil { + return nil, fmt.Errorf("read image config: %w", err) + } + + configFile := &v1.ConfigFile{} + err = json.Unmarshal(out, configFile) + if err != nil { + return nil, fmt.Errorf("unmarshal image config: %w", err) + } + + return configFile, nil +} + +func isContainerRunning() bool { + pid, err := os.ReadFile(ContainerPID) + if err == nil { + isRunning, _ := isProcessRunning(string(pid)) + if isRunning { + return true + } + } + + return false +} + +func isProcessRunning(pid string) (bool, error) { + parsedPid, err := strconv.Atoi(pid) + if err != nil { + return false, fmt.Errorf("parse pid: %w", err) + } + + process, err := os.FindProcess(parsedPid) + if err != nil { + return false, fmt.Errorf("find process: %w", err) + } + + err = process.Signal(syscall.Signal(0)) + if err != nil { + return false, nil + } + + return true, nil +} + +func setPath(containerEnvs []string) { + for _, containerEnv := range containerEnvs { + splitted := strings.SplitN(containerEnv, "=", 2) + if len(splitted) == 2 && strings.ToUpper(splitted[0]) == "PATH" { + _ = os.Setenv(splitted[0], splitted[1]) + } + } +} + +// dockerlessEnv is the environment of the dockerless image (see its Dockerfile), which the +// container gets from the image it was built from instead. +var dockerlessEnv = map[string]string{ + "HOME": "/root", + "USER": "root", + "PATH": "/usr/local/bin:/.dockerless/bin", + "KANIKO_DIR": "/.dockerless", + "SSL_CERT_DIR": "/.dockerless/ssl/certs", +} + +// getContainerEnv returns the container's environment, as docker would build it: HOME, the built +// image's environment, then the one the container was started with (processEnv, minus what the +// dockerless image and DevPod set for dockerless itself), each overriding the one before. +func getContainerEnv(homeDir string, imageEnv []string, processEnv []string) []string { + env := []string{"HOME=" + homeDir} + env = append(env, imageEnv...) + for _, e := range processEnv { + name, value, _ := strings.Cut(e, "=") + if name == "DOCKERLESS" || strings.HasPrefix(name, "DOCKERLESS_") || dockerlessEnv[name] == value { + continue + } + env = append(env, e) + } + setPath(env) + return env +} + +// getUserInfo returns the home directory and credentials of a USER value, which docker accepts +// as user, uid, user:group, uid:gid, user:gid or uid:group. A uid or gid needs no entry in +// /etc/passwd or /etc/group; a uid without one runs with gid 0 and HOME=/. +func getUserInfo(spec string) (string, *syscall.Credential, error) { + name, group, hasGroup := strings.Cut(spec, ":") + if name == "" { + name = "root" + } + + homeDir := "/" + credential := &syscall.Credential{Groups: []uint32{}} + userObj, err := getUser(name) + if err == nil { + uid, err := strconv.ParseUint(userObj.Uid, 10, 32) + if err != nil { + return "", nil, fmt.Errorf("parse uid: %w", err) + } + gid, err := strconv.ParseUint(userObj.Gid, 10, 32) + if err != nil { + return "", nil, fmt.Errorf("parse gid: %w", err) + } + credential.Uid = uint32(uid) + credential.Gid = uint32(gid) + + groupIDs, err := userObj.GroupIds() + if err == nil { + for _, groupID := range groupIDs { + parsed, err := strconv.ParseUint(groupID, 10, 32) + if err == nil { + credential.Groups = append(credential.Groups, uint32(parsed)) + } + } + } + + homeDir = userObj.HomeDir + if homeDir == "" { + homeDir = "/root" + } + } else { + uid, parseErr := strconv.ParseUint(name, 10, 32) + if parseErr != nil { + return "", nil, err + } + credential.Uid = uint32(uid) + } + + if hasGroup { + gid, err := getGroupID(group) + if err != nil { + return "", nil, err + } + credential.Gid = gid + } + + return homeDir, credential, nil +} + +func getUser(name string) (*user.User, error) { + _, err := strconv.Atoi(name) + if err == nil { + user, err := user.LookupId(name) + if err != nil { + return nil, fmt.Errorf("failed to lookup user by id: %w", err) + } + + return user, nil + } + + user, err := user.Lookup(name) + if err != nil { + return nil, fmt.Errorf("failed to lookup user: %w", err) + } + + return user, nil +} + +func getGroupID(name string) (uint32, error) { + gid, err := strconv.ParseUint(name, 10, 32) + if err == nil { + return uint32(gid), nil + } + + group, err := user.LookupGroup(name) + if err != nil { + return 0, fmt.Errorf("failed to lookup group: %w", err) + } + + gid, err = strconv.ParseUint(group.Gid, 10, 32) + if err != nil { + return 0, fmt.Errorf("parse gid: %w", err) + } + + return uint32(gid), nil +} diff --git a/dockerless/cmd/start_test.go b/dockerless/cmd/start_test.go new file mode 100644 index 000000000..a2fa54eb9 --- /dev/null +++ b/dockerless/cmd/start_test.go @@ -0,0 +1,77 @@ +//go:build linux + +package cmd + +import ( + "slices" + "testing" +) + +func TestGetUserInfo(t *testing.T) { + // root (uid 0, gid 0) is in /etc/passwd everywhere; 54321 is assumed to be in neither file. + tests := []struct { + spec string + homeDir string + uid, gid uint32 + }{ + {spec: "", homeDir: "/root", uid: 0, gid: 0}, + {spec: "root", homeDir: "/root", uid: 0, gid: 0}, + {spec: "0", homeDir: "/root", uid: 0, gid: 0}, + {spec: "0:0", homeDir: "/root", uid: 0, gid: 0}, + {spec: "root:54321", homeDir: "/root", uid: 0, gid: 54321}, + {spec: "54321", homeDir: "/", uid: 54321, gid: 0}, + {spec: "54321:54322", homeDir: "/", uid: 54321, gid: 54322}, + {spec: ":54322", homeDir: "/root", uid: 0, gid: 54322}, + } + for _, test := range tests { + t.Run(test.spec, func(t *testing.T) { + homeDir, credential, err := getUserInfo(test.spec) + if err != nil { + t.Fatalf("getUserInfo(%q): %v", test.spec, err) + } + if homeDir != test.homeDir || credential.Uid != test.uid || credential.Gid != test.gid { + t.Errorf("getUserInfo(%q) = %q, uid %d, gid %d; want %q, uid %d, gid %d", + test.spec, homeDir, credential.Uid, credential.Gid, test.homeDir, test.uid, test.gid) + } + }) + } + + for _, spec := range []string{"no-such-user", "root:no-such-group"} { + _, _, err := getUserInfo(spec) + if err == nil { + t.Errorf("getUserInfo(%q) succeeded, want an error", spec) + } + } +} + +func TestGetContainerEnv(t *testing.T) { + imageEnv := []string{"PATH=/usr/bin:/bin", "LANG=C.UTF-8", "FROM_IMAGE=1"} + processEnv := []string{ + // the dockerless image's own environment + "HOME=/root", + "USER=root", + "PATH=/usr/local/bin:/.dockerless/bin", + "KANIKO_DIR=/.dockerless", + "SSL_CERT_DIR=/.dockerless/ssl/certs", + // what DevPod sets for dockerless + "DOCKERLESS=true", + "DOCKERLESS_CONTEXT=/workspaces/x", + // containerEnv and DevPod's variables + "FROM_IMAGE=2", + "MY_VAR=value", + "DEVPOD=true", + } + + env := getContainerEnv("/home/dev", imageEnv, processEnv) + + want := []string{"HOME=/home/dev", "PATH=/usr/bin:/bin", "LANG=C.UTF-8", "FROM_IMAGE=1", "FROM_IMAGE=2", "MY_VAR=value", "DEVPOD=true"} + if !slices.Equal(env, want) { + t.Errorf("getContainerEnv() = %q, want %q", env, want) + } + + // A variable the container was started with overrides the dockerless image's value. + env = getContainerEnv("/home/dev", nil, []string{"PATH=/opt/bin"}) + if !slices.Contains(env, "PATH=/opt/bin") { + t.Errorf("getContainerEnv() = %q, want it to keep PATH=/opt/bin", env) + } +} diff --git a/dockerless/files/nsswitch.conf b/dockerless/files/nsswitch.conf new file mode 100644 index 000000000..f7ea7b784 --- /dev/null +++ b/dockerless/files/nsswitch.conf @@ -0,0 +1,25 @@ +# /etc/nsswitch.conf +# +# As described on the web page https://man7.org/linux/man-pages/man3/gethostbyname.3.html, +# without the nsswitch.conf file, the gethostbyname() and gethostbyaddr() domain queries +# will fail to a local name server, thus the /etc/hosts will take no effect. +# +# For example, when hostaliases are specified for a kubernetes pod, without proper settings +# defined in this file, the hostaliases settings will not take effect. +# +# Following contents of this file is from the ubuntu:16.04 docker image. + +passwd: compat +group: compat +shadow: compat +gshadow: files + +hosts: files dns +networks: files + +protocols: db files +services: db files +ethers: db files +rpc: db files + +netgroup: nis diff --git a/dockerless/go.mod b/dockerless/go.mod new file mode 100644 index 000000000..e6808ec33 --- /dev/null +++ b/dockerless/go.mod @@ -0,0 +1,111 @@ +module github.com/loft-sh/devpod/dockerless + +go 1.27.2 + +require ( + github.com/containerd/platforms v1.0.0-rc.5 + github.com/google/go-containerregistry v0.22.1 + github.com/osscontainertools/kaniko v1.28.6 + github.com/spf13/cobra v1.10.2 +) + +require ( + cloud.google.com/go/compute/metadata v0.10.0 // indirect + github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.3 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.13.0 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.30 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.7 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.1 // indirect + github.com/Azure/go-autorest/logger v0.2.2 // indirect + github.com/Azure/go-autorest/tracing v0.6.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.10.1 // indirect + github.com/agext/levenshtein v1.2.3 // indirect + github.com/aws/aws-sdk-go-v2 v1.47.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.33.9 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.20.9 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.6 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.6 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.66.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.47.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.21 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.6 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.10.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.38.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.51.4 // indirect + github.com/aws/smithy-go v1.28.5 // indirect + github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/log v0.2.0 // indirect + github.com/containerd/typeurl/v2 v2.3.0 // indirect + github.com/cyphar/filepath-securejoin v0.7.0 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/docker/cli v29.9.0+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.10 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ePirat/docker-credential-gitlabci v1.0.0 // indirect + github.com/go-logr/logr v1.4.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.20.1 // indirect + github.com/klauspost/cpuid/v2 v2.4.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/minio/highwayhash v1.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/buildkit v0.34.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.3.3 // indirect + github.com/moby/moby/api v1.56.1 // indirect + github.com/moby/patternmatcher v0.6.1 // indirect + github.com/moby/sys/sequential v0.7.0 // indirect + github.com/moby/sys/signal v0.7.1 // indirect + github.com/moby/sys/user v0.4.1 // indirect + github.com/moby/sys/userns v0.2.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/osscontainertools/docker-credential-acr v0.9.2 // indirect + github.com/otiai10/copy v1.14.1 // indirect + github.com/otiai10/mint v1.6.3 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/sirupsen/logrus v1.10.2 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect + github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect + github.com/ulikunitz/xz v0.5.17 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0 // indirect + go.opentelemetry.io/otel/log v1.47.0 // indirect + go.opentelemetry.io/otel/metric v1.47.0 // indirect + go.opentelemetry.io/otel/sdk v1.47.0 // indirect + go.opentelemetry.io/otel/trace v1.47.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.1 // indirect + golang.org/x/crypto v0.58.0 // indirect + golang.org/x/net v0.61.0 // indirect + golang.org/x/oauth2 v0.37.0 // indirect + golang.org/x/sync v0.24.0 // indirect + golang.org/x/sys v0.49.0 // indirect + golang.org/x/text v0.43.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc // indirect + google.golang.org/grpc v1.84.0 // indirect + google.golang.org/protobuf v1.36.12 // indirect +) diff --git a/dockerless/go.sum b/dockerless/go.sum new file mode 100644 index 000000000..ccd267674 --- /dev/null +++ b/dockerless/go.sum @@ -0,0 +1,312 @@ +cloud.google.com/go/compute/metadata v0.10.0 h1:pyKMUQSwchgkIBBJGdILqQbs/BNJXqwSA7Ej6LAvvtY= +cloud.google.com/go/compute/metadata v0.10.0/go.mod h1:rGFHRrIif570kSibjFTMbt6/4/tzgJWFGI/HVol4GIk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.3 h1:qgnWTiWs1G7AqcKZegFvkJD1bmXJcaDU3uC0ELSgZ5U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.3/go.mod h1:XtxD48QPInqoj+GMlcRlXHEYsoOxFnP6w2JgIZ9rDS4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.1 h1:u93s+zU2JD62im61Bm5CZIc1ZrOJaIAWEg0WOrMVkEo= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.1/go.mod h1:oXtinPO4OLj9d1DOTrqrL1oRwGhcqadvAmrl6wTeGlk= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0 h1:xFaZZ+IubdftrDHnGGwZ6QvQ3KHTtWl2MCK+GMt2vxs= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0/go.mod h1:mCBhUhlMjLLJKr5aqw2TNS/VqJOie8MzWq3DAMJeKso= +github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 h1:ldKsKtEIblsgsr6mPwrd9yRntoX6uLz/K89wsldwx/k= +github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3/go.mod h1:MAm7bk0oDLmD8yIkvfbxPW04fxzphPyL+7GzwHxOp6Y= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.13.0 h1:Terj1CMW4+NY5CpWjSRkqLoxh2DkYDdv/FH2z0897Uc= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.13.0/go.mod h1:7Ut2Qw9vcwpLW5Q4MBy43uSxXS7tK2yjs94OJ6j5thE= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= +github.com/Azure/go-autorest/autorest v0.11.30 h1:iaZ1RGz/ALZtN5eq4Nr1SOFSlf2E4pDI3Tcsl+dZPVE= +github.com/Azure/go-autorest/autorest v0.11.30/go.mod h1:t1kpPIOpIVX7annvothKvb0stsrXa37i7b+xpmBW8Fs= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4= +github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.7 h1:Q9R3utmFg9K1B4OYtAZ7ZUUvIUdzQt7G2MN5Hi/d670= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.7/go.mod h1:bVrAueELJ0CKLBpUHDIvD516TwmHmzqwCpvONWRsw3s= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/date v0.3.1 h1:o9Z8Jyt+VJJTCZ/UORishuHOusBwolhjokt9s5k8I4w= +github.com/Azure/go-autorest/autorest/date v0.3.1/go.mod h1:Dz/RDmXlfiFFS/eW+b/xMUSFs1tboPVy6UjgADToWDM= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.2 h1:hYqBsEBywrrOSW24kkOCXRcKfKhK76OzLTfF+MYDE2o= +github.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos9XYr9dYTFzpqgibw= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0= +github.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.10.1 h1:0O6j18nQDoIff6/mtosT5LNU2WehEwp+LqfX6699oqY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.10.1/go.mod h1:xdYAf5bjkOpsd3auA8riiJW4vneBubt9caavL7626jQ= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/aws/aws-sdk-go-v2 v1.47.3 h1:HRoT/MkNi3bjO5xF4dCzLzvVmkvRGHj3/cLINDnJejk= +github.com/aws/aws-sdk-go-v2 v1.47.3/go.mod h1:xSl+OSI5ZUw6bSMupWc218zE5Pl+65MCCu3ySWFY99A= +github.com/aws/aws-sdk-go-v2/config v1.33.9 h1:Hv6bcQUJVnmuWaKjqD6Ot8sE75NrWsWV20oGT5aWle0= +github.com/aws/aws-sdk-go-v2/config v1.33.9/go.mod h1:ivowmrZNfBQLcg4QkaItgi5wae4Bzot9RzIdbY1hfTg= +github.com/aws/aws-sdk-go-v2/credentials v1.20.9 h1:SNO55PnWhaDafsOcv16lCA0kdoFMl/YlegWoVsU02s0= +github.com/aws/aws-sdk-go-v2/credentials v1.20.9/go.mod h1:+2reOTRtdD3VSBqpRDNwrZIYy8yMCYa7oK3WKTJ/I1I= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.3 h1:gr3b87OLB29jymWPzp/CAyXwQRJVE+sC4Wm0qBIKHsk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.3/go.mod h1:BRjf4L/Yx2hY54o+ZibPbLJyMMeSwqYQXwcJfqW66a8= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.6 h1:CqL8NN3p4I0JTwwV5MEoatIl4Y2amZ2X9dRD+0hbRL0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.6/go.mod h1:exdJ32hMqXl0Wyo6NwqEC5yIhtua3+PDaKE8P29m4qE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.6 h1:FpBq24TG6WVSObTNhQOv7Q0088rounUUlwn3zlNItkM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.6/go.mod h1:ONzdPVYC95oBOu0VWNEsKEntcoemP3xVKjT+qWml1s4= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.6 h1:Qmn60RWuk9+bd2ddvJJqwf3Cpac3VvGMOdbGemtUhX4= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.6/go.mod h1:h/YJhe4AVyQxEUMluph10iqV7mjajH7A9L+BHF5ppis= +github.com/aws/aws-sdk-go-v2/service/ecr v1.66.3 h1:wzl8YVvzpuDndd+i98mQw+f4zimavB8JQlFf6ECc53A= +github.com/aws/aws-sdk-go-v2/service/ecr v1.66.3/go.mod h1:FVkuFklPL5Oj9eM2uCmdCNng5aTA6daPllLiTcXX0fM= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.47.3 h1:tRCCa57WaVreUi3HoDr9ALtuNV9mSLJXWd4+554lSEk= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.47.3/go.mod h1:GHq1L9aTdNRoYkjK8qB9kzAZ8cmo/+SToKOwdAIXdWc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.21 h1:e6YW5uME30TkrElR+4/xcY7NB6CCj2dcv7vtmGXhfhQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.21/go.mod h1:obpHAMP693+f3/A8PkLhIqfK1C+NWWn8zXzqD3OH520= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.6 h1:LHHdobjo5le1sjJdolitV7uVvYg3wkaeM1Ju2fDgeZQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.6/go.mod h1:CBGB0evAiaMD3Lk4wPO7LQCcCZLuhzL5Qf/wfEUHQ5w= +github.com/aws/aws-sdk-go-v2/service/signin v1.10.4 h1:tvpDjuUvgKURf3Yk4hOaGbZ4QdY+070OWcA5d6LUv2A= +github.com/aws/aws-sdk-go-v2/service/signin v1.10.4/go.mod h1:3ibVKg5Ng82Zby8++jELTD2nHxiPk2DJRLEeW/LKp8M= +github.com/aws/aws-sdk-go-v2/service/sso v1.38.4 h1:ChCtagixSFOxltv0o6E3NT8F7ksvZtZGPwUBynO5Nmk= +github.com/aws/aws-sdk-go-v2/service/sso v1.38.4/go.mod h1:+FEO9jrVMpc9oN3CdAxJbtlQEu3z8UddDPSedpeM3PY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.4 h1:j6c+HA4AoS0EBEjArPjavFEBrucJEryfrQU0TffmACk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.4/go.mod h1:RhghaXUk9q66VtOQUX1FdPddQuHnqQZzbB0fEYXmPRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.51.4 h1:OKEfbf2tcfsF2pBk/S9QqNpo6alaAtiBs4hysCsBjPw= +github.com/aws/aws-sdk-go-v2/service/sts v1.51.4/go.mod h1:mCd6qGo9b8hTtfjgsmNkFlzwjyce4owd7HDVFnCLXhM= +github.com/aws/smithy-go v1.28.5 h1:OqbqKtzY9NUnr2yAe9+hiTdFb20tNaK/lI0PMOGXX9k= +github.com/aws/smithy-go v1.28.5/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 h1:JFWXO6QPihCknDdnL6VaQE57km4ZKheHIGd9YiOGcTo= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0/go.mod h1:046/oLyFlYdAghYQE2yHXi/E//VM5Cf3/dFmA+3CZ0c= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/log v0.2.0 h1:BewD/umNgVnoczglOpX8eRMyEy5t5iPlu5AIpnWDONc= +github.com/containerd/log v0.2.0/go.mod h1:/M7L7CXKcPTfNC74XzaK+5H5KbO5+4lJVpuVI6vRLoM= +github.com/containerd/platforms v1.0.0-rc.5 h1:vXd569rDrz8LeMXzAnBsy6LADV5YtsD8oyaRarxdmSU= +github.com/containerd/platforms v1.0.0-rc.5/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A= +github.com/containerd/typeurl/v2 v2.3.0 h1:HZHPhRWo5XMy3QGQoPrUzbW/2ckwjfweHmOwlkIrPAQ= +github.com/containerd/typeurl/v2 v2.3.0/go.mod h1:Qk+PAdUYArVj41TnGi6rJ+48RF0PkcTc4i/taoBcK0w= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cyphar/filepath-securejoin v0.7.0 h1:s0Y3ITPy6sQn5xt54DuYvTF8hu134ooYLUb58DX/HjE= +github.com/cyphar/filepath-securejoin v0.7.0/go.mod h1:ymLGms/u3BYaviIiuKFnUx8EkQEZeK6cInNoAPJA3o4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/docker/cli v29.9.0+incompatible h1:Cw9x50ioSGgGwXJ4eO+hIBry/w3LLbyF8GPAEKz1fnE= +github.com/docker/cli v29.9.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker-credential-helpers v0.9.10 h1:LCWwv7UmS7fQVmdKDWeMuureOeY5kkbC5kZfkzxkS6k= +github.com/docker/docker-credential-helpers v0.9.10/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ePirat/docker-credential-gitlabci v1.0.0 h1:YRkUSvkON6rT88vtscClAmPEYWhtltGEAuRVYtz1/+Y= +github.com/ePirat/docker-credential-gitlabci v1.0.0/go.mod h1:Ptmh+D0lzBQtgb6+QHjXl9HqOn3T1P8fKUHldiSQQGA= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-containerregistry v0.22.1 h1:RZuuSYhTvlDvtsK+NkutoCZ//C0X2ebLK8X8l3ULs84= +github.com/google/go-containerregistry v0.22.1/go.mod h1:bJR35SK8XgisYmhg/FMQ/5RK0S/XrOAqLBV5/LR2XE0= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 h1:Bd7KaOxzULLxtZ/K5s1aLbWhR0+5RToO65TXHsf3bqQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0/go.mod h1:nN7ts3dFXKtCZWc//yfkpcQNKJABg16/uDVAZpLDalo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= +github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ= +github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= +github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= +github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/minio/highwayhash v1.0.4 h1:asJizugGgchQod2ja9NJlGOWq4s7KsAWr5XUc9Clgl4= +github.com/minio/highwayhash v1.0.4/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/moby/buildkit v0.34.0 h1:bG5h2y1s5IVvc/uSGo6hVlYbd2eXVFRuADl9Zf+jeJM= +github.com/moby/buildkit v0.34.0/go.mod h1:e+Za70y2a3nL1U2Y0P8TvhPyKp2Ok2NS+Q4siSArDiM= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME= +github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= +github.com/moby/moby/api v1.56.1 h1:PpWkvVPB7Fr/No8w+TfyJ/I6rWZ2YPZhT3JorIg+06c= +github.com/moby/moby/api v1.56.1/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/mount v0.3.5 h1:eS3fsZTjHaBihwjp4/+5Z3jxqLXYsbwxqpVSfFv3M00= +github.com/moby/sys/mount v0.3.5/go.mod h1:WUQDO+/uCiCIkIztx8SrwIDVn2dtMFRBebRhpDFT71M= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.7.0 h1:ASQNGNROJSuOO6LL6bPHbKvuZu6NU8P4ldPWk31zj/8= +github.com/moby/sys/sequential v0.7.0/go.mod h1:NfSTAp6V3fw4tmkD62PEcOKeZKquXT8VKCkf7aVR79o= +github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= +github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= +github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0= +github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y= +github.com/moby/sys/userns v0.2.1 h1:4OvdM7BcPkASbuouHsbW3aeMJSFlYDldBRnXVZhaRk8= +github.com/moby/sys/userns v0.2.1/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/osscontainertools/docker-credential-acr v0.9.2 h1:muneeHcQjQNdSVgFB/Zt+3YIMha9wiNPQTOA3msyyZc= +github.com/osscontainertools/docker-credential-acr v0.9.2/go.mod h1:zNKY+FPZzXf8kCUdT9ITWOr0/XpSgiFzkoZuNrD2jyo= +github.com/osscontainertools/kaniko v1.28.6 h1:kfjbcyy5giFd2Y8iZ9K5dUSu8i4FiSDuI+QQoNl+28U= +github.com/osscontainertools/kaniko v1.28.6/go.mod h1:4zbizQuE5tEFK7l+tsU7MD+CeyyVHXkm4VSzIO94s34= +github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= +github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= +github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= +github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= +github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4= +github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= +github.com/ulikunitz/xz v0.5.17 h1:flR0y/x1hgM8EGV1AW3Xll6T413G0glV8UfBwR617V4= +github.com/ulikunitz/xz v0.5.17/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A= +github.com/zeebo/assert v1.3.1/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 h1:julhjPeUH/q/7hinbSdDdqt5h7Zw9YWmRlWRhI0jd54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0/go.mod h1:Ao2mz688LH/tFf0yMAenidq6k2YNSx6SIY2q6jDACck= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0 h1:aXZXsZ012wOgVkqaiQv+Z/d8V0xKjmg70F6m4CE94lc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0/go.mod h1:ziu1gUIJhAaxM3EV1e3Ralk5AAyL1UOYz3lJJ4VSI38= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= +go.opentelemetry.io/proto/otlp v1.11.1 h1:SCrPqH9NE5CmBrOqpjNCiT0X5mGwMyGERXdogfR7Yjw= +go.opentelemetry.io/proto/otlp v1.11.1/go.mod h1:wq8W4aSf6bOy8RZyESOeQFr9Pu7wydWX/TY4sgvrTPg= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54= +golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw= +golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI= +golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98= +golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA= +golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= +golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc h1:rZHRz0ogq4Pid1IKtA/ivHi8XONVF5tqUQXA2TtgpkE= +google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:K/n0XLazCJHjD73YuMyNJsN38BPPy1kfFnKxUXaThFU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc h1:4bNTbnb44EqGVy9HaQxSY2jnafSUdgV3qHtedvNpDKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= diff --git a/dockerless/main.go b/dockerless/main.go new file mode 100644 index 000000000..2fab83afc --- /dev/null +++ b/dockerless/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + "os/signal" + + "github.com/loft-sh/devpod/dockerless/cmd" +) + +func main() { + // build the root command + rootCmd := cmd.NewRootCmd() + + ctx := context.Background() + ctx, stop := signal.NotifyContext(ctx, os.Interrupt, os.Kill) + + // execute command + err := rootCmd.ExecuteContext(ctx) + if err != nil { + execExitErr := &exec.ExitError{} + if errors.As(err, &execExitErr) { + stop() + os.Exit(execExitErr.ExitCode()) + } + + _, _ = fmt.Fprintln(os.Stderr, err.Error()) + stop() + os.Exit(1) + } + + stop() +} diff --git a/e2e/tests/up/docker_compose.go b/e2e/tests/up/docker_compose.go index 81eb416cb..eb791afc7 100644 --- a/e2e/tests/up/docker_compose.go +++ b/e2e/tests/up/docker_compose.go @@ -683,9 +683,9 @@ var _ = DevPodDescribe("devpod up test suite", func() { framework.ExpectNoError(err) gomega.Expect(ids).To(gomega.HaveLen(1), "1 compose container to be created") - vclusterVersionOutput, _, err := f.ExecCommandCapture(ctx, []string{"ssh", "--command", "vcluster --version", projectName}) + ghVersionOutput, _, err := f.ExecCommandCapture(ctx, []string{"ssh", "--command", "gh --version", projectName}) framework.ExpectNoError(err) - gomega.Expect(vclusterVersionOutput).To(gomega.ContainSubstring("vcluster version 0.24.1")) + gomega.Expect(ghVersionOutput).To(gomega.ContainSubstring("gh version 2.62.0")) }, ginkgo.SpecTimeout(framework.GetTimeout())) ginkgo.It("should start a new workspace with env-file", func(ctx context.Context) { diff --git a/e2e/tests/up/testdata/docker-compose-features/.devcontainer.json b/e2e/tests/up/testdata/docker-compose-features/.devcontainer.json index 035490e44..433860e33 100755 --- a/e2e/tests/up/testdata/docker-compose-features/.devcontainer.json +++ b/e2e/tests/up/testdata/docker-compose-features/.devcontainer.json @@ -4,8 +4,8 @@ "service": "app", "workspaceFolder": "/workspaces", "features": { - "ghcr.io/loft-sh/devcontainer-features/vcluster:1.0.1": { - "version": "v0.24.1" + "ghcr.io/devcontainers/features/github-cli:1": { + "version": "2.62.0" } } } diff --git a/examples/simple/.devcontainer.json b/examples/simple/.devcontainer.json index f297c0184..ebc525ac3 100644 --- a/examples/simple/.devcontainer.json +++ b/examples/simple/.devcontainer.json @@ -12,9 +12,6 @@ "helm": "latest", "minikube": "none" }, - "ghcr.io/loft-sh/devcontainer-features/vcluster:1": { - "version": "latest" - }, "ghcr.io/larsnieuwenhuizen/features/jqyq:0": {} }, diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index f54a9c49c..30ac868b6 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -14,7 +14,9 @@ import ( "github.com/pkg/errors" ) -var dockerlessImage = "ghcr.io/loft-sh/dockerless:0.2.0" +// dockerlessImage is built from dockerless/ and published by .github/workflows/dockerless.yaml +// when a dockerless-v tag is pushed. +var dockerlessImage = "ghcr.io/curityio/dockerless:0.3.0" const ( DevPodExtraEnvVar = "DEVPOD" diff --git a/pkg/options/resolve.go b/pkg/options/resolve.go index 535b17c58..15dcafcd1 100644 --- a/pkg/options/resolve.go +++ b/pkg/options/resolve.go @@ -224,6 +224,7 @@ func ResolveAgentConfig(devConfig *config.Config, provider *provider2.ProviderCo agentConfig.Dockerless.Image = resolver.ResolveDefaultValue(agentConfig.Dockerless.Image, options) agentConfig.Dockerless.Disabled = types.StrBool(resolver.ResolveDefaultValue(string(agentConfig.Dockerless.Disabled), options)) agentConfig.Dockerless.IgnorePaths = resolver.ResolveDefaultValue(agentConfig.Dockerless.IgnorePaths, options) + agentConfig.Dockerless.Insecure = types.StrBool(resolver.ResolveDefaultValue(string(agentConfig.Dockerless.Insecure), options)) agentConfig.Dockerless.RegistryCache = devConfig.ContextOption(config.ContextOptionRegistryCache) agentConfig.Driver = resolver.ResolveDefaultValue(agentConfig.Driver, options) agentConfig.Local = types.StrBool(resolver.ResolveDefaultValue(string(agentConfig.Local), options)) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 48586255a..12fee0c7f 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -136,6 +136,9 @@ type ProviderDockerlessOptions struct { // Registry to use as remote cache RegistryCache string `json:"registryCache,omitempty"` + // Insecure lets the build reach registries over plain HTTP or with certificates that aren't trusted + Insecure types.StrBool `json:"insecure,omitempty"` + // DisableDockerCredentials prevents docker credentials from getting injected DisableDockerCredentials types.StrBool `json:"disableDockerCredentials,omitempty"` } diff --git a/providers/kubernetes/provider.yaml b/providers/kubernetes/provider.yaml index e267bd153..a74f76703 100644 --- a/providers/kubernetes/provider.yaml +++ b/providers/kubernetes/provider.yaml @@ -28,6 +28,7 @@ optionGroups: - LABELS - DOCKERLESS_DISABLED - DOCKERLESS_IMAGE + - DOCKERLESS_INSECURE name: "Advanced Options" options: DISK_SIZE: @@ -87,6 +88,11 @@ options: DOCKERLESS_IMAGE: description: The dockerless image to use. global: true + DOCKERLESS_INSECURE: + description: If true, dockerless reaches registries (base images and the registry cache) over plain HTTP or with certificates that aren't trusted. Only for registries you trust on a network you trust, such as a registry cache in the cluster. + global: true + type: boolean + default: "false" DOCKERLESS_DISABLED: description: If dockerless should be disabled. Dockerless is the way DevPod uses to build images directly within Kubernetes. If dockerless is disabled and no image is specified, DevPod will fail instead. global: true @@ -107,6 +113,7 @@ agent: dockerless: disabled: ${DOCKERLESS_DISABLED} image: ${DOCKERLESS_IMAGE} + insecure: ${DOCKERLESS_INSECURE} driver: kubernetes kubernetes: kubernetesContext: ${KUBERNETES_CONTEXT}