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 }