package main import ( "bufio" "bytes" "flag" "fmt" "log" "os" "os/exec" "strings" "gopkg.in/yaml.v2" ) 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"] stackKey := project if stackNamespace != "" { stackKey = stackNamespace } if stackKey != "" { stacks[stackKey] = append(stacks[stackKey], container) } } if *stackName == "" { // No stack specified, list available stacks if len(stacks) == 0 { fmt.Println("No Docker Compose projects or Docker Swarm stacks found.") } else { fmt.Println("Available stacks or Docker Compose projects to export:") for stack := range stacks { fmt.Println("- " + stack) } } return } // Stack specified, check if it exists containers, exists := stacks[*stackName] if !exists { log.Fatalf("Stack or project '%s' not found.", *stackName) } // Export the specified stack/project to a docker-compose.yml file err = exportStackToComposeFile(*stackName, containers, *outputFile) if err != nil { log.Fatalf("Error exporting stack %s: %v", *stackName, err) } fmt.Printf("docker-compose.yml generated successfully for stack '%s' in %s\n", *stackName, *outputFile) } // Container represents basic Docker container information. type Container struct { ID string Name string Image string Labels map[string]string Env []string Networks []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 cmd.Stdout = &out err := cmd.Run() if err != nil { return nil, err } scanner := bufio.NewScanner(&out) var containers []Container for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 3 { continue } id := fields[0] name := fields[1] image := fields[2] labels := map[string]string{ "com.docker.compose.project": fields[3], "com.docker.stack.namespace": fields[4], } // Run 'docker inspect' to get detailed container information. env, networks, err := inspectContainer(id) if err != nil { return nil, err } containers = append(containers, Container{ ID: id, Name: name, Image: image, Labels: labels, Env: env, Networks: networks, }) } 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) var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() if err != nil { return nil, nil, err } parts := strings.Split(out.String(), " ") envVars := strings.Fields(parts[0]) networks := strings.Fields(parts[1]) return envVars, networks, nil } // exportStackToComposeFile generates a docker-compose.yml for a given stack or project. func exportStackToComposeFile(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 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 stack %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) } return nil }