retry
This commit is contained in:
parent
7ce835c4de
commit
b18dab5b49
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
106
main.go
106
main.go
|
@ -14,26 +14,21 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
// Command-line flags
|
||||
stackName := flag.String("s", "", "Specify the stack or project name to export.")
|
||||
outputFile := flag.String("o", "docker-compose.yml", "Specify the output file for the YAML.")
|
||||
flag.Parse()
|
||||
|
||||
// Fetch list of all containers with their stack or project labels
|
||||
containers, err := listContainers()
|
||||
if err != nil {
|
||||
log.Fatalf("Error fetching container list: %v", err)
|
||||
}
|
||||
|
||||
// Map to group containers by stack or project
|
||||
stacks := make(map[string][]Container)
|
||||
|
||||
// Group containers by Docker Compose project or Docker Swarm stack
|
||||
for _, container := range containers {
|
||||
project := container.Labels["com.docker.compose.project"]
|
||||
stackNamespace := container.Labels["com.docker.stack.namespace"]
|
||||
|
||||
// Use project name if available; otherwise, use stack namespace
|
||||
stackKey := project
|
||||
if stackNamespace != "" {
|
||||
stackKey = stackNamespace
|
||||
|
@ -45,7 +40,6 @@ func main() {
|
|||
}
|
||||
|
||||
if *stackName == "" {
|
||||
// No stack specified, list available stacks
|
||||
if len(stacks) == 0 {
|
||||
fmt.Println("No Docker Compose projects or Docker Swarm stacks found.")
|
||||
} else {
|
||||
|
@ -57,22 +51,18 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
// Stack specified, check if it exists
|
||||
containers, exists := stacks[*stackName]
|
||||
if !exists {
|
||||
log.Fatalf("Stack or project '%s' not found.", *stackName)
|
||||
}
|
||||
|
||||
// Determine if this is a Docker Swarm stack or a Docker Compose project
|
||||
isSwarm := isDockerSwarmStack(*stackName, containers)
|
||||
|
||||
if isSwarm {
|
||||
// Notify the user that exporting Swarm stacks isn't fully supported
|
||||
fmt.Printf("Warning: The stack '%s' appears to be a Docker Swarm stack. Export functionality for Swarm stacks may not provide complete configurations.\n", *stackName)
|
||||
fmt.Println("Skipping export for Docker Swarm stacks. Please use `docker stack` commands directly for managing Swarm stacks.")
|
||||
return
|
||||
} else {
|
||||
// Handle Docker Compose project export
|
||||
err = exportComposeProjectToComposeFile(*stackName, containers, *outputFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Error exporting Docker Compose project %s: %v", *stackName, err)
|
||||
|
@ -81,17 +71,18 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// Container represents basic Docker container information.
|
||||
type Container struct {
|
||||
ID string
|
||||
Name string
|
||||
Image string
|
||||
Labels map[string]string
|
||||
Env []string
|
||||
Networks []string
|
||||
ID string
|
||||
Name string
|
||||
Image string
|
||||
Labels map[string]string
|
||||
Env []string
|
||||
Networks []string
|
||||
Volumes []string
|
||||
Ports []string
|
||||
RestartPolicy string
|
||||
}
|
||||
|
||||
// listContainers runs 'docker ps' and 'docker inspect' to get a list of all containers.
|
||||
func listContainers() ([]Container, error) {
|
||||
cmd := exec.Command("docker", "ps", "--format", "{{.ID}} {{.Names}} {{.Image}} {{.Label \"com.docker.compose.project\"}} {{.Label \"com.docker.stack.namespace\"}}")
|
||||
var out bytes.Buffer
|
||||
|
@ -106,8 +97,6 @@ func listContainers() ([]Container, error) {
|
|||
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
|
||||
// Check that there are at least 3 fields (ID, Name, Image)
|
||||
if len(fields) < 3 {
|
||||
log.Printf("Warning: Skipping incomplete line: %s", scanner.Text())
|
||||
continue
|
||||
|
@ -117,7 +106,6 @@ func listContainers() ([]Container, error) {
|
|||
name := fields[1]
|
||||
image := fields[2]
|
||||
|
||||
// Handle cases where project or stackNamespace might be missing
|
||||
project := ""
|
||||
stackNamespace := ""
|
||||
if len(fields) > 3 {
|
||||
|
@ -132,49 +120,60 @@ func listContainers() ([]Container, error) {
|
|||
"com.docker.stack.namespace": stackNamespace,
|
||||
}
|
||||
|
||||
// Run 'docker inspect' to get detailed container information.
|
||||
env, networks, err := inspectContainer(id)
|
||||
env, networks, volumes, ports, restartPolicy, err := inspectContainer(id)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Error inspecting container %s: %v", id, err)
|
||||
continue
|
||||
}
|
||||
|
||||
containers = append(containers, Container{
|
||||
ID: id,
|
||||
Name: name,
|
||||
Image: image,
|
||||
Labels: labels,
|
||||
Env: env,
|
||||
Networks: networks,
|
||||
ID: id,
|
||||
Name: name,
|
||||
Image: image,
|
||||
Labels: labels,
|
||||
Env: env,
|
||||
Networks: networks,
|
||||
Volumes: volumes,
|
||||
Ports: ports,
|
||||
RestartPolicy: restartPolicy,
|
||||
})
|
||||
}
|
||||
|
||||
return containers, scanner.Err()
|
||||
}
|
||||
|
||||
// inspectContainer runs 'docker inspect' and parses environment variables and networks.
|
||||
func inspectContainer(containerID string) ([]string, []string, error) {
|
||||
cmd := exec.Command("docker", "inspect", "--format", "{{range .Config.Env}}{{printf \"%s \" .}}{{end}} {{range $key, $value := .NetworkSettings.Networks}}{{$key}} {{end}}", containerID)
|
||||
func inspectContainer(containerID string) ([]string, []string, []string, []string, string, error) {
|
||||
cmd := exec.Command("docker", "inspect", "--format", `{{range .Config.Env}}{{println .}}{{end}}{{range .NetworkSettings.Networks}}{{println .}}{{end}}{{range .Mounts}}{{println .}}{{end}}{{range $p, $conf := .HostConfig.PortBindings}}{{println $p}}{{end}}{{.HostConfig.RestartPolicy.Name}}`, containerID)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, nil, "", err
|
||||
}
|
||||
|
||||
// Split output into environment variables and network names
|
||||
parts := strings.Split(out.String(), " ")
|
||||
if len(parts) < 2 {
|
||||
// Handle cases where no networks are listed
|
||||
parts = append(parts, "")
|
||||
}
|
||||
envVars := strings.Fields(parts[0])
|
||||
networks := strings.Fields(parts[1])
|
||||
lines := strings.Split(out.String(), "\n")
|
||||
|
||||
return envVars, networks, nil
|
||||
var envVars, networks, volumes, ports []string
|
||||
restartPolicy := ""
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "=") {
|
||||
envVars = append(envVars, line)
|
||||
} else if strings.Contains(line, "Name:") && strings.Contains(line, "Driver:") {
|
||||
networks = append(networks, strings.TrimSpace(line))
|
||||
} else if strings.Contains(line, "Source:") && strings.Contains(line, "Destination:") {
|
||||
volume := strings.Split(line, "Destination:")[1]
|
||||
volumes = append(volumes, volume)
|
||||
} else if strings.Contains(line, "/tcp") || strings.Contains(line, "/udp") {
|
||||
ports = append(ports, line)
|
||||
} else if line != "" {
|
||||
restartPolicy = line
|
||||
}
|
||||
}
|
||||
|
||||
return envVars, networks, volumes, ports, restartPolicy, nil
|
||||
}
|
||||
|
||||
// isDockerSwarmStack checks if the stack is a Docker Swarm stack based on the container names.
|
||||
func isDockerSwarmStack(stackName string, containers []Container) bool {
|
||||
for _, container := range containers {
|
||||
if strings.Contains(container.Name, ".") {
|
||||
|
@ -184,43 +183,46 @@ func isDockerSwarmStack(stackName string, containers []Container) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// exportComposeProjectToComposeFile exports a Docker Compose project to a docker-compose.yml file.
|
||||
func exportComposeProjectToComposeFile(stack string, containers []Container, outputFile string) error {
|
||||
// Map to hold docker-compose configuration
|
||||
composeConfig := make(map[string]interface{})
|
||||
composeConfig["services"] = make(map[string]interface{})
|
||||
|
||||
// Process each container in the stack
|
||||
for _, container := range containers {
|
||||
serviceName := container.Name
|
||||
image := container.Image
|
||||
|
||||
// Initialize service configuration
|
||||
serviceConfig := map[string]interface{}{
|
||||
"image": image,
|
||||
}
|
||||
|
||||
// Handle environment variables
|
||||
if len(container.Env) > 0 {
|
||||
serviceConfig["environment"] = container.Env
|
||||
}
|
||||
|
||||
// Handle network settings
|
||||
if len(container.Networks) > 0 {
|
||||
serviceConfig["networks"] = container.Networks
|
||||
}
|
||||
|
||||
// Add service configuration to compose config
|
||||
if len(container.Volumes) > 0 {
|
||||
serviceConfig["volumes"] = container.Volumes
|
||||
}
|
||||
|
||||
if len(container.Ports) > 0 {
|
||||
serviceConfig["ports"] = container.Ports
|
||||
}
|
||||
|
||||
if container.RestartPolicy != "" {
|
||||
serviceConfig["restart"] = container.RestartPolicy
|
||||
}
|
||||
|
||||
composeConfig["services"].(map[string]interface{})[serviceName] = serviceConfig
|
||||
}
|
||||
|
||||
// Convert to YAML
|
||||
yamlData, err := yaml.Marshal(&composeConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating YAML for Docker Compose project %s: %v", stack, err)
|
||||
}
|
||||
|
||||
// Write YAML to a file
|
||||
err = os.WriteFile(outputFile, yamlData, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to file %s: %v", outputFile, err)
|
||||
|
|
Loading…
Reference in New Issue