Repository: grpc/grpc-go
File: internal/xds/bootstrap/bootstrap.go (Go)

Code before:

// The following fields correspond 1:1 with the JSON schema for ServerConfig.
type serverConfigJSON struct {
	ServerURI      string         `json:"server_uri,omitempty"`
	ChannelCreds   []ChannelCreds `json:"channel_creds,omitempty"`
	ServerFeatures []string       `json:"server_features,omitempty"`
}

// MarshalJSON returns marshaled JSON bytes corresponding to this server config.
func (sc *ServerConfig) MarshalJSON() ([]byte, error) {
	server := &serverConfigJSON{
		ServerURI:      sc.serverURI,
		ChannelCreds:   sc.channelCreds,
		ServerFeatures: sc.serverFeatures,
	}
	return json.Marshal(server)
}

// dialer captures the Dialer method specified via the credentials bundle.
type dialer interface {
	// Dialer specifies how to dial the xDS server.
	Dialer(context.Context, string) (net.Conn, error)
}

// UnmarshalJSON takes the json data (a server) and unmarshals it to the struct.
func (sc *ServerConfig) UnmarshalJSON(data []byte) error {
	server := serverConfigJSON{}
	if err := json.Unmarshal(data, &server); err != nil {
		return fmt.Errorf("xds: failed to JSON unmarshal server configuration during bootstrap: %v, config:\n%s", err, string(data))
	}

	sc.serverURI = server.ServerURI
	sc.channelCreds = server.ChannelCreds
	sc.serverFeatures = server.ServerFeatures

	for _, cc := range server.ChannelCreds {
		// We stop at the first credential type that we support.
		c := bootstrap.GetCredentials(cc.Type)
		if c == nil {
			continue
		}
		bundle, cancel, err := c.Build(cc.Config)
		if err != nil {
			return fmt.Errorf("failed to build credentials bundle from bootstrap for %q: %v", cc.Type, err)
		}
		sc.selectedCreds = cc
		sc.credsDialOption = grpc.WithCredentialsBundle(bundle)
		if d, ok := bundle.(dialer); ok {
			sc.dialerOption = grpc.WithContextDialer(d.Dialer)
		}
		sc.cleanups = append(sc.cleanups, cancel)

Review comment (security):
Can this method name be more specific? Maybe something like `DialXDS` or `DialXDSServer` or something else?

The reason I'm suggesting this is because the API to register a credentials type is public. So, users *could* register their own credentials using `bootstrap.RegisterCredentials`, and if they end up having a `Dialer` method on their creds implementation type (which is maybe doing something else), then, they would be in for a surprise.

Or at least, we should document this in the public API in package `xds/bootstrap`?

Thoughts @dfawley 

Code after:
	}
	features := strings.Join(sc.serverFeatures, "-")
	return strings.Join([]string{sc.serverURI, sc.selectedCreds.String(), features}, "-")
}

// The following fields correspond 1:1 with the JSON schema for ServerConfig.
type serverConfigJSON struct {
	ServerURI      string         `json:"server_uri,omitempty"`
	ChannelCreds   []ChannelCreds `json:"channel_creds,omitempty"`
	ServerFeatures []string       `json:"server_features,omitempty"`
}

// MarshalJSON returns marshaled JSON bytes corresponding to this server config.
func (sc *ServerConfig) MarshalJSON() ([]byte, error) {
	server := &serverConfigJSON{
		ServerURI:      sc.serverURI,
		ChannelCreds:   sc.channelCreds,
		ServerFeatures: sc.serverFeatures,
	}
	return json.Marshal(server)
}

// dialer captures the Dialer method specified via the credentials bundle.
type dialer interface {
	// Dialer specifies how to dial the xDS server.
	Dialer(context.Context, string) (net.Conn, error)
}

// UnmarshalJSON takes the json data (a server) and unmarshals it to the struct.
func (sc *ServerConfig) UnmarshalJSON(data []byte) error {
	server := serverConfigJSON{}
	if err := json.Unmarshal(data, &server); err != nil {
		return fmt.Errorf("xds: failed to JSON unmarshal server configuration during bootstrap: %v, config:\n%s", err, string(data))
	}

	sc.serverURI = server.ServerURI
	sc.channelCreds = server.ChannelCreds
	sc.serverFeatures = server.ServerFeatures

	for _, cc := range server.ChannelCreds {
		// We stop at the first credential type that we support.
		c := bootstrap.GetCredentials(cc.Type)
		if c == nil {
			continue
		}
		bundle, cancel, err := c.Build(cc.Config)
		if err != nil {
			return fmt.Errorf("failed to build credentials bundle from bootstrap for %q: %v", cc.Type, err)
		}
		sc.selectedCreds = cc
		sc.credsDialOption = grpc.WithCredentialsBundle(bundle)