mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
![Sebastiaan van Stijn](/assets/img/avatar_default.png)
relevant changes: - swarmkit#2815 Extension and resource API declarations - swarmkit#2816 Moving swap options into `ResourceRequirements` instead of `ContainerSpec`s - relates to moby#37872 - swarmkit#2821 allocator: use a map for network-IDs to prevent O(n2) - swarmkit#2832 [api] Add created object to return types for extension and resource create apis - swarmkit#2831 [controlapi] Extension api implementation - swarmkit#2835 Resource controlapi Implemetation - swarmkit#2802 Use custom gRPC dialer to override default proxy dialer - addresses moby#35395 Swarm worker cannot connect to master if proxy is configured - addresses moby#issues/36951 Swarm nodes cannot join as masters if http proxy is set - relates to swarmkit#2419 Provide custom gRPC dialer to override default proxy dialer Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
133 lines
4.8 KiB
Go
133 lines
4.8 KiB
Go
package controlapi
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/docker/swarmkit/api"
|
|
"github.com/docker/swarmkit/identity"
|
|
"github.com/docker/swarmkit/log"
|
|
"github.com/docker/swarmkit/manager/state/store"
|
|
"github.com/sirupsen/logrus"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
// CreateExtension creates an `Extension` based on the provided `CreateExtensionRequest.Extension`
|
|
// and returns a `CreateExtensionResponse`.
|
|
// - Returns `InvalidArgument` if the `CreateExtensionRequest.Extension` is malformed,
|
|
// or fails validation.
|
|
// - Returns an error if the creation fails.
|
|
func (s *Server) CreateExtension(ctx context.Context, request *api.CreateExtensionRequest) (*api.CreateExtensionResponse, error) {
|
|
if request.Annotations == nil || request.Annotations.Name == "" {
|
|
return nil, status.Errorf(codes.InvalidArgument, "extension name must be provided")
|
|
}
|
|
|
|
extension := &api.Extension{
|
|
ID: identity.NewID(),
|
|
Annotations: *request.Annotations,
|
|
Description: request.Description,
|
|
}
|
|
|
|
err := s.store.Update(func(tx store.Tx) error {
|
|
return store.CreateExtension(tx, extension)
|
|
})
|
|
|
|
switch err {
|
|
case store.ErrNameConflict:
|
|
return nil, status.Errorf(codes.AlreadyExists, "extension %s already exists", request.Annotations.Name)
|
|
case nil:
|
|
log.G(ctx).WithFields(logrus.Fields{
|
|
"extension.Name": request.Annotations.Name,
|
|
"method": "CreateExtension",
|
|
}).Debugf("extension created")
|
|
|
|
return &api.CreateExtensionResponse{Extension: extension}, nil
|
|
default:
|
|
return nil, status.Errorf(codes.Internal, "could not create extension: %v", err.Error())
|
|
}
|
|
}
|
|
|
|
// GetExtension returns a `GetExtensionResponse` with a `Extension` with the same
|
|
// id as `GetExtensionRequest.extension_id`
|
|
// - Returns `NotFound` if the Extension with the given id is not found.
|
|
// - Returns `InvalidArgument` if the `GetExtensionRequest.extension_id` is empty.
|
|
// - Returns an error if the get fails.
|
|
func (s *Server) GetExtension(ctx context.Context, request *api.GetExtensionRequest) (*api.GetExtensionResponse, error) {
|
|
if request.ExtensionID == "" {
|
|
return nil, status.Errorf(codes.InvalidArgument, "extension ID must be provided")
|
|
}
|
|
|
|
var extension *api.Extension
|
|
s.store.View(func(tx store.ReadTx) {
|
|
extension = store.GetExtension(tx, request.ExtensionID)
|
|
})
|
|
|
|
if extension == nil {
|
|
return nil, status.Errorf(codes.NotFound, "extension %s not found", request.ExtensionID)
|
|
}
|
|
|
|
return &api.GetExtensionResponse{Extension: extension}, nil
|
|
}
|
|
|
|
// RemoveExtension removes the extension referenced by `RemoveExtensionRequest.ID`.
|
|
// - Returns `InvalidArgument` if `RemoveExtensionRequest.extension_id` is empty.
|
|
// - Returns `NotFound` if the an extension named `RemoveExtensionRequest.extension_id` is not found.
|
|
// - Returns an error if the deletion fails.
|
|
func (s *Server) RemoveExtension(ctx context.Context, request *api.RemoveExtensionRequest) (*api.RemoveExtensionResponse, error) {
|
|
if request.ExtensionID == "" {
|
|
return nil, status.Errorf(codes.InvalidArgument, "extension ID must be provided")
|
|
}
|
|
|
|
err := s.store.Update(func(tx store.Tx) error {
|
|
// Check if the extension exists
|
|
extension := store.GetExtension(tx, request.ExtensionID)
|
|
if extension == nil {
|
|
return status.Errorf(codes.NotFound, "could not find extension %s", request.ExtensionID)
|
|
}
|
|
|
|
// Check if any resources of this type present in the store, return error if so
|
|
resources, err := store.FindResources(tx, store.ByKind(request.ExtensionID))
|
|
if err != nil {
|
|
return status.Errorf(codes.Internal, "could not find resources using extension %s: %v", request.ExtensionID, err)
|
|
}
|
|
|
|
if len(resources) != 0 {
|
|
resourceNames := make([]string, 0, len(resources))
|
|
// Number of resources for an extension could be quite large.
|
|
// Show a limited number of resources for debugging.
|
|
attachedResourceForDebug := 10
|
|
for _, resource := range resources {
|
|
resourceNames = append(resourceNames, resource.Annotations.Name)
|
|
attachedResourceForDebug = attachedResourceForDebug - 1
|
|
if attachedResourceForDebug == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
extensionName := extension.Annotations.Name
|
|
resourceNameStr := strings.Join(resourceNames, ", ")
|
|
resourceStr := "resources"
|
|
if len(resourceNames) == 1 {
|
|
resourceStr = "resource"
|
|
}
|
|
|
|
return status.Errorf(codes.InvalidArgument, "extension '%s' is in use by the following %s: %v", extensionName, resourceStr, resourceNameStr)
|
|
}
|
|
|
|
return store.DeleteExtension(tx, request.ExtensionID)
|
|
})
|
|
switch err {
|
|
case store.ErrNotExist:
|
|
return nil, status.Errorf(codes.NotFound, "extension %s not found", request.ExtensionID)
|
|
case nil:
|
|
log.G(ctx).WithFields(logrus.Fields{
|
|
"extension.ID": request.ExtensionID,
|
|
"method": "RemoveExtension",
|
|
}).Debugf("extension removed")
|
|
|
|
return &api.RemoveExtensionResponse{}, nil
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|