forked from TrueCloudLab/distribution
Export error descriptors and provide tool generate markdown table
To support accurate specification generation, this changeset includes a quick and dirty tool to generate a markdown table of error codes generated by the registry API. Equivalent supports for routes will likely follow. Exported descriptors could be used to generate other documentation, as well.
This commit is contained in:
parent
87c10960d2
commit
b721b0a15c
3 changed files with 120 additions and 19 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
95
cmd/registry-api-doctable-gen/main.go
Normal file
95
cmd/registry-api-doctable-gen/main.go
Normal file
|
@ -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 a new issue