mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
feat: Initial support for Compose configs (#116)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
)
|
||||
|
||||
// TODO: add support for short syntax configs
|
||||
func configSpecsFromCompose(
|
||||
configs types.Configs, serviceConfigs []types.ServiceConfigObjConfig, workingDir string,
|
||||
) ([]api.ConfigSpec, []api.ConfigMount, error) {
|
||||
var configSpecs []api.ConfigSpec
|
||||
var configMounts []api.ConfigMount
|
||||
|
||||
for _, serviceConfig := range serviceConfigs {
|
||||
var spec api.ConfigSpec
|
||||
|
||||
if projectConfig, exists := configs[serviceConfig.Source]; exists {
|
||||
if projectConfig.External {
|
||||
return nil, nil, fmt.Errorf("external configs are not supported: %s",
|
||||
serviceConfig.Source)
|
||||
}
|
||||
|
||||
spec = api.ConfigSpec{
|
||||
Name: serviceConfig.Source,
|
||||
Content: []byte(projectConfig.Content),
|
||||
}
|
||||
|
||||
// If File is specified, read the file contents
|
||||
if projectConfig.File != "" {
|
||||
configPath := projectConfig.File
|
||||
// TODO: handle this in a separate function?
|
||||
if !filepath.IsAbs(configPath) {
|
||||
configPath = filepath.Join(workingDir, configPath)
|
||||
}
|
||||
|
||||
fileContent, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("read config from file '%s': %w", projectConfig.File, err)
|
||||
}
|
||||
spec.Content = fileContent
|
||||
}
|
||||
} else {
|
||||
return nil, nil, fmt.Errorf("config '%s' not found in project configs", serviceConfig.Source)
|
||||
}
|
||||
|
||||
configSpecs = append(configSpecs, spec)
|
||||
|
||||
// Create config mount
|
||||
target := serviceConfig.Target
|
||||
if target == "" {
|
||||
target = "/" + serviceConfig.Source // Default mount path
|
||||
}
|
||||
|
||||
mount := api.ConfigMount{
|
||||
ConfigName: spec.Name,
|
||||
ContainerPath: target,
|
||||
Uid: serviceConfig.UID,
|
||||
Gid: serviceConfig.GID,
|
||||
}
|
||||
|
||||
if serviceConfig.Mode != nil {
|
||||
mode := os.FileMode(*serviceConfig.Mode)
|
||||
mount.Mode = &mode
|
||||
}
|
||||
|
||||
configMounts = append(configMounts, mount)
|
||||
}
|
||||
|
||||
return configSpecs, configMounts, nil
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfigSpecsFromCompose(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
configs types.Configs
|
||||
serviceConfigs []types.ServiceConfigObjConfig
|
||||
expectedSpecs []api.ConfigSpec
|
||||
expectedMounts []api.ConfigMount
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "project-level config with file",
|
||||
configs: types.Configs{
|
||||
"app-config": types.ConfigObjConfig{
|
||||
File: "testdata/config1.txt",
|
||||
},
|
||||
},
|
||||
serviceConfigs: []types.ServiceConfigObjConfig{
|
||||
{
|
||||
Source: "app-config",
|
||||
Target: "/app/config.json",
|
||||
UID: "1000",
|
||||
GID: "1000",
|
||||
},
|
||||
},
|
||||
expectedSpecs: []api.ConfigSpec{
|
||||
{
|
||||
Name: "app-config",
|
||||
Content: []byte("test config content\n"),
|
||||
},
|
||||
},
|
||||
expectedMounts: []api.ConfigMount{
|
||||
{
|
||||
ConfigName: "app-config",
|
||||
ContainerPath: "/app/config.json",
|
||||
Uid: "1000",
|
||||
Gid: "1000",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "config with mode",
|
||||
configs: types.Configs{
|
||||
"nginx-config": types.ConfigObjConfig{
|
||||
File: "./testdata/nginx.conf",
|
||||
},
|
||||
},
|
||||
serviceConfigs: []types.ServiceConfigObjConfig{
|
||||
{
|
||||
Source: "nginx-config",
|
||||
Target: "/etc/nginx/nginx.conf",
|
||||
Mode: func() *uint32 { m := uint32(0o644); return &m }(),
|
||||
},
|
||||
},
|
||||
expectedSpecs: []api.ConfigSpec{
|
||||
{
|
||||
Name: "nginx-config",
|
||||
Content: []byte("user nginx;\nworker_processes auto;\n"),
|
||||
},
|
||||
},
|
||||
expectedMounts: []api.ConfigMount{
|
||||
{
|
||||
ConfigName: "nginx-config",
|
||||
ContainerPath: "/etc/nginx/nginx.conf",
|
||||
Mode: func() *os.FileMode { m := os.FileMode(0o644); return &m }(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
configSpecs, configMounts, err := configSpecsFromCompose(tt.configs, tt.serviceConfigs, ".")
|
||||
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, tt.expectedSpecs, configSpecs)
|
||||
assert.Equal(t, tt.expectedMounts, configMounts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigSpecEquals(t *testing.T) {
|
||||
config1 := api.ConfigSpec{
|
||||
Name: "test-config",
|
||||
}
|
||||
|
||||
config2 := api.ConfigSpec{
|
||||
Name: "test-config",
|
||||
}
|
||||
|
||||
config3 := api.ConfigSpec{
|
||||
Name: "test-config",
|
||||
Content: []byte("some content"),
|
||||
}
|
||||
|
||||
assert.True(t, config1.Equals(config2))
|
||||
assert.False(t, config1.Equals(config3))
|
||||
}
|
||||
@@ -105,6 +105,15 @@ func ServiceSpecFromCompose(project *types.Project, serviceName string) (api.Ser
|
||||
spec.Volumes = volumeSpecs
|
||||
spec.Container.VolumeMounts = volumeMounts
|
||||
|
||||
// Parse configs
|
||||
configSpecs, configMounts, err := configSpecsFromCompose(project.Configs, service.Configs, project.WorkingDir)
|
||||
if err != nil {
|
||||
return spec, err
|
||||
}
|
||||
|
||||
spec.Configs = configSpecs
|
||||
spec.Container.ConfigMounts = configMounts
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
test config content
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
Reference in New Issue
Block a user