feat: enforce shared application resource quotas across namespaces
This commit is contained in:
@@ -14,17 +14,19 @@ import (
|
||||
|
||||
// BootstrapInput 仅供持有数据库管理权限的初始化命令使用,不暴露为公共 HTTP。
|
||||
type BootstrapInput struct {
|
||||
Application string // Application 应用名称,每次初始化创建独立应用。
|
||||
Namespace string // Namespace 命名空间名称。
|
||||
Environment string // Environment 环境名称,必须显式指定。
|
||||
Queue string // Queue 初始队列名称。
|
||||
WorkerAccess bool // WorkerAccess 显式增加开发用 Worker 接入及消费权限。
|
||||
ScheduleAccess bool // ScheduleAccess 显式增加开发用计划查询及管理权限。
|
||||
Application string // Application 新应用名称,与 ExistingApplicationID 二选一。
|
||||
Namespace string // Namespace 命名空间名称。
|
||||
Environment string // Environment 环境名称,必须显式指定。
|
||||
Queue string // Queue 初始队列名称。
|
||||
WorkerAccess bool // WorkerAccess 显式增加开发用 Worker 接入及消费权限。
|
||||
ScheduleAccess bool // ScheduleAccess 显式增加开发用计划查询及管理权限。
|
||||
ResourceAccess bool // ResourceAccess 显式授予整个应用的额度查询及管理权限。
|
||||
ExistingApplicationID core.ID // ExistingApplicationID 在指定现有应用下创建命名空间,与 Application 二选一。
|
||||
}
|
||||
|
||||
// BootstrapResult 包含一次性交付的原始凭据,禁止记录到日志或提交到版本库。
|
||||
type BootstrapResult struct {
|
||||
ApplicationID core.ID `json:"application_id"` // ApplicationID 新建应用标识。
|
||||
ApplicationID core.ID `json:"application_id"` // ApplicationID 创建或关联的应用标识。
|
||||
NamespaceID core.ID `json:"namespace_id"` // NamespaceID 新建隔离范围。
|
||||
QueueID core.ID `json:"queue_id"` // QueueID 初始队列标识。
|
||||
CredentialID core.ID `json:"credential_id"` // CredentialID 凭据记录标识。
|
||||
@@ -33,11 +35,14 @@ type BootstrapResult struct {
|
||||
}
|
||||
|
||||
func Bootstrap(ctx context.Context, db *sql.DB, in BootstrapInput, deliver func(BootstrapResult) error) error {
|
||||
for _, v := range []string{in.Application, in.Namespace, in.Queue} {
|
||||
for _, v := range []string{in.Namespace, in.Queue} {
|
||||
if !service.ValidName(v, 128) {
|
||||
return fmt.Errorf("invalid bootstrap resource name")
|
||||
}
|
||||
}
|
||||
if (in.ExistingApplicationID == "" && !service.ValidName(in.Application, 128)) || (in.ExistingApplicationID != "" && (in.ExistingApplicationID.Validate() != nil || in.Application != "")) {
|
||||
return fmt.Errorf("exactly one new application name or existing application id is required")
|
||||
}
|
||||
if !service.ValidName(in.Environment, 32) || deliver == nil {
|
||||
return fmt.Errorf("environment and secure delivery are required")
|
||||
}
|
||||
@@ -45,7 +50,15 @@ func Bootstrap(ctx context.Context, db *sql.DB, in BootstrapInput, deliver func(
|
||||
return err
|
||||
}
|
||||
var out BootstrapResult
|
||||
for _, dest := range []*core.ID{&out.ApplicationID, &out.NamespaceID, &out.QueueID, &out.CredentialID} {
|
||||
var err error
|
||||
out.ApplicationID = in.ExistingApplicationID
|
||||
if out.ApplicationID == "" {
|
||||
out.ApplicationID, err = core.NewID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, dest := range []*core.ID{&out.NamespaceID, &out.QueueID, &out.CredentialID} {
|
||||
v, err := core.NewID()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -64,6 +77,9 @@ func Bootstrap(ctx context.Context, db *sql.DB, in BootstrapInput, deliver func(
|
||||
if in.ScheduleAccess {
|
||||
permissions.Actions = append(permissions.Actions, "schedules:read", "schedules:manage")
|
||||
}
|
||||
if in.ResourceAccess {
|
||||
permissions.Actions = append(permissions.Actions, "application:resources:read", "application:resources:manage")
|
||||
}
|
||||
raw, err := json.Marshal(permissions)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -74,8 +90,18 @@ func Bootstrap(ctx context.Context, db *sql.DB, in BootstrapInput, deliver func(
|
||||
return err
|
||||
}
|
||||
out.ExpiresAt = now.Add(30 * 24 * time.Hour)
|
||||
if _, err = tx.ExecContext(ctx, "INSERT INTO orbit_applications(id,name) VALUES (?,?)", out.ApplicationID, in.Application); err != nil {
|
||||
return err
|
||||
if in.ExistingApplicationID == "" {
|
||||
if _, err = tx.ExecContext(ctx, "INSERT INTO orbit_applications(id,name) VALUES (?,?)", out.ApplicationID, in.Application); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tx.ExecContext(ctx, "INSERT INTO orbit_application_resources(application_id) VALUES (?)", out.ApplicationID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var id core.ID
|
||||
if err := tx.QueryRowContext(ctx, `SELECT a.id FROM orbit_applications a JOIN orbit_application_resources r ON r.application_id=a.id WHERE a.id=? AND a.enabled=TRUE FOR SHARE`, out.ApplicationID).Scan(&id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err = tx.ExecContext(ctx, "INSERT INTO orbit_namespaces(id,application_id,name,environment) VALUES (?,?,?,?)", out.NamespaceID, out.ApplicationID, in.Namespace, in.Environment); err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user