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