github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/services/namespaces/local.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package namespaces 18 19 import ( 20 "context" 21 "strings" 22 23 eventstypes "github.com/containerd/containerd/api/events" 24 api "github.com/containerd/containerd/api/services/namespaces/v1" 25 "github.com/containerd/containerd/errdefs" 26 "github.com/containerd/containerd/events" 27 "github.com/containerd/containerd/metadata" 28 "github.com/containerd/containerd/namespaces" 29 "github.com/containerd/containerd/plugin" 30 "github.com/containerd/containerd/services" 31 ptypes "github.com/gogo/protobuf/types" 32 bolt "go.etcd.io/bbolt" 33 "google.golang.org/grpc" 34 "google.golang.org/grpc/codes" 35 "google.golang.org/grpc/status" 36 ) 37 38 func init() { 39 plugin.Register(&plugin.Registration{ 40 Type: plugin.ServicePlugin, 41 ID: services.NamespacesService, 42 Requires: []plugin.Type{ 43 plugin.MetadataPlugin, 44 }, 45 InitFn: func(ic *plugin.InitContext) (interface{}, error) { 46 m, err := ic.Get(plugin.MetadataPlugin) 47 if err != nil { 48 return nil, err 49 } 50 return &local{ 51 db: m.(*metadata.DB), 52 publisher: ic.Events, 53 }, nil 54 }, 55 }) 56 } 57 58 // Provide local namespaces service instead of local namespace store, 59 // because namespace store interface doesn't provide enough functionality 60 // for namespaces service. 61 type local struct { 62 db *metadata.DB 63 publisher events.Publisher 64 } 65 66 var _ api.NamespacesClient = &local{} 67 68 func (l *local) Get(ctx context.Context, req *api.GetNamespaceRequest, _ ...grpc.CallOption) (*api.GetNamespaceResponse, error) { 69 var resp api.GetNamespaceResponse 70 71 return &resp, l.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error { 72 labels, err := store.Labels(ctx, req.Name) 73 if err != nil { 74 return errdefs.ToGRPC(err) 75 } 76 77 resp.Namespace = api.Namespace{ 78 Name: req.Name, 79 Labels: labels, 80 } 81 82 return nil 83 }) 84 } 85 86 func (l *local) List(ctx context.Context, req *api.ListNamespacesRequest, _ ...grpc.CallOption) (*api.ListNamespacesResponse, error) { 87 var resp api.ListNamespacesResponse 88 89 return &resp, l.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error { 90 namespaces, err := store.List(ctx) 91 if err != nil { 92 return err 93 } 94 95 for _, namespace := range namespaces { 96 labels, err := store.Labels(ctx, namespace) 97 if err != nil { 98 // In general, this should be unlikely, since we are holding a 99 // transaction to service this request. 100 return errdefs.ToGRPC(err) 101 } 102 103 resp.Namespaces = append(resp.Namespaces, api.Namespace{ 104 Name: namespace, 105 Labels: labels, 106 }) 107 } 108 109 return nil 110 }) 111 } 112 113 func (l *local) Create(ctx context.Context, req *api.CreateNamespaceRequest, _ ...grpc.CallOption) (*api.CreateNamespaceResponse, error) { 114 var resp api.CreateNamespaceResponse 115 116 if err := l.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { 117 if err := store.Create(ctx, req.Namespace.Name, req.Namespace.Labels); err != nil { 118 return errdefs.ToGRPC(err) 119 } 120 121 for k, v := range req.Namespace.Labels { 122 if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil { 123 return err 124 } 125 } 126 127 resp.Namespace = req.Namespace 128 return nil 129 }); err != nil { 130 return &resp, err 131 } 132 133 if err := l.publisher.Publish(ctx, "/namespaces/create", &eventstypes.NamespaceCreate{ 134 Name: req.Namespace.Name, 135 Labels: req.Namespace.Labels, 136 }); err != nil { 137 return &resp, err 138 } 139 140 return &resp, nil 141 142 } 143 144 func (l *local) Update(ctx context.Context, req *api.UpdateNamespaceRequest, _ ...grpc.CallOption) (*api.UpdateNamespaceResponse, error) { 145 var resp api.UpdateNamespaceResponse 146 if err := l.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { 147 if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 { 148 for _, path := range req.UpdateMask.Paths { 149 switch { 150 case strings.HasPrefix(path, "labels."): 151 key := strings.TrimPrefix(path, "labels.") 152 if err := store.SetLabel(ctx, req.Namespace.Name, key, req.Namespace.Labels[key]); err != nil { 153 return err 154 } 155 default: 156 return status.Errorf(codes.InvalidArgument, "cannot update %q field", path) 157 } 158 } 159 } else { 160 // clear out the existing labels and then set them to the incoming request. 161 // get current set of labels 162 labels, err := store.Labels(ctx, req.Namespace.Name) 163 if err != nil { 164 return errdefs.ToGRPC(err) 165 } 166 167 for k := range labels { 168 if err := store.SetLabel(ctx, req.Namespace.Name, k, ""); err != nil { 169 return err 170 } 171 } 172 173 for k, v := range req.Namespace.Labels { 174 if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil { 175 return err 176 } 177 178 } 179 } 180 181 return nil 182 }); err != nil { 183 return &resp, err 184 } 185 186 if err := l.publisher.Publish(ctx, "/namespaces/update", &eventstypes.NamespaceUpdate{ 187 Name: req.Namespace.Name, 188 Labels: req.Namespace.Labels, 189 }); err != nil { 190 return &resp, err 191 } 192 193 return &resp, nil 194 } 195 196 func (l *local) Delete(ctx context.Context, req *api.DeleteNamespaceRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 197 if err := l.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { 198 return errdefs.ToGRPC(store.Delete(ctx, req.Name)) 199 }); err != nil { 200 return &ptypes.Empty{}, err 201 } 202 // set the namespace in the context before publishing the event 203 ctx = namespaces.WithNamespace(ctx, req.Name) 204 if err := l.publisher.Publish(ctx, "/namespaces/delete", &eventstypes.NamespaceDelete{ 205 Name: req.Name, 206 }); err != nil { 207 return &ptypes.Empty{}, err 208 } 209 210 return &ptypes.Empty{}, nil 211 } 212 213 func (l *local) withStore(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) func(tx *bolt.Tx) error { 214 return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewNamespaceStore(tx)) } 215 } 216 217 func (l *local) withStoreView(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error { 218 return l.db.View(l.withStore(ctx, fn)) 219 } 220 221 func (l *local) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error { 222 return l.db.Update(l.withStore(ctx, fn)) 223 }