github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/integration/volume/volume_test.go (about) 1 package volume 2 3 import ( 4 "context" 5 "net/http" 6 "os" 7 "path/filepath" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/filters" 14 "github.com/docker/docker/api/types/volume" 15 clientpkg "github.com/docker/docker/client" 16 "github.com/docker/docker/integration/internal/container" 17 "github.com/docker/docker/testutil/request" 18 "github.com/google/go-cmp/cmp/cmpopts" 19 "gotest.tools/v3/assert" 20 "gotest.tools/v3/assert/cmp" 21 is "gotest.tools/v3/assert/cmp" 22 ) 23 24 func TestVolumesCreateAndList(t *testing.T) { 25 defer setupTest(t)() 26 client := testEnv.APIClient() 27 ctx := context.Background() 28 29 name := t.Name() 30 // Windows file system is case insensitive 31 if testEnv.OSType == "windows" { 32 name = strings.ToLower(name) 33 } 34 vol, err := client.VolumeCreate(ctx, volume.CreateOptions{ 35 Name: name, 36 }) 37 assert.NilError(t, err) 38 39 expected := volume.Volume{ 40 // Ignore timestamp of CreatedAt 41 CreatedAt: vol.CreatedAt, 42 Driver: "local", 43 Scope: "local", 44 Name: name, 45 Mountpoint: filepath.Join(testEnv.DaemonInfo.DockerRootDir, "volumes", name, "_data"), 46 } 47 assert.Check(t, is.DeepEqual(vol, expected, cmpopts.EquateEmpty())) 48 49 volList, err := client.VolumeList(ctx, filters.Args{}) 50 assert.NilError(t, err) 51 assert.Assert(t, len(volList.Volumes) > 0) 52 53 volumes := volList.Volumes[:0] 54 for _, v := range volList.Volumes { 55 if v.Name == vol.Name { 56 volumes = append(volumes, v) 57 } 58 } 59 60 assert.Check(t, is.Equal(len(volumes), 1)) 61 assert.Check(t, volumes[0] != nil) 62 assert.Check(t, is.DeepEqual(*volumes[0], expected, cmpopts.EquateEmpty())) 63 } 64 65 func TestVolumesRemove(t *testing.T) { 66 defer setupTest(t)() 67 client := testEnv.APIClient() 68 ctx := context.Background() 69 70 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 71 72 id := container.Create(ctx, t, client, container.WithVolume(prefix+slash+"foo")) 73 74 c, err := client.ContainerInspect(ctx, id) 75 assert.NilError(t, err) 76 vname := c.Mounts[0].Name 77 78 err = client.VolumeRemove(ctx, vname, false) 79 assert.Check(t, is.ErrorContains(err, "volume is in use")) 80 81 err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{ 82 Force: true, 83 }) 84 assert.NilError(t, err) 85 86 err = client.VolumeRemove(ctx, vname, false) 87 assert.NilError(t, err) 88 } 89 90 func TestVolumesInspect(t *testing.T) { 91 defer setupTest(t)() 92 client := testEnv.APIClient() 93 ctx := context.Background() 94 95 now := time.Now() 96 vol, err := client.VolumeCreate(ctx, volume.CreateOptions{}) 97 assert.NilError(t, err) 98 99 inspected, err := client.VolumeInspect(ctx, vol.Name) 100 assert.NilError(t, err) 101 102 assert.Check(t, is.DeepEqual(inspected, vol, cmpopts.EquateEmpty())) 103 104 // comparing CreatedAt field time for the new volume to now. Truncate to 1 minute precision to avoid false positive 105 createdAt, err := time.Parse(time.RFC3339, strings.TrimSpace(inspected.CreatedAt)) 106 assert.NilError(t, err) 107 assert.Check(t, createdAt.Unix()-now.Unix() < 60, "CreatedAt (%s) exceeds creation time (%s) 60s", createdAt, now) 108 109 // update atime and mtime for the "_data" directory (which would happen during volume initialization) 110 modifiedAt := time.Now().Local().Add(5 * time.Hour) 111 err = os.Chtimes(inspected.Mountpoint, modifiedAt, modifiedAt) 112 assert.NilError(t, err) 113 114 inspected, err = client.VolumeInspect(ctx, vol.Name) 115 assert.NilError(t, err) 116 117 createdAt2, err := time.Parse(time.RFC3339, strings.TrimSpace(inspected.CreatedAt)) 118 assert.NilError(t, err) 119 120 // Check that CreatedAt didn't change after updating atime and mtime of the "_data" directory 121 // Related issue: #38274 122 assert.Equal(t, createdAt, createdAt2) 123 } 124 125 // TestVolumesInvalidJSON tests that POST endpoints that expect a body return 126 // the correct error when sending invalid JSON requests. 127 func TestVolumesInvalidJSON(t *testing.T) { 128 defer setupTest(t)() 129 130 // POST endpoints that accept / expect a JSON body; 131 endpoints := []string{"/volumes/create"} 132 133 for _, ep := range endpoints { 134 ep := ep 135 t.Run(ep[1:], func(t *testing.T) { 136 t.Parallel() 137 138 t.Run("invalid content type", func(t *testing.T) { 139 res, body, err := request.Post(ep, request.RawString("{}"), request.ContentType("text/plain")) 140 assert.NilError(t, err) 141 assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest)) 142 143 buf, err := request.ReadBody(body) 144 assert.NilError(t, err) 145 assert.Check(t, is.Contains(string(buf), "unsupported Content-Type header (text/plain): must be 'application/json'")) 146 }) 147 148 t.Run("invalid JSON", func(t *testing.T) { 149 res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON) 150 assert.NilError(t, err) 151 assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest)) 152 153 buf, err := request.ReadBody(body) 154 assert.NilError(t, err) 155 assert.Check(t, is.Contains(string(buf), "invalid JSON: invalid character 'i' looking for beginning of object key string")) 156 }) 157 158 t.Run("extra content after JSON", func(t *testing.T) { 159 res, body, err := request.Post(ep, request.RawString(`{} trailing content`), request.JSON) 160 assert.NilError(t, err) 161 assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest)) 162 163 buf, err := request.ReadBody(body) 164 assert.NilError(t, err) 165 assert.Check(t, is.Contains(string(buf), "unexpected content after JSON")) 166 }) 167 168 t.Run("empty body", func(t *testing.T) { 169 // empty body should not produce an 500 internal server error, or 170 // any 5XX error (this is assuming the request does not produce 171 // an internal server error for another reason, but it shouldn't) 172 res, _, err := request.Post(ep, request.RawString(``), request.JSON) 173 assert.NilError(t, err) 174 assert.Check(t, res.StatusCode < http.StatusInternalServerError) 175 }) 176 }) 177 } 178 } 179 180 func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) { 181 if testEnv.OSType == "windows" { 182 return "c:", `\` 183 } 184 return "", "/" 185 } 186 187 func TestVolumePruneAnonymous(t *testing.T) { 188 defer setupTest(t)() 189 190 client := testEnv.APIClient() 191 ctx := context.Background() 192 193 // Create an anonymous volume 194 v, err := client.VolumeCreate(ctx, volume.CreateOptions{}) 195 assert.NilError(t, err) 196 197 // Create a named volume 198 vNamed, err := client.VolumeCreate(ctx, volume.CreateOptions{ 199 Name: "test", 200 }) 201 assert.NilError(t, err) 202 203 // Prune anonymous volumes 204 pruneReport, err := client.VolumesPrune(ctx, filters.Args{}) 205 assert.NilError(t, err) 206 assert.Check(t, is.Equal(len(pruneReport.VolumesDeleted), 1)) 207 assert.Check(t, is.Equal(pruneReport.VolumesDeleted[0], v.Name)) 208 209 _, err = client.VolumeInspect(ctx, vNamed.Name) 210 assert.NilError(t, err) 211 212 // Prune all volumes 213 _, err = client.VolumeCreate(ctx, volume.CreateOptions{}) 214 assert.NilError(t, err) 215 216 pruneReport, err = client.VolumesPrune(ctx, filters.NewArgs(filters.Arg("all", "1"))) 217 assert.NilError(t, err) 218 assert.Check(t, is.Equal(len(pruneReport.VolumesDeleted), 2)) 219 220 // Validate that older API versions still have the old behavior of pruning all local volumes 221 clientOld, err := clientpkg.NewClientWithOpts(clientpkg.FromEnv, clientpkg.WithVersion("1.41")) 222 assert.NilError(t, err) 223 defer clientOld.Close() 224 assert.Equal(t, clientOld.ClientVersion(), "1.41") 225 226 v, err = client.VolumeCreate(ctx, volume.CreateOptions{}) 227 assert.NilError(t, err) 228 vNamed, err = client.VolumeCreate(ctx, volume.CreateOptions{Name: "test-api141"}) 229 assert.NilError(t, err) 230 231 pruneReport, err = clientOld.VolumesPrune(ctx, filters.Args{}) 232 assert.NilError(t, err) 233 assert.Check(t, is.Equal(len(pruneReport.VolumesDeleted), 2)) 234 assert.Check(t, cmp.Contains(pruneReport.VolumesDeleted, v.Name)) 235 assert.Check(t, cmp.Contains(pruneReport.VolumesDeleted, vNamed.Name)) 236 }