diff --git a/pkg/daemon/builder/builder.go b/pkg/daemon/builder/builder.go index 19cb06e1..7e0cdaa1 100644 --- a/pkg/daemon/builder/builder.go +++ b/pkg/daemon/builder/builder.go @@ -26,6 +26,7 @@ import ( "github.com/go-sigma/sigma/pkg/builder" "github.com/go-sigma/sigma/pkg/dal/dao" + "github.com/go-sigma/sigma/pkg/dal/query" "github.com/go-sigma/sigma/pkg/modules/workq" "github.com/go-sigma/sigma/pkg/modules/workq/definition" "github.com/go-sigma/sigma/pkg/types" @@ -36,9 +37,9 @@ import ( func init() { workq.TopicHandlers[enums.DaemonBuilder.String()] = definition.Consumer{ Handler: builderRunner, - MaxRetry: 6, + MaxRetry: 1, Concurrency: 10, - Timeout: time.Minute * 10, + Timeout: time.Minute * 60, } } @@ -85,6 +86,35 @@ func (b runner) runner(ctx context.Context, payload types.DaemonBuilderPayload) return fmt.Errorf("Get runner failed: %v", err) } + defer func() { + var updates map[string]any + if !(payload.Action == enums.DaemonBuilderActionStart || payload.Action == enums.DaemonBuilderActionRestart || payload.Action == enums.DaemonBuilderActionStop) { + updates = map[string]any{ + query.BuilderRunner.Status.ColumnName().String(): enums.BuildStatusFailed, + query.BuilderRunner.StatusMessage.ColumnName().String(): fmt.Sprintf("Daemon builder action(%s) is not support", payload.Action), + query.BuilderRunner.EndedAt.ColumnName().String(): time.Now().UnixMilli(), + } + } + if err != nil { + updates = map[string]any{ + query.BuilderRunner.Status.ColumnName().String(): enums.BuildStatusFailed, + query.BuilderRunner.StatusMessage.ColumnName().String(): err.Error(), + query.BuilderRunner.EndedAt.ColumnName().String(): time.Now().UnixMilli(), + } + } + if len(updates) > 0 { + err = builderService.UpdateRunner(ctx, payload.BuilderID, payload.RunnerID, updates) + if err != nil { + log.Error().Err(err).Msg("Update runner after got error") + } + } + }() + + if builder.Driver == nil { + err = fmt.Errorf("Builder driver is not initialized") + return fmt.Errorf("Builder driver is not initialized, or check config.daemon.builder.enabled is true or not") + } + platforms := []enums.OciPlatform{} for _, p := range strings.Split(builderObj.BuildkitPlatforms, ",") { platforms = append(platforms, enums.OciPlatform(p)) @@ -144,17 +174,12 @@ func (b runner) runner(ctx context.Context, payload types.DaemonBuilderPayload) } buildConfig.Builder.ScmProvider = (*enums.ScmProvider)(&builderObj.CodeRepository.User3rdParty.Provider) // TODO: change type } - if payload.Action == enums.DaemonBuilderActionStart { // nolint: gocritic - err = builder.Driver.Start(ctx, buildConfig) - } else if payload.Action == enums.DaemonBuilderActionRestart { + if payload.Action == enums.DaemonBuilderActionStart || payload.Action == enums.DaemonBuilderActionRestart { err = builder.Driver.Start(ctx, buildConfig) - } else { - log.Error().Err(err).Str("action", payload.Action.String()).Msg("Daemon builder action not found") - return fmt.Errorf("Daemon builder action not found") - } - if err != nil { - log.Error().Err(err).Msg("Start or restart builder failed") - return fmt.Errorf("Start or restart builder failed: %v", err) + if err != nil { + log.Error().Err(err).Msg("Start or restart builder failed") + return fmt.Errorf("Start or restart builder failed: %v", err) + } } return nil } diff --git a/pkg/dal/migrations/mysql/0001_initialize.up.sql b/pkg/dal/migrations/mysql/0001_initialize.up.sql index a5cddb14..ac6ab977 100644 --- a/pkg/dal/migrations/mysql/0001_initialize.up.sql +++ b/pkg/dal/migrations/mysql/0001_initialize.up.sql @@ -666,6 +666,7 @@ CREATE TABLE IF NOT EXISTS `builder_runners` ( `builder_id` bigint NOT NULL, `log` LONGBLOB, `status` ENUM ('Success', 'Failed', 'Pending', 'Scheduling', 'Building', 'Stopping', 'Stopped') NOT NULL DEFAULT 'Pending', + `status_message` varchar(255), -- common settings `tag` varchar(128), `raw_tag` varchar(255) NOT NULL, diff --git a/pkg/dal/migrations/postgresql/0001_initialize.up.sql b/pkg/dal/migrations/postgresql/0001_initialize.up.sql index f2272434..4d7a6c6e 100644 --- a/pkg/dal/migrations/postgresql/0001_initialize.up.sql +++ b/pkg/dal/migrations/postgresql/0001_initialize.up.sql @@ -857,6 +857,7 @@ CREATE TABLE IF NOT EXISTS "builder_runners" ( "builder_id" bigint NOT NULL, "log" bytea, "status" builder_runner_status NOT NULL DEFAULT 'Pending', + "status_message" varchar(255), -- common settings "tag" varchar(128), "raw_tag" varchar(255) NOT NULL, diff --git a/pkg/dal/migrations/sqlite3/0001_initialize.up.sql b/pkg/dal/migrations/sqlite3/0001_initialize.up.sql index e50e6181..dcbac5a1 100644 --- a/pkg/dal/migrations/sqlite3/0001_initialize.up.sql +++ b/pkg/dal/migrations/sqlite3/0001_initialize.up.sql @@ -763,6 +763,7 @@ CREATE TABLE IF NOT EXISTS `builder_runners` ( `builder_id` integer NOT NULL, `log` BLOB, `status` text CHECK (`status` IN ('Success', 'Failed', 'Pending', 'Scheduling', 'Building', 'Stopping', 'Stopped')) NOT NULL DEFAULT 'Pending', + `status_message` varchar(255), -- common settings `tag` varchar(128), -- image tag `raw_tag` varchar(255) NOT NULL, -- image tag diff --git a/pkg/dal/models/builder.go b/pkg/dal/models/builder.go index 1d6df62c..d0f98cb1 100644 --- a/pkg/dal/models/builder.go +++ b/pkg/dal/models/builder.go @@ -81,9 +81,10 @@ type BuilderRunner struct { DeletedAt soft_delete.DeletedAt `gorm:"softDelete:milli"` ID int64 `gorm:"primaryKey"` - BuilderID int64 - Log []byte - Status enums.BuildStatus `gorm:"default:Pending"` + BuilderID int64 + Log []byte + Status enums.BuildStatus `gorm:"default:Pending"` + StatusMessage *string Tag *string RawTag string diff --git a/pkg/dal/query/builder_runners.gen.go b/pkg/dal/query/builder_runners.gen.go index bf611e64..a996fd6b 100644 --- a/pkg/dal/query/builder_runners.gen.go +++ b/pkg/dal/query/builder_runners.gen.go @@ -34,6 +34,7 @@ func newBuilderRunner(db *gorm.DB, opts ...gen.DOOption) builderRunner { _builderRunner.BuilderID = field.NewInt64(tableName, "builder_id") _builderRunner.Log = field.NewBytes(tableName, "log") _builderRunner.Status = field.NewField(tableName, "status") + _builderRunner.StatusMessage = field.NewString(tableName, "status_message") _builderRunner.Tag = field.NewString(tableName, "tag") _builderRunner.RawTag = field.NewString(tableName, "raw_tag") _builderRunner.Description = field.NewString(tableName, "description") @@ -108,22 +109,23 @@ func newBuilderRunner(db *gorm.DB, opts ...gen.DOOption) builderRunner { type builderRunner struct { builderRunnerDo builderRunnerDo - ALL field.Asterisk - CreatedAt field.Int64 - UpdatedAt field.Int64 - DeletedAt field.Uint64 - ID field.Int64 - BuilderID field.Int64 - Log field.Bytes - Status field.Field - Tag field.String - RawTag field.String - Description field.String - ScmBranch field.String - StartedAt field.Int64 - EndedAt field.Int64 - Duration field.Int64 - Builder builderRunnerBelongsToBuilder + ALL field.Asterisk + CreatedAt field.Int64 + UpdatedAt field.Int64 + DeletedAt field.Uint64 + ID field.Int64 + BuilderID field.Int64 + Log field.Bytes + Status field.Field + StatusMessage field.String + Tag field.String + RawTag field.String + Description field.String + ScmBranch field.String + StartedAt field.Int64 + EndedAt field.Int64 + Duration field.Int64 + Builder builderRunnerBelongsToBuilder fieldMap map[string]field.Expr } @@ -147,6 +149,7 @@ func (b *builderRunner) updateTableName(table string) *builderRunner { b.BuilderID = field.NewInt64(table, "builder_id") b.Log = field.NewBytes(table, "log") b.Status = field.NewField(table, "status") + b.StatusMessage = field.NewString(table, "status_message") b.Tag = field.NewString(table, "tag") b.RawTag = field.NewString(table, "raw_tag") b.Description = field.NewString(table, "description") @@ -182,7 +185,7 @@ func (b *builderRunner) GetFieldByName(fieldName string) (field.OrderExpr, bool) } func (b *builderRunner) fillFieldMap() { - b.fieldMap = make(map[string]field.Expr, 15) + b.fieldMap = make(map[string]field.Expr, 16) b.fieldMap["created_at"] = b.CreatedAt b.fieldMap["updated_at"] = b.UpdatedAt b.fieldMap["deleted_at"] = b.DeletedAt @@ -190,6 +193,7 @@ func (b *builderRunner) fillFieldMap() { b.fieldMap["builder_id"] = b.BuilderID b.fieldMap["log"] = b.Log b.fieldMap["status"] = b.Status + b.fieldMap["status_message"] = b.StatusMessage b.fieldMap["tag"] = b.Tag b.fieldMap["raw_tag"] = b.RawTag b.fieldMap["description"] = b.Description diff --git a/pkg/handlers/apidocs/docs.go b/pkg/handlers/apidocs/docs.go index d8aba27b..7f7dbceb 100644 --- a/pkg/handlers/apidocs/docs.go +++ b/pkg/handlers/apidocs/docs.go @@ -4353,6 +4353,28 @@ const docTemplate = `{ } } }, + "/systems/config": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "Get config", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.GetSystemConfigResponse" + } + } + } + } + }, "/systems/endpoint": { "get": { "consumes": [ @@ -6704,6 +6726,23 @@ const docTemplate = `{ } } }, + "types.GetSystemConfigDaemon": { + "type": "object", + "properties": { + "builder": { + "type": "boolean", + "example": false + } + } + }, + "types.GetSystemConfigResponse": { + "type": "object", + "properties": { + "daemon": { + "$ref": "#/definitions/types.GetSystemConfigDaemon" + } + } + }, "types.GetSystemEndpointResponse": { "type": "object", "properties": { diff --git a/pkg/handlers/apidocs/swagger.json b/pkg/handlers/apidocs/swagger.json index daa397bd..fec1dae5 100644 --- a/pkg/handlers/apidocs/swagger.json +++ b/pkg/handlers/apidocs/swagger.json @@ -4345,6 +4345,28 @@ } } }, + "/systems/config": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "System" + ], + "summary": "Get config", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.GetSystemConfigResponse" + } + } + } + } + }, "/systems/endpoint": { "get": { "consumes": [ @@ -6696,6 +6718,23 @@ } } }, + "types.GetSystemConfigDaemon": { + "type": "object", + "properties": { + "builder": { + "type": "boolean", + "example": false + } + } + }, + "types.GetSystemConfigResponse": { + "type": "object", + "properties": { + "daemon": { + "$ref": "#/definitions/types.GetSystemConfigDaemon" + } + } + }, "types.GetSystemEndpointResponse": { "type": "object", "properties": { diff --git a/pkg/handlers/apidocs/swagger.yaml b/pkg/handlers/apidocs/swagger.yaml index 585fb90d..8074580e 100644 --- a/pkg/handlers/apidocs/swagger.yaml +++ b/pkg/handlers/apidocs/swagger.yaml @@ -868,6 +868,17 @@ definitions: example: "2006-01-02 15:04:05" type: string type: object + types.GetSystemConfigDaemon: + properties: + builder: + example: false + type: boolean + type: object + types.GetSystemConfigResponse: + properties: + daemon: + $ref: '#/definitions/types.GetSystemConfigDaemon' + type: object types.GetSystemEndpointResponse: properties: endpoint: @@ -4542,6 +4553,20 @@ paths: summary: Redirect oauth2 provider callback tags: - OAuth2 + /systems/config: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/types.GetSystemConfigResponse' + summary: Get config + tags: + - System /systems/endpoint: get: consumes: diff --git a/pkg/handlers/builders/builders_runner_get.go b/pkg/handlers/builders/builders_runner_get.go index 64b29818..ce334bd0 100644 --- a/pkg/handlers/builders/builders_runner_get.go +++ b/pkg/handlers/builders/builders_runner_get.go @@ -83,11 +83,12 @@ func (h *handler) GetRunner(c echo.Context) error { Log: runnerObj.Log, - Status: runnerObj.Status, - Tag: runnerObj.Tag, - RawTag: runnerObj.RawTag, - Description: runnerObj.Description, - ScmBranch: runnerObj.ScmBranch, + Status: runnerObj.Status, + StatusMessage: runnerObj.StatusMessage, + Tag: runnerObj.Tag, + RawTag: runnerObj.RawTag, + Description: runnerObj.Description, + ScmBranch: runnerObj.ScmBranch, StartedAt: runnerObj.StartedAt, EndedAt: runnerObj.EndedAt, diff --git a/pkg/handlers/builders/builders_runners_list.go b/pkg/handlers/builders/builders_runners_list.go index d6e8190c..9e738ea8 100644 --- a/pkg/handlers/builders/builders_runners_list.go +++ b/pkg/handlers/builders/builders_runners_list.go @@ -81,11 +81,12 @@ func (h *handler) ListRunners(c echo.Context) error { Log: runnerObj.Log, - Status: runnerObj.Status, - Tag: runnerObj.Tag, - RawTag: runnerObj.RawTag, - Description: runnerObj.Description, - ScmBranch: runnerObj.ScmBranch, + Status: runnerObj.Status, + StatusMessage: runnerObj.StatusMessage, + Tag: runnerObj.Tag, + RawTag: runnerObj.RawTag, + Description: runnerObj.Description, + ScmBranch: runnerObj.ScmBranch, StartedAt: runnerObj.StartedAt, EndedAt: runnerObj.EndedAt, diff --git a/pkg/handlers/builders/builders_runners_log.go b/pkg/handlers/builders/builders_runners_log.go index 868d0380..09b4312d 100644 --- a/pkg/handlers/builders/builders_runners_log.go +++ b/pkg/handlers/builders/builders_runners_log.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "net/http" + "strings" "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" @@ -73,20 +74,23 @@ func (h *handler) GetRunnerLog(c echo.Context) error { log.Error().Err(err).Msg("Close the ws failed") } }() - if runnerObj.Status == enums.BuildStatusFailed || runnerObj.Status == enums.BuildStatusSuccess { - // already built - reader, err := logger.Driver.Read(ctx, runnerObj.ID) - if err != nil { - log.Error().Err(err).Msg("Read log failed") - return + if runnerObj.Status == enums.BuildStatusFailed || runnerObj.Status == enums.BuildStatusSuccess { // already built + var reader io.Reader + if logger.Driver == nil { + reader = strings.NewReader("") + } else { + reader, err = logger.Driver.Read(ctx, runnerObj.ID) + if err != nil { + log.Error().Err(err).Msg("Read log failed") + return + } } err = h.sendLogWithGzip(ws, reader) if err != nil { log.Error().Err(err).Msg("Send log failed") return } - } else if runnerObj.Status == enums.BuildStatusBuilding { - // still building + } else if runnerObj.Status == enums.BuildStatusBuilding { // still building for { reader, writer := io.Pipe() go func() { diff --git a/pkg/handlers/systems/handler.go b/pkg/handlers/systems/handler.go index 9f2f063e..9ef6ccc3 100644 --- a/pkg/handlers/systems/handler.go +++ b/pkg/handlers/systems/handler.go @@ -33,6 +33,8 @@ type Handler interface { GetEndpoint(c echo.Context) error // GetVersion handles the get version request GetVersion(c echo.Context) error + // GetConfig handles the get config request + GetConfig(c echo.Context) error } var _ Handler = &handler{} @@ -59,6 +61,7 @@ func (f factory) Initialize(e *echo.Echo) error { repositoryHandler := handlerNew() systemGroup.GET("/endpoint", repositoryHandler.GetEndpoint) systemGroup.GET("/version", repositoryHandler.GetVersion) + systemGroup.GET("/config", repositoryHandler.GetConfig) return nil } diff --git a/pkg/handlers/systems/systems_config_get.go b/pkg/handlers/systems/systems_config_get.go new file mode 100644 index 00000000..93126c93 --- /dev/null +++ b/pkg/handlers/systems/systems_config_get.go @@ -0,0 +1,39 @@ +// Copyright 2023 sigma +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package systems + +import ( + "net/http" + + "github.com/labstack/echo/v4" + + "github.com/go-sigma/sigma/pkg/types" +) + +// GetConfig handles the get config request +// +// @Summary Get config +// @Tags System +// @Accept json +// @Produce json +// @Router /systems/config [get] +// @Success 200 {object} types.GetSystemConfigResponse +func (h *handler) GetConfig(c echo.Context) error { + return c.JSON(http.StatusOK, types.GetSystemConfigResponse{ + Daemon: types.GetSystemConfigDaemon{ + Builder: h.config.Daemon.Builder.Enabled, + }, + }) +} diff --git a/pkg/types/builder.go b/pkg/types/builder.go index 1099e1e2..7b8b5dee 100644 --- a/pkg/types/builder.go +++ b/pkg/types/builder.go @@ -170,10 +170,11 @@ type ListBuilderRunnersRequest struct { // BuilderRunnerItem ... type BuilderRunnerItem struct { - ID int64 `json:"id" example:"10"` - BuilderID int64 `json:"builder_id" example:"10"` - Log []byte `json:"log" example:"log"` - Status enums.BuildStatus `json:"status" example:"Success"` + ID int64 `json:"id" example:"10"` + BuilderID int64 `json:"builder_id" example:"10"` + Log []byte `json:"log" example:"log"` + Status enums.BuildStatus `json:"status" example:"Success"` + StatusMessage *string `json:"status_message" example:""` Tag *string `json:"tag" example:"v1.0"` RawTag string `json:"raw_tag" example:"v1.0"` diff --git a/pkg/types/system.go b/pkg/types/system.go index 93786a3d..ca7bde47 100644 --- a/pkg/types/system.go +++ b/pkg/types/system.go @@ -25,3 +25,13 @@ type GetSystemVersionResponse struct { GitHash string `json:"git_hash" example:"4225b69a"` BuildDate string `json:"build_date" example:"2023-10-16T11:25:45Z"` } + +// GetSystemConfigDaemon ... +type GetSystemConfigDaemon struct { + Builder bool `json:"builder" example:"false"` +} + +// GetSystemConfigResponse ... +type GetSystemConfigResponse struct { + Daemon GetSystemConfigDaemon `json:"daemon"` +} diff --git a/web/src/interfaces/index.ts b/web/src/interfaces/index.ts index 7d06f76d..c759c9f1 100644 --- a/web/src/interfaces/index.ts +++ b/web/src/interfaces/index.ts @@ -293,6 +293,7 @@ export interface IBuilderRunnerItem { id: number; builder_id: number; status: string; + status_message?: string; tag?: string; raw_tag: string; description?: string; diff --git a/web/src/pages/Builder/RunnerList.tsx b/web/src/pages/Builder/RunnerList.tsx index bb0932db..131e112b 100644 --- a/web/src/pages/Builder/RunnerList.tsx +++ b/web/src/pages/Builder/RunnerList.tsx @@ -16,6 +16,7 @@ import axios from "axios"; import dayjs from "dayjs"; +import { Tooltip } from 'flowbite'; import { useNavigate } from 'react-router-dom'; import { Fragment, useEffect, useState } from "react"; import { Dialog, Transition } from "@headlessui/react"; @@ -529,7 +530,21 @@ function TableItem({ localServer, namespace, repositoryObj, runnerObj }: { local