Skip to content

Commit

Permalink
xds/bootstrap: add plugin system for credentials specified in bootstr…
Browse files Browse the repository at this point in the history
…ap file (#5136)
  • Loading branch information
anicr7 committed Feb 1, 2022
1 parent 2209ed9 commit c7f7d3a
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 9 deletions.
26 changes: 26 additions & 0 deletions credentials/insecure/insecure.go
Expand Up @@ -70,3 +70,29 @@ type info struct {
func (info) AuthType() string {
return "insecure"
}

// insecureBundle implements an insecure bundle.
// An insecure bundle provides a thin wrapper around insecureTC to support
// the credentials.Bundle interface.
type insecureBundle struct{}

// NewBundle returns a bundle with disabled transport security and no per rpc credential.
func NewBundle() credentials.Bundle {
return insecureBundle{}
}

// NewWithMode returns a new insecure Bundle. The mode is ignored.
func (insecureBundle) NewWithMode(string) (credentials.Bundle, error) {
return insecureBundle{}, nil
}

// PerRPCCredentials returns an nil implementation as insecure
// bundle does not support a per rpc credential.
func (insecureBundle) PerRPCCredentials() credentials.PerRPCCredentials {
return nil
}

// TransportCredentials returns the underlying insecure transport credential.
func (insecureBundle) TransportCredentials() credentials.TransportCredentials {
return NewCredentials()
}
64 changes: 64 additions & 0 deletions xds/bootstrap/bootstrap.go
@@ -0,0 +1,64 @@
/*
*
* Copyright 2022 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 the functionality to register possible options
// for aspects of the xDS client through the bootstrap file.
//
// Experimental
//
// Notice: This package is EXPERIMENTAL and may be changed or removed
// in a later release.
package bootstrap

import (
"encoding/json"

"google.golang.org/grpc/credentials"
)

// registry is a map from credential type name to Credential builder.
var registry = make(map[string]Credentials)

// Credentials interface encapsulates a credentials.Bundle builder
// that can be used for communicating with the xDS Management server.
type Credentials interface {
// Build returns a credential bundle associated with this credential.
Build(config json.RawMessage) (credentials.Bundle, error)
// Name returns the credential name associated with this credential.
Name() string
}

// RegisterCredentials registers Credentials used for connecting to the xds
// management server.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple credentials are
// registered with the same name, the one registered last will take effect.
func RegisterCredentials(c Credentials) {
registry[c.Name()] = c
}

// GetCredentials returns the credentials associated with a given name.
// If no credentials are registered with the name, nil will be returned.
func GetCredentials(name string) Credentials {
if c, ok := registry[name]; ok {
return c
}

return nil
}
63 changes: 63 additions & 0 deletions xds/bootstrap/bootstrap_test.go
@@ -0,0 +1,63 @@
/*
*
* Copyright 2022 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

import (
"encoding/json"
"testing"

"google.golang.org/grpc/credentials"
)

const testCredsBuilderName = "test_creds"

var builder = &testCredsBuilder{}

func init() {
RegisterCredentials(builder)
}

type testCredsBuilder struct {
config json.RawMessage
}

func (t *testCredsBuilder) Build(config json.RawMessage) (credentials.Bundle, error) {
t.config = config
return nil, nil
}

func (t *testCredsBuilder) Name() string {
return testCredsBuilderName
}

func TestRegisterNew(t *testing.T) {
c := GetCredentials(testCredsBuilderName)
if c == nil {
t.Fatalf("GetCredentials(%q) credential = nil", testCredsBuilderName)
}

const sampleConfig = "sample_config"
rawMessage := json.RawMessage(sampleConfig)
if _, err := c.Build(rawMessage); err != nil {
t.Errorf("Build(%v) error = %v, want nil", rawMessage, err)
}

if got, want := string(builder.config), sampleConfig; got != want {
t.Errorf("Build config = %v, want %v", got, want)
}
}
48 changes: 39 additions & 9 deletions xds/internal/xdsclient/bootstrap/bootstrap.go
Expand Up @@ -31,12 +31,14 @@ import (
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/google"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/pretty"
"google.golang.org/grpc/xds/bootstrap"
"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"

v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
Expand All @@ -49,18 +51,43 @@ const (
// server supports the v3 version of the xDS transport protocol.
serverFeaturesV3 = "xds_v3"

// Type name for Google default credentials.
credsGoogleDefault = "google_default"
credsInsecure = "insecure"
gRPCUserAgentName = "gRPC Go"
clientFeatureNoOverprovisioning = "envoy.lb.does_not_support_overprovisioning"
)

func init() {
bootstrap.RegisterCredentials(&insecureCredsBuilder{})
bootstrap.RegisterCredentials(&googleDefaultCredsBuilder{})
}

var gRPCVersion = fmt.Sprintf("%s %s", gRPCUserAgentName, grpc.Version)

// For overriding in unit tests.
var bootstrapFileReadFunc = ioutil.ReadFile

// insecureCredsBuilder encapsulates a insecure credential that is built using a
// JSON config.
type insecureCredsBuilder struct{}

func (i *insecureCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) {
return insecure.NewBundle(), nil
}
func (i *insecureCredsBuilder) Name() string {
return "insecure"
}

// googleDefaultCredsBuilder encapsulates a Google Default credential that is built using a
// JSON config.
type googleDefaultCredsBuilder struct{}

func (d *googleDefaultCredsBuilder) Build(json.RawMessage) (credentials.Bundle, error) {
return google.NewDefaultCredentials(), nil
}

func (d *googleDefaultCredsBuilder) Name() string {
return "google_default"
}

// ServerConfig contains the configuration to connect to a server, including
// URI, creds, and transport API version (e.g. v2 or v3).
type ServerConfig struct {
Expand Down Expand Up @@ -129,13 +156,16 @@ func (sc *ServerConfig) UnmarshalJSON(data []byte) error {
for _, cc := range server.ChannelCreds {
// We stop at the first credential type that we support.
sc.CredsType = cc.Type
if cc.Type == credsGoogleDefault {
sc.Creds = grpc.WithCredentialsBundle(google.NewDefaultCredentials())
break
} else if cc.Type == credsInsecure {
sc.Creds = grpc.WithTransportCredentials(insecure.NewCredentials())
break
c := bootstrap.GetCredentials(cc.Type)
if c == nil {
continue
}
bundle, 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.Creds = grpc.WithCredentialsBundle(bundle)
break
}
for _, f := range server.ServerFeatures {
if f == serverFeaturesV3 {
Expand Down
30 changes: 30 additions & 0 deletions xds/internal/xdsclient/bootstrap/bootstrap_test.go
Expand Up @@ -35,6 +35,7 @@ import (
"google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/xds/bootstrap"
"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"

v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
Expand Down Expand Up @@ -1015,3 +1016,32 @@ func TestServerConfigMarshalAndUnmarshal(t *testing.T) {
t.Fatalf("diff (-got +want): %v", diff)
}
}

func TestDefaultBundles(t *testing.T) {
if c := bootstrap.GetCredentials("google_default"); c == nil {
t.Errorf(`bootstrap.GetCredentials("google_default") credential is nil, want non-nil`)
}

if c := bootstrap.GetCredentials("insecure"); c == nil {
t.Errorf(`bootstrap.GetCredentials("insecure") credential is nil, want non-nil`)
}
}

func TestCredsBuilders(t *testing.T) {
b := &googleDefaultCredsBuilder{}
if _, err := b.Build(nil); err != nil {
t.Errorf("googleDefaultCredsBuilder.Build failed: %v", err)
}
if got, want := b.Name(), "google_default"; got != want {
t.Errorf("googleDefaultCredsBuilder.Name = %v, want %v", got, want)
}

i := &insecureCredsBuilder{}
if _, err := i.Build(nil); err != nil {
t.Errorf("insecureCredsBuilder.Build failed: %v", err)
}

if got, want := i.Name(), "insecure"; got != want {
t.Errorf("insecureCredsBuilder.Name = %v, want %v", got, want)
}
}

0 comments on commit c7f7d3a

Please sign in to comment.