github.com/bazelbuild/remote-apis-sdks@v0.0.0-20240425170053-8a36686a6350/go/pkg/client/capabilities.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/bazelbuild/remote-apis-sdks/go/pkg/digest"
     7  	"github.com/pkg/errors"
     8  
     9  	repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
    10  )
    11  
    12  // CheckCapabilities verifies that this client can work with the remote server
    13  // in terms of API version and digest function. It sets some client parameters
    14  // according to remote server preferences, like MaxBatchSize.
    15  func (c *Client) CheckCapabilities(ctx context.Context) (err error) {
    16  	// Only query the server once. There is no need for a lock, because we will
    17  	// usually make the call on startup.
    18  	if c.serverCaps == nil {
    19  		if c.serverCaps, err = c.GetCapabilities(ctx); err != nil {
    20  			return err
    21  		}
    22  	}
    23  
    24  	if err := digest.CheckCapabilities(c.serverCaps); err != nil {
    25  		return errors.Wrapf(err, "digest function mismatch")
    26  	}
    27  
    28  	if c.serverCaps.CacheCapabilities != nil {
    29  		c.MaxBatchSize = MaxBatchSize(c.serverCaps.CacheCapabilities.MaxBatchTotalSizeBytes)
    30  	}
    31  
    32  	if useCompression := c.CompressedBytestreamThreshold >= 0; useCompression {
    33  		if c.serverCaps.CacheCapabilities.SupportedCompressors == nil {
    34  			return errors.New("the server does not support compression")
    35  		}
    36  
    37  		foundZstd := false
    38  		for _, sComp := range c.serverCaps.CacheCapabilities.SupportedCompressors {
    39  			if sComp == repb.Compressor_ZSTD {
    40  				foundZstd = true
    41  				break
    42  			}
    43  		}
    44  		if !foundZstd {
    45  			return errors.New("zstd is not supported by server, while the SDK only supports ZSTD compression")
    46  		}
    47  		for _, compressor := range c.serverCaps.CacheCapabilities.SupportedBatchUpdateCompressors {
    48  			if compressor == repb.Compressor_ZSTD {
    49  				c.useBatchCompression = UseBatchCompression(true)
    50  			}
    51  		}
    52  	}
    53  	return nil
    54  }
    55  
    56  // GetCapabilities returns the capabilities for the targeted servers.
    57  // If the CAS URL was set differently to the execution server then the CacheCapabilities will
    58  // be determined from that; ExecutionCapabilities will always come from the main URL.
    59  func (c *Client) GetCapabilities(ctx context.Context) (res *repb.ServerCapabilities, err error) {
    60  	return c.GetCapabilitiesForInstance(ctx, c.InstanceName)
    61  }
    62  
    63  // GetCapabilitiesForInstance returns the capabilities for the targeted servers.
    64  // If the CAS URL was set differently to the execution server then the CacheCapabilities will
    65  // be determined from that; ExecutionCapabilities will always come from the main URL.
    66  func (c *Client) GetCapabilitiesForInstance(ctx context.Context, instance string) (res *repb.ServerCapabilities, err error) {
    67  	req := &repb.GetCapabilitiesRequest{InstanceName: instance}
    68  	caps, err := c.GetBackendCapabilities(ctx, c.Connection, req)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	if c.CASConnection != c.Connection {
    73  		casCaps, err := c.GetBackendCapabilities(ctx, c.CASConnection, req)
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  		caps.CacheCapabilities = casCaps.CacheCapabilities
    78  	}
    79  	return caps, nil
    80  }
    81  
    82  // SupportsActionPlatformProperties returns whether the server's RE API version
    83  // supports the `Action.platform_properties` field.
    84  func (c *Client) SupportsActionPlatformProperties() bool {
    85  	return supportsActionPlatformProperties(c.serverCaps)
    86  }
    87  
    88  // SupportsCommandOutputPaths returns whether the server's RE API version
    89  // supports the `Command.action_paths` field.
    90  func (c *Client) SupportsCommandOutputPaths() bool {
    91  	return supportsCommandOutputPaths(c.serverCaps)
    92  }
    93  
    94  // HighAPIVersionNewerThanOrEqualTo returns whether the latest version reported
    95  // as supported in ServerCapabilities matches or is more recent than a
    96  // reference major/minor version.
    97  func highAPIVersionNewerThanOrEqualTo(serverCapabilities *repb.ServerCapabilities, major int32, minor int32) bool {
    98  	if serverCapabilities == nil {
    99  		return false
   100  	}
   101  
   102  	latestSupportedMajor := serverCapabilities.HighApiVersion.GetMajor()
   103  	latestSupportedMinor := serverCapabilities.HighApiVersion.GetMinor()
   104  
   105  	return (latestSupportedMajor > major || (latestSupportedMajor == major && latestSupportedMinor >= minor))
   106  }
   107  
   108  func supportsActionPlatformProperties(serverCapabilities *repb.ServerCapabilities) bool {
   109  	// According to the RE API spec:
   110  	// "New in version 2.2: clients SHOULD set these platform properties as well
   111  	// as those in the [Command][build.bazel.remote.execution.v2.Command].
   112  	// Servers SHOULD prefer those set here."
   113  	return highAPIVersionNewerThanOrEqualTo(serverCapabilities, 2, 2)
   114  }
   115  
   116  func supportsCommandOutputPaths(serverCapabilities *repb.ServerCapabilities) bool {
   117  	// According to the RE API spec:
   118  	// "[In v2.1 `output_paths`] supersedes the DEPRECATED `output_files` and
   119  	// `output_directories` fields. If `output_paths` is used, `output_files` and
   120  	// `output_directories` will be ignored!"
   121  	return highAPIVersionNewerThanOrEqualTo(serverCapabilities, 2, 1)
   122  }