Repository: kubernetes/kompose
Pull request #1425 (2021-08-30 09:59:42)

Add support for placement preferences docker-compose v3.3+
https://docs.docker.com/engine/reference/commandline/service_create/#specify-service-placement-preferences---placement-pref

https://docs.docker.com/engine/swarm/services/#placement-preferences

> Placement preferences try to place tasks on appropriate nodes in an algorithmic way (currently, only spread evenly).
> Placement preferences are not strictly enforced. If no node has the label you specify in your preference, the service is deployed as though the preference were not set.

So, I think we can use affinity to support it.
For exmple:
```yaml
deploy:
  placement:
    preferences:
      - spread: node.labels.ssd
```
can be converted to:
```yaml
affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - preference:
          matchExpressions:
            - key: ssd
              operator: Exists
        weight: 1

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/kompose/pull/1425#" title="Author self-approved">ichxxx</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **ngtuna** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @ngtuna` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fkompose).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes/kompose/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["ngtuna"]} -->
hangyan: @ichxxx Hi, Thanks for the PR. I think affinity is not the way to do it. Check the topology feature of kubernetes, I think may be this is the way: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
ichxxx: @hangyan OK, I will check that.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d89b163a..cb21685d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,69 @@
 # Changelog
 
+## [v1.25.0](https://github.com/kubernetes/kompose/tree/v1.25.0) (2021-10-23)
+
+[Full Changelog](https://github.com/kubernetes/kompose/compare/v1.24.0...v1.25.0)
+
+**Closed issues:**
+
+- convert with multiple docker-compose files fails [\#1444](https://github.com/kubernetes/kompose/issues/1444)
+- Missing exe extension for windows release [\#1433](https://github.com/kubernetes/kompose/issues/1433)
+- is version of golangci-lint consistent with it github-ci [\#1422](https://github.com/kubernetes/kompose/issues/1422)
+- compatability issue with 64-bit version of windows [\#1415](https://github.com/kubernetes/kompose/issues/1415)
+- Kompose generated network YAML cannot be applied because there are o matches for kind "NetworkPolicy" in version "extensions/v1beta1"  [\#1408](https://github.com/kubernetes/kompose/issues/1408)
+- docker-compose 3.8 [\#1396](https://github.com/kubernetes/kompose/issues/1396)
+- 'Support for readinessProbe' is not include Kompose 1.22.0 [\#1384](https://github.com/kubernetes/kompose/issues/1384)
+- Is it possible to push image to custom container registry? [\#1369](https://github.com/kubernetes/kompose/issues/1369)
+- Windows Binaries for 1.22 not updated correctly [\#1347](https://github.com/kubernetes/kompose/issues/1347)
+- Provide sha256 checksums as Assets instead of strings in a HTML table  [\#1311](https://github.com/kubernetes/kompose/issues/1311)
+- \[BUG\] Kompose doesn't read config from URL [\#1291](https://github.com/kubernetes/kompose/issues/1291)
+- kompose secrets file location [\#1280](https://github.com/kubernetes/kompose/issues/1280)
+- kompose convert Volume mount on the host  isn't supported [\#1271](https://github.com/kubernetes/kompose/issues/1271)
+- Dotfile path \(e.g ./envs/./production/.envs\) errors/is not supported in Kompose [\#1240](https://github.com/kubernetes/kompose/issues/1240)
+- Create route for openshift - targert port should be a string [\#1184](https://github.com/kubernetes/kompose/issues/1184)
+- Support yaml anchors [\#1155](https://github.com/kubernetes/kompose/issues/1155)
+- Support for ipv6 ports annotations [\#1136](https://github.com/kubernetes/kompose/issues/1136)
+- Change Chart.yaml attribute version when converted to helm [\#1127](https://github.com/kubernetes/kompose/issues/1127)
+- HostPath volumes File/Directory detection [\#1113](https://github.com/kubernetes/kompose/issues/1113)
+- error decoding 'Ports': No port specified: ::\<empty\> [\#1104](https://github.com/kubernetes/kompose/issues/1104)
+- Names based on the helm chart name [\#1098](https://github.com/kubernetes/kompose/issues/1098)
+- Issues with volume mounts in docker-compose.yml [\#1097](https://github.com/kubernetes/kompose/issues/1097)
+- volumeMounts and subPath - how to convert named volumes [\#1096](https://github.com/kubernetes/kompose/issues/1096)
+- How do I use a service account? [\#1088](https://github.com/kubernetes/kompose/issues/1088)
+- openshift route with edge termination [\#1085](https://github.com/kubernetes/kompose/issues/1085)
+- Convert version 3.5 of Docker Compose is not supported [\#1058](https://github.com/kubernetes/kompose/issues/1058)
+- Kubernetes multiple volumeMounts with subPath and 1 configmap volume - onle 1 volumeMount works [\#1005](https://github.com/kubernetes/kompose/issues/1005)
+- when to support this parameter build: args? [\#1004](https://github.com/kubernetes/kompose/issues/1004)
+- FATA services.xxx.ports.0 must be a string or number [\#986](https://github.com/kubernetes/kompose/issues/986)
+- Remove status field in the generated yaml [\#975](https://github.com/kubernetes/kompose/issues/975)
+- Unable to kompose up my django container from windows or azure cloudshell. [\#966](https://github.com/kubernetes/kompose/issues/966)
+- Create Rolling updates using Kompose tool [\#965](https://github.com/kubernetes/kompose/issues/965)
+- Support ubuntu's snap package format [\#964](https://github.com/kubernetes/kompose/issues/964)
+- kompose up is not able to mount configmap volume [\#962](https://github.com/kubernetes/kompose/issues/962)
+- Remove RC from supported workload [\#947](https://github.com/kubernetes/kompose/issues/947)
+- depends\_on with conditions [\#939](https://github.com/kubernetes/kompose/issues/939)
+- extended env\_file capability to in-docker-compose.yml substitutions [\#909](https://github.com/kubernetes/kompose/issues/909)
+- Allow specifying imagePullSecret [\#897](https://github.com/kubernetes/kompose/issues/897)
+- Add support for "scratch space" volumes [\#850](https://github.com/kubernetes/kompose/issues/850)
+- Do not generate empty services [\#823](https://github.com/kubernetes/kompose/issues/823)
+- add support for `kompose update` [\#822](https://github.com/kubernetes/kompose/issues/822)
+
+**Merged pull requests:**
+
+- Add cmd tests for emptyvols and emptyDir [\#1447](https://github.com/kubernetes/kompose/pull/1447) ([sbs2001](https://github.com/sbs2001))
+- Fix merge compose file's replicas error [\#1446](https://github.com/kubernetes/kompose/pull/1446) ([hangyan](https://github.com/hangyan))
+- Remove bundle test and it's fixtures [\#1443](https://github.com/kubernetes/kompose/pull/1443) ([sbs2001](https://github.com/sbs2001))
+- fix typo: loadbalander -\> loadbalancer [\#1442](https://github.com/kubernetes/kompose/pull/1442) ([kevmo314](https://github.com/kevmo314))
+- Fix typo in empty volume warning [\#1441](https://github.com/kubernetes/kompose/pull/1441) ([sbs2001](https://github.com/sbs2001))
+- Support group service by shared volumes [\#1439](https://github.com/kubernetes/kompose/pull/1439) ([hangyan](https://github.com/hangyan))
+- Refactor [\#1438](https://github.com/kubernetes/kompose/pull/1438) ([hangyan](https://github.com/hangyan))
+- Update golang versions in ci [\#1437](https://github.com/kubernetes/kompose/pull/1437) ([hangyan](https://github.com/hangyan))
+- Upgrade Ingress apiversion [\#1436](https://github.com/kubernetes/kompose/pull/1436) ([hangyan](https://github.com/hangyan))
+- Remove code for parsing DAB files [\#1435](https://github.com/kubernetes/kompose/pull/1435) ([sbs2001](https://github.com/sbs2001))
+- Document debian package [\#1432](https://github.com/kubernetes/kompose/pull/1432) ([sbs2001](https://github.com/sbs2001))
+- Add instruction to change directory to cloned repo while building. [\#1430](https://github.com/kubernetes/kompose/pull/1430) ([sbs2001](https://github.com/sbs2001))
+- Add install golangci-lint to project dir of bin [\#1428](https://github.com/kubernetes/kompose/pull/1428) ([aiyijing](https://github.com/aiyijing))
+
 ## [v1.24.0](https://github.com/kubernetes/kompose/tree/v1.24.0) (2021-08-29)
 
 [Full Changelog](https://github.com/kubernetes/kompose/compare/v1.23.0...v1.24.0)
@@ -19,6 +83,7 @@
 
 **Merged pull requests:**
 
+- 1.24.0 Release [\#1424](https://github.com/kubernetes/kompose/pull/1424) ([hangyan](https://github.com/hangyan))
 - Fix misuse in struct kobject.ServiceConfig.Port [\#1423](https://github.com/kubernetes/kompose/pull/1423) ([ichxxx](https://github.com/ichxxx))
 - Add local golangci-lint [\#1420](https://github.com/kubernetes/kompose/pull/1420) ([aiyijing](https://github.com/aiyijing))
 - Add support for negated placement constraint [\#1419](https://github.com/kubernetes/kompose/pull/1419) ([ichxxx](https://github.com/ichxxx))
diff --git a/Makefile b/Makefile
index 14ee38d4..c147927d 100644
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,7 @@ cross:
 	GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=on go build  ${BUILD_FLAGS} -installsuffix cgo  -o "bin/kompose-linux-amd64" main.go
 	GOOS=linux GOARCH=arm CGO_ENABLED=0 GO111MODULE=on go build  ${BUILD_FLAGS} -installsuffix cgo  -o "bin/kompose-linux-arm" main.go
 	GOOS=linux GOARCH=arm64 CGO_ENABLED=0 GO111MODULE=on go build  ${BUILD_FLAGS} -installsuffix cgo  -o "bin/kompose-linux-arm64" main.go
-	GOOS=windows GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=on go build  ${BUILD_FLAGS} -installsuffix cgo  -o "bin/kompose-windows-amd64" main.go
+	GOOS=windows GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=on go build  ${BUILD_FLAGS} -installsuffix cgo  -o "bin/kompose-windows-amd64.exe" main.go
 	GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=on go build  ${BUILD_FLAGS} -installsuffix cgo  -o "bin/kompose-darwin-amd64" main.go
 
 .PHONY: clean
diff --git a/README.md b/README.md
index e8c04f87..8863e3c2 100644
--- a/README.md
+++ b/README.md
@@ -46,10 +46,10 @@ __Linux and macOS:__
 
 ```sh
 # Linux
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-linux-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-linux-amd64 -o kompose
 
 # macOS
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-darwin-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-darwin-amd64 -o kompose
 
 chmod +x kompose
 sudo mv ./kompose /usr/local/bin/kompose
@@ -57,7 +57,7 @@ sudo mv ./kompose /usr/local/bin/kompose
 
 __Windows:__
 
-Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-windows-amd64.exe) and add the binary to your PATH.
+Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-windows-amd64.exe) and add the binary to your PATH.
 
 ## Shell autocompletion
 
diff --git a/build/VERSION b/build/VERSION
index 53cc1a6f..ad219194 100644
--- a/build/VERSION
+++ b/build/VERSION
@@ -1,1 +1,1 @@
-1.24.0
+1.25.0
diff --git a/docs/installation.md b/docs/installation.md
index 6ff03934..1c292439 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -10,10 +10,10 @@ __Linux and macOS:__
 
 ```sh
 # Linux
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-linux-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-linux-amd64 -o kompose
 
 # macOS
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-darwin-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-darwin-amd64 -o kompose
 
 chmod +x kompose
 sudo mv ./kompose /usr/local/bin/kompose
@@ -21,7 +21,7 @@ sudo mv ./kompose /usr/local/bin/kompose
 
 __Windows:__
 
-Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-windows-amd64.exe) and add the binary to your PATH.
+Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-windows-amd64.exe) and add the binary to your PATH.
 
 #### Go
 
@@ -54,8 +54,8 @@ sudo dnf -y install kompose
 A deb package is released for compose. Download latest package in the assets in [github releases](https://github.com/kubernetes/kompose/releases).
 
 ```bash
-wget https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose_1.24.0_amd64.deb # Replace 1.24.0 with latest tag
-sudo apt install ./kompose_1.24.0_amd64.deb
+wget https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose_1.25.0_amd64.deb # Replace 1.25.0 with latest tag
+sudo apt install ./kompose_1.25.0_amd64.deb
 ```
 
 #### macOS
diff --git a/docs/introduction.md b/docs/introduction.md
index 44ed439d..bb4640e0 100644
--- a/docs/introduction.md
+++ b/docs/introduction.md
@@ -38,13 +38,13 @@ Grab the Kompose binary!
 
 ```sh
 # Linux
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-linux-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-linux-amd64 -o kompose
 
 # macOS
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-darwin-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-darwin-amd64 -o kompose
 
 chmod +x kompose
 sudo mv ./kompose /usr/local/bin/kompose
 ```
 
-_Windows:_ Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.24.0/kompose-windows-amd64.exe) and add the binary to your PATH.
+_Windows:_ Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.25.0/kompose-windows-amd64.exe) and add the binary to your PATH.
diff --git a/docs/user-guide.md b/docs/user-guide.md
index 3ff992b2..b0d2e690 100755
--- a/docs/user-guide.md
+++ b/docs/user-guide.md
@@ -94,7 +94,7 @@ INFO OpenShift file "foo-buildconfig.yaml" created
 
 ## Alternative Conversions
 
-The default `kompose` transformation will generate Kubernetes [Deployments](http://kubernetes.io/docs/user-guide/deployments/) and [Services](http://kubernetes.io/docs/user-guide/services/), in yaml format. You have alternative option to generate json with `-j`. Also, you can alternatively generate [Replication Controllers](http://kubernetes.io/docs/user-guide/replication-controller/) objects, [Daemon Sets](http://kubernetes.io/docs/admin/daemons/), or [Helm](https://github.com/helm/helm) charts.
+The default `kompose` transformation will generate Kubernetes [Deployments](http://kubernetes.io/docs/user-guide/deployments/) and [Services](http://kubernetes.io/docs/user-guide/services/), in yaml format. You have alternative option to generate json with `-j`. Also, you can alternatively generate [Replication Controllers](http://kubernetes.io/docs/user-guide/replication-controller/) objects, [Daemon Sets](http://kubernetes.io/docs/admin/daemons/), [Statefulset](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) or [Helm](https://github.com/helm/helm) charts.
 
 ```sh
 $ kompose convert -j
@@ -125,6 +125,17 @@ INFO Kubernetes file "web-daemonset.yaml" created
 
 The `*-daemonset.yaml` files contain the Daemon Set objects
 
+```sh
+$ kompose convert --controller statefulset
+INFO Kubernetes file "db-service.yaml" created    
+INFO Kubernetes file "wordpress-service.yaml" created 
+INFO Kubernetes file "db-statefulset.yaml" created 
+INFO Kubernetes file "wordpress-statefulset.yaml" created 
+```
+
+The `*statefulset-.yaml` files contain the Statefulset objects.
+
+
 If you want to generate a Chart to be used with [Helm](https://github.com/kubernetes/helm) simply do:
 
 ```sh
@@ -167,13 +178,18 @@ The currently supported options are:
 | kompose.controller.type | deployment / daemonset / replicationcontroller |
 | kompose.image-pull-policy | kubernetes pods imagePullPolicy |
 | kompose.image-pull-secret | kubernetes secret name for imagePullSecrets |
+| kompose.service.healthcheck.readiness.disable | kubernetes readiness disable |
 | kompose.service.healthcheck.readiness.test | kubernetes readiness exec command |
+| kompose.service.healthcheck.readiness.http_get_path | kubernetes readiness httpGet path |
+| kompose.service.healthcheck.readiness.http_get_port | kubernetes readiness httpGet port |
+| kompose.service.healthcheck.readiness.tcp_port | kubernetes readiness tcpSocket port |
 | kompose.service.healthcheck.readiness.interval | kubernetes readiness interval value |
 | kompose.service.healthcheck.readiness.timeout | kubernetes readiness timeout value |
 | kompose.service.healthcheck.readiness.retries | kubernetes readiness retries value |
 | kompose.service.healthcheck.readiness.start_period | kubernetes readiness start_period |
 | kompose.service.healthcheck.liveness.http_get_path | kubernetes liveness httpGet path |
 | kompose.service.healthcheck.liveness.http_get_port | kubernetes liveness httpGet port |
+| kompose.service.healthcheck.liveness.tcp_port | kubernetes liveness tcpSocket port |
 
 **Note**: `kompose.service.type` label should be defined with `ports` only (except for headless service), otherwise `kompose` will fail.
 
diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go
index 15d8195e..3cd6aea0 100644
--- a/pkg/kobject/kobject.go
+++ b/pkg/kobject/kobject.go
@@ -165,7 +165,7 @@ type HealthChecks struct {
 }
 
 // HealthCheck the healthcheck configuration for a service
-// "StartPeriod" is not yet added to compose, see:
+// "StartPeriod" was added to v3.4 of the compose, see:
 // https://github.com/docker/cli/issues/116
 type HealthCheck struct {
 	Test        []string
@@ -176,6 +176,7 @@ type HealthCheck struct {
 	Disable     bool
 	HTTPPath    string
 	HTTPPort    int32
+	TCPPort     int32
 }
 
 // EnvVar holds the environment variable struct of a container
@@ -215,6 +216,7 @@ type Volumes struct {
 type Placement struct {
 	PositiveConstraints map[string]string
 	NegativeConstraints map[string]string
+	Preferences         []string
 }
 
 // GetConfigMapKeyFromMeta ...
diff --git a/pkg/loader/compose/utils.go b/pkg/loader/compose/utils.go
index d9be422f..e2dea0e5 100644
--- a/pkg/loader/compose/utils.go
+++ b/pkg/loader/compose/utils.go
@@ -60,10 +60,18 @@ const (
 	HealthCheckReadinessRetries = "kompose.service.healthcheck.readiness.retries"
 	// HealthCheckReadinessStartPeriod defines readiness health check start period
 	HealthCheckReadinessStartPeriod = "kompose.service.healthcheck.readiness.start_period"
+	// HealthCheckReadinessHTTPGetPath defines readiness health check HttpGet path
+	HealthCheckReadinessHTTPGetPath = "kompose.service.healthcheck.readiness.http_get_path"
+	// HealthCheckReadinessHTTPGetPort defines readiness health check HttpGet port
+	HealthCheckReadinessHTTPGetPort = "kompose.service.healthcheck.readiness.http_get_port"
+	// HealthCheckReadinessTCPPort defines readiness health check tcp port
+	HealthCheckReadinessTCPPort = "kompose.service.healthcheck.readiness.tcp_port"
 	// HealthCheckLivenessHTTPGetPath defines liveness health check HttpGet path
 	HealthCheckLivenessHTTPGetPath = "kompose.service.healthcheck.liveness.http_get_path"
 	// HealthCheckLivenessHTTPGetPort defines liveness health check HttpGet port
 	HealthCheckLivenessHTTPGetPort = "kompose.service.healthcheck.liveness.http_get_port"
+	// HealthCheckLivenessTCPPort defines liveness health check tcp port
+	HealthCheckLivenessTCPPort = "kompose.service.healthcheck.liveness.tcp_port"
 
 	// ServiceTypeHeadless ...
 	ServiceTypeHeadless = "Headless"
diff --git a/pkg/loader/compose/v3.go b/pkg/loader/compose/v3.go
index eaf9fd79..cf4af85f 100644
--- a/pkg/loader/compose/v3.go
+++ b/pkg/loader/compose/v3.go
@@ -137,9 +137,11 @@ func loadV3Placement(placement types.Placement) kobject.Placement {
 	komposePlacement := kobject.Placement{
 		PositiveConstraints: make(map[string]string),
 		NegativeConstraints: make(map[string]string),
+		Preferences:         make([]string, 0, len(placement.Preferences)),
 	}
+
+	// Convert constraints
 	equal, notEqual := " == ", " != "
-	errMsg := " constraints in placement is not supported, only 'node.hostname', 'engine.labels.operatingsystem' and 'node.labels.xxx' (ex: node.labels.something == anything) is supported as a constraint "
 	for _, j := range placement.Constraints {
 		operator := equal
 		if strings.Contains(j, notEqual) {
@@ -147,19 +149,13 @@ func loadV3Placement(placement types.Placement) kobject.Placement {
 		}
 		p := strings.Split(j, operator)
 		if len(p) < 2 {
-			log.Warn(p[0], errMsg)
+			log.Warnf("Failed to parse placement constraints %s, the correct format is 'label == xxx'", j)
 			continue
 		}
 
-		var key string
-		if p[0] == "node.hostname" {
-			key = "kubernetes.io/hostname"
-		} else if p[0] == "engine.labels.operatingsystem" {
-			key = "beta.kubernetes.io/os"
-		} else if strings.HasPrefix(p[0], "node.labels.") {
-			key = strings.TrimPrefix(p[0], "node.labels.")
-		} else {
-			log.Warn(p[0], errMsg)
+		key, err := convertDockerLabel(p[0])
+		if err != nil {
+			log.Warn("Ignore placement constraints: ", err.Error())
 			continue
 		}
 
@@ -169,9 +165,36 @@ func loadV3Placement(placement types.Placement) kobject.Placement {
 			komposePlacement.NegativeConstraints[key] = p[1]
 		}
 	}
+
+	// Convert preferences
+	for _, p := range placement.Preferences {
+		// Spread is the only supported strategy currently
+		label, err := convertDockerLabel(p.Spread)
+		if err != nil {
+			log.Warn("Ignore placement preferences: ", err.Error())
+			continue
+		}
+		komposePlacement.Preferences = append(komposePlacement.Preferences, label)
+	}
 	return komposePlacement
 }
 
+// Convert docker label to k8s label
+func convertDockerLabel(dockerLabel string) (string, error) {
+	switch dockerLabel {
+	case "node.hostname":
+		return "kubernetes.io/hostname", nil
+	case "engine.labels.operatingsystem":
+		return "kubernetes.io/os", nil
+	default:
+		if strings.HasPrefix(dockerLabel, "node.labels.") {
+			return strings.TrimPrefix(dockerLabel, "node.labels."), nil
+		}
+	}
+	errMsg := fmt.Sprint(dockerLabel, " is not supported, only 'node.hostname', 'engine.labels.operatingsystem' and 'node.labels.xxx' (ex: node.labels.something == anything) is supported")
+	return "", errors.New(errMsg)
+}
+
 // Convert the Docker Compose v3 volumes to []string (the old way)
 // TODO: Check to see if it's a "bind" or "volume". Ignore for now.
 // TODO: Refactor it similar to loadV3Ports
@@ -245,9 +268,9 @@ func loadV3Ports(ports []types.ServicePortConfig, expose []string) []kobject.Por
 a Kubernetes-compatible format.
 */
 func parseHealthCheckReadiness(labels types.Labels) (kobject.HealthCheck, error) {
-	// initialize with CMD as default to not break at return (will be ignored if no test is informed)
-	test := []string{"CMD"}
-	var timeout, interval, retries, startPeriod int32
+	var test []string
+	var httpPath string
+	var httpPort, tcpPort, timeout, interval, retries, startPeriod int32
 	var disable bool
 
 	for key, value := range labels {
@@ -258,6 +281,12 @@ func parseHealthCheckReadiness(labels types.Labels) (kobject.HealthCheck, error)
 			if len(value) > 0 {
 				test, _ = shlex.Split(value)
 			}
+		case HealthCheckReadinessHTTPGetPath:
+			httpPath = value
+		case HealthCheckReadinessHTTPGetPort:
+			httpPort = cast.ToInt32(value)
+		case HealthCheckReadinessTCPPort:
+			tcpPort = cast.ToInt32(value)
 		case HealthCheckReadinessInterval:
 			parse, err := time.ParseDuration(value)
 			if err != nil {
@@ -281,17 +310,22 @@ func parseHealthCheckReadiness(labels types.Labels) (kobject.HealthCheck, error)
 		}
 	}
 
-	if test[0] == "NONE" {
-		disable = true
-		test = test[1:]
-	}
-	if test[0] == "CMD" || test[0] == "CMD-SHELL" {
-		test = test[1:]
+	if len(test) > 0 {
+		if test[0] == "NONE" {
+			disable = true
+			test = test[1:]
+		}
+		// Due to docker/cli adding "CMD-SHELL" to the struct, we remove the first element of composeHealthCheck.Test
+		if test[0] == "CMD" || test[0] == "CMD-SHELL" {
+			test = test[1:]
+		}
 	}
 
-	// Due to docker/cli adding "CMD-SHELL" to the struct, we remove the first element of composeHealthCheck.Test
 	return kobject.HealthCheck{
 		Test:        test,
+		HTTPPath:    httpPath,
+		HTTPPort:    httpPort,
+		TCPPort:     tcpPort,
 		Timeout:     timeout,
 		Interval:    interval,
 		Retries:     retries,
@@ -304,9 +338,8 @@ func parseHealthCheckReadiness(labels types.Labels) (kobject.HealthCheck, error)
 a Kubernetes-compatible format.
 */
 func parseHealthCheck(composeHealthCheck types.HealthCheckConfig, labels types.Labels) (kobject.HealthCheck, error) {
-	var timeout, interval, retries, startPeriod int32
+	var httpPort, tcpPort, timeout, interval, retries, startPeriod int32
 	var test []string
-	var httpPort int32
 	var httpPath string
 
 	// Here we convert the timeout from 1h30s (example) to 36030 seconds.
@@ -348,12 +381,15 @@ func parseHealthCheck(composeHealthCheck types.HealthCheckConfig, labels types.L
 			httpPath = value
 		case HealthCheckLivenessHTTPGetPort:
 			httpPort = cast.ToInt32(value)
+		case HealthCheckLivenessTCPPort:
+			tcpPort = cast.ToInt32(value)
 		}
 	}
 
 	// Due to docker/cli adding "CMD-SHELL" to the struct, we remove the first element of composeHealthCheck.Test
 	return kobject.HealthCheck{
 		Test:        test,
+		TCPPort:     tcpPort,
 		HTTPPath:    httpPath,
 		HTTPPort:    httpPort,
 		Timeout:     timeout,
@@ -426,7 +462,7 @@ func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.Kompose
 
 		// HealthCheck Readiness
 		var readiness, errReadiness = parseHealthCheckReadiness(*&composeServiceConfig.Labels)
-		if readiness.Test != nil && len(readiness.Test) > 0 && len(readiness.Test[0]) > 0 && !readiness.Disable {
+		if !readiness.Disable {
 			serviceConfig.HealthChecks.Readiness = readiness
 			if errReadiness != nil {
 				return kobject.KomposeObject{}, errors.Wrap(errReadiness, "Unable to parse health check")
diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go
index 8312bff0..f2e6317f 100644
--- a/pkg/transformer/kubernetes/k8sutils.go
+++ b/pkg/transformer/kubernetes/k8sutils.go
@@ -45,7 +45,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
 /**
@@ -497,7 +496,7 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
 		volumesMount = append(volumesMount, TmpVolumesMount...)
 	}
 
-	if pvc != nil {
+	if pvc != nil && opt.Controller != StatefulStateController {
 		// Looping on the slice pvc instead of `*objects = append(*objects, pvc...)`
 		// because the type of objects and pvc is different, but when doing append
 		// one element at a time it gets converted to runtime.Object for objects slice
@@ -531,65 +530,14 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
 		template.Spec.Containers[0].VolumeMounts = append(template.Spec.Containers[0].VolumeMounts, volumesMount...)
 		template.Spec.Containers[0].Stdin = service.Stdin
 		template.Spec.Containers[0].TTY = service.Tty
-		template.Spec.Volumes = append(template.Spec.Volumes, volumes...)
+		if opt.Controller != StatefulStateController || opt.Volumes == "configMap" {
+			template.Spec.Volumes = append(template.Spec.Volumes, volumes...)
+		}
 		template.Spec.Affinity = ConfigAffinity(service)
+		template.Spec.TopologySpreadConstraints = ConfigTopologySpreadConstraints(service)
 		// Configure the HealthCheck
-		// We check to see if it's blank
-		if !reflect.DeepEqual(service.HealthChecks.Liveness, kobject.HealthCheck{}) {
-			probe := api.Probe{}
-
-			if len(service.HealthChecks.Liveness.Test) > 0 {
-				probe.Handler = api.Handler{
-					Exec: &api.ExecAction{
-						Command: service.HealthChecks.Liveness.Test,
-					},
-				}
-			} else if !reflect.ValueOf(service.HealthChecks.Liveness.HTTPPath).IsZero() &&
-				!reflect.ValueOf(service.HealthChecks.Liveness.HTTPPort).IsZero() {
-				probe.Handler = api.Handler{
-					HTTPGet: &api.HTTPGetAction{
-						Path: service.HealthChecks.Liveness.HTTPPath,
-						Port: intstr.FromInt(int(service.HealthChecks.Liveness.HTTPPort)),
-					},
-				}
-			} else {
-				return errors.New("Health check must contain a command")
-			}
-
-			probe.TimeoutSeconds = service.HealthChecks.Liveness.Timeout
-			probe.PeriodSeconds = service.HealthChecks.Liveness.Interval
-			probe.FailureThreshold = service.HealthChecks.Liveness.Retries
-
-			// See issue: https://github.com/docker/cli/issues/116
-			// StartPeriod has been added to docker/cli however, it is not yet added
-			// to compose. Once the feature has been implemented, this will automatically work
-			probe.InitialDelaySeconds = service.HealthChecks.Liveness.StartPeriod
-
-			template.Spec.Containers[0].LivenessProbe = &probe
-		}
-		if !reflect.DeepEqual(service.HealthChecks.Readiness, kobject.HealthCheck{}) {
-			probeHealthCheckReadiness := api.Probe{}
-			if len(service.HealthChecks.Readiness.Test) > 0 {
-				probeHealthCheckReadiness.Handler = api.Handler{
-					Exec: &api.ExecAction{
-						Command: service.HealthChecks.Readiness.Test,
-					},
-				}
-			} else {
-				return errors.New("Health check must contain a command")
-			}
-
-			probeHealthCheckReadiness.TimeoutSeconds = service.HealthChecks.Readiness.Timeout
-			probeHealthCheckReadiness.PeriodSeconds = service.HealthChecks.Readiness.Interval
-			probeHealthCheckReadiness.FailureThreshold = service.HealthChecks.Readiness.Retries
-
-			// See issue: https://github.com/docker/cli/issues/116
-			// StartPeriod has been added to docker/cli however, it is not yet added
-			// to compose. Once the feature has been implemented, this will automatically work
-			probeHealthCheckReadiness.InitialDelaySeconds = service.HealthChecks.Readiness.StartPeriod
-
-			template.Spec.Containers[0].ReadinessProbe = &probeHealthCheckReadiness
-		}
+		template.Spec.Containers[0].LivenessProbe = configProbe(service.HealthChecks.Liveness)
+		template.Spec.Containers[0].ReadinessProbe = configProbe(service.HealthChecks.Readiness)
 
 		if service.StopGracePeriod != "" {
 			template.Spec.TerminationGracePeriodSeconds, err = DurationStrToSecondsInt(service.StopGracePeriod)
@@ -692,6 +640,18 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
 				objType.Spec.Strategy.Type = appsv1.RecreateDeploymentStrategyType
 			case *deployapi.DeploymentConfig:
 				objType.Spec.Strategy.Type = deployapi.DeploymentStrategyTypeRecreate
+			case *appsv1.StatefulSet:
+				// embed all PVCs inside the StatefulSet object
+				if opt.Volumes == "configMap" {
+					break
+				}
+				persistentVolumeClaims := make([]api.PersistentVolumeClaim, len(pvc))
+				for i, persistentVolumeClaim := range pvc {
+					persistentVolumeClaims[i] = *persistentVolumeClaim
+					persistentVolumeClaims[i].APIVersion = ""
+					persistentVolumeClaims[i].Kind = ""
+				}
+				objType.Spec.VolumeClaimTemplates = persistentVolumeClaims
 			}
 		}
 	}
diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go
index bcf86802..ec9660c9 100644
--- a/pkg/transformer/kubernetes/kubernetes.go
+++ b/pkg/transformer/kubernetes/kubernetes.go
@@ -29,8 +29,6 @@ import (
 	"strconv"
 	"strings"
 
-	"golang.org/x/tools/godoc/util"
-
 	"github.com/fatih/structs"
 	"github.com/kubernetes/kompose/pkg/kobject"
 	"github.com/kubernetes/kompose/pkg/loader/compose"
@@ -40,6 +38,7 @@ import (
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
+	"golang.org/x/tools/godoc/util"
 	appsv1 "k8s.io/api/apps/v1"
 	api "k8s.io/api/core/v1"
 	networkingv1 "k8s.io/api/networking/v1"
@@ -63,6 +62,8 @@ const (
 	DeploymentController = "deployment"
 	// DaemonSetController is controller type for DaemonSet
 	DaemonSetController = "daemonset"
+	// StatefulStateController is controller type for StatefulSet
+	StatefulStateController = "statefulset"
 )
 
 // CheckUnsupportedKey checks if given komposeObject contains
@@ -425,6 +426,37 @@ func (k *Kubernetes) InitDS(name string, service kobject.ServiceConfig) *appsv1.
 	return ds
 }
 
+func (k *Kubernetes) InitSS(name string, service kobject.ServiceConfig, replicas int) *appsv1.StatefulSet {
+	var podSpec api.PodSpec
+	if len(service.Configs) > 0 {
+		podSpec = k.InitPodSpecWithConfigMap(name, service.Image, service)
+	} else {
+		podSpec = k.InitPodSpec(name, service.Image, service.ImagePullSecret)
+	}
+	rp := int32(replicas)
+	ds := &appsv1.StatefulSet{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "StatefulSet",
+			APIVersion: "apps/v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:   name,
+			Labels: transformer.ConfigAllLabels(name, &service),
+		},
+		Spec: appsv1.StatefulSetSpec{
+			Replicas: &rp,
+			Template: api.PodTemplateSpec{
+				Spec: podSpec,
+			},
+			Selector: &metav1.LabelSelector{
+				MatchLabels: transformer.ConfigLabels(name),
+			},
+			ServiceName: service.Name,
+		},
+	}
+	return ds
+}
+
 func (k *Kubernetes) initIngress(name string, service kobject.ServiceConfig, port int32) *networkingv1.Ingress {
 	hosts := regexp.MustCompile("[ ,]*,[ ,]*").Split(service.ExposeService, -1)
 
@@ -1040,22 +1072,52 @@ func ConfigEnvs(service kobject.ServiceConfig, opt kobject.ConvertOptions) ([]ap
 
 // ConfigAffinity configures the Affinity.
 func ConfigAffinity(service kobject.ServiceConfig) *api.Affinity {
+	var affinity *api.Affinity
+	// Config constraints
+	// Convert constraints to requiredDuringSchedulingIgnoredDuringExecution
 	positiveConstraints := configConstrains(service.Placement.PositiveConstraints, api.NodeSelectorOpIn)
 	negativeConstraints := configConstrains(service.Placement.NegativeConstraints, api.NodeSelectorOpNotIn)
-	if len(positiveConstraints) == 0 && len(negativeConstraints) == 0 {
-		return nil
-	}
-	return &api.Affinity{
-		NodeAffinity: &api.NodeAffinity{
-			RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
-				NodeSelectorTerms: []api.NodeSelectorTerm{
-					{
-						MatchExpressions: append(positiveConstraints, negativeConstraints...),
+	if len(positiveConstraints) != 0 || len(negativeConstraints) != 0 {
+		affinity = &api.Affinity{
+			NodeAffinity: &api.NodeAffinity{
+				RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
+					NodeSelectorTerms: []api.NodeSelectorTerm{
+						{
+							MatchExpressions: append(positiveConstraints, negativeConstraints...),
+						},
 					},
 				},
 			},
-		},
+		}
+	}
+	return affinity
+}
+
+// ConfigTopologySpreadConstraints configures the TopologySpreadConstraints.
+func ConfigTopologySpreadConstraints(service kobject.ServiceConfig) []api.TopologySpreadConstraint {
+	preferencesLen := len(service.Placement.Preferences)
+	constraints := make([]api.TopologySpreadConstraint, 0, preferencesLen)
+
+	// Placement preferences are ignored for global services
+	if service.DeployMode == "global" {
+		log.Warnf("Ignore placement preferences for global service %s", service.Name)
+		return constraints
+	}
+
+	for i, p := range service.Placement.Preferences {
+		constraints = append(constraints, api.TopologySpreadConstraint{
+			// According to the order of preferences, the MaxSkew decreases in order
+			// The minimum value is 1
+			MaxSkew:           int32(preferencesLen - i),
+			TopologyKey:       p,
+			WhenUnsatisfiable: api.ScheduleAnyway,
+			LabelSelector: &metav1.LabelSelector{
+				MatchLabels: transformer.ConfigLabels(service.Name),
+			},
+		})
 	}
+
+	return constraints
 }
 
 func configConstrains(constrains map[string]string, operator api.NodeSelectorOperator) []api.NodeSelectorRequirement {
@@ -1120,6 +1182,10 @@ func (k *Kubernetes) CreateWorkloadAndConfigMapObjects(name string, service kobj
 		objects = append(objects, k.InitDS(name, service))
 	}
 
+	if opt.Controller == StatefulStateController {
+		objects = append(objects, k.InitSS(name, service, replica))
+	}
+
 	if len(service.EnvFile) > 0 {
 		for _, envFile := range service.EnvFile {
 			configMap := k.InitConfigMapForEnv(name, opt, envFile)
@@ -1280,7 +1346,6 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
 			allobjects = append(allobjects, item)
 		}
 	}
-
 	if opt.ServiceGroupMode != "" {
 		log.Debugf("Service group mode is: %s", opt.ServiceGroupMode)
 		komposeObjectToServiceConfigGroupMapping := KomposeObjectToServiceConfigGroupMapping(&komposeObject, opt)
@@ -1363,13 +1428,12 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
 					ImagePullPolicy(name, service),
 					RestartPolicy(name, service),
 					SecurityContext(name, service),
-					LivenessProbe(service),
-					ReadinessProbe(service),
 					HostName(service),
 					DomainName(service),
 					ResourcesLimits(service),
 					ResourcesRequests(service),
 					TerminationGracePeriodSeconds(name, service),
+					TopologySpreadConstraints(service),
 				)
 
 				if serviceAccountName, ok := service.Labels[compose.LabelServiceAccountName]; ok {
@@ -1415,6 +1479,9 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
 			objects = k.CreateWorkloadAndConfigMapObjects(name, service, opt)
 		}
 
+		if opt.Controller == StatefulStateController {
+			service.ServiceType = "Headless"
+		}
 		k.configKubeServiceAndIngressForService(service, name, &objects)
 
 		err := k.UpdateKubernetesObjects(name, service, opt, &objects)
@@ -1451,6 +1518,12 @@ func (k *Kubernetes) UpdateController(obj runtime.Object, updateTemplate func(*a
 			return errors.Wrap(err, "updateTemplate failed")
 		}
 		updateMeta(&t.ObjectMeta)
+	case *appsv1.StatefulSet:
+		err = updateTemplate(&t.Spec.Template)
+		if err != nil {
+			return errors.Wrap(err, "updateTemplate failed")
+		}
+		updateMeta(&t.ObjectMeta)
 	case *deployapi.DeploymentConfig:
 		err = updateTemplate(t.Spec.Template)
 		if err != nil {
diff --git a/pkg/transformer/kubernetes/podspec.go b/pkg/transformer/kubernetes/podspec.go
index ddb6abc7..040a95ef 100644
--- a/pkg/transformer/kubernetes/podspec.go
+++ b/pkg/transformer/kubernetes/podspec.go
@@ -34,14 +34,16 @@ func AddContainer(service kobject.ServiceConfig, opt kobject.ConvertOptions) Pod
 		}
 
 		podSpec.Containers = append(podSpec.Containers, api.Container{
-			Name:       name,
-			Image:      image,
-			Env:        envs,
-			Command:    service.Command,
-			Args:       service.Args,
-			WorkingDir: service.WorkingDir,
-			Stdin:      service.Stdin,
-			TTY:        service.Tty,
+			Name:           name,
+			Image:          image,
+			Env:            envs,
+			Command:        service.Command,
+			Args:           service.Args,
+			WorkingDir:     service.WorkingDir,
+			Stdin:          service.Stdin,
+			TTY:            service.Tty,
+			LivenessProbe:  configProbe(service.HealthChecks.Liveness),
+			ReadinessProbe: configProbe(service.HealthChecks.Readiness),
 		})
 
 		podSpec.Affinity = ConfigAffinity(service)
@@ -258,75 +260,44 @@ func DomainName(service kobject.ServiceConfig) PodSpecOption {
 	}
 }
 
-func LivenessProbe(service kobject.ServiceConfig) PodSpecOption {
-	return func(podSpec *PodSpec) {
-		// Configure the HealthCheck
-		// We check to see if it's blank
-		if !reflect.DeepEqual(service.HealthChecks.Liveness, kobject.HealthCheck{}) {
-			probe := api.Probe{}
-
-			if len(service.HealthChecks.Liveness.Test) > 0 {
-				probe.Handler = api.Handler{
-					Exec: &api.ExecAction{
-						Command: service.HealthChecks.Liveness.Test,
-					},
-				}
-			} else if !reflect.ValueOf(service.HealthChecks.Liveness.HTTPPath).IsZero() &&
-				!reflect.ValueOf(service.HealthChecks.Liveness.HTTPPort).IsZero() {
-				probe.Handler = api.Handler{
-					HTTPGet: &api.HTTPGetAction{
-						Path: service.HealthChecks.Liveness.HTTPPath,
-						Port: intstr.FromInt(int(service.HealthChecks.Liveness.HTTPPort)),
-					},
-				}
-			} else {
-				panic(errors.New("Health check must contain a command"))
-			}
-
-			probe.TimeoutSeconds = service.HealthChecks.Liveness.Timeout
-			probe.PeriodSeconds = service.HealthChecks.Liveness.Interval
-			probe.FailureThreshold = service.HealthChecks.Liveness.Retries
-
-			// See issue: https://github.com/docker/cli/issues/116
-			// StartPeriod has been added to docker/cli however, it is not yet added
-			// to compose. Once the feature has been implemented, this will automatically work
-			probe.InitialDelaySeconds = service.HealthChecks.Liveness.StartPeriod
+func configProbe(healthCheck kobject.HealthCheck) *api.Probe {
+	probe := api.Probe{}
+	// We check to see if it's blank or disable
+	if reflect.DeepEqual(healthCheck, kobject.HealthCheck{}) || healthCheck.Disable {
+		return nil
+	}
 
-			for i := range podSpec.Containers {
-				podSpec.Containers[i].LivenessProbe = &probe
-			}
+	if len(healthCheck.Test) > 0 {
+		probe.Handler = api.Handler{
+			Exec: &api.ExecAction{
+				Command: healthCheck.Test,
+			},
+		}
+	} else if !reflect.ValueOf(healthCheck.HTTPPath).IsZero() && !reflect.ValueOf(healthCheck.HTTPPort).IsZero() {
+		probe.Handler = api.Handler{
+			HTTPGet: &api.HTTPGetAction{
+				Path: healthCheck.HTTPPath,
+				Port: intstr.FromInt(int(healthCheck.HTTPPort)),
+			},
 		}
+	} else if !reflect.ValueOf(healthCheck.TCPPort).IsZero() {
+		probe.Handler = api.Handler{
+			TCPSocket: &api.TCPSocketAction{
+				Port: intstr.FromInt(int(healthCheck.TCPPort)),
+			},
+		}
+	} else {
+		panic(errors.New("Health check must contain a command"))
 	}
-}
 
-func ReadinessProbe(service kobject.ServiceConfig) PodSpecOption {
-	return func(podSpec *PodSpec) {
-		if !reflect.DeepEqual(service.HealthChecks.Readiness, kobject.HealthCheck{}) {
-			probeHealthCheckReadiness := api.Probe{}
-			if len(service.HealthChecks.Readiness.Test) > 0 {
-				probeHealthCheckReadiness.Handler = api.Handler{
-					Exec: &api.ExecAction{
-						Command: service.HealthChecks.Readiness.Test,
-					},
-				}
-			} else {
-				panic(errors.New("Health check must contain a command"))
-			}
-
-			probeHealthCheckReadiness.TimeoutSeconds = service.HealthChecks.Readiness.Timeout
-			probeHealthCheckReadiness.PeriodSeconds = service.HealthChecks.Readiness.Interval
-			probeHealthCheckReadiness.FailureThreshold = service.HealthChecks.Readiness.Retries
+	probe.TimeoutSeconds = healthCheck.Timeout
+	probe.PeriodSeconds = healthCheck.Interval
+	probe.FailureThreshold = healthCheck.Retries
 
-			// See issue: https://github.com/docker/cli/issues/116
-			// StartPeriod has been added to docker/cli however, it is not yet added
-			// to compose. Once the feature has been implemented, this will automatically work
-			probeHealthCheckReadiness.InitialDelaySeconds = service.HealthChecks.Readiness.StartPeriod
-
-			for i := range podSpec.Containers {
-				podSpec.Containers[i].ReadinessProbe = &probeHealthCheckReadiness
-			}
-		}
-	}
+	// See issue: https://github.com/docker/cli/issues/116
+	// StartPeriod has been added to v3.4 of the compose
+	probe.InitialDelaySeconds = healthCheck.StartPeriod
+	return &probe
 }
 
 func ServiceAccountName(serviceAccountName string) PodSpecOption {
@@ -335,6 +306,12 @@ func ServiceAccountName(serviceAccountName string) PodSpecOption {
 	}
 }
 
+func TopologySpreadConstraints(service kobject.ServiceConfig) PodSpecOption {
+	return func(podSpec *PodSpec) {
+		podSpec.TopologySpreadConstraints = ConfigTopologySpreadConstraints(service)
+	}
+}
+
 func (podSpec *PodSpec) Append(ops ...PodSpecOption) *PodSpec {
 	for _, option := range ops {
 		option(podSpec)
diff --git a/pkg/version/version.go b/pkg/version/version.go
index dfbc8f86..a3f1c10d 100644
--- a/pkg/version/version.go
+++ b/pkg/version/version.go
@@ -2,7 +2,7 @@ package version
 
 var (
 	// VERSION  is version number that will be displayed when running ./kompose version
-	VERSION = "1.24.0"
+	VERSION = "1.25.0"
 	// GITCOMMIT is hash of the commit that will be displayed when running ./kompose version
 	// this will be overwritten when running  build like this: go build -ldflags="-X github.com/kubernetes/kompose/pkg/version.GITCOMMIT=$(GITCOMMIT)"
 	// HEAD is default indicating that this was not set during build


Test patch:
diff --git a/pkg/loader/compose/compose_test.go b/pkg/loader/compose/compose_test.go
index aed7a518..1f3b1cfc 100644
--- a/pkg/loader/compose/compose_test.go
+++ b/pkg/loader/compose/compose_test.go
@@ -41,61 +41,157 @@ func durationTypesPtr(value time.Duration) *types.Duration {
 
 func TestParseHealthCheck(t *testing.T) {
 	helperValue := uint64(2)
-	check := types.HealthCheckConfig{
-		Test:        []string{"CMD-SHELL", "echo", "foobar"},
-		Timeout:     durationTypesPtr(1 * time.Second),
-		Interval:    durationTypesPtr(2 * time.Second),
-		Retries:     &helperValue,
-		StartPeriod: durationTypesPtr(3 * time.Second),
+	type input struct {
+		healthCheck types.HealthCheckConfig
+		labels      types.Labels
 	}
-
-	// CMD-SHELL or SHELL is included Test within docker/cli, thus we remove the first value in Test
-	expected := kobject.HealthCheck{
-		Test:        []string{"echo", "foobar"},
-		Timeout:     1,
-		Interval:    2,
-		Retries:     2,
-		StartPeriod: 3,
-	}
-	output, err := parseHealthCheck(check, nil)
-	if err != nil {
-		t.Errorf("Unable to convert HealthCheckConfig: %s", err)
+	testCases := map[string]struct {
+		input    input
+		expected kobject.HealthCheck
+	}{
+		"Exec": {
+			input: input{
+				healthCheck: types.HealthCheckConfig{
+					Test:        []string{"CMD-SHELL", "echo", "foobar"},
+					Timeout:     durationTypesPtr(1 * time.Second),
+					Interval:    durationTypesPtr(2 * time.Second),
+					Retries:     &helperValue,
+					StartPeriod: durationTypesPtr(3 * time.Second),
+				},
+			},
+			// CMD-SHELL or SHELL is included Test within docker/cli, thus we remove the first value in Test
+			expected: kobject.HealthCheck{
+				Test:        []string{"echo", "foobar"},
+				Timeout:     1,
+				Interval:    2,
+				Retries:     2,
+				StartPeriod: 3,
+			},
+		},
+		"HTTPGet": {
+			input: input{
+				healthCheck: types.HealthCheckConfig{
+					Timeout:     durationTypesPtr(1 * time.Second),
+					Interval:    durationTypesPtr(2 * time.Second),
+					Retries:     &helperValue,
+					StartPeriod: durationTypesPtr(3 * time.Second),
+				},
+				labels: types.Labels{
+					"kompose.service.healthcheck.liveness.http_get_path": "/health",
+					"kompose.service.healthcheck.liveness.http_get_port": "8080",
+				},
+			},
+			expected: kobject.HealthCheck{
+				HTTPPath:    "/health",
+				HTTPPort:    8080,
+				Timeout:     1,
+				Interval:    2,
+				Retries:     2,
+				StartPeriod: 3,
+			},
+		},
+		"TCPSocket": {
+			input: input{
+				healthCheck: types.HealthCheckConfig{
+					Timeout:     durationTypesPtr(1 * time.Second),
+					Interval:    durationTypesPtr(2 * time.Second),
+					Retries:     &helperValue,
+					StartPeriod: durationTypesPtr(3 * time.Second),
+				},
+				labels: types.Labels{
+					"kompose.service.healthcheck.liveness.tcp_port": "8080",
+				},
+			},
+			expected: kobject.HealthCheck{
+				TCPPort:     8080,
+				Timeout:     1,
+				Interval:    2,
+				Retries:     2,
+				StartPeriod: 3,
+			},
+		},
 	}
 
-	if !reflect.DeepEqual(output, expected) {
-		t.Errorf("Structs are not equal, expected: %v, output: %v", expected, output)
+	for name, testCase := range testCases {
+		t.Log("Test case:", name)
+		output, err := parseHealthCheck(testCase.input.healthCheck, testCase.input.labels)
+		if err != nil {
+			t.Errorf("Unable to convert HealthCheckConfig: %s", err)
+		}
+
+		if !reflect.DeepEqual(output, testCase.expected) {
+			t.Errorf("Structs are not equal, expected: %v, output: %v", testCase.expected, output)
+		}
 	}
 }
 
-func TestParseHttpHealthCheck(t *testing.T) {
-	helperValue := uint64(2)
-	check := types.HealthCheckConfig{
-		Timeout:     durationTypesPtr(1 * time.Second),
-		Interval:    durationTypesPtr(2 * time.Second),
-		Retries:     &helperValue,
-		StartPeriod: durationTypesPtr(3 * time.Second),
-	}
-	label := types.Labels{
-		HealthCheckLivenessHTTPGetPath: "ping",
-		HealthCheckLivenessHTTPGetPort: "80",
+func TestParseHealthCheckReadiness(t *testing.T) {
+	testCases := map[string]struct {
+		input    types.Labels
+		expected kobject.HealthCheck
+	}{
+		"Exec": {
+			input: types.Labels{
+				"kompose.service.healthcheck.readiness.test":         "echo foobar",
+				"kompose.service.healthcheck.readiness.timeout":      "1s",
+				"kompose.service.healthcheck.readiness.interval":     "2s",
+				"kompose.service.healthcheck.readiness.retries":      "2",
+				"kompose.service.healthcheck.readiness.start_period": "3s",
+			},
+			expected: kobject.HealthCheck{
+				Test:        []string{"echo", "foobar"},
+				Timeout:     1,
+				Interval:    2,
+				Retries:     2,
+				StartPeriod: 3,
+			},
+		},
+		"HTTPGet": {
+			input: types.Labels{
+				"kompose.service.healthcheck.readiness.http_get_path": "/ready",
+				"kompose.service.healthcheck.readiness.http_get_port": "8080",
+				"kompose.service.healthcheck.readiness.timeout":       "1s",
+				"kompose.service.healthcheck.readiness.interval":      "2s",
+				"kompose.service.healthcheck.readiness.retries":       "2",
+				"kompose.service.healthcheck.readiness.start_period":  "3s",
+			},
+			expected: kobject.HealthCheck{
+				HTTPPath:    "/ready",
+				HTTPPort:    8080,
+				Timeout:     1,
+				Interval:    2,
+				Retries:     2,
+				StartPeriod: 3,
+			},
+		},
+		"TCPSocket": {
+			input: types.Labels{
+				"kompose.service.healthcheck.readiness.tcp_port":     "8080",
+				"kompose.service.healthcheck.readiness.timeout":      "1s",
+				"kompose.service.healthcheck.readiness.interval":     "2s",
+				"kompose.service.healthcheck.readiness.retries":      "2",
+				"kompose.service.healthcheck.readiness.start_period": "3s",
+			},
+			expected: kobject.HealthCheck{
+				TCPPort:     8080,
+				Timeout:     1,
+				Interval:    2,
+				Retries:     2,
+				StartPeriod: 3,
+			},
+		},
 	}
 
-	// CMD-SHELL or SHELL is included Test within docker/cli, thus we remove the first value in Test
-	expected := kobject.HealthCheck{
-		HTTPPath:    "ping",
-		HTTPPort:    80,
-		Timeout:     1,
-		Interval:    2,
-		Retries:     2,
-		StartPeriod: 3,
-	}
-	output, err := parseHealthCheck(check, label)
-	if err != nil {
-		t.Errorf("Unable to convert HealthCheckConfig: %s", err)
-	}
+	for name, testCase := range testCases {
+		t.Log("Test case:", name)
+		output, err := parseHealthCheckReadiness(testCase.input)
+		if err != nil {
+			t.Errorf("Unable to convert HealthCheckConfig: %s", err)
+		}
 
-	if !reflect.DeepEqual(output, expected) {
-		t.Errorf("Structs are not equal, expected: %v, output: %v", expected, output)
+		if !reflect.DeepEqual(output, testCase.expected) {
+			t.Errorf("Structs are not equal, expected: %v, output: %v", testCase.expected, output)
+		}
 	}
 }
 
@@ -499,6 +595,11 @@ func TestCheckPlacementCustomLabels(t *testing.T) {
 			"node.labels.something == anything",
 			"node.labels.monitor != xxx",
 		},
+		Preferences: []types.PlacementPreferences{
+			{Spread: "node.labels.zone"},
+			{Spread: "foo"},
+			{Spread: "node.labels.ssd"},
+		},
 	}
 	output := loadV3Placement(placement)
 
@@ -509,10 +610,22 @@ func TestCheckPlacementCustomLabels(t *testing.T) {
 		NegativeConstraints: map[string]string{
 			"monitor": "xxx",
 		},
+		Preferences: []string{
+			"zone", "ssd",
+		},
 	}
 
 	checkConstraints(t, "positive", output.PositiveConstraints, expected.PositiveConstraints)
 	checkConstraints(t, "negative", output.NegativeConstraints, expected.NegativeConstraints)
+
+	if len(output.Preferences) != len(expected.Preferences) {
+		t.Errorf("preferences len is not equal, expected %d, got %d", len(expected.Preferences), len(output.Preferences))
+	}
+	for i := range output.Preferences {
+		if output.Preferences[i] != expected.Preferences[i] {
+			t.Errorf("preference is not equal, expected %s, got %s", expected.Preferences[i], output.Preferences[i])
+		}
+	}
 }
 
 func checkConstraints(t *testing.T, caseName string, output, expected map[string]string) {
diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go
index 293f3b62..2dd3c841 100644
--- a/pkg/transformer/kubernetes/k8sutils_test.go
+++ b/pkg/transformer/kubernetes/k8sutils_test.go
@@ -352,41 +352,96 @@ func TestIsDir(t *testing.T) {
 	}
 }
 
-// TestServiceWithoutPort this tests if Headless Service is created for services without Port.
+// TestServiceWithHealthCheck this tests if Headless Service is created for services with HealthCheck.
 func TestServiceWithHealthCheck(t *testing.T) {
-	service := kobject.ServiceConfig{
-		ContainerName: "name",
-		Image:         "image",
-		ServiceType:   "Headless",
-		HealthChecks: kobject.HealthChecks{
-			Readiness: kobject.HealthCheck{
-				Test:        []string{"arg1", "arg2"},
-				Timeout:     10,
-				Interval:    5,
-				Retries:     3,
-				StartPeriod: 60,
+	testCases := map[string]struct {
+		service kobject.ServiceConfig
+	}{
+		"Exec": {
+			service: kobject.ServiceConfig{
+				ContainerName: "name",
+				Image:         "image",
+				ServiceType:   "Headless",
+				HealthChecks: kobject.HealthChecks{
+					Readiness: kobject.HealthCheck{
+						Test:        []string{"arg1", "arg2"},
+						Timeout:     10,
+						Interval:    5,
+						Retries:     3,
+						StartPeriod: 60,
+					},
+					Liveness: kobject.HealthCheck{
+						Test:        []string{"arg1", "arg2"},
+						Timeout:     11,
+						Interval:    6,
+						Retries:     4,
+						StartPeriod: 61,
+					},
+				},
 			},
-			Liveness: kobject.HealthCheck{
-				Test:        []string{"arg1", "arg2"},
-				Timeout:     11,
-				Interval:    6,
-				Retries:     4,
-				StartPeriod: 61,
+		},
+		"HTTPGet": {
+			service: kobject.ServiceConfig{
+				ContainerName: "name",
+				Image:         "image",
+				ServiceType:   "Headless",
+				HealthChecks: kobject.HealthChecks{
+					Readiness: kobject.HealthCheck{
+						HTTPPath:    "/health",
+						HTTPPort:    8080,
+						Timeout:     10,
+						Interval:    5,
+						Retries:     3,
+						StartPeriod: 60,
+					},
+					Liveness: kobject.HealthCheck{
+						HTTPPath:    "/ready",
+						HTTPPort:    8080,
+						Timeout:     11,
+						Interval:    6,
+						Retries:     4,
+						StartPeriod: 61,
+					},
+				},
+			},
+		},
+		"TCPSocket": {
+			service: kobject.ServiceConfig{
+				ContainerName: "name",
+				Image:         "image",
+				ServiceType:   "Headless",
+				HealthChecks: kobject.HealthChecks{
+					Readiness: kobject.HealthCheck{
+						TCPPort:     8080,
+						Timeout:     10,
+						Interval:    5,
+						Retries:     3,
+						StartPeriod: 60,
+					},
+					Liveness: kobject.HealthCheck{
+						TCPPort:     8080,
+						Timeout:     11,
+						Interval:    6,
+						Retries:     4,
+						StartPeriod: 61,
+					},
+				},
 			},
 		},
 	}
 
-	komposeObject := kobject.KomposeObject{
-		ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
-	}
-	k := Kubernetes{}
-
-	objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1})
-	if err != nil {
-		t.Error(errors.Wrap(err, "k.Transform failed"))
-	}
-	if err := testutils.CheckForHealthCheckLivenessAndReadiness(objects); err != nil {
-		t.Error(err)
+	for _, testCase := range testCases {
+		k := Kubernetes{}
+		komposeObject := kobject.KomposeObject{
+			ServiceConfigs: map[string]kobject.ServiceConfig{"app": testCase.service},
+		}
+		objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1})
+		if err != nil {
+			t.Error(errors.Wrap(err, "k.Transform failed"))
+		}
+		if err := testutils.CheckForHealthCheckLivenessAndReadiness(objects); err != nil {
+			t.Error(err)
+		}
 	}
 }
 
diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go
index 2d68a63e..6134899d 100644
--- a/pkg/transformer/kubernetes/kubernetes_test.go
+++ b/pkg/transformer/kubernetes/kubernetes_test.go
@@ -303,7 +303,9 @@ func TestKomposeConvert(t *testing.T) {
 		// objects generated are deployment, daemonset, ReplicationController, service and pvc
 		"Convert to D, DS, and RC":                  {newKomposeObject(), kobject.ConvertOptions{CreateD: true, CreateDS: true, CreateRC: true, Replicas: replicas, IsReplicaSetFlag: true}, 7},
 		"Convert to D, DS, and RC with v3 replicas": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, CreateDS: true, CreateRC: true}, 7},
-		// TODO: add more tests
+		// objects generated are statefulset
+		"Convert to SS with replicas ":   {newKomposeObject(), kobject.ConvertOptions{Controller: StatefulStateController, Replicas: replicas, IsReplicaSetFlag: true}, 5},
+		"Convert to SS without replicas": {newKomposeObject(), kobject.ConvertOptions{Controller: StatefulStateController}, 5},
 	}
 
 	for name, test := range testCases {
@@ -318,7 +320,7 @@ func TestKomposeConvert(t *testing.T) {
 			t.Errorf("Expected %d objects returned, got %d", test.expectedNumObjs, len(objs))
 		}
 
-		var foundSVC, foundD, foundDS, foundDC bool
+		var foundSVC, foundD, foundDS, foundDC, foundSS bool
 		name := "app"
 		labels := transformer.ConfigLabels(name)
 		config := test.komposeObject.ServiceConfigs[name]
@@ -334,6 +336,7 @@ func TestKomposeConvert(t *testing.T) {
 				}
 				foundSVC = true
 			}
+
 			if test.opt.CreateD {
 				if d, ok := obj.(*appsv1.Deployment); ok {
 					if err := checkPodTemplate(config, d.Spec.Template, labelsWithNetwork); err != nil {
@@ -426,6 +429,60 @@ func TestKomposeConvert(t *testing.T) {
 				}
 			}
 
+			if test.opt.Controller == StatefulStateController {
+				if ss, ok := obj.(*appsv1.StatefulSet); ok {
+					if err := checkPodTemplate(config, ss.Spec.Template, labelsWithNetwork); err != nil {
+						t.Errorf("%v", err)
+					}
+					if err := checkMeta(config, ss.ObjectMeta, name, true); err != nil {
+						t.Errorf("%v", err)
+					}
+					if test.opt.IsReplicaSetFlag {
+						if (int)(*ss.Spec.Replicas) != replicas {
+							t.Errorf("Expected %d replicas, got %d", replicas, ss.Spec.Replicas)
+						}
+					} else {
+						if (int)(*ss.Spec.Replicas) != newServiceConfig().Replicas {
+							t.Errorf("Expected %d replicas, got %d", newServiceConfig().Replicas, ss.Spec.Replicas)
+						}
+					}
+					foundSS = true
+				}
+
+				if u, ok := obj.(*unstructured.Unstructured); ok {
+					if u.GetKind() == "Statefulset" {
+						u.SetGroupVersionKind(schema.GroupVersionKind{
+							Group:   "apps",
+							Version: "v1",
+							Kind:    "Statefulset",
+						})
+						data, err := json.Marshal(u)
+						if err != nil {
+							t.Errorf("%v", err)
+						}
+						var d appsv1.Deployment
+						if err := json.Unmarshal(data, &d); err == nil {
+							if err := checkPodTemplate(config, d.Spec.Template, labelsWithNetwork); err != nil {
+								t.Errorf("%v", err)
+							}
+							if err := checkMeta(config, d.ObjectMeta, name, true); err != nil {
+								t.Errorf("%v", err)
+							}
+							if test.opt.IsReplicaSetFlag {
+								if (int)(*d.Spec.Replicas) != replicas {
+									t.Errorf("Expected %d replicas, got %d", replicas, d.Spec.Replicas)
+								}
+							} else {
+								if (int)(*d.Spec.Replicas) != newServiceConfig().Replicas {
+									t.Errorf("Expected %d replicas, got %d", newServiceConfig().Replicas, d.Spec.Replicas)
+								}
+							}
+							foundSS = true
+						}
+					}
+				}
+			}
+
 			// TODO: k8s & openshift transformer is now separated; either separate the test or combine the transformer
 			if test.opt.CreateDeploymentConfig {
 				if dc, ok := obj.(*deployapi.DeploymentConfig); ok {
@@ -455,6 +512,9 @@ func TestKomposeConvert(t *testing.T) {
 			t.Errorf("Expected create Daemon Set: %v, found Daemon Set: %v", test.opt.CreateDS, foundDS)
 		}
 
+		if test.opt.Controller == StatefulStateController && !foundSS {
+			t.Errorf("Expected create StatefulStateController")
+		}
 		if test.opt.CreateDeploymentConfig != foundDC {
 			t.Errorf("Expected create Deployment Config: %v, found Deployment Config: %v", test.opt.CreateDeploymentConfig, foundDC)
 		}
@@ -608,6 +668,51 @@ func TestConfigAffinity(t *testing.T) {
 	}
 }
 
+func TestConfigTopologySpreadConstraints(t *testing.T) {
+	serviceName := "app"
+	testCases := map[string]struct {
+		service kobject.ServiceConfig
+		result  []api.TopologySpreadConstraint
+	}{
+		"ConfigTopologySpreadConstraint": {
+			service: kobject.ServiceConfig{
+				Name: serviceName,
+				Placement: kobject.Placement{
+					Preferences: []string{
+						"zone", "ssd",
+					},
+				},
+			},
+			result: []api.TopologySpreadConstraint{
+				{
+					MaxSkew:           2,
+					TopologyKey:       "zone",
+					WhenUnsatisfiable: api.ScheduleAnyway,
+					LabelSelector: &metav1.LabelSelector{
+						MatchLabels: transformer.ConfigLabels(serviceName),
+					},
+				},
+				{
+					MaxSkew:           1,
+					TopologyKey:       "ssd",
+					WhenUnsatisfiable: api.ScheduleAnyway,
+					LabelSelector: &metav1.LabelSelector{
+						MatchLabels: transformer.ConfigLabels(serviceName),
+					},
+				},
+			},
+		},
+	}
+
+	for name, test := range testCases {
+		t.Log("Test case:", name)
+		result := ConfigTopologySpreadConstraints(test.service)
+		if !reflect.DeepEqual(result, test.result) {
+			t.Errorf("Not expected result for ConfigTopologySpreadConstraints")
+		}
+	}
+}
+
 func TestMultipleContainersInPod(t *testing.T) {
 	groupName := "pod_group"
 
@@ -744,6 +849,84 @@ func TestServiceAccountNameOnMultipleContainers(t *testing.T) {
 	}
 }
 
+func TestHealthCheckOnMultipleContainers(t *testing.T) {
+	groupName := "pod_group"
+
+	createHealthCheck := func(TCPPort int32) kobject.HealthCheck {
+		return kobject.HealthCheck{
+			TCPPort: TCPPort,
+		}
+	}
+
+	createConfig := func(name string, livenessTCPPort, readinessTCPPort int32) kobject.ServiceConfig {
+		config := newSimpleServiceConfig()
+		config.Labels = map[string]string{compose.LabelServiceGroup: groupName}
+		config.Name = name
+		config.ContainerName = name
+		config.HealthChecks.Liveness = createHealthCheck(livenessTCPPort)
+		config.HealthChecks.Readiness = createHealthCheck(readinessTCPPort)
+		return config
+	}
+
+	testCases := map[string]struct {
+		komposeObject      kobject.KomposeObject
+		opt                kobject.ConvertOptions
+		expectedContainers map[string]api.Container
+	}{
+		"Converted multiple containers to Deployments": {
+			kobject.KomposeObject{
+				ServiceConfigs: map[string]kobject.ServiceConfig{
+					"app1": createConfig("app1", 8081, 9091),
+					"app2": createConfig("app2", 8082, 9092),
+				},
+			},
+			kobject.ConvertOptions{ServiceGroupMode: "label", CreateD: true},
+			map[string]api.Container{
+				"app1": {
+					LivenessProbe:  configProbe(createHealthCheck(8081)),
+					ReadinessProbe: configProbe(createHealthCheck(9091)),
+				},
+				"app2": {
+					LivenessProbe:  configProbe(createHealthCheck(8082)),
+					ReadinessProbe: configProbe(createHealthCheck(9092)),
+				},
+			},
+		},
+	}
+
+	for name, test := range testCases {
+		t.Log("Test case:", name)
+		k := Kubernetes{}
+		// Run Transform
+		objs, err := k.Transform(test.komposeObject, test.opt)
+		if err != nil {
+			t.Error(errors.Wrap(err, "k.Transform failed"))
+		}
+
+		// Check results
+		for _, obj := range objs {
+			if deployment, ok := obj.(*appsv1.Deployment); ok {
+				if len(deployment.Spec.Template.Spec.Containers) != len(test.expectedContainers) {
+					t.Errorf("Containers len is not equal, expected %d, got %d",
+						len(deployment.Spec.Template.Spec.Containers), len(test.expectedContainers))
+				}
+				for _, result := range deployment.Spec.Template.Spec.Containers {
+					expected, ok := test.expectedContainers[result.Name]
+					if !ok {
+						t.Errorf("Container %s doesn't expected", result.Name)
+					}
+					if !reflect.DeepEqual(result.LivenessProbe, expected.LivenessProbe) {
+						t.Errorf("Container %s: LivenessProbe expected %v returned, got %v", result.Name, expected.LivenessProbe, result.LivenessProbe)
+					}
+					if !reflect.DeepEqual(result.ReadinessProbe, expected.ReadinessProbe) {
+						t.Errorf("Container %s: ReadinessProbe expected %v returned, got %v", result.Name, expected.ReadinessProbe, result.ReadinessProbe)
+					}
+				}
+			}
+		}
+	}
+}
+
 func TestCreatePVC(t *testing.T) {
 	storageClassName := "custom-storage-class-name"
 	k := Kubernetes{}
diff --git a/script/test/cmd/tests_new.sh b/script/test/cmd/tests_new.sh
index 6de48208..980b9cf0 100755
--- a/script/test/cmd/tests_new.sh
+++ b/script/test/cmd/tests_new.sh
@@ -171,3 +171,19 @@ k8s_output="$KOMPOSE_ROOT/script/test/fixtures/multiple-files/output-k8s.json"
 ocp_output="$KOMPOSE_ROOT/script/test/fixtures/multiple-files/output-ocp.json"
 convert::expect_success_and_warning "$k8s_cmd" "$k8s_output"
 convert::expect_success "$ocp_cmd" "$ocp_output"
+
+# test health check
+k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/healthcheck/docker-compose-healthcheck.yaml convert --stdout -j --service-group-mode=label --with-kompose-annotation=false"
+os_cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/healthcheck/docker-compose-healthcheck.yaml convert --stdout -j --service-group-mode=label --with-kompose-annotation=false"
+k8s_output="$KOMPOSE_ROOT/script/test/fixtures/healthcheck/output-healthcheck-k8s.json"
+os_output="$KOMPOSE_ROOT/script/test/fixtures/healthcheck/output-healthcheck-os.json"
+convert::expect_success "$k8s_cmd" "$k8s_output"
+convert::expect_success "$os_cmd" "$os_output"
+
+# test statefulset
+k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/statefulset/docker-compose.yaml convert --stdout -j --with-kompose-annotation=false --controller statefulset"
+ocp_cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/statefulset/docker-compose.yaml convert --stdout -j --with-kompose-annotation=false --controller statefulset"
+k8s_output="$KOMPOSE_ROOT/script/test/fixtures/statefulset/output-k8s.json"
+ocp_output="$KOMPOSE_ROOT/script/test/fixtures/statefulset/output-os.json"
+convert::expect_success "$k8s_cmd" "$k8s_output"
+convert::expect_success "$ocp_cmd" "$ocp_output"
diff --git a/script/test/fixtures/deploy/placement/docker-compose-placement.yaml b/script/test/fixtures/deploy/placement/docker-compose-placement.yaml
index 4d1e6f8a..d48459fb 100644
--- a/script/test/fixtures/deploy/placement/docker-compose-placement.yaml
+++ b/script/test/fixtures/deploy/placement/docker-compose-placement.yaml
@@ -1,4 +1,4 @@
-version: "3"
+version: "3.3"
 services:
   redis:
     image: redis
@@ -10,4 +10,8 @@ services:
           - node.hostname == machine
           - engine.labels.operatingsystem == ubuntu 14.04
           - node.labels.foo != bar
-          - baz != qux
\ No newline at end of file
+          - baz != qux
+        preferences:
+          - spread: node.labels.zone
+          - spread: foo
+          - spread: node.labels.ssd
\ No newline at end of file
diff --git a/script/test/fixtures/deploy/placement/output-placement-k8s.json b/script/test/fixtures/deploy/placement/output-placement-k8s.json
index 5cd34d5b..321d5c83 100644
--- a/script/test/fixtures/deploy/placement/output-placement-k8s.json
+++ b/script/test/fixtures/deploy/placement/output-placement-k8s.json
@@ -76,24 +76,52 @@
                         {
                           "key": "kubernetes.io/hostname",
                           "operator": "In",
-                          "values": ["machine"]
+                          "values": [
+                            "machine"
+                          ]
                         },
                         {
-                          "key": "beta.kubernetes.io/os",
+                          "key": "kubernetes.io/os",
                           "operator": "In",
-                          "values": ["ubuntu 14.04"]
+                          "values": [
+                            "ubuntu 14.04"
+                          ]
                         },
                         {
                           "key": "foo",
                           "operator": "NotIn",
-                          "values": ["bar"]
+                          "values": [
+                            "bar"
+                          ]
                         }
                       ]
                     }
                   ]
                 }
               }
-            }
+            },
+            "topologySpreadConstraints": [
+              {
+                "maxSkew": 2,
+                "topologyKey": "zone",
+                "whenUnsatisfiable": "ScheduleAnyway",
+                "labelSelector": {
+                  "matchLabels": {
+                    "io.kompose.service": "redis"
+                  }
+                }
+              },
+              {
+                "maxSkew": 1,
+                "topologyKey": "ssd",
+                "whenUnsatisfiable": "ScheduleAnyway",
+                "labelSelector": {
+                  "matchLabels": {
+                    "io.kompose.service": "redis"
+                  }
+                }
+              }
+            ]
           }
         },
         "strategy": {}
diff --git a/script/test/fixtures/deploy/placement/output-placement-os.json b/script/test/fixtures/deploy/placement/output-placement-os.json
index cce844bf..1a12b827 100644
--- a/script/test/fixtures/deploy/placement/output-placement-os.json
+++ b/script/test/fixtures/deploy/placement/output-placement-os.json
@@ -96,24 +96,52 @@
                         {
                           "key": "kubernetes.io/hostname",
                           "operator": "In",
-                          "values": ["machine"]
+                          "values": [
+                            "machine"
+                          ]
                         },
                         {
-                          "key": "beta.kubernetes.io/os",
+                          "key": "kubernetes.io/os",
                           "operator": "In",
-                          "values": ["ubuntu 14.04"]
+                          "values": [
+                            "ubuntu 14.04"
+                          ]
                         },
                         {
                           "key": "foo",
                           "operator": "NotIn",
-                          "values": ["bar"]
+                          "values": [
+                            "bar"
+                          ]
                         }
                       ]
                     }
                   ]
                 }
               }
-            }
+            },
+            "topologySpreadConstraints": [
+              {
+                "maxSkew": 2,
+                "topologyKey": "zone",
+                "whenUnsatisfiable": "ScheduleAnyway",
+                "labelSelector": {
+                  "matchLabels": {
+                    "io.kompose.service": "redis"
+                  }
+                }
+              },
+              {
+                "maxSkew": 1,
+                "topologyKey": "ssd",
+                "whenUnsatisfiable": "ScheduleAnyway",
+                "labelSelector": {
+                  "matchLabels": {
+                    "io.kompose.service": "redis"
+                  }
+                }
+              }
+            ]
           }
         }
       },
diff --git a/script/test/fixtures/healthcheck/docker-compose-healthcheck.yaml b/script/test/fixtures/healthcheck/docker-compose-healthcheck.yaml
new file mode 100644
index 00000000..91df90fc
--- /dev/null
+++ b/script/test/fixtures/healthcheck/docker-compose-healthcheck.yaml
@@ -0,0 +1,69 @@
+version: "3.3"
+services:
+  # test exec
+  redis:
+    image: redis
+    ports:
+      - "6379"
+    healthcheck:
+      test: echo "liveness"
+      interval: 10s
+      timeout: 1s
+      retries: 5
+    labels:
+      kompose.service.healthcheck.readiness.test: echo "liveness"
+      kompose.service.healthcheck.readiness.interval: 10s
+      kompose.service.healthcheck.readiness.timeout: 1s
+      kompose.service.healthcheck.readiness.retries: 5
+
+  # test http get
+  postgresql:
+    image: postgresql
+    ports:
+      - "5432"
+    healthcheck:
+      interval: 10s
+      timeout: 1s
+      retries: 5
+    labels:
+      kompose.service.healthcheck.liveness.http_get_path: /health
+      kompose.service.healthcheck.liveness.http_get_port: 8080
+      kompose.service.healthcheck.readiness.http_get_path: /ready
+      kompose.service.healthcheck.readiness.http_get_port: 8080
+      kompose.service.healthcheck.readiness.interval: 10s
+      kompose.service.healthcheck.readiness.timeout: 1s
+      kompose.service.healthcheck.readiness.retries: 5
+
+  # test tcp socket
+  mongo:
+    image: mongo
+    ports:
+      - "27017"
+    healthcheck:
+      interval: 10s
+      timeout: 1s
+      retries: 5
+    labels:
+      kompose.service.group: "my-group"
+      kompose.service.healthcheck.liveness.tcp_port: 8080
+      kompose.service.healthcheck.readiness.tcp_port: 9090
+      kompose.service.healthcheck.readiness.interval: 10s
+      kompose.service.healthcheck.readiness.timeout: 1s
+      kompose.service.healthcheck.readiness.retries: 5
+
+  # test multiple service merge
+  mysql:
+    image: mysql
+    ports:
+      - "3306"
+    healthcheck:
+      interval: 11s
+      timeout: 2s
+      retries: 6
+    labels:
+      kompose.service.group: "my-group"
+      kompose.service.healthcheck.liveness.tcp_port: 8081
+      kompose.service.healthcheck.readiness.tcp_port: 9091
+      kompose.service.healthcheck.readiness.interval: 11s
+      kompose.service.healthcheck.readiness.timeout: 2s
+      kompose.service.healthcheck.readiness.retries: 6
\ No newline at end of file
diff --git a/script/test/fixtures/healthcheck/output-healthcheck-k8s.json b/script/test/fixtures/healthcheck/output-healthcheck-k8s.json
new file mode 100644
index 00000000..ef5a29b1
--- /dev/null
+++ b/script/test/fixtures/healthcheck/output-healthcheck-k8s.json
@@ -0,0 +1,401 @@
+{
+  "kind": "List",
+  "apiVersion": "v1",
+  "metadata": {},
+  "items": [
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mongo",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "my-group"
+        },
+        "annotations": {
+          "kompose.service.group": "my-group",
+          "kompose.service.healthcheck.liveness.tcp_port": "8080",
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.tcp_port": "9090",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "27017",
+            "port": 27017,
+            "targetPort": 27017
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "my-group"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mysql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "my-group"
+        },
+        "annotations": {
+          "kompose.service.group": "my-group",
+          "kompose.service.healthcheck.liveness.tcp_port": "8081",
+          "kompose.service.healthcheck.readiness.interval": "11s",
+          "kompose.service.healthcheck.readiness.retries": "6",
+          "kompose.service.healthcheck.readiness.tcp_port": "9091",
+          "kompose.service.healthcheck.readiness.timeout": "2s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "3306",
+            "port": 3306,
+            "targetPort": 3306
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "my-group"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "postgresql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "postgresql"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.liveness.http_get_path": "/health",
+          "kompose.service.healthcheck.liveness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.http_get_path": "/ready",
+          "kompose.service.healthcheck.readiness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "5432",
+            "port": 5432,
+            "targetPort": 5432
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "postgresql"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "redis",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "redis"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.test": "echo \"liveness\"",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "6379",
+            "port": 6379,
+            "targetPort": 6379
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "redis"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Deployment",
+      "apiVersion": "apps/v1",
+      "metadata": {
+        "name": "my-group",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "my-group"
+        },
+        "annotations": {
+          "kompose.service.group": "my-group",
+          "kompose.service.healthcheck.liveness.tcp_port": "8081",
+          "kompose.service.healthcheck.readiness.interval": "11s",
+          "kompose.service.healthcheck.readiness.retries": "6",
+          "kompose.service.healthcheck.readiness.tcp_port": "9091",
+          "kompose.service.healthcheck.readiness.timeout": "2s"
+        }
+      },
+      "spec": {
+        "replicas": 1,
+        "selector": {
+          "matchLabels": {
+            "io.kompose.service": "my-group"
+          }
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "my-group"
+            },
+            "annotations": {
+              "kompose.service.group": "my-group",
+              "kompose.service.healthcheck.liveness.tcp_port": "8080",
+              "kompose.service.healthcheck.readiness.interval": "10s",
+              "kompose.service.healthcheck.readiness.retries": "5",
+              "kompose.service.healthcheck.readiness.tcp_port": "9090",
+              "kompose.service.healthcheck.readiness.timeout": "1s"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "mongo",
+                "image": "mongo",
+                "ports": [
+                  {
+                    "containerPort": 27017
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "tcpSocket": {
+                    "port": 8080
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                },
+                "readinessProbe": {
+                  "tcpSocket": {
+                    "port": 9090
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                }
+              },
+              {
+                "name": "mysql",
+                "image": "mysql",
+                "ports": [
+                  {
+                    "containerPort": 3306
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "tcpSocket": {
+                    "port": 8081
+                  },
+                  "timeoutSeconds": 2,
+                  "periodSeconds": 11,
+                  "failureThreshold": 6
+                },
+                "readinessProbe": {
+                  "tcpSocket": {
+                    "port": 9091
+                  },
+                  "timeoutSeconds": 2,
+                  "periodSeconds": 11,
+                  "failureThreshold": 6
+                }
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        },
+        "strategy": {}
+      },
+      "status": {}
+    },
+    {
+      "kind": "Deployment",
+      "apiVersion": "apps/v1",
+      "metadata": {
+        "name": "postgresql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "postgresql"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.liveness.http_get_path": "/health",
+          "kompose.service.healthcheck.liveness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.http_get_path": "/ready",
+          "kompose.service.healthcheck.readiness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "replicas": 1,
+        "selector": {
+          "matchLabels": {
+            "io.kompose.service": "postgresql"
+          }
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "postgresql"
+            },
+            "annotations": {
+              "kompose.service.healthcheck.liveness.http_get_path": "/health",
+              "kompose.service.healthcheck.liveness.http_get_port": "8080",
+              "kompose.service.healthcheck.readiness.http_get_path": "/ready",
+              "kompose.service.healthcheck.readiness.http_get_port": "8080",
+              "kompose.service.healthcheck.readiness.interval": "10s",
+              "kompose.service.healthcheck.readiness.retries": "5",
+              "kompose.service.healthcheck.readiness.timeout": "1s"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "postgresql",
+                "image": "postgresql",
+                "ports": [
+                  {
+                    "containerPort": 5432
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "httpGet": {
+                    "path": "/health",
+                    "port": 8080
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                },
+                "readinessProbe": {
+                  "httpGet": {
+                    "path": "/ready",
+                    "port": 8080
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                }
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        },
+        "strategy": {}
+      },
+      "status": {}
+    },
+    {
+      "kind": "Deployment",
+      "apiVersion": "apps/v1",
+      "metadata": {
+        "name": "redis",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "redis"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.test": "echo \"liveness\"",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "replicas": 1,
+        "selector": {
+          "matchLabels": {
+            "io.kompose.service": "redis"
+          }
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "redis"
+            },
+            "annotations": {
+              "kompose.service.healthcheck.readiness.interval": "10s",
+              "kompose.service.healthcheck.readiness.retries": "5",
+              "kompose.service.healthcheck.readiness.test": "echo \"liveness\"",
+              "kompose.service.healthcheck.readiness.timeout": "1s"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "redis",
+                "image": "redis",
+                "ports": [
+                  {
+                    "containerPort": 6379
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "exec": {
+                    "command": [
+                      "echo \"liveness\""
+                    ]
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                },
+                "readinessProbe": {
+                  "exec": {
+                    "command": [
+                      "echo",
+                      "liveness"
+                    ]
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                }
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        },
+        "strategy": {}
+      },
+      "status": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/script/test/fixtures/healthcheck/output-healthcheck-os.json b/script/test/fixtures/healthcheck/output-healthcheck-os.json
new file mode 100644
index 00000000..bc733936
--- /dev/null
+++ b/script/test/fixtures/healthcheck/output-healthcheck-os.json
@@ -0,0 +1,660 @@
+{
+  "kind": "List",
+  "apiVersion": "v1",
+  "metadata": {},
+  "items": [
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mongo",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "mongo"
+        },
+        "annotations": {
+          "kompose.service.group": "my-group",
+          "kompose.service.healthcheck.liveness.tcp_port": "8080",
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.tcp_port": "9090",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "27017",
+            "port": 27017,
+            "targetPort": 27017
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "mongo"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mysql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "mysql"
+        },
+        "annotations": {
+          "kompose.service.group": "my-group",
+          "kompose.service.healthcheck.liveness.tcp_port": "8081",
+          "kompose.service.healthcheck.readiness.interval": "11s",
+          "kompose.service.healthcheck.readiness.retries": "6",
+          "kompose.service.healthcheck.readiness.tcp_port": "9091",
+          "kompose.service.healthcheck.readiness.timeout": "2s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "3306",
+            "port": 3306,
+            "targetPort": 3306
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "mysql"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "postgresql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "postgresql"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.liveness.http_get_path": "/health",
+          "kompose.service.healthcheck.liveness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.http_get_path": "/ready",
+          "kompose.service.healthcheck.readiness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "5432",
+            "port": 5432,
+            "targetPort": 5432
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "postgresql"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "redis",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "redis"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.test": "echo \"liveness\"",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "6379",
+            "port": 6379,
+            "targetPort": 6379
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "redis"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "DeploymentConfig",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mongo",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "mongo"
+        },
+        "annotations": {
+          "kompose.service.group": "my-group",
+          "kompose.service.healthcheck.liveness.tcp_port": "8080",
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.tcp_port": "9090",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "strategy": {
+          "resources": {}
+        },
+        "triggers": [
+          {
+            "type": "ConfigChange"
+          },
+          {
+            "type": "ImageChange",
+            "imageChangeParams": {
+              "automatic": true,
+              "containerNames": [
+                "mongo"
+              ],
+              "from": {
+                "kind": "ImageStreamTag",
+                "name": "mongo:latest"
+              }
+            }
+          }
+        ],
+        "replicas": 1,
+        "test": false,
+        "selector": {
+          "io.kompose.service": "mongo"
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "mongo"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "mongo",
+                "image": " ",
+                "ports": [
+                  {
+                    "containerPort": 27017
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "tcpSocket": {
+                    "port": 8080
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                },
+                "readinessProbe": {
+                  "tcpSocket": {
+                    "port": 9090
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                }
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        }
+      },
+      "status": {
+        "latestVersion": 0,
+        "observedGeneration": 0,
+        "replicas": 0,
+        "updatedReplicas": 0,
+        "availableReplicas": 0,
+        "unavailableReplicas": 0
+      }
+    },
+    {
+      "kind": "ImageStream",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mongo",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "mongo"
+        }
+      },
+      "spec": {
+        "lookupPolicy": {
+          "local": false
+        },
+        "tags": [
+          {
+            "name": "",
+            "annotations": null,
+            "from": {
+              "kind": "DockerImage",
+              "name": "mongo"
+            },
+            "generation": null,
+            "importPolicy": {},
+            "referencePolicy": {
+              "type": ""
+            }
+          }
+        ]
+      },
+      "status": {
+        "dockerImageRepository": ""
+      }
+    },
+    {
+      "kind": "DeploymentConfig",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mysql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "mysql"
+        },
+        "annotations": {
+          "kompose.service.group": "my-group",
+          "kompose.service.healthcheck.liveness.tcp_port": "8081",
+          "kompose.service.healthcheck.readiness.interval": "11s",
+          "kompose.service.healthcheck.readiness.retries": "6",
+          "kompose.service.healthcheck.readiness.tcp_port": "9091",
+          "kompose.service.healthcheck.readiness.timeout": "2s"
+        }
+      },
+      "spec": {
+        "strategy": {
+          "resources": {}
+        },
+        "triggers": [
+          {
+            "type": "ConfigChange"
+          },
+          {
+            "type": "ImageChange",
+            "imageChangeParams": {
+              "automatic": true,
+              "containerNames": [
+                "mysql"
+              ],
+              "from": {
+                "kind": "ImageStreamTag",
+                "name": "mysql:latest"
+              }
+            }
+          }
+        ],
+        "replicas": 1,
+        "test": false,
+        "selector": {
+          "io.kompose.service": "mysql"
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "mysql"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "mysql",
+                "image": " ",
+                "ports": [
+                  {
+                    "containerPort": 3306
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "tcpSocket": {
+                    "port": 8081
+                  },
+                  "timeoutSeconds": 2,
+                  "periodSeconds": 11,
+                  "failureThreshold": 6
+                },
+                "readinessProbe": {
+                  "tcpSocket": {
+                    "port": 9091
+                  },
+                  "timeoutSeconds": 2,
+                  "periodSeconds": 11,
+                  "failureThreshold": 6
+                }
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        }
+      },
+      "status": {
+        "latestVersion": 0,
+        "observedGeneration": 0,
+        "replicas": 0,
+        "updatedReplicas": 0,
+        "availableReplicas": 0,
+        "unavailableReplicas": 0
+      }
+    },
+    {
+      "kind": "ImageStream",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "mysql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "mysql"
+        }
+      },
+      "spec": {
+        "lookupPolicy": {
+          "local": false
+        },
+        "tags": [
+          {
+            "name": "",
+            "annotations": null,
+            "from": {
+              "kind": "DockerImage",
+              "name": "mysql"
+            },
+            "generation": null,
+            "importPolicy": {},
+            "referencePolicy": {
+              "type": ""
+            }
+          }
+        ]
+      },
+      "status": {
+        "dockerImageRepository": ""
+      }
+    },
+    {
+      "kind": "DeploymentConfig",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "postgresql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "postgresql"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.liveness.http_get_path": "/health",
+          "kompose.service.healthcheck.liveness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.http_get_path": "/ready",
+          "kompose.service.healthcheck.readiness.http_get_port": "8080",
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "strategy": {
+          "resources": {}
+        },
+        "triggers": [
+          {
+            "type": "ConfigChange"
+          },
+          {
+            "type": "ImageChange",
+            "imageChangeParams": {
+              "automatic": true,
+              "containerNames": [
+                "postgresql"
+              ],
+              "from": {
+                "kind": "ImageStreamTag",
+                "name": "postgresql:latest"
+              }
+            }
+          }
+        ],
+        "replicas": 1,
+        "test": false,
+        "selector": {
+          "io.kompose.service": "postgresql"
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "postgresql"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "postgresql",
+                "image": " ",
+                "ports": [
+                  {
+                    "containerPort": 5432
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "httpGet": {
+                    "path": "/health",
+                    "port": 8080
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                },
+                "readinessProbe": {
+                  "httpGet": {
+                    "path": "/ready",
+                    "port": 8080
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                }
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        }
+      },
+      "status": {
+        "latestVersion": 0,
+        "observedGeneration": 0,
+        "replicas": 0,
+        "updatedReplicas": 0,
+        "availableReplicas": 0,
+        "unavailableReplicas": 0
+      }
+    },
+    {
+      "kind": "ImageStream",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "postgresql",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "postgresql"
+        }
+      },
+      "spec": {
+        "lookupPolicy": {
+          "local": false
+        },
+        "tags": [
+          {
+            "name": "",
+            "annotations": null,
+            "from": {
+              "kind": "DockerImage",
+              "name": "postgresql"
+            },
+            "generation": null,
+            "importPolicy": {},
+            "referencePolicy": {
+              "type": ""
+            }
+          }
+        ]
+      },
+      "status": {
+        "dockerImageRepository": ""
+      }
+    },
+    {
+      "kind": "DeploymentConfig",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "redis",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "redis"
+        },
+        "annotations": {
+          "kompose.service.healthcheck.readiness.interval": "10s",
+          "kompose.service.healthcheck.readiness.retries": "5",
+          "kompose.service.healthcheck.readiness.test": "echo \"liveness\"",
+          "kompose.service.healthcheck.readiness.timeout": "1s"
+        }
+      },
+      "spec": {
+        "strategy": {
+          "resources": {}
+        },
+        "triggers": [
+          {
+            "type": "ConfigChange"
+          },
+          {
+            "type": "ImageChange",
+            "imageChangeParams": {
+              "automatic": true,
+              "containerNames": [
+                "redis"
+              ],
+              "from": {
+                "kind": "ImageStreamTag",
+                "name": "redis:latest"
+              }
+            }
+          }
+        ],
+        "replicas": 1,
+        "test": false,
+        "selector": {
+          "io.kompose.service": "redis"
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "redis"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "redis",
+                "image": " ",
+                "ports": [
+                  {
+                    "containerPort": 6379
+                  }
+                ],
+                "resources": {},
+                "livenessProbe": {
+                  "exec": {
+                    "command": [
+                      "echo \"liveness\""
+                    ]
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                },
+                "readinessProbe": {
+                  "exec": {
+                    "command": [
+                      "echo",
+                      "liveness"
+                    ]
+                  },
+                  "timeoutSeconds": 1,
+                  "periodSeconds": 10,
+                  "failureThreshold": 5
+                }
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        }
+      },
+      "status": {
+        "latestVersion": 0,
+        "observedGeneration": 0,
+        "replicas": 0,
+        "updatedReplicas": 0,
+        "availableReplicas": 0,
+        "unavailableReplicas": 0
+      }
+    },
+    {
+      "kind": "ImageStream",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "redis",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "redis"
+        }
+      },
+      "spec": {
+        "lookupPolicy": {
+          "local": false
+        },
+        "tags": [
+          {
+            "name": "",
+            "annotations": null,
+            "from": {
+              "kind": "DockerImage",
+              "name": "redis"
+            },
+            "generation": null,
+            "importPolicy": {},
+            "referencePolicy": {
+              "type": ""
+            }
+          }
+        ]
+      },
+      "status": {
+        "dockerImageRepository": ""
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/script/test/fixtures/statefulset/docker-compose.yaml b/script/test/fixtures/statefulset/docker-compose.yaml
new file mode 100644
index 00000000..489a2c43
--- /dev/null
+++ b/script/test/fixtures/statefulset/docker-compose.yaml
@@ -0,0 +1,33 @@
+version: "3"
+    
+services:
+  db:
+    image: mysql:5.7
+    volumes:
+      - db_data:/var/lib/mysql
+    restart: always
+    environment:
+      MYSQL_ROOT_PASSWORD: somewordpress
+      MYSQL_DATABASE: wordpress
+      MYSQL_USER: wordpress
+      MYSQL_PASSWORD: wordpress
+    ports:
+      - "3306:3306"
+    
+  wordpress:
+    depends_on:
+      - db
+    image: wordpress:latest
+    volumes:
+      - wordpress_data:/var/www/html
+    ports:
+      - "8000:80"
+    restart: always
+    environment:
+      WORDPRESS_DB_HOST: db:3306
+      WORDPRESS_DB_USER: wordpress
+      WORDPRESS_DB_PASSWORD: wordpress
+      WORDPRESS_DB_NAME: wordpress
+volumes:
+  db_data: {}
+  wordpress_data: {}
\ No newline at end of file
diff --git a/script/test/fixtures/statefulset/output-k8s.json b/script/test/fixtures/statefulset/output-k8s.json
new file mode 100644
index 00000000..5c1e339a
--- /dev/null
+++ b/script/test/fixtures/statefulset/output-k8s.json
@@ -0,0 +1,249 @@
+{
+  "kind": "List",
+  "apiVersion": "v1",
+  "metadata": {},
+  "items": [
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "db",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "db"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "3306",
+            "port": 3306,
+            "targetPort": 3306
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "db"
+        },
+        "clusterIP": "None",
+        "type": "ClusterIP"
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "wordpress",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "wordpress"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "8000",
+            "port": 8000,
+            "targetPort": 80
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "wordpress"
+        },
+        "clusterIP": "None",
+        "type": "ClusterIP"
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "StatefulSet",
+      "apiVersion": "apps/v1",
+      "metadata": {
+        "name": "db",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "db"
+        }
+      },
+      "spec": {
+        "replicas": 1,
+        "selector": {
+          "matchLabels": {
+            "io.kompose.service": "db"
+          }
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "db"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "db",
+                "image": "mysql:5.7",
+                "ports": [
+                  {
+                    "containerPort": 3306
+                  }
+                ],
+                "env": [
+                  {
+                    "name": "MYSQL_DATABASE",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "MYSQL_PASSWORD",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "MYSQL_ROOT_PASSWORD",
+                    "value": "somewordpress"
+                  },
+                  {
+                    "name": "MYSQL_USER",
+                    "value": "wordpress"
+                  }
+                ],
+                "resources": {},
+                "volumeMounts": [
+                  {
+                    "name": "db-data",
+                    "mountPath": "/var/lib/mysql"
+                  }
+                ]
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        },
+        "volumeClaimTemplates": [
+          {
+            "metadata": {
+              "name": "db-data",
+              "creationTimestamp": null,
+              "labels": {
+                "io.kompose.service": "db-data"
+              }
+            },
+            "spec": {
+              "accessModes": [
+                "ReadWriteOnce"
+              ],
+              "resources": {
+                "requests": {
+                  "storage": "100Mi"
+                }
+              }
+            },
+            "status": {}
+          }
+        ],
+        "serviceName": "db",
+        "updateStrategy": {}
+      },
+      "status": {
+        "replicas": 0
+      }
+    },
+    {
+      "kind": "StatefulSet",
+      "apiVersion": "apps/v1",
+      "metadata": {
+        "name": "wordpress",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "wordpress"
+        }
+      },
+      "spec": {
+        "replicas": 1,
+        "selector": {
+          "matchLabels": {
+            "io.kompose.service": "wordpress"
+          }
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "wordpress"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "wordpress",
+                "image": "wordpress:latest",
+                "ports": [
+                  {
+                    "containerPort": 80
+                  }
+                ],
+                "env": [
+                  {
+                    "name": "WORDPRESS_DB_HOST",
+                    "value": "db:3306"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_NAME",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_PASSWORD",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_USER",
+                    "value": "wordpress"
+                  }
+                ],
+                "resources": {},
+                "volumeMounts": [
+                  {
+                    "name": "wordpress-data",
+                    "mountPath": "/var/www/html"
+                  }
+                ]
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        },
+        "volumeClaimTemplates": [
+          {
+            "metadata": {
+              "name": "wordpress-data",
+              "creationTimestamp": null,
+              "labels": {
+                "io.kompose.service": "wordpress-data"
+              }
+            },
+            "spec": {
+              "accessModes": [
+                "ReadWriteOnce"
+              ],
+              "resources": {
+                "requests": {
+                  "storage": "100Mi"
+                }
+              }
+            },
+            "status": {}
+          }
+        ],
+        "serviceName": "wordpress",
+        "updateStrategy": {}
+      },
+      "status": {
+        "replicas": 0
+      }
+    }
+  ]
+}
diff --git a/script/test/fixtures/statefulset/output-os.json b/script/test/fixtures/statefulset/output-os.json
new file mode 100644
index 00000000..0c40d367
--- /dev/null
+++ b/script/test/fixtures/statefulset/output-os.json
@@ -0,0 +1,503 @@
+{
+  "kind": "List",
+  "apiVersion": "v1",
+  "metadata": {},
+  "items": [
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "db",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "db"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "3306",
+            "port": 3306,
+            "targetPort": 3306
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "db"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "wordpress",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "wordpress"
+        }
+      },
+      "spec": {
+        "ports": [
+          {
+            "name": "8000",
+            "port": 8000,
+            "targetPort": 80
+          }
+        ],
+        "selector": {
+          "io.kompose.service": "wordpress"
+        }
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    },
+    {
+      "kind": "StatefulSet",
+      "apiVersion": "apps/v1",
+      "metadata": {
+        "name": "db",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "db"
+        }
+      },
+      "spec": {
+        "replicas": 1,
+        "selector": {
+          "matchLabels": {
+            "io.kompose.service": "db"
+          }
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "db"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "db",
+                "image": "mysql:5.7",
+                "ports": [
+                  {
+                    "containerPort": 3306
+                  }
+                ],
+                "env": [
+                  {
+                    "name": "MYSQL_DATABASE",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "MYSQL_PASSWORD",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "MYSQL_ROOT_PASSWORD",
+                    "value": "somewordpress"
+                  },
+                  {
+                    "name": "MYSQL_USER",
+                    "value": "wordpress"
+                  }
+                ],
+                "resources": {},
+                "volumeMounts": [
+                  {
+                    "name": "db-data",
+                    "mountPath": "/var/lib/mysql"
+                  }
+                ]
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        },
+        "volumeClaimTemplates": [
+          {
+            "metadata": {
+              "name": "db-data",
+              "creationTimestamp": null,
+              "labels": {
+                "io.kompose.service": "db-data"
+              }
+            },
+            "spec": {
+              "accessModes": [
+                "ReadWriteOnce"
+              ],
+              "resources": {
+                "requests": {
+                  "storage": "100Mi"
+                }
+              }
+            },
+            "status": {}
+          }
+        ],
+        "serviceName": "db",
+        "updateStrategy": {}
+      },
+      "status": {
+        "replicas": 0
+      }
+    },
+    {
+      "kind": "DeploymentConfig",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "db",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "db"
+        }
+      },
+      "spec": {
+        "strategy": {
+          "type": "Recreate",
+          "resources": {}
+        },
+        "triggers": [
+          {
+            "type": "ConfigChange"
+          },
+          {
+            "type": "ImageChange",
+            "imageChangeParams": {
+              "automatic": true,
+              "containerNames": [
+                "db"
+              ],
+              "from": {
+                "kind": "ImageStreamTag",
+                "name": "db:5.7"
+              }
+            }
+          }
+        ],
+        "replicas": 1,
+        "test": false,
+        "selector": {
+          "io.kompose.service": "db"
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "db"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "db",
+                "image": " ",
+                "ports": [
+                  {
+                    "containerPort": 3306
+                  }
+                ],
+                "env": [
+                  {
+                    "name": "MYSQL_DATABASE",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "MYSQL_PASSWORD",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "MYSQL_ROOT_PASSWORD",
+                    "value": "somewordpress"
+                  },
+                  {
+                    "name": "MYSQL_USER",
+                    "value": "wordpress"
+                  }
+                ],
+                "resources": {},
+                "volumeMounts": [
+                  {
+                    "name": "db-data",
+                    "mountPath": "/var/lib/mysql"
+                  }
+                ]
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        }
+      },
+      "status": {
+        "latestVersion": 0,
+        "observedGeneration": 0,
+        "replicas": 0,
+        "updatedReplicas": 0,
+        "availableReplicas": 0,
+        "unavailableReplicas": 0
+      }
+    },
+    {
+      "kind": "ImageStream",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "db",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "db"
+        }
+      },
+      "spec": {
+        "lookupPolicy": {
+          "local": false
+        },
+        "tags": [
+          {
+            "name": "",
+            "annotations": null,
+            "from": {
+              "kind": "DockerImage",
+              "name": "mysql:5.7"
+            },
+            "generation": null,
+            "importPolicy": {},
+            "referencePolicy": {
+              "type": ""
+            }
+          }
+        ]
+      },
+      "status": {
+        "dockerImageRepository": ""
+      }
+    },
+    {
+      "kind": "StatefulSet",
+      "apiVersion": "apps/v1",
+      "metadata": {
+        "name": "wordpress",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "wordpress"
+        }
+      },
+      "spec": {
+        "replicas": 1,
+        "selector": {
+          "matchLabels": {
+            "io.kompose.service": "wordpress"
+          }
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "wordpress"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "wordpress",
+                "image": "wordpress:latest",
+                "ports": [
+                  {
+                    "containerPort": 80
+                  }
+                ],
+                "env": [
+                  {
+                    "name": "WORDPRESS_DB_HOST",
+                    "value": "db:3306"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_NAME",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_PASSWORD",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_USER",
+                    "value": "wordpress"
+                  }
+                ],
+                "resources": {},
+                "volumeMounts": [
+                  {
+                    "name": "wordpress-data",
+                    "mountPath": "/var/www/html"
+                  }
+                ]
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        },
+        "volumeClaimTemplates": [
+          {
+            "metadata": {
+              "name": "wordpress-data",
+              "creationTimestamp": null,
+              "labels": {
+                "io.kompose.service": "wordpress-data"
+              }
+            },
+            "spec": {
+              "accessModes": [
+                "ReadWriteOnce"
+              ],
+              "resources": {
+                "requests": {
+                  "storage": "100Mi"
+                }
+              }
+            },
+            "status": {}
+          }
+        ],
+        "serviceName": "wordpress",
+        "updateStrategy": {}
+      },
+      "status": {
+        "replicas": 0
+      }
+    },
+    {
+      "kind": "DeploymentConfig",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "wordpress",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "wordpress"
+        }
+      },
+      "spec": {
+        "strategy": {
+          "type": "Recreate",
+          "resources": {}
+        },
+        "triggers": [
+          {
+            "type": "ConfigChange"
+          },
+          {
+            "type": "ImageChange",
+            "imageChangeParams": {
+              "automatic": true,
+              "containerNames": [
+                "wordpress"
+              ],
+              "from": {
+                "kind": "ImageStreamTag",
+                "name": "wordpress:latest"
+              }
+            }
+          }
+        ],
+        "replicas": 1,
+        "test": false,
+        "selector": {
+          "io.kompose.service": "wordpress"
+        },
+        "template": {
+          "metadata": {
+            "creationTimestamp": null,
+            "labels": {
+              "io.kompose.service": "wordpress"
+            }
+          },
+          "spec": {
+            "containers": [
+              {
+                "name": "wordpress",
+                "image": " ",
+                "ports": [
+                  {
+                    "containerPort": 80
+                  }
+                ],
+                "env": [
+                  {
+                    "name": "WORDPRESS_DB_HOST",
+                    "value": "db:3306"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_NAME",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_PASSWORD",
+                    "value": "wordpress"
+                  },
+                  {
+                    "name": "WORDPRESS_DB_USER",
+                    "value": "wordpress"
+                  }
+                ],
+                "resources": {},
+                "volumeMounts": [
+                  {
+                    "name": "wordpress-data",
+                    "mountPath": "/var/www/html"
+                  }
+                ]
+              }
+            ],
+            "restartPolicy": "Always"
+          }
+        }
+      },
+      "status": {
+        "latestVersion": 0,
+        "observedGeneration": 0,
+        "replicas": 0,
+        "updatedReplicas": 0,
+        "availableReplicas": 0,
+        "unavailableReplicas": 0
+      }
+    },
+    {
+      "kind": "ImageStream",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "wordpress",
+        "creationTimestamp": null,
+        "labels": {
+          "io.kompose.service": "wordpress"
+        }
+      },
+      "spec": {
+        "lookupPolicy": {
+          "local": false
+        },
+        "tags": [
+          {
+            "name": "",
+            "annotations": null,
+            "from": {
+              "kind": "DockerImage",
+              "name": "wordpress:latest"
+            },
+            "generation": null,
+            "importPolicy": {},
+            "referencePolicy": {
+              "type": ""
+            }
+          }
+        ]
+      },
+      "status": {
+        "dockerImageRepository": ""
+      }
+    }
+  ]
+}
diff --git a/script/test/fixtures/unused/v3/output-deploy-k8s.json b/script/test/fixtures/unused/v3/output-deploy-k8s.json
index bce5c3d5..e2a19838 100644
--- a/script/test/fixtures/unused/v3/output-deploy-k8s.json
+++ b/script/test/fixtures/unused/v3/output-deploy-k8s.json
@@ -89,7 +89,7 @@
                           "values": ["machine"]
                         },
                         {
-                          "key": "beta.kubernetes.io/os",
+                          "key": "kubernetes.io/oss",
                           "operator": "In",
                           "values": ["ubuntu 14.04"]
                         },
diff --git a/script/test/fixtures/unused/v3/output-deploy-os.json b/script/test/fixtures/unused/v3/output-deploy-os.json
index e3d0efde..8946c070 100644
--- a/script/test/fixtures/unused/v3/output-deploy-os.json
+++ b/script/test/fixtures/unused/v3/output-deploy-os.json
@@ -104,7 +104,7 @@
                           "values": ["machine"]
                         },
                         {
-                          "key": "beta.kubernetes.io/os",
+                          "key": "kubernetes.io/os",
                           "operator": "In",
                           "values": ["ubuntu 14.04"]
                         },