|
1 | 1 | package swarm |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "context" |
5 | 6 | "fmt" |
6 | 7 | "io" |
7 | 8 | "net/netip" |
8 | 9 | "path" |
9 | 10 | "path/filepath" |
10 | 11 | "strconv" |
| 12 | + "strings" |
| 13 | + "time" |
11 | 14 |
|
12 | 15 | "github.com/cschleiden/go-workflows/workflow" |
| 16 | + "github.com/docker/docker/api/types/container" |
| 17 | + "github.com/docker/docker/api/types/mount" |
13 | 18 | "github.com/docker/docker/api/types/network" |
14 | 19 | "github.com/docker/docker/api/types/swarm" |
15 | 20 | "github.com/docker/go-connections/nat" |
@@ -436,3 +441,79 @@ func (o *Orchestrator) CreatePgBackRestBackup(ctx context.Context, w io.Writer, |
436 | 441 |
|
437 | 442 | return nil |
438 | 443 | } |
| 444 | + |
| 445 | +func (o *Orchestrator) ValidateVolumes(ctx context.Context, spec *database.InstanceSpec) (*database.ValidationResult, error) { |
| 446 | + specVersion := spec.PgEdgeVersion |
| 447 | + if specVersion == nil { |
| 448 | + o.logger.Warn().Msg("PostgresVersion not provided, using default version") |
| 449 | + specVersion = defaultVersion |
| 450 | + } |
| 451 | + |
| 452 | + images, err := GetImages(o.cfg, specVersion) |
| 453 | + if err != nil { |
| 454 | + return nil, fmt.Errorf("image fetch error: %w", err) |
| 455 | + } |
| 456 | + |
| 457 | + var mounts []mount.Mount |
| 458 | + var mountTargets []string |
| 459 | + for _, vol := range spec.ExtraVolumes { |
| 460 | + mounts = append(mounts, docker.BuildMount(vol.HostPath, vol.DestinationPath, false)) |
| 461 | + mountTargets = append(mountTargets, vol.DestinationPath) |
| 462 | + } |
| 463 | + |
| 464 | + cmd := buildVolumeCheckCommand(mountTargets) |
| 465 | + output, err := o.runVolumeValidationContainer(ctx, images.PgEdgeImage, cmd, mounts) |
| 466 | + if err != nil { |
| 467 | + return nil, err |
| 468 | + } |
| 469 | + |
| 470 | + if strings.HasSuffix(output, "OK") { |
| 471 | + return &database.ValidationResult{Success: true, Reason: "All volumes are valid"}, nil |
| 472 | + } |
| 473 | + return &database.ValidationResult{Success: false, Reason: output}, nil |
| 474 | +} |
| 475 | + |
| 476 | +func (o *Orchestrator) runVolumeValidationContainer(ctx context.Context, image string, cmd []string, mounts []mount.Mount) (string, error) { |
| 477 | + // Start container |
| 478 | + containerID, err := o.docker.ContainerRun(ctx, docker.ContainerRunOptions{ |
| 479 | + Config: &container.Config{ |
| 480 | + Image: image, |
| 481 | + Entrypoint: cmd, |
| 482 | + }, |
| 483 | + Host: &container.HostConfig{ |
| 484 | + Mounts: mounts, |
| 485 | + }, |
| 486 | + }) |
| 487 | + if err != nil { |
| 488 | + return "", fmt.Errorf("failed to start container: %w", err) |
| 489 | + } |
| 490 | + // Ensure container is removed afterward |
| 491 | + defer func() { |
| 492 | + if err := o.docker.ContainerRemove(ctx, containerID, container.RemoveOptions{Force: true}); err != nil { |
| 493 | + o.logger.Error().Err(err).Msg("container cleanup failed") |
| 494 | + } |
| 495 | + }() |
| 496 | + |
| 497 | + // Wait for the container to complete |
| 498 | + if err := o.docker.ContainerWait(ctx, containerID, container.WaitConditionNotRunning, 30*time.Second); err != nil { |
| 499 | + return "", fmt.Errorf("container wait failed: %w", err) |
| 500 | + } |
| 501 | + |
| 502 | + // Capture logs |
| 503 | + buf := new(bytes.Buffer) |
| 504 | + if err := o.docker.ContainerLogs(ctx, buf, containerID, container.LogsOptions{ShowStdout: true}); err != nil { |
| 505 | + return "", fmt.Errorf("log fetch failed: %w", err) |
| 506 | + } |
| 507 | + |
| 508 | + // Stop the container gracefully |
| 509 | + timeoutSeconds := 5 |
| 510 | + if err := o.docker.ContainerStop(ctx, containerID, &timeoutSeconds); err != nil { |
| 511 | + o.logger.Warn().Err(err).Msg("graceful stop failed") |
| 512 | + } |
| 513 | + |
| 514 | + return strings.TrimSpace(buf.String()), nil |
| 515 | +} |
| 516 | + |
| 517 | +func buildVolumeCheckCommand(mountTargets []string) []string { |
| 518 | + return []string{"sh", "-c", fmt.Sprintf("for d in %s; do [ -d \"$d\" ] || { echo \"FAIL: $d not found\"; exit 1; }; done; echo OK", strings.Join(mountTargets, " "))} |
| 519 | +} |
0 commit comments