config.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package controlapi
  2. import (
  3. "bytes"
  4. "strings"
  5. "github.com/Sirupsen/logrus"
  6. "github.com/docker/swarmkit/api"
  7. "github.com/docker/swarmkit/identity"
  8. "github.com/docker/swarmkit/log"
  9. "github.com/docker/swarmkit/manager/state/store"
  10. "golang.org/x/net/context"
  11. "google.golang.org/grpc"
  12. "google.golang.org/grpc/codes"
  13. )
  14. // MaxConfigSize is the maximum byte length of the `Config.Spec.Data` field.
  15. const MaxConfigSize = 500 * 1024 // 500KB
  16. // assumes spec is not nil
  17. func configFromConfigSpec(spec *api.ConfigSpec) *api.Config {
  18. return &api.Config{
  19. ID: identity.NewID(),
  20. Spec: *spec,
  21. }
  22. }
  23. // GetConfig returns a `GetConfigResponse` with a `Config` with the same
  24. // id as `GetConfigRequest.ConfigID`
  25. // - Returns `NotFound` if the Config with the given id is not found.
  26. // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty.
  27. // - Returns an error if getting fails.
  28. func (s *Server) GetConfig(ctx context.Context, request *api.GetConfigRequest) (*api.GetConfigResponse, error) {
  29. if request.ConfigID == "" {
  30. return nil, grpc.Errorf(codes.InvalidArgument, "config ID must be provided")
  31. }
  32. var config *api.Config
  33. s.store.View(func(tx store.ReadTx) {
  34. config = store.GetConfig(tx, request.ConfigID)
  35. })
  36. if config == nil {
  37. return nil, grpc.Errorf(codes.NotFound, "config %s not found", request.ConfigID)
  38. }
  39. return &api.GetConfigResponse{Config: config}, nil
  40. }
  41. // UpdateConfig updates a Config referenced by ConfigID with the given ConfigSpec.
  42. // - Returns `NotFound` if the Config is not found.
  43. // - Returns `InvalidArgument` if the ConfigSpec is malformed or anything other than Labels is changed
  44. // - Returns an error if the update fails.
  45. func (s *Server) UpdateConfig(ctx context.Context, request *api.UpdateConfigRequest) (*api.UpdateConfigResponse, error) {
  46. if request.ConfigID == "" || request.ConfigVersion == nil {
  47. return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
  48. }
  49. var config *api.Config
  50. err := s.store.Update(func(tx store.Tx) error {
  51. config = store.GetConfig(tx, request.ConfigID)
  52. if config == nil {
  53. return grpc.Errorf(codes.NotFound, "config %s not found", request.ConfigID)
  54. }
  55. // Check if the Name is different than the current name, or the config is non-nil and different
  56. // than the current config
  57. if config.Spec.Annotations.Name != request.Spec.Annotations.Name ||
  58. (request.Spec.Data != nil && !bytes.Equal(request.Spec.Data, config.Spec.Data)) {
  59. return grpc.Errorf(codes.InvalidArgument, "only updates to Labels are allowed")
  60. }
  61. // We only allow updating Labels
  62. config.Meta.Version = *request.ConfigVersion
  63. config.Spec.Annotations.Labels = request.Spec.Annotations.Labels
  64. return store.UpdateConfig(tx, config)
  65. })
  66. if err != nil {
  67. return nil, err
  68. }
  69. log.G(ctx).WithFields(logrus.Fields{
  70. "config.ID": request.ConfigID,
  71. "config.Name": request.Spec.Annotations.Name,
  72. "method": "UpdateConfig",
  73. }).Debugf("config updated")
  74. return &api.UpdateConfigResponse{
  75. Config: config,
  76. }, nil
  77. }
  78. // ListConfigs returns a `ListConfigResponse` with a list all non-internal `Config`s being
  79. // managed, or all configs matching any name in `ListConfigsRequest.Names`, any
  80. // name prefix in `ListConfigsRequest.NamePrefixes`, any id in
  81. // `ListConfigsRequest.ConfigIDs`, or any id prefix in `ListConfigsRequest.IDPrefixes`.
  82. // - Returns an error if listing fails.
  83. func (s *Server) ListConfigs(ctx context.Context, request *api.ListConfigsRequest) (*api.ListConfigsResponse, error) {
  84. var (
  85. configs []*api.Config
  86. respConfigs []*api.Config
  87. err error
  88. byFilters []store.By
  89. by store.By
  90. labels map[string]string
  91. )
  92. // return all configs that match either any of the names or any of the name prefixes (why would you give both?)
  93. if request.Filters != nil {
  94. for _, name := range request.Filters.Names {
  95. byFilters = append(byFilters, store.ByName(name))
  96. }
  97. for _, prefix := range request.Filters.NamePrefixes {
  98. byFilters = append(byFilters, store.ByNamePrefix(prefix))
  99. }
  100. for _, prefix := range request.Filters.IDPrefixes {
  101. byFilters = append(byFilters, store.ByIDPrefix(prefix))
  102. }
  103. labels = request.Filters.Labels
  104. }
  105. switch len(byFilters) {
  106. case 0:
  107. by = store.All
  108. case 1:
  109. by = byFilters[0]
  110. default:
  111. by = store.Or(byFilters...)
  112. }
  113. s.store.View(func(tx store.ReadTx) {
  114. configs, err = store.FindConfigs(tx, by)
  115. })
  116. if err != nil {
  117. return nil, err
  118. }
  119. // filter by label
  120. for _, config := range configs {
  121. if !filterMatchLabels(config.Spec.Annotations.Labels, labels) {
  122. continue
  123. }
  124. respConfigs = append(respConfigs, config)
  125. }
  126. return &api.ListConfigsResponse{Configs: respConfigs}, nil
  127. }
  128. // CreateConfig creates and returns a `CreateConfigResponse` with a `Config` based
  129. // on the provided `CreateConfigRequest.ConfigSpec`.
  130. // - Returns `InvalidArgument` if the `CreateConfigRequest.ConfigSpec` is malformed,
  131. // or if the config data is too long or contains invalid characters.
  132. // - Returns an error if the creation fails.
  133. func (s *Server) CreateConfig(ctx context.Context, request *api.CreateConfigRequest) (*api.CreateConfigResponse, error) {
  134. if err := validateConfigSpec(request.Spec); err != nil {
  135. return nil, err
  136. }
  137. config := configFromConfigSpec(request.Spec) // the store will handle name conflicts
  138. err := s.store.Update(func(tx store.Tx) error {
  139. return store.CreateConfig(tx, config)
  140. })
  141. switch err {
  142. case store.ErrNameConflict:
  143. return nil, grpc.Errorf(codes.AlreadyExists, "config %s already exists", request.Spec.Annotations.Name)
  144. case nil:
  145. log.G(ctx).WithFields(logrus.Fields{
  146. "config.Name": request.Spec.Annotations.Name,
  147. "method": "CreateConfig",
  148. }).Debugf("config created")
  149. return &api.CreateConfigResponse{Config: config}, nil
  150. default:
  151. return nil, err
  152. }
  153. }
  154. // RemoveConfig removes the config referenced by `RemoveConfigRequest.ID`.
  155. // - Returns `InvalidArgument` if `RemoveConfigRequest.ID` is empty.
  156. // - Returns `NotFound` if the a config named `RemoveConfigRequest.ID` is not found.
  157. // - Returns `ConfigInUse` if the config is currently in use
  158. // - Returns an error if the deletion fails.
  159. func (s *Server) RemoveConfig(ctx context.Context, request *api.RemoveConfigRequest) (*api.RemoveConfigResponse, error) {
  160. if request.ConfigID == "" {
  161. return nil, grpc.Errorf(codes.InvalidArgument, "config ID must be provided")
  162. }
  163. err := s.store.Update(func(tx store.Tx) error {
  164. // Check if the config exists
  165. config := store.GetConfig(tx, request.ConfigID)
  166. if config == nil {
  167. return grpc.Errorf(codes.NotFound, "could not find config %s", request.ConfigID)
  168. }
  169. // Check if any services currently reference this config, return error if so
  170. services, err := store.FindServices(tx, store.ByReferencedConfigID(request.ConfigID))
  171. if err != nil {
  172. return grpc.Errorf(codes.Internal, "could not find services using config %s: %v", request.ConfigID, err)
  173. }
  174. if len(services) != 0 {
  175. serviceNames := make([]string, 0, len(services))
  176. for _, service := range services {
  177. serviceNames = append(serviceNames, service.Spec.Annotations.Name)
  178. }
  179. configName := config.Spec.Annotations.Name
  180. serviceNameStr := strings.Join(serviceNames, ", ")
  181. serviceStr := "services"
  182. if len(serviceNames) == 1 {
  183. serviceStr = "service"
  184. }
  185. return grpc.Errorf(codes.InvalidArgument, "config '%s' is in use by the following %s: %v", configName, serviceStr, serviceNameStr)
  186. }
  187. return store.DeleteConfig(tx, request.ConfigID)
  188. })
  189. switch err {
  190. case store.ErrNotExist:
  191. return nil, grpc.Errorf(codes.NotFound, "config %s not found", request.ConfigID)
  192. case nil:
  193. log.G(ctx).WithFields(logrus.Fields{
  194. "config.ID": request.ConfigID,
  195. "method": "RemoveConfig",
  196. }).Debugf("config removed")
  197. return &api.RemoveConfigResponse{}, nil
  198. default:
  199. return nil, err
  200. }
  201. }
  202. func validateConfigSpec(spec *api.ConfigSpec) error {
  203. if spec == nil {
  204. return grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
  205. }
  206. if err := validateConfigOrSecretAnnotations(spec.Annotations); err != nil {
  207. return err
  208. }
  209. if len(spec.Data) >= MaxConfigSize || len(spec.Data) < 1 {
  210. return grpc.Errorf(codes.InvalidArgument, "config data must be larger than 0 and less than %d bytes", MaxConfigSize)
  211. }
  212. return nil
  213. }