10000 fix: allow setting MagicDir in Options by johnstcn · Pull Request #337 · coder/envbuilder · GitHub
[go: up one dir, main page]

Skip to content

fix: allow setting MagicDir in Options #337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
make MagicDir un-stringable directly
  • Loading branch information
johnstcn committed Sep 9, 2024
commit 88051f87c3c6c532975bd37c95d7ee7b2167776e
40 changes: 28 additions & 12 deletions constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,21 @@ const (
// nothing going on... it's empty!
EmptyWorkspaceDir = WorkspacesDir + "/empty" 8000

// defaultMagicDir is the default working location for envbuilder.
// defaultMagicDirBase is the default working location for envbuilder.
// This is a special directory that must not be modified by the user
// or images. This is intentionally unexported.
defaultMagicDir = "/.envbuilder"
defaultMagicDirBase = "/.envbuilder"

// MagicTempDir is a directory inside the build context inside which
// we place files referenced by MagicDirectives.
MagicTempDir = ".envbuilder.tmp"
)

var (
// DefaultMagicDir is the default working directory for Envbuilder.
// This defaults to /.envbuilder. It should only be used when Envbuilder
// is known to be running as root inside a container.
DefaultMagicDir MagicDir
// ErrNoFallbackImage is returned when no fallback image has been specified.
ErrNoFallbackImage = errors.New("no fallback image has been specified")
// MagicDirectives are directives automatically appended to Dockerfiles
Expand All @@ -37,34 +41,46 @@ COPY --chmod=0644 %[1]s/image %[2]s/image
USER root
WORKDIR /
ENTRYPOINT ["%[2]s/bin/envbuilder"]
`, MagicTempDir, defaultMagicDir)
`, MagicTempDir, defaultMagicDirBase)
)

// MagicDir is a working directory for envbuilder. It
// will also be present in images built by envbuilder.
type MagicDir string
type MagicDir struct {
base string
}

// MagicDirAt returns a MagicDir rooted at filepath.Join(paths...)
func MagicDirAt(paths ...string) MagicDir {
return MagicDir{base: filepath.Join(paths...)}
}

// Join returns the result of filepath.Join([m.Path, paths...]).
func (m MagicDir) Join(paths ...string) string {
return filepath.Join(append([]string{m.Path()}, paths...)...)
}

// String returns the string representation of the MagicDir.
func (m MagicDir) Path() string {
if m == "" {
// Instead of the zero value, use defaultMagicDir.
8000 return defaultMagicDir
// Instead of the zero value, use defaultMagicDir.
if m.base == "" {
return defaultMagicDirBase
}
return filepath.Join("/", string(m))
return m.base
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking this abstraction could be a source of bugs. One might not always think to call String and instead do string(MagicDir) which would result in an empty string or potentially lack of /.

I'd rather see this as a validation step in options so that the input is verified there, and an error can be returned if it's not absolute. The options struct could have the MagicImage and MagicBuilt functions if that's preferable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a simpler option here it to simply change the type to a struct with a single unexported field. I'm less worried about leaking the implementation details; we can simply move MagicDir to an internal package.


// MagicFile is a file that is created in the workspace
// Built is a file that is created in the workspace
// when envbuilder has already been run. This is used
// to skip building when a container is restarting.
// e.g. docker stop -> docker start
func (m MagicDir) Built() string {
return filepath.Join(m.Path(), "built")
return m.Join("built")
}

// MagicImage is a file that is created in the image when
// Image is a file that is created in the image when
// envbuilder has already been run. This is used to skip
// the destructive initial build step when 'resuming' envbuilder
// from a previously built image.
func (m MagicDir) Image() string {
return filepath.Join(m.Path(), "image")
return m.Join("image")
}
8 changes: 4 additions & 4 deletions envbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func Run(ctx context.Context, opts options.Options) error {
if err := util.AddAllowedPathToDefaultIgnoreList(opts.MagicDir.Image()); err != nil {
return fmt.Errorf("add magic image file to ignore list: %w", err)
}
magicTempDir := constants.MagicDir(filepath.Join(buildParams.BuildContext, constants.MagicTempDir))
magicTempDir := constants.MagicDirAt(buildParams.BuildContext, constants.MagicTempDir)
if err := opts.Filesystem.MkdirAll(magicTempDir.Path(), 0o755); err != nil {
return fmt.Errorf("create magic temp dir in build context: %w", err)
}
Expand Down Expand Up @@ -1409,11 +1409,11 @@ func maybeDeleteFilesystem(logger log.Func, force bool) error {
// We always expect the magic directory to be set to the default, signifying that
// the user is running envbuilder in a container.
// If this is set to anything else we should bail out to prevent accidental data loss.
defaultMagicDir := constants.MagicDir("")
// defaultMagicDir := constants.MagicDir("")
kanikoDir, ok := os.LookupEnv("KANIKO_DIR")
if !ok || strings.TrimSpace(kanikoDir) != defaultMagicDir.Path() {
if !ok || strings.TrimSpace(kanikoDir) != constants.DefaultMagicDir.Path() {
if !force {
logger(log.LevelError, "KANIKO_DIR is not set to %s. Bailing!\n", defaultMagicDir.Path())
logger(log.LevelError, "KANIKO_DIR is not set to %s. Bailing!\n", constants.DefaultMagicDir.Path())
logger(log.LevelError, "To bypass this check, set FORCE_SAFE=true.")
return errors.New("safety check failed")
}
Expand Down
2 changes: 1 addition & 1 deletion integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func TestBuildFromDockerfile(t *testing.T) {
require.Equal(t, "hello", strings.TrimSpace(output))

// Verify that the Docker configuration secret file is removed
configJSONContainerPath := filepath.Join(constants.MagicDir("").Path(), "config.json")
configJSONContainerPath := constants.DefaultMagicDir.Join("config.json")
output = execContainer(t, ctr, "stat "+configJSONContainerPath)
require.Contains(t, output, "No such file or directory")
}
Expand Down
3 changes: 1 addition & 2 deletions options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/base64"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/coder/envbuilder/constants"
Expand Down Expand Up @@ -465,7 +464,7 @@ func (o *Options) CLI() serpent.OptionSet {
Flag: "remote-repo-dir",
Env: WithEnvPrefix("REMOTE_REPO_DIR"),
Value: serpent.StringOf(&o.RemoteRepoDir),
Default: filepath.Join(constants.MagicDir("").Path(), "repo"),
Default: constants.DefaultMagicDir.Join("repo"),
Hidden: true,
Description: "Specify the destination directory for the cloned repo when using remote repo build mode.",
},
Expand Down
0