| /* |
| * |
| * Copyright 2020 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 |
| |
| import ( |
| "fmt" |
| "sync" |
| "time" |
| |
| "google.golang.org/grpc/xds/internal/client/bootstrap" |
| ) |
| |
| const defaultWatchExpiryTimeout = 15 * time.Second |
| |
| // This is the Client returned by New(). It contains one client implementation, |
| // and maintains the refcount. |
| var singletonClient = &Client{} |
| |
| // To override in tests. |
| var bootstrapNewConfig = bootstrap.NewConfig |
| |
| // Client is a full fledged gRPC client which queries a set of discovery APIs |
| // (collectively termed as xDS) on a remote management server, to discover |
| // various dynamic resources. |
| // |
| // The xds client is a singleton. It will be shared by the xds resolver and |
| // balancer implementations, across multiple ClientConns and Servers. |
| type Client struct { |
| *clientImpl |
| |
| // This mu protects all the fields, including the embedded clientImpl above. |
| mu sync.Mutex |
| refCount int |
| } |
| |
| // New returns a new xdsClient configured by the bootstrap file specified in env |
| // variable GRPC_XDS_BOOTSTRAP. |
| func New() (*Client, error) { |
| singletonClient.mu.Lock() |
| defer singletonClient.mu.Unlock() |
| // If the client implementation was created, increment ref count and return |
| // the client. |
| if singletonClient.clientImpl != nil { |
| singletonClient.refCount++ |
| return singletonClient, nil |
| } |
| |
| // Create the new client implementation. |
| config, err := bootstrapNewConfig() |
| if err != nil { |
| return nil, fmt.Errorf("xds: failed to read bootstrap file: %v", err) |
| } |
| c, err := newWithConfig(config, defaultWatchExpiryTimeout) |
| if err != nil { |
| return nil, err |
| } |
| |
| singletonClient.clientImpl = c |
| singletonClient.refCount++ |
| return singletonClient, nil |
| } |
| |
| // Close closes the client. It does ref count of the xds client implementation, |
| // and closes the gRPC connection to the management server when ref count |
| // reaches 0. |
| func (c *Client) Close() { |
| c.mu.Lock() |
| defer c.mu.Unlock() |
| c.refCount-- |
| if c.refCount == 0 { |
| c.clientImpl.Close() |
| // Set clientImpl back to nil. So if New() is called after this, a new |
| // implementation will be created. |
| c.clientImpl = nil |
| } |
| } |
| |
| // NewWithConfigForTesting is exported for testing only. |
| func NewWithConfigForTesting(config *bootstrap.Config, watchExpiryTimeout time.Duration) (*Client, error) { |
| cl, err := newWithConfig(config, watchExpiryTimeout) |
| if err != nil { |
| return nil, err |
| } |
| return &Client{clientImpl: cl, refCount: 1}, nil |
| } |