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 docker
18
19import (
20	"fmt"
21	"net/http"
22	"sort"
23	"sync"
24)
25
26var (
27	errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{}
28	idToDescriptors        = map[string]ErrorDescriptor{}
29	groupToDescriptors     = map[string][]ErrorDescriptor{}
30)
31
32var (
33	// ErrorCodeUnknown is a generic error that can be used as a last
34	// resort if there is no situation-specific error message that can be used
35	ErrorCodeUnknown = Register("errcode", ErrorDescriptor{
36		Value:   "UNKNOWN",
37		Message: "unknown error",
38		Description: `Generic error returned when the error does not have an
39			                                            API classification.`,
40		HTTPStatusCode: http.StatusInternalServerError,
41	})
42
43	// ErrorCodeUnsupported is returned when an operation is not supported.
44	ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{
45		Value:   "UNSUPPORTED",
46		Message: "The operation is unsupported.",
47		Description: `The operation was unsupported due to a missing
48		implementation or invalid set of parameters.`,
49		HTTPStatusCode: http.StatusMethodNotAllowed,
50	})
51
52	// ErrorCodeUnauthorized is returned if a request requires
53	// authentication.
54	ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{
55		Value:   "UNAUTHORIZED",
56		Message: "authentication required",
57		Description: `The access controller was unable to authenticate
58		the client. Often this will be accompanied by a
59		Www-Authenticate HTTP response header indicating how to
60		authenticate.`,
61		HTTPStatusCode: http.StatusUnauthorized,
62	})
63
64	// ErrorCodeDenied is returned if a client does not have sufficient
65	// permission to perform an action.
66	ErrorCodeDenied = Register("errcode", ErrorDescriptor{
67		Value:   "DENIED",
68		Message: "requested access to the resource is denied",
69		Description: `The access controller denied access for the
70		operation on a resource.`,
71		HTTPStatusCode: http.StatusForbidden,
72	})
73
74	// ErrorCodeUnavailable provides a common error to report unavailability
75	// of a service or endpoint.
76	ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{
77		Value:          "UNAVAILABLE",
78		Message:        "service unavailable",
79		Description:    "Returned when a service is not available",
80		HTTPStatusCode: http.StatusServiceUnavailable,
81	})
82
83	// ErrorCodeTooManyRequests is returned if a client attempts too many
84	// times to contact a service endpoint.
85	ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{
86		Value:   "TOOMANYREQUESTS",
87		Message: "too many requests",
88		Description: `Returned when a client attempts to contact a
89		service too many times`,
90		HTTPStatusCode: http.StatusTooManyRequests,
91	})
92)
93
94var nextCode = 1000
95var registerLock sync.Mutex
96
97// Register will make the passed-in error known to the environment and
98// return a new ErrorCode
99func Register(group string, descriptor ErrorDescriptor) ErrorCode {
100	registerLock.Lock()
101	defer registerLock.Unlock()
102
103	descriptor.Code = ErrorCode(nextCode)
104
105	if _, ok := idToDescriptors[descriptor.Value]; ok {
106		panic(fmt.Sprintf("ErrorValue %q is already registered", descriptor.Value))
107	}
108	if _, ok := errorCodeToDescriptors[descriptor.Code]; ok {
109		panic(fmt.Sprintf("ErrorCode %v is already registered", descriptor.Code))
110	}
111
112	groupToDescriptors[group] = append(groupToDescriptors[group], descriptor)
113	errorCodeToDescriptors[descriptor.Code] = descriptor
114	idToDescriptors[descriptor.Value] = descriptor
115
116	nextCode++
117	return descriptor.Code
118}
119
120type byValue []ErrorDescriptor
121
122func (a byValue) Len() int           { return len(a) }
123func (a byValue) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
124func (a byValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
125
126// GetGroupNames returns the list of Error group names that are registered
127func GetGroupNames() []string {
128	keys := []string{}
129
130	for k := range groupToDescriptors {
131		keys = append(keys, k)
132	}
133	sort.Strings(keys)
134	return keys
135}
136
137// GetErrorCodeGroup returns the named group of error descriptors
138func GetErrorCodeGroup(name string) []ErrorDescriptor {
139	desc := groupToDescriptors[name]
140	sort.Sort(byValue(desc))
141	return desc
142}
143
144// GetErrorAllDescriptors returns a slice of all ErrorDescriptors that are
145// registered, irrespective of what group they're in
146func GetErrorAllDescriptors() []ErrorDescriptor {
147	result := []ErrorDescriptor{}
148
149	for _, group := range GetGroupNames() {
150		result = append(result, GetErrorCodeGroup(group)...)
151	}
152	sort.Sort(byValue(result))
153	return result
154}
155