/* * * Copyright 2021 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 bootstrap provides functionality to generate bootstrap configuration. package bootstrap import ( "encoding/json" "fmt" "os" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/internal/envconfig" ) var logger = grpclog.Component("internal/xds") // Options wraps the parameters used to generate bootstrap configuration. type Options struct { // NodeID is the node identifier of the gRPC client/server node in the // proxyless service mesh. NodeID string // ServerURI is the address of the management server. ServerURI string // IgnoreResourceDeletion, if true, results in a bootstrap config with the // `server_features` list containing `ignore_resource_deletion`. This results // in gRPC ignoring resource deletions from the management server, as per A53. IgnoreResourceDeletion bool // ClientDefaultListenerResourceNameTemplate is the default listener // resource name template to be used on the gRPC client. ClientDefaultListenerResourceNameTemplate string // ServerListenerResourceNameTemplate is the listener resource name template // to be used on the gRPC server. ServerListenerResourceNameTemplate string // CertificateProviders is the certificate providers configuration. CertificateProviders map[string]json.RawMessage // Authorities is a list of non-default authorities. // // In the config, an authority contains {ServerURI, xds-version, creds, // features, etc}. Note that this fields only has ServerURI (it's a // map[authority-name]ServerURI). The other fields (version, creds, // features) are assumed to be the same as the default authority (they can // be added later if needed). // // If the env var corresponding to federation (envconfig.XDSFederation) is // set, an entry with empty string as the key and empty server config as // value will be added. This will be used by new style resource names with // an empty authority. Authorities map[string]string } // CreateFile creates a temporary file with bootstrap contents, based on the // passed in options, and updates the bootstrap environment variable to point to // this file. // // Returns a cleanup function which will be non-nil if the setup process was // completed successfully. It is the responsibility of the caller to invoke the // cleanup function at the end of the test. func CreateFile(opts Options) (func(), error) { bootstrapContents, err := Contents(opts) if err != nil { return nil, err } f, err := os.CreateTemp("", "test_xds_bootstrap_*") if err != nil { return nil, fmt.Errorf("failed to created bootstrap file: %v", err) } if err := os.WriteFile(f.Name(), bootstrapContents, 0644); err != nil { return nil, fmt.Errorf("failed to created bootstrap file: %v", err) } logger.Infof("Created bootstrap file at %q with contents: %s\n", f.Name(), bootstrapContents) origBootstrapFileName := envconfig.XDSBootstrapFileName envconfig.XDSBootstrapFileName = f.Name() return func() { os.Remove(f.Name()) envconfig.XDSBootstrapFileName = origBootstrapFileName }, nil } // Contents returns the contents to go into a bootstrap file, environment, or // configuration passed to xds.NewXDSResolverWithConfigForTesting. func Contents(opts Options) ([]byte, error) { cfg := &bootstrapConfig{ XdsServers: []server{ { ServerURI: opts.ServerURI, ChannelCreds: []creds{{Type: "insecure"}}, }, }, Node: node{ ID: opts.NodeID, }, CertificateProviders: opts.CertificateProviders, ClientDefaultListenerResourceNameTemplate: opts.ClientDefaultListenerResourceNameTemplate, ServerListenerResourceNameTemplate: opts.ServerListenerResourceNameTemplate, } cfg.XdsServers[0].ServerFeatures = append(cfg.XdsServers[0].ServerFeatures, "xds_v3") if opts.IgnoreResourceDeletion { cfg.XdsServers[0].ServerFeatures = append(cfg.XdsServers[0].ServerFeatures, "ignore_resource_deletion") } auths := make(map[string]authority) if envconfig.XDSFederation { // This will end up using the top-level server list for new style // resources with empty authority. auths[""] = authority{} } for n, auURI := range opts.Authorities { auths[n] = authority{XdsServers: []server{{ ServerURI: auURI, ChannelCreds: []creds{{Type: "insecure"}}, ServerFeatures: cfg.XdsServers[0].ServerFeatures, }}} } cfg.Authorities = auths bootstrapContents, err := json.MarshalIndent(cfg, "", " ") if err != nil { return nil, fmt.Errorf("failed to created bootstrap file: %v", err) } return bootstrapContents, nil } type bootstrapConfig struct { XdsServers []server `json:"xds_servers,omitempty"` Node node `json:"node,omitempty"` CertificateProviders map[string]json.RawMessage `json:"certificate_providers,omitempty"` ClientDefaultListenerResourceNameTemplate string `json:"client_default_listener_resource_name_template,omitempty"` ServerListenerResourceNameTemplate string `json:"server_listener_resource_name_template,omitempty"` Authorities map[string]authority `json:"authorities,omitempty"` } type authority struct { XdsServers []server `json:"xds_servers,omitempty"` } type server struct { ServerURI string `json:"server_uri,omitempty"` ChannelCreds []creds `json:"channel_creds,omitempty"` ServerFeatures []string `json:"server_features,omitempty"` } type creds struct { Type string `json:"type,omitempty"` Config interface{} `json:"config,omitempty"` } type node struct { ID string `json:"id,omitempty"` }