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 }