diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 03dda23a7d..a11653dee3 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -246,6 +246,22 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options } notifyProxies = append(notifyProxies, proxies...) + report.Pods = append(report.Pods, r.Pods...) + validKinds++ + ranContainers = true + case "DaemonSet": + var daemonSetYAML v1apps.DaemonSet + + if err := yaml.Unmarshal(document, &daemonSetYAML); err != nil { + return nil, fmt.Errorf("unable to read YAML as Kube DaemonSet: %w", err) + } + + r, proxies, err := ic.playKubeDaemonSet(ctx, &daemonSetYAML, options, &ipIndex, configMaps, serviceContainer) + if err != nil { + return nil, err + } + notifyProxies = append(notifyProxies, proxies...) + report.Pods = append(report.Pods, r.Pods...) validKinds++ ranContainers = true @@ -366,6 +382,29 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options return report, nil } +func (ic *ContainerEngine) playKubeDaemonSet(ctx context.Context, daemonSetYAML *v1apps.DaemonSet, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, []*notifyproxy.NotifyProxy, error) { + var ( + daemonSetName string + podSpec v1.PodTemplateSpec + report entities.PlayKubeReport + ) + + daemonSetName = daemonSetYAML.ObjectMeta.Name + if daemonSetName == "" { + return nil, nil, errors.New("daemonSet does not have a name") + } + podSpec = daemonSetYAML.Spec.Template + + podName := fmt.Sprintf("%s-pod", daemonSetName) + podReport, proxies, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, daemonSetYAML.Annotations, configMaps, serviceContainer) + if err != nil { + return nil, nil, fmt.Errorf("encountered while bringing up pod %s: %w", podName, err) + } + report.Pods = podReport.Pods + + return &report, proxies, nil +} + func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, []*notifyproxy.NotifyProxy, error) { var ( deploymentName string @@ -1249,7 +1288,7 @@ func sortKubeKinds(documentList [][]byte) ([][]byte, error) { } switch kind { - case "Pod", "Deployment": + case "Pod", "Deployment", "DaemonSet": sortedDocumentList = append(sortedDocumentList, document) default: sortedDocumentList = append([][]byte{document}, sortedDocumentList...) @@ -1354,6 +1393,15 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, opt volumeNames = append(volumeNames, vs.Secret.SecretName) } } + case "DaemonSet": + var daemonSetYAML v1apps.DaemonSet + + if err := yaml.Unmarshal(document, &daemonSetYAML); err != nil { + return nil, fmt.Errorf("unable to read YAML as Kube DaemonSet: %w", err) + } + + podName := fmt.Sprintf("%s-pod", daemonSetYAML.Name) + podNames = append(podNames, podName) case "Deployment": var deploymentYAML v1apps.Deployment diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 886ab70eb5..10d5c70d1f 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -723,6 +723,159 @@ spec: status: {} ` +var daemonSetYamlTemplate = ` +apiVersion: v1 +kind: DaemonSet +metadata: + creationTimestamp: "2019-07-17T14:44:08Z" + name: {{ .Name }} + labels: + app: {{ .Name }} +{{ with .Labels }} + {{ range $key, $value := . }} + {{ $key }}: {{ $value }} + {{ end }} +{{ end }} +{{ with .Annotations }} + annotations: + {{ range $key, $value := . }} + {{ $key }}: {{ $value }} + {{ end }} +{{ end }} + +spec: + selector: + matchLabels: + app: {{ .Name }} + template: + {{ with .PodTemplate }} + metadata: + labels: + app: {{ .Name }} + {{- with .Labels }}{{ range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }}{{ end }} + {{- with .Annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + restartPolicy: {{ .RestartPolicy }} + hostname: {{ .Hostname }} + hostNetwork: {{ .HostNetwork }} + containers: + {{ with .Ctrs }} + {{ range . }} + - command: + {{ range .Cmd }} + - {{.}} + {{ end }} + args: + {{ range .Arg }} + - {{.}} + {{ end }} + env: + - name: HOSTNAME + {{ range .Env }} + - name: {{ .Name }} + {{ if (eq .ValueFrom "configmap") }} + valueFrom: + configMapKeyRef: + name: {{ .RefName }} + key: {{ .RefKey }} + optional: {{ .Optional }} + {{ end }} + {{ if (eq .ValueFrom "secret") }} + valueFrom: + secretKeyRef: + name: {{ .RefName }} + key: {{ .RefKey }} + optional: {{ .Optional }} + {{ end }} + {{ if (eq .ValueFrom "") }} + value: {{ .Value }} + {{ end }} + {{ end }} + {{ with .EnvFrom}} + envFrom: + {{ range . }} + {{ if (eq .From "configmap") }} + - configMapRef: + name: {{ .Name }} + optional: {{ .Optional }} + {{ end }} + {{ if (eq .From "secret") }} + - secretRef: + name: {{ .Name }} + optional: {{ .Optional }} + {{ end }} + {{ end }} + {{ end }} + image: {{ .Image }} + name: {{ .Name }} + imagePullPolicy: {{ .PullPolicy }} + {{- if or .CPURequest .CPULimit .MemoryRequest .MemoryLimit }} + resources: + {{- if or .CPURequest .MemoryRequest }} + requests: + {{if .CPURequest }}cpu: {{ .CPURequest }}{{ end }} + {{if .MemoryRequest }}memory: {{ .MemoryRequest }}{{ end }} + {{- end }} + {{- if or .CPULimit .MemoryLimit }} + limits: + {{if .CPULimit }}cpu: {{ .CPULimit }}{{ end }} + {{if .MemoryLimit }}memory: {{ .MemoryLimit }}{{ end }} + {{- end }} + {{- end }} + {{ if .SecurityContext }} + securityContext: + allowPrivilegeEscalation: true + {{ if .Caps }} + capabilities: + {{ with .CapAdd }} + add: + {{ range . }} + - {{.}} + {{ end }} + {{ end }} + {{ with .CapDrop }} + drop: + {{ range . }} + - {{.}} + {{ end }} + {{ end }} + {{ end }} + privileged: false + readOnlyRootFilesystem: false + workingDir: / + volumeMounts: + {{ if .VolumeMount }} + - name: {{.VolumeName}} + mountPath: {{ .VolumeMountPath }} + readonly: {{.VolumeReadOnly}} + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{ with .Volumes }} + volumes: + {{ range . }} + - name: {{ .Name }} + {{- if (eq .VolumeType "HostPath") }} + hostPath: + path: {{ .HostPath.Path }} + type: {{ .HostPath.Type }} + {{- end }} + {{- if (eq .VolumeType "PersistentVolumeClaim") }} + persistentVolumeClaim: + claimName: {{ .PersistentVolumeClaim.ClaimName }} + {{- end }} + {{ end }} + {{ end }} +{{ end }} +` var deploymentYamlTemplate = ` apiVersion: v1 kind: Deployment @@ -1044,6 +1197,7 @@ var ( defaultCtrImage = ALPINE defaultPodName = "testPod" defaultVolName = "testVol" + defaultDaemonSetName = "testDaemonSet" defaultDeploymentName = "testDeployment" defaultConfigMapName = "testConfigMap" defaultSecretName = "testSecret" @@ -1065,6 +1219,8 @@ func getKubeYaml(kind string, object interface{}) (string, error) { yamlTemplate = configMapYamlTemplate case "pod": yamlTemplate = podYamlTemplate + case "daemonset": + yamlTemplate = daemonSetYamlTemplate case "deployment": yamlTemplate = deploymentYamlTemplate case "persistentVolumeClaim": @@ -1356,6 +1512,29 @@ func withHostUsers(val bool) podOption { } // Deployment describes the options a kube yaml can be configured at deployment level +type DaemonSet struct { + Name string + Labels map[string]string + Annotations map[string]string + PodTemplate *Pod +} + +func getDaemonSet(options ...daemonSetOption) *DaemonSet { + d := DaemonSet{ + Name: defaultDaemonSetName, + Labels: make(map[string]string), + Annotations: make(map[string]string), + PodTemplate: getPod(), + } + for _, option := range options { + option(&d) + } + + return &d +} + +type daemonSetOption func(*DaemonSet) + type Deployment struct { Name string Replicas int32 @@ -1399,6 +1578,16 @@ func withReplicas(replicas int32) deploymentOption { } } +// getPodNameInDeployment returns the Pod object +// with just its name set, so that it can be passed around +// and into getCtrNameInPod for ease of testing +func getPodNameInDaemonSet(d *DaemonSet) Pod { + p := Pod{} + p.Name = fmt.Sprintf("%s-pod", d.Name) + + return p +} + // getPodNameInDeployment returns the Pod object // with just its name set, so that it can be passed around // and into getCtrNameInPod for ease of testing @@ -2951,6 +3140,23 @@ spec: Expect(ctr[0].Config).To(HaveField("StopSignal", uint(51))) }) + It("podman play kube daemonset sanity", func() { + daemonset := getDaemonSet() + err := generateKubeYaml("daemonset", daemonset, kubeYaml) + Expect(err).ToNot(HaveOccurred()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + podName := getPodNameInDaemonSet(daemonset) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podName), "--format", "'{{ .Config.Entrypoint }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + // yaml's command should override the image's Entrypoint + Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) + }) + // Deployment related tests It("podman play kube deployment 1 replica test correct command", func() { deployment := getDeployment()