github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/integration/volume/volume_test.go (about)

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