2024-04-23 15:18:34 +02:00
|
|
|
package generator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-12-03 13:50:58 +01:00
|
|
|
"image"
|
|
|
|
"image/color"
|
|
|
|
"image/png"
|
2024-11-18 17:12:12 +01:00
|
|
|
"katenary/generator/labels"
|
2024-12-03 13:50:58 +01:00
|
|
|
"log"
|
2024-04-23 15:18:34 +02:00
|
|
|
"os"
|
2024-12-03 13:50:58 +01:00
|
|
|
"path/filepath"
|
2024-04-23 15:18:34 +02:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
v1 "k8s.io/api/apps/v1"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
"sigs.k8s.io/yaml"
|
|
|
|
)
|
|
|
|
|
2024-12-17 18:22:45 +01:00
|
|
|
const (
|
|
|
|
htmlContent = "<html><body><h1>Hello, World!</h1></body></html>"
|
|
|
|
developementFile = "templates/web/deployment.yaml"
|
2025-07-06 10:51:25 +02:00
|
|
|
indexHMLFile = "index.html"
|
2024-12-17 18:22:45 +01:00
|
|
|
)
|
2024-10-18 09:34:57 +02:00
|
|
|
|
2024-04-23 15:18:34 +02:00
|
|
|
func TestGenerateWithBoundVolume(t *testing.T) {
|
2024-10-18 09:34:57 +02:00
|
|
|
composeFile := `
|
2024-04-23 15:18:34 +02:00
|
|
|
services:
|
|
|
|
web:
|
|
|
|
image: nginx:1.29
|
|
|
|
volumes:
|
|
|
|
- data:/var/www
|
|
|
|
volumes:
|
|
|
|
data:
|
|
|
|
`
|
2024-10-18 09:34:57 +02:00
|
|
|
tmpDir := setup(composeFile)
|
2024-04-23 15:18:34 +02:00
|
|
|
defer teardown(tmpDir)
|
|
|
|
|
|
|
|
currentDir, _ := os.Getwd()
|
|
|
|
os.Chdir(tmpDir)
|
|
|
|
defer os.Chdir(currentDir)
|
|
|
|
|
2024-12-17 18:22:45 +01:00
|
|
|
output := internalCompileTest(t, "-s", developementFile)
|
2024-04-23 15:18:34 +02:00
|
|
|
|
|
|
|
dt := v1.Deployment{}
|
|
|
|
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
|
2024-04-23 15:45:31 +02:00
|
|
|
t.Errorf(unmarshalError, err)
|
2024-04-23 15:18:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name != "data" {
|
2024-12-17 18:22:45 +01:00
|
|
|
t.Errorf("Expected container volume name to be data: %v", dt)
|
2024-04-23 15:18:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWithStaticFiles(t *testing.T) {
|
2024-10-18 09:34:57 +02:00
|
|
|
composeFile := `
|
2024-04-23 15:18:34 +02:00
|
|
|
services:
|
|
|
|
web:
|
|
|
|
image: nginx:1.29
|
|
|
|
volumes:
|
|
|
|
- ./static:/var/www
|
|
|
|
labels:
|
2024-04-24 13:59:21 +02:00
|
|
|
%s/configmap-files: |-
|
2024-04-23 15:18:34 +02:00
|
|
|
- ./static
|
|
|
|
`
|
2024-11-18 17:12:12 +01:00
|
|
|
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
|
2024-10-18 09:34:57 +02:00
|
|
|
tmpDir := setup(composeFile)
|
2024-04-23 15:18:34 +02:00
|
|
|
defer teardown(tmpDir)
|
|
|
|
|
|
|
|
// create a static directory with an index.html file
|
|
|
|
staticDir := tmpDir + "/static"
|
|
|
|
os.Mkdir(staticDir, 0o755)
|
|
|
|
indexFile, err := os.Create(staticDir + "/index.html")
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to create index.html: %s", err)
|
|
|
|
}
|
2024-10-18 09:34:57 +02:00
|
|
|
indexFile.WriteString(htmlContent)
|
2024-04-23 15:18:34 +02:00
|
|
|
indexFile.Close()
|
|
|
|
|
|
|
|
currentDir, _ := os.Getwd()
|
|
|
|
os.Chdir(tmpDir)
|
|
|
|
defer os.Chdir(currentDir)
|
|
|
|
|
2024-12-17 18:22:45 +01:00
|
|
|
output := internalCompileTest(t, "-s", developementFile)
|
2024-04-23 15:18:34 +02:00
|
|
|
dt := v1.Deployment{}
|
|
|
|
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
|
2024-04-23 15:45:31 +02:00
|
|
|
t.Errorf(unmarshalError, err)
|
2024-04-23 15:18:34 +02:00
|
|
|
}
|
|
|
|
// get the volume mount path
|
|
|
|
volumeMountPath := dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath
|
|
|
|
if volumeMountPath != "/var/www" {
|
|
|
|
t.Errorf("Expected volume mount path to be /var/www, got %s", volumeMountPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
// read the configMap
|
|
|
|
output, err = helmTemplate(ConvertOptions{
|
|
|
|
OutputDir: tmpDir + "/chart",
|
|
|
|
}, "-s", "templates/web/statics/static/configmap.yaml")
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to run helm template: %s", err)
|
|
|
|
}
|
|
|
|
configMap := corev1.ConfigMap{}
|
|
|
|
if err := yaml.Unmarshal([]byte(output), &configMap); err != nil {
|
2024-04-23 15:45:31 +02:00
|
|
|
t.Errorf(unmarshalError, err)
|
2024-04-23 15:18:34 +02:00
|
|
|
}
|
|
|
|
data := configMap.Data
|
|
|
|
if len(data) != 1 {
|
|
|
|
t.Errorf("Expected 1 data, got %d", len(data))
|
|
|
|
}
|
2025-07-06 10:51:25 +02:00
|
|
|
if data[indexHMLFile] != htmlContent {
|
|
|
|
t.Errorf("Expected index.html to be "+htmlContent+", got %s", data[indexHMLFile])
|
2024-04-23 15:18:34 +02:00
|
|
|
}
|
|
|
|
}
|
2024-04-24 20:55:27 +02:00
|
|
|
|
|
|
|
func TestWithFileMapping(t *testing.T) {
|
2024-10-18 09:34:57 +02:00
|
|
|
composeFile := `
|
2024-04-24 20:55:27 +02:00
|
|
|
services:
|
|
|
|
web:
|
|
|
|
image: nginx:1.29
|
|
|
|
volumes:
|
|
|
|
- ./static/index.html:/var/www/index.html
|
|
|
|
labels:
|
|
|
|
%s/configmap-files: |-
|
|
|
|
- ./static/index.html
|
|
|
|
`
|
2024-11-18 17:12:12 +01:00
|
|
|
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
|
2024-10-18 09:34:57 +02:00
|
|
|
tmpDir := setup(composeFile)
|
2024-04-24 20:55:27 +02:00
|
|
|
defer teardown(tmpDir)
|
|
|
|
|
|
|
|
// create a static directory with an index.html file
|
|
|
|
staticDir := tmpDir + "/static"
|
|
|
|
os.Mkdir(staticDir, 0o755)
|
|
|
|
indexFile, err := os.Create(staticDir + "/index.html")
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to create index.html: %s", err)
|
|
|
|
}
|
2024-10-18 09:34:57 +02:00
|
|
|
indexFile.WriteString(htmlContent)
|
2024-04-24 20:55:27 +02:00
|
|
|
indexFile.Close()
|
|
|
|
|
|
|
|
currentDir, _ := os.Getwd()
|
|
|
|
os.Chdir(tmpDir)
|
|
|
|
defer os.Chdir(currentDir)
|
|
|
|
|
2024-12-17 18:22:45 +01:00
|
|
|
output := internalCompileTest(t, "-s", developementFile)
|
2024-04-24 20:55:27 +02:00
|
|
|
dt := v1.Deployment{}
|
|
|
|
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
|
|
|
|
t.Errorf(unmarshalError, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the volume mount path
|
|
|
|
volumeMountPath := dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath
|
|
|
|
if volumeMountPath != "/var/www/index.html" {
|
|
|
|
t.Errorf("Expected volume mount path to be /var/www/index.html, got %s", volumeMountPath)
|
|
|
|
}
|
|
|
|
// but this time, we need a subpath
|
|
|
|
subPath := dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].SubPath
|
2025-07-06 10:51:25 +02:00
|
|
|
if subPath != indexHMLFile {
|
2024-04-24 20:55:27 +02:00
|
|
|
t.Errorf("Expected subpath to be index.html, got %s", subPath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-03 13:50:58 +01:00
|
|
|
func TestBinaryMount(t *testing.T) {
|
|
|
|
composeFile := `
|
|
|
|
services:
|
|
|
|
web:
|
|
|
|
image: nginx
|
|
|
|
volumes:
|
|
|
|
- ./images/foo.png:/var/www/foo
|
|
|
|
labels:
|
|
|
|
%[1]s/configmap-files: |-
|
|
|
|
- ./images/foo.png
|
|
|
|
`
|
|
|
|
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
|
|
|
|
tmpDir := setup(composeFile)
|
|
|
|
log.Println(tmpDir)
|
|
|
|
defer teardown(tmpDir)
|
|
|
|
|
|
|
|
os.Mkdir(filepath.Join(tmpDir, "images"), 0o755)
|
|
|
|
|
|
|
|
// create a png image
|
|
|
|
pngFile := tmpDir + "/images/foo.png"
|
|
|
|
w, h := 100, 100
|
|
|
|
img := image.NewRGBA(image.Rect(0, 0, w, h))
|
|
|
|
red := color.RGBA{255, 0, 0, 255}
|
|
|
|
for y := 0; y < h; y++ {
|
|
|
|
for x := 0; x < w; x++ {
|
|
|
|
img.Set(x, y, red)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
blue := color.RGBA{0, 0, 255, 255}
|
|
|
|
for y := 30; y < 70; y++ {
|
|
|
|
for x := 30; x < 70; x++ {
|
|
|
|
img.Set(x, y, blue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentDir, _ := os.Getwd()
|
|
|
|
os.Chdir(tmpDir)
|
|
|
|
defer os.Chdir(currentDir)
|
|
|
|
|
|
|
|
f, err := os.Create(pngFile)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
png.Encode(f, img)
|
|
|
|
f.Close()
|
2024-12-17 18:22:45 +01:00
|
|
|
output := internalCompileTest(t, "-s", developementFile)
|
2024-12-03 14:03:36 +01:00
|
|
|
d := v1.Deployment{}
|
|
|
|
yaml.Unmarshal([]byte(output), &d)
|
|
|
|
volumes := d.Spec.Template.Spec.Volumes
|
|
|
|
if len(volumes) != 1 {
|
|
|
|
t.Errorf("Expected 1 volume, got %d", len(volumes))
|
|
|
|
}
|
|
|
|
|
|
|
|
cm := corev1.ConfigMap{}
|
|
|
|
cmContent, err := helmTemplate(ConvertOptions{
|
|
|
|
OutputDir: "chart",
|
|
|
|
}, "-s", "templates/web/statics/images/configmap.yaml")
|
2024-12-17 18:22:45 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2024-12-03 14:03:36 +01:00
|
|
|
yaml.Unmarshal([]byte(cmContent), &cm)
|
|
|
|
if im, ok := cm.BinaryData["foo.png"]; !ok {
|
|
|
|
t.Errorf("Expected foo.png to be in the configmap")
|
|
|
|
} else {
|
|
|
|
if len(im) == 0 {
|
|
|
|
t.Errorf("Expected image to be non-empty")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGloballyBinaryMount(t *testing.T) {
|
|
|
|
composeFile := `
|
|
|
|
services:
|
|
|
|
web:
|
|
|
|
image: nginx
|
|
|
|
volumes:
|
|
|
|
- ./images:/var/www/foo
|
|
|
|
labels:
|
|
|
|
%[1]s/configmap-files: |-
|
|
|
|
- ./images
|
|
|
|
`
|
|
|
|
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
|
|
|
|
tmpDir := setup(composeFile)
|
|
|
|
log.Println(tmpDir)
|
|
|
|
defer teardown(tmpDir)
|
|
|
|
|
|
|
|
os.Mkdir(filepath.Join(tmpDir, "images"), 0o755)
|
|
|
|
|
|
|
|
// create a png image
|
|
|
|
pngFile := tmpDir + "/images/foo.png"
|
|
|
|
w, h := 100, 100
|
|
|
|
img := image.NewRGBA(image.Rect(0, 0, w, h))
|
|
|
|
red := color.RGBA{255, 0, 0, 255}
|
|
|
|
for y := 0; y < h; y++ {
|
|
|
|
for x := 0; x < w; x++ {
|
|
|
|
img.Set(x, y, red)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
blue := color.RGBA{0, 0, 255, 255}
|
|
|
|
for y := 30; y < 70; y++ {
|
|
|
|
for x := 30; x < 70; x++ {
|
|
|
|
img.Set(x, y, blue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentDir, _ := os.Getwd()
|
|
|
|
os.Chdir(tmpDir)
|
|
|
|
defer os.Chdir(currentDir)
|
|
|
|
|
|
|
|
f, err := os.Create(pngFile)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
png.Encode(f, img)
|
|
|
|
f.Close()
|
2024-12-17 18:22:45 +01:00
|
|
|
output := internalCompileTest(t, "-s", developementFile)
|
2024-12-03 13:50:58 +01:00
|
|
|
d := v1.Deployment{}
|
|
|
|
yaml.Unmarshal([]byte(output), &d)
|
|
|
|
volumes := d.Spec.Template.Spec.Volumes
|
|
|
|
if len(volumes) != 1 {
|
|
|
|
t.Errorf("Expected 1 volume, got %d", len(volumes))
|
|
|
|
}
|
|
|
|
|
|
|
|
cm := corev1.ConfigMap{}
|
|
|
|
cmContent, err := helmTemplate(ConvertOptions{
|
|
|
|
OutputDir: "chart",
|
|
|
|
}, "-s", "templates/web/statics/images/configmap.yaml")
|
2024-12-17 18:22:45 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2024-12-03 13:50:58 +01:00
|
|
|
yaml.Unmarshal([]byte(cmContent), &cm)
|
|
|
|
if im, ok := cm.BinaryData["foo.png"]; !ok {
|
|
|
|
t.Errorf("Expected foo.png to be in the configmap")
|
|
|
|
} else {
|
|
|
|
if len(im) == 0 {
|
|
|
|
t.Errorf("Expected image to be non-empty")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-24 20:55:27 +02:00
|
|
|
func TestBindFrom(t *testing.T) {
|
2024-10-18 09:34:57 +02:00
|
|
|
composeFile := `
|
2024-04-24 20:55:27 +02:00
|
|
|
services:
|
|
|
|
web:
|
|
|
|
image: nginx:1.29
|
|
|
|
volumes:
|
|
|
|
- data:/var/www
|
|
|
|
|
|
|
|
fpm:
|
|
|
|
image: php:fpm
|
|
|
|
volumes:
|
|
|
|
- data:/var/www
|
|
|
|
labels:
|
|
|
|
%[1]s/ports: |
|
|
|
|
- 9000
|
|
|
|
%[1]s/same-pod: web
|
|
|
|
|
|
|
|
volumes:
|
|
|
|
data:
|
|
|
|
`
|
|
|
|
|
2024-11-18 17:12:12 +01:00
|
|
|
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
|
2024-10-18 09:34:57 +02:00
|
|
|
tmpDir := setup(composeFile)
|
2024-04-24 20:55:27 +02:00
|
|
|
defer teardown(tmpDir)
|
|
|
|
|
|
|
|
currentDir, _ := os.Getwd()
|
|
|
|
os.Chdir(tmpDir)
|
|
|
|
defer os.Chdir(currentDir)
|
|
|
|
|
2024-12-17 18:22:45 +01:00
|
|
|
output := internalCompileTest(t, "-s", developementFile)
|
2024-04-24 20:55:27 +02:00
|
|
|
dt := v1.Deployment{}
|
|
|
|
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
|
|
|
|
t.Errorf(unmarshalError, err)
|
|
|
|
}
|
|
|
|
// both containers should have the same volume mount
|
|
|
|
if dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name != "data" {
|
2024-12-17 18:22:45 +01:00
|
|
|
t.Errorf("Expected container 0 volume name to be data: %v", dt)
|
2024-04-24 20:55:27 +02:00
|
|
|
}
|
|
|
|
if dt.Spec.Template.Spec.Containers[1].VolumeMounts[0].Name != "data" {
|
2024-12-17 18:22:45 +01:00
|
|
|
t.Errorf("Expected container 1 volume name to be data: %v", dt)
|
2024-04-24 20:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
2024-11-22 16:23:00 +01:00
|
|
|
|
|
|
|
func TestExchangeVolume(t *testing.T) {
|
|
|
|
composeFile := `
|
|
|
|
services:
|
|
|
|
app1:
|
|
|
|
image: nginx:1.29
|
|
|
|
labels:
|
|
|
|
%[1]s/exchange-volumes: |-
|
|
|
|
- name: data
|
|
|
|
mountPath: /var/www
|
|
|
|
app2:
|
|
|
|
image: foo:bar
|
|
|
|
labels:
|
|
|
|
%[1]s/same-pod: app1
|
|
|
|
%[1]s/exchange-volumes: |-
|
|
|
|
- name: data
|
|
|
|
mountPath: /opt
|
|
|
|
init: cp -r /var/www /opt
|
|
|
|
`
|
|
|
|
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
|
|
|
|
tmpDir := setup(composeFile)
|
|
|
|
defer teardown(tmpDir)
|
|
|
|
|
|
|
|
currentDir, _ := os.Getwd()
|
|
|
|
os.Chdir(tmpDir)
|
|
|
|
defer os.Chdir(currentDir)
|
|
|
|
output := internalCompileTest(t, "-s", "templates/app1/deployment.yaml")
|
|
|
|
dt := v1.Deployment{}
|
|
|
|
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
|
|
|
|
t.Errorf(unmarshalError, err)
|
|
|
|
}
|
|
|
|
// the deployment should have a volume named "data"
|
|
|
|
volumes := dt.Spec.Template.Spec.Volumes
|
|
|
|
found := false
|
|
|
|
for v := range volumes {
|
|
|
|
if volumes[v].Name == "exchange-data" {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
t.Errorf("Expected volume name to be data: %v", volumes)
|
|
|
|
}
|
|
|
|
mounted := 0
|
|
|
|
// we should have a volume mount for both containers
|
|
|
|
containers := dt.Spec.Template.Spec.Containers
|
|
|
|
for c := range containers {
|
|
|
|
for _, vm := range containers[c].VolumeMounts {
|
|
|
|
if vm.Name == "exchange-data" {
|
|
|
|
mounted++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if mounted != 2 {
|
|
|
|
t.Errorf("Expected 2 mounted volumes, got %d", mounted)
|
|
|
|
}
|
|
|
|
}
|