feat: support container volume mount subpath (#1628)

* feat: support volumes subpath

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>

* docs: add the kompose.volume.sub-path label

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>

* fix: update e2e tests

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>

---------

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>
This commit is contained in:
AhmedGrati 2023-07-05 10:47:01 +01:00 committed by GitHub
parent d1e32e7d7d
commit 485cd2f163
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 267 additions and 2 deletions

View File

@ -200,8 +200,9 @@ The currently supported options are:
| 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 |
| kompose.service.external-traffic-policy | 'cluster', 'local', '' |
| kompose.security-context.fsgroup | kubernetes pod security group fsgroup |
| kompose.service.external-traffic-policy | 'cluster', 'local', '' | |
| kompose.security-context.fsgroup | kubernetes pod security group fsgroup | |
| kompose.volume.sub-path | kubernetes volume mount subpath | |
**Note**: `kompose.service.type` label should be defined with `ports` only (except for headless service), otherwise `kompose` will fail.
@ -446,6 +447,20 @@ services:
labels:
kompose.security-context.fsgroup: 1001
```
- `kompose.volume.sub-path` defines Kubernetes Container [VolumeMounts Subpath](https://kubernetes.io/docs/concepts/storage/volumes/#using-subpath).
For example:
```yaml
version: '3.8'
services:
pgadmin:
image: postgres
labels:
kompose.volume.sub-path: pg-data
```
## Restart
If you want to create normal pods without controller you can use `restart` construct of docker-compose to define that. Follow table below to see what happens on the `restart` value.

View File

@ -142,6 +142,7 @@ type ServiceConfig struct {
MemLimit types.UnitBytes `compose:"mem_limit"`
MemReservation types.UnitBytes `compose:""`
DeployMode string `compose:""`
VolumeMountSubPath string `compose:"kompose.volume.subpath"`
// DeployLabels mapping to kubernetes labels
DeployLabels map[string]string `compose:""`
DeployUpdateConfig types.UpdateConfig `compose:""`

View File

@ -719,6 +719,8 @@ func parseKomposeLabels(labels map[string]string, serviceConfig *kobject.Service
serviceConfig.ImagePullSecret = value
case LabelImagePullPolicy:
serviceConfig.ImagePullPolicy = value
case LabelContainerVolumeSubpath:
serviceConfig.VolumeMountSubPath = value
default:
serviceConfig.Labels[key] = value
}

View File

@ -81,6 +81,9 @@ const (
ServiceTypeHeadless = "Headless"
// LabelSecurityContextFsGroup defines the pod FsGroup
LabelSecurityContextFsGroup = "kompose.security-context.fsgroup"
// LabelContainerVolumeSubpath defines the volume mount subpath inside container
LabelContainerVolumeSubpath = "kompose.volume.subpath"
)
// load environment variables from compose file

View File

@ -887,6 +887,7 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) (
var PVCs []*api.PersistentVolumeClaim
var cms []*api.ConfigMap
var volumeName string
var subpathName string
// Set a var based on if the user wants to use empty volumes
// as opposed to persistent volumes and volume claims
@ -897,6 +898,10 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) (
useEmptyVolumes = true
}
if subpath, ok := service.Labels["kompose.volume.subpath"]; ok {
subpathName = subpath
}
// Override volume type if specified in service labels.
if vt, ok := service.Labels["kompose.volume.type"]; ok {
if _, okk := ValidVolumeSet[vt]; !okk {
@ -993,6 +998,9 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) (
PVCs = append(PVCs, createdPVC)
}
}
if subpathName != "" {
volMount.SubPath = subpathName
}
volumeMounts = append(volumeMounts, volMount)
// create a new volume object using the volsource and add to list

View File

@ -104,6 +104,13 @@ func newServiceConfigWithExternalTrafficPolicy() kobject.ServiceConfig {
}
}
func newServiceConfigWithServiceVolumeMount(volumeMountSubPathValue string) kobject.ServiceConfig {
return kobject.ServiceConfig{
Name: "app",
VolumeMountSubPath: volumeMountSubPathValue,
}
}
func equalStringSlice(s1, s2 []string) bool {
if len(s1) != len(s2) {
return false
@ -1023,3 +1030,24 @@ func TestServiceExternalTrafficPolicy(t *testing.T) {
}
}
}
func TestVolumeMountSubPath(t *testing.T) {
groupName := "pod_group"
expectedSubPathValue := "test-subpath"
komposeObject := kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{"app": newServiceConfigWithServiceVolumeMount(expectedSubPathValue)},
}
k := Kubernetes{}
objs, err := k.Transform(komposeObject, kobject.ConvertOptions{ServiceGroupMode: groupName})
if err != nil {
t.Error(errors.Wrap(err, "k.Transform failed"))
}
for _, obj := range objs {
if deployment, ok := obj.(*appsv1.Deployment); ok {
volMountSubPath := deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].SubPath
if volMountSubPath != expectedSubPathValue {
t.Errorf("Expected VolumeMount Subpath %v, got %v", expectedSubPathValue, volMountSubPath)
}
}
}
}

View File

@ -247,6 +247,7 @@ k8s_output="$KOMPOSE_ROOT/script/test/fixtures/fsgroup/output-k8s.yaml"
os_cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/fsgroup/docker-compose.yaml convert --stdout --with-kompose-annotation=false"
os_output="$KOMPOSE_ROOT/script/test/fixtures/fsgroup/output-os.yaml"
convert::expect_success_and_warning "$k8s_cmd" "$k8s_output"
convert::expect_success_and_warning "$os_cmd" "$os_output"
# Test support for compose.yaml file
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/compose-file-support/docker-compose.yaml convert --stdout --with-kompose-annotation=false"
@ -257,3 +258,12 @@ convert::expect_success "$k8s_cmd" "$k8s_output"
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/compose-env-interpolation/docker-compose.yaml convert --stdout --with-kompose-annotation=false"
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/compose-env-interpolation/output-k8s.yaml"
convert::expect_success "$k8s_cmd" "$k8s_output"
convert::expect_success "$os_cmd" "$os_output"
# Test support for subpath volume
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/vols-subpath/docker-compose.yaml convert --stdout --with-kompose-annotation=false"
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/vols-subpath/output-k8s.yaml"
os_cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/vols-subpath/docker-compose.yaml convert --stdout --with-kompose-annotation=false"
os_output="$KOMPOSE_ROOT/script/test/fixtures/vols-subpath/output-os.yaml"
convert::expect_success_and_warning "$k8s_cmd" "$k8s_output"
convert::expect_success "$os_cmd" "$os_output"

View File

@ -0,0 +1,15 @@
version: '3.8'
volumes:
postgres-data:
services:
postgres:
labels:
kompose.volume.subpath: test
image: postgres
environment:
POSTGRES_USER: dumb_postgres_user
POSTGRES_PASSWORD: postgres_password
POSTGRES_DB: dumb_db
volumes:
- postgres-data:/var/lib/postgresql/data

View File

@ -0,0 +1,80 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.volume.subpath: test
creationTimestamp: null
labels:
io.kompose.service: postgres
name: postgres
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: postgres
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.volume.subpath: test
creationTimestamp: null
labels:
io.kompose.network/vols-subpath-default: "true"
io.kompose.service: postgres
spec:
containers:
- env:
- name: POSTGRES_DB
value: dumb_db
- name: POSTGRES_PASSWORD
value: postgres_password
- name: POSTGRES_USER
value: dumb_postgres_user
image: postgres
name: postgres
resources: {}
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-data
subPath: test
restartPolicy: Always
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres-data
status: {}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: postgres-data
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
status: {}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
creationTimestamp: null
name: vols-subpath-default
spec:
ingress:
- from:
- podSelector:
matchLabels:
io.kompose.network/vols-subpath-default: "true"
podSelector:
matchLabels:
io.kompose.network/vols-subpath-default: "true"

View File

@ -0,0 +1,103 @@
---
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
annotations:
kompose.volume.subpath: test
creationTimestamp: null
labels:
io.kompose.service: postgres
name: postgres
spec:
replicas: 1
selector:
io.kompose.service: postgres
strategy:
resources: {}
type: Recreate
template:
metadata:
creationTimestamp: null
labels:
io.kompose.network/vols-subpath-default: "true"
io.kompose.service: postgres
spec:
containers:
- env:
- name: POSTGRES_DB
value: dumb_db
- name: POSTGRES_PASSWORD
value: postgres_password
- name: POSTGRES_USER
value: dumb_postgres_user
image: ' '
name: postgres
resources: {}
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-data
subPath: test
restartPolicy: Always
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres-data
test: false
triggers:
- type: ConfigChange
- imageChangeParams:
automatic: true
containerNames:
- postgres
from:
kind: ImageStreamTag
name: postgres: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: postgres
name: postgres
spec:
lookupPolicy:
local: false
tags:
- annotations: null
from:
kind: DockerImage
name: postgres
generation: null
importPolicy: {}
name: latest
referencePolicy:
type: ""
status:
dockerImageRepository: ""
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: postgres-data
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
status: {}