factory.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package grpc
  14. import (
  15. "fmt"
  16. "time"
  17. "github.com/go-logr/logr"
  18. "google.golang.org/grpc"
  19. "google.golang.org/grpc/credentials"
  20. "google.golang.org/grpc/keepalive"
  21. ctrl "sigs.k8s.io/controller-runtime"
  22. pb "github.com/external-secrets/external-secrets/proto/provider"
  23. v2 "github.com/external-secrets/external-secrets/providers/v2/common"
  24. )
  25. // NewClient creates a new gRPC client that connects to the provider at the given address.
  26. // If tlsConfig is nil, an insecure connection is used (not recommended for production).
  27. // If log is nil, a default logger will be used.
  28. func NewClient(address string, tlsConfig *TLSConfig) (v2.Provider, error) {
  29. return NewClientWithLogger(address, tlsConfig, ctrl.Log.WithName("grpc-client"))
  30. }
  31. // NewClientWithLogger creates a new gRPC client with a custom logger.
  32. func NewClientWithLogger(address string, tlsConfig *TLSConfig, log logr.Logger) (v2.Provider, error) {
  33. if address == "" {
  34. return nil, fmt.Errorf("provider address cannot be empty")
  35. }
  36. log.Info("creating gRPC client",
  37. "address", address,
  38. "tlsEnabled", tlsConfig != nil)
  39. // Set up connection options.
  40. opts := make([]grpc.DialOption, 0, 2)
  41. opts = append(opts,
  42. grpc.WithKeepaliveParams(keepalive.ClientParameters{
  43. Time: 10 * time.Second, // Send keepalive pings every 10 seconds
  44. Timeout: 5 * time.Second, // Wait 5 seconds for ping ack
  45. PermitWithoutStream: true, // Allow pings when no streams are active
  46. }),
  47. )
  48. log.V(1).Info("configured keepalive parameters",
  49. "time", "10s",
  50. "timeout", "5s",
  51. "permitWithoutStream", true)
  52. // Configure TLS or insecure credentials
  53. if tlsConfig == nil {
  54. return nil, fmt.Errorf("tlsConfig cannot be nil; insecure connections are not allowed in production")
  55. }
  56. log.V(1).Info("configuring TLS",
  57. "serverName", tlsConfig.ServerName,
  58. "hasCACert", len(tlsConfig.CACert) > 0,
  59. "hasClientCert", len(tlsConfig.ClientCert) > 0,
  60. "hasClientKey", len(tlsConfig.ClientKey) > 0)
  61. grpcTLSConfig, err := tlsConfig.ToGRPCTLSConfig()
  62. if err != nil {
  63. log.Error(err, "failed to create TLS config")
  64. return nil, fmt.Errorf("failed to create TLS config: %w", err)
  65. }
  66. opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(grpcTLSConfig)))
  67. log.Info("TLS configured successfully")
  68. // Dial the provider
  69. log.Info("dialing provider", "address", address)
  70. conn, err := grpc.Dial(address, opts...)
  71. if err != nil {
  72. log.Error(err, "failed to dial provider", "address", address)
  73. return nil, fmt.Errorf("failed to dial provider at %s: %w", address, err)
  74. }
  75. log.Info("gRPC connection established",
  76. "address", address,
  77. "target", conn.Target(),
  78. "state", conn.GetState().String())
  79. // Create the gRPC client stub
  80. grpcClient := pb.NewSecretStoreProviderClient(conn)
  81. return &grpcProviderClient{
  82. conn: conn,
  83. client: grpcClient,
  84. log: log.WithValues("target", conn.Target()),
  85. }, nil
  86. }
  87. // NewClientWithConn creates a client from an existing gRPC connection.
  88. // This is useful for testing or when you need more control over connection setup.
  89. func NewClientWithConn(conn *grpc.ClientConn) v2.Provider {
  90. return &grpcProviderClient{
  91. conn: conn,
  92. client: pb.NewSecretStoreProviderClient(conn),
  93. log: ctrl.Log.WithName("grpc-client").WithValues("target", conn.Target()),
  94. }
  95. }
  96. // NewResilientProviderClient creates a production-ready provider client with
  97. // connection pooling, retry logic, and circuit breaking.
  98. // This is the recommended way to create provider clients for production use.
  99. func NewResilientProviderClient(address string, tlsConfig *TLSConfig) (v2.Provider, error) {
  100. config := DefaultResilientClientConfig(address, tlsConfig)
  101. return NewResilientClient(config)
  102. }
  103. // NewResilientProviderClientWithConfig creates a resilient provider client with custom configuration.
  104. func NewResilientProviderClientWithConfig(config ResilientClientConfig) (v2.Provider, error) {
  105. return NewResilientClient(config)
  106. }