func (b *buildCache) GetWithRemoteAPI(ctx context.Context, image string) (io.ReadCloser, error) { v, err := b.client.ServerVersion(ctx) if err != nil { return nil, err } if versions.LessThan(v.Version, "1.11.0") { return nil, fmt.Errorf("Buildcache needs at least Docker version v1.11") } if versions.LessThan(v.Version, "1.12.0") { logrus.Warnf("Docker versions before v1.12.0 have a bug causing extracting build cache through remote API to take very long time and use lots of disk space. Please consider upgrading before using this tool.") } id, err := b.getImageID(ctx, image) if err != nil { return nil, err } ids, err := b.getParentIDS(ctx, id) if err != nil { return nil, err } rc, err := b.client.ImageSave(ctx, ids) if err != nil { return nil, err } return b.filterSaveArchive(rc), nil }
func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } name := r.Form.Get("name") config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body) if err != nil { return err } version := httputils.VersionFromContext(ctx) adjustCPUShares := versions.LessThan(version, "1.19") ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ Name: name, Config: config, HostConfig: hostConfig, NetworkingConfig: networkingConfig, AdjustCPUShares: adjustCPUShares, }) if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, ccr) }
func (s *DockerSuite) TestApiStatsNetworkStatsVersioning(c *check.C) { testRequires(c, SameHostDaemon) testRequires(c, DaemonIsLinux) out, _ := runSleepingContainer(c) id := strings.TrimSpace(out) c.Assert(waitRun(id), checker.IsNil) wg := sync.WaitGroup{} for i := 17; i <= 21; i++ { wg.Add(1) go func() { defer wg.Done() apiVersion := fmt.Sprintf("v1.%d", i) statsJSONBlob := getVersionedStats(c, id, apiVersion) if versions.LessThan(apiVersion, "v1.21") { c.Assert(jsonBlobHasLTv121NetworkStats(statsJSONBlob), checker.Equals, true, check.Commentf("Stats JSON blob from API %s %#v does not look like a <v1.21 API stats structure", apiVersion, statsJSONBlob)) } else { c.Assert(jsonBlobHasGTE121NetworkStats(statsJSONBlob), checker.Equals, true, check.Commentf("Stats JSON blob from API %s %#v does not look like a >=v1.21 API stats structure", apiVersion, statsJSONBlob)) } }() } wg.Wait() }
func (v apiVersion) Compare(other string) (int, error) { if dockerapiversion.LessThan(string(v), other) { return -1, nil } else if dockerapiversion.GreaterThan(string(v), other) { return 1, nil } return 0, nil }
// ContainerInspect returns low-level information about a // container. Returns an error if the container cannot be found, or if // there is an error getting the data. func (daemon *Daemon) ContainerInspect(name string, size bool, version string) (interface{}, error) { switch { case versions.LessThan(version, "1.20"): return daemon.containerInspectPre120(name) case versions.Equal(version, "1.20"): return daemon.containerInspect120(name) } return daemon.ContainerInspectCurrent(name, size) }
func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { info, err := s.backend.SystemInfo() if err != nil { return err } if s.clusterProvider != nil { info.Swarm = s.clusterProvider.Info() } if versions.LessThan("1.25", httputils.VersionFromContext(ctx)) { // TODO: handle this conversion in engine-api type oldInfo struct { *types.Info ExecutionDriver string } return httputils.WriteJSON(w, http.StatusOK, &oldInfo{Info: info, ExecutionDriver: "<not supported>"}) } return httputils.WriteJSON(w, http.StatusOK, info) }
// ToParamWithVersion packs the Args into a string for easy transport from client to server. // The generated string will depend on the specified version (corresponding to the API version). func ToParamWithVersion(version string, a Args) (string, error) { // this way we don't URL encode {}, just empty space if a.Len() == 0 { return "", nil } // for daemons older than v1.10, filter must be of the form map[string][]string buf := []byte{} err := errors.New("") if version != "" && versions.LessThan(version, "1.22") { buf, err = json.Marshal(convertArgsToSlice(a.fields)) } else { buf, err = json.Marshal(a.fields) } if err != nil { return "", err } return string(buf), nil }
// WrapHandler returns a new handler function wrapping the previous one in the request chain. func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { apiVersion := vars["version"] if apiVersion == "" { apiVersion = v.defaultVersion } if versions.GreaterThan(apiVersion, v.defaultVersion) { return badRequestError{fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", apiVersion, v.defaultVersion)} } if versions.LessThan(apiVersion, v.minVersion) { return badRequestError{fmt.Errorf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", apiVersion, v.minVersion)} } header := fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS) w.Header().Set("Server", header) ctx = context.WithValue(ctx, "api-version", apiVersion) return handler(ctx, w, r, vars) } }
// MakeConfigFromV1Config creates an image config from the legacy V1 config format. func MakeConfigFromV1Config(imageJSON []byte, rootfs *image.RootFS, history []image.History) ([]byte, error) { var dver struct { DockerVersion string `json:"docker_version"` } if err := json.Unmarshal(imageJSON, &dver); err != nil { return nil, err } useFallback := versions.LessThan(dver.DockerVersion, noFallbackMinVersion) if useFallback { var v1Image image.V1Image err := json.Unmarshal(imageJSON, &v1Image) if err != nil { return nil, err } imageJSON, err = json.Marshal(v1Image) if err != nil { return nil, err } } var c map[string]*json.RawMessage if err := json.Unmarshal(imageJSON, &c); err != nil { return nil, err } delete(c, "id") delete(c, "parent") delete(c, "Size") // Size is calculated from data on disk and is inconsistent delete(c, "parent_id") delete(c, "layer_id") delete(c, "throwaway") c["rootfs"] = rawJSON(rootfs) c["history"] = rawJSON(history) return json.Marshal(c) }
// ContainerStats writes information about the container to the stream // given in the config object. func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error { if runtime.GOOS == "windows" { return errors.New("Windows does not support stats") } // Remote API version (used for backwards compatibility) apiVersion := config.Version container, err := daemon.GetContainer(prefixOrName) if err != nil { return err } // If the container is not running and requires no stream, return an empty stats. if !container.IsRunning() && !config.Stream { return json.NewEncoder(config.OutStream).Encode(&types.Stats{}) } outStream := config.OutStream if config.Stream { wf := ioutils.NewWriteFlusher(outStream) defer wf.Close() wf.Flush() outStream = wf } var preCPUStats types.CPUStats getStatJSON := func(v interface{}) *types.StatsJSON { ss := v.(types.StatsJSON) ss.PreCPUStats = preCPUStats // ss.MemoryStats.Limit = uint64(update.MemoryLimit) preCPUStats = ss.CPUStats return &ss } enc := json.NewEncoder(outStream) updates := daemon.subscribeToContainerStats(container) defer daemon.unsubscribeToContainerStats(container, updates) noStreamFirstFrame := true for { select { case v, ok := <-updates: if !ok { return nil } var statsJSON interface{} statsJSONPost120 := getStatJSON(v) if versions.LessThan(apiVersion, "1.21") { var ( rxBytes uint64 rxPackets uint64 rxErrors uint64 rxDropped uint64 txBytes uint64 txPackets uint64 txErrors uint64 txDropped uint64 ) for _, v := range statsJSONPost120.Networks { rxBytes += v.RxBytes rxPackets += v.RxPackets rxErrors += v.RxErrors rxDropped += v.RxDropped txBytes += v.TxBytes txPackets += v.TxPackets txErrors += v.TxErrors txDropped += v.TxDropped } statsJSON = &v1p20.StatsJSON{ Stats: statsJSONPost120.Stats, Network: types.NetworkStats{ RxBytes: rxBytes, RxPackets: rxPackets, RxErrors: rxErrors, RxDropped: rxDropped, TxBytes: txBytes, TxPackets: txPackets, TxErrors: txErrors, TxDropped: txDropped, }, } } else { statsJSON = statsJSONPost120 } if !config.Stream && noStreamFirstFrame { // prime the cpu stats so they aren't 0 in the final output noStreamFirstFrame = false continue } if err := enc.Encode(statsJSON); err != nil { return err } if !config.Stream { return nil } case <-ctx.Done(): return nil } } }