Skip to content

Commit

Permalink
Merge pull request #63 from redpanda-data/conn-improvements
Browse files Browse the repository at this point in the history
[bug] Reuse and close connections per resource
  • Loading branch information
r-vasquez authored Mar 25, 2024
2 parents dff67f2 + 6a5d24d commit 4549f76
Show file tree
Hide file tree
Showing 19 changed files with 215 additions and 420 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ require (
github.com/hashicorp/terraform-plugin-go v0.21.0
github.com/hashicorp/terraform-plugin-testing v1.6.0
github.com/redpanda-data/redpanda/src/go/rpk v0.0.0-20240122210157-821bd6d1fd31
golang.org/x/sync v0.6.0
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe
google.golang.org/grpc v1.61.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,6 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
60 changes: 0 additions & 60 deletions redpanda/clients/controlplane.go

This file was deleted.

73 changes: 0 additions & 73 deletions redpanda/clients/dataplane.go

This file was deleted.

40 changes: 18 additions & 22 deletions redpanda/clients/clients.go → redpanda/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package clients provides the CloudV2 clients used by the Redpanda terraform
// provider and the generated resources.
package clients
// Package cloud provides the methods to connect and talk to the Redpanda Cloud
// public API.
package cloud

import (
"context"
Expand All @@ -36,15 +36,15 @@ import (
"google.golang.org/grpc/metadata"
)

// cloudEndpoint is a representation of a cloud V2 endpoint, containing the URLs
// for authentication and the API URL.
type cloudEndpoint struct {
apiURL string // CloudV2 public API URL.
// Endpoint is a representation of a cloud endpoint for a single environment. It
// contains the URLs, audience for authentication and the API URL.
type Endpoint struct {
APIURL string // CloudV2 public API URL.
authURL string // CloudV2 URL for authorization token exchange.
audience string // CloudV2 audience used for token exchange.
}

var cloudAuthEnvironments = map[string]cloudEndpoint{
var endpoints = map[string]Endpoint{
"dev": {
"api.dev.cloud.redpanda.com:443",
"https://dev-cloudv2.us.auth0.com/oauth/token",
Expand All @@ -62,33 +62,27 @@ var cloudAuthEnvironments = map[string]cloudEndpoint{
},
}

// ClientRequest are the client request credentials used to create a connection.
type ClientRequest struct {
ClientID string
ClientSecret string
// TODO: we can use this as the only source of truth for Client Credentials and Envs.
}

type tokenResponse struct {
AccessToken string `json:"access_token"`
Scope string `json:"scope"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
}

// requestTokenAndEnv requests a token.
func requestTokenAndEnv(ctx context.Context, cloudEnv string, cr ClientRequest) (string, *cloudEndpoint, error) {
if cr.ClientID == "" {
// RequestTokenAndEnv requests an authentication token and return the Endpoint
// for a given environment.
func RequestTokenAndEnv(ctx context.Context, cloudEnv, clientID, clientSecret string) (string, *Endpoint, error) {
if clientID == "" {
return "", nil, fmt.Errorf("client_id is not set")
}
if cr.ClientSecret == "" {
if clientSecret == "" {
return "", nil, fmt.Errorf("client_secret is not set")
}
endpoint, found := cloudAuthEnvironments[cloudEnv]
endpoint, found := endpoints[cloudEnv]
if !found {
return "", nil, fmt.Errorf("unable to find requested environment: %q", cloudEnv)
}
payload := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&audience=%s", cr.ClientID, cr.ClientSecret, endpoint.audience)
payload := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&audience=%s", clientID, clientSecret, endpoint.audience)
req, err := http.NewRequestWithContext(ctx, "POST", endpoint.authURL, strings.NewReader(payload))
if err != nil {
return "", nil, fmt.Errorf("unable to issue request to %v: %v", endpoint.authURL, err)
Expand Down Expand Up @@ -118,7 +112,9 @@ func requestTokenAndEnv(ctx context.Context, cloudEnv string, cr ClientRequest)
return tokenContainer.AccessToken, &endpoint, nil
}

func spawnConn(ctx context.Context, url string, authToken string) (*grpc.ClientConn, error) {
// SpawnConn returns a grpc connection to the given URL, it adds a bearer token
// to each request with the given 'authToken'.
func SpawnConn(ctx context.Context, url string, authToken string) (*grpc.ClientConn, error) {
return grpc.DialContext(
ctx,
url,
Expand Down
42 changes: 42 additions & 0 deletions redpanda/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2024 Redpanda Data, Inc.
//
//
// 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 config contains the configuration structs to initialize our clients
// and provider.
package config

import "google.golang.org/grpc"

// Resource is the config used to pass data and dependencies to resource
// implementations.
type Resource struct {
AuthToken string
ClientID string
ClientSecret string
CloudEnv string
ControlPlaneConnection *grpc.ClientConn
}

// Datasource is the config used to pass data and dependencies to data source
// implementations.
type Datasource struct {
AuthToken string
ClientID string
ClientSecret string
CloudEnv string
ControlPlaneConnection *grpc.ClientConn
}

// TODO add cloud provider and region as values to persist
40 changes: 31 additions & 9 deletions redpanda/redpanda.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ import (
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/cloud"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/config"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/models"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/resources/acl"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/resources/cluster"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/resources/namespace"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/resources/network"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/resources/topic"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/resources/user"
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/utils"
"google.golang.org/grpc"
)

// Ensure provider defined types fully satisfy framework interfaces.
Expand All @@ -46,6 +48,8 @@ type Redpanda struct {
cloudEnv string
// version is the Redpanda terraform provider.
version string
// conn is the connection to the control plane API.
conn *grpc.ClientConn
}

const (
Expand Down Expand Up @@ -118,15 +122,33 @@ func (r *Redpanda) Configure(ctx context.Context, request provider.ConfigureRequ
}
// Clients are passed through to downstream resources through the response
// struct.
response.ResourceData = utils.ResourceData{
ClientID: id,
ClientSecret: sec,
CloudEnv: r.cloudEnv,
token, endpoint, err := cloud.RequestTokenAndEnv(ctx, r.cloudEnv, id, sec)
if err != nil {
response.Diagnostics.AddError("failed to authenticate with Redpanda API", err.Error())
return
}
if r.conn == nil {
conn, err := cloud.SpawnConn(ctx, endpoint.APIURL, token)
if err != nil {
response.Diagnostics.AddError("failed to open a connection with the Redpanda Cloud API", err.Error())
return
}
r.conn = conn
}

response.ResourceData = config.Resource{
AuthToken: token,
ClientID: id,
ClientSecret: sec,
CloudEnv: r.cloudEnv,
ControlPlaneConnection: r.conn,
}
response.DataSourceData = utils.DatasourceData{
ClientID: id,
ClientSecret: sec,
CloudEnv: r.cloudEnv,
response.DataSourceData = config.Datasource{
AuthToken: token,
ClientID: id,
ClientSecret: sec,
CloudEnv: r.cloudEnv,
ControlPlaneConnection: r.conn,
}
}

Expand Down
Loading

0 comments on commit 4549f76

Please sign in to comment.