mirror of
https://github.com/juanfont/headscale.git
synced 2025-11-10 01:20:58 +01:00
wip
This commit is contained in:
parent
46477b8021
commit
47cc4d0f9c
@ -44,6 +44,7 @@ const (
|
||||
HeadscaleService_GetPolicy_FullMethodName = "/headscale.v1.HeadscaleService/GetPolicy"
|
||||
HeadscaleService_SetPolicy_FullMethodName = "/headscale.v1.HeadscaleService/SetPolicy"
|
||||
HeadscaleService_Health_FullMethodName = "/headscale.v1.HeadscaleService/Health"
|
||||
HeadscaleService_ListPendingRegistrations_FullMethodName = "/headscale.v1.HeadscaleService/ListPendingRegistrations"
|
||||
)
|
||||
|
||||
// HeadscaleServiceClient is the client API for HeadscaleService service.
|
||||
@ -81,6 +82,8 @@ type HeadscaleServiceClient interface {
|
||||
SetPolicy(ctx context.Context, in *SetPolicyRequest, opts ...grpc.CallOption) (*SetPolicyResponse, error)
|
||||
// --- Health start ---
|
||||
Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error)
|
||||
// --- Pending registrations ---
|
||||
ListPendingRegistrations(ctx context.Context, in *ListPendingRegistrationsRequest, opts ...grpc.CallOption) (*ListPendingRegistrationsResponse, error)
|
||||
}
|
||||
|
||||
type headscaleServiceClient struct {
|
||||
@ -341,6 +344,16 @@ func (c *headscaleServiceClient) Health(ctx context.Context, in *HealthRequest,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *headscaleServiceClient) ListPendingRegistrations(ctx context.Context, in *ListPendingRegistrationsRequest, opts ...grpc.CallOption) (*ListPendingRegistrationsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListPendingRegistrationsResponse)
|
||||
err := c.cc.Invoke(ctx, HeadscaleService_ListPendingRegistrations_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// HeadscaleServiceServer is the server API for HeadscaleService service.
|
||||
// All implementations must embed UnimplementedHeadscaleServiceServer
|
||||
// for forward compatibility.
|
||||
@ -376,6 +389,8 @@ type HeadscaleServiceServer interface {
|
||||
SetPolicy(context.Context, *SetPolicyRequest) (*SetPolicyResponse, error)
|
||||
// --- Health start ---
|
||||
Health(context.Context, *HealthRequest) (*HealthResponse, error)
|
||||
// --- Pending registrations ---
|
||||
ListPendingRegistrations(context.Context, *ListPendingRegistrationsRequest) (*ListPendingRegistrationsResponse, error)
|
||||
mustEmbedUnimplementedHeadscaleServiceServer()
|
||||
}
|
||||
|
||||
@ -461,6 +476,9 @@ func (UnimplementedHeadscaleServiceServer) SetPolicy(context.Context, *SetPolicy
|
||||
func (UnimplementedHeadscaleServiceServer) Health(context.Context, *HealthRequest) (*HealthResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Health not implemented")
|
||||
}
|
||||
func (UnimplementedHeadscaleServiceServer) ListPendingRegistrations(context.Context, *ListPendingRegistrationsRequest) (*ListPendingRegistrationsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListPendingRegistrations not implemented")
|
||||
}
|
||||
func (UnimplementedHeadscaleServiceServer) mustEmbedUnimplementedHeadscaleServiceServer() {}
|
||||
func (UnimplementedHeadscaleServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@ -932,6 +950,24 @@ func _HeadscaleService_Health_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _HeadscaleService_ListPendingRegistrations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListPendingRegistrationsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HeadscaleServiceServer).ListPendingRegistrations(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: HeadscaleService_ListPendingRegistrations_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HeadscaleServiceServer).ListPendingRegistrations(ctx, req.(*ListPendingRegistrationsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// HeadscaleService_ServiceDesc is the grpc.ServiceDesc for HeadscaleService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -1039,6 +1075,10 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "Health",
|
||||
Handler: _HeadscaleService_Health_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListPendingRegistrations",
|
||||
Handler: _HeadscaleService_ListPendingRegistrations_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "headscale/v1/headscale.proto",
|
||||
|
||||
26
gen/go/headscale/v1/pending.pb.go
Normal file
26
gen/go/headscale/v1/pending.pb.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Code generated manually to provide types for PendingRegistrations RPC until buf generate is run.
|
||||
// This file defines protobuf-compatible Go structs without full reflection metadata.
|
||||
// It is sufficient for compiling server code that references these types.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// PendingRegistration is a lightweight representation of a pending registration.
|
||||
type PendingRegistration struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"`
|
||||
MachineKey string `protobuf:"bytes,3,opt,name=machine_key,json=machineKey,proto3" json:"machine_key,omitempty"`
|
||||
NodeKey string `protobuf:"bytes,4,opt,name=node_key,json=nodeKey,proto3" json:"node_key,omitempty"`
|
||||
Expiry *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=expiry,proto3" json:"expiry,omitempty"`
|
||||
}
|
||||
|
||||
// ListPendingRegistrationsRequest is the empty request message.
|
||||
type ListPendingRegistrationsRequest struct{}
|
||||
|
||||
// ListPendingRegistrationsResponse contains the current pending registrations.
|
||||
type ListPendingRegistrationsResponse struct {
|
||||
Registrations []*PendingRegistration `protobuf:"bytes,1,rep,name=registrations,proto3" json:"registrations,omitempty"`
|
||||
}
|
||||
@ -793,4 +793,26 @@ func (api headscaleV1APIServer) Health(
|
||||
return response, healthErr
|
||||
}
|
||||
|
||||
func (api headscaleV1APIServer) ListPendingRegistrations(
|
||||
ctx context.Context,
|
||||
_ *v1.ListPendingRegistrationsRequest,
|
||||
) (*v1.ListPendingRegistrationsResponse, error) {
|
||||
regs := api.h.state.ListPendingRegistrations()
|
||||
resp := &v1.ListPendingRegistrationsResponse{}
|
||||
for _, r := range regs {
|
||||
var ts *timestamppb.Timestamp
|
||||
if r.Expiry != nil {
|
||||
ts = timestamppb.New(*r.Expiry)
|
||||
}
|
||||
resp.Registrations = append(resp.Registrations, &v1.PendingRegistration{
|
||||
Id: r.ID.String(),
|
||||
Hostname: r.Hostname,
|
||||
MachineKey: r.MachineKey,
|
||||
NodeKey: r.NodeKey,
|
||||
Expiry: ts,
|
||||
})
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (api headscaleV1APIServer) mustEmbedUnimplementedHeadscaleServiceServer() {}
|
||||
|
||||
@ -69,6 +69,17 @@ type State struct {
|
||||
primaryRoutes *routes.PrimaryRoutes
|
||||
}
|
||||
|
||||
// PendingRegistration represents a pending node registration entry in memory.
|
||||
// It is populated from the registrationCache and used by API/gRPC layers.
|
||||
// Note: This is an in-memory view only; entries expire automatically from the cache.
|
||||
type PendingRegistration struct {
|
||||
ID types.RegistrationID
|
||||
Hostname string
|
||||
MachineKey string
|
||||
NodeKey string
|
||||
Expiry *time.Time
|
||||
}
|
||||
|
||||
// NewState creates and initializes a new State instance, setting up the database,
|
||||
// IP allocator, DERP map, policy manager, and loading existing users and nodes.
|
||||
func NewState(cfg *types.Config) (*State, error) {
|
||||
@ -968,6 +979,36 @@ func (s *State) SetRegistrationCacheEntry(id types.RegistrationID, entry types.R
|
||||
s.registrationCache.Set(id, entry)
|
||||
}
|
||||
|
||||
// ListPendingRegistrations returns a snapshot of current pending registrations.
|
||||
// It iterates the registrationCache and extracts minimal identifying details.
|
||||
func (s *State) ListPendingRegistrations() []PendingRegistration {
|
||||
if s.registrationCache == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var regs []PendingRegistration
|
||||
|
||||
// zcache/v2 supports Keys iteration; use Range if available for efficiency.
|
||||
// We use Keys here and then look up to read values.
|
||||
for _, id := range s.registrationCache.Keys() {
|
||||
if rn, ok := s.registrationCache.Get(id); ok {
|
||||
var exp *time.Time
|
||||
if rn.Node.Expiry != nil {
|
||||
exp = rn.Node.Expiry
|
||||
}
|
||||
regs = append(regs, PendingRegistration{
|
||||
ID: id,
|
||||
Hostname: rn.Node.Hostname,
|
||||
MachineKey: rn.Node.MachineKey.String(),
|
||||
NodeKey: rn.Node.NodeKey.String(),
|
||||
Expiry: exp,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return regs
|
||||
}
|
||||
|
||||
// logHostinfoValidation logs warnings when hostinfo is nil or has empty hostname.
|
||||
func logHostinfoValidation(machineKey, nodeKey, username, hostname string, hostinfo *tailcfg.Hostinfo) {
|
||||
if hostinfo == nil {
|
||||
|
||||
48
hscontrol/types/pending_registration.go
Normal file
48
hscontrol/types/pending_registration.go
Normal file
@ -0,0 +1,48 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// PendingRegistrationProto converts a state.PendingRegistration-like struct to protobuf.
|
||||
// Kept in types to avoid circular imports from hscontrol/state.
|
||||
// Input is the plain fields to serialize.
|
||||
type PendingRegistrationProto struct {
|
||||
ID string
|
||||
Hostname string
|
||||
MachineKey string
|
||||
NodeKey string
|
||||
// Expiry may be nil
|
||||
ExpiryUnixMilli int64 // not used; we will pass a pointer time via Timestamppb in helpers if needed
|
||||
}
|
||||
|
||||
// BuildPendingRegistration constructs a v1.PendingRegistration.
|
||||
func BuildPendingRegistration(id, hostname, machineKey, nodeKey string, expiry *int64) *v1.PendingRegistration {
|
||||
pr := &v1.PendingRegistration{
|
||||
Id: id,
|
||||
Hostname: hostname,
|
||||
MachineKey: machineKey,
|
||||
NodeKey: nodeKey,
|
||||
}
|
||||
if expiry != nil {
|
||||
// We cannot convert int64 millis without time. Keep the field for potential future.
|
||||
// Callers should prefer using BuildPendingRegistrationWithTimestamp.
|
||||
_ = expiry
|
||||
}
|
||||
return pr
|
||||
}
|
||||
|
||||
// BuildPendingRegistrationWithTimestamp sets a proper protobuf timestamp if provided.
|
||||
func BuildPendingRegistrationWithTimestamp(id, hostname, machineKey, nodeKey string, ts *timestamppb.Timestamp) *v1.PendingRegistration {
|
||||
pr := &v1.PendingRegistration{
|
||||
Id: id,
|
||||
Hostname: hostname,
|
||||
MachineKey: machineKey,
|
||||
NodeKey: nodeKey,
|
||||
}
|
||||
if ts != nil {
|
||||
pr.Expiry = ts
|
||||
}
|
||||
return pr
|
||||
}
|
||||
@ -3,6 +3,7 @@ package headscale.v1;
|
||||
option go_package = "github.com/juanfont/headscale/gen/go/v1";
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
import "headscale/v1/user.proto";
|
||||
import "headscale/v1/preauthkey.proto";
|
||||
@ -190,6 +191,14 @@ service HeadscaleService {
|
||||
}
|
||||
// --- Health end ---
|
||||
|
||||
// --- Pending registrations ---
|
||||
rpc ListPendingRegistrations(ListPendingRegistrationsRequest) returns (ListPendingRegistrationsResponse) {
|
||||
option (google.api.http) = {
|
||||
get : "/api/v1/pending-registrations"
|
||||
};
|
||||
}
|
||||
// --- Pending registrations end ---
|
||||
|
||||
// Implement Tailscale API
|
||||
// rpc GetDevice(GetDeviceRequest) returns(GetDeviceResponse) {
|
||||
// option(google.api.http) = {
|
||||
@ -223,3 +232,17 @@ message HealthRequest {}
|
||||
message HealthResponse {
|
||||
bool database_connectivity = 1;
|
||||
}
|
||||
|
||||
message PendingRegistration {
|
||||
string id = 1;
|
||||
string hostname = 2;
|
||||
string machine_key = 3;
|
||||
string node_key = 4;
|
||||
google.protobuf.Timestamp expiry = 5;
|
||||
}
|
||||
|
||||
message ListPendingRegistrationsRequest {}
|
||||
|
||||
message ListPendingRegistrationsResponse {
|
||||
repeated PendingRegistration registrations = 1;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user