factory.go 4.3 KB

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