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  }