mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-29 16:05:34 +00:00
feat(container): add image update checks with scheduling and task logs
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) GetImageRemoteCheckStatus(c *gin.Context) {
|
||||
status, err := service.GetImageRemoteCheckStatus()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, status)
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
@@ -28,16 +27,15 @@ import (
|
||||
const imageCheckUpdateSetting = "ContainerImageUpdateEnabled"
|
||||
|
||||
type ImageCheckUpdateStatus struct {
|
||||
ProxyRequired bool `json:"proxyRequired"`
|
||||
TaskID string `json:"taskID"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Running bool `json:"running"`
|
||||
Checked int `json:"checked"`
|
||||
Updates int `json:"updates"`
|
||||
Failed int `json:"failed"`
|
||||
Skipped int `json:"skipped"`
|
||||
Error string `json:"error"`
|
||||
FinishedAt int64 `json:"finishedAt"`
|
||||
TaskID string `json:"taskID"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Running bool `json:"running"`
|
||||
Checked int `json:"checked"`
|
||||
Updates int `json:"updates"`
|
||||
Failed int `json:"failed"`
|
||||
Skipped int `json:"skipped"`
|
||||
Error string `json:"error"`
|
||||
FinishedAt int64 `json:"finishedAt"`
|
||||
}
|
||||
|
||||
var imageCheckUpdateState struct {
|
||||
@@ -50,23 +48,10 @@ func GetContainerImageCheckUpdateStatus() (ImageCheckUpdateStatus, error) {
|
||||
if err := global.DB.Where("key = ?", imageCheckUpdateSetting).First(&setting).Error; err != nil {
|
||||
return ImageCheckUpdateStatus{}, err
|
||||
}
|
||||
httpClient, proxyErr := dockerhub.NewHTTPClient()
|
||||
proxyRequired := false
|
||||
if proxyErr != nil {
|
||||
if business, ok := proxyErr.(buserr.BusinessError); ok && business.Msg == "ErrImageCheckProxyRequired" {
|
||||
proxyRequired = true
|
||||
} else {
|
||||
return ImageCheckUpdateStatus{}, proxyErr
|
||||
}
|
||||
}
|
||||
if httpClient != nil && httpClient.Transport != nil {
|
||||
httpClient.CloseIdleConnections()
|
||||
}
|
||||
imageCheckUpdateState.Lock()
|
||||
defer imageCheckUpdateState.Unlock()
|
||||
status := imageCheckUpdateState.status
|
||||
status.Enabled = setting.Value == "Enable"
|
||||
status.ProxyRequired = proxyRequired
|
||||
return status, nil
|
||||
}
|
||||
func SetContainerImageCheckUpdateSchedule(enabled bool) error {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/dockerhub"
|
||||
)
|
||||
|
||||
type ImageRemoteCheckStatus struct {
|
||||
ProxyRequired bool `json:"proxyRequired"`
|
||||
}
|
||||
|
||||
// GetImageRemoteCheckStatus validates local settings without contacting Docker Hub.
|
||||
// Both image searches and update checks use the same proxy policy.
|
||||
func GetImageRemoteCheckStatus() (ImageRemoteCheckStatus, error) {
|
||||
client, err := dockerhub.NewHTTPClient()
|
||||
if err != nil {
|
||||
var business buserr.BusinessError
|
||||
if errors.As(err, &business) && business.Msg == "ErrImageCheckProxyRequired" {
|
||||
return ImageRemoteCheckStatus{ProxyRequired: true}, nil
|
||||
}
|
||||
return ImageRemoteCheckStatus{}, err
|
||||
}
|
||||
if client.Transport != nil {
|
||||
client.CloseIdleConnections()
|
||||
}
|
||||
return ImageRemoteCheckStatus{}, nil
|
||||
}
|
||||
@@ -67,6 +67,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
baRouter.POST("/template", baseApi.CreateComposeTemplate)
|
||||
baRouter.POST("/template/del", baseApi.DeleteComposeTemplate)
|
||||
|
||||
baRouter.GET("/image/remote/check", baseApi.GetImageRemoteCheckStatus)
|
||||
baRouter.GET("/image", baseApi.ListImage)
|
||||
baRouter.GET("/image/all", baseApi.ListAllImage)
|
||||
baRouter.POST("/image/search", baseApi.SearchImage)
|
||||
|
||||
@@ -291,8 +291,17 @@ export const updateDaemonJsonByfile = (params: Container.DaemonJsonUpdateByFile)
|
||||
return http.post(`/containers/daemonjson/update/byfile`, params, TimeoutEnum.T_3M);
|
||||
};
|
||||
|
||||
export interface ContainerImageCheckUpdateStatus {
|
||||
export interface ImageRemoteCheckStatus {
|
||||
proxyRequired: boolean;
|
||||
}
|
||||
export const getImageRemoteCheckStatus = (node: string, signal?: AbortSignal) =>
|
||||
http.get<ImageRemoteCheckStatus>(
|
||||
'/containers/image/remote/check',
|
||||
{},
|
||||
{ headers: { CurrentNode: node }, signal },
|
||||
);
|
||||
|
||||
export interface ContainerImageCheckUpdateStatus {
|
||||
taskID: string;
|
||||
enabled: boolean;
|
||||
running: boolean;
|
||||
|
||||
@@ -505,6 +505,7 @@ import { ElMessageBox } from 'element-plus';
|
||||
import { ArrowDown } from '@element-plus/icons-vue';
|
||||
import {
|
||||
getContainerImageCheckUpdateStatus,
|
||||
getImageRemoteCheckStatus,
|
||||
runContainerImageCheckUpdate,
|
||||
setContainerImageCheckUpdateSchedule,
|
||||
type ContainerImageCheckUpdateStatus,
|
||||
@@ -878,7 +879,10 @@ const onImageCheckUpdateCommand = async (command: string) => {
|
||||
if (generation !== imageCheckUpdateGeneration || node !== currentNode.value) return;
|
||||
imageCheckUpdateStatus.value = status.data;
|
||||
// Disabling an existing schedule must remain possible without a proxy.
|
||||
if ((command === 'check' || !status.data.enabled) && status.data.proxyRequired) {
|
||||
const proxyStatus =
|
||||
command === 'check' || !status.data.enabled ? await getImageRemoteCheckStatus(node) : undefined;
|
||||
if (generation !== imageCheckUpdateGeneration || node !== currentNode.value) return;
|
||||
if (proxyStatus?.data.proxyRequired) {
|
||||
try {
|
||||
await ElMessageBox.confirm(i18n.global.t('container.imageCheckUpdateProxyRequired'), '', {
|
||||
confirmButtonText: i18n.global.t('container.imageCheckUpdateConfigureProxy'),
|
||||
|
||||
Reference in New Issue
Block a user