- Replace form_urlencoded with RFC 3986 compliant URI encoding - Implement aws_uri_encode() matching AWS SigV4 spec exactly - Unreserved chars (A-Z,a-z,0-9,-,_,.,~) not encoded - All other chars percent-encoded with uppercase hex - Preserve slashes in paths, encode in query params - Normalize empty paths to '/' per AWS spec - Fix test expectations (body hash, HMAC values) - Add comprehensive SigV4 signature determinism test This fixes the canonicalization mismatch that caused signature validation failures in T047. Auth can now be enabled for production. Refs: T058.S1
277 lines
7.4 KiB
Protocol Buffer
277 lines
7.4 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package creditservice.v1;
|
|
|
|
option go_package = "github.com/cloud/creditservice/proto/creditservice/v1;creditservicev1";
|
|
|
|
import "google/protobuf/timestamp.proto";
|
|
|
|
// ============================================================================
|
|
// CreditService - Credit/Quota Management
|
|
// ============================================================================
|
|
|
|
service CreditService {
|
|
// Wallet operations
|
|
rpc GetWallet(GetWalletRequest) returns (GetWalletResponse);
|
|
rpc CreateWallet(CreateWalletRequest) returns (CreateWalletResponse);
|
|
rpc TopUp(TopUpRequest) returns (TopUpResponse);
|
|
rpc GetTransactions(GetTransactionsRequest) returns (GetTransactionsResponse);
|
|
|
|
// Admission Control (called by resource services before creation)
|
|
rpc CheckQuota(CheckQuotaRequest) returns (CheckQuotaResponse);
|
|
rpc ReserveCredits(ReserveCreditsRequest) returns (ReserveCreditsResponse);
|
|
rpc CommitReservation(CommitReservationRequest) returns (CommitReservationResponse);
|
|
rpc ReleaseReservation(ReleaseReservationRequest) returns (ReleaseReservationResponse);
|
|
|
|
// Billing (internal, called by billing batch)
|
|
rpc ProcessBilling(ProcessBillingRequest) returns (ProcessBillingResponse);
|
|
|
|
// Quota management
|
|
rpc SetQuota(SetQuotaRequest) returns (SetQuotaResponse);
|
|
rpc GetQuota(GetQuotaRequest) returns (GetQuotaResponse);
|
|
rpc ListQuotas(ListQuotasRequest) returns (ListQuotasResponse);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Core Types
|
|
// ============================================================================
|
|
|
|
// Wallet represents a project's credit account
|
|
message Wallet {
|
|
string project_id = 1;
|
|
string org_id = 2;
|
|
// Balance in smallest credit unit (e.g., 100 = 1.00 credits)
|
|
int64 balance = 3;
|
|
// Reserved credits (pending reservations)
|
|
int64 reserved = 4;
|
|
// Total credits ever deposited
|
|
int64 total_deposited = 5;
|
|
// Total credits consumed
|
|
int64 total_consumed = 6;
|
|
WalletStatus status = 7;
|
|
google.protobuf.Timestamp created_at = 8;
|
|
google.protobuf.Timestamp updated_at = 9;
|
|
}
|
|
|
|
enum WalletStatus {
|
|
WALLET_STATUS_UNSPECIFIED = 0;
|
|
WALLET_STATUS_ACTIVE = 1;
|
|
WALLET_STATUS_SUSPENDED = 2; // Insufficient balance
|
|
WALLET_STATUS_CLOSED = 3;
|
|
}
|
|
|
|
// Transaction represents a credit movement
|
|
message Transaction {
|
|
string id = 1;
|
|
string project_id = 2;
|
|
TransactionType type = 3;
|
|
int64 amount = 4;
|
|
int64 balance_after = 5;
|
|
string description = 6;
|
|
string resource_id = 7; // Optional: related resource
|
|
google.protobuf.Timestamp created_at = 8;
|
|
}
|
|
|
|
enum TransactionType {
|
|
TRANSACTION_TYPE_UNSPECIFIED = 0;
|
|
TRANSACTION_TYPE_TOP_UP = 1; // Credit addition
|
|
TRANSACTION_TYPE_RESERVATION = 2; // Temporary hold
|
|
TRANSACTION_TYPE_CHARGE = 3; // Actual consumption
|
|
TRANSACTION_TYPE_RELEASE = 4; // Reservation release
|
|
TRANSACTION_TYPE_REFUND = 5; // Credit return
|
|
TRANSACTION_TYPE_BILLING_CHARGE = 6; // Periodic billing
|
|
}
|
|
|
|
// Reservation represents a credit hold (2-phase commit)
|
|
message Reservation {
|
|
string id = 1;
|
|
string project_id = 2;
|
|
int64 amount = 3;
|
|
ReservationStatus status = 4;
|
|
string description = 5;
|
|
google.protobuf.Timestamp expires_at = 6;
|
|
google.protobuf.Timestamp created_at = 7;
|
|
}
|
|
|
|
enum ReservationStatus {
|
|
RESERVATION_STATUS_UNSPECIFIED = 0;
|
|
RESERVATION_STATUS_PENDING = 1;
|
|
RESERVATION_STATUS_COMMITTED = 2;
|
|
RESERVATION_STATUS_RELEASED = 3;
|
|
RESERVATION_STATUS_EXPIRED = 4;
|
|
}
|
|
|
|
// Quota represents resource limits per project
|
|
message Quota {
|
|
string project_id = 1;
|
|
ResourceType resource_type = 2;
|
|
int64 limit = 3;
|
|
int64 current_usage = 4;
|
|
}
|
|
|
|
enum ResourceType {
|
|
RESOURCE_TYPE_UNSPECIFIED = 0;
|
|
RESOURCE_TYPE_VM_INSTANCE = 1;
|
|
RESOURCE_TYPE_VM_CPU = 2;
|
|
RESOURCE_TYPE_VM_MEMORY_GB = 3;
|
|
RESOURCE_TYPE_STORAGE_GB = 4;
|
|
RESOURCE_TYPE_NETWORK_PORT = 5;
|
|
RESOURCE_TYPE_LOAD_BALANCER = 6;
|
|
RESOURCE_TYPE_DNS_ZONE = 7;
|
|
RESOURCE_TYPE_DNS_RECORD = 8;
|
|
RESOURCE_TYPE_K8S_CLUSTER = 9;
|
|
RESOURCE_TYPE_K8S_NODE = 10;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Wallet Operations
|
|
// ============================================================================
|
|
|
|
message GetWalletRequest {
|
|
string project_id = 1;
|
|
}
|
|
|
|
message GetWalletResponse {
|
|
Wallet wallet = 1;
|
|
}
|
|
|
|
message CreateWalletRequest {
|
|
string project_id = 1;
|
|
string org_id = 2;
|
|
int64 initial_balance = 3; // Optional initial credit
|
|
}
|
|
|
|
message CreateWalletResponse {
|
|
Wallet wallet = 1;
|
|
}
|
|
|
|
message TopUpRequest {
|
|
string project_id = 1;
|
|
int64 amount = 2;
|
|
string description = 3; // e.g., "Payment ID: xxx"
|
|
}
|
|
|
|
message TopUpResponse {
|
|
Wallet wallet = 1;
|
|
Transaction transaction = 2;
|
|
}
|
|
|
|
message GetTransactionsRequest {
|
|
string project_id = 1;
|
|
// Pagination
|
|
int32 page_size = 2;
|
|
string page_token = 3;
|
|
// Filters
|
|
TransactionType type_filter = 4;
|
|
google.protobuf.Timestamp start_time = 5;
|
|
google.protobuf.Timestamp end_time = 6;
|
|
}
|
|
|
|
message GetTransactionsResponse {
|
|
repeated Transaction transactions = 1;
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Admission Control
|
|
// ============================================================================
|
|
|
|
message CheckQuotaRequest {
|
|
string project_id = 1;
|
|
ResourceType resource_type = 2;
|
|
int32 quantity = 3;
|
|
int64 estimated_cost = 4; // Optional: estimated credit cost
|
|
}
|
|
|
|
message CheckQuotaResponse {
|
|
bool allowed = 1;
|
|
string reason = 2; // Reason if not allowed
|
|
int64 available_balance = 3;
|
|
int64 available_quota = 4;
|
|
}
|
|
|
|
message ReserveCreditsRequest {
|
|
string project_id = 1;
|
|
int64 amount = 2;
|
|
string description = 3;
|
|
string resource_type = 4; // For tracking
|
|
int32 ttl_seconds = 5; // Reservation TTL (default: 300)
|
|
}
|
|
|
|
message ReserveCreditsResponse {
|
|
Reservation reservation = 1;
|
|
}
|
|
|
|
message CommitReservationRequest {
|
|
string reservation_id = 1;
|
|
int64 actual_amount = 2; // May differ from reserved amount
|
|
string resource_id = 3; // Created resource ID for tracking
|
|
}
|
|
|
|
message CommitReservationResponse {
|
|
Transaction transaction = 1;
|
|
Wallet wallet = 2;
|
|
}
|
|
|
|
message ReleaseReservationRequest {
|
|
string reservation_id = 1;
|
|
string reason = 2; // Why released (e.g., "creation failed")
|
|
}
|
|
|
|
message ReleaseReservationResponse {
|
|
bool success = 1;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Billing
|
|
// ============================================================================
|
|
|
|
message ProcessBillingRequest {
|
|
string project_id = 1; // Empty = process all projects
|
|
google.protobuf.Timestamp billing_period_start = 2;
|
|
google.protobuf.Timestamp billing_period_end = 3;
|
|
}
|
|
|
|
message ProcessBillingResponse {
|
|
int32 projects_processed = 1;
|
|
int64 total_charged = 2;
|
|
repeated BillingResult results = 3;
|
|
}
|
|
|
|
message BillingResult {
|
|
string project_id = 1;
|
|
int64 amount_charged = 2;
|
|
bool success = 3;
|
|
string error = 4;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Quota Management
|
|
// ============================================================================
|
|
|
|
message SetQuotaRequest {
|
|
string project_id = 1;
|
|
ResourceType resource_type = 2;
|
|
int64 limit = 3;
|
|
}
|
|
|
|
message SetQuotaResponse {
|
|
Quota quota = 1;
|
|
}
|
|
|
|
message GetQuotaRequest {
|
|
string project_id = 1;
|
|
ResourceType resource_type = 2;
|
|
}
|
|
|
|
message GetQuotaResponse {
|
|
Quota quota = 1;
|
|
}
|
|
|
|
message ListQuotasRequest {
|
|
string project_id = 1;
|
|
}
|
|
|
|
message ListQuotasResponse {
|
|
repeated Quota quotas = 1;
|
|
}
|