Skip to content

Commit

Permalink
feat: 容器镜像使用cli实现
Browse files Browse the repository at this point in the history
  • Loading branch information
devhaozi committed Oct 27, 2024
1 parent b980ab4 commit f110fa5
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 78 deletions.
5 changes: 2 additions & 3 deletions internal/biz/container_image.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package biz

import (
"github.com/docker/docker/api/types/image"

"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/types"
)

type ContainerImageRepo interface {
List() ([]image.Summary, error)
List() ([]types.ContainerImage, error)
Pull(req *request.ContainerImagePull) error
Remove(id string) error
Prune() error
Expand Down
7 changes: 3 additions & 4 deletions internal/data/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (r *containerRepo) ListAll() ([]types.Container, error) {
var containers []types.Container
for _, line := range lines {
if line == "" {
continue // 跳过空行
continue
}

var item struct {
Expand All @@ -60,11 +60,10 @@ func (r *containerRepo) ListAll() ([]types.Container, error) {
Status string `json:"Status"`
}
if err = json.Unmarshal([]byte(line), &item); err != nil {
return nil, err
return nil, fmt.Errorf("unmarshal failed: %w", err)
}

createdAt, _ := time.Parse("2006-01-02 15:04:05 -0700 MST", item.CreatedAt)

containers = append(containers, types.Container{
ID: item.ID,
Name: item.Names,
Expand Down Expand Up @@ -97,7 +96,7 @@ func (r *containerRepo) ListByName(names string) ([]types.Container, error) {

// Create 创建容器
func (r *containerRepo) Create(req *request.ContainerCreate) (string, error) {
sb := strings.Builder{}
var sb strings.Builder
sb.WriteString(fmt.Sprintf("%s create --name %s --image %s", r.cmd, req.Name, req.Image))

for _, port := range req.Ports {
Expand Down
101 changes: 63 additions & 38 deletions internal/data/container_image.go
Original file line number Diff line number Diff line change
@@ -1,78 +1,103 @@
package data

import (
"context"
"encoding/base64"
"encoding/json"
"io"
"fmt"
"strings"
"time"

"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
"github.com/spf13/cast"

"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/types"
)

type containerImageRepo struct {
client *client.Client
cmd string
}

func NewContainerImageRepo(sock ...string) biz.ContainerImageRepo {
if len(sock) == 0 {
sock = append(sock, "/run/podman/podman.sock")
func NewContainerImageRepo(cmd ...string) biz.ContainerImageRepo {
if len(cmd) == 0 {
cmd = append(cmd, "docker")
}
cli, _ := client.NewClientWithOpts(client.WithHost("unix://"+sock[0]), client.WithAPIVersionNegotiation())
return &containerImageRepo{
client: cli,
cmd: cmd[0],
}
}

// List 列出镜像
func (r *containerImageRepo) List() ([]image.Summary, error) {
return r.client.ImageList(context.Background(), image.ListOptions{
All: true,
})
func (r *containerImageRepo) List() ([]types.ContainerImage, error) {
output, err := shell.ExecfWithTimeout(10*time.Second, "%s images -a --format '{{json .}}'", r.cmd)
if err != nil {
return nil, err
}
lines := strings.Split(output, "\n")

var images []types.ContainerImage
for _, line := range lines {
if line == "" {
continue
}

var item struct {
ID string `json:"ID"`
Containers string `json:"Containers"`
Repository string `json:"Repository"`
Tag string `json:"Tag"`
Digest string `json:"Digest"`
CreatedAt string `json:"CreatedAt"`
Size string `json:"Size"`
SharedSize string `json:"SharedSize"`
VirtualSize string `json:"VirtualSize"`
}
if err = json.Unmarshal([]byte(line), &item); err != nil {
return nil, fmt.Errorf("unmarshal failed: %w", err)
}

createdAt, _ := time.Parse("2006-01-02 15:04:05 -0700 MST", item.CreatedAt)
images = append(images, types.ContainerImage{
ID: item.ID,
Containers: cast.ToInt64(item.Containers),
Tag: item.Tag,
CreatedAt: createdAt,
Size: item.Size,
})
}

return images, nil
}

// Pull 拉取镜像
func (r *containerImageRepo) Pull(req *request.ContainerImagePull) error {
options := image.PullOptions{}
var sb strings.Builder

if req.Auth {
authConfig := registry.AuthConfig{
Username: req.Username,
Password: req.Password,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return err
sb.WriteString(fmt.Sprintf("%s login -u %s -p %s", r.cmd, req.Username, req.Password))
if _, err := shell.ExecfWithTimeout(1*time.Minute, sb.String()); err != nil {

Check failure on line 78 in internal/data/container_image.go

View workflow job for this annotation

GitHub Actions / golanci-lint

printf: non-constant format string in call to github.com/TheTNB/panel/pkg/shell.ExecfWithTimeout (govet)
return fmt.Errorf("login failed: %w", err)
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
options.RegistryAuth = authStr
sb.Reset()
}

out, err := r.client.ImagePull(context.Background(), req.Name, options)
if err != nil {
return err
sb.WriteString(fmt.Sprintf("%s pull %s", r.cmd, req.Name))

if _, err := shell.ExecfWithTimeout(20*time.Minute, sb.String()); err != nil { // nolint: govet
return fmt.Errorf("pull failed: %w", err)
}
defer out.Close()

_, err = io.Copy(io.Discard, out)
return err
return nil
}

// Remove 删除镜像
func (r *containerImageRepo) Remove(id string) error {
_, err := r.client.ImageRemove(context.Background(), id, image.RemoveOptions{
Force: true,
PruneChildren: true,
})
_, err := shell.ExecfWithTimeout(30*time.Second, "%s rmi %s", r.cmd, id)
return err
}

// Prune 清理未使用的镜像
func (r *containerImageRepo) Prune() error {
_, err := r.client.ImagesPrune(context.Background(), filters.NewArgs())
_, err := shell.ExecfWithTimeout(30*time.Second, "%s image prune -f", r.cmd)
return err
}
16 changes: 1 addition & 15 deletions internal/service/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,10 @@ func (s *ContainerService) List(w http.ResponseWriter, r *http.Request) {
}

paged, total := Paginate(r, containers)
items := make([]any, 0)
for _, item := range paged {
items = append(items, map[string]any{
"id": item.ID,
"name": item.Name,
"image": item.Image,
"command": item.Command,
"created_at": item.CreatedAt,
"ports": item.Ports,
"labels": item.Labels,
"state": item.State,
"status": item.Status,
})
}

Success(w, chix.M{
"total": total,
"items": items,
"items": paged,
})
}

Expand Down
20 changes: 2 additions & 18 deletions internal/service/container_image.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package service

import (
"net/http"
"time"

"github.com/go-rat/chix"
"net/http"

"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/data"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/str"
)

type ContainerImageService struct {
Expand All @@ -31,22 +28,9 @@ func (s *ContainerImageService) List(w http.ResponseWriter, r *http.Request) {

paged, total := Paginate(r, images)

items := make([]any, 0)
for _, item := range paged {
items = append(items, map[string]any{
"id": item.ID,
"created": time.Unix(item.Created, 0).Format(time.DateTime),
"containers": item.Containers,
"size": str.FormatBytes(float64(item.Size)),
"labels": item.Labels,
"repo_tags": item.RepoTags,
"repo_digests": item.RepoDigests,
})
}

Success(w, chix.M{
"total": total,
"items": items,
"items": paged,
})
}

Expand Down
13 changes: 13 additions & 0 deletions pkg/types/container_image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package types

import (
"time"
)

type ContainerImage struct {
ID string `json:"id"`
Containers int64 `json:"containers"`
Tag string `json:"tag"`
Size string `json:"size"`
CreatedAt time.Time `json:"created_at"`
}

0 comments on commit f110fa5

Please sign in to comment.