Files
katenary/generator/configMap.go
Patrice Ferlet 4367a01769 Big refactorization
- reduce complexity
- use better tools to format the code
- add more tests
- and too many things to list here

We are rewriting for V3, so these commits are sometimes big and not
fully detailed. Of course, further work will be more documented.
2024-05-06 21:11:36 +02:00

237 lines
6.6 KiB
Go

package generator
import (
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/compose-spec/compose-go/types"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/generator/labelStructs"
"katenary/utils"
)
// only used to check interface implementation
var (
_ DataMap = (*ConfigMap)(nil)
_ Yaml = (*ConfigMap)(nil)
)
// NewFileMap creates a new DataMap from a compose service. The appName is the name of the application taken from the project name.
func NewFileMap(service types.ServiceConfig, appName, kind string) DataMap {
switch kind {
case "configmap":
return NewConfigMap(service, appName)
default:
log.Fatalf("Unknown filemap kind: %s", kind)
}
return nil
}
// FileMapUsage is the usage of the filemap.
type FileMapUsage uint8
// FileMapUsage constants.
const (
FileMapUsageConfigMap FileMapUsage = iota // pure configmap for key:values.
FileMapUsageFiles // files in a configmap.
)
// ConfigMap is a kubernetes ConfigMap.
// Implements the DataMap interface.
type ConfigMap struct {
*corev1.ConfigMap
service *types.ServiceConfig
path string
usage FileMapUsage
}
// NewConfigMap creates a new ConfigMap from a compose service. The appName is the name of the application taken from the project name.
// The ConfigMap is filled by environment variables and labels "map-env".
func NewConfigMap(service types.ServiceConfig, appName string) *ConfigMap {
done := map[string]bool{}
drop := map[string]bool{}
labelValues := []string{}
cm := &ConfigMap{
service: &service,
ConfigMap: &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: utils.TplName(service.Name, appName),
Labels: GetLabels(service.Name, appName),
Annotations: Annotations,
},
Data: make(map[string]string),
},
}
// get the secrets from the labels
secrets, err := labelStructs.SecretsFrom(service.Labels[LabelSecrets])
if err != nil {
log.Fatal(err)
}
// drop the secrets from the environment
for _, secret := range secrets {
drop[secret] = true
}
// get the label values from the labels
varDescriptons := utils.GetValuesFromLabel(service, LabelValues)
for value := range varDescriptons {
labelValues = append(labelValues, value)
}
// change the environment variables to the values defined in the values.yaml
for _, value := range labelValues {
if _, ok := service.Environment[value]; !ok {
done[value] = true
continue
}
val := utils.TplValue(service.Name, "environment."+value)
service.Environment[value] = &val
}
// remove the variables that are already defined in the environment
if l, ok := service.Labels[LabelMapEnv]; ok {
envmap, err := labelStructs.MapEnvFrom(l)
if err != nil {
log.Fatal("Error parsing map-env", err)
}
for key, value := range envmap {
cm.AddData(key, strings.ReplaceAll(value, "__APP__", appName))
done[key] = true
}
}
for key, env := range service.Environment {
_, isDropped := drop[key]
_, isDone := done[key]
if isDropped || isDone {
continue
}
cm.AddData(key, *env)
}
return cm
}
// NewConfigMapFromDirectory creates a new ConfigMap from a compose service. This path is the path to the
// file or directory. If the path is a directory, all files in the directory are added to the ConfigMap.
// Each subdirectory are ignored. Note that the Generate() function will create the subdirectories ConfigMaps.
func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string) *ConfigMap {
normalized := path
normalized = strings.TrimLeft(normalized, ".")
normalized = strings.TrimLeft(normalized, "/")
normalized = regexp.MustCompile(`[^a-zA-Z0-9-]+`).ReplaceAllString(normalized, "-")
cm := &ConfigMap{
path: path,
service: &service,
usage: FileMapUsageFiles,
ConfigMap: &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: utils.TplName(service.Name, appName) + "-" + normalized,
Labels: GetLabels(service.Name, appName),
Annotations: Annotations,
},
Data: make(map[string]string),
},
}
// cumulate the path to the WorkingDir
path = filepath.Join(service.WorkingDir, path)
path = filepath.Clean(path)
cm.AppendDir(path)
return cm
}
// SetData sets the data of the configmap. It replaces the entire data.
func (c *ConfigMap) SetData(data map[string]string) {
c.Data = data
}
// AddData adds a key value pair to the configmap. Append or overwrite the value if the key already exists.
func (c *ConfigMap) AddData(key, value string) {
c.Data[key] = value
}
// AddFile adds files from given path to the configmap. It is not recursive, to add all files in a directory,
// you need to call this function for each subdirectory.
func (c *ConfigMap) AppendDir(path string) {
// read all files in the path and add them to the configmap
stat, err := os.Stat(path)
if err != nil {
log.Fatalf("Path %s does not exist\n", path)
}
log.Printf("Appending files from %s to configmap\n", path)
// recursively read all files in the path and add them to the configmap
if stat.IsDir() {
files, err := os.ReadDir(path)
if err != nil {
log.Fatal(err)
}
for _, file := range files {
if file.IsDir() {
continue
}
path := filepath.Join(path, file.Name())
content, err := os.ReadFile(path)
if err != nil {
log.Fatal(err)
}
// remove the path from the file
filename := filepath.Base(path)
c.AddData(filename, string(content))
}
} else {
// add the file to the configmap
content, err := os.ReadFile(path)
if err != nil {
log.Fatal(err)
}
c.AddData(filepath.Base(path), string(content))
}
}
func (c *ConfigMap) AppendFile(path string) {
// read all files in the path and add them to the configmap
stat, err := os.Stat(path)
if err != nil {
log.Fatalf("Path %s does not exist\n", path)
}
// recursively read all files in the path and add them to the configmap
if !stat.IsDir() {
// add the file to the configmap
content, err := os.ReadFile(path)
if err != nil {
log.Fatal(err)
}
c.AddData(filepath.Base(path), string(content))
}
}
// Filename returns the filename of the configmap. If the configmap is used for files, the filename contains the path.
func (c *ConfigMap) Filename() string {
switch c.usage {
case FileMapUsageFiles:
return filepath.Join(c.service.Name, "statics", c.path, "configmap.yaml")
default:
return c.service.Name + ".configmap.yaml"
}
}
// Yaml returns the yaml representation of the configmap
func (c *ConfigMap) Yaml() ([]byte, error) {
return yaml.Marshal(c)
}