diff --git a/apis/server/router.go b/apis/server/router.go index c23f29a38..fec6caf20 100644 --- a/apis/server/router.go +++ b/apis/server/router.go @@ -28,6 +28,9 @@ func initRoute(s *Server) http.Handler { r.Path(versionMatcher + "/version").Methods(http.MethodGet).Handler(s.filter(s.version)) r.Path(versionMatcher + "/auth").Methods(http.MethodPost).Handler(s.filter(s.auth)) + // daemon, we still list this API into system manager. + r.Path(versionMatcher + "/daemon/update").Methods(http.MethodPost).Handler(s.filter(s.updateDaemon)) + // container r.Path(versionMatcher + "/containers/create").Methods(http.MethodPost).Handler(s.filter(s.createContainer)) r.Path(versionMatcher + "/containers/{name:.*}/start").Methods(http.MethodPost).Handler(s.filter(s.startContainer)) diff --git a/apis/server/system_bridge.go b/apis/server/system_bridge.go index 379e9383d..f63b8c0c3 100644 --- a/apis/server/system_bridge.go +++ b/apis/server/system_bridge.go @@ -31,6 +31,17 @@ func (s *Server) version(ctx context.Context, rw http.ResponseWriter, req *http. return EncodeResponse(rw, http.StatusOK, version) } +func (s *Server) updateDaemon(ctx context.Context, rw http.ResponseWriter, req *http.Request) (err error) { + cfg := &types.DaemonUpdateConfig{} + if err := json.NewDecoder(req.Body).Decode(cfg); err != nil { + return httputils.NewHTTPError(err, http.StatusBadRequest) + } + + // TODO: validate cfg in details + + return s.SystemMgr.UpdateDaemon(cfg) +} + func (s *Server) auth(ctx context.Context, rw http.ResponseWriter, req *http.Request) (err error) { auth := types.AuthConfig{} if err := json.NewDecoder(req.Body).Decode(&auth); err != nil { diff --git a/apis/swagger.yml b/apis/swagger.yml index e9bdd4025..2f4041ef1 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -83,6 +83,29 @@ paths: description: "Authentication to check" schema: $ref: "#/definitions/AuthConfig" + + /daemon/update: + post: + summary: "Update daemon's labels and image proxy" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: '#/definitions/Error' + 500: + $ref: "#/responses/500ErrorResponse" + parameters: + - name: "DaemonUpdateConfig" + in: body + description: "Config used to update daemon, only labels and image proxy are allowed." + schema: + $ref: "#/definitions/DaemonUpdateConfig" /images/create: post: @@ -1325,6 +1348,19 @@ definitions: example: - ["unix:///var/run/pouchd.sock", "tcp://0.0.0.0:4243"] + DaemonUpdateConfig: + type: "object" + properties: + Labels: + description: "Labels indentified the attributes of daemon" + type: "array" + items: + type: "string" + example: ["storage=ssd", "zone=hangzhou"] + ImageProxy: + description: "Image proxy used to pull image." + type: "string" + RegistryServiceConfig: description: | RegistryServiceConfig stores daemon registry services configuration. diff --git a/apis/types/daemon_update_config.go b/apis/types/daemon_update_config.go new file mode 100644 index 000000000..d2a572f3f --- /dev/null +++ b/apis/types/daemon_update_config.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" +) + +// DaemonUpdateConfig daemon update config +// swagger:model DaemonUpdateConfig + +type DaemonUpdateConfig struct { + + // Image proxy used to pull image. + ImageProxy string `json:"ImageProxy,omitempty"` + + // Labels indentified the attributes of daemon + Labels []string `json:"Labels"` +} + +/* polymorph DaemonUpdateConfig ImageProxy false */ + +/* polymorph DaemonUpdateConfig Labels false */ + +// Validate validates this daemon update config +func (m *DaemonUpdateConfig) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateLabels(formats); err != nil { + // prop + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *DaemonUpdateConfig) validateLabels(formats strfmt.Registry) error { + + if swag.IsZero(m.Labels) { // not required + return nil + } + + return nil +} + +// MarshalBinary interface implementation +func (m *DaemonUpdateConfig) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *DaemonUpdateConfig) UnmarshalBinary(b []byte) error { + var res DaemonUpdateConfig + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/daemon/config/config.go b/daemon/config/config.go index eac76680d..7c3b7ac0e 100644 --- a/daemon/config/config.go +++ b/daemon/config/config.go @@ -3,6 +3,7 @@ package config import ( "fmt" "strings" + "sync" "github.com/alibaba/pouch/client" "github.com/alibaba/pouch/cri" @@ -13,6 +14,8 @@ import ( // Config refers to daemon's whole configurations. type Config struct { + sync.Mutex + //Volume config VolumeConfig volume.Config diff --git a/daemon/mgr/system.go b/daemon/mgr/system.go index ac2ac2361..fdedf1339 100644 --- a/daemon/mgr/system.go +++ b/daemon/mgr/system.go @@ -1,16 +1,20 @@ package mgr import ( + "fmt" "runtime" + "strings" "sync/atomic" "github.com/alibaba/pouch/apis/types" "github.com/alibaba/pouch/daemon/config" + "github.com/alibaba/pouch/pkg/errtypes" "github.com/alibaba/pouch/pkg/kernel" "github.com/alibaba/pouch/pkg/meta" "github.com/alibaba/pouch/registry" "github.com/alibaba/pouch/version" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -19,6 +23,7 @@ type SystemMgr interface { Info() (types.SystemInfo, error) Version() (types.SystemVersion, error) Auth(*types.AuthConfig) (string, error) + UpdateDaemon(*types.DaemonUpdateConfig) error } // SystemManager is an instance of system management. @@ -131,3 +136,38 @@ func (mgr *SystemManager) Version() (types.SystemVersion, error) { func (mgr *SystemManager) Auth(auth *types.AuthConfig) (string, error) { return mgr.registry.Auth(auth) } + +// UpdateDaemon updates config of daemon, only label and image proxy are allowed. +func (mgr *SystemManager) UpdateDaemon(cfg *types.DaemonUpdateConfig) error { + if cfg == nil || (len(cfg.Labels) == 0 && cfg.ImageProxy == "") { + return errors.Wrap(errtypes.ErrInvalidParam, fmt.Sprintf("daemon update config cannot be empty")) + } + + daemonCfg := mgr.config + + daemonCfg.Lock() + + daemonCfg.ImageProxy = cfg.ImageProxy + + length := len(daemonCfg.Labels) + for _, newLabel := range cfg.Labels { + appearedKey := false + newLabelSlice := strings.SplitN(newLabel, "=", 2) + for i := 0; i < length; i++ { + oldLabelSlice := strings.SplitN(daemonCfg.Labels[i], "=", 2) + if newLabelSlice[0] == oldLabelSlice[0] { + // newLabel's key already appears in daemon's origin labels + daemonCfg.Labels[i] = newLabel + appearedKey = true + continue + } + } + if !appearedKey { + daemonCfg.Labels = append(daemonCfg.Labels, newLabel) + } + } + + daemonCfg.Unlock() + + return nil +} diff --git a/test/api_daemon_update_test.go b/test/api_daemon_update_test.go new file mode 100644 index 000000000..28d5add10 --- /dev/null +++ b/test/api_daemon_update_test.go @@ -0,0 +1,48 @@ +package main + +import ( + "github.com/alibaba/pouch/apis/types" + "github.com/alibaba/pouch/test/environment" + "github.com/alibaba/pouch/test/request" + + "github.com/go-check/check" +) + +// APIDaemonUpdateSuite is the test suite for daemon update API. +type APIDaemonUpdateSuite struct{} + +func init() { + check.Suite(&APIDaemonUpdateSuite{}) +} + +// SetUpTest does common setup in the beginning of each test. +func (suite *APIDaemonUpdateSuite) SetUpTest(c *check.C) { + SkipIfFalse(c, environment.IsLinux) +} + +// TestStartOk tests starting container could work. +func (suite *APIDaemonUpdateSuite) TestUpdateDaemon(c *check.C) { + labels := []string{ + "storage=ssd", + "zone=shanghai", + } + + obj := map[string]interface{}{ + "Labels": labels, + "ImageProxy": "http://192.168.0.3:2378", + } + + body := request.WithJSONBody(obj) + resp, err := request.Post("/daemon/update", body) + c.Assert(err, check.IsNil) + CheckRespStatus(c, resp, 200) + + resp, err = request.Get("/info", body) + + info := types.SystemInfo{} + err = request.DecodeBody(&info, resp.Body) + c.Assert(err, check.IsNil) + + c.Assert(info.Labels, check.DeepEquals, labels) + // TODO: add checking image proxy +}