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