Compare commits
13 Commits
0.1.1-alph
...
0.1.2-alph
Author | SHA1 | Date | |
---|---|---|---|
691c1a3b78 | |||
332f7a8787 | |||
6273e5531a | |||
e0382a8b83 | |||
3385b61272 | |||
a0e02af06e | |||
7adac3662e | |||
ca9ab8a13b | |||
b16897b875 | |||
8ccdb2854b | |||
df60c2c866 | |||
fe2a655796 | |||
714ccf771d |
2
Makefile
2
Makefile
@@ -27,7 +27,7 @@ build: katenary
|
|||||||
katenary: *.go generator/*.go compose/*.go helm/*.go
|
katenary: *.go generator/*.go compose/*.go helm/*.go
|
||||||
@echo Build using $(CTN)
|
@echo Build using $(CTN)
|
||||||
ifeq ($(CTN),podman)
|
ifeq ($(CTN),podman)
|
||||||
@podman run --rm -v $(PWD):/go/src/katenary -w /go/src/katenary --userns keep-id -it golang go build -o katenary -ldflags="-X 'main.Version=$(VERSION)'" .
|
@podman run --rm -v $(PWD):/go/src/katenary:z -w /go/src/katenary --userns keep-id -it golang go build -o katenary -ldflags="-X 'main.Version=$(VERSION)'" .
|
||||||
else
|
else
|
||||||
@docker run --rm -v $(PWD):/go/src/katenary:z -w /go/src/katenary --user $(shell id -u):$(shell id -g) -e HOME=/tmp -it golang go build -o katenary -ldflags="-X 'main.Version=$(VERSION)'" .
|
@docker run --rm -v $(PWD):/go/src/katenary:z -w /go/src/katenary --user $(shell id -u):$(shell id -g) -e HOME=/tmp -it golang go build -o katenary -ldflags="-X 'main.Version=$(VERSION)'" .
|
||||||
endif
|
endif
|
||||||
|
@@ -10,6 +10,10 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ICON_EXCLAMATION = "❕"
|
||||||
|
)
|
||||||
|
|
||||||
// Parser is a docker-compose parser.
|
// Parser is a docker-compose parser.
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
Data *Compose
|
Data *Compose
|
||||||
@@ -72,6 +76,18 @@ func NewParser(filename string) *Parser {
|
|||||||
log.Fatal(strings.Join(missing, "\n"))
|
log.Fatal(strings.Join(missing, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check the build element
|
||||||
|
for name, s := range c.Services {
|
||||||
|
if s.RawBuild == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(ICON_EXCLAMATION +
|
||||||
|
" \x1b[33myou will need to build and push your image named \"" + s.Image + "\"" +
|
||||||
|
" for the \"" + name + "\" service \x1b[0m")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,4 +25,5 @@ type Service struct {
|
|||||||
Volumes []string `yaml:"volumes"`
|
Volumes []string `yaml:"volumes"`
|
||||||
Expose []int `yaml:"expose"`
|
Expose []int `yaml:"expose"`
|
||||||
EnvFiles []string `yaml:"env_file"`
|
EnvFiles []string `yaml:"env_file"`
|
||||||
|
RawBuild interface{} `yaml:"build"`
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,10 @@ const (
|
|||||||
ICON_INGRESS = "🌐"
|
ICON_INGRESS = "🌐"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RELEASE_NAME = helm.RELEASE_NAME
|
||||||
|
)
|
||||||
|
|
||||||
// Values is kept in memory to create a values.yaml file.
|
// Values is kept in memory to create a values.yaml file.
|
||||||
var Values = make(map[string]map[string]interface{})
|
var Values = make(map[string]map[string]interface{})
|
||||||
var VolumeValues = make(map[string]map[string]map[string]interface{})
|
var VolumeValues = make(map[string]map[string]map[string]interface{})
|
||||||
@@ -38,7 +42,7 @@ OK=0
|
|||||||
echo "Checking __service__ port"
|
echo "Checking __service__ port"
|
||||||
while [ $OK != 1 ]; do
|
while [ $OK != 1 ]; do
|
||||||
echo -n "."
|
echo -n "."
|
||||||
nc -z {{ .Release.Name }}-__service__ __port__ && OK=1
|
nc -z ` + RELEASE_NAME + `-__service__ __port__ && OK=1
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
@@ -91,8 +95,14 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
|
|||||||
ActivateColors = false
|
ActivateColors = false
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
section := "configMapRef"
|
||||||
|
if isSecret {
|
||||||
|
section = "secretRef"
|
||||||
|
}
|
||||||
|
|
||||||
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
|
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
|
||||||
"configMapRef": {
|
section: {
|
||||||
"name": store.Metadata().Name,
|
"name": store.Metadata().Name,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -172,7 +182,7 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
|
|||||||
cm := buildCMFromPath(volname)
|
cm := buildCMFromPath(volname)
|
||||||
volname = strings.Replace(volname, "./", "", 1)
|
volname = strings.Replace(volname, "./", "", 1)
|
||||||
volname = strings.ReplaceAll(volname, ".", "-")
|
volname = strings.ReplaceAll(volname, ".", "-")
|
||||||
cm.K8sBase.Metadata.Name = "{{ .Release.Name }}-" + volname + "-" + name
|
cm.K8sBase.Metadata.Name = RELEASE_NAME + "-" + volname + "-" + name
|
||||||
// build a configmap from the volume path
|
// build a configmap from the volume path
|
||||||
volumes = append(volumes, map[string]interface{}{
|
volumes = append(volumes, map[string]interface{}{
|
||||||
"name": volname,
|
"name": volname,
|
||||||
@@ -191,7 +201,7 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
|
|||||||
volumes = append(volumes, map[string]interface{}{
|
volumes = append(volumes, map[string]interface{}{
|
||||||
"name": volname,
|
"name": volname,
|
||||||
"persistentVolumeClaim": map[string]string{
|
"persistentVolumeClaim": map[string]string{
|
||||||
"claimName": "{{ .Release.Name }}-" + volname,
|
"claimName": RELEASE_NAME + "-" + volname,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
mountPoints = append(mountPoints, map[string]interface{}{
|
mountPoints = append(mountPoints, map[string]interface{}{
|
||||||
@@ -269,20 +279,20 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
|
|||||||
// But... some other deployment can wait for it, so we alert that this deployment hasn't got any
|
// But... some other deployment can wait for it, so we alert that this deployment hasn't got any
|
||||||
// associated service.
|
// associated service.
|
||||||
if len(s.Ports) == 0 {
|
if len(s.Ports) == 0 {
|
||||||
locker.Lock()
|
|
||||||
// alert any current or **futur** waiters that this service is not exposed
|
// alert any current or **futur** waiters that this service is not exposed
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-time.Tick(1 * time.Millisecond):
|
case <-time.Tick(1 * time.Millisecond):
|
||||||
|
locker.Lock()
|
||||||
for _, c := range serviceWaiters[name] {
|
for _, c := range serviceWaiters[name] {
|
||||||
c <- -1
|
c <- -1
|
||||||
close(c)
|
close(c)
|
||||||
}
|
}
|
||||||
|
locker.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
locker.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the volumes in Values
|
// add the volumes in Values
|
||||||
@@ -324,7 +334,7 @@ func createService(name string, s *compose.Service) []interface{} {
|
|||||||
if v, ok := s.Labels[helm.LABEL_INGRESS]; ok {
|
if v, ok := s.Labels[helm.LABEL_INGRESS]; ok {
|
||||||
port, err := strconv.Atoi(v)
|
port, err := strconv.Atoi(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("The given port \"%v\" as ingress port in %s service is not an integer\n", v, name)
|
log.Fatalf("The given port \"%v\" as ingress port in \"%s\" service is not an integer\n", v, name)
|
||||||
}
|
}
|
||||||
Cyanf(ICON_INGRESS+" Create an ingress for port %d on %s service\n", port, name)
|
Cyanf(ICON_INGRESS+" Create an ingress for port %d on %s service\n", port, name)
|
||||||
ing := createIngress(name, port, s)
|
ing := createIngress(name, port, s)
|
||||||
@@ -362,7 +372,7 @@ func createIngress(name string, port int, s *compose.Service) *helm.Ingress {
|
|||||||
PathType: "Prefix",
|
PathType: "Prefix",
|
||||||
Backend: helm.IngressBackend{
|
Backend: helm.IngressBackend{
|
||||||
Service: helm.IngressService{
|
Service: helm.IngressService{
|
||||||
Name: "{{ .Release.Name }}-" + name,
|
Name: RELEASE_NAME + "-" + name,
|
||||||
Port: map[string]interface{}{
|
Port: map[string]interface{}{
|
||||||
"number": port,
|
"number": port,
|
||||||
},
|
},
|
||||||
@@ -381,17 +391,20 @@ func createIngress(name string, port int, s *compose.Service) *helm.Ingress {
|
|||||||
// to be able to get the service name. It also try to send the data to any "waiter" for this service.
|
// to be able to get the service name. It also try to send the data to any "waiter" for this service.
|
||||||
func detected(name string, port int) {
|
func detected(name string, port int) {
|
||||||
locker.Lock()
|
locker.Lock()
|
||||||
|
defer locker.Unlock()
|
||||||
|
if _, ok := servicesMap[name]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
servicesMap[name] = port
|
servicesMap[name] = port
|
||||||
go func() {
|
go func() {
|
||||||
cx := serviceWaiters[name]
|
locker.Lock()
|
||||||
for _, c := range cx {
|
defer locker.Unlock()
|
||||||
if v, ok := servicesMap[name]; ok {
|
if cx, ok := serviceWaiters[name]; ok {
|
||||||
c <- v
|
for _, c := range cx {
|
||||||
//close(c)
|
c <- port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
locker.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPort(name string) (int, error) {
|
func getPort(name string) (int, error) {
|
||||||
@@ -404,22 +417,23 @@ func getPort(name string) (int, error) {
|
|||||||
// Waits for a service to be discovered. Sometimes, a deployment depends on another one. See the detected() function.
|
// Waits for a service to be discovered. Sometimes, a deployment depends on another one. See the detected() function.
|
||||||
func waitPort(name string) chan int {
|
func waitPort(name string) chan int {
|
||||||
locker.Lock()
|
locker.Lock()
|
||||||
|
defer locker.Unlock()
|
||||||
c := make(chan int, 0)
|
c := make(chan int, 0)
|
||||||
serviceWaiters[name] = append(serviceWaiters[name], c)
|
serviceWaiters[name] = append(serviceWaiters[name], c)
|
||||||
go func() {
|
go func() {
|
||||||
|
locker.Lock()
|
||||||
|
defer locker.Unlock()
|
||||||
if v, ok := servicesMap[name]; ok {
|
if v, ok := servicesMap[name]; ok {
|
||||||
c <- v
|
c <- v
|
||||||
//close(c)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
locker.Unlock()
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSelector(name string, s *compose.Service) map[string]string {
|
func buildSelector(name string, s *compose.Service) map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"katenary.io/component": name,
|
"katenary.io/component": name,
|
||||||
"katenary.io/release": "{{ .Release.Name }}",
|
"katenary.io/release": RELEASE_NAME,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
generator/writer.go
Normal file
110
generator/writer.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"katenary/compose"
|
||||||
|
"katenary/generator/writers"
|
||||||
|
"katenary/helm"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var PrefixRE = regexp.MustCompile(`\{\{.*\}\}-?`)
|
||||||
|
|
||||||
|
func Generate(p *compose.Parser, katernayVersion, appName, appVersion, composeFile, dirName string) {
|
||||||
|
|
||||||
|
// make the appname global (yes... ugly but easy)
|
||||||
|
helm.Appname = appName
|
||||||
|
helm.Version = katernayVersion
|
||||||
|
templatesDir := filepath.Join(dirName, "templates")
|
||||||
|
files := make(map[string]chan interface{})
|
||||||
|
|
||||||
|
for name, s := range p.Data.Services {
|
||||||
|
files[name] = CreateReplicaObject(name, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// to generate notes, we need to keep an Ingresses list
|
||||||
|
ingresses := make(map[string]*helm.Ingress)
|
||||||
|
|
||||||
|
for n, f := range files {
|
||||||
|
for c := range f {
|
||||||
|
if c == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
kind := c.(helm.Kinded).Get()
|
||||||
|
kind = strings.ToLower(kind)
|
||||||
|
|
||||||
|
// Add a SHA inside the generated file, it's only
|
||||||
|
// to make it easy to check it the compose file corresponds to the
|
||||||
|
// generated helm chart
|
||||||
|
c.(helm.Signable).BuildSHA(composeFile)
|
||||||
|
|
||||||
|
// Some types need special fixes in yaml generation
|
||||||
|
switch c := c.(type) {
|
||||||
|
case *helm.Storage:
|
||||||
|
// For storage, we need to add a "condition" to activate it
|
||||||
|
writers.BuildStorage(c, n, templatesDir)
|
||||||
|
|
||||||
|
case *helm.Deployment:
|
||||||
|
// for the deployment, we need to fix persitence volumes
|
||||||
|
// to be activated only when the storage is "enabled",
|
||||||
|
// either we use an "emptyDir"
|
||||||
|
writers.BuildDeployment(c, n, templatesDir)
|
||||||
|
|
||||||
|
case *helm.Service:
|
||||||
|
// Change the type for service if it's an "exposed" port
|
||||||
|
writers.BuildService(c, n, templatesDir)
|
||||||
|
|
||||||
|
case *helm.Ingress:
|
||||||
|
// we need to make ingresses "activable" from values
|
||||||
|
ingresses[n] = c // keep it to generate notes
|
||||||
|
writers.BuildIngress(c, n, templatesDir)
|
||||||
|
|
||||||
|
case *helm.ConfigMap, *helm.Secret:
|
||||||
|
// there could be several files, so let's force the filename
|
||||||
|
name := c.(helm.Named).Name()
|
||||||
|
name = PrefixRE.ReplaceAllString(name, "")
|
||||||
|
writers.BuildConfigMap(c, kind, n, name, templatesDir)
|
||||||
|
|
||||||
|
default:
|
||||||
|
fname := filepath.Join(templatesDir, n+"."+kind+".yaml")
|
||||||
|
fp, _ := os.Create(fname)
|
||||||
|
enc := yaml.NewEncoder(fp)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
enc.Encode(c)
|
||||||
|
fp.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create the values.yaml file
|
||||||
|
fp, _ := os.Create(filepath.Join(dirName, "values.yaml"))
|
||||||
|
enc := yaml.NewEncoder(fp)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
enc.Encode(Values)
|
||||||
|
fp.Close()
|
||||||
|
|
||||||
|
// Create tht Chart.yaml file
|
||||||
|
fp, _ = os.Create(filepath.Join(dirName, "Chart.yaml"))
|
||||||
|
fp.WriteString(`# Create on ` + time.Now().Format(time.RFC3339) + "\n")
|
||||||
|
fp.WriteString(`# Katenary command line: ` + strings.Join(os.Args, " ") + "\n")
|
||||||
|
enc = yaml.NewEncoder(fp)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
enc.Encode(map[string]interface{}{
|
||||||
|
"apiVersion": "v2",
|
||||||
|
"name": appName,
|
||||||
|
"description": "A helm chart for " + appName,
|
||||||
|
"type": "application",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"appVersion": appVersion,
|
||||||
|
})
|
||||||
|
fp.Close()
|
||||||
|
|
||||||
|
// And finally, create a NOTE.txt file
|
||||||
|
fp, _ = os.Create(filepath.Join(templatesDir, "NOTES.txt"))
|
||||||
|
fp.WriteString(helm.GenerateNotesFile(ingresses))
|
||||||
|
fp.Close()
|
||||||
|
}
|
17
generator/writers/configmap.go
Normal file
17
generator/writers/configmap.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package writers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildConfigMap(c interface{}, kind, servicename, name, templatesDir string) {
|
||||||
|
fname := filepath.Join(templatesDir, servicename+"."+name+"."+kind+".yaml")
|
||||||
|
fp, _ := os.Create(fname)
|
||||||
|
enc := yaml.NewEncoder(fp)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
enc.Encode(c)
|
||||||
|
fp.Close()
|
||||||
|
}
|
40
generator/writers/deployment.go
Normal file
40
generator/writers/deployment.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package writers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"katenary/helm"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildDeployment(deployment *helm.Deployment, name, templatesDir string) {
|
||||||
|
kind := "deployment"
|
||||||
|
fname := filepath.Join(templatesDir, name+"."+kind+".yaml")
|
||||||
|
fp, _ := os.Create(fname)
|
||||||
|
buffer := bytes.NewBuffer(nil)
|
||||||
|
enc := yaml.NewEncoder(buffer)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
enc.Encode(deployment)
|
||||||
|
_content := string(buffer.Bytes())
|
||||||
|
content := strings.Split(string(_content), "\n")
|
||||||
|
dataname := ""
|
||||||
|
component := deployment.Spec.Selector["matchLabels"].(map[string]string)[helm.K+"/component"]
|
||||||
|
for _, line := range content {
|
||||||
|
if strings.Contains(line, "name:") {
|
||||||
|
dataname = strings.Split(line, ":")[1]
|
||||||
|
dataname = strings.TrimSpace(dataname)
|
||||||
|
} else if strings.Contains(line, "persistentVolumeClaim") {
|
||||||
|
line = " {{- if .Values." + component + ".persistence." + dataname + ".enabled }}\n" + line
|
||||||
|
} else if strings.Contains(line, "claimName") {
|
||||||
|
line += "\n {{ else }}"
|
||||||
|
line += "\n emptyDir: {}"
|
||||||
|
line += "\n {{- end }}"
|
||||||
|
}
|
||||||
|
fp.WriteString(line + "\n")
|
||||||
|
}
|
||||||
|
fp.Close()
|
||||||
|
|
||||||
|
}
|
40
generator/writers/ingress.go
Normal file
40
generator/writers/ingress.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package writers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"katenary/helm"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildIngress(ingress *helm.Ingress, name, templatesDir string) {
|
||||||
|
kind := "ingress"
|
||||||
|
buffer := bytes.NewBuffer(nil)
|
||||||
|
fname := filepath.Join(templatesDir, name+"."+kind+".yaml")
|
||||||
|
enc := yaml.NewEncoder(buffer)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
buffer.WriteString("{{- if .Values." + name + ".ingress.enabled -}}\n")
|
||||||
|
enc.Encode(ingress)
|
||||||
|
buffer.WriteString("{{- end -}}")
|
||||||
|
|
||||||
|
fp, _ := os.Create(fname)
|
||||||
|
content := string(buffer.Bytes())
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
for _, l := range lines {
|
||||||
|
if strings.Contains(l, "ingressClassName") {
|
||||||
|
p := strings.Split(l, ":")
|
||||||
|
condition := p[1]
|
||||||
|
condition = strings.ReplaceAll(condition, "'", "")
|
||||||
|
condition = strings.ReplaceAll(condition, "{{", "")
|
||||||
|
condition = strings.ReplaceAll(condition, "}}", "")
|
||||||
|
condition = strings.TrimSpace(condition)
|
||||||
|
condition = "{{- if " + condition + " }}"
|
||||||
|
l = " " + condition + "\n" + l + "\n {{- end }}"
|
||||||
|
}
|
||||||
|
fp.WriteString(l + "\n")
|
||||||
|
}
|
||||||
|
fp.Close()
|
||||||
|
}
|
23
generator/writers/service.go
Normal file
23
generator/writers/service.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package writers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"katenary/helm"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildService(service *helm.Service, name, templatesDir string) {
|
||||||
|
kind := "service"
|
||||||
|
suffix := ""
|
||||||
|
if service.Spec.Type == "NodePort" {
|
||||||
|
suffix = "-external"
|
||||||
|
}
|
||||||
|
fname := filepath.Join(templatesDir, name+suffix+"."+kind+".yaml")
|
||||||
|
fp, _ := os.Create(fname)
|
||||||
|
enc := yaml.NewEncoder(fp)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
enc.Encode(service)
|
||||||
|
fp.Close()
|
||||||
|
}
|
21
generator/writers/storage.go
Normal file
21
generator/writers/storage.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package writers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"katenary/helm"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildStorage(storage *helm.Storage, name, templatesDir string) {
|
||||||
|
kind := "pvc"
|
||||||
|
fname := filepath.Join(templatesDir, name+"."+kind+".yaml")
|
||||||
|
fp, _ := os.Create(fname)
|
||||||
|
volname := storage.K8sBase.Metadata.Labels[helm.K+"/pvc-name"]
|
||||||
|
fp.WriteString("{{ if .Values." + name + ".persistence." + volname + ".enabled }}\n")
|
||||||
|
enc := yaml.NewEncoder(fp)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
enc.Encode(storage)
|
||||||
|
fp.WriteString("{{- end -}}")
|
||||||
|
}
|
@@ -22,7 +22,7 @@ func NewConfigMap(name string) *ConfigMap {
|
|||||||
base := NewBase()
|
base := NewBase()
|
||||||
base.ApiVersion = "v1"
|
base.ApiVersion = "v1"
|
||||||
base.Kind = "ConfigMap"
|
base.Kind = "ConfigMap"
|
||||||
base.Metadata.Name = "{{ .Release.Name }}-" + name
|
base.Metadata.Name = RELEASE_NAME + "-" + name
|
||||||
base.Metadata.Labels[K+"/component"] = name
|
base.Metadata.Labels[K+"/component"] = name
|
||||||
return &ConfigMap{
|
return &ConfigMap{
|
||||||
K8sBase: base,
|
K8sBase: base,
|
||||||
@@ -66,7 +66,7 @@ func NewSecret(name string) *Secret {
|
|||||||
base := NewBase()
|
base := NewBase()
|
||||||
base.ApiVersion = "v1"
|
base.ApiVersion = "v1"
|
||||||
base.Kind = "Secret"
|
base.Kind = "Secret"
|
||||||
base.Metadata.Name = "{{ .Release.Name }}-" + name
|
base.Metadata.Name = RELEASE_NAME + "-" + name
|
||||||
base.Metadata.Labels[K+"/component"] = name
|
base.Metadata.Labels[K+"/component"] = name
|
||||||
return &Secret{
|
return &Secret{
|
||||||
K8sBase: base,
|
K8sBase: base,
|
||||||
|
@@ -10,7 +10,7 @@ type Deployment struct {
|
|||||||
|
|
||||||
func NewDeployment(name string) *Deployment {
|
func NewDeployment(name string) *Deployment {
|
||||||
d := &Deployment{K8sBase: NewBase(), Spec: NewDepSpec()}
|
d := &Deployment{K8sBase: NewBase(), Spec: NewDepSpec()}
|
||||||
d.K8sBase.Metadata.Name = "{{ .Release.Name }}-" + name
|
d.K8sBase.Metadata.Name = RELEASE_NAME + "-" + name
|
||||||
d.K8sBase.ApiVersion = "apps/v1"
|
d.K8sBase.ApiVersion = "apps/v1"
|
||||||
d.K8sBase.Kind = "Deployment"
|
d.K8sBase.Kind = "Deployment"
|
||||||
d.K8sBase.Metadata.Labels[K+"/component"] = name
|
d.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||||
@@ -67,7 +67,7 @@ func NewContainer(name, image string, environment, labels map[string]string) *Co
|
|||||||
for n, v := range environment {
|
for n, v := range environment {
|
||||||
for _, name := range toServices {
|
for _, name := range toServices {
|
||||||
if name == n {
|
if name == n {
|
||||||
v = "{{ .Release.Name }}-" + v
|
v = RELEASE_NAME + "-" + v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container.Env[idx] = Value{Name: n, Value: v}
|
container.Env[idx] = Value{Name: n, Value: v}
|
||||||
|
@@ -8,7 +8,7 @@ type Ingress struct {
|
|||||||
func NewIngress(name string) *Ingress {
|
func NewIngress(name string) *Ingress {
|
||||||
i := &Ingress{}
|
i := &Ingress{}
|
||||||
i.K8sBase = NewBase()
|
i.K8sBase = NewBase()
|
||||||
i.K8sBase.Metadata.Name = "{{ .Release.Name }}-" + name
|
i.K8sBase.Metadata.Name = RELEASE_NAME + "-" + name
|
||||||
i.K8sBase.Kind = "Ingress"
|
i.K8sBase.Kind = "Ingress"
|
||||||
i.ApiVersion = "networking.k8s.io/v1"
|
i.ApiVersion = "networking.k8s.io/v1"
|
||||||
i.K8sBase.Metadata.Labels[K+"/component"] = name
|
i.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||||
@@ -18,7 +18,6 @@ func NewIngress(name string) *Ingress {
|
|||||||
|
|
||||||
func (i *Ingress) SetIngressClass(name string) {
|
func (i *Ingress) SetIngressClass(name string) {
|
||||||
class := "{{ .Values." + name + ".ingress.class }}"
|
class := "{{ .Values." + name + ".ingress.class }}"
|
||||||
i.Metadata.Annotations["kubernetes.io/ingress.class"] = class
|
|
||||||
i.Spec.IngressClassName = class
|
i.Spec.IngressClassName = class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ Your application is now deployed. This may take a while to be up and responding.
|
|||||||
__list__
|
__list__
|
||||||
`
|
`
|
||||||
|
|
||||||
func GenNotes(ingressess map[string]*Ingress) string {
|
func GenerateNotesFile(ingressess map[string]*Ingress) string {
|
||||||
|
|
||||||
list := make([]string, 0)
|
list := make([]string, 0)
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ func NewService(name string) *Service {
|
|||||||
K8sBase: NewBase(),
|
K8sBase: NewBase(),
|
||||||
Spec: NewServiceSpec(),
|
Spec: NewServiceSpec(),
|
||||||
}
|
}
|
||||||
s.K8sBase.Metadata.Name = "{{ .Release.Name }}-" + name
|
s.K8sBase.Metadata.Name = RELEASE_NAME + "-" + name
|
||||||
s.K8sBase.Kind = "Service"
|
s.K8sBase.Kind = "Service"
|
||||||
s.K8sBase.ApiVersion = "v1"
|
s.K8sBase.ApiVersion = "v1"
|
||||||
s.K8sBase.Metadata.Labels[K+"/component"] = name
|
s.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||||
|
@@ -11,7 +11,7 @@ func NewPVC(name, storageName string) *Storage {
|
|||||||
pvc.K8sBase.Kind = "PersistentVolumeClaim"
|
pvc.K8sBase.Kind = "PersistentVolumeClaim"
|
||||||
pvc.K8sBase.Metadata.Labels[K+"/pvc-name"] = storageName
|
pvc.K8sBase.Metadata.Labels[K+"/pvc-name"] = storageName
|
||||||
pvc.K8sBase.ApiVersion = "v1"
|
pvc.K8sBase.ApiVersion = "v1"
|
||||||
pvc.K8sBase.Metadata.Name = "{{ .Release.Name }}-" + storageName
|
pvc.K8sBase.Metadata.Name = RELEASE_NAME + "-" + storageName
|
||||||
pvc.K8sBase.Metadata.Labels[K+"/component"] = name
|
pvc.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||||
pvc.Spec = &PVCSpec{
|
pvc.Spec = &PVCSpec{
|
||||||
Resouces: map[string]interface{}{
|
Resouces: map[string]interface{}{
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package helm
|
package helm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha1"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const K = "katenary.io"
|
const K = "katenary.io"
|
||||||
|
const RELEASE_NAME = "{{ .Release.Name }}"
|
||||||
const (
|
const (
|
||||||
LABEL_ENV_SECRET = K + "/secret-envfiles"
|
LABEL_ENV_SECRET = K + "/secret-envfiles"
|
||||||
LABEL_PORT = K + "/ports"
|
LABEL_PORT = K + "/ports"
|
||||||
@@ -17,9 +18,10 @@ const (
|
|||||||
LABEL_VOL_CM = K + "/configmap-volumes"
|
LABEL_VOL_CM = K + "/configmap-volumes"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Appname = ""
|
var (
|
||||||
|
Appname = ""
|
||||||
var Version = "1.0" // should be set from main.Version
|
Version = "1.0" // should be set from main.Version
|
||||||
|
)
|
||||||
|
|
||||||
type Kinded interface {
|
type Kinded interface {
|
||||||
Get() string
|
Get() string
|
||||||
@@ -58,16 +60,18 @@ func NewBase() *K8sBase {
|
|||||||
b := &K8sBase{
|
b := &K8sBase{
|
||||||
Metadata: NewMetadata(),
|
Metadata: NewMetadata(),
|
||||||
}
|
}
|
||||||
|
// add some information of the build
|
||||||
b.Metadata.Labels[K+"/project"] = GetProjectName()
|
b.Metadata.Labels[K+"/project"] = GetProjectName()
|
||||||
b.Metadata.Labels[K+"/release"] = "{{ .Release.Name }}"
|
b.Metadata.Labels[K+"/release"] = RELEASE_NAME
|
||||||
b.Metadata.Annotations[K+"/version"] = Version
|
b.Metadata.Annotations[K+"/version"] = Version
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *K8sBase) BuildSHA(filename string) {
|
func (k *K8sBase) BuildSHA(filename string) {
|
||||||
c, _ := ioutil.ReadFile(filename)
|
c, _ := ioutil.ReadFile(filename)
|
||||||
sum := sha256.Sum256(c)
|
//sum := sha256.Sum256(c)
|
||||||
k.Metadata.Annotations[K+"/docker-compose-sha256"] = fmt.Sprintf("%x", string(sum[:]))
|
sum := sha1.Sum(c)
|
||||||
|
k.Metadata.Annotations[K+"/docker-compose-sha1"] = fmt.Sprintf("%x", string(sum[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *K8sBase) Get() string {
|
func (k *K8sBase) Get() string {
|
||||||
|
236
main.go
236
main.go
@@ -1,51 +1,96 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"katenary/compose"
|
"katenary/compose"
|
||||||
"katenary/generator"
|
"katenary/generator"
|
||||||
"katenary/helm"
|
"katenary/helm"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ComposeFile = "docker-compose.yaml"
|
var ComposeFile = "docker-compose.yaml"
|
||||||
var AppName = "MyApp"
|
var AppName = "MyApp"
|
||||||
var AppVersion = "0.0.1"
|
var Version = "master" // set at build time to the git version/tag
|
||||||
var Version = "master"
|
|
||||||
var ChartsDir = "chart"
|
var ChartsDir = "chart"
|
||||||
|
|
||||||
var PrefixRE = regexp.MustCompile(`\{\{.*\}\}-?`)
|
func detectGitVersion() (string, error) {
|
||||||
|
defaulVersion := "0.0.1"
|
||||||
|
// Check if .git directory exists
|
||||||
|
if s, err := os.Stat(".git"); err != nil {
|
||||||
|
// .git should be a directory
|
||||||
|
return defaulVersion, errors.New("no git repository found")
|
||||||
|
} else if !s.IsDir() {
|
||||||
|
// .git should be a directory
|
||||||
|
return defaulVersion, errors.New(".git is not a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if "git" executable is callable
|
||||||
|
if _, err := exec.LookPath("git"); err != nil {
|
||||||
|
return defaulVersion, errors.New("git executable not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the latest commit hash
|
||||||
|
if out, err := exec.Command("git", "log", "-n1", "--pretty=format:%h").Output(); err == nil {
|
||||||
|
latestCommit := strings.TrimSpace(string(out))
|
||||||
|
// then get the current branch/tag
|
||||||
|
out, err := exec.Command("git", "branch", "--show-current").Output()
|
||||||
|
if err != nil {
|
||||||
|
return defaulVersion, errors.New("git branch --show-current failed")
|
||||||
|
} else {
|
||||||
|
currentBranch := strings.TrimSpace(string(out))
|
||||||
|
// finally, check if the current tag (if exists) correspond to the current commit
|
||||||
|
// git describe --exact-match --tags <latestCommit>
|
||||||
|
out, err := exec.Command("git", "describe", "--exact-match", "--tags", latestCommit).Output()
|
||||||
|
if err == nil {
|
||||||
|
return strings.TrimSpace(string(out)), nil
|
||||||
|
} else {
|
||||||
|
return currentBranch + "-" + latestCommit, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaulVersion, errors.New("git log failed")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
appVersion := "0.0.1"
|
||||||
|
helpMessageForAppversion := "The version of the application. " +
|
||||||
|
"Default is 0.0.1. If you are using git, it will be the git version. " +
|
||||||
|
"Otherwise, it will be the branch name and the commit hash."
|
||||||
|
if v, err := detectGitVersion(); err == nil {
|
||||||
|
appVersion = v
|
||||||
|
helpMessageForAppversion = "The version of the application. " +
|
||||||
|
"If not set, the version will be detected from git."
|
||||||
|
}
|
||||||
|
|
||||||
|
// flags
|
||||||
flag.StringVar(&ChartsDir, "chart-dir", ChartsDir, "set the chart directory")
|
flag.StringVar(&ChartsDir, "chart-dir", ChartsDir, "set the chart directory")
|
||||||
flag.StringVar(&ComposeFile, "compose", ComposeFile, "set the compose file to parse")
|
flag.StringVar(&ComposeFile, "compose", ComposeFile, "set the compose file to parse")
|
||||||
flag.StringVar(&AppName, "appname", helm.GetProjectName(), "set the helm chart app name")
|
flag.StringVar(&AppName, "appname", helm.GetProjectName(), "set the helm chart app name")
|
||||||
flag.StringVar(&AppVersion, "appversion", AppVersion, "set the chart appVersion")
|
flag.StringVar(&appVersion, "appversion", appVersion, helpMessageForAppversion)
|
||||||
version := flag.Bool("version", false, "Show version and exit")
|
version := flag.Bool("version", false, "show version and exit")
|
||||||
force := flag.Bool("force", false, "force the removal of the chart-dir")
|
force := flag.Bool("force", false, "force the removal of the chart-dir")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
// Only display the version
|
||||||
if *version {
|
if *version {
|
||||||
fmt.Println(Version)
|
fmt.Println(Version)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make the appname global (yes...)
|
|
||||||
helm.Appname = AppName
|
|
||||||
dirname := filepath.Join(ChartsDir, AppName)
|
dirname := filepath.Join(ChartsDir, AppName)
|
||||||
|
|
||||||
if _, err := os.Stat(dirname); err == nil && !*force {
|
if _, err := os.Stat(dirname); err == nil && !*force {
|
||||||
response := ""
|
response := ""
|
||||||
for response != "y" && response != "n" {
|
for response != "y" && response != "n" {
|
||||||
response = "n"
|
response = "n"
|
||||||
fmt.Printf("The %s directory already exists, it will be \x1b[31;1mremoved\x1b[0m!\nDo you really want to continue ? [y/N]: ", dirname)
|
fmt.Printf(""+
|
||||||
|
"The %s directory already exists, it will be \x1b[31;1mremoved\x1b[0m!\n"+
|
||||||
|
"Do you really want to continue? [y/N]: ", dirname)
|
||||||
fmt.Scanf("%s", &response)
|
fmt.Scanf("%s", &response)
|
||||||
response = strings.ToLower(response)
|
response = strings.ToLower(response)
|
||||||
}
|
}
|
||||||
@@ -55,159 +100,24 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
os.RemoveAll(dirname)
|
// cleanup and create the chart directory (until "templates")
|
||||||
templatesDir := filepath.Join(dirname, "templates")
|
if err := os.RemoveAll(dirname); err != nil {
|
||||||
os.MkdirAll(templatesDir, 0755)
|
fmt.Printf("Error removing %s: %s\n", dirname, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
helm.Version = Version
|
// create the templates directory
|
||||||
|
templatesDir := filepath.Join(dirname, "templates")
|
||||||
|
if err := os.MkdirAll(templatesDir, 0755); err != nil {
|
||||||
|
fmt.Printf("Error creating %s: %s\n", templatesDir, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the compose file now
|
||||||
p := compose.NewParser(ComposeFile)
|
p := compose.NewParser(ComposeFile)
|
||||||
p.Parse(AppName)
|
p.Parse(AppName)
|
||||||
|
|
||||||
files := make(map[string]chan interface{})
|
// start generator
|
||||||
|
generator.Generate(p, Version, AppName, appVersion, ComposeFile, dirname)
|
||||||
|
|
||||||
//wait := sync.WaitGroup{}
|
|
||||||
for name, s := range p.Data.Services {
|
|
||||||
//wait.Add(1)
|
|
||||||
// it's mandatory to build in goroutines because some dependencies can
|
|
||||||
// wait for a port number discovery.
|
|
||||||
// So the entire services are built in parallel.
|
|
||||||
//go func(name string, s compose.Service) {
|
|
||||||
// defer wait.Done()
|
|
||||||
o := generator.CreateReplicaObject(name, s)
|
|
||||||
files[name] = o
|
|
||||||
//}(name, s)
|
|
||||||
}
|
|
||||||
//wait.Wait()
|
|
||||||
|
|
||||||
// to generate notes, we need to keep an Ingresses list
|
|
||||||
ingresses := make(map[string]*helm.Ingress)
|
|
||||||
|
|
||||||
for n, f := range files {
|
|
||||||
for c := range f {
|
|
||||||
if c == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
kind := c.(helm.Kinded).Get()
|
|
||||||
kind = strings.ToLower(kind)
|
|
||||||
c.(helm.Signable).BuildSHA(ComposeFile)
|
|
||||||
switch c := c.(type) {
|
|
||||||
case *helm.Storage:
|
|
||||||
fname := filepath.Join(templatesDir, n+"."+kind+".yaml")
|
|
||||||
fp, _ := os.Create(fname)
|
|
||||||
volname := c.K8sBase.Metadata.Labels[helm.K+"/pvc-name"]
|
|
||||||
fp.WriteString("{{ if .Values." + n + ".persistence." + volname + ".enabled }}\n")
|
|
||||||
enc := yaml.NewEncoder(fp)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
enc.Encode(c)
|
|
||||||
fp.WriteString("{{- end -}}")
|
|
||||||
case *helm.Deployment:
|
|
||||||
fname := filepath.Join(templatesDir, n+"."+kind+".yaml")
|
|
||||||
fp, _ := os.Create(fname)
|
|
||||||
buffer := bytes.NewBuffer(nil)
|
|
||||||
enc := yaml.NewEncoder(buffer)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
enc.Encode(c)
|
|
||||||
_content := string(buffer.Bytes())
|
|
||||||
content := strings.Split(string(_content), "\n")
|
|
||||||
dataname := ""
|
|
||||||
component := c.Spec.Selector["matchLabels"].(map[string]string)[helm.K+"/component"]
|
|
||||||
for _, line := range content {
|
|
||||||
if strings.Contains(line, "name:") {
|
|
||||||
dataname = strings.Split(line, ":")[1]
|
|
||||||
dataname = strings.TrimSpace(dataname)
|
|
||||||
} else if strings.Contains(line, "persistentVolumeClaim") {
|
|
||||||
line = " {{- if .Values." + component + ".persistence." + dataname + ".enabled }}\n" + line
|
|
||||||
} else if strings.Contains(line, "claimName") {
|
|
||||||
line += "\n {{ else }}"
|
|
||||||
line += "\n emptyDir: {}"
|
|
||||||
line += "\n {{- end }}"
|
|
||||||
}
|
|
||||||
fp.WriteString(line + "\n")
|
|
||||||
}
|
|
||||||
fp.Close()
|
|
||||||
|
|
||||||
case *helm.Service:
|
|
||||||
suffix := ""
|
|
||||||
if c.Spec.Type == "NodePort" {
|
|
||||||
suffix = "-external"
|
|
||||||
}
|
|
||||||
fname := filepath.Join(templatesDir, n+suffix+"."+kind+".yaml")
|
|
||||||
fp, _ := os.Create(fname)
|
|
||||||
enc := yaml.NewEncoder(fp)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
enc.Encode(c)
|
|
||||||
fp.Close()
|
|
||||||
|
|
||||||
case *helm.Ingress:
|
|
||||||
buffer := bytes.NewBuffer(nil)
|
|
||||||
fname := filepath.Join(templatesDir, n+"."+kind+".yaml")
|
|
||||||
ingresses[n] = c // keep it to generate notes
|
|
||||||
enc := yaml.NewEncoder(buffer)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
buffer.WriteString("{{- if .Values." + n + ".ingress.enabled -}}\n")
|
|
||||||
enc.Encode(c)
|
|
||||||
buffer.WriteString("{{- end -}}")
|
|
||||||
|
|
||||||
fp, _ := os.Create(fname)
|
|
||||||
content := string(buffer.Bytes())
|
|
||||||
lines := strings.Split(content, "\n")
|
|
||||||
for _, l := range lines {
|
|
||||||
if strings.Contains(l, "ingressClassName") {
|
|
||||||
p := strings.Split(l, ":")
|
|
||||||
condition := p[1]
|
|
||||||
condition = strings.ReplaceAll(condition, "'", "")
|
|
||||||
condition = strings.ReplaceAll(condition, "{{", "")
|
|
||||||
condition = strings.ReplaceAll(condition, "}}", "")
|
|
||||||
condition = strings.TrimSpace(condition)
|
|
||||||
condition = "{{- if " + condition + " }}"
|
|
||||||
l = " " + condition + "\n" + l + "\n {{- end }}"
|
|
||||||
}
|
|
||||||
fp.WriteString(l + "\n")
|
|
||||||
}
|
|
||||||
fp.Close()
|
|
||||||
|
|
||||||
case *helm.ConfigMap, *helm.Secret:
|
|
||||||
// there could be several files, so let's force the filename
|
|
||||||
name := c.(helm.Named).Name()
|
|
||||||
name = PrefixRE.ReplaceAllString(name, "")
|
|
||||||
fname := filepath.Join(templatesDir, n+"."+name+"."+kind+".yaml")
|
|
||||||
fp, _ := os.Create(fname)
|
|
||||||
enc := yaml.NewEncoder(fp)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
enc.Encode(c)
|
|
||||||
fp.Close()
|
|
||||||
|
|
||||||
default:
|
|
||||||
fname := filepath.Join(templatesDir, n+"."+kind+".yaml")
|
|
||||||
fp, _ := os.Create(fname)
|
|
||||||
enc := yaml.NewEncoder(fp)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
enc.Encode(c)
|
|
||||||
fp.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fp, _ := os.Create(filepath.Join(dirname, "values.yaml"))
|
|
||||||
enc := yaml.NewEncoder(fp)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
enc.Encode(generator.Values)
|
|
||||||
fp.Close()
|
|
||||||
|
|
||||||
fp, _ = os.Create(filepath.Join(dirname, "Chart.yaml"))
|
|
||||||
enc = yaml.NewEncoder(fp)
|
|
||||||
enc.SetIndent(2)
|
|
||||||
enc.Encode(map[string]interface{}{
|
|
||||||
"apiVersion": "v2",
|
|
||||||
"name": AppName,
|
|
||||||
"description": "A helm chart for " + AppName,
|
|
||||||
"type": "application",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"appVersion": AppVersion,
|
|
||||||
})
|
|
||||||
fp.Close()
|
|
||||||
|
|
||||||
fp, _ = os.Create(filepath.Join(templatesDir, "NOTES.txt"))
|
|
||||||
fp.WriteString(helm.GenNotes(ingresses))
|
|
||||||
fp.Close()
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user