github.com/rish1988/moby@v25.0.2+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  }