1/*
2   Copyright 2020 Docker Compose CLI 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 api
18
19import (
20	"context"
21
22	"github.com/compose-spec/compose-go/types"
23)
24
25// ServiceProxy implements Service by delegating to implementation functions. This allows lazy init and per-method overrides
26type ServiceProxy struct {
27	BuildFn              func(ctx context.Context, project *types.Project, options BuildOptions) error
28	PushFn               func(ctx context.Context, project *types.Project, options PushOptions) error
29	PullFn               func(ctx context.Context, project *types.Project, opts PullOptions) error
30	CreateFn             func(ctx context.Context, project *types.Project, opts CreateOptions) error
31	StartFn              func(ctx context.Context, project *types.Project, options StartOptions) error
32	RestartFn            func(ctx context.Context, project *types.Project, options RestartOptions) error
33	StopFn               func(ctx context.Context, project *types.Project, options StopOptions) error
34	UpFn                 func(ctx context.Context, project *types.Project, options UpOptions) error
35	DownFn               func(ctx context.Context, projectName string, options DownOptions) error
36	LogsFn               func(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error
37	PsFn                 func(ctx context.Context, projectName string, options PsOptions) ([]ContainerSummary, error)
38	ListFn               func(ctx context.Context, options ListOptions) ([]Stack, error)
39	ConvertFn            func(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
40	KillFn               func(ctx context.Context, project *types.Project, options KillOptions) error
41	RunOneOffContainerFn func(ctx context.Context, project *types.Project, opts RunOptions) (int, error)
42	RemoveFn             func(ctx context.Context, project *types.Project, options RemoveOptions) error
43	ExecFn               func(ctx context.Context, project string, opts RunOptions) (int, error)
44	CopyFn               func(ctx context.Context, project *types.Project, opts CopyOptions) error
45	PauseFn              func(ctx context.Context, project string, options PauseOptions) error
46	UnPauseFn            func(ctx context.Context, project string, options PauseOptions) error
47	TopFn                func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
48	EventsFn             func(ctx context.Context, project string, options EventsOptions) error
49	PortFn               func(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error)
50	ImagesFn             func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
51	interceptors         []Interceptor
52}
53
54// NewServiceProxy produces a ServiceProxy
55func NewServiceProxy() *ServiceProxy {
56	return &ServiceProxy{}
57}
58
59// Interceptor allow to customize the compose types.Project before the actual Service method is executed
60type Interceptor func(ctx context.Context, project *types.Project)
61
62var _ Service = &ServiceProxy{}
63
64// WithService configure proxy to use specified Service as delegate
65func (s *ServiceProxy) WithService(service Service) *ServiceProxy {
66	s.BuildFn = service.Build
67	s.PushFn = service.Push
68	s.PullFn = service.Pull
69	s.CreateFn = service.Create
70	s.StartFn = service.Start
71	s.RestartFn = service.Restart
72	s.StopFn = service.Stop
73	s.UpFn = service.Up
74	s.DownFn = service.Down
75	s.LogsFn = service.Logs
76	s.PsFn = service.Ps
77	s.ListFn = service.List
78	s.ConvertFn = service.Convert
79	s.KillFn = service.Kill
80	s.RunOneOffContainerFn = service.RunOneOffContainer
81	s.RemoveFn = service.Remove
82	s.ExecFn = service.Exec
83	s.CopyFn = service.Copy
84	s.PauseFn = service.Pause
85	s.UnPauseFn = service.UnPause
86	s.TopFn = service.Top
87	s.EventsFn = service.Events
88	s.PortFn = service.Port
89	s.ImagesFn = service.Images
90	return s
91}
92
93// WithInterceptor configures Interceptor to be applied to Service method execution
94func (s *ServiceProxy) WithInterceptor(interceptors ...Interceptor) *ServiceProxy {
95	s.interceptors = append(s.interceptors, interceptors...)
96	return s
97}
98
99// Build implements Service interface
100func (s *ServiceProxy) Build(ctx context.Context, project *types.Project, options BuildOptions) error {
101	if s.BuildFn == nil {
102		return ErrNotImplemented
103	}
104	for _, i := range s.interceptors {
105		i(ctx, project)
106	}
107	return s.BuildFn(ctx, project, options)
108}
109
110// Push implements Service interface
111func (s *ServiceProxy) Push(ctx context.Context, project *types.Project, options PushOptions) error {
112	if s.PushFn == nil {
113		return ErrNotImplemented
114	}
115	for _, i := range s.interceptors {
116		i(ctx, project)
117	}
118	return s.PushFn(ctx, project, options)
119}
120
121// Pull implements Service interface
122func (s *ServiceProxy) Pull(ctx context.Context, project *types.Project, options PullOptions) error {
123	if s.PullFn == nil {
124		return ErrNotImplemented
125	}
126	for _, i := range s.interceptors {
127		i(ctx, project)
128	}
129	return s.PullFn(ctx, project, options)
130}
131
132// Create implements Service interface
133func (s *ServiceProxy) Create(ctx context.Context, project *types.Project, options CreateOptions) error {
134	if s.CreateFn == nil {
135		return ErrNotImplemented
136	}
137	for _, i := range s.interceptors {
138		i(ctx, project)
139	}
140	return s.CreateFn(ctx, project, options)
141}
142
143// Start implements Service interface
144func (s *ServiceProxy) Start(ctx context.Context, project *types.Project, options StartOptions) error {
145	if s.StartFn == nil {
146		return ErrNotImplemented
147	}
148	for _, i := range s.interceptors {
149		i(ctx, project)
150	}
151	return s.StartFn(ctx, project, options)
152}
153
154// Restart implements Service interface
155func (s *ServiceProxy) Restart(ctx context.Context, project *types.Project, options RestartOptions) error {
156	if s.RestartFn == nil {
157		return ErrNotImplemented
158	}
159	for _, i := range s.interceptors {
160		i(ctx, project)
161	}
162	return s.RestartFn(ctx, project, options)
163}
164
165// Stop implements Service interface
166func (s *ServiceProxy) Stop(ctx context.Context, project *types.Project, options StopOptions) error {
167	if s.StopFn == nil {
168		return ErrNotImplemented
169	}
170	for _, i := range s.interceptors {
171		i(ctx, project)
172	}
173	return s.StopFn(ctx, project, options)
174}
175
176// Up implements Service interface
177func (s *ServiceProxy) Up(ctx context.Context, project *types.Project, options UpOptions) error {
178	if s.UpFn == nil {
179		return ErrNotImplemented
180	}
181	for _, i := range s.interceptors {
182		i(ctx, project)
183	}
184	return s.UpFn(ctx, project, options)
185}
186
187// Down implements Service interface
188func (s *ServiceProxy) Down(ctx context.Context, project string, options DownOptions) error {
189	if s.DownFn == nil {
190		return ErrNotImplemented
191	}
192	return s.DownFn(ctx, project, options)
193}
194
195// Logs implements Service interface
196func (s *ServiceProxy) Logs(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error {
197	if s.LogsFn == nil {
198		return ErrNotImplemented
199	}
200	return s.LogsFn(ctx, projectName, consumer, options)
201}
202
203// Ps implements Service interface
204func (s *ServiceProxy) Ps(ctx context.Context, project string, options PsOptions) ([]ContainerSummary, error) {
205	if s.PsFn == nil {
206		return nil, ErrNotImplemented
207	}
208	return s.PsFn(ctx, project, options)
209}
210
211// List implements Service interface
212func (s *ServiceProxy) List(ctx context.Context, options ListOptions) ([]Stack, error) {
213	if s.ListFn == nil {
214		return nil, ErrNotImplemented
215	}
216	return s.ListFn(ctx, options)
217}
218
219// Convert implements Service interface
220func (s *ServiceProxy) Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error) {
221	if s.ConvertFn == nil {
222		return nil, ErrNotImplemented
223	}
224	for _, i := range s.interceptors {
225		i(ctx, project)
226	}
227	return s.ConvertFn(ctx, project, options)
228}
229
230// Kill implements Service interface
231func (s *ServiceProxy) Kill(ctx context.Context, project *types.Project, options KillOptions) error {
232	if s.KillFn == nil {
233		return ErrNotImplemented
234	}
235	for _, i := range s.interceptors {
236		i(ctx, project)
237	}
238	return s.KillFn(ctx, project, options)
239}
240
241// RunOneOffContainer implements Service interface
242func (s *ServiceProxy) RunOneOffContainer(ctx context.Context, project *types.Project, options RunOptions) (int, error) {
243	if s.RunOneOffContainerFn == nil {
244		return 0, ErrNotImplemented
245	}
246	for _, i := range s.interceptors {
247		i(ctx, project)
248	}
249	return s.RunOneOffContainerFn(ctx, project, options)
250}
251
252// Remove implements Service interface
253func (s *ServiceProxy) Remove(ctx context.Context, project *types.Project, options RemoveOptions) error {
254	if s.RemoveFn == nil {
255		return ErrNotImplemented
256	}
257	for _, i := range s.interceptors {
258		i(ctx, project)
259	}
260	return s.RemoveFn(ctx, project, options)
261}
262
263// Exec implements Service interface
264func (s *ServiceProxy) Exec(ctx context.Context, project string, options RunOptions) (int, error) {
265	if s.ExecFn == nil {
266		return 0, ErrNotImplemented
267	}
268	return s.ExecFn(ctx, project, options)
269}
270
271// Copy implements Service interface
272func (s *ServiceProxy) Copy(ctx context.Context, project *types.Project, options CopyOptions) error {
273	if s.CopyFn == nil {
274		return ErrNotImplemented
275	}
276	for _, i := range s.interceptors {
277		i(ctx, project)
278	}
279	return s.CopyFn(ctx, project, options)
280}
281
282// Pause implements Service interface
283func (s *ServiceProxy) Pause(ctx context.Context, project string, options PauseOptions) error {
284	if s.PauseFn == nil {
285		return ErrNotImplemented
286	}
287	return s.PauseFn(ctx, project, options)
288}
289
290// UnPause implements Service interface
291func (s *ServiceProxy) UnPause(ctx context.Context, project string, options PauseOptions) error {
292	if s.UnPauseFn == nil {
293		return ErrNotImplemented
294	}
295	return s.UnPauseFn(ctx, project, options)
296}
297
298// Top implements Service interface
299func (s *ServiceProxy) Top(ctx context.Context, project string, services []string) ([]ContainerProcSummary, error) {
300	if s.TopFn == nil {
301		return nil, ErrNotImplemented
302	}
303	return s.TopFn(ctx, project, services)
304}
305
306// Events implements Service interface
307func (s *ServiceProxy) Events(ctx context.Context, project string, options EventsOptions) error {
308	if s.EventsFn == nil {
309		return ErrNotImplemented
310	}
311	return s.EventsFn(ctx, project, options)
312}
313
314// Port implements Service interface
315func (s *ServiceProxy) Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) {
316	if s.PortFn == nil {
317		return "", 0, ErrNotImplemented
318	}
319	return s.PortFn(ctx, project, service, port, options)
320}
321
322// Images implements Service interface
323func (s *ServiceProxy) Images(ctx context.Context, project string, options ImagesOptions) ([]ImageSummary, error) {
324	if s.ImagesFn == nil {
325		return nil, ErrNotImplemented
326	}
327	return s.ImagesFn(ctx, project, options)
328}
329