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.
 | ||||
| 	Description string | ||||
| 
 | ||||
| 	// DefaultStatusCode should to be returned via the HTTP API. Some error
 | ||||
| 	// may have different status codes depending on the situation.
 | ||||
| 	DefaultStatusCode int | ||||
| 	// HTTPStatusCodes provides a list of status under which this error
 | ||||
| 	// condition may arise. If it is empty, the error condition may be seen
 | ||||
| 	// 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, | ||||
| 		Value:   "UNKNOWN", | ||||
| 		Message: "unknown error", | ||||
| 		Description: `Generic error returned when the error does not have an | ||||
| 		API classification.`, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeDigestInvalid, | ||||
|  | @ -40,7 +45,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		include a detail structure with the key "digest", including the | ||||
| 		invalid digest string. This error may also be returned when a manifest | ||||
| 		includes an invalid layer digest.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeSizeInvalid, | ||||
|  | @ -49,7 +54,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Description: `When a layer is uploaded, the provided size will be | ||||
| 		checked against the uploaded content. If they do not match, this error | ||||
| 		will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 		HTTPStatusCodes: []int{http.StatusBadRequest}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeNameInvalid, | ||||
|  | @ -57,7 +62,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Message: "manifest name did not match URI", | ||||
| 		Description: `During a manifest upload, if the name in the manifest | ||||
| 		does not match the uri name, this error will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeTagInvalid, | ||||
|  | @ -65,7 +70,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Message: "manifest tag did not match URI", | ||||
| 		Description: `During a manifest upload, if the tag in the manifest | ||||
| 		does not match the uri tag, this error will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeNameUnknown, | ||||
|  | @ -73,7 +78,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Message: "repository name not known to registry", | ||||
| 		Description: `This is returned if the name used during an operation is | ||||
| 		unknown to the registry.`, | ||||
| 		DefaultStatusCode: http.StatusNotFound, | ||||
| 		HTTPStatusCodes: []int{http.StatusNotFound}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeManifestUnknown, | ||||
|  | @ -81,7 +86,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Message: "manifest unknown", | ||||
| 		Description: `This error is returned when the manifest, identified by | ||||
| 		name and tag is unknown to the repository.`, | ||||
| 		DefaultStatusCode: http.StatusNotFound, | ||||
| 		HTTPStatusCodes: []int{http.StatusNotFound}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeManifestInvalid, | ||||
|  | @ -89,8 +94,9 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Message: "manifest invalid", | ||||
| 		Description: `During upload, manifests undergo several checks ensuring | ||||
| 		validity. If those checks fail, this error may be returned, unless a | ||||
| 		more specific error is included.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 		more specific error is included. The detail will contain information | ||||
| 		the failed validation.`, | ||||
| 		HTTPStatusCodes: []int{http.StatusBadRequest}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeManifestUnverified, | ||||
|  | @ -98,7 +104,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Message: "manifest failed signature verification", | ||||
| 		Description: `During manifest upload, if the manifest fails signature | ||||
| 		verification, this error will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 		HTTPStatusCodes: []int{http.StatusBadRequest}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeBlobUnknown, | ||||
|  | @ -108,7 +114,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		registry in a specified repository. This can be returned with a | ||||
| 		standard get or if a manifest references an unknown layer during | ||||
| 		upload.`, | ||||
| 		DefaultStatusCode: http.StatusNotFound, | ||||
| 		HTTPStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound}, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
|  | @ -117,7 +123,7 @@ var descriptors = []ErrorDescriptor{ | |||
| 		Message: "blob upload unknown to registry", | ||||
| 		Description: `If a blob upload has been cancelled or was never | ||||
| 		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 | ||||
| 
 | ||||
| func init() { | ||||
| 	errorCodeToDescriptors = make(map[ErrorCode]ErrorDescriptor, len(descriptors)) | ||||
| 	idToDescriptors = make(map[string]ErrorDescriptor, len(descriptors)) | ||||
| 	errorCodeToDescriptors = make(map[ErrorCode]ErrorDescriptor, len(Descriptors)) | ||||
| 	idToDescriptors = make(map[string]ErrorDescriptor, len(Descriptors)) | ||||
| 
 | ||||
| 	for _, descriptor := range descriptors { | ||||
| 	for _, descriptor := range Descriptors { | ||||
| 		errorCodeToDescriptors[descriptor.Code] = descriptor | ||||
| 		idToDescriptors[descriptor.Value] = descriptor | ||||
| 	} | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ import ( | |||
| // TestErrorCodes ensures that error code format, mappings and
 | ||||
| // marshaling/unmarshaling. round trips are stable.
 | ||||
| func TestErrorCodes(t *testing.T) { | ||||
| 	for _, desc := range descriptors { | ||||
| 	for _, desc := range Descriptors { | ||||
| 		if 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