|
1 | 1 | package main |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "fmt" |
5 | | - "net" |
6 | | - "strconv" |
7 | 4 | "strings" |
8 | 5 |
|
9 | 6 | "github.com/alibaba/pouch/apis/types" |
10 | | - "github.com/alibaba/pouch/pkg/runconfig" |
11 | | - "github.com/docker/go-connections/nat" |
| 7 | + "github.com/alibaba/pouch/pkg/opts" |
12 | 8 |
|
13 | | - units "github.com/docker/go-units" |
14 | 9 | strfmt "github.com/go-openapi/strfmt" |
15 | 10 | ) |
16 | 11 |
|
@@ -73,125 +68,86 @@ type container struct { |
73 | 68 | } |
74 | 69 |
|
75 | 70 | func (c *container) config() (*types.ContainerCreateConfig, error) { |
76 | | - labels, err := parseLabels(c.labels) |
| 71 | + labels, err := opts.ParseLabels(c.labels) |
77 | 72 | if err != nil { |
78 | 73 | return nil, err |
79 | 74 | } |
80 | 75 |
|
81 | | - if err := validateMemorySwappiness(c.memorySwappiness); err != nil { |
| 76 | + if err := opts.ValidateMemorySwappiness(c.memorySwappiness); err != nil { |
82 | 77 | return nil, err |
83 | 78 | } |
84 | 79 |
|
85 | | - memory, err := parseMemory(c.memory) |
| 80 | + memory, err := opts.ParseMemory(c.memory) |
86 | 81 | if err != nil { |
87 | 82 | return nil, err |
88 | 83 | } |
89 | 84 |
|
90 | | - memorySwap, err := parseMemorySwap(c.memorySwap) |
| 85 | + memorySwap, err := opts.ParseMemorySwap(c.memorySwap) |
91 | 86 | if err != nil { |
92 | 87 | return nil, err |
93 | 88 | } |
94 | 89 |
|
95 | | - intelRdtL3Cbm, err := parseIntelRdt(c.IntelRdtL3Cbm) |
| 90 | + intelRdtL3Cbm, err := opts.ParseIntelRdt(c.IntelRdtL3Cbm) |
96 | 91 | if err != nil { |
97 | 92 | return nil, err |
98 | 93 | } |
99 | 94 |
|
100 | | - deviceMappings, err := parseDeviceMappings(c.devices) |
| 95 | + deviceMappings, err := opts.ParseDeviceMappings(c.devices) |
101 | 96 | if err != nil { |
102 | 97 | return nil, err |
103 | 98 | } |
104 | 99 |
|
105 | | - restartPolicy, err := parseRestartPolicy(c.restartPolicy) |
| 100 | + restartPolicy, err := opts.ParseRestartPolicy(c.restartPolicy) |
106 | 101 | if err != nil { |
107 | 102 | return nil, err |
108 | 103 | } |
109 | 104 |
|
110 | | - sysctls, err := parseSysctls(c.sysctls) |
111 | | - if err != nil { |
| 105 | + if err := opts.ValidateRestartPolicy(restartPolicy); err != nil { |
112 | 106 | return nil, err |
113 | 107 | } |
114 | 108 |
|
115 | | - diskQuota, err := parseDiskQuota(c.diskQuota) |
| 109 | + sysctls, err := opts.ParseSysctls(c.sysctls) |
116 | 110 | if err != nil { |
117 | 111 | return nil, err |
118 | 112 | } |
119 | 113 |
|
120 | | - if err := validateOOMScore(c.oomScoreAdj); err != nil { |
| 114 | + diskQuota, err := opts.ParseDiskQuota(c.diskQuota) |
| 115 | + if err != nil { |
121 | 116 | return nil, err |
122 | 117 | } |
123 | 118 |
|
124 | | - var networkMode string |
125 | | - if len(c.networks) == 0 { |
126 | | - networkMode = "bridge" |
127 | | - } |
128 | | - networkingConfig := &types.NetworkingConfig{ |
129 | | - EndpointsConfig: map[string]*types.EndpointSettings{}, |
| 119 | + if err := opts.ValidateDiskQuota(diskQuota); err != nil { |
| 120 | + return nil, err |
130 | 121 | } |
131 | | - for _, network := range c.networks { |
132 | | - name, parameter, mode, err := parseNetwork(network) |
133 | | - if err != nil { |
134 | | - return nil, err |
135 | | - } |
136 | | - |
137 | | - if networkMode == "" || mode == "mode" { |
138 | | - networkMode = name |
139 | | - } |
140 | 122 |
|
141 | | - if name == "container" { |
142 | | - networkMode = fmt.Sprintf("%s:%s", name, parameter) |
143 | | - } else if ipaddr := net.ParseIP(parameter); ipaddr != nil { |
144 | | - networkingConfig.EndpointsConfig[name] = &types.EndpointSettings{ |
145 | | - IPAddress: parameter, |
146 | | - IPAMConfig: &types.EndpointIPAMConfig{ |
147 | | - IPV4Address: parameter, |
148 | | - }, |
149 | | - } |
150 | | - } |
| 123 | + if err := opts.ValidateOOMScore(c.oomScoreAdj); err != nil { |
| 124 | + return nil, err |
151 | 125 | } |
152 | 126 |
|
153 | | - // parse port binding |
154 | | - tmpPorts, tmpPortBindings, err := nat.ParsePortSpecs(c.ports) |
| 127 | + networkingConfig, networkMode, err := opts.ParseNetworks(c.networks) |
155 | 128 | if err != nil { |
156 | 129 | return nil, err |
157 | 130 | } |
158 | | - // translate ports and portbingings |
159 | | - ports := map[string]interface{}{} |
160 | | - for n, p := range tmpPorts { |
161 | | - ports[string(n)] = p |
| 131 | + |
| 132 | + if err := opts.ValidateNetworks(networkingConfig); err != nil { |
| 133 | + return nil, err |
162 | 134 | } |
163 | | - portBindings := make(types.PortMap) |
164 | | - for n, pbs := range tmpPortBindings { |
165 | | - portBindings[string(n)] = []types.PortBinding{} |
166 | | - for _, tmpPb := range pbs { |
167 | | - pb := types.PortBinding{HostIP: tmpPb.HostIP, HostPort: tmpPb.HostPort} |
168 | | - portBindings[string(n)] = append(portBindings[string(n)], pb) |
169 | | - } |
| 135 | + |
| 136 | + portBindings, err := opts.ParsePortBinding(c.ports) |
| 137 | + if err != nil { |
| 138 | + return nil, err |
170 | 139 | } |
171 | 140 |
|
172 | | - for _, e := range c.expose { |
173 | | - if strings.Contains(e, ":") { |
174 | | - return nil, fmt.Errorf("invalid port format for --expose: %s", e) |
175 | | - } |
| 141 | + // FIXME(ziren): do we need verify portBinding ??? |
| 142 | + if err := opts.ValidatePortBinding(portBindings); err != nil { |
| 143 | + return nil, err |
| 144 | + } |
176 | 145 |
|
177 | | - //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] |
178 | | - proto, port := nat.SplitProtoPort(e) |
179 | | - //parse the start and end port and create a sequence of ports to expose |
180 | | - //if expose a port, the start and end port are the same |
181 | | - start, end, err := nat.ParsePortRange(port) |
182 | | - if err != nil { |
183 | | - return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err) |
184 | | - } |
185 | | - for i := start; i <= end; i++ { |
186 | | - p, err := nat.NewPort(proto, strconv.FormatUint(i, 10)) |
187 | | - if err != nil { |
188 | | - return nil, err |
189 | | - } |
190 | | - if _, exists := ports[string(p)]; !exists { |
191 | | - ports[string(p)] = struct{}{} |
192 | | - } |
193 | | - } |
| 146 | + ports, err := opts.ParseExposedPorts(c.ports, c.expose) |
| 147 | + if err != nil { |
| 148 | + return nil, err |
194 | 149 | } |
| 150 | + |
195 | 151 | config := &types.ContainerCreateConfig{ |
196 | 152 | ContainerConfig: types.ContainerConfig{ |
197 | 153 | Tty: c.tty, |
@@ -258,206 +214,3 @@ func (c *container) config() (*types.ContainerCreateConfig, error) { |
258 | 214 |
|
259 | 215 | return config, nil |
260 | 216 | } |
261 | | - |
262 | | -func parseSysctls(sysctls []string) (map[string]string, error) { |
263 | | - results := make(map[string]string) |
264 | | - for _, sysctl := range sysctls { |
265 | | - fields, err := parseSysctl(sysctl) |
266 | | - if err != nil { |
267 | | - return nil, err |
268 | | - } |
269 | | - k, v := fields[0], fields[1] |
270 | | - results[k] = v |
271 | | - } |
272 | | - return results, nil |
273 | | -} |
274 | | - |
275 | | -func parseSysctl(sysctl string) ([]string, error) { |
276 | | - fields := strings.SplitN(sysctl, "=", 2) |
277 | | - if len(fields) != 2 { |
278 | | - return nil, fmt.Errorf("invalid sysctl %s: sysctl must be in format of key=value", sysctl) |
279 | | - } |
280 | | - return fields, nil |
281 | | -} |
282 | | - |
283 | | -func parseLabels(labels []string) (map[string]string, error) { |
284 | | - results := make(map[string]string) |
285 | | - for _, label := range labels { |
286 | | - fields, err := parseLabel(label) |
287 | | - if err != nil { |
288 | | - return nil, err |
289 | | - } |
290 | | - k, v := fields[0], fields[1] |
291 | | - results[k] = v |
292 | | - } |
293 | | - return results, nil |
294 | | -} |
295 | | - |
296 | | -func parseLabel(label string) ([]string, error) { |
297 | | - fields := strings.SplitN(label, "=", 2) |
298 | | - if len(fields) != 2 { |
299 | | - return nil, fmt.Errorf("invalid label %s: label must be in format of key=value", label) |
300 | | - } |
301 | | - return fields, nil |
302 | | -} |
303 | | - |
304 | | -func parseDeviceMappings(devices []string) ([]*types.DeviceMapping, error) { |
305 | | - results := []*types.DeviceMapping{} |
306 | | - for _, device := range devices { |
307 | | - deviceMapping, err := parseDevice(device) |
308 | | - if err != nil { |
309 | | - return nil, err |
310 | | - } |
311 | | - results = append(results, deviceMapping) |
312 | | - } |
313 | | - return results, nil |
314 | | -} |
315 | | - |
316 | | -func parseDevice(device string) (*types.DeviceMapping, error) { |
317 | | - deviceMapping, err := runconfig.ParseDevice(device) |
318 | | - if err != nil { |
319 | | - return nil, fmt.Errorf("parse devices error: %s", err) |
320 | | - } |
321 | | - if !runconfig.ValidDeviceMode(deviceMapping.CgroupPermissions) { |
322 | | - return nil, fmt.Errorf("%s invalid device mode: %s", device, deviceMapping.CgroupPermissions) |
323 | | - } |
324 | | - return deviceMapping, nil |
325 | | -} |
326 | | - |
327 | | -func parseMemory(memory string) (int64, error) { |
328 | | - if memory == "" { |
329 | | - return 0, nil |
330 | | - } |
331 | | - result, err := units.RAMInBytes(memory) |
332 | | - if err != nil { |
333 | | - return 0, err |
334 | | - } |
335 | | - return result, nil |
336 | | -} |
337 | | - |
338 | | -func parseMemorySwap(memorySwap string) (int64, error) { |
339 | | - if memorySwap == "" { |
340 | | - return 0, nil |
341 | | - } |
342 | | - if memorySwap == "-1" { |
343 | | - return -1, nil |
344 | | - } |
345 | | - result, err := units.RAMInBytes(memorySwap) |
346 | | - if err != nil { |
347 | | - return 0, err |
348 | | - } |
349 | | - return result, nil |
350 | | -} |
351 | | - |
352 | | -func validateMemorySwappiness(memorySwappiness int64) error { |
353 | | - if memorySwappiness != -1 && (memorySwappiness < 0 || memorySwappiness > 100) { |
354 | | - return fmt.Errorf("invalid memory swappiness: %d (its range is -1 or 0-100)", memorySwappiness) |
355 | | - } |
356 | | - return nil |
357 | | -} |
358 | | - |
359 | | -func parseIntelRdt(intelRdtL3Cbm string) (string, error) { |
360 | | - // FIXME: add Intel RDT L3 Cbm validation |
361 | | - return intelRdtL3Cbm, nil |
362 | | -} |
363 | | - |
364 | | -func parseRestartPolicy(restartPolicy string) (*types.RestartPolicy, error) { |
365 | | - policy := &types.RestartPolicy{} |
366 | | - |
367 | | - if restartPolicy == "" { |
368 | | - policy.Name = "no" |
369 | | - return policy, nil |
370 | | - } |
371 | | - |
372 | | - fields := strings.Split(restartPolicy, ":") |
373 | | - policy.Name = fields[0] |
374 | | - |
375 | | - switch policy.Name { |
376 | | - case "always", "unless-stopped", "no": |
377 | | - case "on-failure": |
378 | | - if len(fields) > 2 { |
379 | | - return nil, fmt.Errorf("invalid restart policy: %s", restartPolicy) |
380 | | - } |
381 | | - if len(fields) == 2 { |
382 | | - n, err := strconv.Atoi(fields[1]) |
383 | | - if err != nil { |
384 | | - return nil, fmt.Errorf("invalid restart policy: %v", err) |
385 | | - } |
386 | | - policy.MaximumRetryCount = int64(n) |
387 | | - } |
388 | | - default: |
389 | | - return nil, fmt.Errorf("invalid restart policy: %s", restartPolicy) |
390 | | - } |
391 | | - |
392 | | - return policy, nil |
393 | | -} |
394 | | - |
395 | | -// network format as below: |
396 | | -// [network]:[ip_address], such as: mynetwork:172.17.0.2 or mynetwork(ip alloc by ipam) or 172.17.0.2(default network is bridge) |
397 | | -// [network_mode]:[parameter], such as: host(use host network) or container:containerID(use exist container network) |
398 | | -// [network_mode]:[parameter]:mode, such as: mynetwork:172.17.0.2:mode(if the container has multi-networks, the network is the default network mode) |
399 | | -func parseNetwork(network string) (string, string, string, error) { |
400 | | - var ( |
401 | | - name string |
402 | | - parameter string |
403 | | - mode string |
404 | | - ) |
405 | | - if network == "" { |
406 | | - return "", "", "", fmt.Errorf("invalid network: cannot be empty") |
407 | | - } |
408 | | - arr := strings.Split(network, ":") |
409 | | - switch len(arr) { |
410 | | - case 1: |
411 | | - if ipaddr := net.ParseIP(arr[0]); ipaddr != nil { |
412 | | - parameter = arr[0] |
413 | | - } else { |
414 | | - name = arr[0] |
415 | | - } |
416 | | - case 2: |
417 | | - name = arr[0] |
418 | | - if name == "container" { |
419 | | - parameter = arr[1] |
420 | | - } else if ipaddr := net.ParseIP(arr[1]); ipaddr != nil { |
421 | | - parameter = arr[1] |
422 | | - } else { |
423 | | - mode = arr[1] |
424 | | - } |
425 | | - default: |
426 | | - name = arr[0] |
427 | | - parameter = arr[1] |
428 | | - mode = arr[2] |
429 | | - } |
430 | | - |
431 | | - return name, parameter, mode, nil |
432 | | -} |
433 | | - |
434 | | -func parseDiskQuota(quotas []string) (map[string]string, error) { |
435 | | - var quotaMaps = make(map[string]string) |
436 | | - |
437 | | - for _, quota := range quotas { |
438 | | - if quota == "" { |
439 | | - return nil, fmt.Errorf("invalid format for disk quota: %s", quota) |
440 | | - } |
441 | | - |
442 | | - parts := strings.Split(quota, "=") |
443 | | - switch len(parts) { |
444 | | - case 1: |
445 | | - quotaMaps["/"] = parts[0] |
446 | | - case 2: |
447 | | - quotaMaps[parts[0]] = parts[1] |
448 | | - default: |
449 | | - return nil, fmt.Errorf("invalid format for disk quota: %s", quota) |
450 | | - } |
451 | | - } |
452 | | - |
453 | | - return quotaMaps, nil |
454 | | -} |
455 | | - |
456 | | -// validateOOMScore validates oom score |
457 | | -func validateOOMScore(score int64) error { |
458 | | - if score < -1000 || score > 1000 { |
459 | | - return fmt.Errorf("oom-score-adj should be in range [-1000, 1000]") |
460 | | - } |
461 | | - |
462 | | - return nil |
463 | | -} |
0 commit comments