160 lines
3.5 KiB
Go
160 lines
3.5 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
const (
|
|
materializationNotStarted = "not_started"
|
|
materializationPending = "pending"
|
|
materializationRunning = "running"
|
|
materializationSucceeded = "succeeded"
|
|
materializationFailed = "failed"
|
|
)
|
|
|
|
type bootstrapStructurePrerequisites struct {
|
|
installation InstallationRecord
|
|
admin AdminSummary
|
|
}
|
|
|
|
func (service *Service) GetInstallation(ctx context.Context) (*InstallationRecord, error) {
|
|
record, err := scanInstallationRecord(service.db.Pool.QueryRow(ctx, `
|
|
SELECT
|
|
id::text,
|
|
name,
|
|
mode::text,
|
|
access::text,
|
|
protocol::text,
|
|
host,
|
|
is_bootstrapped,
|
|
materialization_status::text,
|
|
materialization_error
|
|
FROM installations
|
|
WHERE singleton = TRUE
|
|
LIMIT 1;
|
|
`))
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return &record, nil
|
|
}
|
|
|
|
func loadInstallation(ctx context.Context, tx pgx.Tx) (InstallationRecord, error) {
|
|
return scanInstallationRecord(tx.QueryRow(ctx, `
|
|
SELECT
|
|
id::text,
|
|
name,
|
|
mode::text,
|
|
access::text,
|
|
protocol::text,
|
|
host,
|
|
is_bootstrapped,
|
|
materialization_status::text,
|
|
materialization_error
|
|
FROM installations
|
|
WHERE singleton = TRUE
|
|
LIMIT 1;
|
|
`))
|
|
}
|
|
|
|
func updateBootstrappedInstallation(ctx context.Context, tx pgx.Tx) (InstallationRecord, error) {
|
|
return scanInstallationRecord(tx.QueryRow(ctx, `
|
|
UPDATE installations
|
|
SET
|
|
is_bootstrapped = TRUE,
|
|
bootstrapped_at = COALESCE(bootstrapped_at, NOW()),
|
|
materialization_status = 'pending'::bootstrap_materialization_status,
|
|
materialization_error = NULL,
|
|
materialization_requested_at = NOW(),
|
|
materialization_started_at = NULL,
|
|
materialization_finished_at = NULL,
|
|
updated_at = NOW()
|
|
WHERE singleton = TRUE
|
|
RETURNING
|
|
id::text,
|
|
name,
|
|
mode::text,
|
|
access::text,
|
|
protocol::text,
|
|
host,
|
|
is_bootstrapped,
|
|
materialization_status::text,
|
|
materialization_error;
|
|
`))
|
|
}
|
|
|
|
func scanInstallationRecord(row pgx.Row) (InstallationRecord, error) {
|
|
var record InstallationRecord
|
|
if err := row.Scan(
|
|
&record.ID,
|
|
&record.Name,
|
|
&record.Mode,
|
|
&record.Access,
|
|
&record.Protocol,
|
|
&record.Host,
|
|
&record.IsBootstrapped,
|
|
&record.MaterializationStatus,
|
|
&record.MaterializationError,
|
|
); err != nil {
|
|
return InstallationRecord{}, err
|
|
}
|
|
|
|
if strings.TrimSpace(record.MaterializationStatus) == "" {
|
|
record.MaterializationStatus = materializationNotStarted
|
|
}
|
|
|
|
return record, nil
|
|
}
|
|
|
|
func loadPrimaryAdmin(ctx context.Context, tx pgx.Tx) (AdminSummary, error) {
|
|
var admin AdminSummary
|
|
if err := tx.QueryRow(ctx, `
|
|
SELECT id::text, email, display_name
|
|
FROM users
|
|
WHERE is_instance_admin = TRUE
|
|
ORDER BY created_at ASC
|
|
LIMIT 1;
|
|
`).Scan(&admin.ID, &admin.Email, &admin.DisplayName); err != nil {
|
|
return AdminSummary{}, err
|
|
}
|
|
|
|
return admin, nil
|
|
}
|
|
|
|
func (service *Service) loadBootstrapStructurePrerequisites(
|
|
ctx context.Context,
|
|
tx pgx.Tx,
|
|
) (bootstrapStructurePrerequisites, error) {
|
|
installation, err := loadInstallation(ctx, tx)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return bootstrapStructurePrerequisites{}, ErrInstallationNotConfigured
|
|
}
|
|
|
|
return bootstrapStructurePrerequisites{}, err
|
|
}
|
|
|
|
admin, err := loadPrimaryAdmin(ctx, tx)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return bootstrapStructurePrerequisites{}, ErrAdminNotConfigured
|
|
}
|
|
|
|
return bootstrapStructurePrerequisites{}, err
|
|
}
|
|
|
|
return bootstrapStructurePrerequisites{
|
|
installation: installation,
|
|
admin: admin,
|
|
}, nil
|
|
}
|