From 7076d4bb0ca6356c78f52bc7689a4a9a1e8307c9 Mon Sep 17 00:00:00 2001 From: Qiang Li Date: Mon, 8 Jul 2024 19:15:22 -0700 Subject: [PATCH] Update comment/fix typo set default env vars support VERSION build arg Signed-off-by: Qiang Li --- cli/command/app/install.go | 53 +++++++++++++++++++++++++++++--------- cli/command/app/launch.go | 5 ++++ cli/command/app/opts.go | 44 +++++++++++++++++++++++-------- cli/command/app/utils.go | 3 +-- cli/command/image/build.go | 4 +++ e2e/app/install_test.go | 8 +++--- e2e/app/launch_test.go | 4 +-- 7 files changed, 90 insertions(+), 31 deletions(-) diff --git a/cli/command/app/install.go b/cli/command/app/install.go index 3f5f86d67a43..ccd0d013ae94 100644 --- a/cli/command/app/install.go +++ b/cli/command/app/install.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "runtime" + "strings" "time" "github.com/docker/cli/cli" @@ -109,17 +110,18 @@ func addInstallFlags(flags *pflag.FlagSet, dest string, trust bool) *AppOptions // `docker cp` flags include(flags, eflags, []string{"archive", "follow-link"}) - // install specific build args - addBuildArgs(flags) - return options } -func addBuildArgs(flags *pflag.FlagSet) { - flag := flags.Lookup("build-arg") - if flag != nil { - flag.Value.Set("HOSTOS=" + runtime.GOOS) - flag.Value.Set("HOSTARCH=" + runtime.GOARCH) +func setBuildArgs(options *AppOptions) { + bopts := options.buildOpts + if bopts != nil { + bopts.SetBuildArg("HOSTOS=" + runtime.GOOS) + bopts.SetBuildArg("HOSTARCH=" + runtime.GOARCH) + } + version := options.buildVersion() + if version != "" { + bopts.SetBuildArg("VERSION=" + options.buildVersion()) } } @@ -146,8 +148,22 @@ func installApp(ctx context.Context, adapter cliAdapter, flags *pflag.FlagSet, o return nil } +func setDefaultEnv() { + if os.Getenv("DOCKER_BUILDKIT") == "" { + os.Setenv("DOCKER_BUILDKIT", "1") + } + + platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) + if os.Getenv("DOCKER_DEFAULT_PLATFORM") == "" { + os.Setenv("DOCKER_DEFAULT_PLATFORM", platform) + } +} + // runInstall calls the build, run, and cp commands func runInstall(ctx context.Context, dockerCli cliAdapter, flags *pflag.FlagSet, options *AppOptions) (string, error) { + setDefaultEnv() + setBuildArgs(options) + iid, err := buildImage(ctx, dockerCli, options) if err != nil { return "", err @@ -272,8 +288,8 @@ func runPostInstall(dockerCli cliAdapter, dir string, options *AppOptions) (stri // installOne creates a symlink to the only file in appPath func installOne(egress, runPath, binPath, appPath string) (string, error) { - appName := filepath.Base(runPath) - if err := validateName(appName); err != nil { + appName, err := makeAppName(runPath) + if err != nil { return "", err } link := filepath.Join(binPath, appName) @@ -284,10 +300,11 @@ func installOne(egress, runPath, binPath, appPath string) (string, error) { // installRunFile creates a symlink to the run file in appPath // use the base name as the app name func installRunFile(egress, runPath, binPath, appPath string) (string, error) { - appName := filepath.Base(appPath) - if err := validateName(appName); err != nil { + appName, err := makeAppName(appPath) + if err != nil { return "", err } + link := filepath.Join(binPath, appName) target := filepath.Join(appPath, filepath.Base(runPath)) return install(link, target, egress, binPath, appPath) @@ -367,3 +384,15 @@ func installCustom(dir string, appPath string, options *AppOptions) error { return launch(installer, options) } + +// makeAppName derives the app name from the base name of the path +// after removing the version and extension +func makeAppName(path string) (string, error) { + n := filepath.Base(path) + n = strings.SplitN(n, "@", 2)[0] + n = strings.SplitN(n, ".", 2)[0] + if err := validateName(n); err != nil { + return "", err + } + return n, nil +} diff --git a/cli/command/app/launch.go b/cli/command/app/launch.go index 1005cdd8cf13..3728d1a8ad85 100644 --- a/cli/command/app/launch.go +++ b/cli/command/app/launch.go @@ -89,6 +89,11 @@ func launch(app string, options *AppOptions) error { } envs["DOCKER_APP_BASE"] = options._appBase + appPath, err := options.appPath() + if err != nil { + return err + } + envs["DOCKER_APP_PATH"] = appPath return spawn(app, options.launchArgs(), envs, options.detach) } diff --git a/cli/command/app/opts.go b/cli/command/app/opts.go index 6e127b206891..500f2a109fe8 100644 --- a/cli/command/app/opts.go +++ b/cli/command/app/opts.go @@ -23,7 +23,7 @@ const installerName = "install" // uninstallerName is the executable name for custom installation const uninstallerName = "uninstall" -// namePattern is for validating egress and app name +// namePattern is for validating app name const namePattern = "^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$" var nameRegexp = regexp.MustCompile(namePattern) @@ -35,8 +35,29 @@ func validateName(s string) error { return nil } +// semverPattern is for splitting semver from a context path/URL +const semverPattern = `@v?\d+(\.\d+)?(\.\d+)?$` + +var semverRegexp = regexp.MustCompile(semverPattern) + +func splitSemver(s string) (string, string) { + if semverRegexp.MatchString(s) { + idx := strings.LastIndex(s, "@") + // unlikely otherwise ignore + if idx == -1 { + return s, "" + } + v := s[idx+1:] + if v[0] == 'v' { + v = v[1:] + } + return s[:idx], v + } + return s, "" +} + // defaultAppBase is docker app's base location specified by -// DOCKER_APP_BASE environment variable defaulted to ~/.docker-app/ +// DOCKER_APP_BASE environment variable defaulted to ~/.docker/app/ func defaultAppBase() string { if base := os.Getenv("DOCKER_APP_BASE"); base != "" { return filepath.Clean(base) @@ -100,7 +121,16 @@ func (o *AppOptions) buildContext() string { if len(o._args) == 0 { return "." } - return o._args[0] + c, _ := splitSemver(o._args[0]) + return c +} + +func (o *AppOptions) buildVersion() string { + if len(o._args) == 0 { + return "" + } + _, v := splitSemver(o._args[0]) + return v } // runArgs returns the command line args for running the container @@ -182,10 +212,6 @@ func validateAppOptions(options *AppOptions) error { return errors.New("egress is required") } - name := filepath.Base(options.egress) - if err := validateName(name); err != nil { - return fmt.Errorf("invalid egress path: %s %v", options.egress, err) - } return nil } @@ -226,9 +252,5 @@ func makeAppPath(appBase, s string) (string, error) { return "", fmt.Errorf("missing path: %v", u) } - name := filepath.Base(u.Path) - if err := validateName(name); err != nil { - return "", fmt.Errorf("invalid path: %s %v", u.Path, err) - } return filepath.Join(appBase, "pkg", u.Scheme, u.Host, u.Path), nil } diff --git a/cli/command/app/utils.go b/cli/command/app/utils.go index 48f415b72447..bc101306906e 100644 --- a/cli/command/app/utils.go +++ b/cli/command/app/utils.go @@ -11,8 +11,7 @@ import ( "syscall" ) -// spawn runs the specified command and returns the PID -// of the spawned process +// spawn runs the specified command func spawn(bin string, args []string, envMap map[string]string, detach bool) error { toEnv := func() []string { var env []string diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 63cfb72a69f1..a1e40de71d29 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -90,6 +90,10 @@ func (o *BuildOptions) SetImageIDFile(imageIDFile string) { o.imageIDFile = imageIDFile } +func (o *BuildOptions) SetBuildArg(value string) { + o.buildArgs.Set(value) +} + func newBuildOptions() BuildOptions { ulimits := make(map[string]*container.Ulimit) return BuildOptions{ diff --git a/e2e/app/install_test.go b/e2e/app/install_test.go index ddb788903d16..fdfa167eaa15 100644 --- a/e2e/app/install_test.go +++ b/e2e/app/install_test.go @@ -25,7 +25,7 @@ func TestInstallOne(t *testing.T) { fs.WithDir(buildCtx, fs.WithFile("Dockerfile", fmt.Sprintf(` FROM %s - ARG HOSTOS HOSTARCH + ARG HOSTOS HOSTARCH VERSION COPY cool /egress/%s CMD ["echo", "'cool' app successfully built!"] `, fixtures.AlpineImage, coolApp)), @@ -74,7 +74,7 @@ func TestInstallMulti(t *testing.T) { fs.WithDir(buildCtx, fs.WithFile("Dockerfile", fmt.Sprintf(` FROM %s - ARG HOSTOS HOSTARCH + ARG HOSTOS HOSTARCH VERSION COPY . /egress CMD ["echo", "'multi' app successfully built!"] `, fixtures.AlpineImage)), @@ -147,7 +147,7 @@ func TestInstallCustom(t *testing.T) { fs.WithDir(buildCtx, fs.WithFile("Dockerfile", fmt.Sprintf(` FROM %s - ARG HOSTOS HOSTARCH + ARG HOSTOS HOSTARCH VERSION COPY . /egress CMD ["echo", "'custom' app successfully built!"] `, fixtures.AlpineImage)), @@ -208,7 +208,7 @@ func TestInstallCustomDestination(t *testing.T) { fs.WithDir(buildCtx, fs.WithFile("Dockerfile", fmt.Sprintf(` FROM %s - ARG HOSTOS HOSTARCH + ARG HOSTOS HOSTARCH VERSION COPY . %s CMD ["echo", "'service' successfully built!"] `, fixtures.AlpineImage, egress)), diff --git a/e2e/app/launch_test.go b/e2e/app/launch_test.go index 290d59a407e4..07f9ea4cd8da 100644 --- a/e2e/app/launch_test.go +++ b/e2e/app/launch_test.go @@ -34,7 +34,7 @@ func TestLaunchOne(t *testing.T) { fs.WithDir(buildCtx, fs.WithFile("Dockerfile", fmt.Sprintf(` FROM %s - ARG HOSTOS HOSTARCH + ARG HOSTOS HOSTARCH VERSION COPY cool /egress/%s CMD ["echo", "'cool' app successfully built!"] `, fixtures.AlpineImage, coolApp)), @@ -87,7 +87,7 @@ func TestLaunchMulti(t *testing.T) { fs.WithDir(buildCtx, fs.WithFile("Dockerfile", fmt.Sprintf(` FROM %s - ARG HOSTOS HOSTARCH + ARG HOSTOS HOSTARCH VERSION COPY . /egress CMD ["echo", "'multi' app successfully built!"] `, fixtures.AlpineImage)),