Refactorisation and improvements

- image of container is now splitted in repository and tag (in values
  and deployment). The "tag" is tested in deployment
- add "chart-version" option

Code:

- globalize the PVC generation
- ensure types for environment values
- refactored to make generic the container creation in a deployment
- avoiding race condition on ServiceConfig by using a copy (then a
  pointer of this copy)
This commit is contained in:
2022-05-06 13:42:53 +02:00
parent 2721cb1136
commit 3031f37509
9 changed files with 208 additions and 115 deletions

View File

@@ -65,18 +65,21 @@ func main() {
appversion := c.Flag("app-version").Value.String()
composeFile := c.Flag("compose-file").Value.String()
appName := c.Flag("app-name").Value.String()
chartVersion := c.Flag("chart-version").Value.String()
chartDir := c.Flag("output-dir").Value.String()
indentation, err := strconv.Atoi(c.Flag("indent-size").Value.String())
if err != nil {
writers.IndentSize = indentation
}
Convert(composeFile, appversion, appName, chartDir, force)
Convert(composeFile, appversion, appName, chartDir, chartVersion, force)
},
}
convertCmd.Flags().BoolP(
"force", "f", false, "force overwrite of existing output files")
convertCmd.Flags().StringP(
"app-version", "a", AppVersion, "app version")
convertCmd.Flags().StringP(
"chart-version", "v", ChartVersion, "chart version")
convertCmd.Flags().StringP(
"compose-file", "c", ComposeFile, "docker compose file")
convertCmd.Flags().StringP(

View File

@@ -17,6 +17,7 @@ var (
AppName = "MyApp"
ChartsDir = "chart"
AppVersion = "0.0.1"
ChartVersion = "0.1.0"
)
func init() {
@@ -92,7 +93,7 @@ func detectGitVersion() (string, error) {
return defaulVersion, errors.New("git log failed")
}
func Convert(composeFile, appVersion, appName, chartDir string, force bool) {
func Convert(composeFile, appVersion, appName, chartDir, chartVersion string, force bool) {
if len(composeFile) == 0 {
fmt.Println("No compose file given")
return
@@ -138,6 +139,6 @@ func Convert(composeFile, appVersion, appName, chartDir string, force bool) {
}
// start generator
generator.Generate(p, Version, appName, appVersion, ComposeFile, dirname)
generator.Generate(p, Version, appName, appVersion, chartVersion, ComposeFile, dirname)
}

View File

@@ -10,6 +10,7 @@ import (
"net/url"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
@@ -18,6 +19,8 @@ import (
"gopkg.in/yaml.v3"
)
type EnvVal = helm.EnvValue
const (
ICON_PACKAGE = "📦"
ICON_SERVICE = "🔌"
@@ -30,10 +33,9 @@ const (
// Values is kept in memory to create a values.yaml file.
var (
Values = make(map[string]map[string]interface{})
VolumeValues = make(map[string]map[string]map[string]interface{})
VolumeValues = make(map[string]map[string]map[string]EnvVal)
EmptyDirs = []string{}
servicesMap = make(map[string]int)
serviceWaiters = make(map[string][]chan int)
locker = &sync.Mutex{}
dependScript = `
@@ -50,48 +52,22 @@ echo "Done"
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 types.ServiceConfig, linked map[string]types.ServiceConfig) chan interface{} {
ret := make(chan interface{}, len(s.Ports)+len(s.Expose)+2)
go parseService(name, s, linked, ret)
// Create a Deployment for a given compose.Service. It returns a list chan
// of HelmFileGenerator which will be used to generate the files (deployment, secrets, configMap...).
func CreateReplicaObject(name string, s types.ServiceConfig, linked map[string]types.ServiceConfig) HelmFileGenerator {
ret := make(chan HelmFile, runtime.NumCPU())
// there is a bug woth typs.ServiceConfig if we use the pointer. So we need to dereference it.
go buildDeployment(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 types.ServiceConfig, linked map[string]types.ServiceConfig, ret chan interface{}) {
// TODO: this function is a mess, need a complete refactorisation
func buildDeployment(name string, s *types.ServiceConfig, linked map[string]types.ServiceConfig, fileGeneratorChan HelmFileGenerator) {
logger.Magenta(ICON_PACKAGE+" Generating deployment for ", name)
deployment := helm.NewDeployment(name)
// adapt env
applyEnvMapLabel(&s)
// create a container for the deployment.
container := helm.NewContainer(name, s.Image, s.Environment, s.Labels)
// If some variables are secrets, set them now !
if secretFile := setSecretVar(name, &s, container); secretFile != nil {
ret <- secretFile
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
"secretRef": {
"name": secretFile.Metadata().Name,
},
})
}
setEnvToValues(name, &s, container)
prepareContainer(container, s, name)
prepareEnvFromFiles(name, s, container, ret)
// Set the containers to the deployment
deployment.Spec.Template.Spec.Containers = []*helm.Container{container}
// Prepare volumes
madePVC := make(map[string]bool)
deployment.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.
deployment.Spec.Template.Spec.InitContainers = prepareInitContainers(name, s, container)
newContainerForDeployment(name, name, deployment, s, fileGeneratorChan)
// Add selectors
selectors := buildSelector(name, s)
@@ -100,27 +76,11 @@ func parseService(name string, s types.ServiceConfig, linked map[string]types.Se
}
deployment.Spec.Template.Metadata.Labels = selectors
// Now, the linked services
// Now, the linked services (same pod)
for lname, link := range linked {
applyEnvMapLabel(&link)
container := helm.NewContainer(lname, link.Image, link.Environment, link.Labels)
// If some variables are secrets, set them now !
if secretFile := setSecretVar(lname, &link, container); secretFile != nil {
ret <- secretFile
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
"secretRef": {
"name": secretFile.Metadata().Name,
},
})
}
setEnvToValues(lname, &link, container)
prepareContainer(container, link, lname)
prepareEnvFromFiles(lname, link, container, ret)
deployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, container)
deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, prepareVolumes(name, lname, link, container, madePVC, ret)...)
deployment.Spec.Template.Spec.InitContainers = append(deployment.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
newContainerForDeployment(name, lname, deployment, &link, fileGeneratorChan)
// 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...)
@@ -144,39 +104,57 @@ func parseService(name string, s types.ServiceConfig, linked map[string]types.Se
// Then, create Services and possible Ingresses for ingress labels, "ports" and "expose" section
if len(s.Ports) > 0 || len(s.Expose) > 0 {
for _, s := range generateServicesAndIngresses(name, s) {
ret <- s
if s != nil {
fileGeneratorChan <- s
}
}
}
// add the volumes in Values
if len(VolumeValues[name]) > 0 {
AddValues(name, map[string]interface{}{"persistence": VolumeValues[name]})
AddValues(name, map[string]EnvVal{"persistence": VolumeValues[name]})
}
// the deployment is ready, give it
ret <- deployment
fileGeneratorChan <- deployment
// and then, we can say that it's the end
ret <- nil
fileGeneratorChan <- nil
}
// prepareContainer assigns image, command, env, and labels to a container.
func prepareContainer(container *helm.Container, service types.ServiceConfig, 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)
}
container.Image = "{{ .Values." + servicename + ".image }}"
// Get the image tag
imageParts := strings.Split(service.Image, ":")
tag := ""
if len(imageParts) == 2 {
container.Image = imageParts[0]
tag = imageParts[1]
}
vtag := ".Values." + servicename + ".repository.tag"
container.Image = `{{ .Values.` + servicename + `.repository.image }}` +
`{{ if ne ` + vtag + ` "" }}:{{ ` + vtag + ` }}{{ end }}`
container.Command = service.Command
AddValues(servicename, map[string]interface{}{"image": service.Image})
AddValues(servicename, map[string]EnvVal{
"repository": map[string]EnvVal{
"image": imageParts[0],
"tag": tag,
},
})
prepareProbes(servicename, service, container)
generateContainerPorts(service, servicename, container)
}
// Create a service (k8s).
func generateServicesAndIngresses(name string, s types.ServiceConfig) []interface{} {
func generateServicesAndIngresses(name string, s *types.ServiceConfig) []HelmFile {
ret := make([]interface{}, 0) // can handle helm.Service or helm.Ingress
ret := make([]HelmFile, 0) // can handle helm.Service or helm.Ingress
logger.Magenta(ICON_SERVICE+" Generating service for ", name)
ks := helm.NewService(name)
@@ -214,7 +192,7 @@ func generateServicesAndIngresses(name string, s types.ServiceConfig) []interfac
}
// Create an ingress.
func createIngress(name string, port int, s types.ServiceConfig) *helm.Ingress {
func createIngress(name string, port int, s *types.ServiceConfig) *helm.Ingress {
ingress := helm.NewIngress(name)
ingressVal := map[string]interface{}{
@@ -222,7 +200,7 @@ func createIngress(name string, port int, s types.ServiceConfig) *helm.Ingress {
"host": name + "." + helm.Appname + ".tld",
"enabled": false,
}
AddValues(name, map[string]interface{}{"ingress": ingressVal})
AddValues(name, map[string]EnvVal{"ingress": ingressVal})
ingress.Spec.Rules = []helm.IngressRule{
{
@@ -249,7 +227,7 @@ func createIngress(name string, port int, s types.ServiceConfig) *helm.Ingress {
}
// Build the selector for the service.
func buildSelector(name string, s types.ServiceConfig) map[string]string {
func buildSelector(name string, s *types.ServiceConfig) map[string]string {
return map[string]string{
"katenary.io/component": name,
"katenary.io/release": helm.ReleaseNameTpl,
@@ -290,7 +268,7 @@ func buildCMFromPath(path string) *helm.ConfigMap {
}
// generateContainerPorts add the container ports of a service.
func generateContainerPorts(s types.ServiceConfig, 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 {
@@ -323,7 +301,7 @@ func generateContainerPorts(s types.ServiceConfig, name string, container *helm.
}
// prepareVolumes add the volumes of a service.
func prepareVolumes(deployment, name string, s types.ServiceConfig, container *helm.Container, madePVC map[string]bool, ret chan interface{}) []map[string]interface{} {
func prepareVolumes(deployment, name string, s *types.ServiceConfig, container *helm.Container, fileGeneratorChan HelmFileGenerator) []map[string]interface{} {
volumes := make([]map[string]interface{}, 0)
mountPoints := make([]interface{}, 0)
@@ -401,7 +379,9 @@ func prepareVolumes(deployment, name string, s types.ServiceConfig, container *h
"mountPath": volepath,
})
}
ret <- cm
if cm != nil {
fileGeneratorChan <- cm
}
} else {
// rmove minus sign from volume name
volname = strings.ReplaceAll(volname, "-", "")
@@ -439,15 +419,13 @@ func prepareVolumes(deployment, name string, s types.ServiceConfig, container *h
})
logger.Yellow(ICON_STORE+" Generate volume values", volname, "for container named", name, "in deployment", deployment)
AddVolumeValues(deployment, volname, map[string]interface{}{
AddVolumeValues(deployment, volname, map[string]EnvVal{
"enabled": false,
"capacity": "1Gi",
})
if _, ok := madePVC[deployment+volname]; !ok {
madePVC[deployment+volname] = true
pvc := helm.NewPVC(deployment, volname)
ret <- pvc
if pvc := helm.NewPVC(deployment, volname); pvc != nil {
fileGeneratorChan <- pvc
}
}
}
@@ -456,7 +434,7 @@ func prepareVolumes(deployment, name string, s types.ServiceConfig, container *h
}
// prepareInitContainers add the init containers of a service.
func prepareInitContainers(name string, s types.ServiceConfig, 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.
@@ -496,11 +474,11 @@ func prepareInitContainers(name string, s types.ServiceConfig, container *helm.C
}
// prepareProbes generate http/tcp/command probes for a service.
func prepareProbes(name string, s types.ServiceConfig, container *helm.Container) {
func prepareProbes(name string, s *types.ServiceConfig, container *helm.Container) {
// first, check if there a label for the probe
if check, ok := s.Labels[helm.LABEL_HEALTHCHECK]; ok {
check = strings.TrimSpace(check)
p := helm.NewProbeFromService(&s)
p := helm.NewProbeFromService(s)
// get the port of the "url" check
if checkurl, err := url.Parse(check); err == nil {
if err == nil {
@@ -555,12 +533,12 @@ func buildProtoProbe(probe *helm.Probe, u *url.URL) *helm.Probe {
return probe
}
func buildCommandProbe(s types.ServiceConfig) *helm.Probe {
func buildCommandProbe(s *types.ServiceConfig) *helm.Probe {
// Get the first element of the command from ServiceConfig
first := s.HealthCheck.Test[0]
p := helm.NewProbeFromService(&s)
p := helm.NewProbeFromService(s)
switch first {
case "CMD", "CMD-SHELL":
// CMD or CMD-SHELL
@@ -578,7 +556,7 @@ func buildCommandProbe(s types.ServiceConfig) *helm.Probe {
}
// prepareEnvFromFiles generate configMap or secrets from environment files.
func prepareEnvFromFiles(name string, s types.ServiceConfig, container *helm.Container, ret chan interface{}) {
func prepareEnvFromFiles(name string, s *types.ServiceConfig, container *helm.Container, fileGeneratorChan HelmFileGenerator) {
// prepare secrets
secretsFiles := make([]string, 0)
@@ -640,12 +618,14 @@ func prepareEnvFromFiles(name string, s types.ServiceConfig, container *helm.Con
}
}
ret <- store
if store != nil {
fileGeneratorChan <- store
}
}
}
// AddValues adds values to the values.yaml map.
func AddValues(servicename string, values map[string]interface{}) {
func AddValues(servicename string, values map[string]EnvVal) {
locker.Lock()
defer locker.Unlock()
@@ -659,18 +639,18 @@ func AddValues(servicename string, values map[string]interface{}) {
}
// AddVolumeValues add a volume to the values.yaml map for the given deployment name.
func AddVolumeValues(deployment string, volname string, values map[string]interface{}) {
func AddVolumeValues(deployment string, volname string, values map[string]EnvVal) {
locker.Lock()
defer locker.Unlock()
if _, ok := VolumeValues[deployment]; !ok {
VolumeValues[deployment] = make(map[string]map[string]interface{})
VolumeValues[deployment] = make(map[string]map[string]EnvVal)
}
VolumeValues[deployment][volname] = values
}
func readEnvFile(envfilename string) map[string]string {
env := make(map[string]string)
func readEnvFile(envfilename string) map[string]EnvVal {
env := make(map[string]EnvVal)
content, err := ioutil.ReadFile(envfilename)
if err != nil {
logger.ActivateColors = true
@@ -690,14 +670,17 @@ func readEnvFile(envfilename string) map[string]string {
}
// applyEnvMapLabel will get all LABEL_MAP_ENV to rebuild the env map with tpl.
func applyEnvMapLabel(s *types.ServiceConfig) {
func applyEnvMapLabel(s *types.ServiceConfig, c *helm.Container) {
locker.Lock()
defer locker.Unlock()
mapenv, ok := s.Labels[helm.LABEL_MAP_ENV]
if !ok {
return
}
// the mapenv is a YAML string
var envmap map[string]string
var envmap map[string]EnvVal
err := yaml.Unmarshal([]byte(mapenv), &envmap)
if err != nil {
logger.ActivateColors = true
@@ -708,7 +691,18 @@ func applyEnvMapLabel(s *types.ServiceConfig) {
// add in envmap
for k, v := range envmap {
s.Environment[k] = &v
vstring := fmt.Sprintf("%v", v)
s.Environment[k] = &vstring
touched := false
for _, env := range c.Env {
if env.Name == k {
env.Value = v
touched = true
}
}
if !touched {
c.Env = append(c.Env, &helm.Value{Name: k, Value: v})
}
}
}
@@ -716,7 +710,7 @@ func applyEnvMapLabel(s *types.ServiceConfig) {
func setEnvToValues(name string, s *types.ServiceConfig, c *helm.Container) {
// crete the "environment" key
env := make(map[string]interface{})
env := make(map[string]EnvVal)
for k, v := range s.Environment {
env[k] = v
}
@@ -724,19 +718,26 @@ func setEnvToValues(name string, s *types.ServiceConfig, c *helm.Container) {
return
}
AddValues(name, map[string]interface{}{"environment": env})
AddValues(name, map[string]EnvVal{"environment": env})
for k := range env {
v := "{{ tpl .Values." + name + ".environment." + k + " . }}"
s.Environment[k] = &v
touched := false
for _, c := range c.Env {
if c.Name == k {
c.Value = v
touched = true
}
}
if !touched {
c.Env = append(c.Env, &helm.Value{Name: k, Value: v})
}
}
}
func setSecretVar(name string, s *types.ServiceConfig, c *helm.Container) *helm.Secret {
locker.Lock()
defer locker.Unlock()
// get the list of secret vars
secretvars, ok := s.Labels[helm.LABEL_SECRETVARS]
if !ok {
@@ -762,3 +763,51 @@ func setSecretVar(name string, s *types.ServiceConfig, c *helm.Container) *helm.
}
return store
}
// Generate a container in deployment with all needed objects (volumes, secrets, env, ...).
// The deployName shoud be the name of the deployment, we cannot get it from Metadata as this is a variable name.
func newContainerForDeployment(deployName, containerName string, deployment *helm.Deployment, s *types.ServiceConfig, fileGeneratorChan HelmFileGenerator) *helm.Container {
container := helm.NewContainer(containerName, s.Image, s.Environment, s.Labels)
applyEnvMapLabel(s, container)
if secretFile := setSecretVar(containerName, s, container); secretFile != nil {
fileGeneratorChan <- secretFile
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
"secretRef": {
"name": secretFile.Metadata().Name,
},
})
}
setEnvToValues(containerName, s, container)
prepareContainer(container, s, deployName)
prepareEnvFromFiles(deployName, s, container, fileGeneratorChan)
// add the container in deployment
if deployment.Spec.Template.Spec.Containers == nil {
deployment.Spec.Template.Spec.Containers = make([]*helm.Container, 0)
}
deployment.Spec.Template.Spec.Containers = append(
deployment.Spec.Template.Spec.Containers,
container,
)
// add the volumes
if deployment.Spec.Template.Spec.Volumes == nil {
deployment.Spec.Template.Spec.Volumes = make([]map[string]interface{}, 0)
}
deployment.Spec.Template.Spec.Volumes = append(
deployment.Spec.Template.Spec.Volumes,
prepareVolumes(deployName, containerName, s, container, fileGeneratorChan)...,
)
// add init containers
if deployment.Spec.Template.Spec.InitContainers == nil {
deployment.Spec.Template.Spec.InitContainers = make([]*helm.Container, 0)
}
deployment.Spec.Template.Spec.InitContainers = append(
deployment.Spec.Template.Spec.InitContainers,
prepareInitContainers(containerName, s, container)...,
)
return container
}

View File

@@ -98,7 +98,6 @@ services:
volumes:
data:
driver: local
`
var defaultCliFiles = cli.DefaultFileNames
@@ -110,6 +109,10 @@ func init() {
}
func setUp(t *testing.T) (string, *compose.Parser) {
// cleanup "made" files
helm.ResetMadePVC()
cli.DefaultFileNames = defaultCliFiles
// create a temporary directory
@@ -145,7 +148,7 @@ func setUp(t *testing.T) (string, *compose.Parser) {
p.Parse("testapp")
Generate(p, "test-0", "testapp", "1.2.3", DOCKER_COMPOSE_YML, tmp)
Generate(p, "test-0", "testapp", "1.2.3", "4.5.6", DOCKER_COMPOSE_YML, tmp)
return tmp, p
}
@@ -310,6 +313,8 @@ func TestPVC(t *testing.T) {
t.Log("Checking ", name, " pvc file")
_, err := os.Stat(path)
if err != nil {
list, _ := filepath.Glob(tmp + "/templates/*")
t.Log(list)
t.Fatal(err)
}
}

View File

@@ -16,6 +16,9 @@ import (
"gopkg.in/yaml.v3"
)
type HelmFile interface{}
type HelmFileGenerator chan HelmFile
var PrefixRE = regexp.MustCompile(`\{\{.*\}\}-?`)
func portExists(port int, ports []types.ServicePortConfig) bool {
@@ -28,7 +31,7 @@ func portExists(port int, ports []types.ServicePortConfig) bool {
return false
}
func Generate(p *compose.Parser, katernayVersion, appName, appVersion, composeFile, dirName string) {
func Generate(p *compose.Parser, katernayVersion, appName, appVersion, chartVersion, composeFile, dirName string) {
// make the appname global (yes... ugly but easy)
helm.Appname = appName
@@ -41,7 +44,7 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, composeFi
log.Fatal(err)
}
files := make(map[string]chan interface{})
files := make(map[string]HelmFileGenerator)
// Manage services, avoid linked pods and store all services port in servicesMap
avoids := make(map[string]bool)
@@ -140,21 +143,21 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, composeFi
// 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 {
for n, generator := range files {
for helmFile := range generator {
if helmFile == nil {
break
}
kind := c.(helm.Kinded).Get()
kind := helmFile.(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)
helmFile.(helm.Signable).BuildSHA(composeFile)
// Some types need special fixes in yaml generation
switch c := c.(type) {
switch c := helmFile.(type) {
case *helm.Storage:
// For storage, we need to add a "condition" to activate it
writers.BuildStorage(c, n, templatesDir)
@@ -218,7 +221,7 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, composeFi
"name": appName,
"description": "A helm chart for " + appName,
"type": "application",
"version": "0.1.0",
"version": chartVersion,
"appVersion": appVersion,
})

View File

@@ -2,6 +2,7 @@ package writers
import (
"katenary/helm"
"log"
"os"
"path/filepath"
@@ -14,12 +15,18 @@ func BuildStorage(storage *helm.Storage, name, templatesDir string) {
name = storage.Metadata.Labels[helm.K+"/component"]
pvcname := storage.Metadata.Labels[helm.K+"/pvc-name"]
fname := filepath.Join(templatesDir, name+"-"+pvcname+"."+kind+".yaml")
fp, _ := os.Create(fname)
fp, err := os.Create(fname)
if err != nil {
log.Fatal(err)
}
defer fp.Close()
volname := storage.K8sBase.Metadata.Labels[helm.K+"/pvc-name"]
fp.WriteString("{{ if .Values." + name + ".persistence." + volname + ".enabled }}\n")
enc := yaml.NewEncoder(fp)
enc.SetIndent(IndentSize)
enc.Encode(storage)
if err := enc.Encode(storage); err != nil {
log.Fatal(err)
}
fp.WriteString("{{- end -}}")
}

View File

@@ -7,6 +7,8 @@ import (
"github.com/compose-spec/compose-go/types"
)
type EnvValue interface{}
// ContainerPort represent a port mapping.
type ContainerPort struct {
Name string
@@ -16,7 +18,7 @@ type ContainerPort struct {
// Value represent a environment variable with name and value.
type Value struct {
Name string `yaml:"name"`
Value interface{} `yaml:"value"`
Value EnvValue `yaml:"value"`
}
// Container represent a container with name, image, and environment variables. It is used in Deployment.

View File

@@ -47,10 +47,12 @@ func (k *K8sBase) BuildSHA(filename string) {
k.Metadata.Annotations[K+"/docker-compose-sha1"] = fmt.Sprintf("%x", string(sum[:]))
}
// Get returns the Kind.
func (k *K8sBase) Get() string {
return k.Kind
}
// Name returns the name of the object from Metadata.
func (k *K8sBase) Name() string {
return k.Metadata.Name
}

View File

@@ -1,5 +1,20 @@
package helm
import "sync"
var (
made = make(map[string]bool)
locker = sync.Mutex{}
)
// ResetMadePVC resets the cache of made PVCs.
// Useful in tests only.
func ResetMadePVC() {
locker.Lock()
defer locker.Unlock()
made = make(map[string]bool)
}
// Storage is a struct for a PersistentVolumeClaim.
type Storage struct {
*K8sBase `yaml:",inline"`
@@ -8,6 +23,12 @@ type Storage struct {
// NewPVC creates a new PersistentVolumeClaim object.
func NewPVC(name, storageName string) *Storage {
locker.Lock()
defer locker.Unlock()
if _, ok := made[name+storageName]; ok {
return nil
}
made[name+storageName] = true
pvc := &Storage{}
pvc.K8sBase = NewBase()
pvc.K8sBase.Kind = "PersistentVolumeClaim"