This fix tries to address the issue raised in 24392 where
labels with duplicate keys exist in `docker info`, which
contradicts with the specifications in the docs.
The reason for duplicate keys is that labels are stored as
slice of strings in the format of `A=B` (and the input/output).
This fix tries to address this issue by checking conflict
labels when daemon started, and remove duplicate labels (K-V).
The existing `/info` API has not been changed.
An additional integration test has been added to cover the
changes in this fix.
This fix fixes 24392.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
... | ... |
@@ -223,6 +223,21 @@ func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error { |
223 | 223 |
for _, attribute := range info.Labels { |
224 | 224 |
fmt.Fprintf(dockerCli.Out(), " %s\n", attribute) |
225 | 225 |
} |
226 |
+ // TODO: Engine labels with duplicate keys has been deprecated in 1.13 and will be error out |
|
227 |
+ // after 3 release cycles (1.16). For now, a WARNING will be generated. The following will |
|
228 |
+ // be removed eventually. |
|
229 |
+ labelMap := map[string]string{} |
|
230 |
+ for _, label := range info.Labels { |
|
231 |
+ stringSlice := strings.SplitN(label, "=", 2) |
|
232 |
+ if len(stringSlice) > 1 { |
|
233 |
+ // If there is a conflict we will throw out an warning |
|
234 |
+ if v, ok := labelMap[stringSlice[0]]; ok && v != stringSlice[1] { |
|
235 |
+ fmt.Fprintln(dockerCli.Err(), "WARNING: labels with duplicate keys and conflicting values have been deprecated") |
|
236 |
+ break |
|
237 |
+ } |
|
238 |
+ labelMap[stringSlice[0]] = stringSlice[1] |
|
239 |
+ } |
|
240 |
+ } |
|
226 | 241 |
} |
227 | 242 |
|
228 | 243 |
ioutils.FprintfIfTrue(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild) |
... | ... |
@@ -396,6 +396,23 @@ func loadDaemonCliConfig(opts daemonOptions) (*daemon.Config, error) { |
396 | 396 |
return nil, err |
397 | 397 |
} |
398 | 398 |
|
399 |
+ // Labels of the docker engine used to allow multiple values associated with the same key. |
|
400 |
+ // This is deprecated in 1.13, and, be removed after 3 release cycles. |
|
401 |
+ // The following will check the conflict of labels, and report a warning for deprecation. |
|
402 |
+ // |
|
403 |
+ // TODO: After 3 release cycles (1.16) an error will be returned, and labels will be |
|
404 |
+ // sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels): |
|
405 |
+ // |
|
406 |
+ // newLabels, err := daemon.GetConflictFreeLabels(config.Labels) |
|
407 |
+ // if err != nil { |
|
408 |
+ // return nil, err |
|
409 |
+ // } |
|
410 |
+ // config.Labels = newLabels |
|
411 |
+ // |
|
412 |
+ if _, err := daemon.GetConflictFreeLabels(config.Labels); err != nil { |
|
413 |
+ logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err) |
|
414 |
+ } |
|
415 |
+ |
|
399 | 416 |
// Regardless of whether the user sets it to true or false, if they |
400 | 417 |
// specify TLSVerify at all then we need to turn on TLS |
401 | 418 |
if config.IsValueSet(cliflags.FlagTLSVerify) { |
... | ... |
@@ -220,6 +220,30 @@ func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (strin |
220 | 220 |
return advertise, nil |
221 | 221 |
} |
222 | 222 |
|
223 |
+// GetConflictFreeLabels validate Labels for conflict |
|
224 |
+// In swarm the duplicates for labels are removed |
|
225 |
+// so we only take same values here, no conflict values |
|
226 |
+// If the key-value is the same we will only take the last label |
|
227 |
+func GetConflictFreeLabels(labels []string) ([]string, error) { |
|
228 |
+ labelMap := map[string]string{} |
|
229 |
+ for _, label := range labels { |
|
230 |
+ stringSlice := strings.SplitN(label, "=", 2) |
|
231 |
+ if len(stringSlice) > 1 { |
|
232 |
+ // If there is a conflict we will return an error |
|
233 |
+ if v, ok := labelMap[stringSlice[0]]; ok && v != stringSlice[1] { |
|
234 |
+ return nil, fmt.Errorf("conflict labels for %s=%s and %s=%s", stringSlice[0], stringSlice[1], stringSlice[0], v) |
|
235 |
+ } |
|
236 |
+ labelMap[stringSlice[0]] = stringSlice[1] |
|
237 |
+ } |
|
238 |
+ } |
|
239 |
+ |
|
240 |
+ newLabels := []string{} |
|
241 |
+ for k, v := range labelMap { |
|
242 |
+ newLabels = append(newLabels, fmt.Sprintf("%s=%s", k, v)) |
|
243 |
+ } |
|
244 |
+ return newLabels, nil |
|
245 |
+} |
|
246 |
+ |
|
223 | 247 |
// ReloadConfiguration reads the configuration in the host and reloads the daemon and server. |
224 | 248 |
func ReloadConfiguration(configFile string, flags *pflag.FlagSet, reload func(*Config)) error { |
225 | 249 |
logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile) |
... | ... |
@@ -232,6 +256,23 @@ func ReloadConfiguration(configFile string, flags *pflag.FlagSet, reload func(*C |
232 | 232 |
return fmt.Errorf("file configuration validation failed (%v)", err) |
233 | 233 |
} |
234 | 234 |
|
235 |
+ // Labels of the docker engine used to allow multiple values associated with the same key. |
|
236 |
+ // This is deprecated in 1.13, and, be removed after 3 release cycles. |
|
237 |
+ // The following will check the conflict of labels, and report a warning for deprecation. |
|
238 |
+ // |
|
239 |
+ // TODO: After 3 release cycles (1.16) an error will be returned, and labels will be |
|
240 |
+ // sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels): |
|
241 |
+ // |
|
242 |
+ // newLabels, err := GetConflictFreeLabels(newConfig.Labels) |
|
243 |
+ // if err != nil { |
|
244 |
+ // return err |
|
245 |
+ // } |
|
246 |
+ // newConfig.Labels = newLabels |
|
247 |
+ // |
|
248 |
+ if _, err := GetConflictFreeLabels(newConfig.Labels); err != nil { |
|
249 |
+ logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err) |
|
250 |
+ } |
|
251 |
+ |
|
235 | 252 |
reload(newConfig) |
236 | 253 |
return nil |
237 | 254 |
} |
... | ... |
@@ -26,6 +26,14 @@ see [Feature Deprecation Policy](index.md#feature-deprecation-policy). |
26 | 26 |
|
27 | 27 |
The daemon is moved to a separate binary (`dockerd`), and should be used instead. |
28 | 28 |
|
29 |
+### Duplicate keys with conflicting values in engine labels |
|
30 |
+**Deprecated In Release: [v1.13](https://github.com/docker/docker/releases/)** |
|
31 |
+ |
|
32 |
+**Target For Removal In Release: v1.16** |
|
33 |
+ |
|
34 |
+Duplicate keys with conflicting values have been deprecated. A warning is displayed |
|
35 |
+in the output, and an error will be returned in the future. |
|
36 |
+ |
|
29 | 37 |
### Three argument form in `docker import` |
30 | 38 |
**Deprecated In Release: [v0.6.7](https://github.com/docker/docker/releases/tag/v0.6.7)** |
31 | 39 |
|
... | ... |
@@ -219,3 +219,15 @@ func (s *DockerDaemonSuite) TestRegistryMirrors(c *check.C) { |
219 | 219 |
c.Assert(out, checker.Contains, fmt.Sprintf(" %s", registryMirror1)) |
220 | 220 |
c.Assert(out, checker.Contains, fmt.Sprintf(" %s", registryMirror2)) |
221 | 221 |
} |
222 |
+ |
|
223 |
+// Test case for #24392 |
|
224 |
+func (s *DockerDaemonSuite) TestInfoLabels(c *check.C) { |
|
225 |
+ testRequires(c, SameHostDaemon, DaemonIsLinux) |
|
226 |
+ |
|
227 |
+ err := s.d.Start("--label", `test.empty=`, "--label", `test.empty=`, "--label", `test.label="1"`, "--label", `test.label="2"`) |
|
228 |
+ c.Assert(err, checker.IsNil) |
|
229 |
+ |
|
230 |
+ out, err := s.d.Cmd("info") |
|
231 |
+ c.Assert(err, checker.IsNil) |
|
232 |
+ c.Assert(out, checker.Contains, "WARNING: labels with duplicate keys and conflicting values have been deprecated") |
|
233 |
+} |