Browse code

Merge pull request #24533 from yongtang/24392-docker-info-label-duplicate-keys

Remove duplicate keys in labels of `docker info`

Sebastiaan van Stijn authored on 2016/10/25 10:12:28
Showing 5 changed files
... ...
@@ -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
 	fmt.Fprintf(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) {
... ...
@@ -232,6 +232,30 @@ func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (strin
232 232
 	return advertise, nil
233 233
 }
234 234
 
235
+// GetConflictFreeLabels validate Labels for conflict
236
+// In swarm the duplicates for labels are removed
237
+// so we only take same values here, no conflict values
238
+// If the key-value is the same we will only take the last label
239
+func GetConflictFreeLabels(labels []string) ([]string, error) {
240
+	labelMap := map[string]string{}
241
+	for _, label := range labels {
242
+		stringSlice := strings.SplitN(label, "=", 2)
243
+		if len(stringSlice) > 1 {
244
+			// If there is a conflict we will return an error
245
+			if v, ok := labelMap[stringSlice[0]]; ok && v != stringSlice[1] {
246
+				return nil, fmt.Errorf("conflict labels for %s=%s and %s=%s", stringSlice[0], stringSlice[1], stringSlice[0], v)
247
+			}
248
+			labelMap[stringSlice[0]] = stringSlice[1]
249
+		}
250
+	}
251
+
252
+	newLabels := []string{}
253
+	for k, v := range labelMap {
254
+		newLabels = append(newLabels, fmt.Sprintf("%s=%s", k, v))
255
+	}
256
+	return newLabels, nil
257
+}
258
+
235 259
 // ReloadConfiguration reads the configuration in the host and reloads the daemon and server.
236 260
 func ReloadConfiguration(configFile string, flags *pflag.FlagSet, reload func(*Config)) error {
237 261
 	logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile)
... ...
@@ -244,6 +268,23 @@ func ReloadConfiguration(configFile string, flags *pflag.FlagSet, reload func(*C
244 244
 		return fmt.Errorf("file configuration validation failed (%v)", err)
245 245
 	}
246 246
 
247
+	// Labels of the docker engine used to allow multiple values associated with the same key.
248
+	// This is deprecated in 1.13, and, be removed after 3 release cycles.
249
+	// The following will check the conflict of labels, and report a warning for deprecation.
250
+	//
251
+	// TODO: After 3 release cycles (1.16) an error will be returned, and labels will be
252
+	// sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels):
253
+	//
254
+	// newLabels, err := GetConflictFreeLabels(newConfig.Labels)
255
+	// if err != nil {
256
+	//      return err
257
+	// }
258
+	// newConfig.Labels = newLabels
259
+	//
260
+	if _, err := GetConflictFreeLabels(newConfig.Labels); err != nil {
261
+		logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err)
262
+	}
263
+
247 264
 	reload(newConfig)
248 265
 	return nil
249 266
 }
... ...
@@ -35,6 +35,14 @@ see [Feature Deprecation Policy](https://docs.docker.com/engine/#feature-depreca
35 35
 
36 36
 The daemon is moved to a separate binary (`dockerd`), and should be used instead.
37 37
 
38
+### Duplicate keys with conflicting values in engine labels
39
+**Deprecated In Release: [v1.13](https://github.com/docker/docker/releases/)**
40
+
41
+**Target For Removal In Release: v1.16**
42
+
43
+Duplicate keys with conflicting values have been deprecated. A warning is displayed
44
+in the output, and an error will be returned in the future.
45
+
38 46
 ### Three argument form in `docker import`
39 47
 **Deprecated In Release: [v0.6.7](https://github.com/docker/docker/releases/tag/v0.6.7)**
40 48
 
... ...
@@ -220,3 +220,15 @@ func (s *DockerDaemonSuite) TestRegistryMirrors(c *check.C) {
220 220
 	c.Assert(out, checker.Contains, fmt.Sprintf(" %s", registryMirror1))
221 221
 	c.Assert(out, checker.Contains, fmt.Sprintf(" %s", registryMirror2))
222 222
 }
223
+
224
+// Test case for #24392
225
+func (s *DockerDaemonSuite) TestInfoLabels(c *check.C) {
226
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
227
+
228
+	err := s.d.Start("--label", `test.empty=`, "--label", `test.empty=`, "--label", `test.label="1"`, "--label", `test.label="2"`)
229
+	c.Assert(err, checker.IsNil)
230
+
231
+	out, err := s.d.Cmd("info")
232
+	c.Assert(err, checker.IsNil)
233
+	c.Assert(out, checker.Contains, "WARNING: labels with duplicate keys and conflicting values have been deprecated")
234
+}