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  }