github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/api/server/router/swarm/helpers.go (about) 1 package swarm // import "github.com/docker/docker/api/server/router/swarm" 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 8 "github.com/docker/docker/api/server/httputils" 9 basictypes "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/backend" 11 "github.com/docker/docker/api/types/swarm" 12 "github.com/docker/docker/api/types/versions" 13 ) 14 15 // swarmLogs takes an http response, request, and selector, and writes the logs 16 // specified by the selector to the response 17 func (sr *swarmRouter) swarmLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, selector *backend.LogSelector) error { 18 // Args are validated before the stream starts because when it starts we're 19 // sending HTTP 200 by writing an empty chunk of data to tell the client that 20 // daemon is going to stream. By sending this initial HTTP 200 we can't report 21 // any error after the stream starts (i.e. container not found, wrong parameters) 22 // with the appropriate status code. 23 stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr") 24 if !(stdout || stderr) { 25 return fmt.Errorf("Bad parameters: you must choose at least one stream") 26 } 27 28 // there is probably a neater way to manufacture the ContainerLogsOptions 29 // struct, probably in the caller, to eliminate the dependency on net/http 30 logsConfig := &basictypes.ContainerLogsOptions{ 31 Follow: httputils.BoolValue(r, "follow"), 32 Timestamps: httputils.BoolValue(r, "timestamps"), 33 Since: r.Form.Get("since"), 34 Tail: r.Form.Get("tail"), 35 ShowStdout: stdout, 36 ShowStderr: stderr, 37 Details: httputils.BoolValue(r, "details"), 38 } 39 40 tty := false 41 // checking for whether logs are TTY involves iterating over every service 42 // and task. idk if there is a better way 43 for _, service := range selector.Services { 44 s, err := sr.backend.GetService(service, false) 45 if err != nil { 46 // maybe should return some context with this error? 47 return err 48 } 49 tty = (s.Spec.TaskTemplate.ContainerSpec != nil && s.Spec.TaskTemplate.ContainerSpec.TTY) || tty 50 } 51 for _, task := range selector.Tasks { 52 t, err := sr.backend.GetTask(task) 53 if err != nil { 54 // as above 55 return err 56 } 57 tty = t.Spec.ContainerSpec.TTY || tty 58 } 59 60 msgs, err := sr.backend.ServiceLogs(ctx, selector, logsConfig) 61 if err != nil { 62 return err 63 } 64 65 contentType := basictypes.MediaTypeRawStream 66 if !tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") { 67 contentType = basictypes.MediaTypeMultiplexedStream 68 } 69 w.Header().Set("Content-Type", contentType) 70 httputils.WriteLogStream(ctx, w, msgs, logsConfig, !tty) 71 return nil 72 } 73 74 // adjustForAPIVersion takes a version and service spec and removes fields to 75 // make the spec compatible with the specified version. 76 func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) { 77 if cliVersion == "" { 78 return 79 } 80 if versions.LessThan(cliVersion, "1.40") { 81 if service.TaskTemplate.ContainerSpec != nil { 82 // Sysctls for docker swarm services weren't supported before 83 // API version 1.40 84 service.TaskTemplate.ContainerSpec.Sysctls = nil 85 86 if service.TaskTemplate.ContainerSpec.Privileges != nil && service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec != nil { 87 // Support for setting credential-spec through configs was added in API 1.40 88 service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config = "" 89 } 90 for _, config := range service.TaskTemplate.ContainerSpec.Configs { 91 // support for the Runtime target was added in API 1.40 92 config.Runtime = nil 93 } 94 } 95 96 if service.TaskTemplate.Placement != nil { 97 // MaxReplicas for docker swarm services weren't supported before 98 // API version 1.40 99 service.TaskTemplate.Placement.MaxReplicas = 0 100 } 101 } 102 if versions.LessThan(cliVersion, "1.41") { 103 if service.TaskTemplate.ContainerSpec != nil { 104 // Capabilities and Ulimits for docker swarm services weren't 105 // supported before API version 1.41 106 service.TaskTemplate.ContainerSpec.CapabilityAdd = nil 107 service.TaskTemplate.ContainerSpec.CapabilityDrop = nil 108 service.TaskTemplate.ContainerSpec.Ulimits = nil 109 } 110 if service.TaskTemplate.Resources != nil && service.TaskTemplate.Resources.Limits != nil { 111 // Limits.Pids not supported before API version 1.41 112 service.TaskTemplate.Resources.Limits.Pids = 0 113 } 114 115 // jobs were only introduced in API version 1.41. Nil out both Job 116 // modes; if the service is one of these modes and subsequently has no 117 // mode, then something down the pipe will thrown an error. 118 service.Mode.ReplicatedJob = nil 119 service.Mode.GlobalJob = nil 120 } 121 }