mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
POST /projects/:id/import-compose deploys a compose file straight into a project — no template row required. Well-known DB images become managed databases attached to the project network under their service name as DNS alias; other services deploy in dependency order via deployTemplateGraph. - api: extract composeToSpecs converter (shared by the template import-compose preview and the new project endpoint); fix dropped env warnings (pointer receiver); replace insertion sort with sort.Strings - cascade: database_services.project_id links graph-provisioned DBs to their project so project delete tears down runtime + row (previously leaked container + network) - cli: new `containr import` group, `import compose <project> <file|->` - mcp: containr_projects_import_compose tool - openapi + regenerated frontend types; docs snapshot + CLI.md - frontend: compose import modal on the project workspace toolbar - tests: composeToSpecs conversion, ordering, db-image map, warnings
127 lines
3.0 KiB
Go
127 lines
3.0 KiB
Go
package api
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
func composeToSpecsForTest(t *testing.T, y string) ([]TemplateServiceSpec, []string, error) {
|
|
t.Helper()
|
|
var file composeFileDef
|
|
if err := yaml.Unmarshal([]byte(y), &file); err != nil {
|
|
t.Fatalf("yaml: %v", err)
|
|
}
|
|
return composeToSpecs(&file)
|
|
}
|
|
|
|
func TestComposeToSpecsConvertsStack(t *testing.T) {
|
|
specs, warnings, err := composeToSpecsForTest(t, `services:
|
|
web:
|
|
image: traefik/whoami:latest
|
|
ports:
|
|
- "80"
|
|
environment:
|
|
GREETING: hello-compose
|
|
depends_on:
|
|
- db
|
|
db:
|
|
image: postgres:16-alpine
|
|
environment:
|
|
POSTGRES_PASSWORD: pgsecret123
|
|
POSTGRES_DB: appdb
|
|
`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(specs) != 2 {
|
|
t.Fatalf("want 2 specs, got %d", len(specs))
|
|
}
|
|
db, web := specs[0], specs[1]
|
|
if db.Key != "db" || db.Type != "database" || db.Runtime != "postgresql" {
|
|
t.Errorf("db spec wrong: %+v", db)
|
|
}
|
|
if db.Environment["POSTGRES_PASSWORD"] != "pgsecret123" {
|
|
t.Errorf("db env lost: %v", db.Environment)
|
|
}
|
|
if web.Key != "web" || web.Type != "web" || web.Runtime != "traefik/whoami:latest" {
|
|
t.Errorf("web spec wrong: %+v", web)
|
|
}
|
|
if web.Port != 80 {
|
|
t.Errorf("web port = %d, want 80", web.Port)
|
|
}
|
|
if len(web.DependsOn) != 1 || web.DependsOn[0] != "db" {
|
|
t.Errorf("web depends_on = %v, want [db]", web.DependsOn)
|
|
}
|
|
if len(warnings) != 0 {
|
|
t.Errorf("unexpected warnings: %v", warnings)
|
|
}
|
|
}
|
|
|
|
func TestComposeToSpecsDeterministicOrder(t *testing.T) {
|
|
y := `services:
|
|
zebra:
|
|
image: alpine
|
|
alpha:
|
|
image: alpine
|
|
`
|
|
for i := 0; i < 5; i++ {
|
|
specs, _, err := composeToSpecsForTest(t, y)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if specs[0].Key != "alpha" || specs[1].Key != "zebra" {
|
|
t.Fatalf("unstable order: %v", specs)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestComposeToSpecsDatabaseImages(t *testing.T) {
|
|
cases := map[string]string{
|
|
"postgres:16": "postgresql",
|
|
"redis:7": "redis",
|
|
"mysql:8": "mysql",
|
|
"mariadb:11": "mariadb",
|
|
"mongo:7": "mongodb",
|
|
"clickhouse:24": "clickhouse",
|
|
}
|
|
for image, want := range cases {
|
|
specs, _, err := composeToSpecsForTest(t, "services:\n db:\n image: "+image+"\n")
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", image, err)
|
|
}
|
|
if specs[0].Type != "database" || specs[0].Runtime != want {
|
|
t.Errorf("%s → %+v, want database/%s", image, specs[0], want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestComposeToSpecsNoServices(t *testing.T) {
|
|
_, _, err := composeToSpecsForTest(t, "version: '3'\n")
|
|
if err == nil || !strings.Contains(err.Error(), "no services") {
|
|
t.Errorf("want no-services error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestComposeToSpecsWarnings(t *testing.T) {
|
|
_, warnings, err := composeToSpecsForTest(t, `services:
|
|
app:
|
|
build: .
|
|
orphan:
|
|
image: alpine
|
|
environment:
|
|
- UNSET_VAR
|
|
`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
joined := strings.Join(warnings, "\n")
|
|
if !strings.Contains(joined, "build contexts are not supported") {
|
|
t.Errorf("missing build warning: %v", warnings)
|
|
}
|
|
if !strings.Contains(joined, "UNSET_VAR") {
|
|
t.Errorf("missing env warning: %v", warnings)
|
|
}
|
|
}
|