1/*
2   Copyright The containerd Authors.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17package leases
18
19import (
20	"context"
21
22	"google.golang.org/grpc"
23
24	api "github.com/containerd/containerd/api/services/leases/v1"
25	"github.com/containerd/containerd/errdefs"
26	"github.com/containerd/containerd/leases"
27	"github.com/containerd/containerd/plugin"
28	"github.com/containerd/containerd/services"
29	ptypes "github.com/gogo/protobuf/types"
30	"github.com/pkg/errors"
31)
32
33func init() {
34	plugin.Register(&plugin.Registration{
35		Type: plugin.GRPCPlugin,
36		ID:   "leases",
37		Requires: []plugin.Type{
38			plugin.ServicePlugin,
39		},
40		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
41			plugins, err := ic.GetByType(plugin.ServicePlugin)
42			if err != nil {
43				return nil, err
44			}
45			p, ok := plugins[services.LeasesService]
46			if !ok {
47				return nil, errors.New("leases service not found")
48			}
49			i, err := p.Instance()
50			if err != nil {
51				return nil, err
52			}
53			return &service{lm: i.(leases.Manager)}, nil
54		},
55	})
56}
57
58type service struct {
59	lm leases.Manager
60}
61
62func (s *service) Register(server *grpc.Server) error {
63	api.RegisterLeasesServer(server, s)
64	return nil
65}
66
67func (s *service) Create(ctx context.Context, r *api.CreateRequest) (*api.CreateResponse, error) {
68	opts := []leases.Opt{
69		leases.WithLabels(r.Labels),
70	}
71	if r.ID == "" {
72		opts = append(opts, leases.WithRandomID())
73	} else {
74		opts = append(opts, leases.WithID(r.ID))
75	}
76
77	l, err := s.lm.Create(ctx, opts...)
78	if err != nil {
79		return nil, errdefs.ToGRPC(err)
80	}
81
82	return &api.CreateResponse{
83		Lease: leaseToGRPC(l),
84	}, nil
85}
86
87func (s *service) Delete(ctx context.Context, r *api.DeleteRequest) (*ptypes.Empty, error) {
88	var opts []leases.DeleteOpt
89	if r.Sync {
90		opts = append(opts, leases.SynchronousDelete)
91	}
92	if err := s.lm.Delete(ctx, leases.Lease{
93		ID: r.ID,
94	}, opts...); err != nil {
95		return nil, errdefs.ToGRPC(err)
96	}
97	return &ptypes.Empty{}, nil
98}
99
100func (s *service) List(ctx context.Context, r *api.ListRequest) (*api.ListResponse, error) {
101	l, err := s.lm.List(ctx, r.Filters...)
102	if err != nil {
103		return nil, errdefs.ToGRPC(err)
104	}
105
106	apileases := make([]*api.Lease, len(l))
107	for i := range l {
108		apileases[i] = leaseToGRPC(l[i])
109	}
110
111	return &api.ListResponse{
112		Leases: apileases,
113	}, nil
114}
115
116func (s *service) AddResource(ctx context.Context, r *api.AddResourceRequest) (*ptypes.Empty, error) {
117	lease := leases.Lease{
118		ID: r.ID,
119	}
120
121	if err := s.lm.AddResource(ctx, lease, leases.Resource{
122		ID:   r.Resource.ID,
123		Type: r.Resource.Type,
124	}); err != nil {
125		return nil, errdefs.ToGRPC(err)
126	}
127	return &ptypes.Empty{}, nil
128}
129
130func (s *service) DeleteResource(ctx context.Context, r *api.DeleteResourceRequest) (*ptypes.Empty, error) {
131	lease := leases.Lease{
132		ID: r.ID,
133	}
134
135	if err := s.lm.DeleteResource(ctx, lease, leases.Resource{
136		ID:   r.Resource.ID,
137		Type: r.Resource.Type,
138	}); err != nil {
139		return nil, errdefs.ToGRPC(err)
140	}
141	return &ptypes.Empty{}, nil
142}
143
144func (s *service) ListResources(ctx context.Context, r *api.ListResourcesRequest) (*api.ListResourcesResponse, error) {
145	lease := leases.Lease{
146		ID: r.ID,
147	}
148
149	rs, err := s.lm.ListResources(ctx, lease)
150	if err != nil {
151		return nil, errdefs.ToGRPC(err)
152	}
153
154	apiResources := make([]api.Resource, 0, len(rs))
155	for _, i := range rs {
156		apiResources = append(apiResources, api.Resource{
157			ID:   i.ID,
158			Type: i.Type,
159		})
160	}
161	return &api.ListResourcesResponse{
162		Resources: apiResources,
163	}, nil
164}
165
166func leaseToGRPC(l leases.Lease) *api.Lease {
167	return &api.Lease{
168		ID:        l.ID,
169		Labels:    l.Labels,
170		CreatedAt: l.CreatedAt,
171	}
172}
173