diff --git a/apis/swagger.yml b/apis/swagger.yml index 8139f2d15..b0859d732 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -2520,6 +2520,12 @@ definitions: x-nullable: true additionalProperties: type: "string" + SpecAnnotation: + type: "object" + description: "update specAnnotation for container" + x-nullable: true + additionalProperties: + type: "string" ContainerUpgradeConfig: description: | diff --git a/apis/types/update_config.go b/apis/types/update_config.go index 41aa6b1ac..e68edcdc7 100644 --- a/apis/types/update_config.go +++ b/apis/types/update_config.go @@ -28,6 +28,9 @@ type UpdateConfig struct { // restart policy RestartPolicy *RestartPolicy `json:"RestartPolicy,omitempty"` + + // update specAnnotation for container + SpecAnnotation map[string]string `json:"SpecAnnotation,omitempty"` } // UnmarshalJSON unmarshals this object from a JSON structure @@ -48,6 +51,8 @@ func (m *UpdateConfig) UnmarshalJSON(raw []byte) error { Label []string `json:"Label"` RestartPolicy *RestartPolicy `json:"RestartPolicy,omitempty"` + + SpecAnnotation map[string]string `json:"SpecAnnotation,omitempty"` } if err := swag.ReadJSON(raw, &dataAO1); err != nil { return err @@ -61,6 +66,8 @@ func (m *UpdateConfig) UnmarshalJSON(raw []byte) error { m.RestartPolicy = dataAO1.RestartPolicy + m.SpecAnnotation = dataAO1.SpecAnnotation + return nil } @@ -82,6 +89,8 @@ func (m UpdateConfig) MarshalJSON() ([]byte, error) { Label []string `json:"Label"` RestartPolicy *RestartPolicy `json:"RestartPolicy,omitempty"` + + SpecAnnotation map[string]string `json:"SpecAnnotation,omitempty"` } dataAO1.DiskQuota = m.DiskQuota @@ -92,6 +101,8 @@ func (m UpdateConfig) MarshalJSON() ([]byte, error) { dataAO1.RestartPolicy = m.RestartPolicy + dataAO1.SpecAnnotation = m.SpecAnnotation + jsonDataAO1, errAO1 := swag.WriteJSON(dataAO1) if errAO1 != nil { return nil, errAO1 diff --git a/cli/update.go b/cli/update.go index 2a56191dc..98808ce8e 100644 --- a/cli/update.go +++ b/cli/update.go @@ -55,6 +55,7 @@ func (uc *UpdateCommand) addFlags() { flagSet.StringSliceVarP(&uc.labels, "label", "l", nil, "Set label for container") flagSet.StringVar(&uc.restartPolicy, "restart", "", "Restart policy to apply when container exits") flagSet.StringSliceVar(&uc.diskQuota, "disk-quota", nil, "Update disk quota for container(/=10g)") + flagSet.StringSliceVar(&uc.specAnnotation, "annotation", nil, "Update annotation for runtime spec") } // updateRun is the entry of update command. @@ -97,12 +98,18 @@ func (uc *UpdateCommand) updateRun(args []string) error { return err } + annotation, err := opts.ParseAnnotation(uc.specAnnotation) + if err != nil { + return err + } + updateConfig := &types.UpdateConfig{ - Env: uc.env, - Label: uc.labels, - RestartPolicy: restartPolicy, - Resources: resource, - DiskQuota: diskQuota, + Env: uc.env, + Label: uc.labels, + RestartPolicy: restartPolicy, + Resources: resource, + DiskQuota: diskQuota, + SpecAnnotation: annotation, } apiClient := uc.cli.Client() diff --git a/daemon/mgr/container.go b/daemon/mgr/container.go index 5f7bd81cc..ecdfe5a58 100644 --- a/daemon/mgr/container.go +++ b/daemon/mgr/container.go @@ -1163,6 +1163,12 @@ func (mgr *ContainerManager) Update(ctx context.Context, name string, config *ty } } + c.Lock() + if len(config.SpecAnnotation) > 0 { + c.Config.SpecAnnotation = mergeAnnotation(config.SpecAnnotation, c.Config.SpecAnnotation) + } + c.Unlock() + if mgr.containerPlugin != nil && len(config.Env) > 0 { if err = mgr.containerPlugin.PostUpdate(c.BaseFS, c.Config.Env); err != nil { return err diff --git a/daemon/mgr/container_utils.go b/daemon/mgr/container_utils.go index 595bcff12..9f12c1fe6 100644 --- a/daemon/mgr/container_utils.go +++ b/daemon/mgr/container_utils.go @@ -310,3 +310,19 @@ func mergeEnvSlice(newEnv, oldEnv []string) ([]string, error) { return newEnvSlice, nil } + +func mergeAnnotation(newAnnotation, oldAnnotation map[string]string) map[string]string { + if len(newAnnotation) == 0 { + return oldAnnotation + } + + if len(oldAnnotation) == 0 { + oldAnnotation = make(map[string]string) + } + + for k, v := range newAnnotation { + oldAnnotation[k] = v + } + + return oldAnnotation +} diff --git a/test/cli_update_test.go b/test/cli_update_test.go index 4ec76f2ed..d5707deed 100644 --- a/test/cli_update_test.go +++ b/test/cli_update_test.go @@ -539,3 +539,40 @@ func (suite *PouchUpdateSuite) TestUpdateBlkIOLimit(c *check.C) { out := res.Stdout() c.Assert(out, check.Equals, Expected) } + +func checkContainerAnnotation(c *check.C, cName string, annotationKey string, expect string) { + output := command.PouchRun("inspect", cName).Stdout() + result := []types.ContainerJSON{} + if err := json.Unmarshal([]byte(output), &result); err != nil { + c.Errorf("failed to decode inspect output: %v", err) + } + + annotations := result[0].Config.SpecAnnotation + v, found := annotations[annotationKey] + c.Assert(found, check.Equals, true) + c.Assert(v, check.Equals, expect) +} + +// TestUpdateAnnotation is to verity the correctness of update the annotation +func (suite *PouchUpdateSuite) TestUpdateAnnotation(c *check.C) { + cname := "TestUpdateAnnotation1" + annotation1 := "key1=value1" + annotation2 := "key2=value2" + command.PouchRun("run", "-d", "--name", cname, "--annotation", annotation1, "--annotation", annotation2, busyboxImage, "top").Assert(c, icmd.Success) + defer DelContainerForceMultyTime(c, cname) + + annotation1Update := "key1=value1.new" + annotation2Update := "key2=value2.new" + + command.PouchRun("update", "--annotation", annotation1Update, cname).Assert(c, icmd.Success) + checkContainerAnnotation(c, cname, "key1", "value1.new") + checkContainerAnnotation(c, cname, "key2", "value2") + + command.PouchRun("update", "--annotation", annotation2Update, cname).Assert(c, icmd.Success) + checkContainerAnnotation(c, cname, "key1", "value1.new") + checkContainerAnnotation(c, cname, "key2", "value2.new") + + command.PouchRun("restart", cname).Assert(c, icmd.Success) + checkContainerAnnotation(c, cname, "key1", "value1.new") + checkContainerAnnotation(c, cname, "key2", "value2.new") +}