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