From 8635faddf798980cfe75f824bd26406ee498e755 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Mon, 2 Sep 2024 15:46:29 +0100 Subject: [PATCH 01/26] POD-823: Use private dockerless WIP --- cmd/agent/container/setup.go | 2 ++ examples/build/.devcontainer.json | 7 +++++++ examples/build/Dockerfile | 32 +++++++++++++++++++++++++++++++ examples/build/README.md | 6 ++++++ examples/build/app/code | 0 examples/build/files/test | 1 + pkg/devcontainer/single.go | 2 +- 7 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 examples/build/.devcontainer.json create mode 100644 examples/build/Dockerfile create mode 100644 examples/build/README.md create mode 100644 examples/build/app/code create mode 100644 examples/build/files/test diff --git a/cmd/agent/container/setup.go b/cmd/agent/container/setup.go index 3060b00ad..ee309932a 100644 --- a/cmd/agent/container/setup.go +++ b/cmd/agent/container/setup.go @@ -51,6 +51,7 @@ type SetupContainerCmd struct { InjectGitCredentials bool ContainerWorkspaceInfo string SetupInfo string + Repo string } // NewSetupContainerCmd creates a new command @@ -71,6 +72,7 @@ func NewSetupContainerCmd(flags *flags.GlobalFlags) *cobra.Command { setupContainerCmd.Flags().BoolVar(&cmd.InjectGitCredentials, "inject-git-credentials", false, "If DevPod should inject git credentials during setup") setupContainerCmd.Flags().StringVar(&cmd.ContainerWorkspaceInfo, "container-workspace-info", "", "The container workspace info") setupContainerCmd.Flags().StringVar(&cmd.SetupInfo, "setup-info", "", "The container setup info") + setupContainerCmd.Flags().StringVar(&cmd.SetupInfo, "repository", "", "The repository to use as a remote cache") _ = setupContainerCmd.MarkFlagRequired("setup-info") return setupContainerCmd } diff --git a/examples/build/.devcontainer.json b/examples/build/.devcontainer.json new file mode 100644 index 000000000..422fec96c --- /dev/null +++ b/examples/build/.devcontainer.json @@ -0,0 +1,7 @@ +{ + "name": "My dev env", + "build": { + "context": ".", + "dockerfile": "./Dockerfile" + } +} diff --git a/examples/build/Dockerfile b/examples/build/Dockerfile new file mode 100644 index 000000000..96f591f5b --- /dev/null +++ b/examples/build/Dockerfile @@ -0,0 +1,32 @@ +FROM mcr.microsoft.com/devcontainers/go:1.22-bullseye + +ARG TARGETOS +ARG TARGETARCH + +# Install Node.js +RUN \ + --mount=type=cache,target=/var/cache/apt \ + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get update \ + && apt-get install -y --no-install-recommends nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Set environment variables for Rust +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH \ + RUST_VERSION=1.69.0 + +# Install Protobuf compiler +RUN \ + --mount=type=cache,target=/var/cache/apt \ + apt-get update \ + && apt-get install -y --no-install-recommends protobuf-compiler \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY app /app +COPY files /files + +RUN echo hello \ No newline at end of file diff --git a/examples/build/README.md b/examples/build/README.md new file mode 100644 index 000000000..29ea841ee --- /dev/null +++ b/examples/build/README.md @@ -0,0 +1,6 @@ +## Simple Example + +This folder holds a super simple devcontainer configuration. You can start this project via: +``` +devpod up ./examples/simple +``` diff --git a/examples/build/app/code b/examples/build/app/code new file mode 100644 index 000000000..e69de29bb diff --git a/examples/build/files/test b/examples/build/files/test new file mode 100644 index 000000000..30d74d258 --- /dev/null +++ b/examples/build/files/test @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 9355c4866..b718617f4 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" ) -var dockerlessImage = "ghcr.io/loft-sh/dockerless:0.1.4" +var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0" // "ghcr.io/loft-sh/dockerless:0.1.4" // "gcr.io/pascal-project-387807/dockerless:0.2.0" // const ( DevPodExtraEnvVar = "DEVPOD" From e2335f4e4944c7bc6d3901642d1c574e3b08a2bb Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 3 Sep 2024 10:13:01 +0100 Subject: [PATCH 02/26] POD-823: WIP implment cache for docker --- cmd/agent/workspace/build.go | 6 ++--- cmd/build.go | 6 +++-- cmd/up.go | 2 ++ .../clientimplementation/workspace_client.go | 3 +++ pkg/config/context.go | 6 +++++ pkg/devcontainer/build/options.go | 1 + pkg/devcontainer/buildkit/buildkit.go | 5 ++++ pkg/driver/docker/build.go | 24 +++++++++++++------ pkg/provider/workspace.go | 8 +++++-- 9 files changed, 47 insertions(+), 14 deletions(-) diff --git a/cmd/agent/workspace/build.go b/cmd/agent/workspace/build.go index e64982a10..8c3a9f614 100644 --- a/cmd/agent/workspace/build.go +++ b/cmd/agent/workspace/build.go @@ -81,9 +81,9 @@ func (cmd *BuildCmd) Run(ctx context.Context) error { for _, platform := range platforms { // build the image imageName, err := runner.Build(ctx, provider2.BuildOptions{ - CLIOptions: workspaceInfo.CLIOptions, - - Platform: platform, + CLIOptions: workspaceInfo.CLIOptions, + RegistryCache: workspaceInfo.RegistryCache, + Platform: platform, }) if err != nil { logger.Errorf("Error building image: %v", err) diff --git a/cmd/build.go b/cmd/build.go index e6eff8726..467664bcc 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -27,8 +27,9 @@ type BuildCmd struct { ProviderOptions []string - SkipDelete bool - Machine string + SkipDelete bool + Machine string + RegistryCache string } // NewBuildCmd creates a new command @@ -114,6 +115,7 @@ func NewBuildCmd(flags *flags.GlobalFlags) *cobra.Command { buildCmd.Flags().StringSliceVar(&cmd.Platform, "platform", []string{}, "Set target platform for build") buildCmd.Flags().BoolVar(&cmd.SkipPush, "skip-push", false, "If true will not push the image to the repository, useful for testing") buildCmd.Flags().Var(&cmd.GitCloneStrategy, "git-clone-strategy", "The git clone strategy DevPod uses to checkout git based workspaces. Can be full (default), blobless, treeless or shallow") + // buildCmd.Flags().StringVar(&cmd.RegistryCache, "registry-cache", "", "The registry to use as a build cache") // TESTING buildCmd.Flags().BoolVar(&cmd.ForceBuild, "force-build", false, "TESTING ONLY") diff --git a/cmd/up.go b/cmd/up.go index ba567fa72..0e80a785e 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -62,6 +62,7 @@ type UpCmd struct { DotfilesSource string DotfilesScript string + RegistryCache string } // NewUpCmd creates a new up command @@ -167,6 +168,7 @@ func NewUpCmd(flags *flags.GlobalFlags) *cobra.Command { upCmd.Flags().Var(&cmd.GitCloneStrategy, "git-clone-strategy", "The git clone strategy DevPod uses to checkout git based workspaces. Can be full (default), blobless, treeless or shallow") upCmd.Flags().StringVar(&cmd.GitSSHSigningKey, "git-ssh-signing-key", "", "The ssh key to use when signing git commits. Used to explicitly setup DevPod's ssh signature forwarding with given key. Should be same format as value of `git config user.signingkey`") upCmd.Flags().StringVar(&cmd.FallbackImage, "fallback-image", "", "The fallback image to use if no devcontainer configuration has been detected") + // upCmd.Flags().StringVar(&cmd.RegistryCache, "registry-cache", "", "The registry to use as a build cache") upCmd.Flags().BoolVar(&cmd.DisableDaemon, "disable-daemon", false, "If enabled, will not install a daemon into the target machine to track activity") upCmd.Flags().StringVar(&cmd.Source, "source", "", "Optional source for the workspace. E.g. git:https://github.com/my-org/my-repo") diff --git a/pkg/client/clientimplementation/workspace_client.go b/pkg/client/clientimplementation/workspace_client.go index 38bd7fcd5..e81deaf76 100644 --- a/pkg/client/clientimplementation/workspace_client.go +++ b/pkg/client/clientimplementation/workspace_client.go @@ -203,6 +203,9 @@ func (s *workspaceClient) agentInfo(cliOptions provider.CLIOptions) (string, *pr // Get the timeout from the context options agentInfo.InjectTimeout = config.ParseTimeOption(s.devPodConfig, config.ContextOptionAgentInjectTimeout) + // Set registry cache from context option + agentInfo.RegistryCache = s.devPodConfig.ContextOption(config.ContextOptionRegistryCache) + // marshal config out, err := json.Marshal(agentInfo) if err != nil { diff --git a/pkg/config/context.go b/pkg/config/context.go index a65e091d5..b8954c1d8 100644 --- a/pkg/config/context.go +++ b/pkg/config/context.go @@ -14,6 +14,7 @@ const ( ContextOptionSSHAgentForwarding = "SSH_AGENT_FORWARDING" ContextOptionSSHConfigPath = "SSH_CONFIG_PATH" ContextOptionAgentInjectTimeout = "AGENT_INJECT_TIMEOUT" + ContextOptionRegistryCache = "REGISTRY_CACHE" ) var ContextOptions = []ContextOption{ @@ -86,4 +87,9 @@ var ContextOptions = []ContextOption{ Description: "Specifies the timeout to inject the agent", Default: "20", }, + { + Name: ContextOptionRegistryCache, + Description: "Specifies the registry to use as a build cache", + Default: "gcr.io/pascal-project-387807/my-dev-env", // todo remove + }, } diff --git a/pkg/devcontainer/build/options.go b/pkg/devcontainer/build/options.go index 4a48ece66..b313ac437 100644 --- a/pkg/devcontainer/build/options.go +++ b/pkg/devcontainer/build/options.go @@ -8,6 +8,7 @@ type BuildOptions struct { Images []string CacheFrom []string + CacheTo []string Dockerfile string Context string diff --git a/pkg/devcontainer/buildkit/buildkit.go b/pkg/devcontainer/buildkit/buildkit.go index 958a019c1..850a76c8e 100644 --- a/pkg/devcontainer/buildkit/buildkit.go +++ b/pkg/devcontainer/buildkit/buildkit.go @@ -28,6 +28,10 @@ func Build(ctx context.Context, client *buildkit.Client, writer io.Writer, platf if err != nil { return err } + cacheTo, err := ParseCacheEntry(options.CacheTo) + if err != nil { + return err + } // is context stream? attachable := []session.Attachable{} @@ -42,6 +46,7 @@ func Build(ctx context.Context, client *buildkit.Client, writer io.Writer, platf }, Session: attachable, CacheImports: cacheFrom, + CacheExports: cacheTo, } // set options target diff --git a/pkg/driver/docker/build.go b/pkg/driver/docker/build.go index ad69c01ac..f7c02c8ed 100644 --- a/pkg/driver/docker/build.go +++ b/pkg/driver/docker/build.go @@ -57,7 +57,7 @@ func (d *dockerDriver) BuildDevContainer( } // get build options - buildOptions, err := CreateBuildOptions(dockerfilePath, dockerfileContent, parsedConfig, extendedBuildInfo, imageName, options.Repository, options.PrebuildRepositories, prebuildHash) + buildOptions, err := CreateBuildOptions(dockerfilePath, dockerfileContent, parsedConfig, extendedBuildInfo, imageName, options, prebuildHash) if err != nil { return nil, err } @@ -119,8 +119,7 @@ func CreateBuildOptions( parsedConfig *config.SubstitutedConfig, extendedBuildInfo *feature.ExtendedBuildInfo, imageName string, - pushRepository string, - prebuildRepositories []string, + options provider.BuildOptions, prebuildHash string, ) (*build.BuildOptions, error) { var err error @@ -155,10 +154,10 @@ func CreateBuildOptions( if imageName != "" { buildOptions.Images = append(buildOptions.Images, imageName) } - if pushRepository != "" { - buildOptions.Images = append(buildOptions.Images, pushRepository+":"+prebuildHash) + if options.Repository != "" { + buildOptions.Images = append(buildOptions.Images, options.Repository+":"+prebuildHash) } - for _, prebuildRepository := range prebuildRepositories { + for _, prebuildRepository := range options.PrebuildRepositories { buildOptions.Images = append(buildOptions.Images, prebuildRepository+":"+prebuildHash) } buildOptions.Context = config.GetContextPath(parsedConfig.Config) @@ -167,7 +166,15 @@ func CreateBuildOptions( if buildOptions.BuildArgs == nil { buildOptions.BuildArgs = map[string]string{} } - buildOptions.BuildArgs["BUILDKIT_INLINE_CACHE"] = "1" + + // define cache args + if options.RegistryCache != "" { + buildOptions.CacheFrom = []string{fmt.Sprintf("type=registry,ref=%s", options.RegistryCache)} + buildOptions.CacheTo = []string{fmt.Sprintf("type=registry,ref=%s,mode=max", options.RegistryCache)} + } else { + buildOptions.BuildArgs["BUILDKIT_INLINE_CACHE"] = "1" + } + return buildOptions, nil } @@ -303,6 +310,9 @@ func (d *dockerDriver) buildxBuild(ctx context.Context, writer io.Writer, platfo for _, cacheFrom := range options.CacheFrom { args = append(args, "--cache-from", cacheFrom) } + for _, cacheFrom := range options.CacheTo { + args = append(args, "--cache-to", cacheFrom) + } // add additional build cli options args = append(args, options.CliOpts...) diff --git a/pkg/provider/workspace.go b/pkg/provider/workspace.go index 4dbc13652..f0d164dbc 100644 --- a/pkg/provider/workspace.go +++ b/pkg/provider/workspace.go @@ -176,6 +176,9 @@ type AgentWorkspaceInfo struct { // InjectTimeout specifies how long to wait for the agent to be injected into the dev container InjectTimeout time.Duration `json:"injectTimeout,omitempty"` + + // RegistryCache defines the registry to use for caching builds + RegistryCache string `json:"registryCache,omitempty"` } type CLIOptions struct { @@ -215,8 +218,9 @@ type CLIOptions struct { type BuildOptions struct { CLIOptions - Platform string - NoBuild bool + Platform string + RegistryCache string + NoBuild bool } func (w WorkspaceSource) String() string { From ad06732f330a87528b6c1657c06202fff3628655 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 3 Sep 2024 13:33:12 +0100 Subject: [PATCH 03/26] POD-823: Run example and collect logs --- cmd/agent/workspace/up.go | 3 +- examples/build/.devcontainer.json | 3 + examples/build/.devpod-internal/0/NOTES.md | 7 + examples/build/.devpod-internal/0/README.md | 32 +++ .../0/devcontainer-feature.json | 27 ++ .../0/devcontainer-features-install.sh | 31 +++ .../0/devcontainer-features.env | 2 + examples/build/.devpod-internal/0/install.sh | 246 ++++++++++++++++++ .../.devpod-internal/Dockerfile-with-features | 55 ++++ .../devcontainer-features.builtin.env | 2 + examples/build/app/code2 | 1 + examples/build/files/test2 | 1 + pkg/devcontainer/run.go | 5 +- pkg/devcontainer/single.go | 3 +- pkg/driver/docker/build.go | 7 +- 15 files changed, 418 insertions(+), 7 deletions(-) create mode 100644 examples/build/.devpod-internal/0/NOTES.md create mode 100644 examples/build/.devpod-internal/0/README.md create mode 100644 examples/build/.devpod-internal/0/devcontainer-feature.json create mode 100644 examples/build/.devpod-internal/0/devcontainer-features-install.sh create mode 100644 examples/build/.devpod-internal/0/devcontainer-features.env create mode 100755 examples/build/.devpod-internal/0/install.sh create mode 100644 examples/build/.devpod-internal/Dockerfile-with-features create mode 100644 examples/build/.devpod-internal/devcontainer-features.builtin.env create mode 100644 examples/build/app/code2 create mode 100644 examples/build/files/test2 diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index c81504d45..639222418 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -126,7 +126,8 @@ func (cmd *UpCmd) devPodUp(ctx context.Context, workspaceInfo *provider2.AgentWo // start the devcontainer result, err := runner.Up(ctx, devcontainer.UpOptions{ - CLIOptions: workspaceInfo.CLIOptions, + CLIOptions: workspaceInfo.CLIOptions, + RegistryCache: workspaceInfo.RegistryCache, }, workspaceInfo.InjectTimeout) if err != nil { return nil, err diff --git a/examples/build/.devcontainer.json b/examples/build/.devcontainer.json index 422fec96c..33d87350a 100644 --- a/examples/build/.devcontainer.json +++ b/examples/build/.devcontainer.json @@ -3,5 +3,8 @@ "build": { "context": ".", "dockerfile": "./Dockerfile" + }, + "features":{ + "ghcr.io/devcontainers/features/github-cli:1": {} } } diff --git a/examples/build/.devpod-internal/0/NOTES.md b/examples/build/.devpod-internal/0/NOTES.md new file mode 100644 index 000000000..19fe92f31 --- /dev/null +++ b/examples/build/.devpod-internal/0/NOTES.md @@ -0,0 +1,7 @@ + + +## OS Support + +This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. + +`bash` is required to execute the `install.sh` script. diff --git a/examples/build/.devpod-internal/0/README.md b/examples/build/.devpod-internal/0/README.md new file mode 100644 index 000000000..07945081a --- /dev/null +++ b/examples/build/.devpod-internal/0/README.md @@ -0,0 +1,32 @@ + +# GitHub CLI (github-cli) + +Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies. + +## Example Usage + +```json +"features": { + "ghcr.io/devcontainers/features/github-cli:1": {} +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| version | Select version of the GitHub CLI, if not latest. | string | latest | +| installDirectlyFromGitHubRelease | - | boolean | true | + + + +## OS Support + +This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. + +`bash` is required to execute the `install.sh` script. + + +--- + +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/github-cli/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/examples/build/.devpod-internal/0/devcontainer-feature.json b/examples/build/.devpod-internal/0/devcontainer-feature.json new file mode 100644 index 000000000..9b1558ec0 --- /dev/null +++ b/examples/build/.devpod-internal/0/devcontainer-feature.json @@ -0,0 +1,27 @@ +{ + "id": "github-cli", + "version": "1.0.13", + "name": "GitHub CLI", + "documentationURL": "https://github.com/devcontainers/features/tree/main/src/github-cli", + "description": "Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.", + "options": { + "version": { + "type": "string", + "proposals": [ + "latest", + "none" + ], + "default": "latest", + "description": "Select version of the GitHub CLI, if not latest." + }, + "installDirectlyFromGitHubRelease": { + "type": "boolean", + "default": true + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils", + "ghcr.io/devcontainers/features/git" + ] +} + diff --git a/examples/build/.devpod-internal/0/devcontainer-features-install.sh b/examples/build/.devpod-internal/0/devcontainer-features-install.sh new file mode 100644 index 000000000..b6f2a86aa --- /dev/null +++ b/examples/build/.devpod-internal/0/devcontainer-features-install.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -e + +on_exit () { + [ $? -eq 0 ] && exit + echo 'ERROR: Feature "GitHub CLI" (ghcr.io/devcontainers/features/github-cli) failed to install! Look at the documentation at ${documentation} for help troubleshooting this error.' +} + +trap on_exit EXIT + +set -a +. ../devcontainer-features.builtin.env +. ./devcontainer-features.env +set +a + +echo =========================================================================== + +echo 'Feature : GitHub CLI' +echo 'Description : Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.' +echo 'Id : ghcr.io/devcontainers/features/github-cli' +echo 'Version : 1.0.13' +echo 'Documentation : https://github.com/devcontainers/features/tree/main/src/github-cli' +echo 'Options :' +echo ' INSTALLDIRECTLYFROMGITHUBRELEASE="true" + VERSION="latest"' +echo 'Environment :' +printenv +echo =========================================================================== + +chmod +x ./install.sh +./install.sh diff --git a/examples/build/.devpod-internal/0/devcontainer-features.env b/examples/build/.devpod-internal/0/devcontainer-features.env new file mode 100644 index 000000000..305445307 --- /dev/null +++ b/examples/build/.devpod-internal/0/devcontainer-features.env @@ -0,0 +1,2 @@ +INSTALLDIRECTLYFROMGITHUBRELEASE="true" +VERSION="latest" \ No newline at end of file diff --git a/examples/build/.devpod-internal/0/install.sh b/examples/build/.devpod-internal/0/install.sh new file mode 100755 index 000000000..18dae33ce --- /dev/null +++ b/examples/build/.devpod-internal/0/install.sh @@ -0,0 +1,246 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/github.md +# Maintainer: The VS Code and Codespaces Teams + +CLI_VERSION=${VERSION:-"latest"} +INSTALL_DIRECTLY_FROM_GITHUB_RELEASE=${INSTALLDIRECTLYFROMGITHUBRELEASE:-"true"} + +GITHUB_CLI_ARCHIVE_GPG_KEY=23F3D4EA75716059 + +set -e + +# Clean up +rm -rf /var/lib/apt/lists/* + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Get the list of GPG key servers that are reachable +get_gpg_key_servers() { + declare -A keyservers_curl_map=( + ["hkp://keyserver.ubuntu.com"]="http://keyserver.ubuntu.com:11371" + ["hkp://keyserver.ubuntu.com:80"]="http://keyserver.ubuntu.com" + ["hkps://keys.openpgp.org"]="https://keys.openpgp.org" + ["hkp://keyserver.pgp.com"]="http://keyserver.pgp.com:11371" + ) + + local curl_args="" + local keyserver_reachable=false # Flag to indicate if any keyserver is reachable + + if [ ! -z "${KEYSERVER_PROXY}" ]; then + curl_args="--proxy ${KEYSERVER_PROXY}" + fi + + for keyserver in "${!keyservers_curl_map[@]}"; do + local keyserver_curl_url="${keyservers_curl_map[${keyserver}]}" + if curl -s ${curl_args} --max-time 5 ${keyserver_curl_url} > /dev/null; then + echo "keyserver ${keyserver}" + keyserver_reachable=true + else + echo "(*) Keyserver ${keyserver} is not reachable." >&2 + fi + done + + if ! $keyserver_reachable; then + echo "(!) No keyserver is reachable." >&2 + exit 1 + fi +} + +# Import the specified key in a variable name passed in as +receive_gpg_keys() { + local keys=${!1} + local keyring_args="" + if [ ! -z "$2" ]; then + keyring_args="--no-default-keyring --keyring $2" + fi + + # Install curl + if ! type curl > /dev/null 2>&1; then + check_packages curl + fi + + # Use a temporary location for gpg keys to avoid polluting image + export GNUPGHOME="/tmp/tmp-gnupg" + mkdir -p ${GNUPGHOME} + chmod 700 ${GNUPGHOME} + echo -e "disable-ipv6\n$(get_gpg_key_servers)" > ${GNUPGHOME}/dirmngr.conf + # GPG key download sometimes fails for some reason and retrying fixes it. + local retry_count=0 + local gpg_ok="false" + set +e + until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ]; + do + echo "(*) Downloading GPG key..." + ( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true" + if [ "${gpg_ok}" != "true" ]; then + echo "(*) Failed getting key, retrying in 10s..." + (( retry_count++ )) + sleep 10s + fi + done + set -e + if [ "${gpg_ok}" = "false" ]; then + echo "(!) Failed to get gpg key." + exit 1 + fi +} + +apt_get_update() +{ + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update + apt-get -y install --no-install-recommends "$@" + fi +} + +# Figure out correct version of a three part version number is not passed +find_version_from_git_tags() { + local variable_name=$1 + local requested_version=${!variable_name} + if [ "${requested_version}" = "none" ]; then return; fi + local repository=$2 + local prefix=${3:-"tags/v"} + local separator=${4:-"."} + local last_part_optional=${5:-"false"} + if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then + local escaped_separator=${separator//./\\.} + local last_part + if [ "${last_part_optional}" = "true" ]; then + last_part="(${escaped_separator}[0-9]+)?" + else + last_part="${escaped_separator}[0-9]+" + fi + local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" + local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" + if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then + declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" + else + set +e + declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" + set -e + fi + fi + if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then + echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 + exit 1 + fi + echo "${variable_name}=${!variable_name}" +} + +# Use semver logic to decrement a version number then look for the closest match +find_prev_version_from_git_tags() { + local variable_name=$1 + local current_version=${!variable_name} + local repository=$2 + # Normally a "v" is used before the version number, but support alternate cases + local prefix=${3:-"tags/v"} + # Some repositories use "_" instead of "." for version number part separation, support that + local separator=${4:-"."} + # Some tools release versions that omit the last digit (e.g. go) + local last_part_optional=${5:-"false"} + # Some repositories may have tags that include a suffix (e.g. actions/node-versions) + local version_suffix_regex=$6 + # Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios. + set +e + major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')" + minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')" + breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')" + + if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then + ((major=major-1)) + declare -g ${variable_name}="${major}" + # Look for latest version from previous major release + find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}" + # Handle situations like Go's odd version pattern where "0" releases omit the last part + elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then + ((minor=minor-1)) + declare -g ${variable_name}="${major}.${minor}" + # Look for latest version from previous minor release + find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}" + else + ((breakfix=breakfix-1)) + if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then + declare -g ${variable_name}="${major}.${minor}" + else + declare -g ${variable_name}="${major}.${minor}.${breakfix}" + fi + fi + set -e +} + +# Fall back on direct download if no apt package exists +# Fetches .deb file to be installed with dpkg +install_deb_using_github() { + check_packages wget + arch=$(dpkg --print-architecture) + + find_version_from_git_tags CLI_VERSION https://github.com/cli/cli + cli_filename="gh_${CLI_VERSION}_linux_${arch}.deb" + + mkdir -p /tmp/ghcli + pushd /tmp/ghcli + wget https://github.com/cli/cli/releases/download/v${CLI_VERSION}/${cli_filename} + exit_code=$? + set -e + if [ "$exit_code" != "0" ]; then + # Handle situation where git tags are ahead of what was is available to actually download + echo "(!) github-cli version ${CLI_VERSION} failed to download. Attempting to fall back one version to retry..." + find_prev_version_from_git_tags CLI_VERSION https://github.com/cli/cli + wget https://github.com/cli/cli/releases/download/v${CLI_VERSION}/${cli_filename} + fi + + dpkg -i /tmp/ghcli/${cli_filename} + popd + rm -rf /tmp/ghcli +} + +export DEBIAN_FRONTEND=noninteractive + +# Install curl, apt-transport-https, curl, gpg, or dirmngr, git if missing +check_packages curl ca-certificates apt-transport-https dirmngr gnupg2 +if ! type git > /dev/null 2>&1; then + check_packages git +fi + +# Soft version matching +if [ "${CLI_VERSION}" != "latest" ] && [ "${CLI_VERSION}" != "lts" ] && [ "${CLI_VERSION}" != "stable" ]; then + find_version_from_git_tags CLI_VERSION "https://github.com/cli/cli" + version_suffix="=${CLI_VERSION}" +else + version_suffix="" +fi + +# Install the GitHub CLI +echo "Downloading github CLI..." + +if [ "${INSTALL_DIRECTLY_FROM_GITHUB_RELEASE}" = "true" ]; then + install_deb_using_github +else + # Import key safely (new method rather than deprecated apt-key approach) and install + . /etc/os-release + receive_gpg_keys GITHUB_CLI_ARCHIVE_GPG_KEY /usr/share/keyrings/githubcli-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list + apt-get update + apt-get -y install "gh${version_suffix}" + rm -rf "/tmp/gh/gnupg" + echo "Done!" +fi + +# Clean up +rm -rf /var/lib/apt/lists/* diff --git a/examples/build/.devpod-internal/Dockerfile-with-features b/examples/build/.devpod-internal/Dockerfile-with-features new file mode 100644 index 000000000..f662e26f8 --- /dev/null +++ b/examples/build/.devpod-internal/Dockerfile-with-features @@ -0,0 +1,55 @@ +# syntax=docker.io/docker/dockerfile:1.4 +ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder +FROM mcr.microsoft.com/devcontainers/go:1.22-bullseye AS dev_container_auto_added_stage_label + +ARG TARGETOS +ARG TARGETARCH + +# Install Node.js +RUN \ + --mount=type=cache,target=/var/cache/apt \ + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get update \ + && apt-get install -y --no-install-recommends nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Set environment variables for Rust +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH \ + RUST_VERSION=1.69.0 + +# Install Protobuf compiler +RUN \ + --mount=type=cache,target=/var/cache/apt \ + apt-get update \ + && apt-get install -y --no-install-recommends protobuf-compiler \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY app /app +COPY files /files + +RUN echo hello + +FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage + +USER root + +COPY ./.devpod-internal/ /tmp/build-features/ +RUN chmod -R 0755 /tmp/build-features && ls /tmp/build-features + +RUN \ +echo "_CONTAINER_USER_HOME=$(getent passwd root | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env && \ +echo "_REMOTE_USER_HOME=$(getent passwd vscode | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env + + +RUN cd /tmp/build-features/0 \ +&& chmod +x ./devcontainer-features-install.sh \ +&& ./devcontainer-features-install.sh + + + +ARG _DEV_CONTAINERS_IMAGE_USER=root +USER $_DEV_CONTAINERS_IMAGE_USER \ No newline at end of file diff --git a/examples/build/.devpod-internal/devcontainer-features.builtin.env b/examples/build/.devpod-internal/devcontainer-features.builtin.env new file mode 100644 index 000000000..8411d16b2 --- /dev/null +++ b/examples/build/.devpod-internal/devcontainer-features.builtin.env @@ -0,0 +1,2 @@ +_CONTAINER_USER=root +_REMOTE_USER=vscode diff --git a/examples/build/app/code2 b/examples/build/app/code2 new file mode 100644 index 000000000..9daeafb98 --- /dev/null +++ b/examples/build/app/code2 @@ -0,0 +1 @@ +test diff --git a/examples/build/files/test2 b/examples/build/files/test2 new file mode 100644 index 000000000..9daeafb98 --- /dev/null +++ b/examples/build/files/test2 @@ -0,0 +1 @@ +test diff --git a/pkg/devcontainer/run.go b/pkg/devcontainer/run.go index 3d9f02ab9..7671c1236 100644 --- a/pkg/devcontainer/run.go +++ b/pkg/devcontainer/run.go @@ -85,8 +85,9 @@ type runner struct { type UpOptions struct { provider2.CLIOptions - NoBuild bool - ForceBuild bool + NoBuild bool + ForceBuild bool + RegistryCache string } func (r *runner) Up(ctx context.Context, options UpOptions, timeout time.Duration) (*config.Result, error) { diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index b718617f4..8fe237104 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -86,7 +86,8 @@ func (r *runner) runSingleContainer( PrebuildRepositories: options.PrebuildRepositories, ForceDockerless: options.ForceDockerless, }, - NoBuild: options.NoBuild, + NoBuild: options.NoBuild, + RegistryCache: options.RegistryCache, }) if err != nil { return nil, errors.Wrap(err, "build image") diff --git a/pkg/driver/docker/build.go b/pkg/driver/docker/build.go index f7c02c8ed..9b463088d 100644 --- a/pkg/driver/docker/build.go +++ b/pkg/driver/docker/build.go @@ -61,6 +61,7 @@ func (d *dockerDriver) BuildDevContainer( if err != nil { return nil, err } + d.Log.Info("registry cache", options.RegistryCache) // build image writer := d.Log.Writer(logrus.InfoLevel, false) @@ -170,7 +171,7 @@ func CreateBuildOptions( // define cache args if options.RegistryCache != "" { buildOptions.CacheFrom = []string{fmt.Sprintf("type=registry,ref=%s", options.RegistryCache)} - buildOptions.CacheTo = []string{fmt.Sprintf("type=registry,ref=%s,mode=max", options.RegistryCache)} + buildOptions.CacheTo = []string{fmt.Sprintf("type=registry,ref=%s,mode=max,image-manifest=true", options.RegistryCache)} } else { buildOptions.BuildArgs["BUILDKIT_INLINE_CACHE"] = "1" } @@ -310,8 +311,8 @@ func (d *dockerDriver) buildxBuild(ctx context.Context, writer io.Writer, platfo for _, cacheFrom := range options.CacheFrom { args = append(args, "--cache-from", cacheFrom) } - for _, cacheFrom := range options.CacheTo { - args = append(args, "--cache-to", cacheFrom) + for _, cacheTo := range options.CacheTo { + args = append(args, "--cache-to", cacheTo) } // add additional build cli options From aa350eeb4392bd268a3eefbcd750c0109cea2a41 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 3 Sep 2024 15:48:27 +0100 Subject: [PATCH 04/26] POD-823: Remove devpod internal --- examples/build/.devpod-internal/0/NOTES.md | 7 - examples/build/.devpod-internal/0/README.md | 32 --- .../0/devcontainer-feature.json | 27 -- .../0/devcontainer-features-install.sh | 31 --- .../0/devcontainer-features.env | 2 - examples/build/.devpod-internal/0/install.sh | 246 ------------------ .../.devpod-internal/Dockerfile-with-features | 55 ---- .../devcontainer-features.builtin.env | 2 - 8 files changed, 402 deletions(-) delete mode 100644 examples/build/.devpod-internal/0/NOTES.md delete mode 100644 examples/build/.devpod-internal/0/README.md delete mode 100644 examples/build/.devpod-internal/0/devcontainer-feature.json delete mode 100644 examples/build/.devpod-internal/0/devcontainer-features-install.sh delete mode 100644 examples/build/.devpod-internal/0/devcontainer-features.env delete mode 100755 examples/build/.devpod-internal/0/install.sh delete mode 100644 examples/build/.devpod-internal/Dockerfile-with-features delete mode 100644 examples/build/.devpod-internal/devcontainer-features.builtin.env diff --git a/examples/build/.devpod-internal/0/NOTES.md b/examples/build/.devpod-internal/0/NOTES.md deleted file mode 100644 index 19fe92f31..000000000 --- a/examples/build/.devpod-internal/0/NOTES.md +++ /dev/null @@ -1,7 +0,0 @@ - - -## OS Support - -This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. - -`bash` is required to execute the `install.sh` script. diff --git a/examples/build/.devpod-internal/0/README.md b/examples/build/.devpod-internal/0/README.md deleted file mode 100644 index 07945081a..000000000 --- a/examples/build/.devpod-internal/0/README.md +++ /dev/null @@ -1,32 +0,0 @@ - -# GitHub CLI (github-cli) - -Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies. - -## Example Usage - -```json -"features": { - "ghcr.io/devcontainers/features/github-cli:1": {} -} -``` - -## Options - -| Options Id | Description | Type | Default Value | -|-----|-----|-----|-----| -| version | Select version of the GitHub CLI, if not latest. | string | latest | -| installDirectlyFromGitHubRelease | - | boolean | true | - - - -## OS Support - -This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. - -`bash` is required to execute the `install.sh` script. - - ---- - -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/github-cli/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/examples/build/.devpod-internal/0/devcontainer-feature.json b/examples/build/.devpod-internal/0/devcontainer-feature.json deleted file mode 100644 index 9b1558ec0..000000000 --- a/examples/build/.devpod-internal/0/devcontainer-feature.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "id": "github-cli", - "version": "1.0.13", - "name": "GitHub CLI", - "documentationURL": "https://github.com/devcontainers/features/tree/main/src/github-cli", - "description": "Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.", - "options": { - "version": { - "type": "string", - "proposals": [ - "latest", - "none" - ], - "default": "latest", - "description": "Select version of the GitHub CLI, if not latest." - }, - "installDirectlyFromGitHubRelease": { - "type": "boolean", - "default": true - } - }, - "installsAfter": [ - "ghcr.io/devcontainers/features/common-utils", - "ghcr.io/devcontainers/features/git" - ] -} - diff --git a/examples/build/.devpod-internal/0/devcontainer-features-install.sh b/examples/build/.devpod-internal/0/devcontainer-features-install.sh deleted file mode 100644 index b6f2a86aa..000000000 --- a/examples/build/.devpod-internal/0/devcontainer-features-install.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -set -e - -on_exit () { - [ $? -eq 0 ] && exit - echo 'ERROR: Feature "GitHub CLI" (ghcr.io/devcontainers/features/github-cli) failed to install! Look at the documentation at ${documentation} for help troubleshooting this error.' -} - -trap on_exit EXIT - -set -a -. ../devcontainer-features.builtin.env -. ./devcontainer-features.env -set +a - -echo =========================================================================== - -echo 'Feature : GitHub CLI' -echo 'Description : Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.' -echo 'Id : ghcr.io/devcontainers/features/github-cli' -echo 'Version : 1.0.13' -echo 'Documentation : https://github.com/devcontainers/features/tree/main/src/github-cli' -echo 'Options :' -echo ' INSTALLDIRECTLYFROMGITHUBRELEASE="true" - VERSION="latest"' -echo 'Environment :' -printenv -echo =========================================================================== - -chmod +x ./install.sh -./install.sh diff --git a/examples/build/.devpod-internal/0/devcontainer-features.env b/examples/build/.devpod-internal/0/devcontainer-features.env deleted file mode 100644 index 305445307..000000000 --- a/examples/build/.devpod-internal/0/devcontainer-features.env +++ /dev/null @@ -1,2 +0,0 @@ -INSTALLDIRECTLYFROMGITHUBRELEASE="true" -VERSION="latest" \ No newline at end of file diff --git a/examples/build/.devpod-internal/0/install.sh b/examples/build/.devpod-internal/0/install.sh deleted file mode 100755 index 18dae33ce..000000000 --- a/examples/build/.devpod-internal/0/install.sh +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/env bash -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- -# -# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/github.md -# Maintainer: The VS Code and Codespaces Teams - -CLI_VERSION=${VERSION:-"latest"} -INSTALL_DIRECTLY_FROM_GITHUB_RELEASE=${INSTALLDIRECTLYFROMGITHUBRELEASE:-"true"} - -GITHUB_CLI_ARCHIVE_GPG_KEY=23F3D4EA75716059 - -set -e - -# Clean up -rm -rf /var/lib/apt/lists/* - -if [ "$(id -u)" -ne 0 ]; then - echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' - exit 1 -fi - -# Get the list of GPG key servers that are reachable -get_gpg_key_servers() { - declare -A keyservers_curl_map=( - ["hkp://keyserver.ubuntu.com"]="http://keyserver.ubuntu.com:11371" - ["hkp://keyserver.ubuntu.com:80"]="http://keyserver.ubuntu.com" - ["hkps://keys.openpgp.org"]="https://keys.openpgp.org" - ["hkp://keyserver.pgp.com"]="http://keyserver.pgp.com:11371" - ) - - local curl_args="" - local keyserver_reachable=false # Flag to indicate if any keyserver is reachable - - if [ ! -z "${KEYSERVER_PROXY}" ]; then - curl_args="--proxy ${KEYSERVER_PROXY}" - fi - - for keyserver in "${!keyservers_curl_map[@]}"; do - local keyserver_curl_url="${keyservers_curl_map[${keyserver}]}" - if curl -s ${curl_args} --max-time 5 ${keyserver_curl_url} > /dev/null; then - echo "keyserver ${keyserver}" - keyserver_reachable=true - else - echo "(*) Keyserver ${keyserver} is not reachable." >&2 - fi - done - - if ! $keyserver_reachable; then - echo "(!) No keyserver is reachable." >&2 - exit 1 - fi -} - -# Import the specified key in a variable name passed in as -receive_gpg_keys() { - local keys=${!1} - local keyring_args="" - if [ ! -z "$2" ]; then - keyring_args="--no-default-keyring --keyring $2" - fi - - # Install curl - if ! type curl > /dev/null 2>&1; then - check_packages curl - fi - - # Use a temporary location for gpg keys to avoid polluting image - export GNUPGHOME="/tmp/tmp-gnupg" - mkdir -p ${GNUPGHOME} - chmod 700 ${GNUPGHOME} - echo -e "disable-ipv6\n$(get_gpg_key_servers)" > ${GNUPGHOME}/dirmngr.conf - # GPG key download sometimes fails for some reason and retrying fixes it. - local retry_count=0 - local gpg_ok="false" - set +e - until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ]; - do - echo "(*) Downloading GPG key..." - ( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true" - if [ "${gpg_ok}" != "true" ]; then - echo "(*) Failed getting key, retrying in 10s..." - (( retry_count++ )) - sleep 10s - fi - done - set -e - if [ "${gpg_ok}" = "false" ]; then - echo "(!) Failed to get gpg key." - exit 1 - fi -} - -apt_get_update() -{ - if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then - echo "Running apt-get update..." - apt-get update -y - fi -} - -# Checks if packages are installed and installs them if not -check_packages() { - if ! dpkg -s "$@" > /dev/null 2>&1; then - apt_get_update - apt-get -y install --no-install-recommends "$@" - fi -} - -# Figure out correct version of a three part version number is not passed -find_version_from_git_tags() { - local variable_name=$1 - local requested_version=${!variable_name} - if [ "${requested_version}" = "none" ]; then return; fi - local repository=$2 - local prefix=${3:-"tags/v"} - local separator=${4:-"."} - local last_part_optional=${5:-"false"} - if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then - local escaped_separator=${separator//./\\.} - local last_part - if [ "${last_part_optional}" = "true" ]; then - last_part="(${escaped_separator}[0-9]+)?" - else - last_part="${escaped_separator}[0-9]+" - fi - local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" - local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" - if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then - declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" - else - set +e - declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" - set -e - fi - fi - if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then - echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 - exit 1 - fi - echo "${variable_name}=${!variable_name}" -} - -# Use semver logic to decrement a version number then look for the closest match -find_prev_version_from_git_tags() { - local variable_name=$1 - local current_version=${!variable_name} - local repository=$2 - # Normally a "v" is used before the version number, but support alternate cases - local prefix=${3:-"tags/v"} - # Some repositories use "_" instead of "." for version number part separation, support that - local separator=${4:-"."} - # Some tools release versions that omit the last digit (e.g. go) - local last_part_optional=${5:-"false"} - # Some repositories may have tags that include a suffix (e.g. actions/node-versions) - local version_suffix_regex=$6 - # Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios. - set +e - major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')" - minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')" - breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')" - - if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then - ((major=major-1)) - declare -g ${variable_name}="${major}" - # Look for latest version from previous major release - find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}" - # Handle situations like Go's odd version pattern where "0" releases omit the last part - elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then - ((minor=minor-1)) - declare -g ${variable_name}="${major}.${minor}" - # Look for latest version from previous minor release - find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}" - else - ((breakfix=breakfix-1)) - if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then - declare -g ${variable_name}="${major}.${minor}" - else - declare -g ${variable_name}="${major}.${minor}.${breakfix}" - fi - fi - set -e -} - -# Fall back on direct download if no apt package exists -# Fetches .deb file to be installed with dpkg -install_deb_using_github() { - check_packages wget - arch=$(dpkg --print-architecture) - - find_version_from_git_tags CLI_VERSION https://github.com/cli/cli - cli_filename="gh_${CLI_VERSION}_linux_${arch}.deb" - - mkdir -p /tmp/ghcli - pushd /tmp/ghcli - wget https://github.com/cli/cli/releases/download/v${CLI_VERSION}/${cli_filename} - exit_code=$? - set -e - if [ "$exit_code" != "0" ]; then - # Handle situation where git tags are ahead of what was is available to actually download - echo "(!) github-cli version ${CLI_VERSION} failed to download. Attempting to fall back one version to retry..." - find_prev_version_from_git_tags CLI_VERSION https://github.com/cli/cli - wget https://github.com/cli/cli/releases/download/v${CLI_VERSION}/${cli_filename} - fi - - dpkg -i /tmp/ghcli/${cli_filename} - popd - rm -rf /tmp/ghcli -} - -export DEBIAN_FRONTEND=noninteractive - -# Install curl, apt-transport-https, curl, gpg, or dirmngr, git if missing -check_packages curl ca-certificates apt-transport-https dirmngr gnupg2 -if ! type git > /dev/null 2>&1; then - check_packages git -fi - -# Soft version matching -if [ "${CLI_VERSION}" != "latest" ] && [ "${CLI_VERSION}" != "lts" ] && [ "${CLI_VERSION}" != "stable" ]; then - find_version_from_git_tags CLI_VERSION "https://github.com/cli/cli" - version_suffix="=${CLI_VERSION}" -else - version_suffix="" -fi - -# Install the GitHub CLI -echo "Downloading github CLI..." - -if [ "${INSTALL_DIRECTLY_FROM_GITHUB_RELEASE}" = "true" ]; then - install_deb_using_github -else - # Import key safely (new method rather than deprecated apt-key approach) and install - . /etc/os-release - receive_gpg_keys GITHUB_CLI_ARCHIVE_GPG_KEY /usr/share/keyrings/githubcli-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list - apt-get update - apt-get -y install "gh${version_suffix}" - rm -rf "/tmp/gh/gnupg" - echo "Done!" -fi - -# Clean up -rm -rf /var/lib/apt/lists/* diff --git a/examples/build/.devpod-internal/Dockerfile-with-features b/examples/build/.devpod-internal/Dockerfile-with-features deleted file mode 100644 index f662e26f8..000000000 --- a/examples/build/.devpod-internal/Dockerfile-with-features +++ /dev/null @@ -1,55 +0,0 @@ -# syntax=docker.io/docker/dockerfile:1.4 -ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder -FROM mcr.microsoft.com/devcontainers/go:1.22-bullseye AS dev_container_auto_added_stage_label - -ARG TARGETOS -ARG TARGETARCH - -# Install Node.js -RUN \ - --mount=type=cache,target=/var/cache/apt \ - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ - && apt-get update \ - && apt-get install -y --no-install-recommends nodejs \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Set environment variables for Rust -ENV RUSTUP_HOME=/usr/local/rustup \ - CARGO_HOME=/usr/local/cargo \ - PATH=/usr/local/cargo/bin:$PATH \ - RUST_VERSION=1.69.0 - -# Install Protobuf compiler -RUN \ - --mount=type=cache,target=/var/cache/apt \ - apt-get update \ - && apt-get install -y --no-install-recommends protobuf-compiler \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -COPY app /app -COPY files /files - -RUN echo hello - -FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage - -USER root - -COPY ./.devpod-internal/ /tmp/build-features/ -RUN chmod -R 0755 /tmp/build-features && ls /tmp/build-features - -RUN \ -echo "_CONTAINER_USER_HOME=$(getent passwd root | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env && \ -echo "_REMOTE_USER_HOME=$(getent passwd vscode | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env - - -RUN cd /tmp/build-features/0 \ -&& chmod +x ./devcontainer-features-install.sh \ -&& ./devcontainer-features-install.sh - - - -ARG _DEV_CONTAINERS_IMAGE_USER=root -USER $_DEV_CONTAINERS_IMAGE_USER \ No newline at end of file diff --git a/examples/build/.devpod-internal/devcontainer-features.builtin.env b/examples/build/.devpod-internal/devcontainer-features.builtin.env deleted file mode 100644 index 8411d16b2..000000000 --- a/examples/build/.devpod-internal/devcontainer-features.builtin.env +++ /dev/null @@ -1,2 +0,0 @@ -_CONTAINER_USER=root -_REMOTE_USER=vscode From 02968d6078c7fe3a934aacc3a8252b9c1d794033 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 3 Sep 2024 15:52:17 +0100 Subject: [PATCH 05/26] POD-823: Cleanup --- cmd/agent/container/setup.go | 2 -- cmd/build.go | 6 +++--- cmd/up.go | 2 +- examples/build/README.md | 6 +++--- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cmd/agent/container/setup.go b/cmd/agent/container/setup.go index ee309932a..3060b00ad 100644 --- a/cmd/agent/container/setup.go +++ b/cmd/agent/container/setup.go @@ -51,7 +51,6 @@ type SetupContainerCmd struct { InjectGitCredentials bool ContainerWorkspaceInfo string SetupInfo string - Repo string } // NewSetupContainerCmd creates a new command @@ -72,7 +71,6 @@ func NewSetupContainerCmd(flags *flags.GlobalFlags) *cobra.Command { setupContainerCmd.Flags().BoolVar(&cmd.InjectGitCredentials, "inject-git-credentials", false, "If DevPod should inject git credentials during setup") setupContainerCmd.Flags().StringVar(&cmd.ContainerWorkspaceInfo, "container-workspace-info", "", "The container workspace info") setupContainerCmd.Flags().StringVar(&cmd.SetupInfo, "setup-info", "", "The container setup info") - setupContainerCmd.Flags().StringVar(&cmd.SetupInfo, "repository", "", "The repository to use as a remote cache") _ = setupContainerCmd.MarkFlagRequired("setup-info") return setupContainerCmd } diff --git a/cmd/build.go b/cmd/build.go index 467664bcc..5f2ca3c30 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -27,9 +27,9 @@ type BuildCmd struct { ProviderOptions []string - SkipDelete bool - Machine string - RegistryCache string + SkipDelete bool + Machine string + // RegistryCache string } // NewBuildCmd creates a new command diff --git a/cmd/up.go b/cmd/up.go index 0e80a785e..99a133f1e 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -62,7 +62,7 @@ type UpCmd struct { DotfilesSource string DotfilesScript string - RegistryCache string + // RegistryCache string } // NewUpCmd creates a new up command diff --git a/examples/build/README.md b/examples/build/README.md index 29ea841ee..f615ac901 100644 --- a/examples/build/README.md +++ b/examples/build/README.md @@ -1,6 +1,6 @@ -## Simple Example +## Build Example -This folder holds a super simple devcontainer configuration. You can start this project via: +This folder holds a super simple devcontainer configuration that builds a local Dockerfile with a devcontainer feature. You can start this project via: ``` -devpod up ./examples/simple +devpod up ./examples/build ``` From bacb6da2dea29beb7ac0e07b1046a8941674ea8b Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Wed, 4 Sep 2024 10:33:05 +0100 Subject: [PATCH 06/26] POD-823: Configure docker daemon --- cmd/agent/workspace/up.go | 137 ++++++++++++++++++++++++++++-- examples/build/.devcontainer.json | 10 ++- examples/build/Dockerfile | 2 + 3 files changed, 142 insertions(+), 7 deletions(-) diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index 639222418..4ab868b68 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -2,6 +2,7 @@ package workspace import ( "context" + "encoding/base64" "encoding/json" "fmt" "os" @@ -23,6 +24,8 @@ import ( "github.com/loft-sh/devpod/pkg/devcontainer/crane" "github.com/loft-sh/devpod/pkg/dockercredentials" "github.com/loft-sh/devpod/pkg/extract" + "github.com/loft-sh/devpod/pkg/git" + "github.com/loft-sh/devpod/pkg/gitcredentials" provider2 "github.com/loft-sh/devpod/pkg/provider" "github.com/loft-sh/devpod/scripts" "github.com/loft-sh/log" @@ -207,13 +210,14 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo } // install docker in background - errChan := make(chan error, 1) + errChan := make(chan error, 2) go func() { if !workspaceInfo.Agent.IsDockerDriver() || workspaceInfo.Agent.Docker.Install == "false" { errChan <- nil } else { errChan <- installDocker(logger) } + errChan <- configureDockerDaemon(ctx, logger) }() // prepare workspace @@ -235,6 +239,11 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo if err != nil { return nil, nil, "", errors.Wrap(err, "install docker") } + // wait until daemon is configured + err = <-errChan + if err != nil { + return nil, nil, "", errors.Wrap(err, "configure docker") + } return tunnelClient, logger, dockerCredentialsDir, nil } @@ -411,7 +420,101 @@ func prepareImage(workspaceDir, image string) error { return nil } -func installDocker(log log.Logger) error { +func cloneRepository(ctx context.Context, workspaceInfo *provider2.AgentWorkspaceInfo, helper string, log log.Logger) error { + s := workspaceInfo.Workspace.Source + log.Info("Clone repository") + log.Infof("URL: %s\n", s.GitRepository) + if s.GitBranch != "" { + log.Infof("Branch: %s\n", s.GitBranch) + } + if s.GitCommit != "" { + log.Infof("Commit: %s\n", s.GitCommit) + } + if s.GitSubPath != "" { + log.Infof("Subpath: %s\n", s.GitSubPath) + } + if s.GitPRReference != "" { + log.Infof("PR: %s\n", s.GitPRReference) + } + + workspaceDir := workspaceInfo.ContentFolder + // remove the credential helper or otherwise we will receive strange errors within the container + defer func() { + if helper != "" { + if err := gitcredentials.RemoveHelperFromPath(gitcredentials.GetLocalGitConfigPath(workspaceDir)); err != nil { + log.Errorf("Remove git credential helper: %v", err) + } + } + }() + + // check if command exists + if !command.Exists("git") { + local, _ := workspaceInfo.Agent.Local.Bool() + if local { + return fmt.Errorf("seems like git isn't installed on your system. Please make sure to install git and make it available in the PATH") + } + if err := git.InstallBinary(log); err != nil { + return err + } + } + + // setup private ssh key if passed in + extraEnv := []string{} + if workspaceInfo.CLIOptions.SSHKey != "" { + sshExtraEnv, err := setupSSHKey(workspaceInfo.CLIOptions.SSHKey, workspaceInfo.Agent.Path) + if err != nil { + return err + } + extraEnv = append(extraEnv, sshExtraEnv...) + } + + // run git command + cloner := git.NewCloner(workspaceInfo.CLIOptions.GitCloneStrategy) + gitInfo := git.NewGitInfo(s.GitRepository, s.GitBranch, s.GitCommit, s.GitPRReference, s.GitSubPath) + err := git.CloneRepositoryWithEnv(ctx, gitInfo, extraEnv, workspaceDir, helper, cloner, log) + if err != nil { + return fmt.Errorf("clone repository: %w", err) + } + + log.Done("Successfully cloned repository") + + return nil +} + +func setupSSHKey(key string, agentPath string) ([]string, error) { + keyFile, err := os.CreateTemp("", "") + if err != nil { + return nil, err + } + defer os.Remove(keyFile.Name()) + defer keyFile.Close() + + if err := writeSSHKey(keyFile, key); err != nil { + return nil, err + } + + if err := os.Chmod(keyFile.Name(), 0o400); err != nil { + return nil, err + } + + env := []string{"GIT_TERMINAL_PROMPT=0"} + gitSSHCmd := []string{agentPath, "helper", "ssh-git-clone", "--key-file=" + keyFile.Name()} + env = append(env, "GIT_SSH_COMMAND="+command.Quote(gitSSHCmd)) + + return env, nil +} + +func writeSSHKey(key *os.File, sshKey string) error { + data, err := base64.StdEncoding.DecodeString(sshKey) + if err != nil { + return err + } + + _, err = key.WriteString(string(data)) + return err +} + +func installDocker(log log.Logger) (err error) { if !command.Exists("docker") { writer := log.Writer(logrus.InfoLevel, false) defer writer.Close() @@ -421,11 +524,33 @@ func installDocker(log log.Logger) error { shellCommand := exec.Command("sh", "-c", scripts.InstallDocker) shellCommand.Stdout = writer shellCommand.Stderr = writer - err := shellCommand.Run() - if err != nil { + err = shellCommand.Run() + } + return err +} + +func configureDockerDaemon(ctx context.Context, log log.Logger) (err error) { + log.Info("Configuring docker daemon ...") + // Enable image snapshotter in the dameon + var daemonConfig = []byte(`{ + "features": { + "containerd-snapshotter": true + } + }`) + // Check rootless docker + homeDir, err := os.UserHomeDir() + if err != nil { + return err + } + if _, err = os.Stat(fmt.Sprintf("%s/.config/docker", homeDir)); !errors.Is(err, os.ErrNotExist) { + err = os.WriteFile(fmt.Sprintf("%s/.config/docker/daemon.json", homeDir), daemonConfig, 0644) + } + // otherwise assume default + if err != nil { + if err = os.WriteFile("/etc/docker/daemon.json", daemonConfig, 0644); err != nil { return err } } - - return nil + // reload docker daemon + return exec.CommandContext(ctx, "pkill", "-HUP", "dockerd").Run() } diff --git a/examples/build/.devcontainer.json b/examples/build/.devcontainer.json index 33d87350a..d3ba2dc87 100644 --- a/examples/build/.devcontainer.json +++ b/examples/build/.devcontainer.json @@ -5,6 +5,14 @@ "dockerfile": "./Dockerfile" }, "features":{ - "ghcr.io/devcontainers/features/github-cli:1": {} + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "configureZshAsDefaultShell": true, + "installOhMyZsh": true, + "installOhMyZshConfig": true, + "upgradePackages": true, + "username": "loft" + } } } diff --git a/examples/build/Dockerfile b/examples/build/Dockerfile index 96f591f5b..e8b8604eb 100644 --- a/examples/build/Dockerfile +++ b/examples/build/Dockerfile @@ -29,4 +29,6 @@ RUN \ COPY app /app COPY files /files +RUN echo test + RUN echo hello \ No newline at end of file From 5b16fbfce6830199660111395ba263eb8880a9a2 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Wed, 4 Sep 2024 13:16:04 +0100 Subject: [PATCH 07/26] POD-823: Append registry cache to build info# --- cmd/agent/container/setup.go | 4 ++++ cmd/agent/workspace/up.go | 8 +++++++- examples/build/.devcontainer.json | 3 +++ examples/build/app/code | 0 examples/build/app/code2 | 1 - pkg/devcontainer/build.go | 7 ++++++- pkg/devcontainer/config/build.go | 1 + pkg/devcontainer/single.go | 2 +- pkg/driver/docker/build.go | 2 ++ pkg/options/resolve.go | 1 + pkg/provider/provider.go | 3 +++ 11 files changed, 28 insertions(+), 4 deletions(-) delete mode 100644 examples/build/app/code delete mode 100644 examples/build/app/code2 diff --git a/cmd/agent/container/setup.go b/cmd/agent/container/setup.go index 3060b00ad..f6c17eb9f 100644 --- a/cmd/agent/container/setup.go +++ b/cmd/agent/container/setup.go @@ -302,6 +302,10 @@ func dockerlessBuild( args = append(args, parseIgnorePaths(dockerlessOptions.IgnorePaths)...) args = append(args, "--build-arg", "TARGETOS="+runtime.GOOS) args = append(args, "--build-arg", "TARGETARCH="+runtime.GOARCH) + if dockerlessOptions.RegistryCache != "" { + log.Info("Appending registry cache to dockerless build arguments ", dockerlessOptions.RegistryCache) + args = append(args, "--registry-cache", dockerlessOptions.RegistryCache) + } // ignore mounts args = append(args, "--ignore-path", setupInfo.SubstitutionContext.ContainerWorkspaceFolder) diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index 4ab868b68..5bb6b0799 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -217,7 +217,13 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo } else { errChan <- installDocker(logger) } - errChan <- configureDockerDaemon(ctx, logger) + if workspaceInfo.Machine != nil { + errChan <- configureDockerDaemon(ctx, logger) + } else { + logger.Warn("Using existing docker installation, please ensure containerd snapshotter is enabled in the docker daemon") + logger.Warn("See https://docs.docker.com/engine/storage/containerd/") + errChan <- nil + } }() // prepare workspace diff --git a/examples/build/.devcontainer.json b/examples/build/.devcontainer.json index d3ba2dc87..1c5482df6 100644 --- a/examples/build/.devcontainer.json +++ b/examples/build/.devcontainer.json @@ -13,6 +13,9 @@ "installOhMyZshConfig": true, "upgradePackages": true, "username": "loft" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "latest" } } } diff --git a/examples/build/app/code b/examples/build/app/code deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/build/app/code2 b/examples/build/app/code2 deleted file mode 100644 index 9daeafb98..000000000 --- a/examples/build/app/code2 +++ /dev/null @@ -1 +0,0 @@ -test diff --git a/pkg/devcontainer/build.go b/pkg/devcontainer/build.go index 4c01bb484..da0896e30 100644 --- a/pkg/devcontainer/build.go +++ b/pkg/devcontainer/build.go @@ -103,6 +103,7 @@ func (r *runner) build( ImageMetadata: imageMetadata, ImageName: overrideBuildImageName, PrebuildHash: imageTag, + RegistryCache: options.RegistryCache, }, nil } @@ -133,6 +134,7 @@ func (r *runner) extendImage( ImageDetails: imageBuildInfo.ImageDetails, ImageMetadata: extendedBuildInfo.MetadataConfig, ImageName: imageBase, + RegistryCache: options.RegistryCache, }, nil } @@ -319,6 +321,7 @@ func (r *runner) buildImage( ImageMetadata: extendedBuildInfo.MetadataConfig, ImageName: prebuildImage, PrebuildHash: prebuildHash, + RegistryCache: options.RegistryCache, }, nil } else if err != nil { r.Log.Debugf("Error trying to find prebuild image %s: %v", prebuildImage, err) @@ -333,7 +336,7 @@ func (r *runner) buildImage( return nil, fmt.Errorf("cannot build devcontainer because driver is non-docker and dockerless fallback is disabled") } - return dockerlessFallback(r.LocalWorkspaceFolder, substitutionContext.ContainerWorkspaceFolder, parsedConfig, buildInfo, extendedBuildInfo, dockerfileContent) + return dockerlessFallback(r.LocalWorkspaceFolder, substitutionContext.ContainerWorkspaceFolder, parsedConfig, buildInfo, extendedBuildInfo, dockerfileContent, options) } return dockerDriver.BuildDevContainer(ctx, prebuildHash, parsedConfig, extendedBuildInfo, dockerfilePath, dockerfileContent, r.LocalWorkspaceFolder, options) @@ -346,6 +349,7 @@ func dockerlessFallback( buildInfo *config.ImageBuildInfo, extendedBuildInfo *feature.ExtendedBuildInfo, dockerfileContent string, + options provider.BuildOptions, ) (*config.BuildInfo, error) { contextPath := config.GetContextPath(parsedConfig.Config) devPodInternalFolder := filepath.Join(contextPath, config.DevPodContextFeatureFolder) @@ -380,6 +384,7 @@ func dockerlessFallback( User: buildInfo.User, }, + RegistryCache: options.RegistryCache, }, nil } diff --git a/pkg/devcontainer/config/build.go b/pkg/devcontainer/config/build.go index 69e59fdbf..89a7521ca 100644 --- a/pkg/devcontainer/config/build.go +++ b/pkg/devcontainer/config/build.go @@ -21,6 +21,7 @@ type BuildInfo struct { ImageMetadata *ImageMetadataConfig ImageName string PrebuildHash string + RegistryCache string Dockerless *BuildInfoDockerless } diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 8fe237104..2335f3f30 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" ) -var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0" // "ghcr.io/loft-sh/dockerless:0.1.4" // "gcr.io/pascal-project-387807/dockerless:0.2.0" // +var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0-alpha-7" // "ghcr.io/loft-sh/dockerless:0.1.4" const ( DevPodExtraEnvVar = "DEVPOD" diff --git a/pkg/driver/docker/build.go b/pkg/driver/docker/build.go index 9b463088d..457cac299 100644 --- a/pkg/driver/docker/build.go +++ b/pkg/driver/docker/build.go @@ -45,6 +45,7 @@ func (d *dockerDriver) BuildDevContainer( ImageMetadata: extendedBuildInfo.MetadataConfig, ImageName: imageName, PrebuildHash: prebuildHash, + RegistryCache: options.RegistryCache, }, nil } else if err != nil { d.Log.Debugf("Error trying to find local image %s: %v", imageName, err) @@ -112,6 +113,7 @@ func (d *dockerDriver) BuildDevContainer( ImageMetadata: extendedBuildInfo.MetadataConfig, ImageName: imageName, PrebuildHash: prebuildHash, + RegistryCache: options.RegistryCache, }, nil } diff --git a/pkg/options/resolve.go b/pkg/options/resolve.go index 15e778a44..a399cdc8c 100644 --- a/pkg/options/resolve.go +++ b/pkg/options/resolve.go @@ -218,6 +218,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.RegistryCache = devConfig.ContextOption(config.ContextOptionRegistryCache) agentConfig.Driver = resolver.ResolveDefaultValue(agentConfig.Driver, options) agentConfig.Local = types.StrBool(resolver.ResolveDefaultValue(string(agentConfig.Local), options)) agentConfig.Docker.Path = resolver.ResolveDefaultValue(agentConfig.Docker.Path, options) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index bc2b9ff1d..dd669f8cb 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -133,6 +133,9 @@ type ProviderDockerlessOptions struct { // IgnorePaths are additional ignore paths that should be ignored during deletion IgnorePaths string `json:"ignorePaths,omitempty"` + // Registry to use as remote cache + RegistryCache string `json:"registryCache,omitempty"` + // DisableDockerCredentials prevents docker credentials from getting injected DisableDockerCredentials types.StrBool `json:"disableDockerCredentials,omitempty"` } From 590fde7669cc05deec424217631fa5e416d1eb0e Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Wed, 4 Sep 2024 16:04:23 +0100 Subject: [PATCH 08/26] POD-823: Test kaniko build times --- cmd/agent/workspace/up.go | 3 +-- examples/build/.devcontainer.json | 14 -------------- examples/build/Dockerfile | 1 + examples/build/app/code10 | 1 + examples/build/app/test | 0 5 files changed, 3 insertions(+), 16 deletions(-) create mode 100644 examples/build/app/code10 create mode 100644 examples/build/app/test diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index 5bb6b0799..054da369b 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -217,11 +217,10 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo } else { errChan <- installDocker(logger) } + // If we are provisioning the machine, ensure the daemon has required options if workspaceInfo.Machine != nil { errChan <- configureDockerDaemon(ctx, logger) } else { - logger.Warn("Using existing docker installation, please ensure containerd snapshotter is enabled in the docker daemon") - logger.Warn("See https://docs.docker.com/engine/storage/containerd/") errChan <- nil } }() diff --git a/examples/build/.devcontainer.json b/examples/build/.devcontainer.json index 1c5482df6..422fec96c 100644 --- a/examples/build/.devcontainer.json +++ b/examples/build/.devcontainer.json @@ -3,19 +3,5 @@ "build": { "context": ".", "dockerfile": "./Dockerfile" - }, - "features":{ - "ghcr.io/devcontainers/features/github-cli:1": {}, - "ghcr.io/devcontainers/features/common-utils:2": { - "installZsh": true, - "configureZshAsDefaultShell": true, - "installOhMyZsh": true, - "installOhMyZshConfig": true, - "upgradePackages": true, - "username": "loft" - }, - "ghcr.io/devcontainers/features/docker-in-docker:2": { - "version": "latest" - } } } diff --git a/examples/build/Dockerfile b/examples/build/Dockerfile index e8b8604eb..d1ff3ff98 100644 --- a/examples/build/Dockerfile +++ b/examples/build/Dockerfile @@ -30,5 +30,6 @@ COPY app /app COPY files /files RUN echo test +RUN echo layer RUN echo hello \ No newline at end of file diff --git a/examples/build/app/code10 b/examples/build/app/code10 new file mode 100644 index 000000000..9daeafb98 --- /dev/null +++ b/examples/build/app/code10 @@ -0,0 +1 @@ +test diff --git a/examples/build/app/test b/examples/build/app/test new file mode 100644 index 000000000..e69de29bb From c89d96b4e467a41e87cbb0bb8e76248a7c68a93a Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Wed, 4 Sep 2024 17:04:26 +0100 Subject: [PATCH 09/26] POD-823: Implement toggle to export cache manifest --- cmd/agent/workspace/build.go | 1 + pkg/devcontainer/single.go | 1 + pkg/dockerfile/parse.go | 19 +++++++++++++++++++ pkg/driver/docker/build.go | 5 ++++- pkg/provider/workspace.go | 1 + 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/cmd/agent/workspace/build.go b/cmd/agent/workspace/build.go index 8c3a9f614..78eaeb605 100644 --- a/cmd/agent/workspace/build.go +++ b/cmd/agent/workspace/build.go @@ -84,6 +84,7 @@ func (cmd *BuildCmd) Run(ctx context.Context) error { CLIOptions: workspaceInfo.CLIOptions, RegistryCache: workspaceInfo.RegistryCache, Platform: platform, + ExportCache: true, }) if err != nil { logger.Errorf("Error building image: %v", err) diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 2335f3f30..799d66245 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -88,6 +88,7 @@ func (r *runner) runSingleContainer( }, NoBuild: options.NoBuild, RegistryCache: options.RegistryCache, + ExportCache: false, }) if err != nil { return nil, errors.Wrap(err, "build image") diff --git a/pkg/dockerfile/parse.go b/pkg/dockerfile/parse.go index 76fff3a52..0d472b2e0 100644 --- a/pkg/dockerfile/parse.go +++ b/pkg/dockerfile/parse.go @@ -73,6 +73,25 @@ func (d *Dockerfile) FindBaseImage(buildArgs map[string]string, target string) s return "" } +// BuildContextFiles traverses a build stage and returns a list of any file path that would affect the build context +func (d *Dockerfile) BuildContextFiles(target string) (files []string) { + // Get build stage + stage, ok := d.StagesByTarget[target] + if !ok { + stage = d.Stages[len(d.Stages)-1] + } + // Add the values after any ADD or COPY instructions + for _, in := range stage.Instructions { + if strings.HasPrefix(in.Value, "ADD") { + files = append(files, strings.TrimSuffix(in.Value, "ADD ")) + } + if strings.HasPrefix(in.Value, "COPY") { + files = append(files, strings.TrimSuffix(in.Value, "COPY ")) + } + } + return files +} + func (d *Dockerfile) replaceVariables(val string, buildArgs map[string]string, baseImageEnv map[string]string, stage *BaseStage, untilLine int) string { newVal := argumentExpression.ReplaceAllFunc([]byte(val), func(match []byte) []byte { subMatches := argumentExpression.FindStringSubmatch(string(match)) diff --git a/pkg/driver/docker/build.go b/pkg/driver/docker/build.go index 457cac299..b0eb94e4a 100644 --- a/pkg/driver/docker/build.go +++ b/pkg/driver/docker/build.go @@ -173,7 +173,10 @@ func CreateBuildOptions( // define cache args if options.RegistryCache != "" { buildOptions.CacheFrom = []string{fmt.Sprintf("type=registry,ref=%s", options.RegistryCache)} - buildOptions.CacheTo = []string{fmt.Sprintf("type=registry,ref=%s,mode=max,image-manifest=true", options.RegistryCache)} + // only export cache on build not up, otherwise we slow down the workspace start time + if options.ExportCache { + buildOptions.CacheTo = []string{fmt.Sprintf("type=registry,ref=%s,mode=max,image-manifest=true", options.RegistryCache)} + } } else { buildOptions.BuildArgs["BUILDKIT_INLINE_CACHE"] = "1" } diff --git a/pkg/provider/workspace.go b/pkg/provider/workspace.go index f0d164dbc..730eaa515 100644 --- a/pkg/provider/workspace.go +++ b/pkg/provider/workspace.go @@ -220,6 +220,7 @@ type BuildOptions struct { Platform string RegistryCache string + ExportCache bool NoBuild bool } From 6b478a7fa37ebe9cb6e9e92ade7a35bfc097dc31 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 09:15:13 +0100 Subject: [PATCH 10/26] POD-823: Improve hashing of image tag using parsed dockerfile --- pkg/devcontainer/build.go | 2 +- pkg/devcontainer/config/prebuild.go | 14 ++++++++++++-- pkg/dockerfile/parse.go | 25 ++++++++++++------------- pkg/util/hash/hash.go | 15 ++++++++++++++- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/pkg/devcontainer/build.go b/pkg/devcontainer/build.go index da0896e30..61aeed009 100644 --- a/pkg/devcontainer/build.go +++ b/pkg/devcontainer/build.go @@ -289,7 +289,7 @@ func (r *runner) buildImage( return nil, err } - prebuildHash, err := config.CalculatePrebuildHash(parsedConfig.Config, options.Platform, targetArch, config.GetContextPath(parsedConfig.Config), dockerfilePath, dockerfileContent, r.Log) + prebuildHash, err := config.CalculatePrebuildHash(parsedConfig.Config, options.Platform, targetArch, config.GetContextPath(parsedConfig.Config), dockerfilePath, dockerfileContent, r.Log, buildInfo) if err != nil { return nil, err } diff --git a/pkg/devcontainer/config/prebuild.go b/pkg/devcontainer/config/prebuild.go index ce351a4e6..6c1c21714 100644 --- a/pkg/devcontainer/config/prebuild.go +++ b/pkg/devcontainer/config/prebuild.go @@ -15,7 +15,11 @@ import ( "github.com/loft-sh/log/hash" ) -func CalculatePrebuildHash(originalConfig *DevContainerConfig, platform, architecture, contextPath, dockerfilePath, dockerfileContent string, log log.Logger) (string, error) { +func CalculatePrebuildHash( + originalConfig *DevContainerConfig, + platform, architecture, contextPath, dockerfilePath, dockerfileContent string, + log log.Logger, + buildInfo *ImageBuildInfo) (string, error) { parsedConfig := CloneDevContainerConfig(originalConfig) if platform != "" { @@ -57,8 +61,14 @@ func CalculatePrebuildHash(originalConfig *DevContainerConfig, platform, archite } excludes = append(excludes, DevPodContextFeatureFolder+"/") + // find exact files to hash + // todo pass down target or search all + // todo update DirectoryHash function + includes := buildInfo.Dockerfile.BuildContextFiles() + log.Debug("Build context files to use for hash are ", includes) + // get hash of the context directory - contextHash, err := util.DirectoryHash(contextPath, excludes) + contextHash, err := util.DirectoryHash(contextPath, excludes, includes, log) if err != nil { return "", err } diff --git a/pkg/dockerfile/parse.go b/pkg/dockerfile/parse.go index 0d472b2e0..3ddf07fb8 100644 --- a/pkg/dockerfile/parse.go +++ b/pkg/dockerfile/parse.go @@ -74,19 +74,18 @@ func (d *Dockerfile) FindBaseImage(buildArgs map[string]string, target string) s } // BuildContextFiles traverses a build stage and returns a list of any file path that would affect the build context -func (d *Dockerfile) BuildContextFiles(target string) (files []string) { - // Get build stage - stage, ok := d.StagesByTarget[target] - if !ok { - stage = d.Stages[len(d.Stages)-1] - } - // Add the values after any ADD or COPY instructions - for _, in := range stage.Instructions { - if strings.HasPrefix(in.Value, "ADD") { - files = append(files, strings.TrimSuffix(in.Value, "ADD ")) - } - if strings.HasPrefix(in.Value, "COPY") { - files = append(files, strings.TrimSuffix(in.Value, "COPY ")) +func (d *Dockerfile) BuildContextFiles() (files []string) { + // Iterate over all build stages + for _, stage := range d.StagesByTarget { + // Add the values of any ADD or COPY instructions + for _, in := range stage.Instructions { + if strings.HasPrefix(in.Value, "ADD") || strings.HasPrefix(in.Value, "COPY") { + // Take all parts except the first (ADD/COPY) and the last (destination on remote), e.g. "COPY src files /app", we want src and files + parts := strings.Split(in.Original, " ") + if len(parts) > 2 { + files = append(files, parts[1:len(parts)-1]...) + } + } } } return files diff --git a/pkg/util/hash/hash.go b/pkg/util/hash/hash.go index b0c1df8a0..51607fff4 100644 --- a/pkg/util/hash/hash.go +++ b/pkg/util/hash/hash.go @@ -12,6 +12,8 @@ import ( "sort" "strings" + "github.com/loft-sh/log" + "github.com/docker/docker/pkg/longpath" "github.com/moby/patternmatcher" "github.com/pkg/errors" @@ -22,7 +24,7 @@ var ( errFileReadOverLimit = errors.New("read files over limit") ) -func DirectoryHash(srcPath string, excludePatterns []string) (string, error) { +func DirectoryHash(srcPath string, excludePatterns, includeFiles []string, log log.Logger) (string, error) { srcPath, err := filepath.Abs(srcPath) if err != nil { return "", err @@ -86,6 +88,17 @@ func DirectoryHash(srcPath string, excludePatterns []string) (string, error) { } relFilePath = filepath.ToSlash(relFilePath) + // Ensure file affects build context + include := false + for _, f := range includeFiles { + if strings.HasPrefix(relFilePath, f) { + include = true + } + } + if !include { + return nil + } + skip := false // If "include" is an exact match for the current file From 1d9160354f3bf50b388ee242a2beb64ecfcb4a34 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 09:33:46 +0100 Subject: [PATCH 11/26] POD-823: Add todo comment --- cmd/build.go | 2 -- cmd/up.go | 2 -- pkg/devcontainer/single.go | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 5f2ca3c30..e6eff8726 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -29,7 +29,6 @@ type BuildCmd struct { SkipDelete bool Machine string - // RegistryCache string } // NewBuildCmd creates a new command @@ -115,7 +114,6 @@ func NewBuildCmd(flags *flags.GlobalFlags) *cobra.Command { buildCmd.Flags().StringSliceVar(&cmd.Platform, "platform", []string{}, "Set target platform for build") buildCmd.Flags().BoolVar(&cmd.SkipPush, "skip-push", false, "If true will not push the image to the repository, useful for testing") buildCmd.Flags().Var(&cmd.GitCloneStrategy, "git-clone-strategy", "The git clone strategy DevPod uses to checkout git based workspaces. Can be full (default), blobless, treeless or shallow") - // buildCmd.Flags().StringVar(&cmd.RegistryCache, "registry-cache", "", "The registry to use as a build cache") // TESTING buildCmd.Flags().BoolVar(&cmd.ForceBuild, "force-build", false, "TESTING ONLY") diff --git a/cmd/up.go b/cmd/up.go index 99a133f1e..ba567fa72 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -62,7 +62,6 @@ type UpCmd struct { DotfilesSource string DotfilesScript string - // RegistryCache string } // NewUpCmd creates a new up command @@ -168,7 +167,6 @@ func NewUpCmd(flags *flags.GlobalFlags) *cobra.Command { upCmd.Flags().Var(&cmd.GitCloneStrategy, "git-clone-strategy", "The git clone strategy DevPod uses to checkout git based workspaces. Can be full (default), blobless, treeless or shallow") upCmd.Flags().StringVar(&cmd.GitSSHSigningKey, "git-ssh-signing-key", "", "The ssh key to use when signing git commits. Used to explicitly setup DevPod's ssh signature forwarding with given key. Should be same format as value of `git config user.signingkey`") upCmd.Flags().StringVar(&cmd.FallbackImage, "fallback-image", "", "The fallback image to use if no devcontainer configuration has been detected") - // upCmd.Flags().StringVar(&cmd.RegistryCache, "registry-cache", "", "The registry to use as a build cache") upCmd.Flags().BoolVar(&cmd.DisableDaemon, "disable-daemon", false, "If enabled, will not install a daemon into the target machine to track activity") upCmd.Flags().StringVar(&cmd.Source, "source", "", "Optional source for the workspace. E.g. git:https://github.com/my-org/my-repo") diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 799d66245..0b6e323af 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" ) -var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0-alpha-7" // "ghcr.io/loft-sh/dockerless:0.1.4" +var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0-alpha-7" // "ghcr.io/loft-sh/dockerless:0.1.4" todo update once dockerless PR is merged const ( DevPodExtraEnvVar = "DEVPOD" From 2a14b7c2d894da1a81195c021957132a9eb66dd7 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 09:38:59 +0100 Subject: [PATCH 12/26] POD-823: Fix rebase error --- cmd/agent/workspace/up.go | 97 --------------------------------------- 1 file changed, 97 deletions(-) diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index 054da369b..203b1d4b1 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -2,7 +2,6 @@ package workspace import ( "context" - "encoding/base64" "encoding/json" "fmt" "os" @@ -24,8 +23,6 @@ import ( "github.com/loft-sh/devpod/pkg/devcontainer/crane" "github.com/loft-sh/devpod/pkg/dockercredentials" "github.com/loft-sh/devpod/pkg/extract" - "github.com/loft-sh/devpod/pkg/git" - "github.com/loft-sh/devpod/pkg/gitcredentials" provider2 "github.com/loft-sh/devpod/pkg/provider" "github.com/loft-sh/devpod/scripts" "github.com/loft-sh/log" @@ -425,100 +422,6 @@ func prepareImage(workspaceDir, image string) error { return nil } -func cloneRepository(ctx context.Context, workspaceInfo *provider2.AgentWorkspaceInfo, helper string, log log.Logger) error { - s := workspaceInfo.Workspace.Source - log.Info("Clone repository") - log.Infof("URL: %s\n", s.GitRepository) - if s.GitBranch != "" { - log.Infof("Branch: %s\n", s.GitBranch) - } - if s.GitCommit != "" { - log.Infof("Commit: %s\n", s.GitCommit) - } - if s.GitSubPath != "" { - log.Infof("Subpath: %s\n", s.GitSubPath) - } - if s.GitPRReference != "" { - log.Infof("PR: %s\n", s.GitPRReference) - } - - workspaceDir := workspaceInfo.ContentFolder - // remove the credential helper or otherwise we will receive strange errors within the container - defer func() { - if helper != "" { - if err := gitcredentials.RemoveHelperFromPath(gitcredentials.GetLocalGitConfigPath(workspaceDir)); err != nil { - log.Errorf("Remove git credential helper: %v", err) - } - } - }() - - // check if command exists - if !command.Exists("git") { - local, _ := workspaceInfo.Agent.Local.Bool() - if local { - return fmt.Errorf("seems like git isn't installed on your system. Please make sure to install git and make it available in the PATH") - } - if err := git.InstallBinary(log); err != nil { - return err - } - } - - // setup private ssh key if passed in - extraEnv := []string{} - if workspaceInfo.CLIOptions.SSHKey != "" { - sshExtraEnv, err := setupSSHKey(workspaceInfo.CLIOptions.SSHKey, workspaceInfo.Agent.Path) - if err != nil { - return err - } - extraEnv = append(extraEnv, sshExtraEnv...) - } - - // run git command - cloner := git.NewCloner(workspaceInfo.CLIOptions.GitCloneStrategy) - gitInfo := git.NewGitInfo(s.GitRepository, s.GitBranch, s.GitCommit, s.GitPRReference, s.GitSubPath) - err := git.CloneRepositoryWithEnv(ctx, gitInfo, extraEnv, workspaceDir, helper, cloner, log) - if err != nil { - return fmt.Errorf("clone repository: %w", err) - } - - log.Done("Successfully cloned repository") - - return nil -} - -func setupSSHKey(key string, agentPath string) ([]string, error) { - keyFile, err := os.CreateTemp("", "") - if err != nil { - return nil, err - } - defer os.Remove(keyFile.Name()) - defer keyFile.Close() - - if err := writeSSHKey(keyFile, key); err != nil { - return nil, err - } - - if err := os.Chmod(keyFile.Name(), 0o400); err != nil { - return nil, err - } - - env := []string{"GIT_TERMINAL_PROMPT=0"} - gitSSHCmd := []string{agentPath, "helper", "ssh-git-clone", "--key-file=" + keyFile.Name()} - env = append(env, "GIT_SSH_COMMAND="+command.Quote(gitSSHCmd)) - - return env, nil -} - -func writeSSHKey(key *os.File, sshKey string) error { - data, err := base64.StdEncoding.DecodeString(sshKey) - if err != nil { - return err - } - - _, err = key.WriteString(string(data)) - return err -} - func installDocker(log log.Logger) (err error) { if !command.Exists("docker") { writer := log.Writer(logrus.InfoLevel, false) From 8af4580749abd992d5e08850a09ef70591df3bd0 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 09:41:02 +0100 Subject: [PATCH 13/26] POD-823: Simplify examples build --- examples/build/Dockerfile | 3 --- examples/build/app/code10 | 1 - examples/build/app/test | 0 examples/build/files/test | 1 - examples/build/files/test2 | 1 - 5 files changed, 6 deletions(-) delete mode 100644 examples/build/app/code10 delete mode 100644 examples/build/app/test delete mode 100644 examples/build/files/test delete mode 100644 examples/build/files/test2 diff --git a/examples/build/Dockerfile b/examples/build/Dockerfile index d1ff3ff98..96f591f5b 100644 --- a/examples/build/Dockerfile +++ b/examples/build/Dockerfile @@ -29,7 +29,4 @@ RUN \ COPY app /app COPY files /files -RUN echo test -RUN echo layer - RUN echo hello \ No newline at end of file diff --git a/examples/build/app/code10 b/examples/build/app/code10 deleted file mode 100644 index 9daeafb98..000000000 --- a/examples/build/app/code10 +++ /dev/null @@ -1 +0,0 @@ -test diff --git a/examples/build/app/test b/examples/build/app/test deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/build/files/test b/examples/build/files/test deleted file mode 100644 index 30d74d258..000000000 --- a/examples/build/files/test +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/examples/build/files/test2 b/examples/build/files/test2 deleted file mode 100644 index 9daeafb98..000000000 --- a/examples/build/files/test2 +++ /dev/null @@ -1 +0,0 @@ -test From a26cb5a110d4ce58dd67b42485efa254b2bb76af Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 09:41:40 +0100 Subject: [PATCH 14/26] =?UTF-8?q?POD-823:=20Remove=20default=20registry?= =?UTF-8?q?=C2=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/config/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/context.go b/pkg/config/context.go index b8954c1d8..36be953f7 100644 --- a/pkg/config/context.go +++ b/pkg/config/context.go @@ -90,6 +90,6 @@ var ContextOptions = []ContextOption{ { Name: ContextOptionRegistryCache, Description: "Specifies the registry to use as a build cache", - Default: "gcr.io/pascal-project-387807/my-dev-env", // todo remove + Default: "", }, } From 368c75207b8f213b5701e4ec06eafd14ef0cd432 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 09:50:35 +0100 Subject: [PATCH 15/26] POD-823: Fix e2e tests --- e2e/tests/build/build.go | 23 +++++++++++++++++++---- pkg/devcontainer/config/prebuild.go | 2 +- pkg/driver/docker/build.go | 2 +- pkg/util/hash/hash.go | 4 +--- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/e2e/tests/build/build.go b/e2e/tests/build/build.go index c12e4e0c1..8bb448571 100644 --- a/e2e/tests/build/build.go +++ b/e2e/tests/build/build.go @@ -57,13 +57,18 @@ var _ = DevPodDescribe("devpod build test suite", func() { err = f.DevPodBuild(ctx, tempDir, "--force-build", "--platform", "linux/amd64,linux/arm64", "--repository", prebuildRepo, "--skip-push") framework.ExpectNoError(err) + // parse the dockerfile + file, err := dockerfile.Parse(modifiedDockerfileContents) + framework.ExpectNoError(err) + info := &config.ImageBuildInfo{Dockerfile: file} + // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default) + prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) framework.ExpectNoError(err) - prebuildHash, err = config.CalculatePrebuildHash(cfg, "linux/arm64", "arm64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default) + prebuildHash, err = config.CalculatePrebuildHash(cfg, "linux/arm64", "arm64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) framework.ExpectNoError(err) @@ -98,8 +103,13 @@ var _ = DevPodDescribe("devpod build test suite", func() { err = f.DevPodBuild(ctx, tempDir, "--skip-push") framework.ExpectNoError(err) + // parse the dockerfile + file, err := dockerfile.Parse(modifiedDockerfileContents) + framework.ExpectNoError(err) + info := &config.ImageBuildInfo{Dockerfile: file} + // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default) + prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, dockerdriver.GetImageName(tempDir, prebuildHash), false) framework.ExpectNoError(err) @@ -154,8 +164,13 @@ var _ = DevPodDescribe("devpod build test suite", func() { err = f.DevPodBuild(ctx, tempDir, "--force-build", "--force-internal-buildkit", "--repository", prebuildRepo, "--skip-push") framework.ExpectNoError(err) + // parse the dockerfile + file, err := dockerfile.Parse(modifiedDockerfileContents) + framework.ExpectNoError(err) + info := &config.ImageBuildInfo{Dockerfile: file} + // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default) + prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) diff --git a/pkg/devcontainer/config/prebuild.go b/pkg/devcontainer/config/prebuild.go index 6c1c21714..9715295ba 100644 --- a/pkg/devcontainer/config/prebuild.go +++ b/pkg/devcontainer/config/prebuild.go @@ -68,7 +68,7 @@ func CalculatePrebuildHash( log.Debug("Build context files to use for hash are ", includes) // get hash of the context directory - contextHash, err := util.DirectoryHash(contextPath, excludes, includes, log) + contextHash, err := util.DirectoryHash(contextPath, excludes, includes) if err != nil { return "", err } diff --git a/pkg/driver/docker/build.go b/pkg/driver/docker/build.go index b0eb94e4a..c17388aef 100644 --- a/pkg/driver/docker/build.go +++ b/pkg/driver/docker/build.go @@ -62,7 +62,7 @@ func (d *dockerDriver) BuildDevContainer( if err != nil { return nil, err } - d.Log.Info("registry cache", options.RegistryCache) + d.Log.Debug("Using registry cache", options.RegistryCache) // build image writer := d.Log.Writer(logrus.InfoLevel, false) diff --git a/pkg/util/hash/hash.go b/pkg/util/hash/hash.go index 51607fff4..c17f94af6 100644 --- a/pkg/util/hash/hash.go +++ b/pkg/util/hash/hash.go @@ -12,8 +12,6 @@ import ( "sort" "strings" - "github.com/loft-sh/log" - "github.com/docker/docker/pkg/longpath" "github.com/moby/patternmatcher" "github.com/pkg/errors" @@ -24,7 +22,7 @@ var ( errFileReadOverLimit = errors.New("read files over limit") ) -func DirectoryHash(srcPath string, excludePatterns, includeFiles []string, log log.Logger) (string, error) { +func DirectoryHash(srcPath string, excludePatterns, includeFiles []string) (string, error) { srcPath, err := filepath.Abs(srcPath) if err != nil { return "", err From 86c89a14d4f75c8312f2b0540c14a547043c53fb Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 13:40:43 +0100 Subject: [PATCH 16/26] POD-823: Implement unit test for parser --- pkg/devcontainer/single.go | 3 ++- pkg/dockerfile/parse.go | 2 +- pkg/dockerfile/parse_test.go | 24 ++++++++++++++++++++++++ pkg/dockerfile/test_Dockerfile | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 pkg/dockerfile/parse_test.go create mode 100644 pkg/dockerfile/test_Dockerfile diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 0b6e323af..6c57b4171 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" ) -var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0-alpha-7" // "ghcr.io/loft-sh/dockerless:0.1.4" todo update once dockerless PR is merged +var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0-alpha-8" // "ghcr.io/loft-sh/dockerless:0.1.4" todo update once dockerless PR is merged const ( DevPodExtraEnvVar = "DEVPOD" @@ -188,6 +188,7 @@ func (r *runner) getDockerlessRunOptions( "DOCKERLESS": "true", "DOCKERLESS_CONTEXT": buildInfo.Dockerless.Context, "DOCKERLESS_DOCKERFILE": buildInfo.Dockerless.Dockerfile, + "GODEBUG": "http2client=0", } for k, v := range mergedConfig.ContainerEnv { env[k] = v diff --git a/pkg/dockerfile/parse.go b/pkg/dockerfile/parse.go index 3ddf07fb8..740faa4eb 100644 --- a/pkg/dockerfile/parse.go +++ b/pkg/dockerfile/parse.go @@ -76,7 +76,7 @@ func (d *Dockerfile) FindBaseImage(buildArgs map[string]string, target string) s // BuildContextFiles traverses a build stage and returns a list of any file path that would affect the build context func (d *Dockerfile) BuildContextFiles() (files []string) { // Iterate over all build stages - for _, stage := range d.StagesByTarget { + for _, stage := range d.Stages { // Add the values of any ADD or COPY instructions for _, in := range stage.Instructions { if strings.HasPrefix(in.Value, "ADD") || strings.HasPrefix(in.Value, "COPY") { diff --git a/pkg/dockerfile/parse_test.go b/pkg/dockerfile/parse_test.go new file mode 100644 index 000000000..99301ef7a --- /dev/null +++ b/pkg/dockerfile/parse_test.go @@ -0,0 +1,24 @@ +package dockerfile + +import ( + _ "embed" + "fmt" + "testing" + + "gotest.tools/assert" +) + +//go:embed test_Dockerfile +var testDockerFileContents string + +func TestBuildContextFiles(t *testing.T) { + dockerFile, err := Parse(testDockerFileContents) + assert.NilError(t, err) + + fmt.Print(dockerFile.Stages) + + files := dockerFile.BuildContextFiles() + assert.Equal(t, len(files), 2) + assert.Equal(t, files[0], "app") + assert.Equal(t, files[1], "files") +} diff --git a/pkg/dockerfile/test_Dockerfile b/pkg/dockerfile/test_Dockerfile new file mode 100644 index 000000000..96f591f5b --- /dev/null +++ b/pkg/dockerfile/test_Dockerfile @@ -0,0 +1,32 @@ +FROM mcr.microsoft.com/devcontainers/go:1.22-bullseye + +ARG TARGETOS +ARG TARGETARCH + +# Install Node.js +RUN \ + --mount=type=cache,target=/var/cache/apt \ + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get update \ + && apt-get install -y --no-install-recommends nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Set environment variables for Rust +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH \ + RUST_VERSION=1.69.0 + +# Install Protobuf compiler +RUN \ + --mount=type=cache,target=/var/cache/apt \ + apt-get update \ + && apt-get install -y --no-install-recommends protobuf-compiler \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY app /app +COPY files /files + +RUN echo hello \ No newline at end of file From d1177c826eb37b3a226829875011089ad3292cad Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 13:53:31 +0100 Subject: [PATCH 17/26] POD-823: PR feedback --- cmd/agent/container/setup.go | 2 +- cmd/agent/workspace/up.go | 3 ++- e2e/tests/build/build.go | 8 ++++---- examples/build/.devcontainer.json | 3 +++ pkg/config/context.go | 2 +- pkg/devcontainer/build.go | 2 +- pkg/devcontainer/config/prebuild.go | 4 ++-- 7 files changed, 14 insertions(+), 10 deletions(-) diff --git a/cmd/agent/container/setup.go b/cmd/agent/container/setup.go index f6c17eb9f..74e9f0523 100644 --- a/cmd/agent/container/setup.go +++ b/cmd/agent/container/setup.go @@ -303,7 +303,7 @@ func dockerlessBuild( args = append(args, "--build-arg", "TARGETOS="+runtime.GOOS) args = append(args, "--build-arg", "TARGETARCH="+runtime.GOARCH) if dockerlessOptions.RegistryCache != "" { - log.Info("Appending registry cache to dockerless build arguments ", dockerlessOptions.RegistryCache) + log.Debug("Appending registry cache to dockerless build arguments ", dockerlessOptions.RegistryCache) args = append(args, "--registry-cache", dockerlessOptions.RegistryCache) } diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index 203b1d4b1..3d4c2c68e 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -215,7 +215,8 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo errChan <- installDocker(logger) } // If we are provisioning the machine, ensure the daemon has required options - if workspaceInfo.Machine != nil { + local, err := workspaceInfo.Agent.Local.Bool() + if workspaceInfo.Agent.IsDockerDriver() && err != nil && local { errChan <- configureDockerDaemon(ctx, logger) } else { errChan <- nil diff --git a/e2e/tests/build/build.go b/e2e/tests/build/build.go index 8bb448571..5d54dbfc7 100644 --- a/e2e/tests/build/build.go +++ b/e2e/tests/build/build.go @@ -63,12 +63,12 @@ var _ = DevPodDescribe("devpod build test suite", func() { info := &config.ImageBuildInfo{Dockerfile: file} // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) + prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, info, log.Default) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) framework.ExpectNoError(err) - prebuildHash, err = config.CalculatePrebuildHash(cfg, "linux/arm64", "arm64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) + prebuildHash, err = config.CalculatePrebuildHash(cfg, "linux/arm64", "arm64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, info, log.Default) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) framework.ExpectNoError(err) @@ -109,7 +109,7 @@ var _ = DevPodDescribe("devpod build test suite", func() { info := &config.ImageBuildInfo{Dockerfile: file} // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) + prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, info, log.Default) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, dockerdriver.GetImageName(tempDir, prebuildHash), false) framework.ExpectNoError(err) @@ -170,7 +170,7 @@ var _ = DevPodDescribe("devpod build test suite", func() { info := &config.ImageBuildInfo{Dockerfile: file} // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, log.Default, info) + prebuildHash, err := config.CalculatePrebuildHash(cfg, "linux/amd64", "amd64", filepath.Dir(cfg.Origin), dockerfilePath, modifiedDockerfileContents, info, log.Default) framework.ExpectNoError(err) _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) diff --git a/examples/build/.devcontainer.json b/examples/build/.devcontainer.json index 422fec96c..33d87350a 100644 --- a/examples/build/.devcontainer.json +++ b/examples/build/.devcontainer.json @@ -3,5 +3,8 @@ "build": { "context": ".", "dockerfile": "./Dockerfile" + }, + "features":{ + "ghcr.io/devcontainers/features/github-cli:1": {} } } diff --git a/pkg/config/context.go b/pkg/config/context.go index 36be953f7..b4d676131 100644 --- a/pkg/config/context.go +++ b/pkg/config/context.go @@ -89,7 +89,7 @@ var ContextOptions = []ContextOption{ }, { Name: ContextOptionRegistryCache, - Description: "Specifies the registry to use as a build cache", + Description: "Specifies the registry to use as a build cache, e.g. gcr.io/my-project/my-dev-env", Default: "", }, } diff --git a/pkg/devcontainer/build.go b/pkg/devcontainer/build.go index 61aeed009..fa5ae3101 100644 --- a/pkg/devcontainer/build.go +++ b/pkg/devcontainer/build.go @@ -289,7 +289,7 @@ func (r *runner) buildImage( return nil, err } - prebuildHash, err := config.CalculatePrebuildHash(parsedConfig.Config, options.Platform, targetArch, config.GetContextPath(parsedConfig.Config), dockerfilePath, dockerfileContent, r.Log, buildInfo) + prebuildHash, err := config.CalculatePrebuildHash(parsedConfig.Config, options.Platform, targetArch, config.GetContextPath(parsedConfig.Config), dockerfilePath, dockerfileContent, buildInfo, r.Log) if err != nil { return nil, err } diff --git a/pkg/devcontainer/config/prebuild.go b/pkg/devcontainer/config/prebuild.go index 9715295ba..c91184b5e 100644 --- a/pkg/devcontainer/config/prebuild.go +++ b/pkg/devcontainer/config/prebuild.go @@ -18,8 +18,8 @@ import ( func CalculatePrebuildHash( originalConfig *DevContainerConfig, platform, architecture, contextPath, dockerfilePath, dockerfileContent string, - log log.Logger, - buildInfo *ImageBuildInfo) (string, error) { + buildInfo *ImageBuildInfo, + log log.Logger) (string, error) { parsedConfig := CloneDevContainerConfig(originalConfig) if platform != "" { From b5169bd64bc91e9e7542cf7340356c8c89fa53d8 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Thu, 5 Sep 2024 14:43:50 +0100 Subject: [PATCH 18/26] POD-823: PR feedback --- cmd/agent/workspace/up.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index 3d4c2c68e..adc218740 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -216,7 +216,7 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo } // If we are provisioning the machine, ensure the daemon has required options local, err := workspaceInfo.Agent.Local.Bool() - if workspaceInfo.Agent.IsDockerDriver() && err != nil && local { + if workspaceInfo.Agent.IsDockerDriver() && err != nil && !local { errChan <- configureDockerDaemon(ctx, logger) } else { errChan <- nil From b2530b2baf122114282d8a39ab554cbb60cbc885 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Fri, 6 Sep 2024 16:07:18 +0100 Subject: [PATCH 19/26] POD-823: Use official dockerless image --- pkg/devcontainer/single.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 6c57b4171..f70cfcd16 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" ) -var dockerlessImage = "gcr.io/pascal-project-387807/dockerless:0.2.0-alpha-8" // "ghcr.io/loft-sh/dockerless:0.1.4" todo update once dockerless PR is merged +var dockerlessImage = "ghcr.io/loft-sh/dockerless:0.2.0" const ( DevPodExtraEnvVar = "DEVPOD" From f24e0134865c16e28ab7ba74317e86d7010508d2 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 10 Sep 2024 08:00:56 +0100 Subject: [PATCH 20/26] POD-823: Mount cluster wide cache for dockerless --- pkg/devcontainer/single.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index f70cfcd16..70aa5309e 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -217,6 +217,11 @@ func (r *runner) getDockerlessRunOptions( Source: "dockerless-" + r.ID, Target: "/workspaces/.dockerless", }) + mounts = append(mounts, &config.Mount{ + Type: "volume", + Source: "dockerless-cache", + Target: "/.dockerless/cache", + }) uid := "" if r.WorkspaceConfig != nil && r.WorkspaceConfig.Workspace != nil { From 7a2f783ddad32ee2861c691d415ae8da2e492ead Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 10 Sep 2024 08:24:29 +0100 Subject: [PATCH 21/26] POD-823: Fix unit tests --- cmd/agent/workspace/up.go | 2 +- pkg/devcontainer/config/prebuild.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index adc218740..d30731a5b 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -217,7 +217,7 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo // If we are provisioning the machine, ensure the daemon has required options local, err := workspaceInfo.Agent.Local.Bool() if workspaceInfo.Agent.IsDockerDriver() && err != nil && !local { - errChan <- configureDockerDaemon(ctx, logger) + errChan <- nil // configureDockerDaemon(ctx, logger) todo clean up } else { errChan <- nil } diff --git a/pkg/devcontainer/config/prebuild.go b/pkg/devcontainer/config/prebuild.go index c91184b5e..bc5f53651 100644 --- a/pkg/devcontainer/config/prebuild.go +++ b/pkg/devcontainer/config/prebuild.go @@ -64,7 +64,10 @@ func CalculatePrebuildHash( // find exact files to hash // todo pass down target or search all // todo update DirectoryHash function - includes := buildInfo.Dockerfile.BuildContextFiles() + var includes []string + if buildInfo.Dockerfile != nil { + includes = buildInfo.Dockerfile.BuildContextFiles() + } log.Debug("Build context files to use for hash are ", includes) // get hash of the context directory From 6b568d8a7e9c049c7a08ca5d7b76148fc38ef9dd Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 10 Sep 2024 09:04:59 +0100 Subject: [PATCH 22/26] POD-823: Configure docker daemon after install --- cmd/agent/workspace/up.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index d30731a5b..d7a9fa51e 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -214,13 +214,6 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo } else { errChan <- installDocker(logger) } - // If we are provisioning the machine, ensure the daemon has required options - local, err := workspaceInfo.Agent.Local.Bool() - if workspaceInfo.Agent.IsDockerDriver() && err != nil && !local { - errChan <- nil // configureDockerDaemon(ctx, logger) todo clean up - } else { - errChan <- nil - } }() // prepare workspace @@ -237,12 +230,21 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo } } - // wait until docker is installed + // wait until docker is installed before configuring docker daemon err = <-errChan if err != nil { return nil, nil, "", errors.Wrap(err, "install docker") } - // wait until daemon is configured + + // If we are provisioning the machine, ensure the daemon has required options + local, err := workspaceInfo.Agent.Local.Bool() + if workspaceInfo.Agent.IsDockerDriver() && err != nil && !local { + errChan <- configureDockerDaemon(ctx, logger) + } else { + errChan <- nil + } + + // wait until docker daemon is configured err = <-errChan if err != nil { return nil, nil, "", errors.Wrap(err, "configure docker") From 712436db9a38d041caa87776575ba9d1c0a3cd01 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 10 Sep 2024 10:12:02 +0100 Subject: [PATCH 23/26] POD-823: Print warning instead of error for configuring daemon --- cmd/agent/workspace/up.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/agent/workspace/up.go b/cmd/agent/workspace/up.go index d7a9fa51e..e1044ff67 100644 --- a/cmd/agent/workspace/up.go +++ b/cmd/agent/workspace/up.go @@ -241,13 +241,15 @@ func initWorkspace(ctx context.Context, cancel context.CancelFunc, workspaceInfo if workspaceInfo.Agent.IsDockerDriver() && err != nil && !local { errChan <- configureDockerDaemon(ctx, logger) } else { + logger.Debug("Skipping configuring daemon") errChan <- nil } // wait until docker daemon is configured err = <-errChan if err != nil { - return nil, nil, "", errors.Wrap(err, "configure docker") + logger.Warn("Could not find docker daemon config file, if using the registry cache, please ensure the daemon is configured with containerd-snapshotter=true") + logger.Warn("More info at https://docs.docker.com/engine/storage/containerd/") } return tunnelClient, logger, dockerCredentialsDir, nil From 2e15836923886ed996af2d7f9f0b6fa54b161b23 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 10 Sep 2024 13:28:14 +0100 Subject: [PATCH 24/26] POD-823: Comment on use of http1 --- pkg/devcontainer/single.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 70aa5309e..9aa2c470e 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -188,7 +188,7 @@ func (r *runner) getDockerlessRunOptions( "DOCKERLESS": "true", "DOCKERLESS_CONTEXT": buildInfo.Dockerless.Context, "DOCKERLESS_DOCKERFILE": buildInfo.Dockerless.Dockerfile, - "GODEBUG": "http2client=0", + "GODEBUG": "http2client=0", // https://github.com/GoogleContainerTools/kaniko/issues/875 } for k, v := range mergedConfig.ContainerEnv { env[k] = v From 555cbe34bc67e0816bbaca58d872c91f1189e322 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Tue, 10 Sep 2024 14:24:52 +0100 Subject: [PATCH 25/26] Remove local mount caching --- examples/build/Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/build/Dockerfile b/examples/build/Dockerfile index 96f591f5b..bcfff340e 100644 --- a/examples/build/Dockerfile +++ b/examples/build/Dockerfile @@ -5,7 +5,6 @@ ARG TARGETARCH # Install Node.js RUN \ - --mount=type=cache,target=/var/cache/apt \ curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get update \ && apt-get install -y --no-install-recommends nodejs \ @@ -20,7 +19,6 @@ ENV RUSTUP_HOME=/usr/local/rustup \ # Install Protobuf compiler RUN \ - --mount=type=cache,target=/var/cache/apt \ apt-get update \ && apt-get install -y --no-install-recommends protobuf-compiler \ && apt-get clean \ From aa1e749b9308302f53032a5d64a5a92f6a2b9894 Mon Sep 17 00:00:00 2001 From: Bryan Kneis Date: Wed, 11 Sep 2024 07:45:12 +0100 Subject: [PATCH 26/26] POD-823: Remove inner cache --- pkg/devcontainer/single.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/devcontainer/single.go b/pkg/devcontainer/single.go index 9aa2c470e..8f9710187 100644 --- a/pkg/devcontainer/single.go +++ b/pkg/devcontainer/single.go @@ -217,11 +217,6 @@ func (r *runner) getDockerlessRunOptions( Source: "dockerless-" + r.ID, Target: "/workspaces/.dockerless", }) - mounts = append(mounts, &config.Mount{ - Type: "volume", - Source: "dockerless-cache", - Target: "/.dockerless/cache", - }) uid := "" if r.WorkspaceConfig != nil && r.WorkspaceConfig.Workspace != nil {