github.com/moby/docker@v26.1.3+incompatible/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/container" 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 http.ResponseWriter, 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 LogsOptions 30 // struct, probably in the caller, to eliminate the dependency on net/http 31 logsConfig := &container.LogsOptions{ 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 contentType := basictypes.MediaTypeRawStream 67 if !tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") { 68 contentType = basictypes.MediaTypeMultiplexedStream 69 } 70 w.Header().Set("Content-Type", contentType) 71 httputils.WriteLogStream(ctx, w, msgs, logsConfig, !tty) 72 return nil 73 } 74 75 // adjustForAPIVersion takes a version and service spec and removes fields to 76 // make the spec compatible with the specified version. 77 func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) { 78 if cliVersion == "" { 79 return 80 } 81 if versions.LessThan(cliVersion, "1.40") { 82 if service.TaskTemplate.ContainerSpec != nil { 83 // Sysctls for docker swarm services weren't supported before 84 // API version 1.40 85 service.TaskTemplate.ContainerSpec.Sysctls = nil 86 87 if service.TaskTemplate.ContainerSpec.Privileges != nil && service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec != nil { 88 // Support for setting credential-spec through configs was added in API 1.40 89 service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config = "" 90 } 91 for _, config := range service.TaskTemplate.ContainerSpec.Configs { 92 // support for the Runtime target was added in API 1.40 93 config.Runtime = nil 94 } 95 } 96 97 if service.TaskTemplate.Placement != nil { 98 // MaxReplicas for docker swarm services weren't supported before 99 // API version 1.40 100 service.TaskTemplate.Placement.MaxReplicas = 0 101 } 102 } 103 if versions.LessThan(cliVersion, "1.41") { 104 if service.TaskTemplate.ContainerSpec != nil { 105 // Capabilities and Ulimits for docker swarm services weren't 106 // supported before API version 1.41 107 service.TaskTemplate.ContainerSpec.CapabilityAdd = nil 108 service.TaskTemplate.ContainerSpec.CapabilityDrop = nil 109 service.TaskTemplate.ContainerSpec.Ulimits = nil 110 } 111 if service.TaskTemplate.Resources != nil && service.TaskTemplate.Resources.Limits != nil { 112 // Limits.Pids not supported before API version 1.41 113 service.TaskTemplate.Resources.Limits.Pids = 0 114 } 115 116 // jobs were only introduced in API version 1.41. Nil out both Job 117 // modes; if the service is one of these modes and subsequently has no 118 // mode, then something down the pipe will thrown an error. 119 service.Mode.ReplicatedJob = nil 120 service.Mode.GlobalJob = nil 121 } 122 123 if versions.LessThan(cliVersion, "1.44") { 124 // seccomp, apparmor, and no_new_privs were added in 1.44. 125 if service.TaskTemplate.ContainerSpec != nil && service.TaskTemplate.ContainerSpec.Privileges != nil { 126 service.TaskTemplate.ContainerSpec.Privileges.Seccomp = nil 127 service.TaskTemplate.ContainerSpec.Privileges.AppArmor = nil 128 service.TaskTemplate.ContainerSpec.Privileges.NoNewPrivileges = false 129 } 130 } 131 }