github.com/lalkh/containerd@v1.4.3/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  }