Repository: grpc/grpc-go

Issue:
Version `v1.74.2` Doesn't allow empty `Node ID` value anymore
### What version of gRPC are you using?
`v1.74.2`
### What version of Go are you using (`go version`)?

go `1.24.4`

### What operating system (Linux, Windows, …) and version?
Linux

### What did you do?
We are using an empty Node ID value in our bootstrap file!

### What did you expect to see?
Until `v1.73.0`, this was working as expected.

### What did you see instead?
`xdsclient: node ID is empty (check that target is valid)"`

Discussion:
@davinci26: It seems to be an artifact of https://github.com/grpc/grpc-go/pull/8391 

https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#envoy-v3-api-msg-config-core-v3-node envoy docs seem a bit ambiguous  around allowing `""` but from a quickly playing around with envoy config it seems valid

cc @purnesh42H @arjan-bal: Hi @shadialtarsha and @davinci26, thanks for reporting this. We recently made significant refactorings to the xDS client used within gRPC, with the goal of eventually providing the xDS client as a standalone library for use outside of gRPC. These changes were intended to be transparent to users. However, it seems that requiring a node ID may be backward-incompatible. I checked [gRPC C++](https://github.com/grpc/grpc/blob/2ca9ac63ced7b77cf0eb885276fbedec1ab0bd10/src/core/xds/grpc/xds_bootstrap_grpc.cc#L67), and it also treats the node ID as optional.

@easwars, can you confirm if we should make the xDS node ID optional again?@davinci26: > with the goal of eventually providing the xDS client as a standalone library for use outside of gRPC

Ohhhh that is very very exciting :D we would absolutely use this for other protocols as well like doing XDS for rest HTTP and even storage protocols

keep us posted we are happy to try out stuff :D @easwars: @arjan-bal : Thanks for checking with the C++ team. If node ID is optional there, it should be optional in Go as well.

@davinci26 : Would you mind sending us a PR for this? I'd be happy to review it. @davinci26: @easwars @arjan-bal is there an ETA/plan for ` We recently made significant refactorings to the xDS client used within gRPC, with the goal of eventually providing the xDS client as a standalone library for use outside of gRPC` 

We would actually be very very interested in consuming it if thats the case so I wanted to know if there is an internal issue tracking this/eta or anything you can share on that front.
@easwars: We still haven't completely moved the xDS client functionality out of our repo into a standalone thing. But we are close, and have something [here](https://github.com/grpc/grpc-go/tree/master/xds/internal/clients), where we have moved out as much of the gRPC-specific code out of the xDS client.

Would you be willing it try it out? We would really appreciate if you could do that and give us any feedback that you have. Thanks.

Solution patch:
diff --git a/xds/internal/clients/xdsclient/xdsclient.go b/xds/internal/clients/xdsclient/xdsclient.go
index e8138198..93d26217 100644
--- a/xds/internal/clients/xdsclient/xdsclient.go
+++ b/xds/internal/clients/xdsclient/xdsclient.go
@@ -101,8 +101,6 @@ type XDSClient struct {
 // New returns a new xDS Client configured with the provided config.
 func New(config Config) (*XDSClient, error) {
 	switch {
-	case config.Node.ID == "":
-		return nil, errors.New("xdsclient: node ID is empty")
 	case config.ResourceTypes == nil:
 		return nil, errors.New("xdsclient: resource types map is nil")
 	case config.TransportBuilder == nil:



Test patch:
diff --git a/xds/internal/clients/xdsclient/xdsclient_test.go b/xds/internal/clients/xdsclient/xdsclient_test.go
index 69b64006..073362d0 100644
--- a/xds/internal/clients/xdsclient/xdsclient_test.go
+++ b/xds/internal/clients/xdsclient/xdsclient_test.go
@@ -36,11 +36,6 @@ func (s) TestXDSClient_New(t *testing.T) {
 		config  Config
 		wantErr string
 	}{
-		{
-			name:    "empty node ID",
-			config:  Config{},
-			wantErr: "node ID is empty",
-		},
 		{
 			name: "nil resource types",
 			config: Config{
@@ -75,6 +70,16 @@ func (s) TestXDSClient_New(t *testing.T) {
 			},
 			wantErr: "",
 		},
+		{
+			name: "success with servers and empty nodeID",
+			config: Config{
+				Node:             clients.Node{ID: ""},
+				ResourceTypes:    map[string]ResourceType{xdsresource.V3ListenerURL: listenerType},
+				TransportBuilder: grpctransport.NewBuilder(configs),
+				Servers:          []ServerConfig{{ServerIdentifier: clients.ServerIdentifier{ServerURI: "dummy-server"}}},
+			},
+			wantErr: "",
+		},
 		{
 			name: "success with authorities",
 			config: Config{