File: extension.go

package info (click to toggle)
docker.io 26.1.5%2Bdfsg1-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 68,576 kB
  • sloc: sh: 5,748; makefile: 912; ansic: 664; asm: 228; python: 162
file content (132 lines) | stat: -rw-r--r-- 4,854 bytes parent folder | download
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
	}
}