func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // log the request log.Debugf("Calling %s %s", localMethod, localRoute) if logging { log.Infof("%s %s", r.Method, r.RequestURI) } if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") { userAgent := strings.Split(r.Header.Get("User-Agent"), "/") if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) { log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion) } } version := version.Version(mux.Vars(r)["version"]) if version == "" { version = api.APIVERSION } if corsHeaders != "" { writeCorsHeaders(w, r, corsHeaders) } if version.GreaterThan(api.APIVERSION) { http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound) return } if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil { log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) httpError(w, err) } } }
// MakeImageConfig returns immutable configuration JSON for image based on the // v1Compatibility object, layer digest and parent StrongID. SHA256() of this // config is the new image ID (strongID). func MakeImageConfig(v1Compatibility []byte, layerID, parentID digest.Digest) ([]byte, error) { // Detect images created after 1.8.3 img, err := NewImgJSON(v1Compatibility) if err != nil { return nil, err } useFallback := version.Version(img.DockerVersion).LessThan(noFallbackMinVersion) if useFallback { // Fallback for pre-1.8.3. Calculate base config based on Image struct // so that fields with default values added by Docker will use same ID logrus.Debugf("Using fallback hash for %v", layerID) v1Compatibility, err = json.Marshal(img) if err != nil { return nil, err } } var c map[string]*json.RawMessage if err := json.Unmarshal(v1Compatibility, &c); err != nil { return nil, err } if err := layerID.Validate(); err != nil { return nil, fmt.Errorf("invalid layerID: %v", err) } c["layer_id"] = rawJSON(layerID) if parentID != "" { if err := parentID.Validate(); err != nil { return nil, fmt.Errorf("invalid parentID %v", err) } c["parent_id"] = rawJSON(parentID) } delete(c, "id") delete(c, "parent") delete(c, "Size") // Size is calculated from data on disk and is inconsitent return json.Marshal(c) }
// we keep enableCors just for legacy usage, need to be removed in the future func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders string, dockerVersion string) *mux.Router { r := mux.NewRouter() if os.Getenv("DEBUG") != "" { AttachProfiler(r) } m := map[string]map[string]HttpApiFunc{ "GET": { "/_ping": ping, "/events": getEvents, "/info": getInfo, "/version": getVersion, "/images/json": getImagesJSON, "/images/viz": getImagesViz, "/images/search": getImagesSearch, "/images/get": getImagesGet, "/images/{name:.*}/get": getImagesGet, "/images/{name:.*}/history": getImagesHistory, "/images/{name:.*}/json": getImagesByName, "/containers/ps": getContainersJSON, "/containers/json": getContainersJSON, "/containers/{name:.*}/export": getContainersExport, "/containers/{name:.*}/changes": getContainersChanges, "/containers/{name:.*}/json": getContainersByName, "/containers/{name:.*}/top": getContainersTop, "/containers/{name:.*}/logs": getContainersLogs, "/containers/{name:.*}/stats": getContainersStats, "/containers/{name:.*}/attach/ws": wsContainersAttach, "/exec/{id:.*}/json": getExecByID, }, "POST": { "/auth": postAuth, "/commit": postCommit, "/build": postBuild, "/images/create": postImagesCreate, "/images/load": postImagesLoad, "/images/{name:.*}/push": postImagesPush, "/images/{name:.*}/tag": postImagesTag, "/containers/create": postContainersCreate, "/containers/{name:.*}/kill": postContainersKill, "/containers/{name:.*}/pause": postContainersPause, "/containers/{name:.*}/unpause": postContainersUnpause, "/containers/{name:.*}/restart": postContainersRestart, "/containers/{name:.*}/start": postContainersStart, "/containers/{name:.*}/stop": postContainersStop, "/containers/{name:.*}/wait": postContainersWait, "/containers/{name:.*}/resize": postContainersResize, "/containers/{name:.*}/attach": postContainersAttach, "/containers/{name:.*}/copy": postContainersCopy, "/containers/{name:.*}/exec": postContainerExecCreate, "/exec/{name:.*}/start": postContainerExecStart, "/exec/{name:.*}/resize": postContainerExecResize, "/containers/{name:.*}/rename": postContainerRename, }, "DELETE": { "/containers/{name:.*}": deleteContainers, "/images/{name:.*}": deleteImages, }, "OPTIONS": { "": optionsHandler, }, } // If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*" // otherwise, all head values will be passed to HTTP handler if corsHeaders == "" && enableCors { corsHeaders = "*" } for method, routes := range m { for route, fct := range routes { log.Debugf("Registering %s, %s", method, route) // NOTE: scope issue, make sure the variables are local and won't be changed localRoute := route localFct := fct localMethod := method // build the handler function f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, corsHeaders, version.Version(dockerVersion)) // add the new route if localRoute == "" { r.Methods(localMethod).HandlerFunc(f) } else { r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) r.Path(localRoute).Methods(localMethod).HandlerFunc(f) } } } return r }
"regexp" "time" "github.com/flynn/flynn/Godeps/_workspace/src/github.com/Sirupsen/logrus" "github.com/flynn/flynn/Godeps/_workspace/src/github.com/docker/distribution/digest" derr "github.com/flynn/flynn/Godeps/_workspace/src/github.com/docker/docker/errors" "github.com/flynn/flynn/Godeps/_workspace/src/github.com/docker/docker/pkg/version" "github.com/flynn/flynn/Godeps/_workspace/src/github.com/docker/docker/runconfig" ) var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`) // noFallbackMinVersion is the minimum version for which v1compatibility // information will not be marshaled through the Image struct to remove // blank fields. var noFallbackMinVersion = version.Version("1.8.3") // Descriptor provides the information necessary to register an image in // the graph. type Descriptor interface { ID() string Parent() string MarshalConfig() ([]byte, error) } // Image stores the image configuration. // All fields in this struct must be marked `omitempty` to keep getting // predictable hashes from the old `v1Compatibility` configuration. type Image struct { // ID a unique 64 character identifier of the image ID string `json:"id,omitempty"`