forked from LaconicNetwork/kompose
Merge pull request #157 from sebgoa/headless
Handle Headless Services when no ports are present
This commit is contained in:
commit
5383914f30
27
pkg/testutils/kubernetes.go
Normal file
27
pkg/testutils/kubernetes.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package testutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckForHeadless is helper function for tests.
|
||||||
|
// It checks if all Services in objects are Headless Services and if there is at least one such Services.
|
||||||
|
func CheckForHeadless(objects []runtime.Object) error {
|
||||||
|
serviceCreated := false
|
||||||
|
for _, obj := range objects {
|
||||||
|
if svc, ok := obj.(*api.Service); ok {
|
||||||
|
serviceCreated = true
|
||||||
|
// Check if it is a headless services
|
||||||
|
if svc.Spec.ClusterIP != "None" {
|
||||||
|
return errors.New("this is not a Headless services")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !serviceCreated {
|
||||||
|
return errors.New("no Service created")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -258,7 +258,7 @@ func convertToVersion(obj runtime.Object, groupVersion unversioned.GroupVersion)
|
|||||||
// PortsExist checks if service has ports defined
|
// PortsExist checks if service has ports defined
|
||||||
func (k *Kubernetes) PortsExist(name string, service kobject.ServiceConfig) bool {
|
func (k *Kubernetes) PortsExist(name string, service kobject.ServiceConfig) bool {
|
||||||
if len(service.Port) == 0 {
|
if len(service.Port) == 0 {
|
||||||
logrus.Warningf("[%s] Service cannot be created because of missing port.", name)
|
logrus.Debugf("[%s] No ports defined. Headless service will be created.", name)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -282,6 +282,31 @@ func (k *Kubernetes) CreateService(name string, service kobject.ServiceConfig, o
|
|||||||
return svc
|
return svc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateHeadlessService creates a k8s headless service.
|
||||||
|
// Thi is used for docker-compose services without ports. For such services we can't create regular Kubernetes Service.
|
||||||
|
// and without Service Pods can't find each other using DNS names.
|
||||||
|
// Instead of regular Kubernetes Service we create Headless Service. DNS of such service points directly to Pod IP address.
|
||||||
|
// You can find more about Headless Services in Kubernetes documentation https://kubernetes.io/docs/user-guide/services/#headless-services
|
||||||
|
func (k *Kubernetes) CreateHeadlessService(name string, service kobject.ServiceConfig, objects []runtime.Object) *api.Service {
|
||||||
|
svc := k.InitSvc(name, service)
|
||||||
|
|
||||||
|
servicePorts := []api.ServicePort{}
|
||||||
|
// Configure a dummy port: https://github.com/kubernetes/kubernetes/issues/32766.
|
||||||
|
servicePorts = append(servicePorts, api.ServicePort{
|
||||||
|
Name: "headless",
|
||||||
|
Port: 55555,
|
||||||
|
})
|
||||||
|
|
||||||
|
svc.Spec.Ports = servicePorts
|
||||||
|
svc.Spec.ClusterIP = "None"
|
||||||
|
|
||||||
|
// Configure annotations
|
||||||
|
annotations := transformer.ConfigAnnotations(service)
|
||||||
|
svc.ObjectMeta.Annotations = annotations
|
||||||
|
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateKubernetesObjects loads configurations to k8s objects
|
// UpdateKubernetesObjects loads configurations to k8s objects
|
||||||
func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) {
|
func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) {
|
||||||
// Configure the environment variables.
|
// Configure the environment variables.
|
||||||
|
|||||||
@ -24,6 +24,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/kompose/pkg/kobject"
|
"github.com/kubernetes-incubator/kompose/pkg/kobject"
|
||||||
|
"github.com/kubernetes-incubator/kompose/pkg/testutils"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
)
|
)
|
||||||
@ -161,3 +163,22 @@ func TestIsDir(t *testing.T) {
|
|||||||
t.Errorf("Error removing the temporary directory during cleanup: %v", err)
|
t.Errorf("Error removing the temporary directory during cleanup: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestServiceWithoutPort this tests if Headless Service is created for services without Port.
|
||||||
|
func TestServiceWithoutPort(t *testing.T) {
|
||||||
|
service := kobject.ServiceConfig{
|
||||||
|
ContainerName: "name",
|
||||||
|
Image: "image",
|
||||||
|
}
|
||||||
|
|
||||||
|
komposeObject := kobject.KomposeObject{
|
||||||
|
ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
|
||||||
|
}
|
||||||
|
k := Kubernetes{}
|
||||||
|
|
||||||
|
objects := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1})
|
||||||
|
if err := testutils.CheckForHeadless(objects); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -477,6 +477,9 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
|
|||||||
if service.ExposeService != "" {
|
if service.ExposeService != "" {
|
||||||
objects = append(objects, k.initIngress(name, service, svc.Spec.Ports[0].Port))
|
objects = append(objects, k.initIngress(name, service, svc.Spec.Ports[0].Port))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
svc := k.CreateHeadlessService(name, service, objects)
|
||||||
|
objects = append(objects, svc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -371,6 +371,9 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
|
|||||||
if service.ExposeService != "" {
|
if service.ExposeService != "" {
|
||||||
objects = append(objects, o.initRoute(name, service, svc.Spec.Ports[0].Port))
|
objects = append(objects, o.initRoute(name, service, svc.Spec.Ports[0].Port))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
svc := o.CreateHeadlessService(name, service, objects)
|
||||||
|
objects = append(objects, svc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o.UpdateKubernetesObjects(name, service, &objects)
|
o.UpdateKubernetesObjects(name, service, &objects)
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import (
|
|||||||
|
|
||||||
"github.com/kubernetes-incubator/kompose/pkg/kobject"
|
"github.com/kubernetes-incubator/kompose/pkg/kobject"
|
||||||
"github.com/kubernetes-incubator/kompose/pkg/testutils"
|
"github.com/kubernetes-incubator/kompose/pkg/testutils"
|
||||||
|
"github.com/kubernetes-incubator/kompose/pkg/transformer/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newServiceConfig() kobject.ServiceConfig {
|
func newServiceConfig() kobject.ServiceConfig {
|
||||||
@ -309,3 +310,22 @@ func TestInitBuildConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestServiceWithoutPort this tests if Headless Service is created for services without Port.
|
||||||
|
func TestServiceWithoutPort(t *testing.T) {
|
||||||
|
service := kobject.ServiceConfig{
|
||||||
|
ContainerName: "name",
|
||||||
|
Image: "image",
|
||||||
|
}
|
||||||
|
|
||||||
|
komposeObject := kobject.KomposeObject{
|
||||||
|
ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
|
||||||
|
}
|
||||||
|
o := OpenShift{Kubernetes: kubernetes.Kubernetes{}}
|
||||||
|
|
||||||
|
objects := o.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1})
|
||||||
|
if err := testutils.CheckForHeadless(objects); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -21,9 +21,8 @@ source $KOMPOSE_ROOT/script/test/cmd/lib.sh
|
|||||||
# Tests related to docker-compose file in /script/test/fixtures/etherpad
|
# Tests related to docker-compose file in /script/test/fixtures/etherpad
|
||||||
convert::expect_failure "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout"
|
convert::expect_failure "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout"
|
||||||
|
|
||||||
# commenting this test case out until image handling is fixed
|
|
||||||
convert::expect_failure "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose-no-image.yml convert --stdout"
|
convert::expect_failure "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose-no-image.yml convert --stdout"
|
||||||
convert::expect_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose-no-ports.yml convert --stdout" "Service cannot be created because of missing port."
|
|
||||||
export $(cat $KOMPOSE_ROOT/script/test/fixtures/etherpad/envs)
|
export $(cat $KOMPOSE_ROOT/script/test/fixtures/etherpad/envs)
|
||||||
# kubernetes test
|
# kubernetes test
|
||||||
convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/etherpad/output-k8s.json" "Unsupported depends_on key - ignoring"
|
convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/etherpad/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/etherpad/output-k8s.json" "Unsupported depends_on key - ignoring"
|
||||||
@ -51,9 +50,9 @@ convert::expect_success_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/s
|
|||||||
######
|
######
|
||||||
# Tests related to docker-compose file in /script/test/fixtures/entrypoint-command
|
# Tests related to docker-compose file in /script/test/fixtures/entrypoint-command
|
||||||
# kubernetes test
|
# kubernetes test
|
||||||
convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/output-k8s.json" "Service cannot be created because of missing port."
|
convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/output-k8s.json"
|
||||||
# openshift test
|
# openshift test
|
||||||
convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/output-os.json" "Service cannot be created because of missing port."
|
convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/docker-compose.yml convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/entrypoint-command/output-os.json"
|
||||||
|
|
||||||
|
|
||||||
######
|
######
|
||||||
@ -105,7 +104,7 @@ convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_RO
|
|||||||
|
|
||||||
######
|
######
|
||||||
# Test related to kompose --bundle convert to ensure that DSB bundles are converted properly
|
# Test related to kompose --bundle convert to ensure that DSB bundles are converted properly
|
||||||
convert::expect_success_and_warning "kompose --bundle $KOMPOSE_ROOT/script/test/fixtures/bundles/dsb/docker-voting-bundle.dsb convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/bundles/dsb/output-k8s.json" "Service cannot be created because of missing port."
|
convert::expect_success_and_warning "kompose --bundle $KOMPOSE_ROOT/script/test/fixtures/bundles/dsb/docker-voting-bundle.dsb convert --stdout -j" "$KOMPOSE_ROOT/script/test/fixtures/bundles/dsb/output-k8s.json" "Unsupported Networks key - ignoring"
|
||||||
|
|
||||||
######
|
######
|
||||||
# Test related to restart options in docker-compose
|
# Test related to restart options in docker-compose
|
||||||
|
|||||||
89
script/test/fixtures/bundles/dsb/output-k8s.json
vendored
89
script/test/fixtures/bundles/dsb/output-k8s.json
vendored
@ -3,6 +3,33 @@
|
|||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"items": [
|
"items": [
|
||||||
|
{
|
||||||
|
"kind": "Service",
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"metadata": {
|
||||||
|
"name": "worker",
|
||||||
|
"creationTimestamp": null,
|
||||||
|
"labels": {
|
||||||
|
"service": "worker"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"name": "headless",
|
||||||
|
"port": 55555,
|
||||||
|
"targetPort": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selector": {
|
||||||
|
"service": "worker"
|
||||||
|
},
|
||||||
|
"clusterIP": "None"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"loadBalancer": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Service",
|
"kind": "Service",
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
@ -117,6 +144,37 @@
|
|||||||
"loadBalancer": {}
|
"loadBalancer": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "Deployment",
|
||||||
|
"apiVersion": "extensions/v1beta1",
|
||||||
|
"metadata": {
|
||||||
|
"name": "worker",
|
||||||
|
"creationTimestamp": null
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"replicas": 1,
|
||||||
|
"template": {
|
||||||
|
"metadata": {
|
||||||
|
"creationTimestamp": null,
|
||||||
|
"labels": {
|
||||||
|
"service": "worker"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "worker",
|
||||||
|
"image": "docker/example-voting-app-worker",
|
||||||
|
"resources": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"restartPolicy": "Always"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strategy": {}
|
||||||
|
},
|
||||||
|
"status": {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Deployment",
|
"kind": "Deployment",
|
||||||
"apiVersion": "extensions/v1beta1",
|
"apiVersion": "extensions/v1beta1",
|
||||||
@ -270,37 +328,6 @@
|
|||||||
"strategy": {}
|
"strategy": {}
|
||||||
},
|
},
|
||||||
"status": {}
|
"status": {}
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Deployment",
|
|
||||||
"apiVersion": "extensions/v1beta1",
|
|
||||||
"metadata": {
|
|
||||||
"name": "worker",
|
|
||||||
"creationTimestamp": null
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"replicas": 1,
|
|
||||||
"template": {
|
|
||||||
"metadata": {
|
|
||||||
"creationTimestamp": null,
|
|
||||||
"labels": {
|
|
||||||
"service": "worker"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "worker",
|
|
||||||
"image": "docker/example-voting-app-worker",
|
|
||||||
"resources": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"restartPolicy": "Always"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"strategy": {}
|
|
||||||
},
|
|
||||||
"status": {}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,33 @@
|
|||||||
"strategy": {}
|
"strategy": {}
|
||||||
},
|
},
|
||||||
"status": {}
|
"status": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Service",
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"metadata": {
|
||||||
|
"name": "base",
|
||||||
|
"creationTimestamp": null,
|
||||||
|
"labels": {
|
||||||
|
"service": "base"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"name": "headless",
|
||||||
|
"port": 55555,
|
||||||
|
"targetPort": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selector": {
|
||||||
|
"service": "base"
|
||||||
|
},
|
||||||
|
"clusterIP": "None"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"loadBalancer": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,33 @@
|
|||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"items": [
|
"items": [
|
||||||
|
{
|
||||||
|
"kind": "Service",
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"metadata": {
|
||||||
|
"name": "base",
|
||||||
|
"creationTimestamp": null,
|
||||||
|
"labels": {
|
||||||
|
"service": "base"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"name": "headless",
|
||||||
|
"port": 55555,
|
||||||
|
"targetPort": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selector": {
|
||||||
|
"service": "base"
|
||||||
|
},
|
||||||
|
"clusterIP": "None"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"loadBalancer": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "DeploymentConfig",
|
"kind": "DeploymentConfig",
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
|
|||||||
1344
script/test/fixtures/envvars-separators/output-k8s.json
vendored
1344
script/test/fixtures/envvars-separators/output-k8s.json
vendored
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user