github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/container.go (about)

     1  package generate
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  
     7  	"github.com/containers/image/v5/manifest"
     8  	"github.com/containers/podman/v2/libpod"
     9  	"github.com/containers/podman/v2/libpod/image"
    10  	ann "github.com/containers/podman/v2/pkg/annotations"
    11  	envLib "github.com/containers/podman/v2/pkg/env"
    12  	"github.com/containers/podman/v2/pkg/signal"
    13  	"github.com/containers/podman/v2/pkg/specgen"
    14  	spec "github.com/opencontainers/runtime-spec/specs-go"
    15  	"github.com/pkg/errors"
    16  	"github.com/sirupsen/logrus"
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  // Fill any missing parts of the spec generator (e.g. from the image).
    21  // Returns a set of warnings or any fatal error that occurred.
    22  func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) ([]string, error) {
    23  	var (
    24  		newImage *image.Image
    25  		err      error
    26  	)
    27  
    28  	// Only add image configuration if we have an image
    29  	if s.Image != "" {
    30  		newImage, err = r.ImageRuntime().NewFromLocal(s.Image)
    31  		if err != nil {
    32  			return nil, err
    33  		}
    34  
    35  		_, mediaType, err := newImage.Manifest(ctx)
    36  		if err != nil {
    37  			if errors.Cause(err) != image.ErrImageIsBareList {
    38  				return nil, err
    39  			}
    40  			// if err is not runnable image
    41  			// use the local store image with repo@digest matches with the list, if exists
    42  			manifestByte, manifestType, err := newImage.GetManifest(ctx, nil)
    43  			if err != nil {
    44  				return nil, err
    45  			}
    46  			list, err := manifest.ListFromBlob(manifestByte, manifestType)
    47  			if err != nil {
    48  				return nil, err
    49  			}
    50  			images, err := r.ImageRuntime().GetImages()
    51  			if err != nil {
    52  				return nil, err
    53  			}
    54  			findLocal := false
    55  			listDigest, err := list.ChooseInstance(r.SystemContext())
    56  			if err != nil {
    57  				return nil, err
    58  			}
    59  			for _, img := range images {
    60  				for _, imageDigest := range img.Digests() {
    61  					if imageDigest == listDigest {
    62  						newImage = img
    63  						s.Image = img.ID()
    64  						mediaType = manifestType
    65  						findLocal = true
    66  						logrus.Debug("image contains manifest list, using image from local storage")
    67  						break
    68  					}
    69  				}
    70  			}
    71  			if !findLocal {
    72  				return nil, image.ErrImageIsBareList
    73  			}
    74  		}
    75  
    76  		if s.HealthConfig == nil && mediaType == manifest.DockerV2Schema2MediaType {
    77  			s.HealthConfig, err = newImage.GetHealthCheck(ctx)
    78  			if err != nil {
    79  				return nil, err
    80  			}
    81  		}
    82  
    83  		// Image stop signal
    84  		if s.StopSignal == nil {
    85  			stopSignal, err := newImage.StopSignal(ctx)
    86  			if err != nil {
    87  				return nil, err
    88  			}
    89  			if stopSignal != "" {
    90  				sig, err := signal.ParseSignalNameOrNumber(stopSignal)
    91  				if err != nil {
    92  					return nil, err
    93  				}
    94  				s.StopSignal = &sig
    95  			}
    96  		}
    97  	}
    98  
    99  	rtc, err := r.GetConfig()
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	// First transform the os env into a map. We need it for the labels later in
   104  	// any case.
   105  	osEnv, err := envLib.ParseSlice(os.Environ())
   106  	if err != nil {
   107  		return nil, errors.Wrap(err, "error parsing host environment variables")
   108  	}
   109  
   110  	// Get Default Environment from containers.conf
   111  	defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnv())
   112  	if err != nil {
   113  		return nil, errors.Wrap(err, "error parsing fields in containers.conf")
   114  	}
   115  	if defaultEnvs["container"] == "" {
   116  		defaultEnvs["container"] = "podman"
   117  	}
   118  	var envs map[string]string
   119  
   120  	// Image Environment defaults
   121  	if newImage != nil {
   122  		// Image envs from the image if they don't exist
   123  		// already, overriding the default environments
   124  		imageEnvs, err := newImage.Env(ctx)
   125  		if err != nil {
   126  			return nil, err
   127  		}
   128  
   129  		envs, err = envLib.ParseSlice(imageEnvs)
   130  		if err != nil {
   131  			return nil, errors.Wrap(err, "Env fields from image failed to parse")
   132  		}
   133  		defaultEnvs = envLib.Join(defaultEnvs, envs)
   134  	}
   135  
   136  	// Caller Specified defaults
   137  	if s.EnvHost {
   138  		defaultEnvs = envLib.Join(defaultEnvs, osEnv)
   139  	} else if s.HTTPProxy {
   140  		for _, envSpec := range []string{
   141  			"http_proxy",
   142  			"HTTP_PROXY",
   143  			"https_proxy",
   144  			"HTTPS_PROXY",
   145  			"ftp_proxy",
   146  			"FTP_PROXY",
   147  			"no_proxy",
   148  			"NO_PROXY",
   149  		} {
   150  			if v, ok := osEnv[envSpec]; ok {
   151  				defaultEnvs[envSpec] = v
   152  			}
   153  		}
   154  	}
   155  
   156  	s.Env = envLib.Join(defaultEnvs, s.Env)
   157  
   158  	// Labels and Annotations
   159  	annotations := make(map[string]string)
   160  	if newImage != nil {
   161  		labels, err := newImage.Labels(ctx)
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  
   166  		// labels from the image that dont exist already
   167  		if len(labels) > 0 && s.Labels == nil {
   168  			s.Labels = make(map[string]string)
   169  		}
   170  		for k, v := range labels {
   171  			if _, exists := s.Labels[k]; !exists {
   172  				s.Labels[k] = v
   173  			}
   174  		}
   175  
   176  		// Add annotations from the image
   177  		imgAnnotations, err := newImage.Annotations(ctx)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  		for k, v := range imgAnnotations {
   182  			annotations[k] = v
   183  		}
   184  	}
   185  
   186  	// in the event this container is in a pod, and the pod has an infra container
   187  	// we will want to configure it as a type "container" instead defaulting to
   188  	// the behavior of a "sandbox" container
   189  	// In Kata containers:
   190  	// - "sandbox" is the annotation that denotes the container should use its own
   191  	//   VM, which is the default behavior
   192  	// - "container" denotes the container should join the VM of the SandboxID
   193  	//   (the infra container)
   194  
   195  	if len(s.Pod) > 0 {
   196  		annotations[ann.SandboxID] = s.Pod
   197  		annotations[ann.ContainerType] = ann.ContainerTypeContainer
   198  	}
   199  
   200  	// now pass in the values from client
   201  	for k, v := range s.Annotations {
   202  		annotations[k] = v
   203  	}
   204  	s.Annotations = annotations
   205  
   206  	// workdir
   207  	if s.WorkDir == "" {
   208  		if newImage != nil {
   209  			workingDir, err := newImage.WorkingDir(ctx)
   210  			if err != nil {
   211  				return nil, err
   212  			}
   213  			s.WorkDir = workingDir
   214  		}
   215  	}
   216  	if s.WorkDir == "" {
   217  		s.WorkDir = "/"
   218  	}
   219  
   220  	if len(s.SeccompProfilePath) < 1 {
   221  		p, err := libpod.DefaultSeccompPath()
   222  		if err != nil {
   223  			return nil, err
   224  		}
   225  		s.SeccompProfilePath = p
   226  	}
   227  
   228  	if len(s.User) == 0 && newImage != nil {
   229  		s.User, err = newImage.User(ctx)
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  	}
   234  	if err := finishThrottleDevices(s); err != nil {
   235  		return nil, err
   236  	}
   237  	// Unless already set via the CLI, check if we need to disable process
   238  	// labels or set the defaults.
   239  	if len(s.SelinuxOpts) == 0 {
   240  		if err := setLabelOpts(s, r, s.PidNS, s.IpcNS); err != nil {
   241  			return nil, err
   242  		}
   243  	}
   244  
   245  	// If caller did not specify Pids Limits load default
   246  	if s.ResourceLimits == nil || s.ResourceLimits.Pids == nil {
   247  		if s.CgroupsMode != "disabled" {
   248  			limit := rtc.PidsLimit()
   249  			if limit != 0 {
   250  				if s.ResourceLimits == nil {
   251  					s.ResourceLimits = &spec.LinuxResources{}
   252  				}
   253  				s.ResourceLimits.Pids = &spec.LinuxPids{
   254  					Limit: limit,
   255  				}
   256  			}
   257  		}
   258  	}
   259  
   260  	warnings, err := verifyContainerResources(s)
   261  	if err != nil {
   262  		return warnings, err
   263  	}
   264  
   265  	// Warn on net=host/container/pod/none and port mappings.
   266  	if (s.NetNS.NSMode == specgen.Host || s.NetNS.NSMode == specgen.FromContainer ||
   267  		s.NetNS.NSMode == specgen.FromPod || s.NetNS.NSMode == specgen.NoNetwork) &&
   268  		len(s.PortMappings) > 0 {
   269  		warnings = append(warnings, "Port mappings have been discarded as one of the Host, Container, Pod, and None network modes are in use")
   270  	}
   271  
   272  	return warnings, nil
   273  }
   274  
   275  // finishThrottleDevices takes the temporary representation of the throttle
   276  // devices in the specgen and looks up the major and major minors. it then
   277  // sets the throttle devices proper in the specgen
   278  func finishThrottleDevices(s *specgen.SpecGenerator) error {
   279  	if bps := s.ThrottleReadBpsDevice; len(bps) > 0 {
   280  		for k, v := range bps {
   281  			statT := unix.Stat_t{}
   282  			if err := unix.Stat(k, &statT); err != nil {
   283  				return err
   284  			}
   285  			v.Major = (int64(unix.Major(statT.Rdev)))
   286  			v.Minor = (int64(unix.Minor(statT.Rdev)))
   287  			s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v)
   288  		}
   289  	}
   290  	if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 {
   291  		for k, v := range bps {
   292  			statT := unix.Stat_t{}
   293  			if err := unix.Stat(k, &statT); err != nil {
   294  				return err
   295  			}
   296  			v.Major = (int64(unix.Major(statT.Rdev)))
   297  			v.Minor = (int64(unix.Minor(statT.Rdev)))
   298  			s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v)
   299  		}
   300  	}
   301  	if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 {
   302  		for k, v := range iops {
   303  			statT := unix.Stat_t{}
   304  			if err := unix.Stat(k, &statT); err != nil {
   305  				return err
   306  			}
   307  			v.Major = (int64(unix.Major(statT.Rdev)))
   308  			v.Minor = (int64(unix.Minor(statT.Rdev)))
   309  			s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v)
   310  		}
   311  	}
   312  	if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 {
   313  		for k, v := range iops {
   314  			statT := unix.Stat_t{}
   315  			if err := unix.Stat(k, &statT); err != nil {
   316  				return err
   317  			}
   318  			v.Major = (int64(unix.Major(statT.Rdev)))
   319  			v.Minor = (int64(unix.Minor(statT.Rdev)))
   320  			s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v)
   321  		}
   322  	}
   323  	return nil
   324  }