|
4 | 4 | package volume |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "cmp" |
7 | 8 | "context" |
8 | 9 | "errors" |
9 | 10 | "fmt" |
10 | | - "sort" |
| 11 | + "slices" |
11 | 12 | "strings" |
12 | 13 |
|
13 | 14 | "github.com/docker/cli/cli" |
@@ -117,93 +118,68 @@ func hasClusterVolumeOptionSet(flags *pflag.FlagSet) bool { |
117 | 118 | } |
118 | 119 |
|
119 | 120 | func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions) error { |
120 | | - volOpts := client.VolumeCreateOptions{ |
121 | | - Driver: options.driver, |
122 | | - DriverOpts: options.driverOpts.GetAll(), |
123 | | - Name: options.name, |
124 | | - Labels: opts.ConvertKVStringsToMap(options.labels.GetSlice()), |
| 121 | + res, err := dockerCli.Client().VolumeCreate(ctx, client.VolumeCreateOptions{ |
| 122 | + Driver: options.driver, |
| 123 | + DriverOpts: options.driverOpts.GetAll(), |
| 124 | + Name: options.name, |
| 125 | + Labels: opts.ConvertKVStringsToMap(options.labels.GetSlice()), |
| 126 | + ClusterVolumeSpec: clusterVolumeSpec(options), |
| 127 | + }) |
| 128 | + if err != nil { |
| 129 | + return err |
125 | 130 | } |
126 | | - if options.cluster { |
127 | | - volOpts.ClusterVolumeSpec = &volume.ClusterVolumeSpec{ |
128 | | - Group: options.group, |
129 | | - AccessMode: &volume.AccessMode{ |
130 | | - Scope: volume.Scope(options.scope), |
131 | | - Sharing: volume.SharingMode(options.sharing), |
132 | | - }, |
133 | | - Availability: volume.Availability(options.availability), |
134 | | - } |
135 | | - |
136 | | - switch options.accessType { |
137 | | - case "mount": |
138 | | - volOpts.ClusterVolumeSpec.AccessMode.MountVolume = &volume.TypeMount{} |
139 | | - case "block": |
140 | | - volOpts.ClusterVolumeSpec.AccessMode.BlockVolume = &volume.TypeBlock{} |
141 | | - } |
142 | | - |
143 | | - vcr := &volume.CapacityRange{} |
144 | | - if r := options.requiredBytes.Value(); r >= 0 { |
145 | | - vcr.RequiredBytes = r |
146 | | - } |
147 | | - |
148 | | - if l := options.limitBytes.Value(); l >= 0 { |
149 | | - vcr.LimitBytes = l |
150 | | - } |
151 | | - volOpts.ClusterVolumeSpec.CapacityRange = vcr |
152 | | - |
153 | | - for key, secret := range options.secrets.GetAll() { |
154 | | - volOpts.ClusterVolumeSpec.Secrets = append( |
155 | | - volOpts.ClusterVolumeSpec.Secrets, |
156 | | - volume.Secret{ |
157 | | - Key: key, |
158 | | - Secret: secret, |
159 | | - }, |
160 | | - ) |
161 | | - } |
162 | | - sort.SliceStable(volOpts.ClusterVolumeSpec.Secrets, func(i, j int) bool { |
163 | | - return volOpts.ClusterVolumeSpec.Secrets[i].Key < volOpts.ClusterVolumeSpec.Secrets[j].Key |
164 | | - }) |
165 | 131 |
|
166 | | - // TODO(dperny): ignore if no topology specified |
167 | | - topology := &volume.TopologyRequirement{} |
168 | | - for _, top := range options.requisiteTopology.GetSlice() { |
169 | | - // each topology takes the form segment=value,segment=value |
170 | | - // comma-separated list of equal separated maps |
171 | | - segments := map[string]string{} |
172 | | - for segment := range strings.SplitSeq(top, ",") { |
173 | | - // TODO(dperny): validate topology syntax |
174 | | - k, v, _ := strings.Cut(segment, "=") |
175 | | - segments[k] = v |
176 | | - } |
177 | | - topology.Requisite = append( |
178 | | - topology.Requisite, |
179 | | - volume.Topology{Segments: segments}, |
180 | | - ) |
181 | | - } |
182 | | - |
183 | | - for _, top := range options.preferredTopology.GetSlice() { |
184 | | - // each topology takes the form segment=value,segment=value |
185 | | - // comma-separated list of equal separated maps |
186 | | - segments := map[string]string{} |
187 | | - for segment := range strings.SplitSeq(top, ",") { |
188 | | - // TODO(dperny): validate topology syntax |
189 | | - k, v, _ := strings.Cut(segment, "=") |
190 | | - segments[k] = v |
191 | | - } |
| 132 | + _, _ = fmt.Fprintln(dockerCli.Out(), res.Volume.Name) |
| 133 | + return nil |
| 134 | +} |
192 | 135 |
|
193 | | - topology.Preferred = append( |
194 | | - topology.Preferred, |
195 | | - volume.Topology{Segments: segments}, |
196 | | - ) |
197 | | - } |
| 136 | +func clusterVolumeSpec(options createOptions) *volume.ClusterVolumeSpec { |
| 137 | + if !options.cluster { |
| 138 | + return nil |
| 139 | + } |
198 | 140 |
|
199 | | - volOpts.ClusterVolumeSpec.AccessibilityRequirements = topology |
| 141 | + var secrets []volume.Secret |
| 142 | + for key, secret := range options.secrets.GetAll() { |
| 143 | + secrets = append(secrets, volume.Secret{Key: key, Secret: secret}) |
200 | 144 | } |
| 145 | + slices.SortFunc(secrets, func(a, b volume.Secret) int { |
| 146 | + return cmp.Compare(a.Key, b.Key) |
| 147 | + }) |
201 | 148 |
|
202 | | - res, err := dockerCli.Client().VolumeCreate(ctx, volOpts) |
203 | | - if err != nil { |
204 | | - return err |
| 149 | + accessMode := &volume.AccessMode{ |
| 150 | + Scope: volume.Scope(options.scope), |
| 151 | + Sharing: volume.SharingMode(options.sharing), |
| 152 | + } |
| 153 | + switch options.accessType { |
| 154 | + case "mount": |
| 155 | + accessMode.MountVolume = &volume.TypeMount{} |
| 156 | + case "block": |
| 157 | + accessMode.BlockVolume = &volume.TypeBlock{} |
205 | 158 | } |
206 | 159 |
|
207 | | - _, _ = fmt.Fprintln(dockerCli.Out(), res.Volume.Name) |
208 | | - return nil |
| 160 | + return &volume.ClusterVolumeSpec{ |
| 161 | + Group: options.group, |
| 162 | + AccessMode: accessMode, |
| 163 | + AccessibilityRequirements: &volume.TopologyRequirement{ |
| 164 | + Requisite: parseTopologies(options.requisiteTopology.GetSlice()), |
| 165 | + Preferred: parseTopologies(options.preferredTopology.GetSlice()), |
| 166 | + }, |
| 167 | + CapacityRange: &volume.CapacityRange{ |
| 168 | + RequiredBytes: max(options.requiredBytes.Value(), 0), |
| 169 | + LimitBytes: max(options.limitBytes.Value(), 0), |
| 170 | + }, |
| 171 | + Secrets: secrets, |
| 172 | + Availability: volume.Availability(options.availability), |
| 173 | + } |
| 174 | +} |
| 175 | + |
| 176 | +func parseTopologies(values []string) []volume.Topology { |
| 177 | + topologies := make([]volume.Topology, 0, len(values)) |
| 178 | + for _, top := range values { |
| 179 | + // TODO(dperny): validate topology syntax |
| 180 | + topologies = append(topologies, volume.Topology{ |
| 181 | + Segments: opts.ConvertKVStringsToMap(strings.Split(top, ",")), |
| 182 | + }) |
| 183 | + } |
| 184 | + return topologies |
209 | 185 | } |
0 commit comments