Merge pull request #841 from stevvooe/next-generation
Export error descriptors and provide tool generate markdown tablemaster
						commit
						500d11564b
					
				|  | @ -20,16 +20,21 @@ type ErrorDescriptor struct { | ||||||
| 	// for use in documentation.
 | 	// for use in documentation.
 | ||||||
| 	Description string | 	Description string | ||||||
| 
 | 
 | ||||||
| 	// DefaultStatusCode should to be returned via the HTTP API. Some error
 | 	// HTTPStatusCodes provides a list of status under which this error
 | ||||||
| 	// may have different status codes depending on the situation.
 | 	// condition may arise. If it is empty, the error condition may be seen
 | ||||||
| 	DefaultStatusCode int | 	// for any status code.
 | ||||||
|  | 	HTTPStatusCodes []int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var descriptors = []ErrorDescriptor{ | // Descriptors provides a list of HTTP API Error codes that may be encountered
 | ||||||
|  | // when interacting with the registry API.
 | ||||||
|  | var Descriptors = []ErrorDescriptor{ | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeUnknown, | 		Code:    ErrorCodeUnknown, | ||||||
| 		Value:   "UNKNOWN", | 		Value:   "UNKNOWN", | ||||||
| 		Message: "unknown error", | 		Message: "unknown error", | ||||||
|  | 		Description: `Generic error returned when the error does not have an | ||||||
|  | 		API classification.`, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeDigestInvalid, | 		Code:    ErrorCodeDigestInvalid, | ||||||
|  | @ -40,7 +45,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		include a detail structure with the key "digest", including the | 		include a detail structure with the key "digest", including the | ||||||
| 		invalid digest string. This error may also be returned when a manifest | 		invalid digest string. This error may also be returned when a manifest | ||||||
| 		includes an invalid layer digest.`, | 		includes an invalid layer digest.`, | ||||||
| 		DefaultStatusCode: http.StatusBadRequest, | 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeSizeInvalid, | 		Code:    ErrorCodeSizeInvalid, | ||||||
|  | @ -49,7 +54,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Description: `When a layer is uploaded, the provided size will be | 		Description: `When a layer is uploaded, the provided size will be | ||||||
| 		checked against the uploaded content. If they do not match, this error | 		checked against the uploaded content. If they do not match, this error | ||||||
| 		will be returned.`, | 		will be returned.`, | ||||||
| 		DefaultStatusCode: http.StatusBadRequest, | 		HTTPStatusCodes: []int{http.StatusBadRequest}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeNameInvalid, | 		Code:    ErrorCodeNameInvalid, | ||||||
|  | @ -57,7 +62,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Message: "manifest name did not match URI", | 		Message: "manifest name did not match URI", | ||||||
| 		Description: `During a manifest upload, if the name in the manifest | 		Description: `During a manifest upload, if the name in the manifest | ||||||
| 		does not match the uri name, this error will be returned.`, | 		does not match the uri name, this error will be returned.`, | ||||||
| 		DefaultStatusCode: http.StatusBadRequest, | 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeTagInvalid, | 		Code:    ErrorCodeTagInvalid, | ||||||
|  | @ -65,7 +70,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Message: "manifest tag did not match URI", | 		Message: "manifest tag did not match URI", | ||||||
| 		Description: `During a manifest upload, if the tag in the manifest | 		Description: `During a manifest upload, if the tag in the manifest | ||||||
| 		does not match the uri tag, this error will be returned.`, | 		does not match the uri tag, this error will be returned.`, | ||||||
| 		DefaultStatusCode: http.StatusBadRequest, | 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeNameUnknown, | 		Code:    ErrorCodeNameUnknown, | ||||||
|  | @ -73,7 +78,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Message: "repository name not known to registry", | 		Message: "repository name not known to registry", | ||||||
| 		Description: `This is returned if the name used during an operation is | 		Description: `This is returned if the name used during an operation is | ||||||
| 		unknown to the registry.`, | 		unknown to the registry.`, | ||||||
| 		DefaultStatusCode: http.StatusNotFound, | 		HTTPStatusCodes: []int{http.StatusNotFound}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeManifestUnknown, | 		Code:    ErrorCodeManifestUnknown, | ||||||
|  | @ -81,7 +86,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Message: "manifest unknown", | 		Message: "manifest unknown", | ||||||
| 		Description: `This error is returned when the manifest, identified by | 		Description: `This error is returned when the manifest, identified by | ||||||
| 		name and tag is unknown to the repository.`, | 		name and tag is unknown to the repository.`, | ||||||
| 		DefaultStatusCode: http.StatusNotFound, | 		HTTPStatusCodes: []int{http.StatusNotFound}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeManifestInvalid, | 		Code:    ErrorCodeManifestInvalid, | ||||||
|  | @ -89,8 +94,9 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Message: "manifest invalid", | 		Message: "manifest invalid", | ||||||
| 		Description: `During upload, manifests undergo several checks ensuring | 		Description: `During upload, manifests undergo several checks ensuring | ||||||
| 		validity. If those checks fail, this error may be returned, unless a | 		validity. If those checks fail, this error may be returned, unless a | ||||||
| 		more specific error is included.`, | 		more specific error is included. The detail will contain information | ||||||
| 		DefaultStatusCode: http.StatusBadRequest, | 		the failed validation.`, | ||||||
|  | 		HTTPStatusCodes: []int{http.StatusBadRequest}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeManifestUnverified, | 		Code:    ErrorCodeManifestUnverified, | ||||||
|  | @ -98,7 +104,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Message: "manifest failed signature verification", | 		Message: "manifest failed signature verification", | ||||||
| 		Description: `During manifest upload, if the manifest fails signature | 		Description: `During manifest upload, if the manifest fails signature | ||||||
| 		verification, this error will be returned.`, | 		verification, this error will be returned.`, | ||||||
| 		DefaultStatusCode: http.StatusBadRequest, | 		HTTPStatusCodes: []int{http.StatusBadRequest}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Code:    ErrorCodeBlobUnknown, | 		Code:    ErrorCodeBlobUnknown, | ||||||
|  | @ -108,7 +114,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		registry in a specified repository. This can be returned with a | 		registry in a specified repository. This can be returned with a | ||||||
| 		standard get or if a manifest references an unknown layer during | 		standard get or if a manifest references an unknown layer during | ||||||
| 		upload.`, | 		upload.`, | ||||||
| 		DefaultStatusCode: http.StatusNotFound, | 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	{ | 	{ | ||||||
|  | @ -117,7 +123,7 @@ var descriptors = []ErrorDescriptor{ | ||||||
| 		Message: "blob upload unknown to registry", | 		Message: "blob upload unknown to registry", | ||||||
| 		Description: `If a blob upload has been cancelled or was never | 		Description: `If a blob upload has been cancelled or was never | ||||||
| 		started, this error code may be returned.`, | 		started, this error code may be returned.`, | ||||||
| 		DefaultStatusCode: http.StatusNotFound, | 		HTTPStatusCodes: []int{http.StatusNotFound}, | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -125,10 +131,10 @@ var errorCodeToDescriptors map[ErrorCode]ErrorDescriptor | ||||||
| var idToDescriptors map[string]ErrorDescriptor | var idToDescriptors map[string]ErrorDescriptor | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	errorCodeToDescriptors = make(map[ErrorCode]ErrorDescriptor, len(descriptors)) | 	errorCodeToDescriptors = make(map[ErrorCode]ErrorDescriptor, len(Descriptors)) | ||||||
| 	idToDescriptors = make(map[string]ErrorDescriptor, len(descriptors)) | 	idToDescriptors = make(map[string]ErrorDescriptor, len(Descriptors)) | ||||||
| 
 | 
 | ||||||
| 	for _, descriptor := range descriptors { | 	for _, descriptor := range Descriptors { | ||||||
| 		errorCodeToDescriptors[descriptor.Code] = descriptor | 		errorCodeToDescriptors[descriptor.Code] = descriptor | ||||||
| 		idToDescriptors[descriptor.Value] = descriptor | 		idToDescriptors[descriptor.Value] = descriptor | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ import ( | ||||||
| // TestErrorCodes ensures that error code format, mappings and
 | // TestErrorCodes ensures that error code format, mappings and
 | ||||||
| // marshaling/unmarshaling. round trips are stable.
 | // marshaling/unmarshaling. round trips are stable.
 | ||||||
| func TestErrorCodes(t *testing.T) { | func TestErrorCodes(t *testing.T) { | ||||||
| 	for _, desc := range descriptors { | 	for _, desc := range Descriptors { | ||||||
| 		if desc.Code.String() != desc.Value { | 		if desc.Code.String() != desc.Value { | ||||||
| 			t.Fatalf("error code string incorrect: %q != %q", desc.Code.String(), desc.Value) | 			t.Fatalf("error code string incorrect: %q != %q", desc.Code.String(), desc.Value) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -0,0 +1,95 @@ | ||||||
|  | // registry-api-doctable-gen uses various descriptors within the registry code
 | ||||||
|  | // base to generate markdown tables for use in documentation. This is only
 | ||||||
|  | // meant to facilitate updates to documentation and not as an automated tool.
 | ||||||
|  | //
 | ||||||
|  | // For now, this only includes support for error codes:
 | ||||||
|  | //
 | ||||||
|  | // 	$ registry-api-doctable-gen errors
 | ||||||
|  | //
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  | 	"text/tabwriter" | ||||||
|  | 
 | ||||||
|  | 	"github.com/docker/docker-registry/api/errors" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  | 
 | ||||||
|  | 	if len(os.Args) < 2 { | ||||||
|  | 		log.Fatalln("please specify a table to generate: (errors)") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch os.Args[1] { | ||||||
|  | 	case "errors": | ||||||
|  | 		dumpErrors(os.Stdout) | ||||||
|  | 	default: | ||||||
|  | 		log.Fatalln("unknown descriptor table:", os.Args[1]) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func dumpErrors(wr io.Writer) { | ||||||
|  | 	writer := tabwriter.NewWriter(os.Stdout, 8, 8, 0, '\t', 0) | ||||||
|  | 	defer writer.Flush() | ||||||
|  | 
 | ||||||
|  | 	fmt.Fprint(writer, "|") | ||||||
|  | 	dtype := reflect.TypeOf(errors.ErrorDescriptor{}) | ||||||
|  | 	var fieldsPrinted int | ||||||
|  | 	for i := 0; i < dtype.NumField(); i++ { | ||||||
|  | 		field := dtype.Field(i) | ||||||
|  | 		if field.Name == "Value" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fmt.Fprint(writer, field.Name, "|") | ||||||
|  | 		fieldsPrinted++ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	divider := strings.Repeat("-", 8) | ||||||
|  | 	var parts []string | ||||||
|  | 	for i := 0; i < fieldsPrinted; i++ { | ||||||
|  | 		parts = append(parts, divider) | ||||||
|  | 	} | ||||||
|  | 	divider = strings.Join(parts, "|") | ||||||
|  | 
 | ||||||
|  | 	fmt.Fprintln(writer, "\n"+divider) | ||||||
|  | 
 | ||||||
|  | 	for _, descriptor := range errors.Descriptors { | ||||||
|  | 		fmt.Fprint(writer, "|") | ||||||
|  | 
 | ||||||
|  | 		v := reflect.ValueOf(descriptor) | ||||||
|  | 		for i := 0; i < dtype.NumField(); i++ { | ||||||
|  | 			value := v.Field(i).Interface() | ||||||
|  | 			field := v.Type().Field(i) | ||||||
|  | 			if field.Name == "Value" { | ||||||
|  | 				continue | ||||||
|  | 			} else if field.Name == "Description" { | ||||||
|  | 				value = strings.Replace(value.(string), "\n", " ", -1) | ||||||
|  | 			} else if field.Name == "Code" { | ||||||
|  | 				value = fmt.Sprintf("`%s`", value) | ||||||
|  | 			} else if field.Name == "HTTPStatusCodes" { | ||||||
|  | 				if len(value.([]int)) > 0 { | ||||||
|  | 					var codes []string | ||||||
|  | 					for _, code := range value.([]int) { | ||||||
|  | 						codes = append(codes, fmt.Sprint(code)) | ||||||
|  | 					} | ||||||
|  | 					value = strings.Join(codes, ", ") | ||||||
|  | 				} else { | ||||||
|  | 					value = "Any" | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			fmt.Fprint(writer, value, "|") | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fmt.Fprint(writer, "\n") | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue