forked from LaconicNetwork/kompose
commit
4f6b92f6c8
@ -49,7 +49,7 @@ __Glossary:__
|
||||
| external_links | X | X | X | | Kubernetes uses a flat-structure for all containers and thus external_links does not have a 1-1 conversion |
|
||||
| extra_hosts | N | N | N | | |
|
||||
| group_add | ✓ | ✓ | ✓ | | |
|
||||
| healthcheck | - | N | N | | |
|
||||
| healthcheck | - | N | ✓ | | |
|
||||
| image | ✓ | ✓ | ✓ | Deployment.Spec.Containers.Image | |
|
||||
| isolation | X | X | X | | Not applicable as this applies to Windows with HyperV support |
|
||||
| labels | ✓ | ✓ | ✓ | Metadata.Annotations | |
|
||||
|
||||
@ -99,8 +99,20 @@ type ServiceConfig struct {
|
||||
Dockerfile string `compose:"dockerfile"`
|
||||
Replicas int `compose:"replicas"`
|
||||
GroupAdd []int64 `compose:"group_add"`
|
||||
// Volumes is a struct which contains all information about each volume
|
||||
Volumes []Volumes `compose:""`
|
||||
Volumes []Volumes `compose:""`
|
||||
HealthChecks HealthCheck `compose:""`
|
||||
}
|
||||
|
||||
// HealthCheck the healthcheck configuration for a service
|
||||
// "StartPeriod" is not yet added to compose, see:
|
||||
// https://github.com/docker/cli/issues/116
|
||||
type HealthCheck struct {
|
||||
Test []string
|
||||
Timeout int32
|
||||
Interval int32
|
||||
Retries int32
|
||||
StartPeriod int32
|
||||
Disable bool
|
||||
}
|
||||
|
||||
// EnvVar holds the environment variable struct of a container
|
||||
|
||||
@ -32,6 +32,34 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestParseHealthCheck(t *testing.T) {
|
||||
helperValue := uint64(2)
|
||||
check := types.HealthCheckConfig{
|
||||
Test: []string{"CMD-SHELL", "echo", "foobar"},
|
||||
Timeout: "1s",
|
||||
Interval: "2s",
|
||||
Retries: &helperValue,
|
||||
StartPeriod: "3s",
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to convert HealthCheckConfig: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(output, expected) {
|
||||
t.Errorf("Structs are not equal, expected: %s, output: %s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadV3Volumes(t *testing.T) {
|
||||
vol := types.ServiceVolumeConfig{
|
||||
Type: "volume",
|
||||
@ -66,6 +94,7 @@ func TestLoadV3Ports(t *testing.T) {
|
||||
if output[0] != expected {
|
||||
t.Errorf("Expected %v, got %v", expected, output[0])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Test if service types are parsed properly on user input
|
||||
|
||||
@ -20,6 +20,7 @@ import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
libcomposeyaml "github.com/docker/libcompose/yaml"
|
||||
|
||||
@ -162,6 +163,52 @@ func loadV3Ports(ports []types.ServicePortConfig) []kobject.Ports {
|
||||
return komposePorts
|
||||
}
|
||||
|
||||
/* Convert the HealthCheckConfig as designed by Docker to
|
||||
a Kubernetes-compatible format.
|
||||
*/
|
||||
func parseHealthCheck(composeHealthCheck types.HealthCheckConfig) (kobject.HealthCheck, error) {
|
||||
|
||||
var timeout, interval, retries, startPeriod int32
|
||||
|
||||
// Here we convert the timeout from 1h30s (example) to 36030 seconds.
|
||||
if composeHealthCheck.Timeout != "" {
|
||||
parse, err := time.ParseDuration(composeHealthCheck.Timeout)
|
||||
if err != nil {
|
||||
return kobject.HealthCheck{}, errors.Wrap(err, "unable to parse health check timeout variable")
|
||||
}
|
||||
timeout = int32(parse.Seconds())
|
||||
}
|
||||
|
||||
if composeHealthCheck.Interval != "" {
|
||||
parse, err := time.ParseDuration(composeHealthCheck.Interval)
|
||||
if err != nil {
|
||||
return kobject.HealthCheck{}, errors.Wrap(err, "unable to parse health check interval variable")
|
||||
}
|
||||
interval = int32(parse.Seconds())
|
||||
}
|
||||
|
||||
if *composeHealthCheck.Retries != 0 {
|
||||
retries = int32(*composeHealthCheck.Retries)
|
||||
}
|
||||
|
||||
if composeHealthCheck.StartPeriod != "" {
|
||||
parse, err := time.ParseDuration(composeHealthCheck.StartPeriod)
|
||||
if err != nil {
|
||||
return kobject.HealthCheck{}, errors.Wrap(err, "unable to parse health check startPeriod variable")
|
||||
}
|
||||
startPeriod = int32(parse.Seconds())
|
||||
}
|
||||
|
||||
// Due to docker/cli adding "CMD-SHELL" to the struct, we remove the first element of composeHealthCheck.Test
|
||||
return kobject.HealthCheck{
|
||||
Test: composeHealthCheck.Test[1:],
|
||||
Timeout: timeout,
|
||||
Interval: interval,
|
||||
Retries: retries,
|
||||
StartPeriod: startPeriod,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.KomposeObject, error) {
|
||||
|
||||
// Step 1. Initialize what's going to be returned
|
||||
@ -198,8 +245,18 @@ func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.Kompose
|
||||
// Deploy keys
|
||||
//
|
||||
|
||||
// mode:
|
||||
serviceConfig.DeployMode = composeServiceConfig.Deploy.Mode
|
||||
|
||||
// HealthCheck
|
||||
if composeServiceConfig.HealthCheck != nil && !composeServiceConfig.HealthCheck.Disable {
|
||||
var err error
|
||||
serviceConfig.HealthChecks, err = parseHealthCheck(*composeServiceConfig.HealthCheck)
|
||||
if err != nil {
|
||||
return kobject.KomposeObject{}, errors.Wrap(err, "Unable to parse health check")
|
||||
}
|
||||
}
|
||||
|
||||
if (composeServiceConfig.Deploy.Resources != types.Resources{}) {
|
||||
|
||||
// memory:
|
||||
|
||||
@ -381,6 +381,33 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
|
||||
template.Spec.Containers[0].TTY = service.Tty
|
||||
template.Spec.Volumes = volumes
|
||||
|
||||
// Configure the HealthCheck
|
||||
// We check to see if it's blank
|
||||
if !reflect.DeepEqual(service.HealthChecks, kobject.HealthCheck{}) {
|
||||
probe := api.Probe{}
|
||||
|
||||
if len(service.HealthChecks.Test) > 0 {
|
||||
probe.Handler = api.Handler{
|
||||
Exec: &api.ExecAction{
|
||||
Command: service.HealthChecks.Test,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return errors.New("Health check must contain a command")
|
||||
}
|
||||
|
||||
probe.TimeoutSeconds = service.HealthChecks.Timeout
|
||||
probe.PeriodSeconds = service.HealthChecks.Interval
|
||||
probe.FailureThreshold = service.HealthChecks.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.StartPeriod
|
||||
|
||||
template.Spec.Containers[0].LivenessProbe = &probe
|
||||
}
|
||||
|
||||
if service.StopGracePeriod != "" {
|
||||
template.Spec.TerminationGracePeriodSeconds, err = DurationStrToSecondsInt(service.StopGracePeriod)
|
||||
if err != nil {
|
||||
|
||||
@ -392,6 +392,15 @@ convert::expect_success "kompose --provider=openshift convert --stdout -j" "/tmp
|
||||
# Return back to the original path
|
||||
cd $CURRENT_DIR
|
||||
|
||||
# Test HealthCheck
|
||||
cmd="kompose convert --stdout -j -f $KOMPOSE_ROOT/script/test/fixtures/healthcheck/docker-compose.yaml"
|
||||
sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" "$KOMPOSE_ROOT/script/test/fixtures/healthcheck/output-k8s-template.json" > /tmp/output-k8s.json
|
||||
convert::expect_success "kompose convert --stdout -j -f $KOMPOSE_ROOT/script/test/fixtures/healthcheck/docker-compose.yaml" "/tmp/output-k8s.json"
|
||||
|
||||
cmd="kompose convert --stdout -j --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/healthcheck/docker-compose.yaml"
|
||||
sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" "$KOMPOSE_ROOT/script/test/fixtures/healthcheck/output-os-template.json" > /tmp/output-os.json
|
||||
convert::expect_success "kompose convert --stdout -j --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/healthcheck/docker-compose.yaml" "/tmp/output-os.json"
|
||||
|
||||
# Test V3 Support of Docker Compose
|
||||
|
||||
# Test deploy mode: global
|
||||
|
||||
10
script/test/fixtures/healthcheck/docker-compose.yaml
vendored
Normal file
10
script/test/fixtures/healthcheck/docker-compose.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
healthcheck:
|
||||
test: echo "hello world"
|
||||
interval: 10s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
86
script/test/fixtures/healthcheck/output-k8s-template.json
vendored
Normal file
86
script/test/fixtures/healthcheck/output-k8s-template.json
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "redis",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "redis"
|
||||
},
|
||||
"annotations": {
|
||||
"kompose.cmd": "%CMD%",
|
||||
"kompose.version": "%VERSION%"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"ports": [
|
||||
{
|
||||
"name": "headless",
|
||||
"port": 55555,
|
||||
"targetPort": 0
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"io.kompose.service": "redis"
|
||||
},
|
||||
"clusterIP": "None"
|
||||
},
|
||||
"status": {
|
||||
"loadBalancer": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"apiVersion": "extensions/v1beta1",
|
||||
"metadata": {
|
||||
"name": "redis",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "redis"
|
||||
},
|
||||
"annotations": {
|
||||
"kompose.cmd": "%CMD%",
|
||||
"kompose.version": "%VERSION%"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "redis"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "redis",
|
||||
"image": "redis",
|
||||
"resources": {},
|
||||
"livenessProbe": {
|
||||
"exec": {
|
||||
"command": [
|
||||
"echo \"hello world\""
|
||||
]
|
||||
},
|
||||
"timeoutSeconds": 1,
|
||||
"periodSeconds": 10,
|
||||
"failureThreshold": 5
|
||||
}
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always"
|
||||
}
|
||||
},
|
||||
"strategy": {}
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
138
script/test/fixtures/healthcheck/output-os-template.json
vendored
Normal file
138
script/test/fixtures/healthcheck/output-os-template.json
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "redis",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "redis"
|
||||
},
|
||||
"annotations": {
|
||||
"kompose.cmd": "%CMD%",
|
||||
"kompose.version": "%VERSION%"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"ports": [
|
||||
{
|
||||
"name": "headless",
|
||||
"port": 55555,
|
||||
"targetPort": 0
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"io.kompose.service": "redis"
|
||||
},
|
||||
"clusterIP": "None"
|
||||
},
|
||||
"status": {
|
||||
"loadBalancer": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "DeploymentConfig",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "redis",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "redis"
|
||||
},
|
||||
"annotations": {
|
||||
"kompose.cmd": "%CMD%",
|
||||
"kompose.version": "%VERSION%"
|
||||
}
|
||||
},
|
||||
"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": " ",
|
||||
"resources": {},
|
||||
"livenessProbe": {
|
||||
"exec": {
|
||||
"command": [
|
||||
"echo \"hello world\""
|
||||
]
|
||||
},
|
||||
"timeoutSeconds": 1,
|
||||
"periodSeconds": 10,
|
||||
"failureThreshold": 5
|
||||
}
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always"
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
{
|
||||
"kind": "ImageStream",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "redis",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"io.kompose.service": "redis"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"tags": [
|
||||
{
|
||||
"name": "latest",
|
||||
"annotations": null,
|
||||
"from": {
|
||||
"kind": "DockerImage",
|
||||
"name": "redis"
|
||||
},
|
||||
"generation": null,
|
||||
"importPolicy": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"dockerImageRepository": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -197,6 +197,16 @@
|
||||
"mountPath": "/tmp"
|
||||
}
|
||||
],
|
||||
"livenessProbe": {
|
||||
"exec": {
|
||||
"command": [
|
||||
"echo \"hello world\""
|
||||
]
|
||||
},
|
||||
"timeoutSeconds": 1,
|
||||
"periodSeconds": 10,
|
||||
"failureThreshold": 5
|
||||
},
|
||||
"securityContext": {
|
||||
"capabilities": {
|
||||
"add": [
|
||||
|
||||
@ -197,6 +197,16 @@
|
||||
"mountPath": "/tmp"
|
||||
}
|
||||
],
|
||||
"livenessProbe": {
|
||||
"exec": {
|
||||
"command": [
|
||||
"echo \"hello world\""
|
||||
]
|
||||
},
|
||||
"timeoutSeconds": 1,
|
||||
"periodSeconds": 10,
|
||||
"failureThreshold": 5
|
||||
},
|
||||
"securityContext": {
|
||||
"capabilities": {
|
||||
"add": [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user