1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
package controlapi
import (
"context"
"strings"
"github.com/moby/swarmkit/v2/api"
"github.com/moby/swarmkit/v2/identity"
"github.com/moby/swarmkit/v2/log"
"github.com/moby/swarmkit/v2/manager/state/store"
"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(log.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(log.Fields{
"extension.ID": request.ExtensionID,
"method": "RemoveExtension",
}).Debugf("extension removed")
return &api.RemoveExtensionResponse{}, nil
default:
return nil, err
}
}
|