Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions pkg/kobject/kobject.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package kobject
import (
"path/filepath"
"strconv"
"strings"
"time"

"github.com/compose-spec/compose-go/v2/types"
Expand All @@ -27,6 +28,7 @@ import (
"github.com/spf13/cast"
v1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)

Expand Down Expand Up @@ -349,3 +351,62 @@ func (s *ServiceConfig) GetOSUpdateStrategy() *deployapi.RollingDeploymentStrate

return nil
}

func (s *ServiceConfig) CheckServiceType() error {
svcType := s.ServiceType
typeFromLabel := s.DeployLabels["kompose.service.type"]
if len(svcType) != 0 {
return nil
} else if len(typeFromLabel) != 0 {
_, err := handleServiceType(typeFromLabel)
if err != nil {
return err
}
return nil
}
return nil
}

func (s *ServiceConfig) GetServiceType() string {
svcType := s.ServiceType
typeFromLabel := s.DeployLabels["kompose.service.type"]
if len(svcType) != 0 {
return svcType
} else if len(typeFromLabel) != 0 {
res, err := handleServiceType(typeFromLabel)
if err != nil {
return ""
}
return res
}
return ""
}

func (s *ServiceConfig) GetServiceNodePort() int32 {
port := s.NodePortPort
var portFromLabel int32
if labelPort, ok := s.DeployLabels["kompose.service.nodeport.port"]; ok {
portFromLabel = cast.ToInt32(labelPort)
}
if port != 0 {
return port
} else if portFromLabel != 0 {
return portFromLabel
}
return 0
}

func handleServiceType(ServiceType string) (string, error) {
switch strings.ToLower(ServiceType) {
case "", "clusterip":
return string(api.ServiceTypeClusterIP), nil
case "nodeport":
return string(api.ServiceTypeNodePort), nil
case "loadbalancer":
return string(api.ServiceTypeLoadBalancer), nil
case "headless":
return "Headless", nil
default:
return "", errors.New("Unknown value " + ServiceType + " , supported values are 'nodeport, clusterip, headless or loadbalancer'")
}
}
7 changes: 4 additions & 3 deletions pkg/transformer/kubernetes/k8sutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ func (k *Kubernetes) initSvcObject(name string, service kobject.ServiceConfig, p
svc.Spec.Selector = transformer.ConfigLabels(service.Name)

svc.Spec.Ports = ports
svc.Spec.Type = api.ServiceType(service.ServiceType)
svc.Spec.Type = api.ServiceType(service.GetServiceType())

// Configure annotations
annotations := transformer.ConfigAnnotations(service)
Expand All @@ -458,17 +458,18 @@ func (k *Kubernetes) CreateLBService(name string, service kobject.ServiceConfig)

// CreateService creates a k8s service
func (k *Kubernetes) CreateService(name string, service kobject.ServiceConfig) *api.Service {
serviceType := service.GetServiceType()
svc := k.InitSvc(name, service)

// Configure the service ports.
servicePorts := k.ConfigServicePorts(service)
svc.Spec.Ports = servicePorts

if service.ServiceType == "Headless" {
if serviceType == "Headless" {
svc.Spec.Type = api.ServiceTypeClusterIP
svc.Spec.ClusterIP = "None"
} else {
svc.Spec.Type = api.ServiceType(service.ServiceType)
svc.Spec.Type = api.ServiceType(serviceType)
}

// Configure annotations
Expand Down
92 changes: 92 additions & 0 deletions pkg/transformer/kubernetes/k8sutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,98 @@ func TestCreateServiceWithServiceUser(t *testing.T) {
}
}

/*
Test the creation of a service with service type NodePort.
The expected result is that Kompose will set user in PodSpec
*/
func TestCreateServiceWithServiceTypeNodePort(t *testing.T) {
// An example service
service := kobject.ServiceConfig{
ContainerName: "name",
Image: "image",
Environment: []kobject.EnvVar{{Name: "env", Value: "value"}},
Port: []kobject.Ports{{HostPort: 123, ContainerPort: 456, Protocol: string(corev1.ProtocolTCP)}},
Command: []string{"cmd"},
WorkingDir: "dir",
Args: []string{"arg1", "arg2"},
VolList: []string{"/tmp/volume"},
Network: []string{"network1", "network2"}, // not supported
DeployLabels: map[string]string{"kompose.service.type": "nodeport", "kompose.service": "my-service"},
Annotations: map[string]string{"kompose.service.type": "nodeport"},
CPUQuota: 1, // not supported
CapAdd: []string{"cap_add"}, // not supported
CapDrop: []string{"cap_drop"}, // not supported
Expose: []string{"expose"}, // not supported
Privileged: true,
Restart: "always",
User: "1234:5678",
}

komposeObject := kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
}
k := Kubernetes{}

objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1})
if err != nil {
t.Error(errors.Wrap(err, "k.Transform failed"))
}

for _, obj := range objects {
if svc, ok := obj.(*corev1.Service); ok {
if svc.Spec.Type != corev1.ServiceTypeNodePort {
t.Errorf("Service type is not NodePort")
}
}
}
}

/*
Test the creation of a service with service type LoadBalancer.
The expected result is that Kompose will set user in PodSpec
*/
func TestCreateServiceWithServiceTypeLoadBalancer(t *testing.T) {
// An example service
service := kobject.ServiceConfig{
ContainerName: "name",
Image: "image",
Environment: []kobject.EnvVar{{Name: "env", Value: "value"}},
Port: []kobject.Ports{{HostPort: 123, ContainerPort: 456, Protocol: string(corev1.ProtocolTCP)}},
Command: []string{"cmd"},
WorkingDir: "dir",
Args: []string{"arg1", "arg2"},
VolList: []string{"/tmp/volume"},
Network: []string{"network1", "network2"}, // not supported
DeployLabels: map[string]string{"kompose.service.type": "loadbalancer", "kompose.service": "my-service"},
Annotations: map[string]string{"kompose.service.type": "loadbalancer"},
CPUQuota: 1, // not supported
CapAdd: []string{"cap_add"}, // not supported
CapDrop: []string{"cap_drop"}, // not supported
Expose: []string{"expose"}, // not supported
Privileged: true,
Restart: "always",
User: "1234:5678",
}

komposeObject := kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
}
k := Kubernetes{}

objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 1})
if err != nil {
t.Error(errors.Wrap(err, "k.Transform failed"))
}

for _, obj := range objects {
if svc, ok := obj.(*corev1.Service); ok {
if svc.Spec.Type != corev1.ServiceTypeLoadBalancer {
t.Errorf("Service type is not NodePort")
}
}
}
}

func TestCreateServiceWithConfigLongSyntax(t *testing.T) {
content := "setting: true"
target := "/etc/config.yaml"
Expand Down
28 changes: 20 additions & 8 deletions pkg/transformer/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ func (k *Kubernetes) ConfigServicePorts(service kobject.ServiceConfig) []api.Ser
servicePorts := []api.ServicePort{}
seenPorts := make(map[int]struct{}, len(service.Port))

serviceType := service.GetServiceType()
var servicePort api.ServicePort
for _, port := range service.Port {
if port.HostPort == 0 {
Expand All @@ -766,7 +767,7 @@ func (k *Kubernetes) ConfigServicePorts(service kobject.ServiceConfig) []api.Ser
name := strconv.Itoa(int(port.HostPort))
if _, ok := seenPorts[int(port.HostPort)]; ok {
// https://github.com/kubernetes/kubernetes/issues/2995
if service.ServiceType == string(api.ServiceTypeLoadBalancer) {
if serviceType == string(api.ServiceTypeLoadBalancer) {
log.Fatalf("Service %s of type LoadBalancer cannot use TCP and UDP for the same port", name)
}
name = fmt.Sprintf("%s-%s", name, strings.ToLower(port.Protocol))
Expand All @@ -778,8 +779,9 @@ func (k *Kubernetes) ConfigServicePorts(service kobject.ServiceConfig) []api.Ser
TargetPort: targetPort,
}

if service.ServiceType == string(api.ServiceTypeNodePort) && service.NodePortPort != 0 {
servicePort.NodePort = service.NodePortPort
nodePortPort := service.GetServiceNodePort()
if serviceType == string(api.ServiceTypeNodePort) && nodePortPort != 0 {
servicePort.NodePort = nodePortPort
}

// If the default is already TCP, no need to include protocol.
Expand Down Expand Up @@ -1491,9 +1493,14 @@ func buildServiceImage(opt kobject.ConvertOptions, service kobject.ServiceConfig
return nil
}

func (k *Kubernetes) configKubeServiceAndIngressForService(service kobject.ServiceConfig, name string, objects *[]runtime.Object) {
func (k *Kubernetes) configKubeServiceAndIngressForService(service kobject.ServiceConfig, name string, objects *[]runtime.Object) error {
err := service.CheckServiceType()
if err != nil {
return err
}
svcType := service.GetServiceType()
if k.PortsExist(service) {
if service.ServiceType == "LoadBalancer" {
if svcType == "LoadBalancer" {
svcs := k.CreateLBService(name, service)
for _, svc := range svcs {
svc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyType(service.ServiceExternalTrafficPolicy)
Expand All @@ -1513,7 +1520,7 @@ func (k *Kubernetes) configKubeServiceAndIngressForService(service kobject.Servi
}
}
} else {
if service.ServiceType == "Headless" {
if svcType == "Headless" {
svc := k.CreateHeadlessService(name, service)
*objects = append(*objects, svc)
if service.ServiceExternalTrafficPolicy != "" {
Expand All @@ -1523,6 +1530,7 @@ func (k *Kubernetes) configKubeServiceAndIngressForService(service kobject.Servi
log.Warnf("Service %q won't be created because 'ports' is not specified", service.Name)
}
}
return nil
}

func (k *Kubernetes) configNetworkPolicyForService(service kobject.ServiceConfig, name string, objects *[]runtime.Object) error {
Expand Down Expand Up @@ -1610,7 +1618,9 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
}
// override..
objects = append(objects, k.CreateWorkloadAndConfigMapObjects(groupName, service, opt)...)
k.configKubeServiceAndIngressForService(service, groupName, &objects)
if err := k.configKubeServiceAndIngressForService(service, groupName, &objects); err != nil {
return nil, err
}

// Configure the container volumes.
volumesMount, volumes, pvc, cms, err := k.ConfigVolumes(groupName, service)
Expand Down Expand Up @@ -1706,7 +1716,9 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
if opt.Controller == StatefulStateController {
service.ServiceType = "Headless"
}
k.configKubeServiceAndIngressForService(service, name, &objects)
if err := k.configKubeServiceAndIngressForService(service, name, &objects); err != nil {
return nil, err
}
err := k.UpdateKubernetesObjects(name, service, opt, &objects)
if err != nil {
return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
Expand Down
3 changes: 3 additions & 0 deletions pkg/transformer/openshift/openshift.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
log.Warningf("Create multiple service to avoid using mixed protocol in the same service when it's loadbalancer type")
}
} else {
if err = service.CheckServiceType(); err != nil {
return nil, errors.Wrap(err, "CheckServiceType failed")
}
svc := o.CreateService(name, service)
objects = append(objects, svc)

Expand Down
Loading