make tags first class node owner (#2885)
This PR changes tags to be something that exists on nodes in addition to users, to being its own thing. It is part of moving our tags support towards the correct tailscale compatible implementation. There are probably rough edges in this PR, but the intention is to get it in, and then start fixing bugs from 0.28.0 milestone (long standing tags issue) to discover what works and what doesnt. Updates #2417 Closes #2619
This commit is contained in:
parent
705b239677
commit
eb788cd007
49 changed files with 3102 additions and 757 deletions
|
|
@ -172,7 +172,7 @@ func (api headscaleV1APIServer) CreatePreAuthKey(
|
|||
}
|
||||
|
||||
preAuthKey, err := api.h.state.CreatePreAuthKey(
|
||||
types.UserID(user.ID),
|
||||
user.TypedID(),
|
||||
request.GetReusable(),
|
||||
request.GetEphemeral(),
|
||||
&expiration,
|
||||
|
|
@ -341,6 +341,17 @@ func (api headscaleV1APIServer) SetTags(
|
|||
ctx context.Context,
|
||||
request *v1.SetTagsRequest,
|
||||
) (*v1.SetTagsResponse, error) {
|
||||
// Validate tags not empty - tagged nodes must have at least one tag
|
||||
if len(request.GetTags()) == 0 {
|
||||
return &v1.SetTagsResponse{
|
||||
Node: nil,
|
||||
}, status.Error(
|
||||
codes.InvalidArgument,
|
||||
"cannot remove all tags from a node - tagged nodes must have at least one tag",
|
||||
)
|
||||
}
|
||||
|
||||
// Validate tag format
|
||||
for _, tag := range request.GetTags() {
|
||||
err := validateTag(tag)
|
||||
if err != nil {
|
||||
|
|
@ -348,6 +359,16 @@ func (api headscaleV1APIServer) SetTags(
|
|||
}
|
||||
}
|
||||
|
||||
// User XOR Tags: nodes are either tagged or user-owned, never both.
|
||||
// Setting tags on a user-owned node converts it to a tagged node.
|
||||
// Once tagged, a node cannot be converted back to user-owned.
|
||||
_, found := api.h.state.GetNodeByID(types.NodeID(request.GetNodeId()))
|
||||
if !found {
|
||||
return &v1.SetTagsResponse{
|
||||
Node: nil,
|
||||
}, status.Error(codes.NotFound, "node not found")
|
||||
}
|
||||
|
||||
node, nodeChange, err := api.h.state.SetNodeTags(types.NodeID(request.GetNodeId()), request.GetTags())
|
||||
if err != nil {
|
||||
return &v1.SetTagsResponse{
|
||||
|
|
@ -529,13 +550,19 @@ func nodesToProto(state *state.State, nodes views.Slice[types.NodeView]) []*v1.N
|
|||
for index, node := range nodes.All() {
|
||||
resp := node.Proto()
|
||||
|
||||
// Tags-as-identity: tagged nodes show as TaggedDevices user in API responses
|
||||
// (UserID may be set internally for "created by" tracking)
|
||||
if node.IsTagged() {
|
||||
resp.User = types.TaggedDevices.Proto()
|
||||
}
|
||||
|
||||
var tags []string
|
||||
for _, tag := range node.RequestTags() {
|
||||
if state.NodeCanHaveTag(node, tag) {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
resp.ValidTags = lo.Uniq(append(tags, node.ForcedTags().AsSlice()...))
|
||||
resp.ValidTags = lo.Uniq(append(tags, node.Tags().AsSlice()...))
|
||||
|
||||
resp.SubnetRoutes = util.PrefixesToString(append(state.GetNodePrimaryRoutes(node.ID()), node.ExitRoutes()...))
|
||||
response[index] = resp
|
||||
|
|
@ -780,7 +807,7 @@ func (api headscaleV1APIServer) DebugCreateNode(
|
|||
NodeKey: key.NewNode().Public(),
|
||||
MachineKey: key.NewMachine().Public(),
|
||||
Hostname: request.GetName(),
|
||||
User: *user,
|
||||
User: user,
|
||||
|
||||
Expiry: &time.Time{},
|
||||
LastSeen: &time.Time{},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue