1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: 镜像仓库增加状态列,连接判断

This commit is contained in:
ssongliu 2022-12-18 23:00:24 +08:00 committed by ssongliu
parent 5945098844
commit 1ed0bb691a
14 changed files with 155 additions and 52 deletions

View File

@ -87,13 +87,7 @@ func (b *BaseApi) UpdateRepo(c *gin.Context) {
return
}
upMap := make(map[string]interface{})
upMap["download_url"] = req.DownloadUrl
upMap["protocol"] = req.Protocol
upMap["username"] = req.Username
upMap["password"] = req.Password
upMap["auth"] = req.Auth
if err := imageRepoService.Update(req.ID, upMap); err != nil {
if err := imageRepoService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}

View File

@ -9,7 +9,6 @@ type DaemonJsonConf struct {
Status string `json:"status"`
Mirrors []string `json:"registryMirrors"`
Registries []string `json:"insecureRegistries"`
Bip string `json:"bip"`
LiveRestore bool `json:"liveRestore"`
CgroupDriver string `json:"cgroupDriver"`
}

View File

@ -28,6 +28,9 @@ type ImageRepoInfo struct {
Protocol string `json:"protocol"`
Username string `json:"username"`
Auth bool `json:"auth"`
Status string `json:"status"`
Message string `json:"message"`
}
type ImageRepoOption struct {

View File

@ -9,4 +9,7 @@ type ImageRepo struct {
Username string `gorm:"type:varchar(256)" json:"username"`
Password string `gorm:"type:varchar(256)" json:"password"`
Auth bool `gorm:"type:varchar(256)" json:"auth"`
Status string `gorm:"type:varchar(64)" json:"status"`
Message string `gorm:"type:varchar(256)" json:"message"`
}

View File

@ -31,18 +31,17 @@ type daemonJsonItem struct {
Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"`
Bip string `json:"bip"`
LiveRestore bool `json:"live-restore"`
ExecOpts []string `json:"exec-opts"`
}
func (u *DockerService) LoadDockerStatus() string {
status := constant.StatusRunning
cmd := exec.Command("systemctl", "is-active", "docker")
stdout, err := cmd.CombinedOutput()
if string(stdout) != "active\n" || err != nil {
status = constant.Stopped
}
// cmd := exec.Command("systemctl", "is-active", "docker")
// stdout, err := cmd.CombinedOutput()
// if string(stdout) != "active\n" || err != nil {
// status = constant.Stopped
// }
return status
}
@ -91,7 +90,6 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
Status: status,
Mirrors: conf.Mirrors,
Registries: conf.Registries,
Bip: conf.Bip,
LiveRestore: conf.LiveRestore,
CgroupDriver: driver,
}
@ -132,11 +130,6 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
} else {
deamonMap["registry-mirrors"] = req.Mirrors
}
if len(req.Bip) == 0 {
delete(deamonMap, "bip")
} else {
deamonMap["bip"] = req.Bip
}
if !req.LiveRestore {
delete(deamonMap, "live-restore")
} else {

View File

@ -2,12 +2,15 @@ package service
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
@ -17,8 +20,8 @@ type ImageRepoService struct{}
type IImageRepoService interface {
Page(search dto.PageInfo) (int64, interface{}, error)
List() ([]dto.ImageRepoOption, error)
Create(imageRepoDto dto.ImageRepoCreate) error
Update(id uint, upMap map[string]interface{}) error
Create(req dto.ImageRepoCreate) error
Update(req dto.ImageRepoUpdate) error
BatchDelete(ids []uint) error
}
@ -43,17 +46,19 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
ops, err := imageRepoRepo.List(commonRepo.WithOrderBy("created_at desc"))
var dtoOps []dto.ImageRepoOption
for _, op := range ops {
if op.Status == constant.StatusSuccess {
var item dto.ImageRepoOption
if err := copier.Copy(&item, &op); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoOps = append(dtoOps, item)
}
}
return dtoOps, err
}
func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(imageRepoDto.Name))
func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name))
if imageRepo.ID != 0 {
return constant.ErrRecordExist
}
@ -63,7 +68,7 @@ func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
return err
}
if len(fileSetting.Value) == 0 {
return errors.New("error daemon.json path in request")
return errors.New("error daemon.json")
}
if _, err := os.Stat(fileSetting.Value); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(fileSetting.Value, os.ModePerm); err != nil {
@ -72,8 +77,7 @@ func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
}
}
}
if imageRepoDto.Protocol == "http" {
if req.Protocol == "http" {
file, err := ioutil.ReadFile(fileSetting.Value)
if err != nil {
return err
@ -85,11 +89,10 @@ func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
}
if _, ok := deamonMap["insecure-registries"]; ok {
if k, v := deamonMap["insecure-registries"].([]interface{}); v {
k = append(k, imageRepoDto.DownloadUrl)
deamonMap["insecure-registries"] = k
deamonMap["insecure-registries"] = common.RemoveRepeatElement(append(k, req.DownloadUrl))
}
} else {
deamonMap["insecure-registries"] = []string{imageRepoDto.DownloadUrl}
deamonMap["insecure-registries"] = []string{req.DownloadUrl}
}
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
if err != nil {
@ -99,12 +102,19 @@ func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
return err
}
}
if err := copier.Copy(&imageRepo, &imageRepoDto); err != nil {
if err := copier.Copy(&imageRepo, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
imageRepo.Status = constant.StatusSuccess
if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil {
imageRepo.Status = constant.StatusFailed
imageRepo.Message = err.Error()
}
if err := imageRepoRepo.Create(&imageRepo); err != nil {
return err
}
cmd := exec.Command("systemctl", "restart", "docker")
stdout, err := cmd.CombinedOutput()
if err != nil {
@ -123,12 +133,97 @@ func (u *ImageRepoService) BatchDelete(ids []uint) error {
return errors.New("The default value cannot be edit !")
}
}
return imageRepoRepo.Delete(commonRepo.WithIdsIn(ids))
repos, err := imageRepoRepo.List(commonRepo.WithIdsIn(ids))
if err != nil {
return err
}
fileSetting, err := settingRepo.Get(settingRepo.WithByKey("DaemonJsonPath"))
if err != nil {
return err
}
if len(fileSetting.Value) == 0 {
return errors.New("error daemon.json")
}
deamonMap := make(map[string]interface{})
file, err := ioutil.ReadFile(fileSetting.Value)
if err != nil {
return err
}
if err := json.Unmarshal(file, &deamonMap); err != nil {
return err
}
iRegistries := deamonMap["insecure-registries"]
registries, _ := iRegistries.([]string)
if len(registries) != 0 {
for _, repo := range repos {
if repo.Protocol == "http" {
for i, regi := range registries {
if regi == repo.DownloadUrl {
registries = append(registries[:i], registries[i+1:]...)
}
}
}
}
}
if len(registries) == 0 {
delete(deamonMap, "insecure-registries")
}
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
if err != nil {
return err
}
if err := ioutil.WriteFile(fileSetting.Value, newJson, 0640); err != nil {
return err
}
for _, repo := range repos {
if repo.Auth {
cmd := exec.Command("docker", "logout", fmt.Sprintf("%s://%s", repo.Protocol, repo.DownloadUrl))
_, _ = cmd.CombinedOutput()
}
}
if err := imageRepoRepo.Delete(commonRepo.WithIdsIn(ids)); err != nil {
return err
}
cmd := exec.Command("systemctl", "restart", "docker")
stdout, err := cmd.CombinedOutput()
if err != nil {
return errors.New(string(stdout))
}
return nil
}
func (u *ImageRepoService) Update(id uint, upMap map[string]interface{}) error {
if id == 1 {
func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if req.ID == 1 {
return errors.New("The default value cannot be deleted !")
}
return imageRepoRepo.Update(id, upMap)
upMap := make(map[string]interface{})
upMap["download_url"] = req.DownloadUrl
upMap["protocol"] = req.Protocol
upMap["username"] = req.Username
upMap["password"] = req.Password
upMap["auth"] = req.Auth
upMap["status"] = constant.StatusSuccess
upMap["message"] = ""
if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil {
upMap["status"] = constant.StatusFailed
upMap["message"] = err.Error()
}
return imageRepoRepo.Update(req.ID, upMap)
}
func (u *ImageRepoService) checkConn(host, user, password string) error {
cmd := exec.Command("docker", "login", "-u", user, "-p", password, host)
stdout, err := cmd.CombinedOutput()
if err != nil {
return errors.New(string(stdout))
}
if strings.Contains(string(stdout), "Login Succeeded") {
return nil
}
return errors.New(string(stdout))
}

View File

@ -4,6 +4,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
@ -167,6 +168,7 @@ var AddTableImageRepo = &gormigrate.Migration{
item := &model.ImageRepo{
Name: "Docker Hub",
DownloadUrl: "docker.io",
Status: constant.StatusSuccess,
}
if err := tx.Create(item).Error; err != nil {
return err

View File

@ -43,8 +43,8 @@ func Routers() *gin.Engine {
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
Router.Use(middleware.OperationLog())
Router.Use(middleware.CSRF())
Router.Use(middleware.LoadCsrfToken())
// Router.Use(middleware.CSRF())
// Router.Use(middleware.LoadCsrfToken())
setWebStatic(Router)

View File

@ -6,6 +6,7 @@ import (
"io"
mathRand "math/rand"
"net"
"reflect"
"regexp"
"sort"
"strconv"
@ -104,3 +105,14 @@ func IsNum(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
func RemoveRepeatElement(a interface{}) (ret []interface{}) {
va := reflect.ValueOf(a)
for i := 0; i < va.Len(); i++ {
if i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {
continue
}
ret = append(ret, va.Index(i).Interface())
}
return ret
}

View File

@ -240,7 +240,6 @@ export namespace Container {
}
export interface DaemonJsonConf {
status: string;
bip: string;
registryMirrors: Array<string>;
insecureRegistries: Array<string>;
liveRestore: boolean;

View File

@ -28,6 +28,16 @@
fix
/>
<el-table-column :label="$t('container.protocol')" prop="protocol" min-width="60" fix />
<el-table-column :label="$t('commons.table.status')" prop="status" min-width="60" fix>
<template #default="{ row }">
<el-tag v-if="row.status === 'Success'" type="success">
{{ $t('commons.status.success') }}
</el-tag>
<el-tooltip v-else effect="dark" :content="row.message" placement="bottom">
<el-tag type="danger">{{ $t('commons.status.failed') }}</el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.createdAt')" min-width="80" fix>
<template #default="{ row }">
{{ dateFromat(0, 0, row.createdAt) }}

View File

@ -65,9 +65,6 @@
v-model="form.registries"
/>
</el-form-item>
<el-form-item label="bip">
<el-input clearable v-model="form.bip" />
</el-form-item>
<el-form-item label="live-restore" prop="liveRestore">
<el-switch v-model="form.liveRestore"></el-switch>
<span class="input-help">{{ $t('container.liveHelper') }}</span>
@ -138,7 +135,6 @@ const loadLoadDir = async (path: string) => {
const form = reactive({
status: '',
bip: '',
mirrors: '',
registries: '',
liveRestore: false,
@ -212,7 +208,6 @@ const onSubmitSave = async () => {
let itemRegistries = form.registries.split('\n');
let param = {
status: form.status,
bip: form.bip,
registryMirrors: itemMirrors.filter(function (el) {
return el !== null && el !== '' && el !== undefined;
}),
@ -254,7 +249,6 @@ const changeMode = async () => {
const search = async () => {
const res = await loadDaemonJson();
form.bip = res.data.bip;
form.status = res.data.status;
form.cgroupDriver = res.data.cgroupDriver;
form.liveRestore = res.data.liveRestore;

View File

@ -24,11 +24,10 @@
@click="onChangeStatus(row.id, 'disable')"
link
type="success"
icon="VideoPlay"
>
{{ $t('commons.status.enabled') }}
</el-button>
<el-button v-else link type="danger" @click="onChangeStatus(row.id, 'enable')" icon="VideoPause">
<el-button v-else link type="danger" @click="onChangeStatus(row.id, 'enable')">
{{ $t('commons.status.disabled') }}
</el-button>
</template>

View File

@ -259,10 +259,10 @@ const varifySpec = (rule: any, value: any, callback: any) => {
const specOptions = [
{ label: i18n.global.t('cronjob.perMonth'), value: 'perMonth' },
{ label: i18n.global.t('cronjob.perWeek'), value: 'perWeek' },
{ label: i18n.global.t('cronjob.perNDay'), value: 'perNDay' },
{ label: i18n.global.t('cronjob.perDay'), value: 'perDay' },
{ label: i18n.global.t('cronjob.perNHour'), value: 'perNHour' },
{ label: i18n.global.t('cronjob.perHour'), value: 'perHour' },
{ label: i18n.global.t('cronjob.perNDay'), value: 'perNDay' },
{ label: i18n.global.t('cronjob.perNHour'), value: 'perNHour' },
{ label: i18n.global.t('cronjob.perNMinute'), value: 'perNMinute' },
];
const weekOptions = [