github.1git.de/docker/cli@v26.1.3+incompatible/cli/command/volume/create_test.go (about)

     1  package volume
     2  
     3  import (
     4  	"io"
     5  	"reflect"
     6  	"sort"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/docker/cli/internal/test"
    11  	"github.com/docker/docker/api/types/volume"
    12  	"github.com/pkg/errors"
    13  	"gotest.tools/v3/assert"
    14  	is "gotest.tools/v3/assert/cmp"
    15  )
    16  
    17  func TestVolumeCreateErrors(t *testing.T) {
    18  	testCases := []struct {
    19  		args             []string
    20  		flags            map[string]string
    21  		volumeCreateFunc func(volume.CreateOptions) (volume.Volume, error)
    22  		expectedError    string
    23  	}{
    24  		{
    25  			args: []string{"volumeName"},
    26  			flags: map[string]string{
    27  				"name": "volumeName",
    28  			},
    29  			expectedError: "conflicting options: either specify --name or provide positional arg, not both",
    30  		},
    31  		{
    32  			args:          []string{"too", "many"},
    33  			expectedError: "requires at most 1 argument",
    34  		},
    35  		{
    36  			volumeCreateFunc: func(createBody volume.CreateOptions) (volume.Volume, error) {
    37  				return volume.Volume{}, errors.Errorf("error creating volume")
    38  			},
    39  			expectedError: "error creating volume",
    40  		},
    41  	}
    42  	for _, tc := range testCases {
    43  		cmd := newCreateCommand(
    44  			test.NewFakeCli(&fakeClient{
    45  				volumeCreateFunc: tc.volumeCreateFunc,
    46  			}),
    47  		)
    48  		cmd.SetArgs(tc.args)
    49  		for key, value := range tc.flags {
    50  			cmd.Flags().Set(key, value)
    51  		}
    52  		cmd.SetOut(io.Discard)
    53  		cmd.SetErr(io.Discard)
    54  		assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
    55  	}
    56  }
    57  
    58  func TestVolumeCreateWithName(t *testing.T) {
    59  	name := "foo"
    60  	cli := test.NewFakeCli(&fakeClient{
    61  		volumeCreateFunc: func(body volume.CreateOptions) (volume.Volume, error) {
    62  			if body.Name != name {
    63  				return volume.Volume{}, errors.Errorf("expected name %q, got %q", name, body.Name)
    64  			}
    65  			return volume.Volume{
    66  				Name: body.Name,
    67  			}, nil
    68  		},
    69  	})
    70  
    71  	buf := cli.OutBuffer()
    72  
    73  	// Test by flags
    74  	cmd := newCreateCommand(cli)
    75  	cmd.Flags().Set("name", name)
    76  	assert.NilError(t, cmd.Execute())
    77  	assert.Check(t, is.Equal(name, strings.TrimSpace(buf.String())))
    78  
    79  	// Then by args
    80  	buf.Reset()
    81  	cmd = newCreateCommand(cli)
    82  	cmd.SetArgs([]string{name})
    83  	assert.NilError(t, cmd.Execute())
    84  	assert.Check(t, is.Equal(name, strings.TrimSpace(buf.String())))
    85  }
    86  
    87  func TestVolumeCreateWithFlags(t *testing.T) {
    88  	expectedDriver := "foo"
    89  	expectedOpts := map[string]string{
    90  		"bar": "1",
    91  		"baz": "baz",
    92  	}
    93  	expectedLabels := map[string]string{
    94  		"lbl1": "v1",
    95  		"lbl2": "v2",
    96  	}
    97  	name := "banana"
    98  
    99  	cli := test.NewFakeCli(&fakeClient{
   100  		volumeCreateFunc: func(body volume.CreateOptions) (volume.Volume, error) {
   101  			if body.Name != "" {
   102  				return volume.Volume{}, errors.Errorf("expected empty name, got %q", body.Name)
   103  			}
   104  			if body.Driver != expectedDriver {
   105  				return volume.Volume{}, errors.Errorf("expected driver %q, got %q", expectedDriver, body.Driver)
   106  			}
   107  			if !reflect.DeepEqual(body.DriverOpts, expectedOpts) {
   108  				return volume.Volume{}, errors.Errorf("expected drivers opts %v, got %v", expectedOpts, body.DriverOpts)
   109  			}
   110  			if !reflect.DeepEqual(body.Labels, expectedLabels) {
   111  				return volume.Volume{}, errors.Errorf("expected labels %v, got %v", expectedLabels, body.Labels)
   112  			}
   113  			return volume.Volume{
   114  				Name: name,
   115  			}, nil
   116  		},
   117  	})
   118  
   119  	cmd := newCreateCommand(cli)
   120  	cmd.Flags().Set("driver", "foo")
   121  	cmd.Flags().Set("opt", "bar=1")
   122  	cmd.Flags().Set("opt", "baz=baz")
   123  	cmd.Flags().Set("label", "lbl1=v1")
   124  	cmd.Flags().Set("label", "lbl2=v2")
   125  	assert.NilError(t, cmd.Execute())
   126  	assert.Check(t, is.Equal(name, strings.TrimSpace(cli.OutBuffer().String())))
   127  }
   128  
   129  func TestVolumeCreateCluster(t *testing.T) {
   130  	cli := test.NewFakeCli(&fakeClient{
   131  		volumeCreateFunc: func(body volume.CreateOptions) (volume.Volume, error) {
   132  			if body.Driver == "csi" && body.ClusterVolumeSpec == nil {
   133  				return volume.Volume{}, errors.New("expected ClusterVolumeSpec, but none present")
   134  			}
   135  			if body.Driver == "notcsi" && body.ClusterVolumeSpec != nil {
   136  				return volume.Volume{}, errors.New("expected no ClusterVolumeSpec, but present")
   137  			}
   138  			return volume.Volume{}, nil
   139  		},
   140  	})
   141  
   142  	cmd := newCreateCommand(cli)
   143  	cmd.Flags().Set("type", "block")
   144  	cmd.Flags().Set("group", "gronp")
   145  	cmd.Flags().Set("driver", "csi")
   146  	cmd.SetArgs([]string{"name"})
   147  
   148  	assert.NilError(t, cmd.Execute())
   149  
   150  	cmd = newCreateCommand(cli)
   151  	cmd.Flags().Set("driver", "notcsi")
   152  	cmd.SetArgs([]string{"name"})
   153  
   154  	assert.NilError(t, cmd.Execute())
   155  }
   156  
   157  func TestVolumeCreateClusterOpts(t *testing.T) {
   158  	expectedBody := volume.CreateOptions{
   159  		Name:       "name",
   160  		Driver:     "csi",
   161  		DriverOpts: map[string]string{},
   162  		Labels:     map[string]string{},
   163  		ClusterVolumeSpec: &volume.ClusterVolumeSpec{
   164  			Group: "gronp",
   165  			AccessMode: &volume.AccessMode{
   166  				Scope:   volume.ScopeMultiNode,
   167  				Sharing: volume.SharingOneWriter,
   168  				// TODO(dperny): support mount options
   169  				MountVolume: &volume.TypeMount{},
   170  			},
   171  			// TODO(dperny): topology requirements
   172  			CapacityRange: &volume.CapacityRange{
   173  				RequiredBytes: 1234,
   174  				LimitBytes:    567890,
   175  			},
   176  			Secrets: []volume.Secret{
   177  				{Key: "key1", Secret: "secret1"},
   178  				{Key: "key2", Secret: "secret2"},
   179  			},
   180  			Availability: volume.AvailabilityActive,
   181  			AccessibilityRequirements: &volume.TopologyRequirement{
   182  				Requisite: []volume.Topology{
   183  					{Segments: map[string]string{"region": "R1", "zone": "Z1"}},
   184  					{Segments: map[string]string{"region": "R1", "zone": "Z2"}},
   185  					{Segments: map[string]string{"region": "R1", "zone": "Z3"}},
   186  				},
   187  				Preferred: []volume.Topology{
   188  					{Segments: map[string]string{"region": "R1", "zone": "Z2"}},
   189  					{Segments: map[string]string{"region": "R1", "zone": "Z3"}},
   190  				},
   191  			},
   192  		},
   193  	}
   194  
   195  	cli := test.NewFakeCli(&fakeClient{
   196  		volumeCreateFunc: func(body volume.CreateOptions) (volume.Volume, error) {
   197  			sort.SliceStable(body.ClusterVolumeSpec.Secrets, func(i, j int) bool {
   198  				return body.ClusterVolumeSpec.Secrets[i].Key < body.ClusterVolumeSpec.Secrets[j].Key
   199  			})
   200  			assert.DeepEqual(t, body, expectedBody)
   201  			return volume.Volume{}, nil
   202  		},
   203  	})
   204  
   205  	cmd := newCreateCommand(cli)
   206  	cmd.SetArgs([]string{"name"})
   207  	cmd.Flags().Set("driver", "csi")
   208  	cmd.Flags().Set("group", "gronp")
   209  	cmd.Flags().Set("scope", "multi")
   210  	cmd.Flags().Set("sharing", "onewriter")
   211  	cmd.Flags().Set("type", "mount")
   212  	cmd.Flags().Set("sharing", "onewriter")
   213  	cmd.Flags().Set("required-bytes", "1234")
   214  	cmd.Flags().Set("limit-bytes", "567890")
   215  
   216  	cmd.Flags().Set("secret", "key1=secret1")
   217  	cmd.Flags().Set("secret", "key2=secret2")
   218  
   219  	cmd.Flags().Set("topology-required", "region=R1,zone=Z1")
   220  	cmd.Flags().Set("topology-required", "region=R1,zone=Z2")
   221  	cmd.Flags().Set("topology-required", "region=R1,zone=Z3")
   222  
   223  	cmd.Flags().Set("topology-preferred", "region=R1,zone=Z2")
   224  	cmd.Flags().Set("topology-preferred", "region=R1,zone=Z3")
   225  
   226  	cmd.Execute()
   227  }