forked from LaconicNetwork/kompose
Add support for windows volume (#1417)
Signed-off-by: aiyijing <aiyijing@live.com>
This commit is contained in:
parent
e8966d9e2c
commit
473f54fdaf
@ -64,6 +64,13 @@ func CreateOutFile(out string) (*os.File, error) {
|
||||
|
||||
// ParseVolume parses a given volume, which might be [name:][host:]container[:access_mode]
|
||||
func ParseVolume(volume string) (name, host, container, mode string, err error) {
|
||||
if containWindowsPath(volume) {
|
||||
return parseWindowsVolume(volume)
|
||||
}
|
||||
return parseVolume(volume)
|
||||
}
|
||||
|
||||
func parseVolume(volume string) (name, host, container, mode string, err error) {
|
||||
separator := ":"
|
||||
|
||||
// Parse based on ":"
|
||||
@ -112,6 +119,88 @@ func ParseVolume(volume string) (name, host, container, mode string, err error)
|
||||
return
|
||||
}
|
||||
|
||||
// parseVolume parses window volume.
|
||||
// example: windows host mount to windows container
|
||||
// volume = dataVolumeName:C:\Users\Data:D:\config:rw
|
||||
// it can be parsed:
|
||||
// name=dataVolumeName, host=C:\Users\Data, container=D:\config, mode=rw
|
||||
// example: windows host mount to linux container
|
||||
// volume = dataVolumeName:C:\Users\Data:/etc/config:rw
|
||||
// it can be parsed:
|
||||
// name=dataVolumeName, host=C:\Users\Data, container=/etc/config, mode=rw
|
||||
func parseWindowsVolume(volume string) (name, host, container, mode string, err error) {
|
||||
var (
|
||||
buffer, volumePaths []string
|
||||
volumeStrings = strings.Split(volume, ":")
|
||||
)
|
||||
|
||||
// extract path and leave order
|
||||
for _, fragment := range volumeStrings {
|
||||
switch {
|
||||
case containWindowsPath(fragment):
|
||||
if len(buffer) == 0 {
|
||||
err = fmt.Errorf("invalid windows volume %s", volume)
|
||||
return
|
||||
}
|
||||
|
||||
driveLetter := buffer[len(buffer)-1]
|
||||
if len(driveLetter) != 1 {
|
||||
err = fmt.Errorf("invalid windows volume %s", volume)
|
||||
return
|
||||
}
|
||||
volumePaths = append(volumePaths, driveLetter+":"+fragment)
|
||||
buffer = buffer[:len(buffer)-1]
|
||||
|
||||
case isPath(fragment):
|
||||
volumePaths = append(volumePaths, fragment)
|
||||
default:
|
||||
buffer = append(buffer, fragment)
|
||||
}
|
||||
}
|
||||
|
||||
// set name and mode if exist
|
||||
if len(buffer) == 1 {
|
||||
if volumeStrings[0] == buffer[0] {
|
||||
name = buffer[0]
|
||||
} else if volumeStrings[len(volumeStrings)-1] == buffer[0] {
|
||||
mode = buffer[0]
|
||||
}
|
||||
} else if len(buffer) == 2 {
|
||||
name = buffer[0]
|
||||
mode = buffer[1]
|
||||
} else if len(buffer) > 2 {
|
||||
err = fmt.Errorf("invalid windows volume %s", volume)
|
||||
return
|
||||
}
|
||||
|
||||
// Support in pass time
|
||||
// Check to see if :Z or :z exists. We do not support SELinux relabeling at the moment.
|
||||
// See https://github.com/kubernetes/kompose/issues/176
|
||||
// Otherwise, check to see if "rw" or "ro" has been passed
|
||||
if mode == "z" || mode == "Z" {
|
||||
log.Warnf("Volume mount \"%s\" will be mounted without labeling support. :z or :Z not supported", volume)
|
||||
mode = ""
|
||||
}
|
||||
|
||||
// Set host and container if exist
|
||||
if len(volumePaths) == 1 {
|
||||
container = volumePaths[0]
|
||||
} else if len(volumePaths) == 2 {
|
||||
host = volumePaths[0]
|
||||
container = volumePaths[1]
|
||||
} else {
|
||||
err = fmt.Errorf("invalid windows volume %s", volume)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// containWindowsPath check whether it contains windows path.
|
||||
// windows path's separator is "\"
|
||||
func containWindowsPath(substring string) bool {
|
||||
return strings.Contains(substring, "\\")
|
||||
}
|
||||
|
||||
// ParseIngressPath parse path for ingress.
|
||||
// eg. example.com/org -> example.com org
|
||||
func ParseIngressPath(url string) (string, string) {
|
||||
|
||||
@ -34,6 +34,7 @@ func TestFormatProviderName(t *testing.T) {
|
||||
// When passing "z" or "Z" we expect "" back.
|
||||
func TestZParseVolumeLabeling(t *testing.T) {
|
||||
testCase := "/foobar:/foobar:Z"
|
||||
windowVolumeTestCase := "C:\\foobar:/foobar:Z"
|
||||
_, _, _, mode, err := ParseVolume(testCase)
|
||||
if err != nil {
|
||||
t.Errorf("In test case %q, returned unexpected error %v", testCase, err)
|
||||
@ -41,6 +42,204 @@ func TestZParseVolumeLabeling(t *testing.T) {
|
||||
if mode != "" {
|
||||
t.Errorf("In test case %q, returned mode %s, expected \"\"", testCase, mode)
|
||||
}
|
||||
|
||||
_, _, _, mode, err = ParseVolume(windowVolumeTestCase)
|
||||
if err != nil {
|
||||
t.Errorf("In test case %q, returned unexpected error %v", windowVolumeTestCase, err)
|
||||
}
|
||||
if mode != "" {
|
||||
t.Errorf("In test case %q, returned mode %s, expected \"\"", windowVolumeTestCase, mode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWindowsVolumeMountLinuxContainer(t *testing.T) {
|
||||
name := "datavolume"
|
||||
windowsHosts := "C:\\Users"
|
||||
linuxContainer := "/etc/configs/"
|
||||
mode := "rw"
|
||||
|
||||
tests := []struct {
|
||||
test, volume, name, host, container, mode string
|
||||
}{
|
||||
{
|
||||
"name:host:container:mode",
|
||||
fmt.Sprintf("%s:%s:%s:%s", name, windowsHosts, linuxContainer, mode),
|
||||
name,
|
||||
windowsHosts,
|
||||
linuxContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"host:container:mode",
|
||||
fmt.Sprintf("%s:%s:%s", windowsHosts, linuxContainer, mode),
|
||||
"",
|
||||
windowsHosts,
|
||||
linuxContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"name:container:mode",
|
||||
fmt.Sprintf("%s:%s:%s", name, linuxContainer, mode),
|
||||
name,
|
||||
"",
|
||||
linuxContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"name:host:container",
|
||||
fmt.Sprintf("%s:%s:%s", name, windowsHosts, linuxContainer),
|
||||
name,
|
||||
windowsHosts,
|
||||
linuxContainer,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"host:container",
|
||||
fmt.Sprintf("%s:%s", windowsHosts, linuxContainer),
|
||||
"",
|
||||
windowsHosts,
|
||||
linuxContainer,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"container:mode",
|
||||
fmt.Sprintf("%s:%s", linuxContainer, mode),
|
||||
"",
|
||||
"",
|
||||
linuxContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"name:container",
|
||||
fmt.Sprintf("%s:%s", name, linuxContainer),
|
||||
name,
|
||||
"",
|
||||
linuxContainer,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"container",
|
||||
fmt.Sprintf("%s", linuxContainer),
|
||||
"",
|
||||
"",
|
||||
linuxContainer,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
name, host, container, mode, err := ParseVolume(test.volume)
|
||||
if err != nil {
|
||||
t.Errorf("In test case %q, returned unexpected error %v", test.test, err)
|
||||
}
|
||||
if name != test.name {
|
||||
t.Errorf("In test case %q, returned volume name %s, expected %s", test.test, name, test.name)
|
||||
}
|
||||
if host != test.host {
|
||||
t.Errorf("In test case %q, returned host path %s, expected %s", test.test, host, test.host)
|
||||
}
|
||||
if container != test.container {
|
||||
t.Errorf("In test case %q, returned container path %s, expected %s", test.test, container, test.container)
|
||||
}
|
||||
if mode != test.mode {
|
||||
t.Errorf("In test case %q, returned access mode %s, expected %s", test.test, mode, test.mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWindowsVolumeMountWindowsContainer(t *testing.T) {
|
||||
name := "datavolume"
|
||||
windowsHosts := "C:\\Users"
|
||||
windowsContainer := "D:\\Users"
|
||||
mode := "rw"
|
||||
|
||||
tests := []struct {
|
||||
test, volume, name, host, container, mode string
|
||||
}{
|
||||
{
|
||||
"name:host:container:mode",
|
||||
fmt.Sprintf("%s:%s:%s:%s", name, windowsHosts, windowsContainer, mode),
|
||||
name,
|
||||
windowsHosts,
|
||||
windowsContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"host:container:mode",
|
||||
fmt.Sprintf("%s:%s:%s", windowsHosts, windowsContainer, mode),
|
||||
"",
|
||||
windowsHosts,
|
||||
windowsContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"name:container:mode",
|
||||
fmt.Sprintf("%s:%s:%s", name, windowsContainer, mode),
|
||||
name,
|
||||
"",
|
||||
windowsContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"name:host:container",
|
||||
fmt.Sprintf("%s:%s:%s", name, windowsHosts, windowsContainer),
|
||||
name,
|
||||
windowsHosts,
|
||||
windowsContainer,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"host:container",
|
||||
fmt.Sprintf("%s:%s", windowsHosts, windowsContainer),
|
||||
"",
|
||||
windowsHosts,
|
||||
windowsContainer,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"container:mode",
|
||||
fmt.Sprintf("%s:%s", windowsContainer, mode),
|
||||
"",
|
||||
"",
|
||||
windowsContainer,
|
||||
mode,
|
||||
},
|
||||
{
|
||||
"name:container",
|
||||
fmt.Sprintf("%s:%s", name, windowsContainer),
|
||||
name,
|
||||
"",
|
||||
windowsContainer,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"container",
|
||||
fmt.Sprintf("%s", windowsContainer),
|
||||
"",
|
||||
"",
|
||||
windowsContainer,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
name, host, container, mode, err := ParseVolume(test.volume)
|
||||
if err != nil {
|
||||
t.Errorf("In test case %q, returned unexpected error %v", test.test, err)
|
||||
}
|
||||
if name != test.name {
|
||||
t.Errorf("In test case %q, returned volume name %s, expected %s", test.test, name, test.name)
|
||||
}
|
||||
if host != test.host {
|
||||
t.Errorf("In test case %q, returned host path %s, expected %s", test.test, host, test.host)
|
||||
}
|
||||
if container != test.container {
|
||||
t.Errorf("In test case %q, returned container path %s, expected %s", test.test, container, test.container)
|
||||
}
|
||||
if mode != test.mode {
|
||||
t.Errorf("In test case %q, returned access mode %s, expected %s", test.test, mode, test.mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolume(t *testing.T) {
|
||||
|
||||
@ -106,3 +106,12 @@ convert::expect_warning "$cmd" "Push image registry 'whatever' is specified but
|
||||
#TEST the kompose.volume.storage-class-name label
|
||||
convert::check_artifacts_generated "kompose -f $KOMPOSE_ROOT/script/test/fixtures/storage-class-name/docker-compose.yml convert -o $TEMP_DIR/output-k8s.json -j" "$TEMP_DIR/output-k8s.json"
|
||||
convert::check_artifacts_generated "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/storage-class-name/docker-compose.yml convert -o $TEMP_DIR/output-os.json -j" "$TEMP_DIR/output-os.json"
|
||||
|
||||
# TEST the windows volume
|
||||
# windows host path to windows container
|
||||
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/windows/docker-compose.yaml convert --stdout -j --with-kompose-annotation=false"
|
||||
os_cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/windows/docker-compose.yaml convert --stdout -j --with-kompose-annotation=false"
|
||||
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/volume-mounts/windows/output-k8s.json"
|
||||
os_output="$KOMPOSE_ROOT/script/test/fixtures/volume-mounts/windows/output-os.json"
|
||||
convert::expect_success "$k8s_cmd" "$k8s_output"
|
||||
convert::expect_success "$os_cmd" "$os_output"
|
||||
|
||||
8
script/test/fixtures/volume-mounts/windows/docker-compose.yaml
vendored
Normal file
8
script/test/fixtures/volume-mounts/windows/docker-compose.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
version: '3.4'
|
||||
services:
|
||||
db:
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- "80"
|
||||
volumes:
|
||||
- C:\Users\data_sux:D:\config:rw
|
||||
115
script/test/fixtures/volume-mounts/windows/output-k8s.json
vendored
Normal file
115
script/test/fixtures/volume-mounts/windows/output-k8s.json
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "db",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "db"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"ports": [
|
||||
{
|
||||
"name": "80",
|
||||
"port": 80,
|
||||
"targetPort": 80
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"io.kompose.service": "db"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"loadBalancer": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"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": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "db-claim0",
|
||||
"persistentVolumeClaim": {
|
||||
"claimName": "db-claim0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "db",
|
||||
"image": "nginx:latest",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 80
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "db-claim0",
|
||||
"mountPath": "D:\\config"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "Recreate"
|
||||
}
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
{
|
||||
"kind": "PersistentVolumeClaim",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "db-claim0",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "db-claim0"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"accessModes": [
|
||||
"ReadWriteOnce"
|
||||
],
|
||||
"resources": {
|
||||
"requests": {
|
||||
"storage": "100Mi"
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
174
script/test/fixtures/volume-mounts/windows/output-os.json
vendored
Normal file
174
script/test/fixtures/volume-mounts/windows/output-os.json
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "db",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "db"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"ports": [
|
||||
{
|
||||
"name": "80",
|
||||
"port": 80,
|
||||
"targetPort": 80
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"io.kompose.service": "db"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"loadBalancer": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"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:latest"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"replicas": 1,
|
||||
"test": false,
|
||||
"selector": {
|
||||
"io.kompose.service": "db"
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "db"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "db-claim0",
|
||||
"persistentVolumeClaim": {
|
||||
"claimName": "db-claim0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "db",
|
||||
"image": " ",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 80
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "db-claim0",
|
||||
"mountPath": "D:\\config"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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": "nginx:latest"
|
||||
},
|
||||
"generation": null,
|
||||
"importPolicy": {},
|
||||
"referencePolicy": {
|
||||
"type": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"dockerImageRepository": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PersistentVolumeClaim",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "db-claim0",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "db-claim0"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"accessModes": [
|
||||
"ReadWriteOnce"
|
||||
],
|
||||
"resources": {
|
||||
"requests": {
|
||||
"storage": "100Mi"
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user