Merge commit 'b4a63e0a3~1' into problame/standby-horizon-leases

This commit is contained in:
Christian Schwarz
2025-08-06 18:46:27 +02:00
2 changed files with 220 additions and 313 deletions

View File

@@ -1,13 +0,0 @@
DO $$
DECLARE
query varchar;
BEGIN
FOR query IN
SELECT pg_catalog.format('ALTER FUNCTION %I(%s) OWNER TO {db_owner};', p.oid::regproc, pg_catalog.pg_get_function_identity_arguments(p.oid))
FROM pg_catalog.pg_proc p
WHERE p.pronamespace OPERATOR(pg_catalog.=) 'anon'::regnamespace::oid
LOOP
EXECUTE query;
END LOOP;
END
$$;

View File

@@ -71,8 +71,9 @@ const DEFAULT_PG_VERSION_NUM: &str = "17";
const DEFAULT_PAGESERVER_CONTROL_PLANE_API: &str = "http://127.0.0.1:1234/upcall/v1/"; const DEFAULT_PAGESERVER_CONTROL_PLANE_API: &str = "http://127.0.0.1:1234/upcall/v1/";
/// Neon CLI.
#[derive(clap::Parser)] #[derive(clap::Parser)]
#[command(version = GIT_VERSION, about, name = "Neon CLI")] #[command(version = GIT_VERSION, name = "Neon CLI")]
struct Cli { struct Cli {
#[command(subcommand)] #[command(subcommand)]
command: NeonLocalCmd, command: NeonLocalCmd,
@@ -107,30 +108,31 @@ enum NeonLocalCmd {
Stop(StopCmdArgs), Stop(StopCmdArgs),
} }
/// Initialize a new Neon repository, preparing configs for services to start with.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Initialize a new Neon repository, preparing configs for services to start with")]
struct InitCmdArgs { struct InitCmdArgs {
#[clap(long, help("How many pageservers to create (default 1)"))] /// How many pageservers to create (default 1).
#[clap(long)]
num_pageservers: Option<u16>, num_pageservers: Option<u16>,
#[clap(long)] #[clap(long)]
config: Option<PathBuf>, config: Option<PathBuf>,
#[clap(long, help("Force initialization even if the repository is not empty"))] /// Force initialization even if the repository is not empty.
#[clap(long, default_value = "must-not-exist")]
#[arg(value_parser)] #[arg(value_parser)]
#[clap(default_value = "must-not-exist")]
force: InitForceMode, force: InitForceMode,
} }
/// Start pageserver and safekeepers.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Start pageserver and safekeepers")]
struct StartCmdArgs { struct StartCmdArgs {
#[clap(long = "start-timeout", default_value = "10s")] #[clap(long = "start-timeout", default_value = "10s")]
timeout: humantime::Duration, timeout: humantime::Duration,
} }
/// Stop pageserver and safekeepers.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Stop pageserver and safekeepers")]
struct StopCmdArgs { struct StopCmdArgs {
#[arg(value_enum)] #[arg(value_enum)]
#[clap(long, default_value_t = StopMode::Fast)] #[clap(long, default_value_t = StopMode::Fast)]
@@ -143,8 +145,8 @@ enum StopMode {
Immediate, Immediate,
} }
/// Manage tenants.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage tenants")]
enum TenantCmd { enum TenantCmd {
List, List,
Create(TenantCreateCmdArgs), Create(TenantCreateCmdArgs),
@@ -155,38 +157,36 @@ enum TenantCmd {
#[derive(clap::Args)] #[derive(clap::Args)]
struct TenantCreateCmdArgs { struct TenantCreateCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: Option<TenantId>, tenant_id: Option<TenantId>,
#[clap( /// Use a specific timeline id when creating a tenant and its initial timeline.
long, #[clap(long)]
help = "Use a specific timeline id when creating a tenant and its initial timeline"
)]
timeline_id: Option<TimelineId>, timeline_id: Option<TimelineId>,
#[clap(short = 'c')] #[clap(short = 'c')]
config: Vec<String>, config: Vec<String>,
/// Postgres version to use for the initial timeline.
#[arg(default_value = DEFAULT_PG_VERSION_NUM)] #[arg(default_value = DEFAULT_PG_VERSION_NUM)]
#[clap(long, help = "Postgres version to use for the initial timeline")] #[clap(long)]
pg_version: PgMajorVersion, pg_version: PgMajorVersion,
#[clap( /// Use this tenant in future CLI commands where tenant_id is needed, but not specified.
long, #[clap(long)]
help = "Use this tenant in future CLI commands where tenant_id is needed, but not specified"
)]
set_default: bool, set_default: bool,
#[clap(long, help = "Number of shards in the new tenant")] /// Number of shards in the new tenant.
#[clap(long)]
#[arg(default_value_t = 0)] #[arg(default_value_t = 0)]
shard_count: u8, shard_count: u8,
#[clap(long, help = "Sharding stripe size in pages")] /// Sharding stripe size in pages.
#[clap(long)]
shard_stripe_size: Option<u32>, shard_stripe_size: Option<u32>,
#[clap(long, help = "Placement policy shards in this tenant")] /// Placement policy shards in this tenant.
#[clap(long)]
#[arg(value_parser = parse_placement_policy)] #[arg(value_parser = parse_placement_policy)]
placement_policy: Option<PlacementPolicy>, placement_policy: Option<PlacementPolicy>,
} }
@@ -195,44 +195,35 @@ fn parse_placement_policy(s: &str) -> anyhow::Result<PlacementPolicy> {
Ok(serde_json::from_str::<PlacementPolicy>(s)?) Ok(serde_json::from_str::<PlacementPolicy>(s)?)
} }
/// Set a particular tenant as default in future CLI commands where tenant_id is needed, but not
/// specified.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(
about = "Set a particular tenant as default in future CLI commands where tenant_id is needed, but not specified"
)]
struct TenantSetDefaultCmdArgs { struct TenantSetDefaultCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: TenantId, tenant_id: TenantId,
} }
#[derive(clap::Args)] #[derive(clap::Args)]
struct TenantConfigCmdArgs { struct TenantConfigCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: Option<TenantId>, tenant_id: Option<TenantId>,
#[clap(short = 'c')] #[clap(short = 'c')]
config: Vec<String>, config: Vec<String>,
} }
/// Import a tenant that is present in remote storage, and create branches for its timelines.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(
about = "Import a tenant that is present in remote storage, and create branches for its timelines"
)]
struct TenantImportCmdArgs { struct TenantImportCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: TenantId, tenant_id: TenantId,
} }
/// Manage timelines.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage timelines")]
enum TimelineCmd { enum TimelineCmd {
List(TimelineListCmdArgs), List(TimelineListCmdArgs),
Branch(TimelineBranchCmdArgs), Branch(TimelineBranchCmdArgs),
@@ -240,98 +231,87 @@ enum TimelineCmd {
Import(TimelineImportCmdArgs), Import(TimelineImportCmdArgs),
} }
/// List all timelines available to this pageserver.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "List all timelines available to this pageserver")]
struct TimelineListCmdArgs { struct TimelineListCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_shard_id: Option<TenantShardId>, tenant_shard_id: Option<TenantShardId>,
} }
/// Create a new timeline, branching off from another timeline.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Create a new timeline, branching off from another timeline")]
struct TimelineBranchCmdArgs { struct TimelineBranchCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: Option<TenantId>, tenant_id: Option<TenantId>,
/// New timeline's ID, as a 32-byte hexadecimal string.
#[clap(long, help = "New timeline's ID")] #[clap(long)]
timeline_id: Option<TimelineId>, timeline_id: Option<TimelineId>,
/// Human-readable alias for the new timeline.
#[clap(long, help = "Human-readable alias for the new timeline")] #[clap(long)]
branch_name: String, branch_name: String,
/// Use last Lsn of another timeline (and its data) as base when creating the new timeline. The
#[clap( /// timeline gets resolved by its branch name.
long, #[clap(long)]
help = "Use last Lsn of another timeline (and its data) as base when creating the new timeline. The timeline gets resolved by its branch name."
)]
ancestor_branch_name: Option<String>, ancestor_branch_name: Option<String>,
/// When using another timeline as base, use a specific Lsn in it instead of the latest one.
#[clap( #[clap(long)]
long,
help = "When using another timeline as base, use a specific Lsn in it instead of the latest one"
)]
ancestor_start_lsn: Option<Lsn>, ancestor_start_lsn: Option<Lsn>,
} }
/// Create a new blank timeline.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Create a new blank timeline")]
struct TimelineCreateCmdArgs { struct TimelineCreateCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: Option<TenantId>, tenant_id: Option<TenantId>,
/// New timeline's ID, as a 32-byte hexadecimal string.
#[clap(long, help = "New timeline's ID")] #[clap(long)]
timeline_id: Option<TimelineId>, timeline_id: Option<TimelineId>,
/// Human-readable alias for the new timeline.
#[clap(long, help = "Human-readable alias for the new timeline")] #[clap(long)]
branch_name: String, branch_name: String,
/// Postgres version.
#[arg(default_value = DEFAULT_PG_VERSION_NUM)] #[arg(default_value = DEFAULT_PG_VERSION_NUM)]
#[clap(long, help = "Postgres version")] #[clap(long)]
pg_version: PgMajorVersion, pg_version: PgMajorVersion,
} }
/// Import a timeline from a basebackup directory.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Import timeline from a basebackup directory")]
struct TimelineImportCmdArgs { struct TimelineImportCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: Option<TenantId>, tenant_id: Option<TenantId>,
/// New timeline's ID, as a 32-byte hexadecimal string.
#[clap(long, help = "New timeline's ID")] #[clap(long)]
timeline_id: TimelineId, timeline_id: TimelineId,
/// Human-readable alias for the new timeline.
#[clap(long, help = "Human-readable alias for the new timeline")] #[clap(long)]
branch_name: String, branch_name: String,
/// Basebackup tarfile to import.
#[clap(long, help = "Basebackup tarfile to import")] #[clap(long)]
base_tarfile: PathBuf, base_tarfile: PathBuf,
/// LSN the basebackup starts at.
#[clap(long, help = "Lsn the basebackup starts at")] #[clap(long)]
base_lsn: Lsn, base_lsn: Lsn,
/// WAL to add after base.
#[clap(long, help = "Wal to add after base")] #[clap(long)]
wal_tarfile: Option<PathBuf>, wal_tarfile: Option<PathBuf>,
/// LSN the basebackup ends at.
#[clap(long, help = "Lsn the basebackup ends at")] #[clap(long)]
end_lsn: Option<Lsn>, end_lsn: Option<Lsn>,
/// Postgres version of the basebackup being imported.
#[arg(default_value = DEFAULT_PG_VERSION_NUM)] #[arg(default_value = DEFAULT_PG_VERSION_NUM)]
#[clap(long, help = "Postgres version of the backup being imported")] #[clap(long)]
pg_version: PgMajorVersion, pg_version: PgMajorVersion,
} }
/// Manage pageservers.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage pageservers")]
enum PageserverCmd { enum PageserverCmd {
Status(PageserverStatusCmdArgs), Status(PageserverStatusCmdArgs),
Start(PageserverStartCmdArgs), Start(PageserverStartCmdArgs),
@@ -339,223 +319,202 @@ enum PageserverCmd {
Restart(PageserverRestartCmdArgs), Restart(PageserverRestartCmdArgs),
} }
/// Show status of a local pageserver.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Show status of a local pageserver")]
struct PageserverStatusCmdArgs { struct PageserverStatusCmdArgs {
#[clap(long = "id", help = "pageserver id")] /// Pageserver ID.
#[clap(long = "id")]
pageserver_id: Option<NodeId>, pageserver_id: Option<NodeId>,
} }
/// Start local pageserver.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Start local pageserver")]
struct PageserverStartCmdArgs { struct PageserverStartCmdArgs {
#[clap(long = "id", help = "pageserver id")] /// Pageserver ID.
#[clap(long = "id")]
pageserver_id: Option<NodeId>, pageserver_id: Option<NodeId>,
/// Timeout until we fail the command.
#[clap(short = 't', long, help = "timeout until we fail the command")] #[clap(short = 't', long)]
#[arg(default_value = "10s")] #[arg(default_value = "10s")]
start_timeout: humantime::Duration, start_timeout: humantime::Duration,
} }
/// Stop local pageserver.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Stop local pageserver")]
struct PageserverStopCmdArgs { struct PageserverStopCmdArgs {
#[clap(long = "id", help = "pageserver id")] /// Pageserver ID.
#[clap(long = "id")]
pageserver_id: Option<NodeId>, pageserver_id: Option<NodeId>,
/// If 'immediate', don't flush repository data at shutdown
#[clap( #[clap(short = 'm')]
short = 'm',
help = "If 'immediate', don't flush repository data at shutdown"
)]
#[arg(value_enum, default_value = "fast")] #[arg(value_enum, default_value = "fast")]
stop_mode: StopMode, stop_mode: StopMode,
} }
/// Restart local pageserver.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Restart local pageserver")]
struct PageserverRestartCmdArgs { struct PageserverRestartCmdArgs {
#[clap(long = "id", help = "pageserver id")] /// Pageserver ID.
#[clap(long = "id")]
pageserver_id: Option<NodeId>, pageserver_id: Option<NodeId>,
/// Timeout until we fail the command.
#[clap(short = 't', long, help = "timeout until we fail the command")] #[clap(short = 't', long)]
#[arg(default_value = "10s")] #[arg(default_value = "10s")]
start_timeout: humantime::Duration, start_timeout: humantime::Duration,
} }
/// Manage storage controller.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage storage controller")]
enum StorageControllerCmd { enum StorageControllerCmd {
Start(StorageControllerStartCmdArgs), Start(StorageControllerStartCmdArgs),
Stop(StorageControllerStopCmdArgs), Stop(StorageControllerStopCmdArgs),
} }
/// Start storage controller.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Start storage controller")]
struct StorageControllerStartCmdArgs { struct StorageControllerStartCmdArgs {
#[clap(short = 't', long, help = "timeout until we fail the command")] /// Timeout until we fail the command.
#[clap(short = 't', long)]
#[arg(default_value = "10s")] #[arg(default_value = "10s")]
start_timeout: humantime::Duration, start_timeout: humantime::Duration,
/// Identifier used to distinguish storage controller instances.
#[clap( #[clap(long)]
long,
help = "Identifier used to distinguish storage controller instances"
)]
#[arg(default_value_t = 1)] #[arg(default_value_t = 1)]
instance_id: u8, instance_id: u8,
/// Base port for the storage controller instance identified by instance-id (defaults to
#[clap( /// pageserver cplane api).
long, #[clap(long)]
help = "Base port for the storage controller instance idenfified by instance-id (defaults to pageserver cplane api)"
)]
base_port: Option<u16>, base_port: Option<u16>,
#[clap( /// Whether the storage controller should handle pageserver-reported local disk loss events.
long, #[clap(long)]
help = "Whether the storage controller should handle pageserver-reported local disk loss events."
)]
handle_ps_local_disk_loss: Option<bool>, handle_ps_local_disk_loss: Option<bool>,
} }
/// Stop storage controller.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Stop storage controller")]
struct StorageControllerStopCmdArgs { struct StorageControllerStopCmdArgs {
#[clap( /// If 'immediate', don't flush repository data at shutdown
short = 'm', #[clap(short = 'm')]
help = "If 'immediate', don't flush repository data at shutdown"
)]
#[arg(value_enum, default_value = "fast")] #[arg(value_enum, default_value = "fast")]
stop_mode: StopMode, stop_mode: StopMode,
/// Identifier used to distinguish storage controller instances.
#[clap( #[clap(long)]
long,
help = "Identifier used to distinguish storage controller instances"
)]
#[arg(default_value_t = 1)] #[arg(default_value_t = 1)]
instance_id: u8, instance_id: u8,
} }
/// Manage storage broker.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage storage broker")]
enum StorageBrokerCmd { enum StorageBrokerCmd {
Start(StorageBrokerStartCmdArgs), Start(StorageBrokerStartCmdArgs),
Stop(StorageBrokerStopCmdArgs), Stop(StorageBrokerStopCmdArgs),
} }
/// Start broker.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Start broker")]
struct StorageBrokerStartCmdArgs { struct StorageBrokerStartCmdArgs {
#[clap(short = 't', long, help = "timeout until we fail the command")] /// Timeout until we fail the command.
#[arg(default_value = "10s")] #[clap(short = 't', long, default_value = "10s")]
start_timeout: humantime::Duration, start_timeout: humantime::Duration,
} }
/// Stop broker.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "stop broker")]
struct StorageBrokerStopCmdArgs { struct StorageBrokerStopCmdArgs {
#[clap( /// If 'immediate', don't flush repository data on shutdown.
short = 'm', #[clap(short = 'm')]
help = "If 'immediate', don't flush repository data at shutdown"
)]
#[arg(value_enum, default_value = "fast")] #[arg(value_enum, default_value = "fast")]
stop_mode: StopMode, stop_mode: StopMode,
} }
/// Manage safekeepers.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage safekeepers")]
enum SafekeeperCmd { enum SafekeeperCmd {
Start(SafekeeperStartCmdArgs), Start(SafekeeperStartCmdArgs),
Stop(SafekeeperStopCmdArgs), Stop(SafekeeperStopCmdArgs),
Restart(SafekeeperRestartCmdArgs), Restart(SafekeeperRestartCmdArgs),
} }
/// Manage object storage.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage object storage")]
enum EndpointStorageCmd { enum EndpointStorageCmd {
Start(EndpointStorageStartCmd), Start(EndpointStorageStartCmd),
Stop(EndpointStorageStopCmd), Stop(EndpointStorageStopCmd),
} }
/// Start object storage.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Start object storage")]
struct EndpointStorageStartCmd { struct EndpointStorageStartCmd {
#[clap(short = 't', long, help = "timeout until we fail the command")] /// Timeout until we fail the command.
#[clap(short = 't', long)]
#[arg(default_value = "10s")] #[arg(default_value = "10s")]
start_timeout: humantime::Duration, start_timeout: humantime::Duration,
} }
/// Stop object storage.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Stop object storage")]
struct EndpointStorageStopCmd { struct EndpointStorageStopCmd {
/// If 'immediate', don't flush repository data on shutdown.
#[clap(short = 'm')]
#[arg(value_enum, default_value = "fast")] #[arg(value_enum, default_value = "fast")]
#[clap(
short = 'm',
help = "If 'immediate', don't flush repository data at shutdown"
)]
stop_mode: StopMode, stop_mode: StopMode,
} }
/// Start local safekeeper.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Start local safekeeper")]
struct SafekeeperStartCmdArgs { struct SafekeeperStartCmdArgs {
#[clap(help = "safekeeper id")] /// Safekeeper ID.
#[arg(default_value_t = NodeId(1))] #[arg(default_value_t = NodeId(1))]
id: NodeId, id: NodeId,
#[clap( /// Additional safekeeper invocation options, e.g. -e=--http-auth-public-key-path=foo.
short = 'e', #[clap(short = 'e', long = "safekeeper-extra-opt")]
long = "safekeeper-extra-opt",
help = "Additional safekeeper invocation options, e.g. -e=--http-auth-public-key-path=foo"
)]
extra_opt: Vec<String>, extra_opt: Vec<String>,
#[clap(short = 't', long, help = "timeout until we fail the command")] /// Timeout until we fail the command.
#[clap(short = 't', long)]
#[arg(default_value = "10s")] #[arg(default_value = "10s")]
start_timeout: humantime::Duration, start_timeout: humantime::Duration,
} }
/// Stop local safekeeper.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Stop local safekeeper")]
struct SafekeeperStopCmdArgs { struct SafekeeperStopCmdArgs {
#[clap(help = "safekeeper id")] /// Safekeeper ID.
#[arg(default_value_t = NodeId(1))] #[arg(default_value_t = NodeId(1))]
id: NodeId, id: NodeId,
/// If 'immediate', don't flush repository data on shutdown.
#[arg(value_enum, default_value = "fast")] #[arg(value_enum, default_value = "fast")]
#[clap( #[clap(short = 'm')]
short = 'm',
help = "If 'immediate', don't flush repository data at shutdown"
)]
stop_mode: StopMode, stop_mode: StopMode,
} }
/// Restart local safekeeper.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Restart local safekeeper")]
struct SafekeeperRestartCmdArgs { struct SafekeeperRestartCmdArgs {
#[clap(help = "safekeeper id")] /// Safekeeper ID.
#[arg(default_value_t = NodeId(1))] #[arg(default_value_t = NodeId(1))]
id: NodeId, id: NodeId,
/// If 'immediate', don't flush repository data on shutdown.
#[arg(value_enum, default_value = "fast")] #[arg(value_enum, default_value = "fast")]
#[clap( #[clap(short = 'm')]
short = 'm',
help = "If 'immediate', don't flush repository data at shutdown"
)]
stop_mode: StopMode, stop_mode: StopMode,
#[clap( /// Additional safekeeper invocation options, e.g. -e=--http-auth-public-key-path=foo.
short = 'e', #[clap(short = 'e', long = "safekeeper-extra-opt")]
long = "safekeeper-extra-opt",
help = "Additional safekeeper invocation options, e.g. -e=--http-auth-public-key-path=foo"
)]
extra_opt: Vec<String>, extra_opt: Vec<String>,
#[clap(short = 't', long, help = "timeout until we fail the command")] /// Timeout until we fail the command.
#[clap(short = 't', long)]
#[arg(default_value = "10s")] #[arg(default_value = "10s")]
start_timeout: humantime::Duration, start_timeout: humantime::Duration,
} }
/// Manage Postgres instances.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage Postgres instances")]
enum EndpointCmd { enum EndpointCmd {
List(EndpointListCmdArgs), List(EndpointListCmdArgs),
Create(EndpointCreateCmdArgs), Create(EndpointCreateCmdArgs),
@@ -567,33 +526,27 @@ enum EndpointCmd {
GenerateJwt(EndpointGenerateJwtCmdArgs), GenerateJwt(EndpointGenerateJwtCmdArgs),
} }
/// List endpoints.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "List endpoints")]
struct EndpointListCmdArgs { struct EndpointListCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_shard_id: Option<TenantShardId>, tenant_shard_id: Option<TenantShardId>,
} }
/// Create a compute endpoint.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Create a compute endpoint")]
struct EndpointCreateCmdArgs { struct EndpointCreateCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: Option<TenantId>, tenant_id: Option<TenantId>,
/// Postgres endpoint ID.
#[clap(help = "Postgres endpoint id")]
endpoint_id: Option<String>, endpoint_id: Option<String>,
#[clap(long, help = "Name of the branch the endpoint will run on")] /// Name of the branch the endpoint will run on.
#[clap(long)]
branch_name: Option<String>, branch_name: Option<String>,
#[clap( /// Specify LSN on the timeline to start from. By default, end of the timeline would be used.
long, #[clap(long)]
help = "Specify Lsn on the timeline to start from. By default, end of the timeline would be used"
)]
lsn: Option<Lsn>, lsn: Option<Lsn>,
#[clap(long)] #[clap(long)]
pg_port: Option<u16>, pg_port: Option<u16>,
@@ -607,16 +560,13 @@ struct EndpointCreateCmdArgs {
#[clap(long, value_parser = parse_compute_features)] #[clap(long, value_parser = parse_compute_features)]
features: Option<ComputeFeatures>, features: Option<ComputeFeatures>,
#[clap( /// Don't do basebackup, create endpoint directory with only config files.
long, #[clap(long, action = clap::ArgAction::Set, default_value_t = false)]
help = "Don't do basebackup, create endpoint directory with only config files",
action = clap::ArgAction::Set,
default_value_t = false
)]
config_only: bool, config_only: bool,
/// Postgres version.
#[arg(default_value = DEFAULT_PG_VERSION_NUM)] #[arg(default_value = DEFAULT_PG_VERSION_NUM)]
#[clap(long, help = "Postgres version")] #[clap(long)]
pg_version: PgMajorVersion, pg_version: PgMajorVersion,
/// Use gRPC to communicate with Pageservers, by generating grpc:// connstrings. /// Use gRPC to communicate with Pageservers, by generating grpc:// connstrings.
@@ -627,25 +577,20 @@ struct EndpointCreateCmdArgs {
#[clap(long)] #[clap(long)]
grpc: bool, grpc: bool,
#[clap( /// If set, the node will be a hot replica on the specified timeline.
long, #[clap(long, action = clap::ArgAction::Set, default_value_t = false)]
help = "If set, the node will be a hot replica on the specified timeline",
action = clap::ArgAction::Set,
default_value_t = false
)]
hot_standby: bool, hot_standby: bool,
/// If set, will set up the catalog for neon_superuser.
#[clap(long, help = "If set, will set up the catalog for neon_superuser")] #[clap(long)]
update_catalog: bool, update_catalog: bool,
/// Allow multiple primary endpoints running on the same branch. Shouldn't be used normally, but
#[clap( /// useful for tests.
long, #[clap(long)]
help = "Allow multiple primary endpoints running on the same branch. Shouldn't be used normally, but useful for tests."
)]
allow_multiple: bool, allow_multiple: bool,
/// Only allow changing it on creation /// Name of the privileged role for the endpoint.
#[clap(long, help = "Name of the privileged role for the endpoint")] // Only allow changing it on creation.
#[clap(long)]
privileged_role_name: Option<String>, privileged_role_name: Option<String>,
} }
@@ -656,148 +601,123 @@ fn parse_compute_features(s: &str) -> anyhow::Result<ComputeFeatures> {
serde_json::from_str(s).context("parse compute features arg as JSON array") serde_json::from_str(s).context("parse compute features arg as JSON array")
} }
/// Start Postgres. If the endpoint doesn't exist yet, it is created.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Start postgres. If the endpoint doesn't exist yet, it is created.")]
struct EndpointStartCmdArgs { struct EndpointStartCmdArgs {
#[clap(help = "Postgres endpoint id")] /// Postgres endpoint ID.
endpoint_id: String, endpoint_id: String,
/// Pageserver ID.
#[clap(long = "pageserver-id")] #[clap(long = "pageserver-id")]
endpoint_pageserver_id: Option<NodeId>, endpoint_pageserver_id: Option<NodeId>,
/// Safekeepers membership generation to prefix neon.safekeepers with.
#[clap( #[clap(long)]
long,
help = "Safekeepers membership generation to prefix neon.safekeepers with. Normally neon_local sets it on its own, but this option allows to override. Non zero value forces endpoint to use membership configurations."
)]
safekeepers_generation: Option<u32>, safekeepers_generation: Option<u32>,
#[clap( /// List of safekeepers endpoint will talk to.
long, #[clap(long)]
help = "List of safekeepers endpoint will talk to. Normally neon_local chooses them on its own, but this option allows to override."
)]
safekeepers: Option<String>, safekeepers: Option<String>,
/// Configure the remote extensions storage proxy gateway URL to request for extensions.
#[clap( #[clap(long, alias = "remote-ext-config")]
long,
help = "Configure the remote extensions storage proxy gateway URL to request for extensions.",
alias = "remote-ext-config"
)]
remote_ext_base_url: Option<String>, remote_ext_base_url: Option<String>,
/// If set, will create test user `user` and `neondb` database. Requires `update-catalog = true`
#[clap( #[clap(long)]
long,
help = "If set, will create test user `user` and `neondb` database. Requires `update-catalog = true`"
)]
create_test_user: bool, create_test_user: bool,
/// Allow multiple primary endpoints running on the same branch. Shouldn't be used normally, but
#[clap( /// useful for tests.
long, #[clap(long)]
help = "Allow multiple primary endpoints running on the same branch. Shouldn't be used normally, but useful for tests."
)]
allow_multiple: bool, allow_multiple: bool,
/// Timeout until we fail the command.
#[clap(short = 't', long, value_parser= humantime::parse_duration, help = "timeout until we fail the command")] #[clap(short = 't', long, value_parser= humantime::parse_duration)]
#[arg(default_value = "90s")] #[arg(default_value = "90s")]
start_timeout: Duration, start_timeout: Duration,
#[clap( /// Download LFC cache from endpoint storage on endpoint startup
long, #[clap(long, default_value = "false")]
help = "Download LFC cache from endpoint storage on endpoint startup",
default_value = "false"
)]
autoprewarm: bool, autoprewarm: bool,
#[clap(long, help = "Upload LFC cache to endpoint storage periodically")] /// Upload LFC cache to endpoint storage periodically
#[clap(long)]
offload_lfc_interval_seconds: Option<std::num::NonZeroU64>, offload_lfc_interval_seconds: Option<std::num::NonZeroU64>,
#[clap( /// Run in development mode, skipping VM-specific operations like process termination
long, #[clap(long, action = clap::ArgAction::SetTrue)]
help = "Run in development mode, skipping VM-specific operations like process termination",
action = clap::ArgAction::SetTrue
)]
dev: bool, dev: bool,
} }
/// Reconfigure an endpoint.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Reconfigure an endpoint")]
struct EndpointReconfigureCmdArgs { struct EndpointReconfigureCmdArgs {
#[clap( /// Tenant id. Represented as a hexadecimal string 32 symbols length
long = "tenant-id", #[clap(long = "tenant-id")]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: Option<TenantId>, tenant_id: Option<TenantId>,
/// Postgres endpoint ID.
#[clap(help = "Postgres endpoint id")]
endpoint_id: String, endpoint_id: String,
/// Pageserver ID.
#[clap(long = "pageserver-id")] #[clap(long = "pageserver-id")]
endpoint_pageserver_id: Option<NodeId>, endpoint_pageserver_id: Option<NodeId>,
#[clap(long)] #[clap(long)]
safekeepers: Option<String>, safekeepers: Option<String>,
} }
/// Refresh the endpoint's configuration by forcing it reload it's spec
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Refresh the endpoint's configuration by forcing it reload it's spec")]
struct EndpointRefreshConfigurationArgs { struct EndpointRefreshConfigurationArgs {
#[clap(help = "Postgres endpoint id")] /// Postgres endpoint id
endpoint_id: String, endpoint_id: String,
} }
/// Stop an endpoint.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Stop an endpoint")]
struct EndpointStopCmdArgs { struct EndpointStopCmdArgs {
#[clap(help = "Postgres endpoint id")] /// Postgres endpoint ID.
endpoint_id: String, endpoint_id: String,
/// Also delete data directory (now optional, should be default in future).
#[clap( #[clap(long)]
long,
help = "Also delete data directory (now optional, should be default in future)"
)]
destroy: bool, destroy: bool,
#[clap(long, help = "Postgres shutdown mode")] /// Postgres shutdown mode, passed to `pg_ctl -m <mode>`.
#[clap(long)]
#[clap(default_value = "fast")] #[clap(default_value = "fast")]
mode: EndpointTerminateMode, mode: EndpointTerminateMode,
} }
/// Update the pageservers in the spec file of the compute endpoint
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Update the pageservers in the spec file of the compute endpoint")]
struct EndpointUpdatePageserversCmdArgs { struct EndpointUpdatePageserversCmdArgs {
#[clap(help = "Postgres endpoint id")] /// Postgres endpoint id
endpoint_id: String, endpoint_id: String,
#[clap(short = 'p', long, help = "Specified pageserver id")] /// Specified pageserver id
#[clap(short = 'p', long)]
pageserver_id: Option<NodeId>, pageserver_id: Option<NodeId>,
} }
/// Generate a JWT for an endpoint.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Generate a JWT for an endpoint")]
struct EndpointGenerateJwtCmdArgs { struct EndpointGenerateJwtCmdArgs {
#[clap(help = "Postgres endpoint id")] /// Postgres endpoint ID.
endpoint_id: String, endpoint_id: String,
/// Scope to generate the JWT with.
#[clap(short = 's', long, help = "Scope to generate the JWT with", value_parser = ComputeClaimsScope::from_str)] #[clap(short = 's', long, value_parser = ComputeClaimsScope::from_str)]
scope: Option<ComputeClaimsScope>, scope: Option<ComputeClaimsScope>,
} }
/// Manage neon_local branch name mappings.
#[derive(clap::Subcommand)] #[derive(clap::Subcommand)]
#[clap(about = "Manage neon_local branch name mappings")]
enum MappingsCmd { enum MappingsCmd {
Map(MappingsMapCmdArgs), Map(MappingsMapCmdArgs),
} }
/// Create new mapping which cannot exist already.
#[derive(clap::Args)] #[derive(clap::Args)]
#[clap(about = "Create new mapping which cannot exist already")]
struct MappingsMapCmdArgs { struct MappingsMapCmdArgs {
#[clap( /// Tenant ID, as a 32-byte hexadecimal string.
long, #[clap(long)]
help = "Tenant id. Represented as a hexadecimal string 32 symbols length"
)]
tenant_id: TenantId, tenant_id: TenantId,
#[clap( /// Timeline ID, as a 32-byte hexadecimal string.
long, #[clap(long)]
help = "Timeline id. Represented as a hexadecimal string 32 symbols length"
)]
timeline_id: TimelineId, timeline_id: TimelineId,
#[clap(long, help = "Branch name to give to the timeline")] /// Branch name to give to the timeline.
#[clap(long)]
branch_name: String, branch_name: String,
} }