github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/abi/play.go (about)

     1  package abi
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strconv"
    12  	"strings"
    13  
    14  	buildahDefine "github.com/containers/buildah/define"
    15  	"github.com/containers/common/libimage"
    16  	nettypes "github.com/containers/common/libnetwork/types"
    17  	"github.com/containers/common/pkg/config"
    18  	"github.com/containers/image/v5/types"
    19  	"github.com/hanks177/podman/v4/libpod"
    20  	"github.com/hanks177/podman/v4/libpod/define"
    21  	"github.com/hanks177/podman/v4/pkg/autoupdate"
    22  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    23  	v1apps "github.com/hanks177/podman/v4/pkg/k8s.io/api/apps/v1"
    24  	v1 "github.com/hanks177/podman/v4/pkg/k8s.io/api/core/v1"
    25  	"github.com/hanks177/podman/v4/pkg/specgen"
    26  	"github.com/hanks177/podman/v4/pkg/specgen/generate"
    27  	"github.com/hanks177/podman/v4/pkg/specgen/generate/kube"
    28  	"github.com/hanks177/podman/v4/pkg/specgenutil"
    29  	"github.com/hanks177/podman/v4/pkg/util"
    30  	"github.com/ghodss/yaml"
    31  	"github.com/opencontainers/go-digest"
    32  	"github.com/pkg/errors"
    33  	"github.com/sirupsen/logrus"
    34  	yamlv2 "gopkg.in/yaml.v2"
    35  )
    36  
    37  // createServiceContainer creates a container that can later on
    38  // be associated with the pods of a K8s yaml.  It will be started along with
    39  // the first pod.
    40  func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name string, options entities.PlayKubeOptions) (*libpod.Container, error) {
    41  	// Make sure to replace the service container as well if requested by
    42  	// the user.
    43  	if options.Replace {
    44  		if _, err := ic.ContainerRm(ctx, []string{name}, entities.RmOptions{Force: true, Ignore: true}); err != nil {
    45  			return nil, fmt.Errorf("replacing service container: %w", err)
    46  		}
    47  	}
    48  
    49  	// Similar to infra containers, a service container is using the pause image.
    50  	image, err := generate.PullOrBuildInfraImage(ic.Libpod, "")
    51  	if err != nil {
    52  		return nil, fmt.Errorf("image for service container: %w", err)
    53  	}
    54  
    55  	ctrOpts := entities.ContainerCreateOptions{
    56  		// Inherited from infra containers
    57  		ImageVolume:      "bind",
    58  		IsInfra:          false,
    59  		MemorySwappiness: -1,
    60  		// No need to spin up slirp etc.
    61  		Net: &entities.NetOptions{Network: specgen.Namespace{NSMode: specgen.NoNetwork}},
    62  	}
    63  
    64  	// Create and fill out the runtime spec.
    65  	s := specgen.NewSpecGenerator(image, false)
    66  	if err := specgenutil.FillOutSpecGen(s, &ctrOpts, []string{}); err != nil {
    67  		return nil, fmt.Errorf("completing spec for service container: %w", err)
    68  	}
    69  	s.Name = name
    70  
    71  	runtimeSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, s, false, nil)
    72  	if err != nil {
    73  		return nil, fmt.Errorf("creating runtime spec for service container: %w", err)
    74  	}
    75  	opts = append(opts, libpod.WithIsService())
    76  	opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeConmon))
    77  
    78  	// Create a new libpod container based on the spec.
    79  	ctr, err := ic.Libpod.NewContainer(ctx, runtimeSpec, spec, false, opts...)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("creating service container: %w", err)
    82  	}
    83  
    84  	return ctr, nil
    85  }
    86  
    87  // Creates the name for a service container based on the provided content of a
    88  // K8s yaml file.
    89  func serviceContainerName(content []byte) string {
    90  	// The name of the service container is the first 12
    91  	// characters of the yaml file's hash followed by the
    92  	// '-service' suffix to guarantee a predictable and
    93  	// discoverable name.
    94  	hash := digest.FromBytes(content).Encoded()
    95  	return hash[0:12] + "-service"
    96  }
    97  
    98  func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options entities.PlayKubeOptions) (_ *entities.PlayKubeReport, finalErr error) {
    99  	report := &entities.PlayKubeReport{}
   100  	validKinds := 0
   101  
   102  	// read yaml document
   103  	content, err := ioutil.ReadAll(body)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	// split yaml document
   109  	documentList, err := splitMultiDocYAML(content)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	// sort kube kinds
   115  	documentList, err = sortKubeKinds(documentList)
   116  	if err != nil {
   117  		return nil, errors.Wrap(err, "unable to sort kube kinds")
   118  	}
   119  
   120  	ipIndex := 0
   121  
   122  	var configMaps []v1.ConfigMap
   123  
   124  	// create pod on each document if it is a pod or deployment
   125  	// any other kube kind will be skipped
   126  	for _, document := range documentList {
   127  		kind, err := getKubeKind(document)
   128  		if err != nil {
   129  			return nil, errors.Wrap(err, "unable to read kube YAML")
   130  		}
   131  
   132  		// TODO: create constants for the various "kinds" of yaml files.
   133  		var serviceContainer *libpod.Container
   134  		if options.ServiceContainer && (kind == "Pod" || kind == "Deployment") {
   135  			ctr, err := ic.createServiceContainer(ctx, serviceContainerName(content), options)
   136  			if err != nil {
   137  				return nil, err
   138  			}
   139  			serviceContainer = ctr
   140  			// Make sure to remove the container in case something goes wrong below.
   141  			defer func() {
   142  				if finalErr == nil {
   143  					return
   144  				}
   145  				if err := ic.Libpod.RemoveContainer(ctx, ctr, true, false, nil); err != nil {
   146  					logrus.Errorf("Cleaning up service container after failure: %v", err)
   147  				}
   148  			}()
   149  		}
   150  
   151  		switch kind {
   152  		case "Pod":
   153  			var podYAML v1.Pod
   154  			var podTemplateSpec v1.PodTemplateSpec
   155  
   156  			if err := yaml.Unmarshal(document, &podYAML); err != nil {
   157  				return nil, errors.Wrap(err, "unable to read YAML as Kube Pod")
   158  			}
   159  
   160  			podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
   161  			podTemplateSpec.Spec = podYAML.Spec
   162  			for name, val := range podYAML.Annotations {
   163  				if len(val) > define.MaxKubeAnnotation {
   164  					return nil, errors.Errorf("invalid annotation %q=%q value length exceeds Kubernetetes max %d", name, val, define.MaxKubeAnnotation)
   165  				}
   166  			}
   167  			for name, val := range options.Annotations {
   168  				if podYAML.Annotations == nil {
   169  					podYAML.Annotations = make(map[string]string)
   170  				}
   171  				podYAML.Annotations[name] = val
   172  			}
   173  
   174  			r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex, podYAML.Annotations, configMaps, serviceContainer)
   175  			if err != nil {
   176  				return nil, err
   177  			}
   178  
   179  			report.Pods = append(report.Pods, r.Pods...)
   180  			validKinds++
   181  		case "Deployment":
   182  			var deploymentYAML v1apps.Deployment
   183  
   184  			if err := yaml.Unmarshal(document, &deploymentYAML); err != nil {
   185  				return nil, errors.Wrap(err, "unable to read YAML as Kube Deployment")
   186  			}
   187  
   188  			r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps, serviceContainer)
   189  			if err != nil {
   190  				return nil, err
   191  			}
   192  
   193  			report.Pods = append(report.Pods, r.Pods...)
   194  			validKinds++
   195  		case "PersistentVolumeClaim":
   196  			var pvcYAML v1.PersistentVolumeClaim
   197  
   198  			if err := yaml.Unmarshal(document, &pvcYAML); err != nil {
   199  				return nil, errors.Wrap(err, "unable to read YAML as Kube PersistentVolumeClaim")
   200  			}
   201  
   202  			r, err := ic.playKubePVC(ctx, &pvcYAML)
   203  			if err != nil {
   204  				return nil, err
   205  			}
   206  
   207  			report.Volumes = append(report.Volumes, r.Volumes...)
   208  			validKinds++
   209  		case "ConfigMap":
   210  			var configMap v1.ConfigMap
   211  
   212  			if err := yaml.Unmarshal(document, &configMap); err != nil {
   213  				return nil, errors.Wrap(err, "unable to read YAML as Kube ConfigMap")
   214  			}
   215  			configMaps = append(configMaps, configMap)
   216  		default:
   217  			logrus.Infof("Kube kind %s not supported", kind)
   218  			continue
   219  		}
   220  	}
   221  
   222  	if validKinds == 0 {
   223  		if len(configMaps) > 0 {
   224  			return nil, fmt.Errorf("ConfigMaps in podman are not a standalone object and must be used in a container")
   225  		}
   226  		return nil, fmt.Errorf("YAML document does not contain any supported kube kind")
   227  	}
   228  
   229  	return report, nil
   230  }
   231  
   232  func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, error) {
   233  	var (
   234  		deploymentName string
   235  		podSpec        v1.PodTemplateSpec
   236  		numReplicas    int32
   237  		i              int32
   238  		report         entities.PlayKubeReport
   239  	)
   240  
   241  	deploymentName = deploymentYAML.ObjectMeta.Name
   242  	if deploymentName == "" {
   243  		return nil, errors.Errorf("Deployment does not have a name")
   244  	}
   245  	numReplicas = 1
   246  	if deploymentYAML.Spec.Replicas != nil {
   247  		numReplicas = *deploymentYAML.Spec.Replicas
   248  	}
   249  	podSpec = deploymentYAML.Spec.Template
   250  
   251  	// create "replicas" number of pods
   252  	for i = 0; i < numReplicas; i++ {
   253  		podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
   254  		podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps, serviceContainer)
   255  		if err != nil {
   256  			return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName)
   257  		}
   258  		report.Pods = append(report.Pods, podReport.Pods...)
   259  	}
   260  	return &report, nil
   261  }
   262  
   263  func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int, annotations map[string]string, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, error) {
   264  	var (
   265  		writer      io.Writer
   266  		playKubePod entities.PlayKubePod
   267  		report      entities.PlayKubeReport
   268  	)
   269  
   270  	// Create the secret manager before hand
   271  	secretsManager, err := ic.Libpod.SecretsManager()
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  
   276  	// Assert the pod has a name
   277  	if podName == "" {
   278  		return nil, errors.Errorf("pod does not have a name")
   279  	}
   280  
   281  	podOpt := entities.PodCreateOptions{
   282  		Infra:      true,
   283  		Net:        &entities.NetOptions{NoHosts: options.NoHosts},
   284  		ExitPolicy: string(config.PodExitPolicyStop),
   285  	}
   286  	podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, podYAML)
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  
   291  	if len(options.Networks) > 0 {
   292  		ns, networks, netOpts, err := specgen.ParseNetworkFlag(options.Networks)
   293  		if err != nil {
   294  			return nil, err
   295  		}
   296  
   297  		if (ns.IsBridge() && len(networks) == 0) || ns.IsHost() {
   298  			return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
   299  		}
   300  
   301  		podOpt.Net.Network = ns
   302  		podOpt.Net.Networks = networks
   303  		podOpt.Net.NetworkOptions = netOpts
   304  	}
   305  
   306  	if options.Userns == "" {
   307  		options.Userns = "host"
   308  	}
   309  
   310  	// Validate the userns modes supported.
   311  	podOpt.Userns, err = specgen.ParseUserNamespace(options.Userns)
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  
   316  	// FIXME This is very hard to support properly with a good ux
   317  	if len(options.StaticIPs) > *ipIndex {
   318  		if !podOpt.Net.Network.IsBridge() {
   319  			return nil, errors.Wrap(define.ErrInvalidArg, "static ip addresses can only be set when the network mode is bridge")
   320  		}
   321  		if len(podOpt.Net.Networks) != 1 {
   322  			return nil, errors.Wrap(define.ErrInvalidArg, "cannot set static ip addresses for more than network, use netname:ip=<ip> syntax to specify ips for more than network")
   323  		}
   324  		for name, netOpts := range podOpt.Net.Networks {
   325  			netOpts.StaticIPs = append(netOpts.StaticIPs, options.StaticIPs[*ipIndex])
   326  			podOpt.Net.Networks[name] = netOpts
   327  		}
   328  	} else if len(options.StaticIPs) > 0 {
   329  		// only warn if the user has set at least one ip
   330  		logrus.Warn("No more static ips left using a random one")
   331  	}
   332  	if len(options.StaticMACs) > *ipIndex {
   333  		if !podOpt.Net.Network.IsBridge() {
   334  			return nil, errors.Wrap(define.ErrInvalidArg, "static mac address can only be set when the network mode is bridge")
   335  		}
   336  		if len(podOpt.Net.Networks) != 1 {
   337  			return nil, errors.Wrap(define.ErrInvalidArg, "cannot set static mac address for more than network, use netname:mac=<mac> syntax to specify mac for more than network")
   338  		}
   339  		for name, netOpts := range podOpt.Net.Networks {
   340  			netOpts.StaticMAC = nettypes.HardwareAddr(options.StaticMACs[*ipIndex])
   341  			podOpt.Net.Networks[name] = netOpts
   342  		}
   343  	} else if len(options.StaticIPs) > 0 {
   344  		// only warn if the user has set at least one mac
   345  		logrus.Warn("No more static macs left using a random one")
   346  	}
   347  	*ipIndex++
   348  
   349  	p := specgen.NewPodSpecGenerator()
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  
   354  	p, err = entities.ToPodSpecGen(*p, &podOpt)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  	podSpec := entities.PodSpec{PodSpecGen: *p}
   359  
   360  	configMapIndex := make(map[string]struct{})
   361  	for _, configMap := range configMaps {
   362  		configMapIndex[configMap.Name] = struct{}{}
   363  	}
   364  	for _, p := range options.ConfigMaps {
   365  		f, err := os.Open(p)
   366  		if err != nil {
   367  			return nil, err
   368  		}
   369  		defer f.Close()
   370  
   371  		cm, err := readConfigMapFromFile(f)
   372  		if err != nil {
   373  			return nil, errors.Wrapf(err, "%q", p)
   374  		}
   375  
   376  		if _, present := configMapIndex[cm.Name]; present {
   377  			return nil, errors.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p)
   378  		}
   379  
   380  		configMaps = append(configMaps, cm)
   381  	}
   382  
   383  	volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes, configMaps)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  
   388  	// Go through the volumes and create a podman volume for all volumes that have been
   389  	// defined by a configmap
   390  	for _, v := range volumes {
   391  		if v.Type == kube.KubeVolumeTypeConfigMap && !v.Optional {
   392  			vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source))
   393  			if err != nil {
   394  				if errors.Is(err, define.ErrVolumeExists) {
   395  					// Volume for this configmap already exists do not
   396  					// error out instead reuse the current volume.
   397  					vol, err = ic.Libpod.GetVolume(v.Source)
   398  					if err != nil {
   399  						return nil, errors.Wrapf(err, "cannot re-use local volume for volume from configmap %q", v.Source)
   400  					}
   401  				} else {
   402  					return nil, errors.Wrapf(err, "cannot create a local volume for volume from configmap %q", v.Source)
   403  				}
   404  			}
   405  			mountPoint, err := vol.MountPoint()
   406  			if err != nil || mountPoint == "" {
   407  				return nil, errors.Wrapf(err, "unable to get mountpoint of volume %q", vol.Name())
   408  			}
   409  			// Create files and add data to the volume mountpoint based on the Items in the volume
   410  			for k, v := range v.Items {
   411  				dataPath := filepath.Join(mountPoint, k)
   412  				f, err := os.Create(dataPath)
   413  				if err != nil {
   414  					return nil, errors.Wrapf(err, "cannot create file %q at volume mountpoint %q", k, mountPoint)
   415  				}
   416  				defer f.Close()
   417  				_, err = f.WriteString(v)
   418  				if err != nil {
   419  					return nil, err
   420  				}
   421  			}
   422  		}
   423  	}
   424  
   425  	seccompPaths, err := kube.InitializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot)
   426  	if err != nil {
   427  		return nil, err
   428  	}
   429  
   430  	var ctrRestartPolicy string
   431  	switch podYAML.Spec.RestartPolicy {
   432  	case v1.RestartPolicyAlways:
   433  		ctrRestartPolicy = define.RestartPolicyAlways
   434  	case v1.RestartPolicyOnFailure:
   435  		ctrRestartPolicy = define.RestartPolicyOnFailure
   436  	case v1.RestartPolicyNever:
   437  		ctrRestartPolicy = define.RestartPolicyNo
   438  	default: // Default to Always
   439  		ctrRestartPolicy = define.RestartPolicyAlways
   440  	}
   441  
   442  	if podOpt.Infra {
   443  		infraImage := util.DefaultContainerConfig().Engine.InfraImage
   444  		infraOptions := entities.NewInfraContainerCreateOptions()
   445  		infraOptions.Hostname = podSpec.PodSpecGen.PodBasicConfig.Hostname
   446  		infraOptions.UserNS = options.Userns
   447  		podSpec.PodSpecGen.InfraImage = infraImage
   448  		podSpec.PodSpecGen.NoInfra = false
   449  		podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(infraImage, false)
   450  		podSpec.PodSpecGen.InfraContainerSpec.NetworkOptions = p.NetworkOptions
   451  		podSpec.PodSpecGen.InfraContainerSpec.SdNotifyMode = define.SdNotifyModeIgnore
   452  
   453  		err = specgenutil.FillOutSpecGen(podSpec.PodSpecGen.InfraContainerSpec, &infraOptions, []string{})
   454  		if err != nil {
   455  			return nil, err
   456  		}
   457  	}
   458  
   459  	if serviceContainer != nil {
   460  		podSpec.PodSpecGen.ServiceContainerID = serviceContainer.ID()
   461  	}
   462  
   463  	// Create the Pod
   464  	pod, err := generate.MakePod(&podSpec, ic.Libpod)
   465  	if err != nil {
   466  		return nil, err
   467  	}
   468  
   469  	podInfraID, err := pod.InfraContainerID()
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  
   474  	if !options.Quiet {
   475  		writer = os.Stderr
   476  	}
   477  
   478  	containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers))
   479  	initContainers := make([]*libpod.Container, 0, len(podYAML.Spec.InitContainers))
   480  
   481  	var cwd string
   482  	if options.ContextDir != "" {
   483  		cwd = options.ContextDir
   484  	} else {
   485  		cwd, err = os.Getwd()
   486  		if err != nil {
   487  			return nil, err
   488  		}
   489  	}
   490  
   491  	ctrNames := make(map[string]string)
   492  	for _, initCtr := range podYAML.Spec.InitContainers {
   493  		// Error out if same name is used for more than one container
   494  		if _, ok := ctrNames[initCtr.Name]; ok {
   495  			return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, initCtr.Name)
   496  		}
   497  		ctrNames[initCtr.Name] = ""
   498  		// Init containers cannot have either of lifecycle, livenessProbe, readinessProbe, or startupProbe set
   499  		if initCtr.Lifecycle != nil || initCtr.LivenessProbe != nil || initCtr.ReadinessProbe != nil || initCtr.StartupProbe != nil {
   500  			return nil, errors.Errorf("cannot create an init container that has either of lifecycle, livenessProbe, readinessProbe, or startupProbe set")
   501  		}
   502  		pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, initCtr, options)
   503  		if err != nil {
   504  			return nil, err
   505  		}
   506  
   507  		for k, v := range podSpec.PodSpecGen.Labels { // add podYAML labels
   508  			labels[k] = v
   509  		}
   510  
   511  		specgenOpts := kube.CtrSpecGenOptions{
   512  			Annotations:        annotations,
   513  			ConfigMaps:         configMaps,
   514  			Container:          initCtr,
   515  			Image:              pulledImage,
   516  			InitContainerType:  define.AlwaysInitContainer,
   517  			Labels:             labels,
   518  			LogDriver:          options.LogDriver,
   519  			LogOptions:         options.LogOptions,
   520  			NetNSIsHost:        p.NetNS.IsHost(),
   521  			PodID:              pod.ID(),
   522  			PodInfraID:         podInfraID,
   523  			PodName:            podName,
   524  			PodSecurityContext: podYAML.Spec.SecurityContext,
   525  			RestartPolicy:      ctrRestartPolicy,
   526  			SeccompPaths:       seccompPaths,
   527  			SecretsManager:     secretsManager,
   528  			UserNSIsHost:       p.Userns.IsHost(),
   529  			Volumes:            volumes,
   530  		}
   531  		specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
   532  		if err != nil {
   533  			return nil, err
   534  		}
   535  		specGen.SdNotifyMode = define.SdNotifyModeIgnore
   536  		rtSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, specGen, false, nil)
   537  		if err != nil {
   538  			return nil, err
   539  		}
   540  		opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeIgnore))
   541  		ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...)
   542  		if err != nil {
   543  			return nil, err
   544  		}
   545  
   546  		initContainers = append(initContainers, ctr)
   547  	}
   548  	for _, container := range podYAML.Spec.Containers {
   549  		// Error out if the same name is used for more than one container
   550  		if _, ok := ctrNames[container.Name]; ok {
   551  			return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, container.Name)
   552  		}
   553  		ctrNames[container.Name] = ""
   554  		pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, container, options)
   555  		if err != nil {
   556  			return nil, err
   557  		}
   558  
   559  		for k, v := range podSpec.PodSpecGen.Labels { // add podYAML labels
   560  			labels[k] = v
   561  		}
   562  
   563  		specgenOpts := kube.CtrSpecGenOptions{
   564  			Annotations:        annotations,
   565  			ConfigMaps:         configMaps,
   566  			Container:          container,
   567  			Image:              pulledImage,
   568  			Labels:             labels,
   569  			LogDriver:          options.LogDriver,
   570  			LogOptions:         options.LogOptions,
   571  			NetNSIsHost:        p.NetNS.IsHost(),
   572  			PodID:              pod.ID(),
   573  			PodInfraID:         podInfraID,
   574  			PodName:            podName,
   575  			PodSecurityContext: podYAML.Spec.SecurityContext,
   576  			RestartPolicy:      ctrRestartPolicy,
   577  			SeccompPaths:       seccompPaths,
   578  			SecretsManager:     secretsManager,
   579  			UserNSIsHost:       p.Userns.IsHost(),
   580  			Volumes:            volumes,
   581  		}
   582  		specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
   583  		if err != nil {
   584  			return nil, err
   585  		}
   586  		specGen.RawImageName = container.Image
   587  		rtSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, specGen, false, nil)
   588  		if err != nil {
   589  			return nil, err
   590  		}
   591  		opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeIgnore))
   592  		ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...)
   593  		if err != nil {
   594  			return nil, err
   595  		}
   596  		containers = append(containers, ctr)
   597  	}
   598  
   599  	if options.Start != types.OptionalBoolFalse {
   600  		// Start the containers
   601  		podStartErrors, err := pod.Start(ctx)
   602  		if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
   603  			return nil, err
   604  		}
   605  		for id, err := range podStartErrors {
   606  			playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, errors.Wrapf(err, "error starting container %s", id).Error())
   607  			fmt.Println(playKubePod.ContainerErrors)
   608  		}
   609  	}
   610  
   611  	playKubePod.ID = pod.ID()
   612  	for _, ctr := range containers {
   613  		playKubePod.Containers = append(playKubePod.Containers, ctr.ID())
   614  	}
   615  	for _, initCtr := range initContainers {
   616  		playKubePod.InitContainers = append(playKubePod.InitContainers, initCtr.ID())
   617  	}
   618  
   619  	report.Pods = append(report.Pods, playKubePod)
   620  
   621  	return &report, nil
   622  }
   623  
   624  // getImageAndLabelInfo returns the image information and how the image should be pulled plus as well as labels to be used for the container in the pod.
   625  // Moved this to a separate function so that it can be used for both init and regular containers when playing a kube yaml.
   626  func (ic *ContainerEngine) getImageAndLabelInfo(ctx context.Context, cwd string, annotations map[string]string, writer io.Writer, container v1.Container, options entities.PlayKubeOptions) (*libimage.Image, map[string]string, error) {
   627  	// Contains all labels obtained from kube
   628  	labels := make(map[string]string)
   629  	var pulledImage *libimage.Image
   630  	buildFile, err := getBuildFile(container.Image, cwd)
   631  	if err != nil {
   632  		return nil, nil, err
   633  	}
   634  	existsLocally, err := ic.Libpod.LibimageRuntime().Exists(container.Image)
   635  	if err != nil {
   636  		return nil, nil, err
   637  	}
   638  	if (len(buildFile) > 0) && ((!existsLocally && options.Build != types.OptionalBoolFalse) || (options.Build == types.OptionalBoolTrue)) {
   639  		buildOpts := new(buildahDefine.BuildOptions)
   640  		commonOpts := new(buildahDefine.CommonBuildOptions)
   641  		buildOpts.ConfigureNetwork = buildahDefine.NetworkDefault
   642  		buildOpts.Isolation = buildahDefine.IsolationChroot
   643  		buildOpts.CommonBuildOpts = commonOpts
   644  		buildOpts.Output = container.Image
   645  		buildOpts.ContextDirectory = filepath.Dir(buildFile)
   646  		if _, _, err := ic.Libpod.Build(ctx, *buildOpts, []string{buildFile}...); err != nil {
   647  			return nil, nil, err
   648  		}
   649  		i, _, err := ic.Libpod.LibimageRuntime().LookupImage(container.Image, new(libimage.LookupImageOptions))
   650  		if err != nil {
   651  			return nil, nil, err
   652  		}
   653  		pulledImage = i
   654  	} else {
   655  		// NOTE: set the pull policy to "newer".  This will cover cases
   656  		// where the "latest" tag requires a pull and will also
   657  		// transparently handle "localhost/" prefixed files which *may*
   658  		// refer to a locally built image OR an image running a
   659  		// registry on localhost.
   660  		pullPolicy := config.PullPolicyNewer
   661  		if len(container.ImagePullPolicy) > 0 {
   662  			// Make sure to lower the strings since K8s pull policy
   663  			// may be capitalized (see bugzilla.redhat.com/show_bug.cgi?id=1985905).
   664  			rawPolicy := string(container.ImagePullPolicy)
   665  			pullPolicy, err = config.ParsePullPolicy(strings.ToLower(rawPolicy))
   666  			if err != nil {
   667  				return nil, nil, err
   668  			}
   669  		}
   670  		// This ensures the image is the image store
   671  		pullOptions := &libimage.PullOptions{}
   672  		pullOptions.AuthFilePath = options.Authfile
   673  		pullOptions.CertDirPath = options.CertDir
   674  		pullOptions.SignaturePolicyPath = options.SignaturePolicy
   675  		pullOptions.Writer = writer
   676  		pullOptions.Username = options.Username
   677  		pullOptions.Password = options.Password
   678  		pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
   679  
   680  		pulledImages, err := ic.Libpod.LibimageRuntime().Pull(ctx, container.Image, pullPolicy, pullOptions)
   681  		if err != nil {
   682  			return nil, nil, err
   683  		}
   684  		pulledImage = pulledImages[0]
   685  	}
   686  
   687  	// Handle kube annotations
   688  	for k, v := range annotations {
   689  		switch k {
   690  		// Auto update annotation without container name will apply to
   691  		// all containers within the pod
   692  		case autoupdate.Label, autoupdate.AuthfileLabel:
   693  			labels[k] = v
   694  		// Auto update annotation with container name will apply only
   695  		// to the specified container
   696  		case fmt.Sprintf("%s/%s", autoupdate.Label, container.Name),
   697  			fmt.Sprintf("%s/%s", autoupdate.AuthfileLabel, container.Name):
   698  			prefixAndCtr := strings.Split(k, "/")
   699  			labels[prefixAndCtr[0]] = v
   700  		}
   701  	}
   702  
   703  	return pulledImage, labels, nil
   704  }
   705  
   706  // playKubePVC creates a podman volume from a kube persistent volume claim.
   707  func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.PersistentVolumeClaim) (*entities.PlayKubeReport, error) {
   708  	var report entities.PlayKubeReport
   709  	opts := make(map[string]string)
   710  
   711  	// Get pvc name.
   712  	// This is the only required pvc attribute to create a podman volume.
   713  	name := pvcYAML.Name
   714  	if strings.TrimSpace(name) == "" {
   715  		return nil, fmt.Errorf("persistent volume claim name can not be empty")
   716  	}
   717  
   718  	// Create podman volume options.
   719  	volOptions := []libpod.VolumeCreateOption{
   720  		libpod.WithVolumeName(name),
   721  		libpod.WithVolumeLabels(pvcYAML.Labels),
   722  	}
   723  
   724  	// Get pvc annotations and create remaining podman volume options if available.
   725  	// These are podman volume options that do not match any of the persistent volume claim
   726  	// attributes, so they can be configured using annotations since they will not affect k8s.
   727  	for k, v := range pvcYAML.Annotations {
   728  		switch k {
   729  		case util.VolumeDriverAnnotation:
   730  			volOptions = append(volOptions, libpod.WithVolumeDriver(v))
   731  		case util.VolumeDeviceAnnotation:
   732  			opts["device"] = v
   733  		case util.VolumeTypeAnnotation:
   734  			opts["type"] = v
   735  		case util.VolumeUIDAnnotation:
   736  			uid, err := strconv.Atoi(v)
   737  			if err != nil {
   738  				return nil, errors.Wrapf(err, "cannot convert uid %s to integer", v)
   739  			}
   740  			volOptions = append(volOptions, libpod.WithVolumeUID(uid))
   741  			opts["UID"] = v
   742  		case util.VolumeGIDAnnotation:
   743  			gid, err := strconv.Atoi(v)
   744  			if err != nil {
   745  				return nil, errors.Wrapf(err, "cannot convert gid %s to integer", v)
   746  			}
   747  			volOptions = append(volOptions, libpod.WithVolumeGID(gid))
   748  			opts["GID"] = v
   749  		case util.VolumeMountOptsAnnotation:
   750  			opts["o"] = v
   751  		}
   752  	}
   753  	volOptions = append(volOptions, libpod.WithVolumeOptions(opts))
   754  
   755  	// Create volume.
   756  	vol, err := ic.Libpod.NewVolume(ctx, volOptions...)
   757  	if err != nil {
   758  		return nil, err
   759  	}
   760  
   761  	report.Volumes = append(report.Volumes, entities.PlayKubeVolume{
   762  		Name: vol.Name(),
   763  	})
   764  
   765  	return &report, nil
   766  }
   767  
   768  // readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag
   769  func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) {
   770  	var cm v1.ConfigMap
   771  
   772  	content, err := ioutil.ReadAll(r)
   773  	if err != nil {
   774  		return cm, errors.Wrapf(err, "unable to read ConfigMap YAML content")
   775  	}
   776  
   777  	if err := yaml.Unmarshal(content, &cm); err != nil {
   778  		return cm, errors.Wrapf(err, "unable to read YAML as Kube ConfigMap")
   779  	}
   780  
   781  	if cm.Kind != "ConfigMap" {
   782  		return cm, errors.Errorf("invalid YAML kind: %q. [ConfigMap] is the only supported by --configmap", cm.Kind)
   783  	}
   784  
   785  	return cm, nil
   786  }
   787  
   788  // splitMultiDocYAML reads multiple documents in a YAML file and
   789  // returns them as a list.
   790  func splitMultiDocYAML(yamlContent []byte) ([][]byte, error) {
   791  	var documentList [][]byte
   792  
   793  	d := yamlv2.NewDecoder(bytes.NewReader(yamlContent))
   794  	for {
   795  		var o interface{}
   796  		// read individual document
   797  		err := d.Decode(&o)
   798  		if err == io.EOF {
   799  			break
   800  		}
   801  		if err != nil {
   802  			return nil, errors.Wrapf(err, "multi doc yaml could not be split")
   803  		}
   804  
   805  		if o != nil {
   806  			// back to bytes
   807  			document, err := yamlv2.Marshal(o)
   808  			if err != nil {
   809  				return nil, errors.Wrapf(err, "individual doc yaml could not be marshalled")
   810  			}
   811  
   812  			documentList = append(documentList, document)
   813  		}
   814  	}
   815  
   816  	return documentList, nil
   817  }
   818  
   819  // getKubeKind unmarshals a kube YAML document and returns its kind.
   820  func getKubeKind(obj []byte) (string, error) {
   821  	var kubeObject v1.ObjectReference
   822  
   823  	if err := yaml.Unmarshal(obj, &kubeObject); err != nil {
   824  		return "", err
   825  	}
   826  
   827  	return kubeObject.Kind, nil
   828  }
   829  
   830  // sortKubeKinds adds the correct creation order for the kube kinds.
   831  // Any pod dependency will be created first like volumes, secrets, etc.
   832  func sortKubeKinds(documentList [][]byte) ([][]byte, error) {
   833  	var sortedDocumentList [][]byte
   834  
   835  	for _, document := range documentList {
   836  		kind, err := getKubeKind(document)
   837  		if err != nil {
   838  			return nil, err
   839  		}
   840  
   841  		switch kind {
   842  		case "Pod", "Deployment":
   843  			sortedDocumentList = append(sortedDocumentList, document)
   844  		default:
   845  			sortedDocumentList = append([][]byte{document}, sortedDocumentList...)
   846  		}
   847  	}
   848  
   849  	return sortedDocumentList, nil
   850  }
   851  func imageNamePrefix(imageName string) string {
   852  	prefix := imageName
   853  	s := strings.Split(prefix, ":")
   854  	if len(s) > 0 {
   855  		prefix = s[0]
   856  	}
   857  	s = strings.Split(prefix, "/")
   858  	if len(s) > 0 {
   859  		prefix = s[len(s)-1]
   860  	}
   861  	s = strings.Split(prefix, "@")
   862  	if len(s) > 0 {
   863  		prefix = s[0]
   864  	}
   865  	return prefix
   866  }
   867  
   868  func getBuildFile(imageName string, cwd string) (string, error) {
   869  	buildDirName := imageNamePrefix(imageName)
   870  	containerfilePath := filepath.Join(cwd, buildDirName, "Containerfile")
   871  	dockerfilePath := filepath.Join(cwd, buildDirName, "Dockerfile")
   872  
   873  	_, err := os.Stat(containerfilePath)
   874  	if err == nil {
   875  		logrus.Debugf("Building %s with %s", imageName, containerfilePath)
   876  		return containerfilePath, nil
   877  	}
   878  	// If the error is not because the file does not exist, take
   879  	// a mulligan and try Dockerfile.  If that also fails, return that
   880  	// error
   881  	if err != nil && !os.IsNotExist(err) {
   882  		logrus.Error(err.Error())
   883  	}
   884  
   885  	_, err = os.Stat(dockerfilePath)
   886  	if err == nil {
   887  		logrus.Debugf("Building %s with %s", imageName, dockerfilePath)
   888  		return dockerfilePath, nil
   889  	}
   890  	// Strike two
   891  	if os.IsNotExist(err) {
   892  		return "", nil
   893  	}
   894  	return "", err
   895  }
   896  
   897  func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) {
   898  	var (
   899  		podNames []string
   900  	)
   901  	reports := new(entities.PlayKubeReport)
   902  
   903  	// read yaml document
   904  	content, err := ioutil.ReadAll(body)
   905  	if err != nil {
   906  		return nil, err
   907  	}
   908  
   909  	// split yaml document
   910  	documentList, err := splitMultiDocYAML(content)
   911  	if err != nil {
   912  		return nil, err
   913  	}
   914  
   915  	// sort kube kinds
   916  	documentList, err = sortKubeKinds(documentList)
   917  	if err != nil {
   918  		return nil, errors.Wrap(err, "unable to sort kube kinds")
   919  	}
   920  
   921  	for _, document := range documentList {
   922  		kind, err := getKubeKind(document)
   923  		if err != nil {
   924  			return nil, errors.Wrap(err, "unable to read as kube YAML")
   925  		}
   926  
   927  		switch kind {
   928  		case "Pod":
   929  			var podYAML v1.Pod
   930  			if err := yaml.Unmarshal(document, &podYAML); err != nil {
   931  				return nil, errors.Wrap(err, "unable to read YAML as Kube Pod")
   932  			}
   933  			podNames = append(podNames, podYAML.ObjectMeta.Name)
   934  		case "Deployment":
   935  			var deploymentYAML v1apps.Deployment
   936  
   937  			if err := yaml.Unmarshal(document, &deploymentYAML); err != nil {
   938  				return nil, errors.Wrap(err, "unable to read YAML as Kube Deployment")
   939  			}
   940  			var numReplicas int32 = 1
   941  			deploymentName := deploymentYAML.ObjectMeta.Name
   942  			if deploymentYAML.Spec.Replicas != nil {
   943  				numReplicas = *deploymentYAML.Spec.Replicas
   944  			}
   945  			for i := 0; i < int(numReplicas); i++ {
   946  				podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
   947  				podNames = append(podNames, podName)
   948  			}
   949  		default:
   950  			continue
   951  		}
   952  	}
   953  
   954  	// Add the reports
   955  	reports.StopReport, err = ic.PodStop(ctx, podNames, entities.PodStopOptions{})
   956  	if err != nil {
   957  		return nil, err
   958  	}
   959  
   960  	reports.RmReport, err = ic.PodRm(ctx, podNames, entities.PodRmOptions{})
   961  	if err != nil {
   962  		return nil, err
   963  	}
   964  
   965  	return reports, nil
   966  }