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

Add an experimental googleclientauth extension #703

Merged
merged 3 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions extension/googleclientauthextension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Authenticator - Google Client Credentials

| Status | |
| ------------- |-----------|
| Stability | [alpha] |

[alpha]: https://github.com/open-telemetry/opentelemetry-collector#alpha

This extension provides Google OAuth2 Client Credentials and Metadata for gRPC and http based exporters.

The authenticator type has to be set to `googleclientauth`.

## Configuration

```yaml
extensions:
googleclientauth:

receivers:
otlp:
protocols:
grpc:

exporters:
otlp/withauth:
endpoint: 0.0.0.0:5000
ca_file: /tmp/certs/ca.pem
auth:
authenticator: googleclientauth

service:
extensions: [googleclientauth]
pipelines:
metrics:
receivers: [otlp]
processors: []
exporters: [otlp/withauth]
```

Following are the configuration fields:
- **project** - The Google Cloud Project telemetry is sent to if the gcp.project.id resource attribute is not set. If unspecified, this is determined using application default credentials.
- [**scopes**](https://datatracker.ietf.org/doc/html/rfc6749#section-3.3) - The resource server's token endpoint URLs.
dashpole marked this conversation as resolved.
Show resolved Hide resolved
- [**quota_project**](https://cloud.google.com/apis/docs/system-parameters) - The project for quota and billing purposes. The caller must have serviceusage.services.use permission on the project.

## Building into a Collector

This extension is not included in any collector distributions today. To build a collector with this component, add the following to your collector builder configuration:

```yaml
extensions:
- import: github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension
gomod: github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension v0.43.0
```
61 changes: 61 additions & 0 deletions extension/googleclientauthextension/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2023 Google LLC
//
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"go.opentelemetry.io/collector/component"
)

// Config stores the configuration for GCP Client Credentials.
type Config struct {
// Project is the project telemetry is sent to if the gcp.project.id
// resource attribute is not set. If unspecified, this is determined using
// application default credentials.
Project string `mapstructure:"project"`

// QuotaProject specifies a project for quota and billing purposes. The
// caller must have serviceusage.services.use permission on the project.
//
// For more information please read:
// https://cloud.google.com/apis/docs/system-parameters
QuotaProject string `mapstructure:"quota_project"`

// Scope specifies optional requested permissions.
// See https://datatracker.ietf.org/doc/html/rfc6749#section-3.3
Scopes []string `mapstructure:"scopes,omitempty"`

// TODO: Support impersonation, similar to what exists in the googlecloud collector exporter.
}

var _ component.Config = (*Config)(nil)

// Validate checks if the extension configuration is valid.
func (cfg *Config) Validate() error {
return nil
}

// defaultScopes are the scopes required for writing logs, metrics, and traces.
var defaultScopes = []string{
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring.write",
"https://www.googleapis.com/auth/trace.append",
}

func createDefaultConfig() component.Config {
return &Config{
Scopes: defaultScopes,
}
}
57 changes: 57 additions & 0 deletions extension/googleclientauthextension/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2023 Google LLC
//
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap/confmaptest"

"github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension/internal/metadata"
)

func TestLoadConfig(t *testing.T) {
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
require.NoError(t, err)
factory := NewFactory()
cfg := factory.CreateDefaultConfig()

sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "").String())
require.NoError(t, err)
require.NoError(t, component.UnmarshalConfig(sub, cfg))

assert.Equal(t, cfg.(*Config), factory.CreateDefaultConfig().(*Config))

sub, err = cm.Sub(component.NewIDWithName(metadata.Type, "customname").String())
require.NoError(t, err)
require.NoError(t, component.UnmarshalConfig(sub, cfg))

assert.Equal(t,
&Config{
Project: "my-project",
Scopes: []string{"https://www.something.com/hello", "https://www.something.com/world"},
QuotaProject: "other-project",
},
cfg,
)
}

func TestValidate(t *testing.T) {
assert.NoError(t, NewFactory().CreateDefaultConfig().(*Config).Validate())
}
17 changes: 17 additions & 0 deletions extension/googleclientauthextension/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2023 Google LLC
//
// 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 googleclientauthextension provides gRPC and HTTP credentials and metadata
// using Application Default Credentials: https://cloud.google.com/docs/authentication#adc
package googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"
88 changes: 88 additions & 0 deletions extension/googleclientauthextension/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2023 Google LLC
//
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"context"
"encoding/json"
"errors"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/extension"
"go.opentelemetry.io/collector/extension/auth"
"golang.org/x/oauth2/google"
"google.golang.org/grpc/credentials/oauth"

"github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension/internal/metadata"
)

// NewFactory creates a factory for the GCP Auth extension.
func NewFactory() extension.Factory {
return extension.NewFactory(
metadata.Type,
createDefaultConfig,
createExtension,
metadata.ExtensionStability,
)
}

func createExtension(ctx context.Context, set extension.CreateSettings, cfg component.Config) (extension.Extension, error) {
config := cfg.(*Config)
creds, err := google.FindDefaultCredentials(ctx, config.Scopes...)
if err != nil {
return nil, err
}
if config.Project == "" {
config.Project = creds.ProjectID
}
if config.Project == "" {
return nil, errors.New("no project set in config or found with application default credentials")
}
if config.QuotaProject == "" {
config.QuotaProject = quotaProjectFromCreds(creds)
}

ca := clientAuthenticator{
TokenSource: oauth.TokenSource{TokenSource: creds.TokenSource},
config: config,
}

return auth.NewClient(
auth.WithClientRoundTripper(ca.roundTripper),
auth.WithClientPerRPCCredentials(ca.perRPCCredentials),
), nil
}

// clientAuthenticator supplies credentials from an oauth.TokenSource.
type clientAuthenticator struct {
oauth.TokenSource
config *Config
}

// quotaProjectFromCreds retrieves quota project from the credentials file.
// Based on how the google go client gets quota project:
// https://github.com/googleapis/google-api-go-client/blob/113082d14d54f188d1b6c34c652e416592fc51b5/internal/creds.go#L159
func quotaProjectFromCreds(creds *google.Credentials) string {
if creds == nil {
return ""
}
var v struct {
QuotaProject string `json:"quota_project_id"`
}
if err := json.Unmarshal(creds.JSON, &v); err != nil {
return ""
}
return v.QuotaProject
}
43 changes: 43 additions & 0 deletions extension/googleclientauthextension/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2023 Google LLC
//
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/extension"
)

func TestCreateDefaultConfig(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")
assert.NoError(t, componenttest.CheckConfigStruct(cfg))
}

func TestNewFactory(t *testing.T) {
f := NewFactory()
assert.NotNil(t, f)
}

func TestCreateExtension(t *testing.T) {
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "testdata/fake_creds.json")
ext, err := NewFactory().CreateExtension(context.Background(), extension.CreateSettings{}, createDefaultConfig())
assert.NotNil(t, ext)
assert.NoError(t, err)
}
48 changes: 48 additions & 0 deletions extension/googleclientauthextension/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension

go 1.19

require (
github.com/stretchr/testify v1.8.4
go.opentelemetry.io/collector/component v0.82.0
go.opentelemetry.io/collector/confmap v0.82.0
go.opentelemetry.io/collector/extension v0.82.0
go.opentelemetry.io/collector/extension/auth v0.82.0
golang.org/x/oauth2 v0.10.0
google.golang.org/grpc v1.57.0
)

require (
cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/knadh/koanf v1.5.0 // indirect
github.com/knadh/koanf/v2 v2.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/collector/config/configtelemetry v0.82.0 // indirect
go.opentelemetry.io/collector/featuregate v1.0.0-rcv0014 // indirect
go.opentelemetry.io/collector/pdata v1.0.0-rcv0014 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

retract (
v0.76.2
v0.76.1
v0.65.0
)
Loading