diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go index a5b4ba9e5..d98b77514 100644 --- a/pkg/kobject/kobject.go +++ b/pkg/kobject/kobject.go @@ -110,6 +110,7 @@ type ServiceConfig struct { WorkingDir string `compose:""` DomainName string `compose:"domainname"` HostName string `compose:"hostname"` + ReadOnly bool `compose:"read_only"` Args []string `compose:"args"` VolList []string `compose:"volumes"` Network []string `compose:"network"` diff --git a/pkg/loader/compose/compose.go b/pkg/loader/compose/compose.go index db3bfab89..a09b74cad 100644 --- a/pkg/loader/compose/compose.go +++ b/pkg/loader/compose/compose.go @@ -462,6 +462,7 @@ func dockerComposeToKomposeMapping(composeObject *types.Project) (kobject.Kompos serviceConfig.Expose = composeServiceConfig.Expose serviceConfig.Privileged = composeServiceConfig.Privileged serviceConfig.User = composeServiceConfig.User + serviceConfig.ReadOnly = composeServiceConfig.ReadOnly serviceConfig.Stdin = composeServiceConfig.StdinOpen serviceConfig.Tty = composeServiceConfig.Tty serviceConfig.TmpFs = composeServiceConfig.Tmpfs diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 90d12fed1..5e5e0be2c 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -573,6 +573,11 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic securityContext.Capabilities = capabilities } + //set readOnlyRootFilesystem if it is enabled + if service.ReadOnly { + securityContext.ReadOnlyRootFilesystem = &service.ReadOnly + } + // update template only if securityContext is not empty if *securityContext != (api.SecurityContext{}) { template.Spec.Containers[0].SecurityContext = securityContext diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go index 410e666f6..15cfd87fc 100644 --- a/pkg/transformer/kubernetes/k8sutils_test.go +++ b/pkg/transformer/kubernetes/k8sutils_test.go @@ -629,3 +629,31 @@ func TestArgsInterpolation(t *testing.T) { } } } + +func TestReadOnlyRootFS(t *testing.T) { + // An example service + service := kobject.ServiceConfig{ + ContainerName: "name", + Image: "image", + ReadOnly: true, + } + + // An example object generated via k8s runtime.Objects() + komposeObject := kobject.KomposeObject{ + ServiceConfigs: map[string]kobject.ServiceConfig{"app": service}, + } + k := Kubernetes{} + objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } + + for _, obj := range objects { + if deployment, ok := obj.(*appsv1.Deployment); ok { + readOnlyFS := deployment.Spec.Template.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem + if *readOnlyFS != true { + t.Errorf("Expected ReadOnlyRootFileSystem %v upon conversion, actual %v", true, readOnlyFS) + } + } + } +} diff --git a/script/test/cmd/tests_new.sh b/script/test/cmd/tests_new.sh index 5ce8f0551..b44d3ef0d 100755 --- a/script/test/cmd/tests_new.sh +++ b/script/test/cmd/tests_new.sh @@ -277,3 +277,11 @@ convert::expect_success "$os_cmd" "$os_output" k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/custom-build-push/docker-compose.yaml convert --build-command 'docker build -t ahmedgrati/kompose-test ./script/test/fixtures/custom-build-push' --push-command 'docker push ahmedgrati/kompose-test' --stdout --with-kompose-annotation=false" k8s_output="$KOMPOSE_ROOT/script/test/fixtures/custom-build-push/output-k8s.yaml" convert::expect_success "$os_cmd" "$os_output" + +# Test support for read only root fs +k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/read-only/docker-compose.yaml convert --stdout --with-kompose-annotation=false" +k8s_output="$KOMPOSE_ROOT/script/test/fixtures/read-only/output-k8s.yaml" +os_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/read-only/docker-compose.yaml convert --stdout --with-kompose-annotation=false --provider openshift" +os_output="$KOMPOSE_ROOT/script/test/fixtures/read-only/output-os.yaml" +convert::expect_success "$k8s_cmd" "$k8s_output" +convert::expect_success "$os_cmd" "$os_output" diff --git a/script/test/fixtures/read-only/docker-compose.yaml b/script/test/fixtures/read-only/docker-compose.yaml new file mode 100644 index 000000000..f3cb52cdd --- /dev/null +++ b/script/test/fixtures/read-only/docker-compose.yaml @@ -0,0 +1,6 @@ +services: + test: + image: alpine + read_only: true + ports: + - 80:80 diff --git a/script/test/fixtures/read-only/output-k8s.yaml b/script/test/fixtures/read-only/output-k8s.yaml new file mode 100644 index 000000000..d18582fde --- /dev/null +++ b/script/test/fixtures/read-only/output-k8s.yaml @@ -0,0 +1,52 @@ +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + io.kompose.service: test + name: test +spec: + ports: + - name: "80" + port: 80 + targetPort: 80 + selector: + io.kompose.service: test +status: + loadBalancer: {} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + io.kompose.service: test + name: test +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: test + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + io.kompose.network/read-only-default: "true" + io.kompose.service: test + spec: + containers: + - image: alpine + name: test + ports: + - containerPort: 80 + hostPort: 80 + protocol: TCP + resources: {} + securityContext: + readOnlyRootFilesystem: true + restartPolicy: Always +status: {} + diff --git a/script/test/fixtures/read-only/output-os.yaml b/script/test/fixtures/read-only/output-os.yaml new file mode 100644 index 000000000..40cc310ed --- /dev/null +++ b/script/test/fixtures/read-only/output-os.yaml @@ -0,0 +1,93 @@ +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + io.kompose.service: test + name: test +spec: + ports: + - name: "80" + port: 80 + targetPort: 80 + selector: + io.kompose.service: test +status: + loadBalancer: {} + +--- +apiVersion: apps.openshift.io/v1 +kind: DeploymentConfig +metadata: + creationTimestamp: null + labels: + io.kompose.service: test + name: test +spec: + replicas: 1 + selector: + io.kompose.service: test + strategy: + resources: {} + template: + metadata: + creationTimestamp: null + labels: + io.kompose.network/read-only-default: "true" + io.kompose.service: test + spec: + containers: + - image: ' ' + name: test + ports: + - containerPort: 80 + hostPort: 80 + protocol: TCP + resources: {} + securityContext: + readOnlyRootFilesystem: true + restartPolicy: Always + test: false + triggers: + - type: ConfigChange + - imageChangeParams: + automatic: true + containerNames: + - test + from: + kind: ImageStreamTag + name: test:latest + type: ImageChange +status: + availableReplicas: 0 + latestVersion: 0 + observedGeneration: 0 + replicas: 0 + unavailableReplicas: 0 + updatedReplicas: 0 + +--- +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + creationTimestamp: null + labels: + io.kompose.service: test + name: test +spec: + lookupPolicy: + local: false + tags: + - annotations: null + from: + kind: DockerImage + name: alpine + generation: null + importPolicy: {} + name: latest + referencePolicy: + type: "" +status: + dockerImageRepository: "" +