mirror of
https://github.com/quay/quay.git
synced 2026-10-23 09:49:06 +03:00
OMR 2.0.x users run `mirror-registry install|upgrade|uninstall`; OMR 3.0 shipped the same CLI as `quay`, which also collides with the Quay product name. Rename the binary and its entrypoint so the command name is consistent across versions. - Makefile: GO_BINARY_NAME and GO_CMD_DIR; cmd/quay moves to cmd/mirror-registry. - Dockerfile.mirror: build output and ENTRYPOINT. - cmd package: BinaryName constant used for the root command and the version output; user-facing error hints that named `quay init` / `quay install` / `quay db upgrade` (now `mirror-registry install`, the command that upgrades an existing schema). - ci-go.yaml: artifact name and every bin/quay invocation. Unchanged: systemd unit quay.service, Quadlet quay.container, default data directory /var/lib/quay, image quay.io/quay/quay-mirror, and the Go module path.
79 lines
2.4 KiB
Go
79 lines
2.4 KiB
Go
// Package bootstrap provides first-run initialization for the registry.
|
|
package bootstrap
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"github.com/google/uuid"
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"github.com/quay/quay/internal/dal/daldb"
|
|
)
|
|
|
|
// HasUsers reports whether the database already contains a login user.
|
|
func HasUsers(ctx context.Context, db *sql.DB) (bool, error) {
|
|
q := daldb.New(db)
|
|
count, err := q.CountUsers(ctx)
|
|
if err != nil {
|
|
return false, fmt.Errorf("count users: %w", err)
|
|
}
|
|
return count > 0, nil
|
|
}
|
|
|
|
// RequireAdminUser verifies that install-time provisioning has created a user
|
|
// and returns the first login username for standalone default authorization.
|
|
func RequireAdminUser(ctx context.Context, db *sql.DB) (string, error) {
|
|
var username string
|
|
err := db.QueryRowContext(ctx, `
|
|
SELECT username FROM "user"
|
|
WHERE organization = 0 AND robot = 0
|
|
ORDER BY id LIMIT 1
|
|
`).Scan(&username)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return "", fmt.Errorf("database has no users; run `mirror-registry init` or `mirror-registry install` to provision the initial administrator")
|
|
}
|
|
return "", fmt.Errorf("find initial administrator: %w", err)
|
|
}
|
|
return username, nil
|
|
}
|
|
|
|
// AdminUser creates the initial admin user if no users exist in the database.
|
|
// It returns true if a user was created, false if users already exist. Password
|
|
// generation and persistence belong to the one-shot installer, not the server.
|
|
func AdminUser(ctx context.Context, db *sql.DB, username, password string) (bool, error) {
|
|
q := daldb.New(db)
|
|
|
|
hasUsers, err := HasUsers(ctx, db)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if hasUsers {
|
|
return false, nil
|
|
}
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return false, errors.New("hash initial administrator password")
|
|
}
|
|
|
|
email := fmt.Sprintf("%s@localhost", username)
|
|
if _, err := q.CreateAdminUser(ctx, daldb.CreateAdminUserParams{
|
|
Uuid: sql.NullString{String: uuid.NewString(), Valid: true},
|
|
Username: username,
|
|
PasswordHash: sql.NullString{String: string(hash), Valid: true},
|
|
Email: email,
|
|
}); err != nil {
|
|
// Driver errors can include bound values, so do not propagate the
|
|
// password-derived hash to command-level logs.
|
|
return false, errors.New("create initial administrator")
|
|
}
|
|
|
|
slog.Info("admin user created", "username", username)
|
|
return true, nil
|
|
}
|