/* * * Copyright 2019 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // Package client contains the implementation of the xds client used by // grpc-lb-v2. package client import ( "bytes" "encoding/json" "io/ioutil" "os" "github.com/golang/protobuf/jsonpb" "google.golang.org/grpc" "google.golang.org/grpc/credentials/google" "google.golang.org/grpc/grpclog" basepb "google.golang.org/grpc/xds/internal/proto/envoy/api/v2/core/base" ) const ( // Environment variable which holds the name of the xDS bootstrap file. bootstrapFileEnv = "GRPC_XDS_BOOTSTRAP" // Type name for Google default credentials. googleDefaultCreds = "google_default" ) // For overriding from unit tests. var fileReadFunc = ioutil.ReadFile // Config provides the xDS client with several key bits of information that it // requires in its interaction with an xDS server. The Config is initialized // from the bootstrap file. If that process fails for any reason, it uses the // defaults passed in. type Config struct { // BalancerName is the name of the xDS server to connect to. BalancerName string // Creds contains the credentials to be used while talking to the xDS // server, as a grpc.DialOption. Creds grpc.DialOption // NodeProto contains the basepb.Node proto to be used in xDS calls made to the // server. NodeProto *basepb.Node } type channelCreds struct { Type string `json:"type"` Config json.RawMessage `json:"config"` } type xdsServer struct { ServerURI string `json:"server_uri"` ChannelCreds []channelCreds `json:"channel_creds"` } // NewConfig returns a new instance of Config initialized by reading the // bootstrap file found at ${GRPC_XDS_BOOTSTRAP}. // // The format of the bootstrap file will be as follows: // { // "xds_server": { // "server_uri": , // "channel_creds": [ // { // "type": , // "config": // } // ] // }, // "node": // } // // Currently, we support exactly one type of credential, which is // "google_default", where we use the host's default certs for transport // credentials and a Google oauth token for call credentials. // // This function tries to process as much of the bootstrap file as possible (in // the presence of the errors) and may return a Config object with certain // fields left unspecified, in which case the caller should use some sane // defaults. func NewConfig() *Config { config := &Config{} fName, ok := os.LookupEnv(bootstrapFileEnv) if !ok { grpclog.Errorf("xds: %s environment variable not set", bootstrapFileEnv) return config } grpclog.Infof("xds: Reading bootstrap file from %s", fName) data, err := fileReadFunc(fName) if err != nil { grpclog.Errorf("xds: bootstrap file {%v} read failed: %v", fName, err) return config } var jsonData map[string]json.RawMessage if err := json.Unmarshal(data, &jsonData); err != nil { grpclog.Errorf("xds: json.Unmarshal(%v) failed during bootstrap: %v", string(data), err) return config } m := jsonpb.Unmarshaler{AllowUnknownFields: true} for k, v := range jsonData { switch k { case "node": n := &basepb.Node{} if err := m.Unmarshal(bytes.NewReader(v), n); err != nil { grpclog.Errorf("xds: jsonpb.Unmarshal(%v) failed during bootstrap: %v", string(v), err) break } config.NodeProto = n case "xds_server": xs := &xdsServer{} if err := json.Unmarshal(v, &xs); err != nil { grpclog.Errorf("xds: json.Unmarshal(%v) failed during bootstrap: %v", string(v), err) break } config.BalancerName = xs.ServerURI for _, cc := range xs.ChannelCreds { if cc.Type == googleDefaultCreds { config.Creds = grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()) // We stop at the first credential type that we support. break } } default: // Do not fail the xDS bootstrap when an unknown field is seen. grpclog.Warningf("xds: unexpected data in bootstrap file: {%v, %v}", k, string(v)) } } return config }