2021-11-30 12:04:28 +01:00
package generator
import (
"fmt"
2021-12-02 15:42:01 +01:00
"io/ioutil"
2021-12-01 08:31:51 +01:00
"katenary/compose"
"katenary/helm"
2022-04-01 10:43:08 +02:00
"katenary/logger"
2021-12-01 16:50:32 +01:00
"log"
2022-02-14 14:37:09 +01:00
"net/url"
2021-11-30 15:35:32 +01:00
"os"
2021-12-02 15:42:01 +01:00
"path/filepath"
2021-11-30 12:04:28 +01:00
"strconv"
"strings"
"sync"
2021-12-02 10:21:05 +00:00
"time"
2021-11-30 12:04:28 +01:00
"errors"
2022-02-14 14:37:09 +01:00
"github.com/google/shlex"
2021-11-30 12:04:28 +01:00
)
var servicesMap = make ( map [ string ] int )
var serviceWaiters = make ( map [ string ] [ ] chan int )
var locker = & sync . Mutex { }
2021-11-30 15:45:36 +01:00
2021-12-02 10:21:05 +00:00
const (
ICON_PACKAGE = "📦"
ICON_SERVICE = "🔌"
ICON_SECRET = "🔏"
ICON_CONF = "📝"
ICON_STORE = "⚡"
ICON_INGRESS = "🌐"
)
2021-12-05 09:05:48 +01:00
const (
RELEASE_NAME = helm . RELEASE_NAME
)
2021-11-30 15:45:36 +01:00
// Values is kept in memory to create a values.yaml file.
2021-11-30 12:04:28 +01:00
var Values = make ( map [ string ] map [ string ] interface { } )
2021-11-30 17:29:42 +01:00
var VolumeValues = make ( map [ string ] map [ string ] map [ string ] interface { } )
2022-02-16 17:40:11 +01:00
var EmptyDirs = [ ] string { }
2021-11-30 12:04:28 +01:00
2021-11-30 15:45:36 +01:00
var dependScript = `
2021-11-30 12:04:28 +01:00
OK = 0
echo "Checking __service__ port"
while [ $ OK != 1 ] ; do
echo - n "."
2022-02-14 14:37:09 +01:00
nc - z ` + RELEASE_NAME + ` - __service__ __port__ 2 > & 1 > / dev / null && OK = 1 || sleep 1
2021-11-30 12:04:28 +01:00
done
echo
echo "Done"
`
2022-02-16 17:40:11 +01:00
var madeDeployments = make ( map [ string ] helm . Deployment , 0 )
2021-11-30 15:45:36 +01:00
// Create a Deployment for a given compose.Service. It returns a list of objects: a Deployment and a possible Service (kubernetes represnetation as maps).
2022-02-16 17:40:11 +01:00
func CreateReplicaObject ( name string , s * compose . Service , linked map [ string ] * compose . Service ) chan interface { } {
2021-12-02 10:21:05 +00:00
ret := make ( chan interface { } , len ( s . Ports ) + len ( s . Expose ) + 1 )
2022-02-16 17:40:11 +01:00
go parseService ( name , s , linked , ret )
2021-12-02 10:21:05 +00:00
return ret
}
// This function will try to yied deployment and services based on a service from the compose file structure.
2022-02-16 17:40:11 +01:00
func parseService ( name string , s * compose . Service , linked map [ string ] * compose . Service , ret chan interface { } ) {
2022-04-01 10:43:08 +02:00
logger . Magenta ( ICON_PACKAGE + " Generating deployment for " , name )
2021-11-30 12:04:28 +01:00
2021-12-02 10:21:05 +00:00
o := helm . NewDeployment ( name )
2022-02-16 17:40:11 +01:00
2021-11-30 12:04:28 +01:00
container := helm . NewContainer ( name , s . Image , s . Environment , s . Labels )
2022-03-31 14:12:20 +02:00
prepareContainer ( container , s , name )
2022-04-01 10:43:08 +02:00
prepareEnvFromFiles ( name , s , container , ret )
2021-12-02 10:21:05 +00:00
2022-02-14 14:37:09 +01:00
// Set the container to the deployment
o . Spec . Template . Spec . Containers = [ ] * helm . Container { container }
2021-11-30 17:29:42 +01:00
2021-12-02 10:21:05 +00:00
// Prepare volumes
2022-02-16 17:40:11 +01:00
madePVC := make ( map [ string ] bool )
o . Spec . Template . Spec . Volumes = prepareVolumes ( name , name , s , container , madePVC , ret )
// Now, for "depends_on" section, it's a bit tricky to get dependencies, see the function below.
o . Spec . Template . Spec . InitContainers = prepareInitContainers ( name , s , container )
2021-12-02 15:42:01 +01:00
2022-02-14 14:37:09 +01:00
// Add selectors
selectors := buildSelector ( name , s )
2021-11-30 12:04:28 +01:00
o . Spec . Selector = map [ string ] interface { } {
2022-02-14 14:37:09 +01:00
"matchLabels" : selectors ,
2021-11-30 12:04:28 +01:00
}
2022-02-14 14:37:09 +01:00
o . Spec . Template . Metadata . Labels = selectors
2021-11-30 12:04:28 +01:00
2022-02-16 17:40:11 +01:00
// Now, the linked services
for lname , link := range linked {
container := helm . NewContainer ( lname , link . Image , link . Environment , link . Labels )
2022-03-31 14:12:20 +02:00
prepareContainer ( container , link , lname )
2022-04-01 10:43:08 +02:00
prepareEnvFromFiles ( lname , link , container , ret )
2022-02-16 17:40:11 +01:00
o . Spec . Template . Spec . Containers = append ( o . Spec . Template . Spec . Containers , container )
o . Spec . Template . Spec . Volumes = append ( o . Spec . Template . Spec . Volumes , prepareVolumes ( name , lname , link , container , madePVC , ret ) ... )
o . Spec . Template . Spec . InitContainers = append ( o . Spec . Template . Spec . InitContainers , prepareInitContainers ( lname , link , container ) ... )
//append ports and expose ports to the deployment, to be able to generate them in the Service file
if len ( link . Ports ) > 0 || len ( link . Expose ) > 0 {
s . Ports = append ( s . Ports , link . Ports ... )
s . Expose = append ( s . Expose , link . Expose ... )
}
}
// Remove duplicates in volumes
volumes := make ( [ ] map [ string ] interface { } , 0 )
done := make ( map [ string ] bool )
for _ , vol := range o . Spec . Template . Spec . Volumes {
name := vol [ "name" ] . ( string )
if _ , ok := done [ name ] ; ok {
continue
} else {
done [ name ] = true
volumes = append ( volumes , vol )
}
}
o . Spec . Template . Spec . Volumes = volumes
2021-11-30 12:04:28 +01:00
2022-02-14 14:37:09 +01:00
// Then, create Services and possible Ingresses for ingress labels, "ports" and "expose" section
2021-11-30 12:04:28 +01:00
if len ( s . Ports ) > 0 || len ( s . Expose ) > 0 {
2022-02-14 14:37:09 +01:00
for _ , s := range generateServicesAndIngresses ( name , s ) {
2021-12-02 10:21:05 +00:00
ret <- s
}
2021-11-30 12:04:28 +01:00
}
2021-12-02 10:21:05 +00:00
// Special case, it there is no "ports", so there is no associated services...
// But... some other deployment can wait for it, so we alert that this deployment hasn't got any
// associated service.
if len ( s . Ports ) == 0 {
2022-02-14 14:37:09 +01:00
// alert any current or **future** waiters that this service is not exposed
2021-12-02 10:21:05 +00:00
go func ( ) {
2022-04-01 09:22:00 +02:00
defer func ( ) {
// recover from panic
if r := recover ( ) ; r != nil {
// log the stack trace
fmt . Println ( r )
}
} ( )
2021-12-02 10:21:05 +00:00
for {
select {
case <- time . Tick ( 1 * time . Millisecond ) :
2021-12-17 12:07:23 +01:00
locker . Lock ( )
2021-12-02 10:21:05 +00:00
for _ , c := range serviceWaiters [ name ] {
c <- - 1
close ( c )
}
2021-12-17 12:07:23 +01:00
locker . Unlock ( )
2021-12-02 10:21:05 +00:00
}
}
} ( )
}
// add the volumes in Values
2021-11-30 17:29:42 +01:00
if len ( VolumeValues [ name ] ) > 0 {
2021-12-02 10:21:05 +00:00
locker . Lock ( )
2021-11-30 17:29:42 +01:00
Values [ name ] [ "persistence" ] = VolumeValues [ name ]
2021-12-02 10:21:05 +00:00
locker . Unlock ( )
2021-11-30 17:29:42 +01:00
}
2021-12-02 10:21:05 +00:00
// the deployment is ready, give it
ret <- o
2021-11-30 15:35:32 +01:00
2021-12-02 10:21:05 +00:00
// and then, we can say that it's the end
ret <- nil
2021-11-30 12:04:28 +01:00
}
2022-03-31 14:12:20 +02:00
// prepareContainer assigns image, command, env, and labels to a container.
func prepareContainer ( container * helm . Container , service * compose . Service , servicename string ) {
2022-04-01 08:04:37 +02:00
// if there is no image name, this should fail!
if service . Image == "" {
log . Fatal ( ICON_PACKAGE + " No image name for service " , servicename )
}
2022-03-31 14:12:20 +02:00
container . Image = "{{ .Values." + servicename + ".image }}"
container . Command = service . Command
Values [ servicename ] = map [ string ] interface { } {
"image" : service . Image ,
}
prepareProbes ( servicename , service , container )
generateContainerPorts ( service , servicename , container )
}
2021-11-30 15:45:36 +01:00
// Create a service (k8s).
2022-02-14 14:37:09 +01:00
func generateServicesAndIngresses ( name string , s * compose . Service ) [ ] interface { } {
2021-11-30 12:04:28 +01:00
2022-02-14 14:37:09 +01:00
ret := make ( [ ] interface { } , 0 ) // can handle helm.Service or helm.Ingress
2022-04-01 10:43:08 +02:00
logger . Magenta ( ICON_SERVICE + " Generating service for " , name )
2021-12-01 15:17:34 +01:00
ks := helm . NewService ( name )
2021-11-30 15:35:32 +01:00
2021-11-30 12:04:28 +01:00
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 ] )
}
2021-12-01 16:50:32 +01:00
ks . Spec . Ports = append ( ks . Spec . Ports , helm . NewServicePort ( target , target ) )
2021-11-30 12:04:28 +01:00
if i == 0 {
detected ( name , target )
}
}
ks . Spec . Selector = buildSelector ( name , s )
2021-12-01 08:31:51 +01:00
ret = append ( ret , ks )
2021-12-02 14:56:51 +01:00
if v , ok := s . Labels [ helm . LABEL_INGRESS ] ; ok {
2021-12-02 10:21:05 +00:00
port , err := strconv . Atoi ( v )
if err != nil {
2021-12-05 09:05:48 +01:00
log . Fatalf ( "The given port \"%v\" as ingress port in \"%s\" service is not an integer\n" , v , name )
2021-12-02 10:21:05 +00:00
}
2022-04-01 10:43:08 +02:00
logger . Cyanf ( ICON_INGRESS + " Create an ingress for port %d on %s service\n" , port , name )
2021-12-02 10:21:05 +00:00
ing := createIngress ( name , port , s )
2021-12-01 08:31:51 +01:00
ret = append ( ret , ing )
2021-11-30 12:04:28 +01:00
}
2021-12-01 16:50:32 +01:00
if len ( s . Expose ) > 0 {
2022-04-01 10:43:08 +02:00
logger . Magenta ( ICON_SERVICE + " Generating service for " , name + "-external" )
2021-12-01 16:50:32 +01:00
ks := helm . NewService ( name + "-external" )
ks . Spec . Type = "NodePort"
for _ , p := range s . Expose {
ks . Spec . Ports = append ( ks . Spec . Ports , helm . NewServicePort ( p , p ) )
}
ks . Spec . Selector = buildSelector ( name , s )
ret = append ( ret , ks )
}
2021-12-01 08:31:51 +01:00
return ret
2021-11-30 12:04:28 +01:00
}
2021-11-30 15:45:36 +01:00
// Create an ingress.
2021-12-03 11:49:32 +01:00
func createIngress ( name string , port int , s * compose . Service ) * helm . Ingress {
2021-11-30 12:04:28 +01:00
ingress := helm . NewIngress ( name )
Values [ name ] [ "ingress" ] = map [ string ] interface { } {
2021-11-30 15:35:32 +01:00
"class" : "nginx" ,
2021-12-02 10:21:05 +00:00
"host" : name + "." + helm . Appname + ".tld" ,
2021-11-30 12:04:28 +01:00
"enabled" : false ,
}
ingress . Spec . Rules = [ ] helm . IngressRule {
{
Host : fmt . Sprintf ( "{{ .Values.%s.ingress.host }}" , name ) ,
Http : helm . IngressHttp {
Paths : [ ] helm . IngressPath { {
Path : "/" ,
PathType : "Prefix" ,
2022-02-16 10:37:46 +01:00
Backend : & helm . IngressBackend {
2021-11-30 12:04:28 +01:00
Service : helm . IngressService {
2021-12-05 09:05:48 +01:00
Name : RELEASE_NAME + "-" + name ,
2021-11-30 12:04:28 +01:00
Port : map [ string ] interface { } {
"number" : port ,
} ,
} ,
} ,
} } ,
} ,
} ,
}
2021-11-30 15:35:32 +01:00
ingress . SetIngressClass ( name )
2021-11-30 12:04:28 +01:00
2021-12-01 08:31:51 +01:00
return ingress
2021-11-30 12:04:28 +01:00
}
2021-12-02 10:21:05 +00:00
// This function is called when a possible service is detected, it append the port in a map to make others
// to be able to get the service name. It also try to send the data to any "waiter" for this service.
2021-11-30 12:04:28 +01:00
func detected ( name string , port int ) {
locker . Lock ( )
2021-12-17 12:05:38 +01:00
defer locker . Unlock ( )
if _ , ok := servicesMap [ name ] ; ok {
return
}
2021-11-30 12:04:28 +01:00
servicesMap [ name ] = port
go func ( ) {
2021-12-17 12:07:23 +01:00
locker . Lock ( )
defer locker . Unlock ( )
2021-12-17 12:05:38 +01:00
if cx , ok := serviceWaiters [ name ] ; ok {
for _ , c := range cx {
c <- port
2021-11-30 12:04:28 +01:00
}
}
} ( )
}
func getPort ( name string ) ( int , error ) {
if v , ok := servicesMap [ name ] ; ok {
return v , nil
}
return - 1 , errors . New ( "Not found" )
}
2021-11-30 15:45:36 +01:00
// Waits for a service to be discovered. Sometimes, a deployment depends on another one. See the detected() function.
2021-11-30 12:04:28 +01:00
func waitPort ( name string ) chan int {
locker . Lock ( )
2021-12-17 12:05:38 +01:00
defer locker . Unlock ( )
2021-11-30 12:04:28 +01:00
c := make ( chan int , 0 )
serviceWaiters [ name ] = append ( serviceWaiters [ name ] , c )
go func ( ) {
2021-12-17 12:07:23 +01:00
locker . Lock ( )
defer locker . Unlock ( )
2021-11-30 12:04:28 +01:00
if v , ok := servicesMap [ name ] ; ok {
c <- v
}
} ( )
return c
}
2022-02-14 14:37:09 +01:00
// Build the selector for the service.
2021-12-03 11:49:32 +01:00
func buildSelector ( name string , s * compose . Service ) map [ string ] string {
2021-11-30 12:04:28 +01:00
return map [ string ] string {
"katenary.io/component" : name ,
2021-12-05 09:05:48 +01:00
"katenary.io/release" : RELEASE_NAME ,
2021-11-30 12:04:28 +01:00
}
}
2021-12-02 15:42:01 +01:00
2022-02-14 14:37:09 +01:00
// buildCMFromPath generates a ConfigMap from a path.
2021-12-02 15:42:01 +01:00
func buildCMFromPath ( path string ) * helm . ConfigMap {
stat , err := os . Stat ( path )
if err != nil {
return nil
}
files := make ( map [ string ] string , 0 )
if stat . IsDir ( ) {
found , _ := filepath . Glob ( path + "/*" )
for _ , f := range found {
if s , err := os . Stat ( f ) ; err != nil || s . IsDir ( ) {
if err != nil {
fmt . Fprintf ( os . Stderr , "An error occured reading volume path %s\n" , err . Error ( ) )
} else {
2022-04-01 10:43:08 +02:00
logger . ActivateColors = true
logger . Yellowf ( "Warning, %s is a directory, at this time we only " +
2021-12-02 15:42:01 +01:00
"can create configmap for first level file list\n" , f )
2022-04-01 10:43:08 +02:00
logger . ActivateColors = false
2021-12-02 15:42:01 +01:00
}
continue
}
_ , filename := filepath . Split ( f )
c , _ := ioutil . ReadFile ( f )
files [ filename ] = string ( c )
}
}
cm := helm . NewConfigMap ( "" )
cm . Data = files
return cm
}
2022-02-14 14:37:09 +01:00
// generateContainerPorts add the container ports of a service.
func generateContainerPorts ( s * compose . Service , 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 )
}
}
container . Ports = append ( container . Ports , & helm . ContainerPort {
Name : portName ,
ContainerPort : portNumber ,
} )
exists [ portNumber ] = name
}
// manage the "expose" section to be a NodePort in Kubernetes
for _ , port := range s . Expose {
if _ , exist := exists [ port ] ; exist {
continue
}
container . Ports = append ( container . Ports , & helm . ContainerPort {
Name : name ,
ContainerPort : port ,
} )
}
}
// prepareVolumes add the volumes of a service.
2022-02-16 17:40:11 +01:00
func prepareVolumes ( deployment , name string , s * compose . Service , container * helm . Container , madePVC map [ string ] bool , ret chan interface { } ) [ ] map [ string ] interface { } {
2022-02-14 14:37:09 +01:00
volumes := make ( [ ] map [ string ] interface { } , 0 )
mountPoints := make ( [ ] interface { } , 0 )
configMapsVolumes := make ( [ ] string , 0 )
if v , ok := s . Labels [ helm . LABEL_VOL_CM ] ; ok {
configMapsVolumes = strings . Split ( v , "," )
}
2022-04-01 09:22:00 +02:00
2022-02-14 14:37:09 +01:00
for _ , volume := range s . Volumes {
2022-04-01 09:22:00 +02:00
2022-02-14 14:37:09 +01:00
parts := strings . Split ( volume , ":" )
2022-04-01 08:18:45 +02:00
if len ( parts ) == 1 {
// this is a volume declaration for Docker only, avoid it
continue
}
2022-04-01 09:22:00 +02:00
2022-02-14 14:37:09 +01:00
volname := parts [ 0 ]
volepath := parts [ 1 ]
isCM := false
for _ , cmVol := range configMapsVolumes {
cmVol = strings . TrimSpace ( cmVol )
if volname == cmVol {
isCM = true
break
}
}
if ! isCM && ( strings . HasPrefix ( volname , "." ) || strings . HasPrefix ( volname , "/" ) ) {
// local volume cannt be mounted
2022-04-01 10:43:08 +02:00
logger . ActivateColors = true
logger . Redf ( "You cannot, at this time, have local volume in %s deployment\n" , name )
logger . ActivateColors = false
2022-02-14 14:37:09 +01:00
continue
}
if isCM {
2022-02-17 11:38:23 +01:00
// check if the volname path points on a file, if so, we need to add subvolume to the interface
stat , _ := os . Stat ( volname )
pointToFile := ""
if ! stat . IsDir ( ) {
pointToFile = filepath . Base ( volname )
volname = filepath . Dir ( volname )
}
2022-02-14 14:37:09 +01:00
// the volume is a path and it's explicitally asked to be a configmap in labels
cm := buildCMFromPath ( volname )
volname = strings . Replace ( volname , "./" , "" , 1 )
2022-02-17 11:04:04 +01:00
volname = strings . ReplaceAll ( volname , "/" , "-" )
2022-02-14 14:37:09 +01:00
volname = strings . ReplaceAll ( volname , "." , "-" )
cm . K8sBase . Metadata . Name = RELEASE_NAME + "-" + volname + "-" + name
2022-02-17 11:38:23 +01:00
2022-02-14 14:37:09 +01:00
// build a configmap from the volume path
volumes = append ( volumes , map [ string ] interface { } {
"name" : volname ,
"configMap" : map [ string ] string {
"name" : cm . K8sBase . Metadata . Name ,
} ,
} )
2022-02-17 11:38:23 +01:00
if len ( pointToFile ) > 0 {
mountPoints = append ( mountPoints , map [ string ] interface { } {
"name" : volname ,
"mountPath" : volepath ,
"subPath" : pointToFile ,
} )
} else {
mountPoints = append ( mountPoints , map [ string ] interface { } {
"name" : volname ,
"mountPath" : volepath ,
} )
}
2022-02-14 14:37:09 +01:00
ret <- cm
} else {
// rmove minus sign from volume name
volname = strings . ReplaceAll ( volname , "-" , "" )
2022-02-16 17:40:11 +01:00
isEmptyDir := false
for _ , v := range EmptyDirs {
v = strings . ReplaceAll ( v , "-" , "" )
if v == volname {
volumes = append ( volumes , map [ string ] interface { } {
"name" : volname ,
"emptyDir" : map [ string ] string { } ,
} )
2022-02-17 10:43:07 +01:00
mountPoints = append ( mountPoints , map [ string ] interface { } {
"name" : volname ,
"mountPath" : volepath ,
} )
container . VolumeMounts = mountPoints
2022-02-16 17:40:11 +01:00
isEmptyDir = true
break
}
}
if isEmptyDir {
continue
}
2022-02-14 14:37:09 +01:00
volumes = append ( volumes , map [ string ] interface { } {
"name" : volname ,
"persistentVolumeClaim" : map [ string ] string {
"claimName" : RELEASE_NAME + "-" + volname ,
} ,
} )
mountPoints = append ( mountPoints , map [ string ] interface { } {
"name" : volname ,
"mountPath" : volepath ,
} )
2022-04-01 10:43:08 +02:00
logger . Yellow ( ICON_STORE + " Generate volume values" , volname , "for container named" , name , "in deployment" , deployment )
2022-02-14 14:37:09 +01:00
locker . Lock ( )
2022-02-16 17:40:11 +01:00
if _ , ok := VolumeValues [ deployment ] ; ! ok {
VolumeValues [ deployment ] = make ( map [ string ] map [ string ] interface { } )
2022-02-14 14:37:09 +01:00
}
2022-02-16 17:40:11 +01:00
VolumeValues [ deployment ] [ volname ] = map [ string ] interface { } {
2022-02-14 14:37:09 +01:00
"enabled" : false ,
"capacity" : "1Gi" ,
}
locker . Unlock ( )
2022-02-16 17:40:11 +01:00
if _ , ok := madePVC [ deployment + volname ] ; ! ok {
madePVC [ deployment + volname ] = true
pvc := helm . NewPVC ( deployment , volname )
ret <- pvc
}
2022-02-14 14:37:09 +01:00
}
}
container . VolumeMounts = mountPoints
return volumes
}
// prepareInitContainers add the init containers of a service.
func prepareInitContainers ( name string , s * compose . Service , 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 {
c := helm . NewContainer ( "check-" + dp , "busybox" , nil , s . Labels )
command := strings . ReplaceAll ( strings . TrimSpace ( dependScript ) , "__service__" , dp )
foundPort := - 1
if defaultPort , err := getPort ( dp ) ; err != nil {
// BUG: Sometimes the chan remains opened
foundPort = <- waitPort ( dp )
} else {
foundPort = defaultPort
}
if foundPort == - 1 {
log . Fatalf (
"ERROR, the %s service is waiting for %s port number, " +
"but it is never discovered. You must declare at least one port in " +
"the \"ports\" section of the service in the docker-compose file" ,
name ,
dp ,
)
}
command = strings . ReplaceAll ( command , "__port__" , strconv . Itoa ( foundPort ) )
c . Command = [ ] string {
"sh" ,
"-c" ,
command ,
}
initContainers = append ( initContainers , c )
}
return initContainers
}
// prepareProbes generate http/tcp/command probes for a service.
func prepareProbes ( name string , s * compose . Service , 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" {
probe . Exec = & helm . Exec {
Command : s . HealthCheck . Test [ 1 : ] ,
}
}
container . LivenessProbe = probe
}
}
// prepareEnvFromFiles generate configMap or secrets from environment files.
func prepareEnvFromFiles ( name string , s * compose . Service , container * helm . Container , ret chan interface { } ) {
// prepare secrets
secretsFiles := make ( [ ] string , 0 )
if v , ok := s . Labels [ helm . LABEL_ENV_SECRET ] ; ok {
secretsFiles = strings . Split ( v , "," )
}
// manage environment files (env_file in compose)
for _ , envfile := range s . EnvFiles {
f := strings . ReplaceAll ( envfile , "_" , "-" )
f = strings . ReplaceAll ( f , ".env" , "" )
f = strings . ReplaceAll ( f , "." , "" )
f = strings . ReplaceAll ( f , "/" , "" )
cf := f + "-" + name
isSecret := false
for _ , s := range secretsFiles {
if s == envfile {
isSecret = true
}
}
var store helm . InlineConfig
if ! isSecret {
2022-04-01 10:43:08 +02:00
logger . Bluef ( ICON_CONF + " Generating configMap %s\n" , cf )
2022-02-14 14:37:09 +01:00
store = helm . NewConfigMap ( cf )
} else {
2022-04-01 10:43:08 +02:00
logger . Bluef ( ICON_SECRET + " Generating secret %s\n" , cf )
2022-02-14 14:37:09 +01:00
store = helm . NewSecret ( cf )
}
if err := store . AddEnvFile ( envfile ) ; err != nil {
2022-04-01 10:43:08 +02:00
logger . ActivateColors = true
logger . Red ( err . Error ( ) )
logger . ActivateColors = false
2022-02-14 14:37:09 +01:00
os . Exit ( 2 )
}
section := "configMapRef"
if isSecret {
section = "secretRef"
}
container . EnvFrom = append ( container . EnvFrom , map [ string ] map [ string ] string {
section : {
"name" : store . Metadata ( ) . Name ,
} ,
} )
ret <- store
}
}