Files
kubevpn/pkg/util/docker.go
2025-06-18 10:16:30 +08:00

216 lines
5.6 KiB
Go

package util
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/pkg/stdcopy"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func RunLogsWaitRunning(ctx context.Context, name string) error {
buf := bytes.NewBuffer(nil)
w := io.MultiWriter(buf, os.Stdout)
args := []string{"logs", name, "--since", "0m", "--details", "--follow"}
cmd := exec.Command("docker", args...)
cmd.Stdout = w
go cmd.Start()
cancel, cancelFunc := context.WithCancel(ctx)
defer cancelFunc()
go func() {
t := time.NewTicker(time.Second)
defer t.Stop()
for range t.C {
// keyword, maybe can find another way more elegant
if strings.Contains(buf.String(), config.Slogan) {
cancelFunc()
return
}
}
}()
var errChan = make(chan error)
go func() {
var err error
_, err = stdcopy.StdCopy(w, os.Stdout, buf)
if err != nil {
errChan <- err
}
}()
select {
case err := <-errChan:
return err
case <-cancel.Done():
return nil
}
}
func RunLogsSinceNow(name string, follow bool) error {
args := []string{"logs", name, "--since", "0m", "--details"}
if follow {
args = append(args, "--follow")
}
output, err := exec.Command("docker", args...).CombinedOutput()
_, err = stdcopy.StdCopy(os.Stdout, os.Stderr, bytes.NewReader(output))
return err
}
// CreateNetwork
// docker create kubevpn-traffic-manager --labels owner=config.ConfigMapPodTrafficManager --subnet 198.18.0.0/16 --gateway 198.18.0.100
func CreateNetwork(ctx context.Context, name string) (string, error) {
args := []string{
"network",
"inspect",
name,
}
_, err := exec.CommandContext(ctx, "docker", args...).CombinedOutput()
if err == nil {
return name, nil
}
args = []string{
"network",
"create",
name,
"--label", "owner=" + name,
"--subnet", config.DockerCIDR.String(),
"--gateway", config.DockerRouterIP.String(),
"--driver", "bridge",
"--scope", "local",
}
id, err := exec.CommandContext(ctx, "docker", args...).CombinedOutput()
if err != nil {
return "", err
}
return string(id), nil
}
func RunContainer(ctx context.Context, args []string) error {
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.G(ctx).Debugf("Run container with cmd: %v", cmd.Args)
err := cmd.Start()
if err != nil {
log.G(ctx).Errorf("Failed to run container with cmd: %v: %v", cmd.Args, err)
return err
}
return cmd.Wait()
}
func WaitDockerContainerRunning(ctx context.Context, name string) error {
log.G(ctx).Infof("Wait container %s to be running...", name)
for ctx.Err() == nil {
time.Sleep(time.Second * 1)
inspect, err := ContainerInspect(ctx, name)
if err != nil {
return err
}
if inspect.State != nil && (inspect.State.Status == "exited" || inspect.State.Status == "dead" || inspect.State.Dead) {
err = errors.New(fmt.Sprintf("container status: %s", inspect.State.Status))
break
}
if inspect.State != nil && inspect.State.Running {
break
}
}
log.G(ctx).Infof("Container %s is running now", name)
return nil
}
func ContainerInspect(ctx context.Context, name string) (types.ContainerJSON, error) {
output, err := exec.CommandContext(ctx, "docker", "inspect", name).CombinedOutput()
if err != nil {
log.G(ctx).Errorf("Failed to wait container to be ready output: %s: %v", string(output), err)
_ = RunLogsSinceNow(name, false)
return types.ContainerJSON{}, err
}
var inspect []types.ContainerJSON
rdr := bytes.NewReader(output)
err = json.NewDecoder(rdr).Decode(&inspect)
if err != nil {
return types.ContainerJSON{}, err
}
if len(inspect) == 0 {
return types.ContainerJSON{}, err
}
return inspect[0], nil
}
func NetworkInspect(ctx context.Context, name string) (network.Inspect, error) {
output, err := exec.CommandContext(ctx, "docker", "network", "inspect", name).CombinedOutput()
if err != nil {
log.G(ctx).Errorf("Failed to wait container to be ready: %v", err)
_ = RunLogsSinceNow(name, false)
return network.Inspect{}, err
}
var inspect []network.Inspect
rdr := bytes.NewReader(output)
err = json.NewDecoder(rdr).Decode(&inspect)
if err != nil {
return network.Inspect{}, err
}
if len(inspect) == 0 {
return network.Inspect{}, err
}
return inspect[0], nil
}
func NetworkRemove(ctx context.Context, name string) error {
output, err := exec.CommandContext(ctx, "docker", "network", "remove", name).CombinedOutput()
if err != nil && strings.Contains(string(output), "not found") {
return nil
}
return err
}
// NetworkDisconnect
// docker network disconnect --force
func NetworkDisconnect(ctx context.Context, containerName string) ([]byte, error) {
output, err := exec.CommandContext(ctx, "docker", "network", "disconnect", "--force", config.ConfigMapPodTrafficManager, containerName).CombinedOutput()
if err != nil && strings.Contains(string(output), "not found") {
return output, nil
}
return output, err
}
// ContainerRemove
// docker remove --force
func ContainerRemove(ctx context.Context, containerName string) ([]byte, error) {
output, err := exec.CommandContext(ctx, "docker", "remove", "--force", containerName).CombinedOutput()
if err != nil && strings.Contains(string(output), "not found") {
return output, nil
}
return output, err
}
func ContainerKill(ctx context.Context, name *string) ([]byte, error) {
output, err := exec.CommandContext(ctx, "docker", "kill", *name, "--signal", "SIGTERM").CombinedOutput()
if err != nil && strings.Contains(string(output), "not found") {
return output, nil
}
return output, err
}