github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/controlapi/extension.go (about) 1 package controlapi 2 3 import ( 4 "context" 5 "strings" 6 7 "github.com/docker/swarmkit/api" 8 "github.com/docker/swarmkit/identity" 9 "github.com/docker/swarmkit/log" 10 "github.com/docker/swarmkit/manager/state/store" 11 "github.com/sirupsen/logrus" 12 "google.golang.org/grpc/codes" 13 "google.golang.org/grpc/status" 14 ) 15 16 // CreateExtension creates an `Extension` based on the provided `CreateExtensionRequest.Extension` 17 // and returns a `CreateExtensionResponse`. 18 // - Returns `InvalidArgument` if the `CreateExtensionRequest.Extension` is malformed, 19 // or fails validation. 20 // - Returns an error if the creation fails. 21 func (s *Server) CreateExtension(ctx context.Context, request *api.CreateExtensionRequest) (*api.CreateExtensionResponse, error) { 22 if request.Annotations == nil || request.Annotations.Name == "" { 23 return nil, status.Errorf(codes.InvalidArgument, "extension name must be provided") 24 } 25 26 extension := &api.Extension{ 27 ID: identity.NewID(), 28 Annotations: *request.Annotations, 29 Description: request.Description, 30 } 31 32 err := s.store.Update(func(tx store.Tx) error { 33 return store.CreateExtension(tx, extension) 34 }) 35 36 switch err { 37 case store.ErrNameConflict: 38 return nil, status.Errorf(codes.AlreadyExists, "extension %s already exists", request.Annotations.Name) 39 case nil: 40 log.G(ctx).WithFields(logrus.Fields{ 41 "extension.Name": request.Annotations.Name, 42 "method": "CreateExtension", 43 }).Debugf("extension created") 44 45 return &api.CreateExtensionResponse{Extension: extension}, nil 46 default: 47 return nil, status.Errorf(codes.Internal, "could not create extension: %v", err.Error()) 48 } 49 } 50 51 // GetExtension returns a `GetExtensionResponse` with a `Extension` with the same 52 // id as `GetExtensionRequest.extension_id` 53 // - Returns `NotFound` if the Extension with the given id is not found. 54 // - Returns `InvalidArgument` if the `GetExtensionRequest.extension_id` is empty. 55 // - Returns an error if the get fails. 56 func (s *Server) GetExtension(ctx context.Context, request *api.GetExtensionRequest) (*api.GetExtensionResponse, error) { 57 if request.ExtensionID == "" { 58 return nil, status.Errorf(codes.InvalidArgument, "extension ID must be provided") 59 } 60 61 var extension *api.Extension 62 s.store.View(func(tx store.ReadTx) { 63 extension = store.GetExtension(tx, request.ExtensionID) 64 }) 65 66 if extension == nil { 67 return nil, status.Errorf(codes.NotFound, "extension %s not found", request.ExtensionID) 68 } 69 70 return &api.GetExtensionResponse{Extension: extension}, nil 71 } 72 73 // RemoveExtension removes the extension referenced by `RemoveExtensionRequest.ID`. 74 // - Returns `InvalidArgument` if `RemoveExtensionRequest.extension_id` is empty. 75 // - Returns `NotFound` if the an extension named `RemoveExtensionRequest.extension_id` is not found. 76 // - Returns an error if the deletion fails. 77 func (s *Server) RemoveExtension(ctx context.Context, request *api.RemoveExtensionRequest) (*api.RemoveExtensionResponse, error) { 78 if request.ExtensionID == "" { 79 return nil, status.Errorf(codes.InvalidArgument, "extension ID must be provided") 80 } 81 82 err := s.store.Update(func(tx store.Tx) error { 83 // Check if the extension exists 84 extension := store.GetExtension(tx, request.ExtensionID) 85 if extension == nil { 86 return status.Errorf(codes.NotFound, "could not find extension %s", request.ExtensionID) 87 } 88 89 // Check if any resources of this type present in the store, return error if so 90 resources, err := store.FindResources(tx, store.ByKind(request.ExtensionID)) 91 if err != nil { 92 return status.Errorf(codes.Internal, "could not find resources using extension %s: %v", request.ExtensionID, err) 93 } 94 95 if len(resources) != 0 { 96 resourceNames := make([]string, 0, len(resources)) 97 // Number of resources for an extension could be quite large. 98 // Show a limited number of resources for debugging. 99 attachedResourceForDebug := 10 100 for _, resource := range resources { 101 resourceNames = append(resourceNames, resource.Annotations.Name) 102 attachedResourceForDebug = attachedResourceForDebug - 1 103 if attachedResourceForDebug == 0 { 104 break 105 } 106 } 107 108 extensionName := extension.Annotations.Name 109 resourceNameStr := strings.Join(resourceNames, ", ") 110 resourceStr := "resources" 111 if len(resourceNames) == 1 { 112 resourceStr = "resource" 113 } 114 115 return status.Errorf(codes.InvalidArgument, "extension '%s' is in use by the following %s: %v", extensionName, resourceStr, resourceNameStr) 116 } 117 118 return store.DeleteExtension(tx, request.ExtensionID) 119 }) 120 switch err { 121 case store.ErrNotExist: 122 return nil, status.Errorf(codes.NotFound, "extension %s not found", request.ExtensionID) 123 case nil: 124 log.G(ctx).WithFields(logrus.Fields{ 125 "extension.ID": request.ExtensionID, 126 "method": "RemoveExtension", 127 }).Debugf("extension removed") 128 129 return &api.RemoveExtensionResponse{}, nil 130 default: 131 return nil, err 132 } 133 }