Repository: kubernetes/kompose
Pull request #1848 (2024-03-30 20:31:17)

add networkmode service:

#### What type of PR is this?

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
-->
/kind feature

#### What this PR does / why we need it:
adjusting the network mode of services in docker compose (services:) and generate a mapping of deployments based on the network mode of each service merging containers into the destination deployment, and removing transferred deployments

compose.yaml example:

```
version: '3'

services:
  client:
    image: busybox
    command: ["sleep", "infinity"]
    container_name: threats-client
    restart: unless-stopped
    ports:
      - '8080:8080'

  server:
    image: busybox
    command: ["sleep", "infinity"]
    container_name: threats-server
    restart: unless-stopped
    network_mode: service:client
```
converted to add two container inside client-deployment:

```
....
 spec:
      containers:
        - args:
            - sleep
            - infinity
          image: busybox
          name: threats-client
          ports:
            - containerPort: 8080
              hostPort: 8080
              protocol: TCP
        - args:
            - sleep
            - infinity
          image: busybox
          name: threats-server
      restartPolicy: Always
```

#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Add feature commented in #1846

#### Special notes for your reviewer:



Patch:
diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go
index 399ceb9e..95758092 100644
--- a/pkg/kobject/kobject.go
+++ b/pkg/kobject/kobject.go
@@ -118,6 +118,7 @@ type ServiceConfig struct {
 	ReadOnly                      bool               `compose:"read_only"`
 	Args                          []string           `compose:"args"`
 	VolList                       []string           `compose:"volumes"`
+	NetworkMode                   string             `compose:"network_mode"`
 	Network                       []string           `compose:"network"`
 	Labels                        map[string]string  `compose:"labels"`
 	Annotations                   map[string]string  `compose:""`
diff --git a/pkg/loader/compose/compose.go b/pkg/loader/compose/compose.go
index 2eaa5dc1..9a4d2513 100644
--- a/pkg/loader/compose/compose.go
+++ b/pkg/loader/compose/compose.go
@@ -487,6 +487,7 @@ func dockerComposeToKomposeMapping(composeObject *types.Project) (kobject.Kompos
 		serviceConfig.HostName = composeServiceConfig.Hostname
 		serviceConfig.DomainName = composeServiceConfig.DomainName
 		serviceConfig.Secrets = composeServiceConfig.Secrets
+		serviceConfig.NetworkMode = composeServiceConfig.NetworkMode
 
 		if composeServiceConfig.StopGracePeriod != nil {
 			serviceConfig.StopGracePeriod = composeServiceConfig.StopGracePeriod.String()
diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go
index 3369e1d1..adf83f06 100644
--- a/pkg/transformer/kubernetes/k8sutils.go
+++ b/pkg/transformer/kubernetes/k8sutils.go
@@ -72,6 +72,15 @@ type HpaValues struct {
 	MemoryUtilization int32
 }
 
+const (
+	NetworkModeService = "service:"
+)
+
+type DeploymentMapping struct {
+	SourceDeploymentName string
+	TargetDeploymentName string
+}
+
 /**
  * Generate Helm Chart configuration
  */
@@ -1251,3 +1260,101 @@ func setVolumeAccessMode(mode string, volumeAccesMode []api.PersistentVolumeAcce
 
 	return volumeAccesMode
 }
+
+// fixNetworkModeToService is responsible for adjusting the network mode of services in docker compose (services:)
+// generate a mapping of deployments based on the network mode of each service
+// merging containers into the destination deployment, and removing transferred deployments
+func (k *Kubernetes) fixNetworkModeToService(objects *[]runtime.Object, services map[string]kobject.ServiceConfig) {
+	deploymentMappings := searchNetworkModeToService(services)
+	if len(deploymentMappings) == 0 {
+		return
+	}
+	mergeContainersIntoDestinationDeployment(deploymentMappings, objects)
+	removeDeploymentTransfered(deploymentMappings, objects)
+}
+
+// mergeContainersIntoDestinationDeployment takes a list of deployment mappings and a list of runtime objects
+// and merges containers from source deployment into the destination deployment
+func mergeContainersIntoDestinationDeployment(deploymentMappings []DeploymentMapping, objects *[]runtime.Object) {
+	for _, currentDeploymentMap := range deploymentMappings {
+		addContainersFromSourceToTargetDeployment(objects, currentDeploymentMap)
+	}
+}
+
+// addContainersFromSourceToTargetDeployment adds containers from the source deployment
+// if current deployment name matches source deployment name
+func addContainersFromSourceToTargetDeployment(objects *[]runtime.Object, currentDeploymentMap DeploymentMapping) {
+	for _, obj := range *objects {
+		if deploy, ok := obj.(*appsv1.Deployment); ok {
+			if deploy.ObjectMeta.Name == currentDeploymentMap.SourceDeploymentName {
+				addContainersToTargetDeployment(objects, deploy.Spec.Template.Spec.Containers, currentDeploymentMap.TargetDeploymentName)
+			}
+		}
+	}
+}
+
+// addContainersToTargetDeployment takes
+// - list of runtime objects
+// - list of containers to append
+// - deployment name to transfer
+// appends the containers to the target deployment if its name matches
+func addContainersToTargetDeployment(objects *[]runtime.Object, containersToAppend []api.Container, nameDeploymentToTransfer string) {
+	for _, obj := range *objects {
+		if deploy, ok := obj.(*appsv1.Deployment); ok {
+			if deploy.ObjectMeta.Name == nameDeploymentToTransfer {
+				deploy.Spec.Template.Spec.Containers = append(deploy.Spec.Template.Spec.Containers, containersToAppend...)
+			}
+		}
+	}
+}
+
+// searchNetworkModeToService iterates over services and checking their network mode service:
+// its separates over process of transferring containers,
+// it determines where each container should be removed from and where it should be added to
+func searchNetworkModeToService(services map[string]kobject.ServiceConfig) (deploymentMappings []DeploymentMapping) {
+	deploymentMappings = []DeploymentMapping{}
+	for _, service := range services {
+		if !strings.Contains(service.NetworkMode, NetworkModeService) {
+			continue
+		}
+		splitted := strings.Split(service.NetworkMode, ":")
+		if len(splitted) < 2 {
+			continue
+		}
+		deploymentMappings = append(deploymentMappings, DeploymentMapping{
+			SourceDeploymentName: service.Name,
+			TargetDeploymentName: splitted[1],
+		})
+	}
+	return deploymentMappings
+}
+
+// removeDeploymentTransfered iterates over a list of DeploymentMapping and
+// removes each deployment that marked in deploymentMappings
+func removeDeploymentTransfered(deploymentMappings []DeploymentMapping, objects *[]runtime.Object) {
+	for _, currentDeploymentMap := range deploymentMappings {
+		removeTargetDeployment(objects, currentDeploymentMap.SourceDeploymentName)
+	}
+}
+
+// removeTargetDeployment iterates over a list of runtime objects
+// and removes the target deployment from the list
+func removeTargetDeployment(objects *[]runtime.Object, targetDeploymentName string) {
+	for i := len(*objects) - 1; i >= 0; i-- {
+		if deploy, ok := (*objects)[i].(*appsv1.Deployment); ok {
+			if deploy.ObjectMeta.Name == targetDeploymentName {
+				*objects = removeFromSlice(*objects, (*objects)[i])
+			}
+		}
+	}
+}
+
+// removeFromSlice removes a specific object from a slice of runtime objects and returns the updated slice
+func removeFromSlice(objects []runtime.Object, objectToRemove runtime.Object) []runtime.Object {
+	for i, currentObject := range objects {
+		if reflect.DeepEqual(currentObject, objectToRemove) {
+			return append(objects[:i], objects[i+1:]...)
+		}
+	}
+	return objects
+}
diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go
index a31d0eed..65796a80 100644
--- a/pkg/transformer/kubernetes/kubernetes.go
+++ b/pkg/transformer/kubernetes/kubernetes.go
@@ -1678,6 +1678,7 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
 		transformer.AssignNamespaceToObjects(&allobjects, komposeObject.Namespace)
 	}
 	// k.FixWorkloadVersion(&allobjects)
+	k.fixNetworkModeToService(&allobjects, komposeObject.ServiceConfigs)
 	return allobjects, nil
 }
 


Test patch:
diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go
index 20b86056..8619192e 100644
--- a/pkg/transformer/kubernetes/k8sutils_test.go
+++ b/pkg/transformer/kubernetes/k8sutils_test.go
@@ -33,6 +33,7 @@ import (
 	api "k8s.io/api/core/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 )
 
 /*
@@ -2440,3 +2441,1396 @@ func Test_checkIsEmptyDir(t *testing.T) {
 		})
 	}
 }
+
+func Test_removeFromSlice(t *testing.T) {
+	type args struct {
+		objects        []runtime.Object
+		objectToRemove runtime.Object
+	}
+	tests := []struct {
+		name string
+		args args
+		want []runtime.Object
+	}{
+		{
+			name: "remove object in the middle",
+			args: args{
+				objects: []runtime.Object{
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				objectToRemove: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+			},
+			want: []runtime.Object{
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove objects in the middle and last object",
+			args: args{
+				objects: []runtime.Object{
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+				},
+				objectToRemove: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+			},
+			want: []runtime.Object{
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+			},
+		},
+		{
+			name: "remove 1 object",
+			args: args{
+				objects: []runtime.Object{
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				objectToRemove: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+			},
+			want: []runtime.Object{
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove object at the beginning",
+			args: args{
+				objects: []runtime.Object{
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				objectToRemove: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+			},
+			want: []runtime.Object{
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove object at the end",
+			args: args{
+				objects: []runtime.Object{
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+				},
+				objectToRemove: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+			},
+			want: []runtime.Object{
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove object that doesn't exist",
+			args: args{
+				objects: []runtime.Object{
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				objectToRemove: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "missing"}},
+			},
+			want: []runtime.Object{
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove object from empty slice",
+			args: args{
+				objects:        []runtime.Object{},
+				objectToRemove: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+			},
+			want: []runtime.Object{},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := removeFromSlice(tt.args.objects, tt.args.objectToRemove); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("removeFromSlice() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_removeTargetDeployment(t *testing.T) {
+	type args struct {
+		objects              *[]runtime.Object
+		targetDeploymentName string
+	}
+	tests := []struct {
+		name string
+		args args
+		want *[]runtime.Object
+	}{
+		{
+			name: "remove middle object",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				targetDeploymentName: "remove",
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove 2 objects from slice",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				targetDeploymentName: "remove",
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove 2 object from slice, only persist last one",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				targetDeploymentName: "remove",
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove target deployment from an empty slice",
+			args: args{
+				objects:              &[]runtime.Object{},
+				targetDeploymentName: "remove",
+			},
+			want: &[]runtime.Object{},
+		},
+		{
+			name: "remove target deployment that does not exist",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				targetDeploymentName: "missing",
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+		{
+			name: "remove target deployment from a slice with only the target deployment",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+				},
+				targetDeploymentName: "remove",
+			},
+			want: &[]runtime.Object{},
+		},
+		{
+			name: "remove all targets",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "remove"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+				targetDeploymentName: "remove",
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			removeTargetDeployment(tt.args.objects, tt.args.targetDeploymentName)
+			if !reflect.DeepEqual(tt.args.objects, tt.want) {
+				t.Errorf("removeFromSlice() = %v, want %v", tt.args.objects, tt.want)
+			}
+		})
+	}
+}
+
+func Test_removeDeploymentTransfered(t *testing.T) {
+	type args struct {
+		deploymentMappings []DeploymentMapping
+		objects            *[]runtime.Object
+	}
+	tests := []struct {
+		name string
+		args args
+		want *[]runtime.Object
+	}{
+		{
+			name: "remove deployment already transferred",
+			args: args{
+				deploymentMappings: []DeploymentMapping{
+					{
+						TargetDeploymentName: "app",
+						SourceDeploymentName: "db",
+					},
+				},
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+			},
+		},
+		{
+			name: "remove multiple deployments already transferred",
+			args: args{
+				deploymentMappings: []DeploymentMapping{
+					{
+						TargetDeploymentName: "app",
+						SourceDeploymentName: "db",
+					},
+					{
+						TargetDeploymentName: "web",
+						SourceDeploymentName: "cache",
+					},
+				},
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "web"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cache"}},
+				},
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "web"}},
+			},
+		},
+		{
+			name: "remove deployment transferred with multiple containers",
+			args: args{
+				deploymentMappings: []DeploymentMapping{
+					{
+						TargetDeploymentName: "app",
+						SourceDeploymentName: "db",
+					},
+				},
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+			},
+		},
+		{
+			name: "remove deployment transferred with different container names",
+			args: args{
+				deploymentMappings: []DeploymentMapping{
+					{
+						TargetDeploymentName: "app",
+						SourceDeploymentName: "db",
+					},
+				},
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "db"}},
+				},
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+			},
+		},
+		{
+			name: "remove deployment transferred when no matching source deployment",
+			args: args{
+				deploymentMappings: []DeploymentMapping{
+					{
+						TargetDeploymentName: "app",
+						SourceDeploymentName: "db",
+					},
+				},
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+				},
+			},
+			want: &[]runtime.Object{
+				&appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "app"}},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			removeDeploymentTransfered(tt.args.deploymentMappings, tt.args.objects)
+			if !reflect.DeepEqual(tt.args.objects, tt.want) {
+				t.Errorf("removeFromSlice() = %v, want %v", tt.args.objects, tt.want)
+			}
+		})
+	}
+}
+
+func Test_searchNetworkModeToService(t *testing.T) {
+	tests := []struct {
+		name     string
+		services map[string]kobject.ServiceConfig
+		want     []DeploymentMapping
+	}{
+		{
+			name: "search network mode to service",
+			services: map[string]kobject.ServiceConfig{
+				"app": {
+					Name: "app",
+				},
+				"db": {
+					Name:        "db",
+					NetworkMode: "service:app",
+				},
+			},
+			want: []DeploymentMapping{
+				{
+					SourceDeploymentName: "db",
+					TargetDeploymentName: "app",
+				},
+			},
+		},
+		{
+			name: "error and not set service:app",
+			services: map[string]kobject.ServiceConfig{
+				"app": {
+					Name: "app",
+				},
+				"db": {
+					Name: "db",
+				},
+			},
+			want: []DeploymentMapping{},
+		},
+		{
+			name: "search network mode to service with multiple source deployments",
+			services: map[string]kobject.ServiceConfig{
+				"app": {
+					Name: "app",
+				},
+				"db1": {
+					Name:        "db1",
+					NetworkMode: "service:app",
+				},
+				"db2": {
+					Name:        "db2",
+					NetworkMode: "service:app",
+				},
+			},
+			want: []DeploymentMapping{
+				{
+					SourceDeploymentName: "db1",
+					TargetDeploymentName: "app",
+				},
+				{
+					SourceDeploymentName: "db2",
+					TargetDeploymentName: "app",
+				},
+			},
+		},
+		{
+			name: "search network mode to service with multiple target deployments",
+			services: map[string]kobject.ServiceConfig{
+				"app1": {
+					Name: "app1",
+				},
+				"app2": {
+					Name: "app2",
+				},
+				"db": {
+					Name:        "db",
+					NetworkMode: "service:app1",
+				},
+			},
+			want: []DeploymentMapping{
+				{
+					SourceDeploymentName: "db",
+					TargetDeploymentName: "app1",
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if gotDeploymentMappings := searchNetworkModeToService(tt.services); !reflect.DeepEqual(gotDeploymentMappings, tt.want) {
+				t.Errorf("searchNetworkModeToService() = %v, want %v", gotDeploymentMappings, tt.want)
+			}
+		})
+	}
+}
+
+func Test_addContainersToTargetDeployment(t *testing.T) {
+	k := Kubernetes{}
+
+	appK, err := k.Transform(
+		kobject.KomposeObject{
+			ServiceConfigs: map[string]kobject.ServiceConfig{"app": {
+				ContainerName: "app",
+				Image:         "image",
+			},
+			},
+		}, kobject.ConvertOptions{CreateD: true, Replicas: 3})
+	if err != nil {
+		t.Error(errors.Wrap(err, "k.Transform failed"))
+	}
+
+	dbK, err := k.Transform(
+		kobject.KomposeObject{
+			ServiceConfigs: map[string]kobject.ServiceConfig{"db": {
+				ContainerName: "db",
+				Image:         "image",
+				NetworkMode:   "service:app",
+			},
+			},
+		}, kobject.ConvertOptions{CreateD: true, Replicas: 3})
+	if err != nil {
+		t.Error(errors.Wrap(err, "k.Transform failed"))
+	}
+
+	type args struct {
+		objects                  []runtime.Object
+		containersToAppend       []api.Container
+		nameDeploymentToTransfer string
+	}
+	const (
+		FirstObject  = 0
+		SecondObject = 1
+	)
+	tests := []struct {
+		name         string
+		args         args
+		wantAfter    int
+		wantBefore   int
+		targetObject int
+	}{
+
+		{
+			name: "no containers to add, appk target transfer",
+			args: args{
+				objects:                  []runtime.Object{appK[0]},
+				containersToAppend:       []api.Container{},
+				nameDeploymentToTransfer: "app",
+			},
+			wantBefore:   1,
+			wantAfter:    1,
+			targetObject: FirstObject,
+		},
+		{
+			name: "no match in the deployment names, no target found",
+			args: args{
+				objects:                  []runtime.Object{dbK[0]},
+				containersToAppend:       []api.Container{},
+				nameDeploymentToTransfer: "app",
+			},
+			wantBefore:   1,
+			wantAfter:    1,
+			targetObject: FirstObject,
+		},
+		{
+			name: "1 container more, appk target transfer",
+			args: args{
+				objects:                  []runtime.Object{appK[0]},
+				containersToAppend:       []api.Container{{Name: "new-1"}},
+				nameDeploymentToTransfer: "app",
+			},
+			wantBefore:   1,
+			wantAfter:    2,
+			targetObject: FirstObject,
+		},
+		{
+			name: "1 containers and 2 more, appk target transfer",
+			args: args{
+				objects:                  []runtime.Object{appK[0], dbK[0]},
+				containersToAppend:       []api.Container{{Name: "new-1"}, {Name: "new-2"}},
+				nameDeploymentToTransfer: "app",
+			},
+			wantBefore:   1,
+			wantAfter:    3,
+			targetObject: FirstObject,
+		},
+		{
+			name: "one containers to transfer, appk target transfer",
+			args: args{
+				objects:                  []runtime.Object{appK[0]},
+				containersToAppend:       []api.Container{{Name: "new-1"}},
+				nameDeploymentToTransfer: "app",
+			},
+			wantBefore:   1,
+			wantAfter:    2,
+			targetObject: FirstObject,
+		},
+		{
+			name: "1 container in appk and add 2 container in dbK, db target transfer",
+			args: args{
+				objects:                  []runtime.Object{appK[0], dbK[0]},
+				containersToAppend:       []api.Container{{Name: "new-1"}, {Name: "new-2"}},
+				nameDeploymentToTransfer: "db",
+			},
+			wantBefore:   1,
+			wantAfter:    3,
+			targetObject: SecondObject,
+		},
+		{
+			name: "1 container in appk, cannot add 2 container in dbK, app target transfer",
+			args: args{
+				objects:                  []runtime.Object{appK[0], dbK[0]},
+				containersToAppend:       []api.Container{{Name: "new-1"}, {Name: "new-2"}},
+				nameDeploymentToTransfer: "app",
+			},
+			wantBefore:   1,
+			wantAfter:    1,
+			targetObject: SecondObject,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			beforeContainers := (tt.args.objects)[tt.targetObject].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(beforeContainers) != tt.wantBefore {
+				t.Errorf("Before Expected %d containers, got %d", tt.wantBefore, len(beforeContainers))
+			}
+
+			addContainersToTargetDeployment(&tt.args.objects, tt.args.containersToAppend, tt.args.nameDeploymentToTransfer)
+			afterContainers := (tt.args.objects)[tt.targetObject].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(afterContainers) != tt.wantAfter {
+				t.Errorf("After Expected %d containers, got %d", tt.wantAfter, len(afterContainers))
+			}
+			// reset containers
+			newContainer := appK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers[0]
+			appK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers = nil
+			appK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers = append(appK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers, newContainer)
+
+			newContainer = dbK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers[0]
+			dbK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers = nil
+			dbK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers = append(dbK[0].(*appsv1.Deployment).Spec.Template.Spec.Containers, newContainer)
+		})
+	}
+}
+
+func Test_addContainersFromSourceToTargetDeployment(t *testing.T) {
+	type args struct {
+		objects              *[]runtime.Object
+		currentDeploymentMap DeploymentMapping
+	}
+	const (
+		FirstObject  = 0
+		SecondObject = 1
+	)
+	tests := []struct {
+		name             string
+		args             args
+		wantBefore       int
+		wantAfter        int
+		targetDeployment int
+	}{
+		{
+			name: "add one container more to target deployment",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				currentDeploymentMap: DeploymentMapping{
+					SourceDeploymentName: "db",
+					TargetDeploymentName: "app",
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        2,
+			targetDeployment: FirstObject,
+		},
+		{
+			name: "no containers to transfer",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "new-1",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				currentDeploymentMap: DeploymentMapping{},
+			},
+			wantBefore:       1,
+			wantAfter:        1,
+			targetDeployment: FirstObject,
+		},
+		{
+			name: "no containers to transfer",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "new-1",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				currentDeploymentMap: DeploymentMapping{
+					SourceDeploymentName: "app",
+					TargetDeploymentName: "db",
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        2,
+			targetDeployment: SecondObject,
+		},
+		{
+			name: "target deployment not found",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "new-1",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				currentDeploymentMap: DeploymentMapping{
+					SourceDeploymentName: "no-exist-deployment",
+					TargetDeploymentName: "target-no-exist",
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        1,
+			targetDeployment: FirstObject,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			beforeContainers := (*tt.args.objects)[tt.targetDeployment].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(beforeContainers) != tt.wantBefore {
+				t.Errorf("Before expected %d containers, got %d", tt.wantBefore, len(beforeContainers))
+			}
+
+			addContainersFromSourceToTargetDeployment(tt.args.objects, tt.args.currentDeploymentMap)
+			afterContainers := (*tt.args.objects)[tt.targetDeployment].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(afterContainers) != tt.wantAfter {
+				t.Errorf("After expected %d containers, got %d", tt.wantAfter, len(afterContainers))
+			}
+		})
+	}
+}
+
+func Test_mergeContainersIntoDestinationDeployment(t *testing.T) {
+	type args struct {
+		deploymentMappings []DeploymentMapping
+		objects            *[]runtime.Object
+	}
+	const (
+		FirstObject  = 0
+		SecondObject = 1
+	)
+	tests := []struct {
+		name             string
+		args             args
+		wantBefore       int
+		wantAfter        int
+		targetDeployment int
+	}{
+		{
+			name: "merge containers into db",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{},
+								},
+							},
+						},
+					},
+				},
+				deploymentMappings: []DeploymentMapping{
+					{
+						SourceDeploymentName: "app",
+						TargetDeploymentName: "db",
+					},
+				},
+			},
+			wantBefore:       0,
+			wantAfter:        1,
+			targetDeployment: SecondObject,
+		},
+		{
+			name: "merge containers into destination deployment",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db-1",
+											Image: "image",
+										},
+										{
+											Name:  "db-2",
+											Image: "image",
+										},
+										{
+											Name:  "db-3",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				deploymentMappings: []DeploymentMapping{
+					{
+						SourceDeploymentName: "db",
+						TargetDeploymentName: "app",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        4,
+			targetDeployment: FirstObject,
+		},
+		{
+			name: "no containers to transfer",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				deploymentMappings: []DeploymentMapping{},
+			},
+			wantBefore:       1,
+			wantAfter:        1,
+			targetDeployment: FirstObject,
+		},
+		{
+			name: "merge 2 containers db deployment into app deployment",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+										{
+											Name:  "db-2",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				deploymentMappings: []DeploymentMapping{
+					{
+						SourceDeploymentName: "db",
+						TargetDeploymentName: "app",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        3,
+			targetDeployment: FirstObject,
+		},
+		{
+			name: "merge containers in app deployment into db deployment",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				deploymentMappings: []DeploymentMapping{
+					{
+						SourceDeploymentName: "app",
+						TargetDeploymentName: "db",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        2,
+			targetDeployment: SecondObject,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			beforeContainers := (*tt.args.objects)[tt.targetDeployment].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(beforeContainers) != tt.wantBefore {
+				t.Errorf("Before expected %d containers, got %d", tt.wantBefore, len(beforeContainers))
+			}
+			mergeContainersIntoDestinationDeployment(tt.args.deploymentMappings, tt.args.objects)
+			afterContainers := (*tt.args.objects)[tt.targetDeployment].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(afterContainers) != tt.wantAfter {
+				t.Errorf("After expected %d containers, got %d", tt.wantAfter, len(afterContainers))
+			}
+		})
+	}
+}
+
+func TestKubernetes_fixNetworkModeToService(t *testing.T) {
+	type args struct {
+		objects  *[]runtime.Object
+		services map[string]kobject.ServiceConfig
+	}
+	const (
+		FirstObject  = 0
+		SecondObject = 1
+	)
+	tests := []struct {
+		name             string
+		args             args
+		wantBefore       int
+		wantAfter        int
+		targetDeployment int
+		wantDeployments  int
+	}{
+		{
+			name: "fix network mode to service with transfer",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "nginx",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				services: map[string]kobject.ServiceConfig{
+					"nginx": {
+						Name:  "nginx",
+						Image: "image",
+					},
+					"app": {
+						Name:        "app",
+						Image:       "image",
+						NetworkMode: "service:db",
+					},
+					"db": {
+						Name:  "db",
+						Image: "image",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        2,
+			targetDeployment: FirstObject,
+			wantDeployments:  2,
+		},
+		{
+			name: "not transfer because wronge service name",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				services: map[string]kobject.ServiceConfig{
+					"app": {
+						Name:  "app",
+						Image: "image",
+					},
+					"db": {
+						Name:        "db",
+						Image:       "image",
+						NetworkMode: "service:wrong",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        1,
+			targetDeployment: FirstObject,
+			wantDeployments:  1,
+		},
+		{
+			name: "deployment db removed",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				services: map[string]kobject.ServiceConfig{
+					"app": {
+						Name:        "app",
+						Image:       "image",
+						NetworkMode: "service:db",
+					},
+					"db": {
+						Name:  "db",
+						Image: "image",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        2,
+			targetDeployment: FirstObject, // db deployment removed
+			wantDeployments:  1,
+		},
+		{
+			name: "deployment app removed and added 1 container",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+
+				services: map[string]kobject.ServiceConfig{
+					"app": {
+						Name:        "app",
+						Image:       "image",
+						NetworkMode: "service:db",
+					},
+					"db": {
+						Name:  "db",
+						Image: "image",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        2,
+			targetDeployment: FirstObject,
+			wantDeployments:  1,
+		},
+		{
+			name: "deployment db removed and added 3 containers",
+			args: args{
+				objects: &[]runtime.Object{
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "app"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "app",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+					&appsv1.Deployment{
+						ObjectMeta: metav1.ObjectMeta{Name: "db"},
+						Spec: appsv1.DeploymentSpec{
+							Template: corev1.PodTemplateSpec{
+								Spec: corev1.PodSpec{
+									Containers: []corev1.Container{
+										{
+											Name:  "db-1",
+											Image: "image",
+										},
+										{
+											Name:  "db-2",
+											Image: "image",
+										},
+										{
+											Name:  "db-3",
+											Image: "image",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				services: map[string]kobject.ServiceConfig{
+					"app": {
+						Name:        "app",
+						Image:       "image",
+						NetworkMode: "service:db",
+					},
+					"db": {
+						Name:  "db",
+						Image: "image",
+					},
+				},
+			},
+			wantBefore:       1,
+			wantAfter:        4,
+			targetDeployment: FirstObject, // db deployment removed
+			wantDeployments:  1,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			k := &Kubernetes{}
+			beforeContainers := (*tt.args.objects)[tt.targetDeployment].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(beforeContainers) != tt.wantBefore {
+				t.Errorf("Expected %d containers, got %d", tt.wantBefore, len(beforeContainers))
+			}
+
+			k.fixNetworkModeToService(tt.args.objects, tt.args.services)
+			afterContainers := (*tt.args.objects)[tt.targetDeployment].(*appsv1.Deployment).Spec.Template.Spec.Containers
+			if len(afterContainers) != tt.wantAfter {
+				t.Errorf("Expected %d containers, got %d", tt.wantAfter, len(afterContainers))
+			}
+			if len(*tt.args.objects) != tt.wantDeployments {
+				t.Errorf("Expected %d deployments, got %d", tt.wantDeployments, len(*tt.args.objects))
+			}
+		})
+	}
+}
diff --git a/script/test/cmd/tests_new.sh b/script/test/cmd/tests_new.sh
index d798b31f..7da09857 100755
--- a/script/test/cmd/tests_new.sh
+++ b/script/test/cmd/tests_new.sh
@@ -372,4 +372,9 @@ k8s_output="$KOMPOSE_ROOT/script/test/fixtures/configmap-file-configs/output-k8s
 os_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/configmap-file-configs/compose-3.yaml convert --provider openshift --stdout --with-kompose-annotation=false"
 os_output="$KOMPOSE_ROOT/script/test/fixtures/configmap-file-configs/output-os-3.yaml"
 convert::expect_success "$k8s_cmd" "$k8s_output" || exit 1
-convert::expect_success "$os_cmd" "$os_output" || exit 1
\ No newline at end of file
+convert::expect_success "$os_cmd" "$os_output" || exit 1
+
+# Test network_mode: service:
+k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/network-mode-service/compose.yaml convert --stdout --with-kompose-annotation=false"
+k8s_output="$KOMPOSE_ROOT/script/test/fixtures/network-mode-service/output-k8s.yaml"
+convert::expect_success "$k8s_cmd" "$k8s_output" || exit 1
\ No newline at end of file
diff --git a/script/test/fixtures/network-mode-service/compose.yaml b/script/test/fixtures/network-mode-service/compose.yaml
new file mode 100644
index 00000000..96de959d
--- /dev/null
+++ b/script/test/fixtures/network-mode-service/compose.yaml
@@ -0,0 +1,17 @@
+version: '3.8'
+
+services:
+  client:
+    image: busybox
+    command: ['sleep', 'infinity']
+    container_name: threats-client
+    restart: unless-stopped
+    ports:
+      - '8080:8080'
+
+  server:
+    image: busybox
+    command: ['sleep', 'infinity']
+    container_name: threats-server
+    restart: unless-stopped
+    network_mode: service:client
\ No newline at end of file
diff --git a/script/test/fixtures/network-mode-service/output-k8s.yaml b/script/test/fixtures/network-mode-service/output-k8s.yaml
new file mode 100644
index 00000000..3f867958
--- /dev/null
+++ b/script/test/fixtures/network-mode-service/output-k8s.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    io.kompose.service: client
+  name: client
+spec:
+  ports:
+    - name: "8080"
+      port: 8080
+      targetPort: 8080
+  selector:
+    io.kompose.service: client
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    io.kompose.service: client
+  name: client
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      io.kompose.service: client
+  template:
+    metadata:
+      labels:
+        io.kompose.network/network-mode-service-default: "true"
+        io.kompose.service: client
+    spec:
+      containers:
+        - args:
+            - sleep
+            - infinity
+          image: busybox
+          name: threats-client
+          ports:
+            - containerPort: 8080
+              hostPort: 8080
+              protocol: TCP
+        - args:
+            - sleep
+            - infinity
+          image: busybox
+          name: threats-server
+      restartPolicy: Always
\ No newline at end of file