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  }