From 3c6dc12eb8cf91d6777ac91a1080bb473c522971 Mon Sep 17 00:00:00 2001 From: Juan Bustamante Date: Wed, 24 Apr 2024 10:07:19 -0500 Subject: [PATCH] Adding new implementation for parsing volumen input when running on darwin. This is important because docker 26 did a refactoring and safepath.Join is not available for darwin. This is a workaround for now Signed-off-by: Juan Bustamante --- go.mod | 1 + go.sum | 2 ++ pkg/client/build.go | 26 -------------- pkg/client/build_test.go | 6 +++- pkg/client/process_volumes.go | 53 ++++++++++++++++++++++++++++ pkg/client/process_volumes_darwin.go | 51 ++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 27 deletions(-) create mode 100644 pkg/client/process_volumes.go create mode 100644 pkg/client/process_volumes_darwin.go diff --git a/go.mod b/go.mod index 0a12c2651..9ba3a1ca1 100644 --- a/go.mod +++ b/go.mod @@ -117,6 +117,7 @@ require ( github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/selinux v1.11.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect diff --git a/go.sum b/go.sum index 86529ba8a..defabe69e 100644 --- a/go.sum +++ b/go.sum @@ -305,6 +305,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= +github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= diff --git a/pkg/client/build.go b/pkg/client/build.go index 80283774f..34cb2eea9 100644 --- a/pkg/client/build.go +++ b/pkg/client/build.go @@ -21,7 +21,6 @@ import ( "github.com/buildpacks/imgutil/local" "github.com/buildpacks/imgutil/remote" "github.com/buildpacks/lifecycle/platform/files" - "github.com/docker/cli/cli/compose/loader" types "github.com/docker/docker/api/types/image" "github.com/google/go-containerregistry/pkg/name" "github.com/pkg/errors" @@ -1377,31 +1376,6 @@ func randString(n int) string { return string(b) } -func processVolumes(imgOS string, volumes []string) (processed []string, warnings []string, err error) { - for _, v := range volumes { - volume, err := loader.ParseVolume(v) - if err != nil { - return nil, nil, errors.Wrapf(err, "platform volume %q has invalid format", v) - } - - sensitiveDirs := []string{"/cnb", "/layers"} - if imgOS == "windows" { - sensitiveDirs = []string{`c:/cnb`, `c:\cnb`, `c:/layers`, `c:\layers`} - } - for _, p := range sensitiveDirs { - if strings.HasPrefix(strings.ToLower(volume.Target), p) { - warnings = append(warnings, fmt.Sprintf("Mounting to a sensitive directory %s", style.Symbol(volume.Target))) - } - mode := "ro" - if !volume.ReadOnly { - mode = "rw" - } - processed = append(processed, fmt.Sprintf("%s:%s:%s", volume.Source, volume.Target, mode)) - } - } - return processed, warnings, nil -} - func (c *Client) logImageNameAndSha(ctx context.Context, publish bool, imageRef name.Reference) error { // The image name and sha are printed in the lifecycle logs, and there is no need to print it again, unless output is suppressed. if !logging.IsQuiet(c.logger) { diff --git a/pkg/client/build_test.go b/pkg/client/build_test.go index 27cd02d23..7361c467e 100644 --- a/pkg/client/build_test.go +++ b/pkg/client/build_test.go @@ -2763,7 +2763,11 @@ api = "0.2" Volumes: []string{":::"}, }, }) - h.AssertError(t, err, `platform volume ":::" has invalid format: invalid volume specification: ':::'`) + if runtime.GOOS == "darwin" { + h.AssertError(t, err, `platform volume ":::" has invalid format: invalid spec: :::: empty section between colons`) + } else { + h.AssertError(t, err, `platform volume ":::" has invalid format: invalid volume specification: ':::'`) + } }) }) diff --git a/pkg/client/process_volumes.go b/pkg/client/process_volumes.go new file mode 100644 index 000000000..b079c6bf6 --- /dev/null +++ b/pkg/client/process_volumes.go @@ -0,0 +1,53 @@ +//go:build linux || windows + +package client + +import ( + "fmt" + "runtime" + "strings" + + "github.com/docker/docker/volume/mounts" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +func processVolumes(imgOS string, volumes []string) (processed []string, warnings []string, err error) { + var parser mounts.Parser + switch "windows" { + case imgOS: + parser = mounts.NewWindowsParser() + case runtime.GOOS: + parser = mounts.NewLCOWParser() + default: + parser = mounts.NewLinuxParser() + } + for _, v := range volumes { + volume, err := parser.ParseMountRaw(v, "") + if err != nil { + return nil, nil, errors.Wrapf(err, "platform volume %q has invalid format", v) + } + + sensitiveDirs := []string{"/cnb", "/layers"} + if imgOS == "windows" { + sensitiveDirs = []string{`c:/cnb`, `c:\cnb`, `c:/layers`, `c:\layers`} + } + for _, p := range sensitiveDirs { + if strings.HasPrefix(strings.ToLower(volume.Spec.Target), p) { + warnings = append(warnings, fmt.Sprintf("Mounting to a sensitive directory %s", style.Symbol(volume.Spec.Target))) + } + } + + processed = append(processed, fmt.Sprintf("%s:%s:%s", volume.Spec.Source, volume.Spec.Target, processMode(volume.Mode))) + } + return processed, warnings, nil +} + +func processMode(mode string) string { + if mode == "" { + return "ro" + } + + return mode +} diff --git a/pkg/client/process_volumes_darwin.go b/pkg/client/process_volumes_darwin.go new file mode 100644 index 000000000..35ea69cc9 --- /dev/null +++ b/pkg/client/process_volumes_darwin.go @@ -0,0 +1,51 @@ +package client + +import ( + "fmt" + "strings" + + "github.com/docker/cli/cli/compose/loader" + "github.com/docker/cli/cli/compose/types" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +func processVolumes(imgOS string, volumes []string) (processed []string, warnings []string, err error) { + for _, v := range volumes { + volume, err := parseVolume(v) + if err != nil { + return nil, nil, err + } + sensitiveDirs := []string{"/cnb", "/layers"} + if imgOS == "windows" { + sensitiveDirs = []string{`c:/cnb`, `c:\cnb`, `c:/layers`, `c:\layers`} + } + for _, p := range sensitiveDirs { + if strings.HasPrefix(strings.ToLower(volume.Target), p) { + warnings = append(warnings, fmt.Sprintf("Mounting to a sensitive directory %s", style.Symbol(volume.Target))) + } + } + mode := "ro" + if strings.HasSuffix(v, ":rw") && !volume.ReadOnly { + mode = "rw" + } + processed = append(processed, fmt.Sprintf("%s:%s:%s", volume.Source, volume.Target, mode)) + } + return processed, warnings, nil +} + +func parseVolume(volume string) (types.ServiceVolumeConfig, error) { + // volume format: ':[:]' + split := strings.Split(volume, ":") + if len(split) == 3 { + if split[2] != "ro" && split[2] != "rw" && !strings.Contains(split[2], "volume-opt") { + return types.ServiceVolumeConfig{}, errors.New(fmt.Sprintf("platform volume %q has invalid format: invalid mode: %s", volume, split[2])) + } + } + config, err := loader.ParseVolume(volume) + if err != nil { + return config, errors.Wrapf(err, "platform volume %q has invalid format", volume) + } + return config, nil +}