Skip to content

Fix pruning anon volume created from image config#45147

Merged
thaJeztah merged 1 commit intomoby:masterfrom
cpuguy83:fix_volume_anon_from_image
Mar 14, 2023
Merged

Fix pruning anon volume created from image config#45147
thaJeztah merged 1 commit intomoby:masterfrom
cpuguy83:fix_volume_anon_from_image

Conversation

@cpuguy83
Copy link
Copy Markdown
Member

@cpuguy83 cpuguy83 commented Mar 11, 2023

Volumes created from the image config were not being pruned because the volume service did not think they were anonymous since the code to create passes along a generated name instead of letting the volume service generate it.

This changes the code path to have the volume service generate the name instead of doing it ahead of time.

@cpuguy83 cpuguy83 marked this pull request as ready for review March 11, 2023 22:38
@cpuguy83 cpuguy83 requested a review from neersighted March 11, 2023 22:38
@cpuguy83 cpuguy83 force-pushed the fix_volume_anon_from_image branch from 594ee61 to 96aa0a9 Compare March 11, 2023 22:39
@neersighted neersighted added status/2-code-review kind/bug Bugs are bugs. The cause may or may not be known at triage time so debugging may be needed. process/cherry-pick area/volumes Volumes labels Mar 12, 2023
@neersighted neersighted added this to the v-next milestone Mar 12, 2023
@thaJeztah
Copy link
Copy Markdown
Member

Linter is failing;

integration/volume/volume_test.go:6:2: `io/ioutil` is in the denylist (depguard)
	^

@thaJeztah thaJeztah added kind/bugfix PR's that fix bugs and removed kind/bug Bugs are bugs. The cause may or may not be known at triage time so debugging may be needed. labels Mar 12, 2023
}

v, err := daemon.volumes.Create(context.TODO(), name, hostConfig.VolumeDriver, volumeopts.WithCreateReference(container.ID))
v, err := daemon.volumes.Create(context.TODO(), "", hostConfig.VolumeDriver, volumeopts.WithCreateReference(container.ID))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For a follow-up; given that name is optional, we should change this signature;

  • move name to options
  • and/or require "anonymous" to be set as option (if we need to disambiguate "forgot to pass a name" from "must be anonymous, and generate a name for me")

func (s *VolumesService) Create(ctx context.Context, name, driverName string, options ...opts.CreateOption) (*volumetypes.Volume, error) {
if name == "" {
name = stringid.GenerateRandomID()
options = append(options, opts.WithCreateLabel(AnonymousLabel, ""))
}

@thaJeztah thaJeztah force-pushed the fix_volume_anon_from_image branch from 96aa0a9 to 8a6d4d5 Compare March 12, 2023 15:05
@thaJeztah
Copy link
Copy Markdown
Member

Copy link
Copy Markdown
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

LGTM (assuming CI is happy)

@thaJeztah
Copy link
Copy Markdown
Member

Hm... this one looks related (failing on windows);

INFO: make.ps1 starting at 03/12/2023 15:23:19
Running D:\a\moby\moby\go\src\github.com\docker\docker\integration\build
INFO: Windows Base image is  mcr.microsoft.com/windows/servercore:ltsc2022
...
=== FAIL: github.com/docker/docker/integration/volume TestVolumePruneAnonFromImage (0.28s)
    container.go:52: assertion failed: error is not nil: Error response from daemon: No such image: busybox:withvolume

Other test is probably a flaky;

=== FAIL: github.com/docker/docker/integration/container TestLogs/driver_local/without_tty/only_stderr (1.65s)
    logs_test.go:137: assertion failed: error is not nil: Error response from daemon: failed to create task for container: failed to create shim task: hcs::CreateComputeSystem 5df8dff857afcd0d31dafc15ea17aa0d661b3a4606ba42ed3b2a61fcd96430d6: The parameter is incorrect.: unknown

Comment on lines +257 to +259
_, err = io.Copy(io.Discard, resp.Body)
resp.Body.Close()
assert.NilError(t, err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this need the ugly hack we use elsewhere to check if the image was actually built? (I have a feeling there's still other tests that don't check for this, because of the kinda horrible streaming response);

_, err = io.Copy(out, resp.Body)
resp.Body.Close()
assert.NilError(t, err)
assert.Check(t, is.Contains(out.String(), "Successfully built"))

@thaJeztah
Copy link
Copy Markdown
Member

Pushed a commit to capture the output;

=== RUN   TestVolumePruneAnonFromImage
    volume_test.go:262: assertion failed: string "{\"stream\":\"Step 1/2 : FROM busybox\"}\r\n{\"stream\":\"\\n\"}\r\n{\"stream\":\" ---\\u003e 6071e3b1a29d\\n\"}\r\n{\"stream\":\"Step 2/2 : VOLUME /foo\"}\r\n{\"stream\":\"\\n\"}\r\n{\"errorDetail\":{\"message\":\"Unrecognised volume spec: invalid volume specification: '/foo'\"},\"error\":\"Unrecognised volume spec: invalid volume specification: '/foo'\"}\r\n" does not contain "Successfully built"
    container.go:52: assertion failed: error is not nil: Error response from daemon: No such image: busybox:withvolume
--- FAIL: TestVolumePruneAnonFromImage (0.28s)
FAIL

So, something looks broken indeed in the test (or does the Windows driver not generate a name now for the anonymous volume?)

Or is it something silly and does VOLUME in the Windows Dockerfile need a drive-letter or C:\foo to exist?

@thaJeztah
Copy link
Copy Markdown
Member

This is the error;

Unrecognised volume spec: invalid volume specification: '/foo'

Which comes from;

mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver)
if err != nil {
return fmt.Errorf("Unrecognised volume spec: %v", err)
}

The ParseMountRaw() there is handled by the WindowsParser;

return NewWindowsParser()

Which uses windowsSplitRawSpecRegexp to parse

arr, err := p.splitRawSpec(raw, windowsSplitRawSpecRegexp)

This is where things get a bit confusing, as that RegExp looks to be parsing the raw spec according to the <source>:<destination>:<options> format;

windowsSplitRawSpecRegexp = regexp.MustCompile(`^` + rxSource + rxDestination + rxMode + `$`)

But in the Dockerfile, this would only be destination (not source), unless the raw value here is already processed (or expected to) include the name of the anonymous volume, which the error indicates it isn't (/foo) 🤔

@thaJeztah
Copy link
Copy Markdown
Member

thaJeztah commented Mar 13, 2023

This is where things get a bit confusing, as that RegExp looks to be parsing the raw spec according to the <source>:<destination>:<options> format

Hmm.. I guess that's still "okay" because <source> is optional, and will generate a name once we get further down;

func (p *windowsParser) parseMount(arr []string, raw, volumeDriver string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
var spec mount.Mount
var mode string
switch len(arr) {
case 1:
// Just a destination path in the container
spec.Target = arr[0]

But I guess Windows also needs the same changes as we did for non-windows (i.e., don't generate a name there?)

// If the mountpoint doesn't have a name, generate one.
if len(mp.Name) == 0 {
mp.Name = stringid.GenerateRandomID()
}

@thaJeztah
Copy link
Copy Markdown
Member

Or is it something silly and does VOLUME in the Windows Dockerfile need a drive-letter or C:\foo to exist?

So, yes, it looks like we need something like this;

func (s *DockerCLIBuildSuite) TestBuildEnvironmentReplacementVolume(c *testing.T) {
name := "testbuildenvironmentreplacement"
var volumePath string
if testEnv.OSType == "windows" {
volumePath = "c:/quux"
} else {
volumePath = "/quux"
}

@thaJeztah thaJeztah force-pushed the fix_volume_anon_from_image branch from cca29d4 to 2a6d7d0 Compare March 13, 2023 11:35
@thaJeztah
Copy link
Copy Markdown
Member

mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver)
if err != nil {
return fmt.Errorf("Unrecognised volume spec: %v", err)
}

Looking at the original PR (#16433), that PR added ParseMountRaw() for Windows (which parses the whole <source>:<dest>:<options> syntax), whereas the Linux/Unix code only took care of the destination. Given that config.Volumes here is the (image) config (?), shouldn't it only ever have the destination, not a "raw" spec?

@thaJeztah thaJeztah force-pushed the fix_volume_anon_from_image branch from a4d643f to 8b2bcb7 Compare March 13, 2023 12:41
@thaJeztah
Copy link
Copy Markdown
Member

Almost; looks like Destination is c:\foo (lowercase). ISTR there was some code to always change mounts to lowercase on Windows, so perhaps that's it here?

=== RUN   TestVolumePruneAnonFromImage
    volume_test.go:277: skipping: {Type:volume Name:430fc743010de7cd363f1ea349497520aa234decd45a0c2dba90cdd72f998857 Source:C:\Users\runneradmin\AppData\Local\Temp\moby-root\volumes\430fc743010de7cd363f1ea349497520aa234decd45a0c2dba90cdd72f998857\_data Destination:c:\foo Driver:local Mode: RW:true Propagation:}
    volume_test.go:283: assertion failed: volumeName is ""

@thaJeztah thaJeztah force-pushed the fix_volume_anon_from_image branch from 8b2bcb7 to bb5cbe4 Compare March 13, 2023 13:59
@thaJeztah
Copy link
Copy Markdown
Member

Looks like CI is at least happy with the latest changes (but it needs cleaning up)

@cpuguy83 cpuguy83 force-pushed the fix_volume_anon_from_image branch 3 times, most recently from e1f7c7e to 7810785 Compare March 13, 2023 17:38
@thaJeztah
Copy link
Copy Markdown
Member

@cpuguy83 looks like something broke again 😢

=== RUN   TestVolumePruneAnonFromImage
    container.go:52: assertion failed: error is not nil: Error response from daemon: hcsshim::ExpandScratchSize failed in Win32: The system cannot find the file specified. (0x2)
--- FAIL: TestVolumePruneAnonFromImage (0.21s)

@cpuguy83 cpuguy83 force-pushed the fix_volume_anon_from_image branch from 7810785 to 67a231f Compare March 13, 2023 19:03
@thaJeztah
Copy link
Copy Markdown
Member

@cpuguy83 cpuguy83 force-pushed the fix_volume_anon_from_image branch 2 times, most recently from bdc7126 to 977661c Compare March 13, 2023 21:04

var volumeName string
for _, m := range inspect.Mounts {
if m.Destination != volDest {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like we're back to this part not finding the volume 😞 (I suspect here it expects it to be c:\foo)

@cpuguy83 cpuguy83 force-pushed the fix_volume_anon_from_image branch 3 times, most recently from 82df2c6 to b1a6e9b Compare March 13, 2023 23:38
@cpuguy83
Copy link
Copy Markdown
Member Author

finally it is green.

@thaJeztah
Copy link
Copy Markdown
Member

Oh! My PR was merged, and looks like a (probably minor) merge conflict; I'll do a quick rebase in a bit

Volumes created from the image config were not being pruned because the
volume service did not think they were anonymous since the code to
create passes along a generated name instead of letting the volume
service generate it.

This changes the code path to have the volume service generate the name
instead of doing it ahead of time.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
@thaJeztah thaJeztah force-pushed the fix_volume_anon_from_image branch from b1a6e9b to 146df5f Compare March 14, 2023 10:07
Copy link
Copy Markdown
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

still green; LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants