diff --git a/app/http/controllers/container_controller.go b/app/http/controllers/container_controller.go index 5bb17cce..4891b20e 100644 --- a/app/http/controllers/container_controller.go +++ b/app/http/controllers/container_controller.go @@ -1,19 +1,384 @@ package controllers import ( + "fmt" + "strconv" + "strings" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/go-connections/nat" "github.com/goravel/framework/contracts/http" + requests "panel/app/http/requests/container" + "panel/internal/services" ) type ContainerController struct { - // Dependent services + container services.Container } func NewContainerController() *ContainerController { return &ContainerController{ - // Inject services + container: services.NewContainer(), } } -func (r *ContainerController) Index(ctx http.Context) http.Response { - return nil +func (r *ContainerController) ContainerList(ctx http.Context) http.Response { + containers, err := r.container.ContainerListAll() + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, containers) +} + +func (r *ContainerController) ContainerSearch(ctx http.Context) http.Response { + fields := strings.Fields(ctx.Request().Query("names")) + containers, err := r.container.ContainerListByNames(fields) + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, containers) +} + +func (r *ContainerController) ContainerCreate(ctx http.Context) http.Response { + var request requests.ContainerCreate + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + var hostConf container.HostConfig + var networkConf network.NetworkingConfig + + portMap := make(nat.PortMap) + for _, port := range request.Ports { + if port.ContainerStart-port.ContainerEnd != port.HostStart-port.HostEnd { + return Error(ctx, http.StatusUnprocessableEntity, fmt.Sprintf("容器端口和主机端口数量不匹配(容器: %d 主机: %d)", port.ContainerStart-port.ContainerEnd, port.HostStart-port.HostEnd)) + } + if port.ContainerStart > port.ContainerEnd || port.HostStart > port.HostEnd || port.ContainerStart < 1 || port.HostStart < 1 { + return Error(ctx, http.StatusUnprocessableEntity, "端口范围不正确") + } + + count := 0 + for host := port.HostStart; host <= port.HostEnd; host++ { + bindItem := nat.PortBinding{HostPort: strconv.Itoa(host), HostIP: port.Host} + portMap[nat.Port(fmt.Sprintf("%d/%s", port.ContainerStart+count, port.Protocol))] = []nat.PortBinding{bindItem} + count++ + } + } + + exposed := make(nat.PortSet) + for port := range portMap { + exposed[port] = struct{}{} + } + + if request.Network != "" { + switch request.Network { + case "host", "none", "bridge": + hostConf.NetworkMode = container.NetworkMode(request.Network) + } + networkConf.EndpointsConfig = map[string]*network.EndpointSettings{request.Network: {}} + } else { + networkConf = network.NetworkingConfig{} + } + + hostConf.Privileged = request.Privileged + hostConf.AutoRemove = request.AutoRemove + hostConf.CPUShares = request.CPUShares + hostConf.PublishAllPorts = request.PublishAllPorts + hostConf.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(request.RestartPolicy)} + if request.RestartPolicy == "on-failure" { + hostConf.RestartPolicy.MaximumRetryCount = 5 + } + hostConf.NanoCPUs = request.CPUs * 1000000000 + hostConf.Memory = request.Memory * 1024 * 1024 + hostConf.MemorySwap = 0 + hostConf.PortBindings = portMap + hostConf.Binds = []string{} + + volumes := make(map[string]struct{}) + for _, volume := range request.Volumes { + volumes[volume.Container] = struct{}{} + hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.Host, volume.Container, volume.Mode)) + } + + id, err := r.container.ContainerCreate(request.Name, + container.Config{ + Image: request.Image, + Env: request.Env, + Entrypoint: request.Entrypoint, + Cmd: request.Command, + Labels: r.container.SliceToMap(request.Labels), + ExposedPorts: exposed, + OpenStdin: request.OpenStdin, + Tty: request.Tty, + Volumes: volumes, + }, + hostConf, + networkConf, + ) + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, id) +} + +func (r *ContainerController) ContainerRemove(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerRemove(request.ID); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerStart(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerStart(request.ID); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerStop(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerStop(request.ID); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerRestart(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerRestart(request.ID); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerPause(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerPause(request.ID); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerUnpause(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerUnpause(request.ID); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerInspect(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + data, err := r.container.ContainerInspect(request.ID) + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, data) +} + +func (r *ContainerController) ContainerKill(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerKill(request.ID); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerRename(ctx http.Context) http.Response { + var request requests.ContainerRename + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + if err := r.container.ContainerRename(request.ID, request.Name); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) ContainerStats(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + data, err := r.container.ContainerStats(request.ID) + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, data) +} + +func (r *ContainerController) ContainerExist(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + exist, err := r.container.ContainerExist(request.ID) + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, exist) +} + +func (r *ContainerController) ContainerLogs(ctx http.Context) http.Response { + var request requests.ID + if sanitize := Sanitize(ctx, &request); sanitize != nil { + return sanitize + } + + data, err := r.container.ContainerLogs(request.ID) + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, data) +} + +func (r *ContainerController) ContainerPrune(ctx http.Context) http.Response { + if err := r.container.ContainerPrune(); err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, nil) +} + +func (r *ContainerController) NetworkList(ctx http.Context) http.Response { + networks, err := r.container.NetworkList() + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, networks) +} + +func (r *ContainerController) NetworkCreate(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) NetworkRemove(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) NetworkExist(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) NetworkInspect(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) NetworkConnect(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) NetworkDisconnect(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) NetworkPrune(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) ImageList(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) ImageExist(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) ImagePull(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) ImageRemove(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) ImagePrune(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) ImageInspect(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) VolumeList(ctx http.Context) http.Response { + volumes, err := r.container.VolumeList() + if err != nil { + return Error(ctx, http.StatusInternalServerError, err.Error()) + } + + return Success(ctx, volumes) +} + +func (r *ContainerController) VolumeCreate(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) VolumeExist(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) VolumeInspect(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) VolumeRemove(ctx http.Context) http.Response { + return Success(ctx, nil) +} + +func (r *ContainerController) VolumePrune(ctx http.Context) http.Response { + return Success(ctx, nil) } diff --git a/app/http/requests/container/container_create.go b/app/http/requests/container/container_create.go new file mode 100644 index 00000000..fae295fe --- /dev/null +++ b/app/http/requests/container/container_create.go @@ -0,0 +1,66 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type ContainerCreate struct { + Name string `form:"name" json:"name"` + Image string `form:"image" json:"image"` + Ports []ContainerPort `form:"ports" json:"ports"` + Network string `form:"network" json:"network"` + Volumes []ContainerVolume `form:"volumes" json:"volumes"` + Labels []string `form:"labels" json:"labels"` + Env []string `form:"env" json:"env"` + Entrypoint []string `form:"entrypoint" json:"entrypoint"` + Command []string `form:"command" json:"command"` + RestartPolicy string `form:"restart_policy" json:"restart_policy"` + AutoRemove bool `form:"auto_remove" json:"auto_remove"` + Privileged bool `form:"privileged" json:"privileged"` + OpenStdin bool `form:"openStdin" json:"open_stdin"` + PublishAllPorts bool `form:"publish_all_ports" json:"publish_all_ports"` + Tty bool `form:"tty" json:"tty"` + CPUShares int64 `form:"cpu_shares" json:"cpu_shares"` + CPUs int64 `form:"cpus" json:"cpus"` + Memory int64 `form:"memory" json:"memory"` +} + +func (r *ContainerCreate) Authorize(ctx http.Context) error { + return nil +} + +func (r *ContainerCreate) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "name": "required|string", + "image": "required|string", + "ports": "slice", + "network": "string", + "volumes": "slice", + "labels": "slice", + "env": "slice", + "entrypoint": "slice", + "command": "slice", + "restart_policy": "string|in:always,on-failure,unless-stopped,no", + "auto_remove": "bool", + "privileged": "bool", + "open_stdin": "bool", + "publish_all_ports": "bool", + "tty": "bool", + "cpu_shares": "int", + "cpus": "int", + "memory": "int", + } +} + +func (r *ContainerCreate) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ContainerCreate) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ContainerCreate) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/container/container_rename.go b/app/http/requests/container/container_rename.go new file mode 100644 index 00000000..569295d7 --- /dev/null +++ b/app/http/requests/container/container_rename.go @@ -0,0 +1,34 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type ContainerRename struct { + ID string `form:"id" json:"id"` + Name string `form:"name" json:"name"` +} + +func (r *ContainerRename) Authorize(ctx http.Context) error { + return nil +} + +func (r *ContainerRename) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|string", + "name": "required|string", + } +} + +func (r *ContainerRename) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ContainerRename) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ContainerRename) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/container/container_update.go b/app/http/requests/container/container_update.go new file mode 100644 index 00000000..66743726 --- /dev/null +++ b/app/http/requests/container/container_update.go @@ -0,0 +1,68 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type ContainerUpdate struct { + ID string `form:"id" json:"id"` + Name string `form:"name" json:"name"` + Image string `form:"image" json:"image"` + Ports []ContainerPort `form:"ports" json:"ports"` + Network string `form:"network" json:"network"` + Volumes []ContainerVolume `form:"volumes" json:"volumes"` + Labels []string `form:"labels" json:"labels"` + Env []string `form:"env" json:"env"` + Entrypoint []string `form:"entrypoint" json:"entrypoint"` + Command []string `form:"command" json:"command"` + RestartPolicy string `form:"restart_policy" json:"restart_policy"` + AutoRemove bool `form:"auto_remove" json:"auto_remove"` + Privileged bool `form:"privileged" json:"privileged"` + OpenStdin bool `form:"openStdin" json:"open_stdin"` + PublishAllPorts bool `form:"publish_all_ports" json:"publish_all_ports"` + Tty bool `form:"tty" json:"tty"` + CPUShares int64 `form:"cpu_shares" json:"cpu_shares"` + CPUs int64 `form:"cpus" json:"cpus"` + Memory int64 `form:"memory" json:"memory"` +} + +func (r *ContainerUpdate) Authorize(ctx http.Context) error { + return nil +} + +func (r *ContainerUpdate) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|string", + "name": "required|string", + "image": "required|string", + "ports": "slice", + "network": "string", + "volumes": "slice", + "labels": "slice", + "env": "slice", + "entrypoint": "slice", + "command": "slice", + "restart_policy": "string|in:always,on-failure,unless-stopped,no", + "auto_remove": "bool", + "privileged": "bool", + "open_stdin": "bool", + "publish_all_ports": "bool", + "tty": "bool", + "cpu_shares": "int", + "cpus": "int", + "memory": "int", + } +} + +func (r *ContainerUpdate) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ContainerUpdate) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ContainerUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/container/id.go b/app/http/requests/container/id.go new file mode 100644 index 00000000..a6a00050 --- /dev/null +++ b/app/http/requests/container/id.go @@ -0,0 +1,32 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type ID struct { + ID string `form:"id" json:"id"` +} + +func (r *ID) Authorize(ctx http.Context) error { + return nil +} + +func (r *ID) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|string", + } +} + +func (r *ID) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ID) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *ID) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/container/types.go b/app/http/requests/container/types.go new file mode 100644 index 00000000..016b8473 --- /dev/null +++ b/app/http/requests/container/types.go @@ -0,0 +1,16 @@ +package requests + +type ContainerPort struct { + ContainerStart int `json:"start"` + ContainerEnd int `json:"end"` + Host string `json:"host"` + HostStart int `json:"hostStart"` + HostEnd int `json:"hostEnd"` + Protocol string `json:"protocol"` +} + +type ContainerVolume struct { + Host string `json:"host"` + Container string `json:"container"` + Mode string `json:"mode"` +} diff --git a/internal/container.go b/internal/container.go index 5bf0569c..368569f6 100644 --- a/internal/container.go +++ b/internal/container.go @@ -1 +1,50 @@ package internal + +import ( + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/volume" +) + +type Container interface { + ContainerListAll() ([]types.Container, error) + ContainerListByNames(names []string) ([]types.Container, error) + ContainerCreate(name string, config container.Config, host container.HostConfig, networkConfig network.NetworkingConfig) (string, error) + ContainerRemove(id string) error + ContainerStart(id string) error + ContainerStop(id string) error + ContainerRestart(id string) error + ContainerPause(id string) error + ContainerUnpause(id string) error + ContainerInspect(id string) (types.ContainerJSON, error) + ContainerKill(id string) error + ContainerRename(id string, newName string) error + ContainerStats(id string) (types.ContainerStats, error) + ContainerExist(name string) (bool, error) + ContainerUpdate(id string, config container.UpdateConfig) error + ContainerLogs(id string) (string, error) + ContainerPrune() error + NetworkList() ([]types.NetworkResource, error) + NetworkCreate(name string) error + NetworkRemove(id string) error + NetworkExist(name string) (bool, error) + NetworkInspect(id string) (types.NetworkResource, error) + NetworkConnect(networkID string, containerID string) error + NetworkDisconnect(networkID string, containerID string) error + NetworkPrune() error + ImageList() ([]image.Summary, error) + ImageExist(reference string) (bool, error) + ImagePull(reference string) error + ImageRemove(imageID string) error + ImagePrune() error + ImageInspect(imageID string) (types.ImageInspect, error) + VolumeList() ([]*volume.Volume, error) + VolumeCreate(name string, options, labels map[string]string) (volume.Volume, error) + VolumeExist(name string) (bool, error) + VolumeInspect(volumeID string) (volume.Volume, error) + VolumeRemove(volumeID string) error + VolumePrune() error + SliceToMap(slice []string) map[string]string +} diff --git a/internal/services/container.go b/internal/services/container.go index 17b21a60..332bbcd9 100644 --- a/internal/services/container.go +++ b/internal/services/container.go @@ -3,6 +3,7 @@ package services import ( "context" "io" + "strings" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -17,12 +18,12 @@ type Container struct { client *client.Client } -func NewContainer(sock ...string) *Container { +func NewContainer(sock ...string) Container { if len(sock) == 0 { sock[0] = "/run/podman/podman.sock" } cli, _ := client.NewClientWithOpts(client.WithHost("unix://"+sock[0]), client.WithAPIVersionNegotiation()) - return &Container{ + return Container{ client: cli, } } @@ -81,6 +82,11 @@ func (r *Container) ContainerStop(id string) error { return r.client.ContainerStop(context.Background(), id, container.StopOptions{}) } +// ContainerRestart 重启容器 +func (r *Container) ContainerRestart(id string) error { + return r.client.ContainerRestart(context.Background(), id, container.StopOptions{}) +} + // ContainerPause 暂停容器 func (r *Container) ContainerPause(id string) error { return r.client.ContainerPause(context.Background(), id) @@ -111,8 +117,8 @@ func (r *Container) ContainerStats(id string) (types.ContainerStats, error) { return r.client.ContainerStats(context.Background(), id, false) } -// ContainerExists 判断容器是否存在 -func (r *Container) ContainerExists(name string) (bool, error) { +// ContainerExist 判断容器是否存在 +func (r *Container) ContainerExist(name string) (bool, error) { var options container.ListOptions options.Filters = filters.NewArgs(filters.Arg("name", name)) containers, err := r.client.ContainerList(context.Background(), options) @@ -155,11 +161,6 @@ func (r *Container) ContainerPrune() error { return err } -// ContainerRestart 重启容器 -func (r *Container) ContainerRestart(id string) error { - return r.client.ContainerRestart(context.Background(), id, container.StopOptions{}) -} - // NetworkList 列出网络 func (r *Container) NetworkList() ([]types.NetworkResource, error) { return r.client.NetworkList(context.Background(), types.NetworkListOptions{}) @@ -174,6 +175,11 @@ func (r *Container) NetworkCreate(name string) error { return err } +// NetworkRemove 删除网络 +func (r *Container) NetworkRemove(id string) error { + return r.client.NetworkRemove(context.Background(), id) +} + // NetworkExist 判断网络是否存在 func (r *Container) NetworkExist(name string) (bool, error) { var options types.NetworkListOptions @@ -207,11 +213,6 @@ func (r *Container) NetworkPrune() error { return err } -// NetworkRemove 删除网络 -func (r *Container) NetworkRemove(id string) error { - return r.client.NetworkRemove(context.Background(), id) -} - // ImageList 列出镜像 func (r *Container) ImageList() ([]image.Summary, error) { return r.client.ImageList(context.Background(), types.ImageListOptions{ @@ -258,6 +259,12 @@ func (r *Container) ImageInspect(id string) (types.ImageInspect, error) { return img, err } +// VolumeList 列出存储卷 +func (r *Container) VolumeList() ([]*volume.Volume, error) { + volumes, err := r.client.VolumeList(context.Background(), volume.ListOptions{}) + return volumes.Volumes, err +} + // VolumeCreate 创建存储卷 func (r *Container) VolumeCreate(name string, options, labels map[string]string) (volume.Volume, error) { return r.client.VolumeCreate(context.Background(), volume.CreateOptions{ @@ -268,12 +275,6 @@ func (r *Container) VolumeCreate(name string, options, labels map[string]string) }) } -// VolumeList 列出存储卷 -func (r *Container) VolumeList() ([]*volume.Volume, error) { - volumes, err := r.client.VolumeList(context.Background(), volume.ListOptions{}) - return volumes.Volumes, err -} - // VolumeExist 判断存储卷是否存在 func (r *Container) VolumeExist(name string) (bool, error) { var options volume.ListOptions @@ -301,3 +302,15 @@ func (r *Container) VolumePrune() error { _, err := r.client.VolumesPrune(context.Background(), filters.NewArgs()) return err } + +// SliceToMap 将切片转换为 map +func (r *Container) SliceToMap(slice []string) map[string]string { + m := make(map[string]string) + for _, s := range slice { + if strings.Contains(s, "=") { + sps := strings.SplitN(s, "=", 2) + m[sps[0]] = sps[1] + } + } + return m +} diff --git a/routes/api.go b/routes/api.go index 5e358b5d..0ddfc86b 100644 --- a/routes/api.go +++ b/routes/api.go @@ -113,6 +113,54 @@ func Api() { r.Get("pingStatus", safeController.GetPingStatus) r.Post("pingStatus", safeController.SetPingStatus) }) + r.Prefix("container").Middleware(middleware.Jwt()).Group(func(r route.Router) { + containerController := controllers.NewContainerController() + r.Get("list", containerController.ContainerList) + r.Get("search", containerController.ContainerSearch) + r.Post("create", containerController.ContainerCreate) + r.Post("remove", containerController.ContainerRemove) + r.Post("start", containerController.ContainerStart) + r.Post("stop", containerController.ContainerStop) + r.Post("restart", containerController.ContainerRestart) + r.Post("pause", containerController.ContainerPause) + r.Post("unpause", containerController.ContainerUnpause) + r.Get("inspect", containerController.ContainerInspect) + r.Post("kill", containerController.ContainerKill) + r.Post("rename", containerController.ContainerRename) + r.Get("stats", containerController.ContainerStats) + r.Post("exist", containerController.ContainerExist) + r.Get("logs", containerController.ContainerLogs) + r.Post("prune", containerController.ContainerPrune) + + r.Prefix("network").Group(func(r route.Router) { + r.Get("list", containerController.NetworkList) + r.Post("create", containerController.NetworkCreate) + r.Post("remove", containerController.NetworkRemove) + r.Post("exist", containerController.NetworkExist) + r.Get("inspect", containerController.NetworkInspect) + r.Post("connect", containerController.NetworkConnect) + r.Post("disconnect", containerController.NetworkDisconnect) + r.Post("prune", containerController.NetworkPrune) + }) + + r.Prefix("image").Group(func(r route.Router) { + r.Get("list", containerController.ImageList) + r.Post("exist", containerController.ImageExist) + r.Post("pull", containerController.ImagePull) + r.Post("remove", containerController.ImageRemove) + r.Post("prune", containerController.ImagePrune) + r.Get("inspect", containerController.ImageInspect) + }) + + r.Prefix("volume").Group(func(r route.Router) { + r.Get("list", containerController.VolumeList) + r.Post("create", containerController.VolumeCreate) + r.Post("exist", containerController.VolumeExist) + r.Get("inspect", containerController.VolumeInspect) + r.Post("remove", containerController.VolumeRemove) + r.Post("prune", containerController.VolumePrune) + }) + }) r.Prefix("file").Middleware(middleware.Jwt()).Group(func(r route.Router) { fileController := controllers.NewFileController() r.Post("create", fileController.Create)