mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
feat(pre-deploy): add support for x-pre_deploy extension in Compose + e2e test
This commit is contained in:
@@ -31,6 +31,10 @@ func assertServiceMatchesSpec(t *testing.T, svc api.Service, spec api.ServiceSpe
|
||||
for _, mc := range svc.Containers {
|
||||
assertContainerMatchesSpec(t, mc.Container, spec)
|
||||
}
|
||||
|
||||
if spec.PreDeploy != nil {
|
||||
assertHookContainersMatchSpec(t, svc, spec)
|
||||
}
|
||||
}
|
||||
|
||||
func assertContainerMatchesSpec(t *testing.T, ctr api.ServiceContainer, spec api.ServiceSpec) {
|
||||
@@ -139,6 +143,78 @@ func assertContainerMatchesSpec(t *testing.T, ctr api.ServiceContainer, spec api
|
||||
assert.Contains(t, ctr.NetworkSettings.Networks, machinedocker.NetworkName)
|
||||
}
|
||||
|
||||
// assertHookContainersMatchSpec validates that hook containers in the service match the pre-deploy hook spec.
|
||||
func assertHookContainersMatchSpec(t *testing.T, svc api.Service, spec api.ServiceSpec) {
|
||||
t.Helper()
|
||||
require.NotEmpty(t, svc.HookContainers, "Expected at least one hook container")
|
||||
|
||||
for _, mc := range svc.HookContainers {
|
||||
ctr := mc.Container
|
||||
|
||||
// Verify labels.
|
||||
assert.True(t, api.ValidateServiceID(ctr.Config.Labels[api.LabelServiceID]))
|
||||
assert.Equal(t, spec.Name, ctr.Config.Labels[api.LabelServiceName])
|
||||
assert.Equal(t, api.LabelHookPreDeploy, ctr.Config.Labels[api.LabelHook])
|
||||
assert.Contains(t, ctr.Config.Labels, api.LabelManaged)
|
||||
assert.NotContains(t, ctr.Config.Labels, api.LabelServiceMode,
|
||||
"Hook containers should not have the service mode label")
|
||||
|
||||
assert.EqualValues(t, spec.PreDeploy.Command, ctr.Config.Cmd)
|
||||
if spec.Container.Entrypoint != nil {
|
||||
assert.EqualValues(t, spec.Container.Entrypoint, ctr.Config.Entrypoint)
|
||||
}
|
||||
|
||||
// Service env vars are inherited by hook containers.
|
||||
for _, env := range spec.Container.Env.ToSlice() {
|
||||
assert.Contains(t, ctr.Config.Env, env)
|
||||
}
|
||||
// Hook-specific env vars.
|
||||
for _, env := range spec.PreDeploy.Env.ToSlice() {
|
||||
assert.Contains(t, ctr.Config.Env, env)
|
||||
}
|
||||
assert.Contains(t, ctr.Config.Env, "UNCLOUD_HOOK_PRE_DEPLOY=true")
|
||||
|
||||
assert.Equal(t, spec.Container.Image, ctr.Config.Image)
|
||||
assert.Equal(t, spec.Container.Init, ctr.HostConfig.Init)
|
||||
assert.True(t, strings.HasPrefix(ctr.Name, spec.Name+"-pre-deploy-"),
|
||||
"Hook container name %q should start with %q", ctr.Name, spec.Name+"-pre-deploy-")
|
||||
|
||||
// Privileged is overridden by PreDeploy.Privileged if set, otherwise inherited from the service.
|
||||
if spec.PreDeploy.Privileged != nil {
|
||||
assert.Equal(t, *spec.PreDeploy.Privileged, ctr.HostConfig.Privileged)
|
||||
} else {
|
||||
assert.Equal(t, spec.Container.Privileged, ctr.HostConfig.Privileged)
|
||||
}
|
||||
|
||||
// User is overridden by PreDeploy.User if set.
|
||||
if spec.PreDeploy.User != "" {
|
||||
assert.Equal(t, spec.PreDeploy.User, ctr.Config.User)
|
||||
} else if spec.Container.User != "" {
|
||||
assert.Equal(t, spec.Container.User, ctr.Config.User)
|
||||
}
|
||||
|
||||
// Compute resources.
|
||||
assert.Equal(t, spec.Container.Resources.CPU, ctr.HostConfig.Resources.NanoCPUs)
|
||||
assert.Equal(t, spec.Container.Resources.Memory, ctr.HostConfig.Resources.Memory)
|
||||
assert.Equal(t, spec.Container.Resources.MemoryReservation, ctr.HostConfig.Resources.MemoryReservation)
|
||||
|
||||
// Hook-specific overrides: disabled restart, disabled healthcheck, no ports.
|
||||
assert.Equal(t, container.RestartPolicy{Name: container.RestartPolicyDisabled}, ctr.HostConfig.RestartPolicy)
|
||||
require.NotNil(t, ctr.Config.Healthcheck)
|
||||
assert.Equal(t, []string{"NONE"}, ctr.Config.Healthcheck.Test)
|
||||
assert.Empty(t, ctr.HostConfig.PortBindings)
|
||||
|
||||
assert.False(t, ctr.State.Running, "Hook container should not be running")
|
||||
assert.Equal(t, 0, ctr.State.ExitCode, "Hook container should exit with code 0")
|
||||
|
||||
assertContainerMountsMatchSpec(t, ctr.HostConfig.Mounts, spec)
|
||||
|
||||
// Verify network settings.
|
||||
assert.Len(t, ctr.NetworkSettings.Networks, 1)
|
||||
assert.Contains(t, ctr.NetworkSettings.Networks, machinedocker.NetworkName)
|
||||
}
|
||||
}
|
||||
|
||||
func assertContainerMountsMatchSpec(t *testing.T, mounts []mount.Mount, spec api.ServiceSpec) {
|
||||
expectedMounts, err := machinedocker.ToDockerMounts(spec.Volumes, spec.Container.VolumeMounts)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -601,4 +601,60 @@ volumes:
|
||||
assert.ElementsMatch(t, machines.ToSlice(), expectedMachines,
|
||||
"Containers should be distributed across all machines")
|
||||
})
|
||||
|
||||
t.Run("pre-deploy hook", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
name := "test-compose-predeploy"
|
||||
volumeName := "test-compose-predeploy-data"
|
||||
t.Cleanup(func() {
|
||||
removeServices(t, cli, name)
|
||||
removeVolumes(t, cli, volumeName)
|
||||
})
|
||||
|
||||
project, err := compose.LoadProject(ctx, []string{"fixtures/compose-predeploy.yaml"})
|
||||
require.NoError(t, err)
|
||||
|
||||
deployment, err := compose.NewDeployment(ctx, cli, project)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = deployment.Run(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
svc, err := cli.InspectService(ctx, name)
|
||||
require.NoError(t, err)
|
||||
|
||||
assertServiceMatchesSpec(t, svc, api.ServiceSpec{
|
||||
Name: name,
|
||||
Container: api.ContainerSpec{
|
||||
Image: "busybox:1.37.0-uclibc",
|
||||
Command: []string{"sleep", "600"},
|
||||
Env: api.EnvVars{"SERVICE_VAR": "from-service"},
|
||||
VolumeMounts: []api.VolumeMount{
|
||||
{
|
||||
VolumeName: volumeName,
|
||||
ContainerPath: "/data",
|
||||
},
|
||||
},
|
||||
},
|
||||
Volumes: []api.VolumeSpec{
|
||||
{
|
||||
Name: volumeName,
|
||||
Type: api.VolumeTypeVolume,
|
||||
},
|
||||
},
|
||||
PreDeploy: &api.PreDeployHook{
|
||||
Command: []string{"sh", "-c", "echo hello-from-predeploy > /data/predeploy.txt"},
|
||||
Env: api.EnvVars{"HOOK_VAR": "from-hook"},
|
||||
User: "root",
|
||||
},
|
||||
})
|
||||
|
||||
// Verify the pre-deploy hook wrote to the shared volume by reading the file from the running container.
|
||||
containerID := svc.Containers[0].Container.ID
|
||||
output, err := execInContainerAndReadOutput(t, ctx, cli, name, containerID,
|
||||
[]string{"cat", "/data/predeploy.txt"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "hello-from-predeploy\n", output)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
test-compose-predeploy:
|
||||
image: busybox:1.37.0-uclibc
|
||||
command: ["sleep", "600"]
|
||||
environment:
|
||||
SERVICE_VAR: from-service
|
||||
volumes:
|
||||
- test-compose-predeploy-data:/data
|
||||
x-pre_deploy:
|
||||
command: [sh, -c, echo hello-from-predeploy > /data/predeploy.txt]
|
||||
environment:
|
||||
HOOK_VAR: from-hook
|
||||
user: root
|
||||
timeout: 30s
|
||||
|
||||
volumes:
|
||||
test-compose-predeploy-data:
|
||||
Reference in New Issue
Block a user