Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xds/bootstrap: add plugin system for credentials specified in bootstrap file #5136

Merged
merged 26 commits into from Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6a1259c
Support xDS credentials plugin.
anicr7 Jan 15, 2022
1e6e23b
Remove the blank line after the comment and the package statement.
anicr7 Jan 18, 2022
5f60e4b
Remove blank line from Builder interface
anicr7 Jan 28, 2022
9808f0c
Use "Get" as the getter name to be consistent with current
anicr7 Jan 28, 2022
b4f79bc
Add method to return name associated with registered builder
anicr7 Jan 28, 2022
116361c
Allow credentials builder to return error due to JSON config issues.
anicr7 Jan 28, 2022
da005ea
Move the credentials registry into bootstrap, delete previously added
anicr7 Jan 28, 2022
3007940
Fix comment header for RegisterCredentials
anicr7 Jan 28, 2022
497bec3
Move the packages around for better organization as per the suggestions.
anicr7 Jan 31, 2022
1a5ce53
Don't export creds builders. Remove google/insecure and use
anicr7 Jan 31, 2022
2a95e62
Add experimental labels around xds/bootstrap package
anicr7 Jan 31, 2022
0c1de51
Move the CredsBuilders and registration back to internal/bootstrap.go.
anicr7 Jan 31, 2022
0251470
Separate GetCredentials and Build.
anicr7 Jan 31, 2022
b132d05
Fix comment for googleDefaultCredsBuilder
anicr7 Jan 31, 2022
c4dcabf
Add else statement warning about non registered creds
anicr7 Feb 1, 2022
e83cb32
Simplify unit tests
anicr7 Feb 1, 2022
f72d3db
Remove warning log when credential is not registered
anicr7 Feb 1, 2022
a7eae17
Address review comments.
anicr7 Feb 1, 2022
990f2f4
Combine couple of if conditions
anicr7 Feb 1, 2022
362d1e0
Use consts in unit tests
anicr7 Feb 1, 2022
37ea507
Revert comments from creds back to credentials. Also change insecureB…
anicr7 Feb 1, 2022
711c075
Change log warning to returning an error instead, when building bundl…
anicr7 Feb 1, 2022
256872a
Remove extra comments and fix one comment to use credential instead o…
anicr7 Feb 1, 2022
1fa6ba1
Move initialization to global context
anicr7 Feb 1, 2022
7685e5f
Remove error tag from the error message
anicr7 Feb 1, 2022
9895c8c
Remove _ for single parameter functions
anicr7 Feb 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: since this method takes no other parameters, you could skip the _.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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()
dfawley marked this conversation as resolved.
Show resolved Hide resolved
}
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.
anicr7 marked this conversation as resolved.
Show resolved Hide resolved
//
// 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.
dfawley marked this conversation as resolved.
Show resolved Hide resolved
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here and in other places. The _ can be skipped since it is the only parameter to the method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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
}
anicr7 marked this conversation as resolved.
Show resolved Hide resolved
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)
}
}