// The Podman package handles any interactions with Podman entities or state. package podman import ( "log" "os" "os/exec" service_definition "spud/service_definition" ) // Creates a Podman volume of name `name` if it does not exist. // // If the volume exists, then behaviour depends on `existsOk`: // - If `existsOk` is truthy, then the already-exists error is ignored and // nothing is done; // - Else, an error is returned. func CreateVolume(name string, existsOk bool) error { args := []string{"volume", "create", name} if existsOk { args = append(args, "--ignore") } command := exec.Command("podman", args...) if err := command.Run(); err != nil { return err } log.Printf("✅ Created volume \"%s\".", name) return nil } // Creates a Podman pod to keep related containers together. // // The pod created will expose ports necessary for individual containers // to be accessible from the host. func CreatePod(name string, ports []service_definition.PortMapping) error { args := []string{"pod", "create", "--replace"} for _, portMapping := range ports { portMapStr := portMapping.Host + ":" + portMapping.Container if portMapping.Type != "" { portMapStr = portMapStr + "/" + portMapping.Type } portArgs := []string{"-p", portMapStr} args = append(args, portArgs...) } args = append(args, name) command := exec.Command("podman", args...) if err := command.Run(); err != nil { return err } log.Printf("✅ Created pod \"%s\".", name) return nil } // Stops a running pod. func StopPod(name string) error { args := []string{"pod", "stop", name} command := exec.Command("podman", args...) if err := command.Run(); err != nil { return err } log.Printf("✅ Stopped pod \"%s\" and child containers.", name) return nil } // Creates individual containers. // // Individual containers do not expose any ports by themselves, these // are handled by the pod that wraps the containers. func CreateContainer(definition service_definition.ContainerDefinition, knownVolumes map[string]string, service string) error { namespacedContainerName := service + "_" + definition.Name args := []string{ "run", "-d", "--name", namespacedContainerName, "--pod", service, "--replace", } if definition.Network != "" { args = append(args, []string{"--network", definition.Network}...) } if definition.PIDNamespace != "" { args = append(args, []string{"--pid", definition.PIDNamespace}...) } if definition.EnvFile != "" { args = append(args, []string{"--env-file", definition.EnvFile}...) } for _, volume := range definition.Volumes { var host string var suffix string container := volume.Container if volume.Name != "" { namespacedName := service + "_" + volume.Name host = namespacedName } else if volume.Host != "" { host = volume.Host } else { log.Fatal("Invalid volume source configuration") } if volume.ReadOnly == true { suffix = ":ro" } arg := []string{"-v", host + ":" + container + suffix} args = append(args, arg...) } args = append(args, definition.Image) for _, extra := range definition.ExtraArgs { args = append(args, extra) } command := exec.Command("podman", args...) if err := command.Start(); err != nil { log.Fatal("Failed to start") return err } if err := command.Wait(); err != nil { log.Fatal(args) return err } log.Printf("✅ Started container \"%s\".", namespacedContainerName) return nil } // Builds a container image. func Build(imageDefinition service_definition.BuildImage) error { args := []string{"build", "-f", imageDefinition.Path, "-t", imageDefinition.TagPrefix} command := exec.Command("podman", args...) command.Stdout = os.Stdout command.Stderr = os.Stderr if err := command.Run(); err != nil { return err } return nil }