WIP Try to use composer-go
- there are problems to resolve with health check - compose/parser.go and compose/types.go should be recheck - need to recheck writers
This commit is contained in:
@@ -3,11 +3,9 @@ package generator
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"katenary/compose"
|
||||
"katenary/helm"
|
||||
"katenary/logger"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -17,7 +15,7 @@ import (
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
var servicesMap = make(map[string]int)
|
||||
@@ -56,14 +54,14 @@ echo "Done"
|
||||
var madeDeployments = make(map[string]helm.Deployment, 0)
|
||||
|
||||
// Create a Deployment for a given compose.Service. It returns a list of objects: a Deployment and a possible Service (kubernetes represnetation as maps).
|
||||
func CreateReplicaObject(name string, s *compose.Service, linked map[string]*compose.Service) chan interface{} {
|
||||
func CreateReplicaObject(name string, s types.ServiceConfig, linked map[string]types.ServiceConfig) chan interface{} {
|
||||
ret := make(chan interface{}, len(s.Ports)+len(s.Expose)+1)
|
||||
go parseService(name, s, linked, ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
// This function will try to yied deployment and services based on a service from the compose file structure.
|
||||
func parseService(name string, s *compose.Service, linked map[string]*compose.Service, ret chan interface{}) {
|
||||
func parseService(name string, s types.ServiceConfig, linked map[string]types.ServiceConfig, ret chan interface{}) {
|
||||
logger.Magenta(ICON_PACKAGE+" Generating deployment for ", name)
|
||||
|
||||
o := helm.NewDeployment(name)
|
||||
@@ -167,7 +165,7 @@ func parseService(name string, s *compose.Service, linked map[string]*compose.Se
|
||||
}
|
||||
|
||||
// prepareContainer assigns image, command, env, and labels to a container.
|
||||
func prepareContainer(container *helm.Container, service *compose.Service, servicename string) {
|
||||
func prepareContainer(container *helm.Container, service types.ServiceConfig, servicename string) {
|
||||
// if there is no image name, this should fail!
|
||||
if service.Image == "" {
|
||||
log.Fatal(ICON_PACKAGE+" No image name for service ", servicename)
|
||||
@@ -182,22 +180,16 @@ func prepareContainer(container *helm.Container, service *compose.Service, servi
|
||||
}
|
||||
|
||||
// Create a service (k8s).
|
||||
func generateServicesAndIngresses(name string, s *compose.Service) []interface{} {
|
||||
func generateServicesAndIngresses(name string, s types.ServiceConfig) []interface{} {
|
||||
|
||||
ret := make([]interface{}, 0) // can handle helm.Service or helm.Ingress
|
||||
logger.Magenta(ICON_SERVICE+" Generating service for ", name)
|
||||
ks := helm.NewService(name)
|
||||
|
||||
for i, p := range s.Ports {
|
||||
port := strings.Split(p, ":")
|
||||
src, _ := strconv.Atoi(port[0])
|
||||
target := src
|
||||
if len(port) > 1 {
|
||||
target, _ = strconv.Atoi(port[1])
|
||||
}
|
||||
ks.Spec.Ports = append(ks.Spec.Ports, helm.NewServicePort(target, target))
|
||||
ks.Spec.Ports = append(ks.Spec.Ports, helm.NewServicePort(int(p.Target), int(p.Target)))
|
||||
if i == 0 {
|
||||
detected(name, target)
|
||||
detected(name, int(p.Target))
|
||||
}
|
||||
}
|
||||
ks.Spec.Selector = buildSelector(name, s)
|
||||
@@ -217,7 +209,9 @@ func generateServicesAndIngresses(name string, s *compose.Service) []interface{}
|
||||
logger.Magenta(ICON_SERVICE+" Generating service for ", name+"-external")
|
||||
ks := helm.NewService(name + "-external")
|
||||
ks.Spec.Type = "NodePort"
|
||||
for _, p := range s.Expose {
|
||||
for _, expose := range s.Expose {
|
||||
|
||||
p, _ := strconv.Atoi(expose)
|
||||
ks.Spec.Ports = append(ks.Spec.Ports, helm.NewServicePort(p, p))
|
||||
}
|
||||
ks.Spec.Selector = buildSelector(name, s)
|
||||
@@ -228,7 +222,7 @@ func generateServicesAndIngresses(name string, s *compose.Service) []interface{}
|
||||
}
|
||||
|
||||
// Create an ingress.
|
||||
func createIngress(name string, port int, s *compose.Service) *helm.Ingress {
|
||||
func createIngress(name string, port int, s types.ServiceConfig) *helm.Ingress {
|
||||
ingress := helm.NewIngress(name)
|
||||
Values[name]["ingress"] = map[string]interface{}{
|
||||
"class": "nginx",
|
||||
@@ -303,7 +297,7 @@ func waitPort(name string) chan int {
|
||||
}
|
||||
|
||||
// Build the selector for the service.
|
||||
func buildSelector(name string, s *compose.Service) map[string]string {
|
||||
func buildSelector(name string, s types.ServiceConfig) map[string]string {
|
||||
return map[string]string{
|
||||
"katenary.io/component": name,
|
||||
"katenary.io/release": RELEASE_NAME,
|
||||
@@ -344,31 +338,28 @@ func buildCMFromPath(path string) *helm.ConfigMap {
|
||||
}
|
||||
|
||||
// generateContainerPorts add the container ports of a service.
|
||||
func generateContainerPorts(s *compose.Service, name string, container *helm.Container) {
|
||||
func generateContainerPorts(s types.ServiceConfig, name string, container *helm.Container) {
|
||||
|
||||
exists := make(map[int]string)
|
||||
for _, port := range s.Ports {
|
||||
_p := strings.Split(port, ":")
|
||||
port = _p[0]
|
||||
if len(_p) > 1 {
|
||||
port = _p[1]
|
||||
}
|
||||
portNumber, _ := strconv.Atoi(port)
|
||||
portName := name
|
||||
for _, n := range exists {
|
||||
if name == n {
|
||||
portName = fmt.Sprintf("%s-%d", name, portNumber)
|
||||
portName = fmt.Sprintf("%s-%d", name, port.Target)
|
||||
}
|
||||
}
|
||||
container.Ports = append(container.Ports, &helm.ContainerPort{
|
||||
Name: portName,
|
||||
ContainerPort: portNumber,
|
||||
ContainerPort: int(port.Target),
|
||||
})
|
||||
exists[portNumber] = name
|
||||
exists[int(port.Target)] = name
|
||||
}
|
||||
|
||||
// manage the "expose" section to be a NodePort in Kubernetes
|
||||
for _, port := range s.Expose {
|
||||
for _, expose := range s.Expose {
|
||||
|
||||
port, _ := strconv.Atoi(expose)
|
||||
|
||||
if _, exist := exists[port]; exist {
|
||||
continue
|
||||
}
|
||||
@@ -380,7 +371,7 @@ func generateContainerPorts(s *compose.Service, name string, container *helm.Con
|
||||
}
|
||||
|
||||
// prepareVolumes add the volumes of a service.
|
||||
func prepareVolumes(deployment, name string, s *compose.Service, container *helm.Container, madePVC map[string]bool, ret chan interface{}) []map[string]interface{} {
|
||||
func prepareVolumes(deployment, name string, s types.ServiceConfig, container *helm.Container, madePVC map[string]bool, ret chan interface{}) []map[string]interface{} {
|
||||
|
||||
volumes := make([]map[string]interface{}, 0)
|
||||
mountPoints := make([]interface{}, 0)
|
||||
@@ -389,16 +380,10 @@ func prepareVolumes(deployment, name string, s *compose.Service, container *helm
|
||||
configMapsVolumes = strings.Split(v, ",")
|
||||
}
|
||||
|
||||
for _, volume := range s.Volumes {
|
||||
for _, vol := range s.Volumes {
|
||||
|
||||
parts := strings.Split(volume, ":")
|
||||
if len(parts) == 1 {
|
||||
// this is a volume declaration for Docker only, avoid it
|
||||
continue
|
||||
}
|
||||
|
||||
volname := parts[0]
|
||||
volepath := parts[1]
|
||||
volname := vol.Source
|
||||
volepath := vol.Target
|
||||
|
||||
isCM := false
|
||||
for _, cmVol := range configMapsVolumes {
|
||||
@@ -511,12 +496,12 @@ func prepareVolumes(deployment, name string, s *compose.Service, container *helm
|
||||
}
|
||||
|
||||
// prepareInitContainers add the init containers of a service.
|
||||
func prepareInitContainers(name string, s *compose.Service, container *helm.Container) []*helm.Container {
|
||||
func prepareInitContainers(name string, s types.ServiceConfig, container *helm.Container) []*helm.Container {
|
||||
|
||||
// We need to detect others services, but we probably not have parsed them yet, so
|
||||
// we will wait for them for a while.
|
||||
initContainers := make([]*helm.Container, 0)
|
||||
for _, dp := range s.DependsOn {
|
||||
for dp, _ := range s.DependsOn {
|
||||
c := helm.NewContainer("check-"+dp, "busybox", nil, s.Labels)
|
||||
command := strings.ReplaceAll(strings.TrimSpace(dependScript), "__service__", dp)
|
||||
|
||||
@@ -549,79 +534,12 @@ func prepareInitContainers(name string, s *compose.Service, container *helm.Cont
|
||||
}
|
||||
|
||||
// prepareProbes generate http/tcp/command probes for a service.
|
||||
func prepareProbes(name string, s *compose.Service, container *helm.Container) {
|
||||
func prepareProbes(name string, s types.ServiceConfig, container *helm.Container) {
|
||||
|
||||
// manage the healthcheck property, if any
|
||||
if s.HealthCheck != nil {
|
||||
if s.HealthCheck.Interval == "" {
|
||||
s.HealthCheck.Interval = "10s"
|
||||
}
|
||||
interval, err := time.ParseDuration(s.HealthCheck.Interval)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if s.HealthCheck.StartPeriod == "" {
|
||||
s.HealthCheck.StartPeriod = "0s"
|
||||
}
|
||||
|
||||
initialDelaySeconds, err := time.ParseDuration(s.HealthCheck.StartPeriod)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
probe := helm.NewProbe(int(interval.Seconds()), int(initialDelaySeconds.Seconds()), 1, s.HealthCheck.Retries)
|
||||
|
||||
healthCheckLabel := s.Labels[helm.LABEL_HEALTHCHECK]
|
||||
|
||||
if healthCheckLabel != "" {
|
||||
|
||||
path := "/"
|
||||
port := 80
|
||||
|
||||
u, err := url.Parse(healthCheckLabel)
|
||||
if err == nil {
|
||||
path = u.Path
|
||||
port, _ = strconv.Atoi(u.Port())
|
||||
} else {
|
||||
path = "/"
|
||||
port = 80
|
||||
}
|
||||
|
||||
if strings.HasPrefix(healthCheckLabel, "http://") {
|
||||
probe.HttpGet = &helm.HttpGet{
|
||||
Path: path,
|
||||
Port: port,
|
||||
}
|
||||
} else if strings.HasPrefix(healthCheckLabel, "tcp://") {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
probe.TCP = &helm.TCP{
|
||||
Port: port,
|
||||
}
|
||||
} else {
|
||||
c, _ := shlex.Split(healthCheckLabel)
|
||||
probe.Exec = &helm.Exec{
|
||||
|
||||
Command: c,
|
||||
}
|
||||
}
|
||||
} else if s.HealthCheck.Test[0] == "CMD" || s.HealthCheck.Test[0] == "CMD-SHELL" {
|
||||
probe.Exec = &helm.Exec{
|
||||
Command: s.HealthCheck.Test[1:],
|
||||
}
|
||||
} else {
|
||||
probe.Exec = &helm.Exec{
|
||||
Command: s.HealthCheck.Test,
|
||||
}
|
||||
}
|
||||
container.LivenessProbe = probe
|
||||
}
|
||||
}
|
||||
|
||||
// prepareEnvFromFiles generate configMap or secrets from environment files.
|
||||
func prepareEnvFromFiles(name string, s *compose.Service, container *helm.Container, ret chan interface{}) {
|
||||
func prepareEnvFromFiles(name string, s types.ServiceConfig, container *helm.Container, ret chan interface{}) {
|
||||
|
||||
// prepare secrets
|
||||
secretsFiles := make([]string, 0)
|
||||
@@ -630,7 +548,7 @@ func prepareEnvFromFiles(name string, s *compose.Service, container *helm.Contai
|
||||
}
|
||||
|
||||
// manage environment files (env_file in compose)
|
||||
for _, envfile := range s.EnvFiles {
|
||||
for _, envfile := range s.EnvFile {
|
||||
f := strings.ReplaceAll(envfile, "_", "-")
|
||||
f = strings.ReplaceAll(f, ".env", "")
|
||||
f = strings.ReplaceAll(f, ".", "")
|
||||
|
@@ -117,7 +117,8 @@ func TestCommand(t *testing.T) {
|
||||
tmp, p := setUp(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
for name := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
if name == "web2" {
|
||||
// Ensure that the command is correctly set
|
||||
// The command should be a string array
|
||||
@@ -161,7 +162,8 @@ func TestEnvs(t *testing.T) {
|
||||
tmp, p := setUp(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
for name := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
|
||||
if name == "php" {
|
||||
// the "DB_HOST" environment variable inside the template must be set to '{{ .Release.Name }}-database'
|
||||
@@ -197,7 +199,8 @@ func TestSamePod(t *testing.T) {
|
||||
tmp, p := setUp(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
for name, service := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
|
||||
|
||||
if _, found := service.Labels[helm.LABEL_SAMEPOD]; found {
|
||||
@@ -222,7 +225,8 @@ func TestPorts(t *testing.T) {
|
||||
tmp, p := setUp(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
for name, service := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
path := ""
|
||||
|
||||
// if the service has a port found in helm.LABEL_PORT or ports, so the service file should exist
|
||||
@@ -249,7 +253,8 @@ func TestPVC(t *testing.T) {
|
||||
tmp, p := setUp(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
for name := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
path := filepath.Join(tmp, "templates", name+"-data.pvc.yaml")
|
||||
|
||||
// the "database" service should have a pvc file in templates (name-data.pvc.yaml)
|
||||
@@ -269,7 +274,8 @@ func TestIngress(t *testing.T) {
|
||||
tmp, p := setUp(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
for name := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
path := filepath.Join(tmp, "templates", name+".ingress.yaml")
|
||||
|
||||
// the "web" service should have a ingress file in templates (name.ingress.yaml)
|
||||
@@ -289,7 +295,8 @@ func TestUnmappedVolumes(t *testing.T) {
|
||||
tmp, p := setUp(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
for name := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
if name == "novol" {
|
||||
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
|
||||
fp, _ := os.Open(path)
|
||||
@@ -310,7 +317,8 @@ func TestEqualSignOnEnv(t *testing.T) {
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
// if the name is eqenv, the service should habe environment
|
||||
for name, _ := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
name := service.Name
|
||||
if name == "eqenv" {
|
||||
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
|
||||
fp, _ := os.Open(path)
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -32,13 +33,15 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, composeFi
|
||||
|
||||
// list avoided services
|
||||
avoids := make(map[string]bool)
|
||||
for n, service := range p.Data.Services {
|
||||
for _, service := range p.Data.Services {
|
||||
n := service.Name
|
||||
if _, ok := service.Labels[helm.LABEL_SAMEPOD]; ok {
|
||||
avoids[n] = true
|
||||
}
|
||||
}
|
||||
|
||||
for name, s := range p.Data.Services {
|
||||
for _, s := range p.Data.Services {
|
||||
name := s.Name
|
||||
|
||||
// Manage emptyDir volumes
|
||||
if empty, ok := s.Labels[helm.LABEL_EMPTYDIRS]; ok {
|
||||
@@ -49,11 +52,12 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, composeFi
|
||||
}
|
||||
|
||||
// fetch corresponding service in "links"
|
||||
linked := make(map[string]*compose.Service, 0)
|
||||
linked := make(map[string]types.ServiceConfig, 0)
|
||||
// find service linked to this one
|
||||
for n, service := range p.Data.Services {
|
||||
if _, ok := service.Labels[helm.LABEL_SAMEPOD]; ok {
|
||||
if service.Labels[helm.LABEL_SAMEPOD] == name {
|
||||
for _, service := range p.Data.Services {
|
||||
n := service.Name
|
||||
for _, label := range service.Labels {
|
||||
if label == helm.LABEL_SAMEPOD {
|
||||
linked[n] = service
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user