diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 27512c53..882460dd 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -4,7 +4,6 @@ import ( "fmt" "log" "net/netip" - "slices" "strconv" "strings" "time" @@ -22,7 +21,6 @@ import ( func init() { rootCmd.AddCommand(nodeCmd) listNodesCmd.Flags().StringP("user", "u", "", "Filter by user") - listNodesCmd.Flags().BoolP("tags", "t", false, "Show tags") listNodesCmd.Flags().StringP("namespace", "n", "", "User") listNodesNamespaceFlag := listNodesCmd.Flags().Lookup("namespace") @@ -148,10 +146,6 @@ var listNodesCmd = &cobra.Command{ if err != nil { ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) } - showTags, err := cmd.Flags().GetBool("tags") - if err != nil { - ErrorOutput(err, fmt.Sprintf("Error getting tags flag: %s", err), output) - } ctx, client, conn, cancel := newHeadscaleCLIWithConfig() defer cancel() @@ -174,7 +168,7 @@ var listNodesCmd = &cobra.Command{ SuccessOutput(response.GetNodes(), "", output) } - tableData, err := nodesToPtables(user, showTags, response.GetNodes()) + tableData, err := nodesToPtables(user, response.GetNodes()) if err != nil { ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output) } @@ -482,7 +476,6 @@ be assigned to nodes.`, func nodesToPtables( currentUser string, - showTags bool, nodes []*v1.Node, ) (pterm.TableData, error) { tableHeader := []string{ @@ -492,6 +485,7 @@ func nodesToPtables( "MachineKey", "NodeKey", "User", + "Tags", "IP addresses", "Ephemeral", "Last seen", @@ -499,13 +493,6 @@ func nodesToPtables( "Connected", "Expired", } - if showTags { - tableHeader = append(tableHeader, []string{ - "ForcedTags", - "InvalidTags", - "ValidTags", - }...) - } tableData := pterm.TableData{tableHeader} for _, node := range nodes { @@ -560,28 +547,17 @@ func nodesToPtables( expired = pterm.LightRed("yes") } - var forcedTags string - for _, tag := range node.GetForcedTags() { - forcedTags += "\n" + tag + // TODO(kradalby): as part of CLI rework, we should add the posibility to show "unusable" tags as mentioned in + // https://github.com/juanfont/headscale/issues/2981 + var tagsBuilder strings.Builder + + for _, tag := range node.GetTags() { + tagsBuilder.WriteString("\n" + tag) } - forcedTags = strings.TrimLeft(forcedTags, "\n") - var invalidTags string - for _, tag := range node.GetInvalidTags() { - if !slices.Contains(node.GetForcedTags(), tag) { - invalidTags += "\n" + pterm.LightRed(tag) - } - } + tags := tagsBuilder.String() - invalidTags = strings.TrimLeft(invalidTags, "\n") - var validTags string - for _, tag := range node.GetValidTags() { - if !slices.Contains(node.GetForcedTags(), tag) { - validTags += "\n" + pterm.LightGreen(tag) - } - } - - validTags = strings.TrimLeft(validTags, "\n") + tags = strings.TrimLeft(tags, "\n") var user string if currentUser == "" || (currentUser == node.GetUser().GetName()) { @@ -608,6 +584,7 @@ func nodesToPtables( machineKey.ShortString(), nodeKey.ShortString(), user, + tags, strings.Join([]string{IPV4Address, IPV6Address}, ", "), strconv.FormatBool(ephemeral), lastSeenTime, @@ -615,9 +592,6 @@ func nodesToPtables( online, expired, } - if showTags { - nodeData = append(nodeData, []string{forcedTags, invalidTags, validTags}...) - } tableData = append( tableData, nodeData, diff --git a/gen/go/headscale/v1/node.pb.go b/gen/go/headscale/v1/node.pb.go index bdf5ce72..b4b7e8f6 100644 --- a/gen/go/headscale/v1/node.pb.go +++ b/gen/go/headscale/v1/node.pb.go @@ -75,27 +75,29 @@ func (RegisterMethod) EnumDescriptor() ([]byte, []int) { } type Node struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - MachineKey string `protobuf:"bytes,2,opt,name=machine_key,json=machineKey,proto3" json:"machine_key,omitempty"` - NodeKey string `protobuf:"bytes,3,opt,name=node_key,json=nodeKey,proto3" json:"node_key,omitempty"` - DiscoKey string `protobuf:"bytes,4,opt,name=disco_key,json=discoKey,proto3" json:"disco_key,omitempty"` - IpAddresses []string `protobuf:"bytes,5,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` - Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` - User *User `protobuf:"bytes,7,opt,name=user,proto3" json:"user,omitempty"` - LastSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - Expiry *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=expiry,proto3" json:"expiry,omitempty"` - PreAuthKey *PreAuthKey `protobuf:"bytes,11,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - RegisterMethod RegisterMethod `protobuf:"varint,13,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` - ForcedTags []string `protobuf:"bytes,18,rep,name=forced_tags,json=forcedTags,proto3" json:"forced_tags,omitempty"` - InvalidTags []string `protobuf:"bytes,19,rep,name=invalid_tags,json=invalidTags,proto3" json:"invalid_tags,omitempty"` - ValidTags []string `protobuf:"bytes,20,rep,name=valid_tags,json=validTags,proto3" json:"valid_tags,omitempty"` - GivenName string `protobuf:"bytes,21,opt,name=given_name,json=givenName,proto3" json:"given_name,omitempty"` - Online bool `protobuf:"varint,22,opt,name=online,proto3" json:"online,omitempty"` - ApprovedRoutes []string `protobuf:"bytes,23,rep,name=approved_routes,json=approvedRoutes,proto3" json:"approved_routes,omitempty"` - AvailableRoutes []string `protobuf:"bytes,24,rep,name=available_routes,json=availableRoutes,proto3" json:"available_routes,omitempty"` - SubnetRoutes []string `protobuf:"bytes,25,rep,name=subnet_routes,json=subnetRoutes,proto3" json:"subnet_routes,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + MachineKey string `protobuf:"bytes,2,opt,name=machine_key,json=machineKey,proto3" json:"machine_key,omitempty"` + NodeKey string `protobuf:"bytes,3,opt,name=node_key,json=nodeKey,proto3" json:"node_key,omitempty"` + DiscoKey string `protobuf:"bytes,4,opt,name=disco_key,json=discoKey,proto3" json:"disco_key,omitempty"` + IpAddresses []string `protobuf:"bytes,5,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + User *User `protobuf:"bytes,7,opt,name=user,proto3" json:"user,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + Expiry *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=expiry,proto3" json:"expiry,omitempty"` + PreAuthKey *PreAuthKey `protobuf:"bytes,11,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + RegisterMethod RegisterMethod `protobuf:"varint,13,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` + // Deprecated + // repeated string forced_tags = 18; + // repeated string invalid_tags = 19; + // repeated string valid_tags = 20; + GivenName string `protobuf:"bytes,21,opt,name=given_name,json=givenName,proto3" json:"given_name,omitempty"` + Online bool `protobuf:"varint,22,opt,name=online,proto3" json:"online,omitempty"` + ApprovedRoutes []string `protobuf:"bytes,23,rep,name=approved_routes,json=approvedRoutes,proto3" json:"approved_routes,omitempty"` + AvailableRoutes []string `protobuf:"bytes,24,rep,name=available_routes,json=availableRoutes,proto3" json:"available_routes,omitempty"` + SubnetRoutes []string `protobuf:"bytes,25,rep,name=subnet_routes,json=subnetRoutes,proto3" json:"subnet_routes,omitempty"` + Tags []string `protobuf:"bytes,26,rep,name=tags,proto3" json:"tags,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -214,27 +216,6 @@ func (x *Node) GetRegisterMethod() RegisterMethod { return RegisterMethod_REGISTER_METHOD_UNSPECIFIED } -func (x *Node) GetForcedTags() []string { - if x != nil { - return x.ForcedTags - } - return nil -} - -func (x *Node) GetInvalidTags() []string { - if x != nil { - return x.InvalidTags - } - return nil -} - -func (x *Node) GetValidTags() []string { - if x != nil { - return x.ValidTags - } - return nil -} - func (x *Node) GetGivenName() string { if x != nil { return x.GivenName @@ -270,6 +251,13 @@ func (x *Node) GetSubnetRoutes() []string { return nil } +func (x *Node) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + type RegisterNodeRequest struct { state protoimpl.MessageState `protogen:"open.v1"` User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` @@ -1210,7 +1198,7 @@ var File_headscale_v1_node_proto protoreflect.FileDescriptor const file_headscale_v1_node_proto_rawDesc = "" + "\n" + - "\x17headscale/v1/node.proto\x12\fheadscale.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1dheadscale/v1/preauthkey.proto\x1a\x17headscale/v1/user.proto\"\x98\x06\n" + + "\x17headscale/v1/node.proto\x12\fheadscale.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1dheadscale/v1/preauthkey.proto\x1a\x17headscale/v1/user.proto\"\xc9\x05\n" + "\x04Node\x12\x0e\n" + "\x02id\x18\x01 \x01(\x04R\x02id\x12\x1f\n" + "\vmachine_key\x18\x02 \x01(\tR\n" + @@ -1227,19 +1215,15 @@ const file_headscale_v1_node_proto_rawDesc = "" + "preAuthKey\x129\n" + "\n" + "created_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12E\n" + - "\x0fregister_method\x18\r \x01(\x0e2\x1c.headscale.v1.RegisterMethodR\x0eregisterMethod\x12\x1f\n" + - "\vforced_tags\x18\x12 \x03(\tR\n" + - "forcedTags\x12!\n" + - "\finvalid_tags\x18\x13 \x03(\tR\vinvalidTags\x12\x1d\n" + - "\n" + - "valid_tags\x18\x14 \x03(\tR\tvalidTags\x12\x1d\n" + + "\x0fregister_method\x18\r \x01(\x0e2\x1c.headscale.v1.RegisterMethodR\x0eregisterMethod\x12\x1d\n" + "\n" + "given_name\x18\x15 \x01(\tR\tgivenName\x12\x16\n" + "\x06online\x18\x16 \x01(\bR\x06online\x12'\n" + "\x0fapproved_routes\x18\x17 \x03(\tR\x0eapprovedRoutes\x12)\n" + "\x10available_routes\x18\x18 \x03(\tR\x0favailableRoutes\x12#\n" + - "\rsubnet_routes\x18\x19 \x03(\tR\fsubnetRoutesJ\x04\b\t\x10\n" + - "J\x04\b\x0e\x10\x12\";\n" + + "\rsubnet_routes\x18\x19 \x03(\tR\fsubnetRoutes\x12\x12\n" + + "\x04tags\x18\x1a \x03(\tR\x04tagsJ\x04\b\t\x10\n" + + "J\x04\b\x0e\x10\x15\";\n" + "\x13RegisterNodeRequest\x12\x12\n" + "\x04user\x18\x01 \x01(\tR\x04user\x12\x10\n" + "\x03key\x18\x02 \x01(\tR\x03key\">\n" + diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json index 0791e8ca..4116349a 100644 --- a/gen/openapiv2/headscale/v1/headscale.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -1178,26 +1178,9 @@ "registerMethod": { "$ref": "#/definitions/v1RegisterMethod" }, - "forcedTags": { - "type": "array", - "items": { - "type": "string" - } - }, - "invalidTags": { - "type": "array", - "items": { - "type": "string" - } - }, - "validTags": { - "type": "array", - "items": { - "type": "string" - } - }, "givenName": { - "type": "string" + "type": "string", + "title": "Deprecated\nrepeated string forced_tags = 18;\nrepeated string invalid_tags = 19;\nrepeated string valid_tags = 20;" }, "online": { "type": "boolean" @@ -1219,6 +1202,12 @@ "items": { "type": "string" } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/hscontrol/grpcv1.go b/hscontrol/grpcv1.go index 9573d1ea..6c384201 100644 --- a/hscontrol/grpcv1.go +++ b/hscontrol/grpcv1.go @@ -339,11 +339,11 @@ func (api headscaleV1APIServer) SetTags( // 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", - ) + Node: nil, + }, status.Error( + codes.InvalidArgument, + "cannot remove all tags from a node - tagged nodes must have at least one tag", + ) } // Validate tag format @@ -551,8 +551,6 @@ func nodesToProto(state *state.State, nodes views.Slice[types.NodeView]) []*v1.N resp.User = types.TaggedDevices.Proto() } - resp.ValidTags = node.Tags().AsSlice() - resp.SubnetRoutes = util.PrefixesToString(append(state.GetNodePrimaryRoutes(node.ID()), node.ExitRoutes()...)) response[index] = resp } diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index fe4c544f..41cd9759 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -377,7 +377,7 @@ func (node *Node) Proto() *v1.Node { Name: node.Hostname, GivenName: node.GivenName, User: nil, // Will be set below based on node type - ForcedTags: node.Tags, + Tags: node.Tags, Online: node.IsOnline != nil && *node.IsOnline, // Only ApprovedRoutes and AvailableRoutes is set here. SubnetRoutes has diff --git a/integration/acl_test.go b/integration/acl_test.go index e2f693cd..ede029ce 100644 --- a/integration/acl_test.go +++ b/integration/acl_test.go @@ -2814,7 +2814,7 @@ func TestACLTagPropagation(t *testing.T) { assert.NotNil(c, node, "Node should still exist") if node != nil { - assert.ElementsMatch(c, tt.tagChange, node.GetValidTags(), "Tags should be updated") + assert.ElementsMatch(c, tt.tagChange, node.GetTags(), "Tags should be updated") } }, 10*time.Second, 500*time.Millisecond, "verifying tag change applied") diff --git a/integration/tags_test.go b/integration/tags_test.go index d0008835..3183d944 100644 --- a/integration/tags_test.go +++ b/integration/tags_test.go @@ -67,7 +67,7 @@ func tagsEqual(actual, expected []string) bool { // assertNodeHasTagsWithCollect asserts that a node has exactly the expected tags (order-independent). func assertNodeHasTagsWithCollect(c *assert.CollectT, node *v1.Node, expectedTags []string) { - actualTags := node.GetValidTags() + actualTags := node.GetTags() sortedActual := append([]string{}, actualTags...) sortedExpected := append([]string{}, expectedTags...) @@ -78,7 +78,7 @@ func assertNodeHasTagsWithCollect(c *assert.CollectT, node *v1.Node, expectedTag // assertNodeHasNoTagsWithCollect asserts that a node has no tags. func assertNodeHasNoTagsWithCollect(c *assert.CollectT, node *v1.Node) { - assert.Empty(c, node.GetValidTags(), "Node %s should have no tags, but has: %v", node.GetName(), node.GetValidTags()) + assert.Empty(c, node.GetTags(), "Node %s should have no tags, but has: %v", node.GetName(), node.GetTags()) } // ============================================================================= @@ -152,7 +152,7 @@ func TestTagsAuthKeyWithTagRequestDifferentTag(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node state") @@ -222,7 +222,7 @@ func TestTagsAuthKeyWithTagNoAdvertiseFlag(t *testing.T) { if len(nodes) == 1 { node := nodes[0] - t.Logf("Node registered with tags: %v", node.GetValidTags()) + t.Logf("Node registered with tags: %v", node.GetTags()) assertNodeHasTagsWithCollect(c, node, []string{"tag:valid-owned"}) } }, 30*time.Second, 500*time.Millisecond, "verifying node inherited tags from auth key") @@ -320,10 +320,10 @@ func TestTagsAuthKeyWithTagCannotAddViaCLI(t *testing.T) { if len(nodes) == 1 { // If still only has original tag, that's the expected behavior - if tagsEqual(nodes[0].GetValidTags(), []string{"tag:valid-owned"}) { - t.Logf("Test 2.3 PASS: Tags unchanged after CLI attempt: %v", nodes[0].GetValidTags()) + if tagsEqual(nodes[0].GetTags(), []string{"tag:valid-owned"}) { + t.Logf("Test 2.3 PASS: Tags unchanged after CLI attempt: %v", nodes[0].GetTags()) } else { - t.Logf("Test 2.3 FAIL: Tags changed unexpectedly to: %v", nodes[0].GetValidTags()) + t.Logf("Test 2.3 FAIL: Tags changed unexpectedly to: %v", nodes[0].GetTags()) assert.Fail(c, "Tags should not have changed") } } @@ -416,10 +416,10 @@ func TestTagsAuthKeyWithTagCannotChangeViaCLI(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - if tagsEqual(nodes[0].GetValidTags(), []string{"tag:valid-owned"}) { - t.Logf("Test 2.4 PASS: Tags unchanged: %v", nodes[0].GetValidTags()) + if tagsEqual(nodes[0].GetTags(), []string{"tag:valid-owned"}) { + t.Logf("Test 2.4 PASS: Tags unchanged: %v", nodes[0].GetTags()) } else { - t.Logf("Test 2.4 FAIL: Tags changed unexpectedly to: %v", nodes[0].GetValidTags()) + t.Logf("Test 2.4 FAIL: Tags changed unexpectedly to: %v", nodes[0].GetTags()) assert.Fail(c, "Tags should not have changed") } } @@ -509,7 +509,7 @@ func TestTagsAuthKeyWithTagAdminOverrideReauthPreserves(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("After admin assignment, tags are: %v", nodes[0].GetValidTags()) + t.Logf("After admin assignment, tags are: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:second"}) } }, 10*time.Second, 500*time.Millisecond, "verifying admin tag assignment") @@ -535,7 +535,7 @@ func TestTagsAuthKeyWithTagAdminOverrideReauthPreserves(t *testing.T) { if len(nodes) >= 1 { // Find the most recently updated node (in case a new one was created) node := nodes[len(nodes)-1] - t.Logf("After reauth, tags are: %v", node.GetValidTags()) + t.Logf("After reauth, tags are: %v", node.GetTags()) // Expected: admin-assigned tags are preserved through reauth assertNodeHasTagsWithCollect(c, node, []string{"tag:second"}) @@ -648,7 +648,7 @@ func TestTagsAuthKeyWithTagCLICannotModifyAdminTags(t *testing.T) { assert.Len(c, nodes, 1, "Should have exactly 1 node") if len(nodes) == 1 { - t.Logf("After CLI attempt, tags are: %v", nodes[0].GetValidTags()) + t.Logf("After CLI attempt, tags are: %v", nodes[0].GetTags()) // Expected: tags should remain unchanged (admin wins) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:valid-owned", "tag:second"}) @@ -726,7 +726,7 @@ func TestTagsAuthKeyWithoutTagCannotRequestTags(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node state") @@ -793,7 +793,7 @@ func TestTagsAuthKeyWithoutTagRegisterNoTags(t *testing.T) { assert.Len(c, nodes, 1) if len(nodes) == 1 { - t.Logf("Node registered with tags: %v", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v", nodes[0].GetTags()) assertNodeHasNoTagsWithCollect(c, nodes[0]) } }, 30*time.Second, 500*time.Millisecond, "verifying node has no tags") @@ -889,10 +889,10 @@ func TestTagsAuthKeyWithoutTagCannotAddViaCLI(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - if len(nodes[0].GetValidTags()) == 0 { + if len(nodes[0].GetTags()) == 0 { t.Logf("Test 3.3 PASS: Tags still empty after CLI attempt") } else { - t.Logf("Test 3.3 FAIL: Tags changed to: %v", nodes[0].GetValidTags()) + t.Logf("Test 3.3 FAIL: Tags changed to: %v", nodes[0].GetTags()) assert.Fail(c, "Tags should not have changed") } } @@ -1003,7 +1003,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithReset(t *testing.T) { assert.Len(c, nodes, 1, "Should have exactly 1 node") if len(nodes) == 1 { - t.Logf("After --reset, tags are: %v", nodes[0].GetValidTags()) + t.Logf("After --reset, tags are: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:valid-owned"}) } }, 10*time.Second, 500*time.Millisecond, "admin tags should be preserved after --reset") @@ -1113,7 +1113,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithEmptyAdvertise(t *testing.T) assert.Len(c, nodes, 1, "Should have exactly 1 node") if len(nodes) == 1 { - t.Logf("After empty --advertise-tags, tags are: %v", nodes[0].GetValidTags()) + t.Logf("After empty --advertise-tags, tags are: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:valid-owned"}) } }, 10*time.Second, 500*time.Millisecond, "admin tags should be preserved after empty --advertise-tags") @@ -1223,7 +1223,7 @@ func TestTagsAuthKeyWithoutTagCLICannotReduceAdminMultiTag(t *testing.T) { assert.Len(c, nodes, 1, "Should have exactly 1 node") if len(nodes) == 1 { - t.Logf("After CLI reduce attempt, tags are: %v", nodes[0].GetValidTags()) + t.Logf("After CLI reduce attempt, tags are: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:valid-owned", "tag:second"}) } }, 10*time.Second, 500*time.Millisecond, "admin tags should be preserved after CLI reduce attempt") @@ -1300,7 +1300,7 @@ func TestTagsUserLoginOwnedTagAtRegistration(t *testing.T) { assert.Len(c, nodes, 1, "Should have exactly 1 node") if len(nodes) == 1 { - t.Logf("Node registered with tags: %v", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:valid-owned"}) } }, 30*time.Second, 500*time.Millisecond, "verifying node has advertised tag") @@ -1373,9 +1373,9 @@ func TestTagsUserLoginNonExistentTagAtRegistration(t *testing.T) { t.Logf("Test 1.2 PASS: Registration rejected - no nodes registered") } else { // If a node was registered, it should NOT have the non-existent tag - assert.NotContains(c, nodes[0].GetValidTags(), "tag:nonexistent", + assert.NotContains(c, nodes[0].GetTags(), "tag:nonexistent", "Non-existent tag should not be applied to node") - t.Logf("Test 1.2: Node registered with tags: %v (non-existent tag correctly rejected)", nodes[0].GetValidTags()) + t.Logf("Test 1.2: Node registered with tags: %v (non-existent tag correctly rejected)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node registration result") } @@ -1442,9 +1442,9 @@ func TestTagsUserLoginUnownedTagAtRegistration(t *testing.T) { t.Logf("Test 1.3 PASS: Registration rejected - no nodes registered") } else { // If a node was registered, it should NOT have the unowned tag - assert.NotContains(c, nodes[0].GetValidTags(), "tag:valid-unowned", + assert.NotContains(c, nodes[0].GetTags(), "tag:valid-unowned", "Unowned tag should not be applied to node (tag:valid-unowned is owned by other-user)") - t.Logf("Test 1.3: Node registered with tags: %v (unowned tag correctly rejected)", nodes[0].GetValidTags()) + t.Logf("Test 1.3: Node registered with tags: %v (unowned tag correctly rejected)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node registration result") } @@ -1509,7 +1509,7 @@ func TestTagsUserLoginAddTagViaCLIReauth(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Initial tags: %v", nodes[0].GetValidTags()) + t.Logf("Initial tags: %v", nodes[0].GetTags()) } }, 30*time.Second, 500*time.Millisecond, "checking initial tags") @@ -1530,12 +1530,12 @@ func TestTagsUserLoginAddTagViaCLIReauth(t *testing.T) { assert.NoError(c, err) if len(nodes) >= 1 { - t.Logf("Test 1.4: After CLI, tags are: %v", nodes[0].GetValidTags()) + t.Logf("Test 1.4: After CLI, tags are: %v", nodes[0].GetTags()) - if tagsEqual(nodes[0].GetValidTags(), []string{"tag:valid-owned", "tag:second"}) { + if tagsEqual(nodes[0].GetTags(), []string{"tag:valid-owned", "tag:second"}) { t.Logf("Test 1.4 PASS: Both tags present after reauth") } else { - t.Logf("Test 1.4: Tags are %v (may require manual reauth completion)", nodes[0].GetValidTags()) + t.Logf("Test 1.4: Tags are %v (may require manual reauth completion)", nodes[0].GetTags()) } } }, 30*time.Second, 500*time.Millisecond, "checking tags after CLI") @@ -1601,7 +1601,7 @@ func TestTagsUserLoginRemoveTagViaCLIReauth(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Initial tags: %v", nodes[0].GetValidTags()) + t.Logf("Initial tags: %v", nodes[0].GetTags()) } }, 30*time.Second, 500*time.Millisecond, "checking initial tags") @@ -1622,9 +1622,9 @@ func TestTagsUserLoginRemoveTagViaCLIReauth(t *testing.T) { assert.NoError(c, err) if len(nodes) >= 1 { - t.Logf("Test 1.5: After CLI, tags are: %v", nodes[0].GetValidTags()) + t.Logf("Test 1.5: After CLI, tags are: %v", nodes[0].GetTags()) - if tagsEqual(nodes[0].GetValidTags(), []string{"tag:valid-owned"}) { + if tagsEqual(nodes[0].GetTags(), []string{"tag:valid-owned"}) { t.Logf("Test 1.5 PASS: Only one tag after removal") } } @@ -1697,7 +1697,7 @@ func TestTagsUserLoginCLINoOpAfterAdminAssignment(t *testing.T) { if len(nodes) == 1 { nodeID = nodes[0].GetId() - t.Logf("Step 1: Node %d registered with tags: %v", nodeID, nodes[0].GetValidTags()) + t.Logf("Step 1: Node %d registered with tags: %v", nodeID, nodes[0].GetTags()) } }, 30*time.Second, 500*time.Millisecond, "waiting for initial registration") @@ -1710,7 +1710,7 @@ func TestTagsUserLoginCLINoOpAfterAdminAssignment(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Step 2: After admin assignment, tags: %v", nodes[0].GetValidTags()) + t.Logf("Step 2: After admin assignment, tags: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:second"}) } }, 10*time.Second, 500*time.Millisecond, "verifying admin assignment") @@ -1731,7 +1731,7 @@ func TestTagsUserLoginCLINoOpAfterAdminAssignment(t *testing.T) { assert.Len(c, nodes, 1, "Should have exactly 1 node") if len(nodes) == 1 { - t.Logf("Step 3: After CLI, tags are: %v", nodes[0].GetValidTags()) + t.Logf("Step 3: After CLI, tags are: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:second"}) } }, 10*time.Second, 500*time.Millisecond, "admin tags should be preserved - CLI advertise-tags should be no-op") @@ -1816,7 +1816,7 @@ func TestTagsUserLoginCLICannotRemoveAdminTags(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("After admin assignment, tags: %v", nodes[0].GetValidTags()) + t.Logf("After admin assignment, tags: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:valid-owned", "tag:second"}) } }, 10*time.Second, 500*time.Millisecond, "verifying admin assignment") @@ -1837,7 +1837,7 @@ func TestTagsUserLoginCLICannotRemoveAdminTags(t *testing.T) { assert.Len(c, nodes, 1, "Should have exactly 1 node") if len(nodes) == 1 { - t.Logf("Test 1.7: After CLI, tags are: %v", nodes[0].GetValidTags()) + t.Logf("Test 1.7: After CLI, tags are: %v", nodes[0].GetTags()) assertNodeHasTagsWithCollect(c, nodes[0], []string{"tag:valid-owned", "tag:second"}) } }, 10*time.Second, 500*time.Millisecond, "admin tags should be preserved - CLI cannot remove them") @@ -1912,7 +1912,7 @@ func TestTagsAuthKeyWithTagRequestNonExistentTag(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node state") @@ -1983,7 +1983,7 @@ func TestTagsAuthKeyWithTagRequestUnownedTag(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node state") @@ -2058,7 +2058,7 @@ func TestTagsAuthKeyWithoutTagRequestNonExistentTag(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node state") @@ -2129,7 +2129,7 @@ func TestTagsAuthKeyWithoutTagRequestUnownedTag(t *testing.T) { assert.NoError(c, err) if len(nodes) == 1 { - t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetValidTags()) + t.Logf("Node registered with tags: %v (expected rejection)", nodes[0].GetTags()) } }, 10*time.Second, 500*time.Millisecond, "checking node state") @@ -2202,7 +2202,7 @@ func TestTagsAdminAPICannotSetNonExistentTag(t *testing.T) { if len(nodes) == 1 { nodeID = nodes[0].GetId() - t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetValidTags()) + t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetTags()) } }, 30*time.Second, 500*time.Millisecond, "waiting for registration") @@ -2275,7 +2275,7 @@ func TestTagsAdminAPICanSetUnownedTag(t *testing.T) { if len(nodes) == 1 { nodeID = nodes[0].GetId() - t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetValidTags()) + t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetTags()) } }, 30*time.Second, 500*time.Millisecond, "waiting for registration") @@ -2359,7 +2359,7 @@ func TestTagsAdminAPICannotRemoveAllTags(t *testing.T) { if len(nodes) == 1 { nodeID = nodes[0].GetId() - t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetValidTags()) + t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetTags()) } }, 30*time.Second, 500*time.Millisecond, "waiting for registration") @@ -2442,7 +2442,7 @@ func TestTagsAdminAPICannotSetInvalidFormat(t *testing.T) { if len(nodes) == 1 { nodeID = nodes[0].GetId() - t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetValidTags()) + t.Logf("Node %d registered with tags: %v", nodeID, nodes[0].GetTags()) } }, 30*time.Second, 500*time.Millisecond, "waiting for registration")