package client import ( "context" "fmt" "sync" grpcService "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/services/tree" "google.golang.org/grpc" ) type TreeClient struct { mu sync.RWMutex address string opts []grpc.DialOption conn *grpc.ClientConn service grpcService.TreeServiceClient dialed bool } // NewTreeClient creates new tree client with auto dial. func NewTreeClient(addr string, opts ...grpc.DialOption) *TreeClient { return &TreeClient{ address: addr, opts: opts, } } func (c *TreeClient) dial(ctx context.Context) error { c.mu.Lock() defer c.mu.Unlock() if c.dialed { return fmt.Errorf("couldn't dial '%s': connection already established", c.address) } conn, err := grpc.Dial(c.address, c.opts...) if err != nil { return fmt.Errorf("grpc dial node tree service: %w", err) } serviceClient := grpcService.NewTreeServiceClient(conn) if _, err = serviceClient.Healthcheck(ctx, &grpcService.HealthcheckRequest{}); err != nil { return fmt.Errorf("healthcheck tree service: %w", err) } c.conn = conn c.service = serviceClient c.dialed = true return nil } func (c *TreeClient) TreeClient(ctx context.Context) (grpcService.TreeServiceClient, error) { c.mu.RLock() dialed := c.dialed c.mu.RUnlock() if !dialed { if err := c.dial(ctx); err != nil { return nil, err } } return c.service, nil } func (c *TreeClient) Address() string { return c.address }