1package daemon
2
3import (
4	"fmt"
5	"strings"
6	"syscall"
7
8	"github.com/pkg/errors"
9	"google.golang.org/grpc"
10)
11
12func errNotRunning(id string) error {
13	return stateConflictError{errors.Errorf("Container %s is not running", id)}
14}
15
16func containerNotFound(id string) error {
17	return objNotFoundError{"container", id}
18}
19
20func volumeNotFound(id string) error {
21	return objNotFoundError{"volume", id}
22}
23
24type objNotFoundError struct {
25	object string
26	id     string
27}
28
29func (e objNotFoundError) Error() string {
30	return "No such " + e.object + ": " + e.id
31}
32
33func (e objNotFoundError) NotFound() {}
34
35type stateConflictError struct {
36	cause error
37}
38
39func (e stateConflictError) Error() string {
40	return e.cause.Error()
41}
42
43func (e stateConflictError) Cause() error {
44	return e.cause
45}
46
47func (e stateConflictError) Conflict() {}
48
49func errContainerIsRestarting(containerID string) error {
50	cause := errors.Errorf("Container %s is restarting, wait until the container is running", containerID)
51	return stateConflictError{cause}
52}
53
54func errExecNotFound(id string) error {
55	return objNotFoundError{"exec instance", id}
56}
57
58func errExecPaused(id string) error {
59	cause := errors.Errorf("Container %s is paused, unpause the container before exec", id)
60	return stateConflictError{cause}
61}
62
63func errNotPaused(id string) error {
64	cause := errors.Errorf("Container %s is already paused", id)
65	return stateConflictError{cause}
66}
67
68type nameConflictError struct {
69	id   string
70	name string
71}
72
73func (e nameConflictError) Error() string {
74	return fmt.Sprintf("Conflict. The container name %q is already in use by container %q. You have to remove (or rename) that container to be able to reuse that name.", e.name, e.id)
75}
76
77func (nameConflictError) Conflict() {}
78
79type validationError struct {
80	cause error
81}
82
83func (e validationError) Error() string {
84	return e.cause.Error()
85}
86
87func (e validationError) InvalidParameter() {}
88
89func (e validationError) Cause() error {
90	return e.cause
91}
92
93type notAllowedError struct {
94	cause error
95}
96
97func (e notAllowedError) Error() string {
98	return e.cause.Error()
99}
100
101func (e notAllowedError) Forbidden() {}
102
103func (e notAllowedError) Cause() error {
104	return e.cause
105}
106
107type containerNotModifiedError struct {
108	running bool
109}
110
111func (e containerNotModifiedError) Error() string {
112	if e.running {
113		return "Container is already started"
114	}
115	return "Container is already stopped"
116}
117
118func (e containerNotModifiedError) NotModified() {}
119
120type systemError struct {
121	cause error
122}
123
124func (e systemError) Error() string {
125	return e.cause.Error()
126}
127
128func (e systemError) SystemError() {}
129
130func (e systemError) Cause() error {
131	return e.cause
132}
133
134type invalidIdentifier string
135
136func (e invalidIdentifier) Error() string {
137	return fmt.Sprintf("invalid name or ID supplied: %q", string(e))
138}
139
140func (invalidIdentifier) InvalidParameter() {}
141
142type duplicateMountPointError string
143
144func (e duplicateMountPointError) Error() string {
145	return "Duplicate mount point: " + string(e)
146}
147func (duplicateMountPointError) InvalidParameter() {}
148
149type containerFileNotFound struct {
150	file      string
151	container string
152}
153
154func (e containerFileNotFound) Error() string {
155	return "Could not find the file " + e.file + " in container " + e.container
156}
157
158func (containerFileNotFound) NotFound() {}
159
160type invalidFilter struct {
161	filter string
162	value  interface{}
163}
164
165func (e invalidFilter) Error() string {
166	msg := "Invalid filter '" + e.filter
167	if e.value != nil {
168		msg += fmt.Sprintf("=%s", e.value)
169	}
170	return msg + "'"
171}
172
173func (e invalidFilter) InvalidParameter() {}
174
175type unknownError struct {
176	cause error
177}
178
179func (e unknownError) Error() string {
180	return e.cause.Error()
181}
182
183func (unknownError) Unknown() {}
184
185func (e unknownError) Cause() error {
186	return e.cause
187}
188
189type startInvalidConfigError string
190
191func (e startInvalidConfigError) Error() string {
192	return string(e)
193}
194
195func (e startInvalidConfigError) InvalidParameter() {} // Is this right???
196
197func translateContainerdStartErr(cmd string, setExitCode func(int), err error) error {
198	errDesc := grpc.ErrorDesc(err)
199	contains := func(s1, s2 string) bool {
200		return strings.Contains(strings.ToLower(s1), s2)
201	}
202	var retErr error = unknownError{errors.New(errDesc)}
203	// if we receive an internal error from the initial start of a container then lets
204	// return it instead of entering the restart loop
205	// set to 127 for container cmd not found/does not exist)
206	if contains(errDesc, cmd) &&
207		(contains(errDesc, "executable file not found") ||
208			contains(errDesc, "no such file or directory") ||
209			contains(errDesc, "system cannot find the file specified")) {
210		setExitCode(127)
211		retErr = startInvalidConfigError(errDesc)
212	}
213	// set to 126 for container cmd can't be invoked errors
214	if contains(errDesc, syscall.EACCES.Error()) {
215		setExitCode(126)
216		retErr = startInvalidConfigError(errDesc)
217	}
218
219	// attempted to mount a file onto a directory, or a directory onto a file, maybe from user specified bind mounts
220	if contains(errDesc, syscall.ENOTDIR.Error()) {
221		errDesc += ": Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type"
222		setExitCode(127)
223		retErr = startInvalidConfigError(errDesc)
224	}
225
226	// TODO: it would be nice to get some better errors from containerd so we can return better errors here
227	return retErr
228}
229
230// TODO: cpuguy83 take care of it once the new library is ready
231type errNotFound struct{ error }
232
233func (errNotFound) NotFound() {}
234
235func (e errNotFound) Cause() error {
236	return e.error
237}
238
239// notFound is a helper to create an error of the class with the same name from any error type
240func notFound(err error) error {
241	if err == nil {
242		return nil
243	}
244	return errNotFound{err}
245}
246