diff --git a/generator/color_test.go b/generator/color_test.go new file mode 100644 index 0000000..00c2479 --- /dev/null +++ b/generator/color_test.go @@ -0,0 +1,8 @@ +package generator + +import "testing" + +func TestColor(t *testing.T) { + Red("Red text") + Grey("Grey text") +} diff --git a/generator/main.go b/generator/main.go index d31e8f1..f859957 100644 --- a/generator/main.go +++ b/generator/main.go @@ -4,7 +4,7 @@ import ( "fmt" "helm-compose/compose" "helm-compose/helm" - "log" + "os" "strconv" "strings" "sync" @@ -34,6 +34,7 @@ echo "Done" func CreateReplicaObject(name string, s compose.Service) (ret []interface{}) { + Magenta("Generating deployment for ", name) o := helm.NewDeployment() ret = append(ret, o) o.Metadata.Name = "{{ .Release.Name }}-" + name @@ -45,14 +46,25 @@ func CreateReplicaObject(name string, s compose.Service) (ret []interface{}) { "image": s.Image, } + exists := make(map[int]string) for _, port := range s.Ports { 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: name, + Name: portName, ContainerPort: portNumber, }) + exists[portNumber] = name } for _, port := range s.Expose { + if _, exist := exists[port]; exist { + continue + } container.Ports = append(container.Ports, &helm.ContainerPort{ Name: name, ContainerPort: port, @@ -70,9 +82,10 @@ func CreateReplicaObject(name string, s compose.Service) (ret []interface{}) { initContainers := make([]*helm.Container, 0) for _, dp := range s.DependsOn { if len(s.Ports) == 0 && len(s.Expose) == 0 { - log.Fatalf("Sorry, you need to expose or declare at least one port for the %s service to check \"depends_on\"", name) + Redf("No port exposed for %s that is in dependency", name) + os.Exit(1) } - c := helm.NewContainer("check-"+name, "busybox", nil, s.Labels) + c := helm.NewContainer("check-"+dp, "busybox", nil, s.Labels) command := strings.ReplaceAll(strings.TrimSpace(DependScript), "__service__", dp) wait.Add(1) @@ -102,14 +115,19 @@ func CreateReplicaObject(name string, s compose.Service) (ret []interface{}) { ret = append(ret, ks) } + Green("Done deployment ", name) + return } func createService(name string, s compose.Service) *helm.Service { + Magenta("Generating service for ", name) ks := helm.NewService() ks.Metadata.Name = "{{ .Release.Name }}-" + name defaultPort := 0 + names := make(map[int]int) + for i, p := range s.Ports { port := strings.Split(p, ":") src, _ := strconv.Atoi(port[0]) @@ -118,12 +136,16 @@ func createService(name string, s compose.Service) *helm.Service { target, _ = strconv.Atoi(port[1]) } ks.Spec.Ports = append(ks.Spec.Ports, helm.NewServicePort(src, target)) + names[target] = 1 if i == 0 { defaultPort = target detected(name, target) } } for i, p := range s.Expose { + if _, ok := names[p]; ok { + continue + } ks.Spec.Ports = append(ks.Spec.Ports, helm.NewServicePort(p, p)) if i == 0 { defaultPort = p @@ -134,16 +156,17 @@ func createService(name string, s compose.Service) *helm.Service { ks.Spec.Selector = buildSelector(name, s) if v, ok := s.Labels[helm.K+"/expose-ingress"]; ok && v == "true" { - log.Println("Expose ingress for ", name) createIngress(name, defaultPort, s) } + Green("Done service ", name) return ks } func createIngress(name string, port int, s compose.Service) { ingress := helm.NewIngress(name) Values[name]["ingress"] = map[string]interface{}{ + "class": "nginx", "host": "chart.example.tld", "enabled": false, } @@ -166,6 +189,7 @@ func createIngress(name string, port int, s compose.Service) { }, }, } + ingress.SetIngressClass(name) locker.Lock() Ingresses[name] = ingress diff --git a/generator/utils.go b/generator/utils.go new file mode 100644 index 0000000..f89d3c9 --- /dev/null +++ b/generator/utils.go @@ -0,0 +1,76 @@ +package generator + +import ( + "fmt" + "os" + "sync" +) + +type Color int + +const ( + GREY Color = 30 + iota + RED + GREEN + YELLOW + BLUE + MAGENTA + CYAN +) + +var waiter = sync.Mutex{} + +func color(c Color, args ...interface{}) { + waiter.Lock() + fmt.Fprintf(os.Stdout, "\x1b[%dm", c) + fmt.Fprint(os.Stdout, args...) + fmt.Fprintf(os.Stdout, "\x1b[0m\n") + waiter.Unlock() +} + +func colorf(c Color, format string, args ...interface{}) { + waiter.Lock() + fmt.Fprintf(os.Stdout, "\x1b[%dm", c) + fmt.Fprintf(os.Stdout, format, args...) + fmt.Fprintf(os.Stdout, "\x1b[0m") + waiter.Unlock() +} + +func Grey(args ...interface{}) { + color(GREY, args...) +} + +func Red(args ...interface{}) { + color(RED, args...) +} +func Green(args ...interface{}) { + color(GREEN, args...) +} +func Yellow(args ...interface{}) { + color(YELLOW, args...) +} +func Blue(args ...interface{}) { + color(BLUE, args...) +} +func Magenta(args ...interface{}) { + color(MAGENTA, args...) +} +func Greyf(format string, args ...interface{}) { + colorf(GREY, format, args...) +} + +func Redf(format string, args ...interface{}) { + colorf(RED, format, args...) +} +func Greenf(format string, args ...interface{}) { + colorf(GREEN, format, args...) +} +func Yellowf(format string, args ...interface{}) { + colorf(YELLOW, format, args...) +} +func Bluef(format string, args ...interface{}) { + colorf(BLUE, format, args...) +} +func Magentaf(format string, args ...interface{}) { + colorf(MAGENTA, format, args...) +} diff --git a/helm/ingress.go b/helm/ingress.go index 243c3d8..0c87be1 100644 --- a/helm/ingress.go +++ b/helm/ingress.go @@ -15,8 +15,15 @@ func NewIngress(name string) *Ingress { return i } +func (i *Ingress) SetIngressClass(name string) { + class := "{{ .Values." + name + ".ingress.class }}" + i.Metadata.Annotations["kuberntes.io/ingress.class"] = class + i.Spec.IngressClassName = class +} + type IngressSpec struct { - Rules []IngressRule + IngressClassName string `yaml:"ingressClassName,omitempty"` + Rules []IngressRule } type IngressRule struct { @@ -30,7 +37,7 @@ type IngressHttp struct { type IngressPath struct { Path string - PathType string + PathType string `yaml:"pathType"` Backend IngressBackend } diff --git a/helm/notes.go b/helm/notes.go new file mode 100644 index 0000000..ecc8078 --- /dev/null +++ b/helm/notes.go @@ -0,0 +1,24 @@ +package helm + +import "strings" + +var NOTES = ` +Congratulations, + +Your application is now deployed. This may take a while to be up and responding. + +__list__ +` + +func GenNotes(ingressess map[string]*Ingress) string { + + list := make([]string, 0) + + for name, ing := range ingressess { + for _, r := range ing.Spec.Rules { + list = append(list, "{{ if .Values."+name+".ingress.enabled }}- "+name+" is accessible on : http://"+r.Host+"{{- end }}") + } + } + + return strings.ReplaceAll(NOTES, "__list__", strings.Join(list, "\n")) +} diff --git a/main.go b/main.go index e960d68..b257cf1 100644 --- a/main.go +++ b/main.go @@ -15,11 +15,13 @@ import ( var ComposeFile = "docker-compose.yaml" var AppName = "MyApp" +var AppVersion = "0.0.1" func main() { flag.StringVar(&ComposeFile, "compose", ComposeFile, "set the compose file to parse") flag.StringVar(&AppName, "appname", AppName, "Give the helm chart app name") + flag.StringVar(&AppVersion, "appversion", AppVersion, "Set the chart appVersion") flag.Parse() p := compose.NewParser(ComposeFile) @@ -39,6 +41,7 @@ func main() { wait.Wait() dirname := filepath.Join("chart", AppName) + os.RemoveAll(dirname) templatesDir := filepath.Join(dirname, "templates") os.MkdirAll(templatesDir, 0755) @@ -56,7 +59,6 @@ func main() { } for name, ing := range generator.Ingresses { - fname := filepath.Join(templatesDir, name+".ingress.yaml") fp, _ := os.Create(fname) enc := yaml.NewEncoder(fp) @@ -82,7 +84,12 @@ func main() { "description": "A helm chart for " + AppName, "type": "application", "version": "0.1.0", - "appVersion": "x", + "appVersion": AppVersion, }) fp.Close() + + fp, _ = os.Create(filepath.Join(templatesDir, "NOTES.txt")) + fp.WriteString(helm.GenNotes(generator.Ingresses)) + fp.Close() + }