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 }