@@ -19,12 +19,16 @@ import (
1919 "runtime"
2020 "slices"
2121 "strings"
22+ "sync"
2223 "testing"
2324 "time"
2425
2526 cacheimporttypes "github.com/moby/buildkit/cache/remotecache/v1/types"
2627 "github.com/tonistiigi/fsutil"
2728 "golang.org/x/sync/errgroup"
29+ "google.golang.org/grpc"
30+ grpcgzip "google.golang.org/grpc/encoding/gzip"
31+ "google.golang.org/grpc/stats"
2832 "google.golang.org/grpc/status"
2933 "google.golang.org/protobuf/proto"
3034 "google.golang.org/protobuf/types/known/anypb"
@@ -141,6 +145,7 @@ var allTests = integration.TestFuncs(
141145 testFrontendUseForwardedSolveResults ,
142146 testFrontendEvaluate ,
143147 testFrontendInputs ,
148+ testFrontendInputsCompressed ,
144149 testErrorsSourceMap ,
145150 testMultiArgs ,
146151 testFrontendSubrequests ,
@@ -7340,6 +7345,115 @@ COPY foo foo2
73407345 require .Equal (t , expected , actual )
73417346}
73427347
7348+ type inputsPayloadStats struct {
7349+ mu sync.Mutex
7350+ payloads int
7351+ inCompressions []string
7352+ outCompressions []string
7353+ }
7354+
7355+ type inputsPayloadStatsMethodKey struct {}
7356+
7357+ func (s * inputsPayloadStats ) TagRPC (ctx context.Context , info * stats.RPCTagInfo ) context.Context {
7358+ return context .WithValue (ctx , inputsPayloadStatsMethodKey {}, info .FullMethodName )
7359+ }
7360+
7361+ func (s * inputsPayloadStats ) HandleRPC (ctx context.Context , stat stats.RPCStats ) {
7362+ method , _ := ctx .Value (inputsPayloadStatsMethodKey {}).(string )
7363+ if ! strings .HasSuffix (method , "/LLBBridge/Inputs" ) {
7364+ return
7365+ }
7366+ switch stat := stat .(type ) {
7367+ case * stats.InHeader :
7368+ if stat .Client {
7369+ s .mu .Lock ()
7370+ defer s .mu .Unlock ()
7371+ s .inCompressions = append (s .inCompressions , stat .Compression )
7372+ }
7373+ case * stats.OutHeader :
7374+ if stat .Client {
7375+ s .mu .Lock ()
7376+ defer s .mu .Unlock ()
7377+ s .outCompressions = append (s .outCompressions , stat .Compression )
7378+ }
7379+ case * stats.InPayload :
7380+ if stat .Client {
7381+ s .mu .Lock ()
7382+ defer s .mu .Unlock ()
7383+ s .payloads ++
7384+ }
7385+ }
7386+ }
7387+
7388+ func (s * inputsPayloadStats ) TagConn (ctx context.Context , _ * stats.ConnTagInfo ) context.Context {
7389+ return ctx
7390+ }
7391+
7392+ func (s * inputsPayloadStats ) HandleConn (context.Context , stats.ConnStats ) {}
7393+
7394+ func (s * inputsPayloadStats ) inputsPayloadCount () int {
7395+ s .mu .Lock ()
7396+ defer s .mu .Unlock ()
7397+ return s .payloads
7398+ }
7399+
7400+ func (s * inputsPayloadStats ) inputsCompressions () (in , out []string ) {
7401+ s .mu .Lock ()
7402+ defer s .mu .Unlock ()
7403+ return slices .Clone (s .inCompressions ), slices .Clone (s .outCompressions )
7404+ }
7405+
7406+ func testFrontendInputsCompressed (t * testing.T , sb integration.Sandbox ) {
7407+ integration .SkipOnPlatform (t , "windows" )
7408+ f := getFrontend (t , sb )
7409+ if _ , ok := f .(* clientFrontend ); ! ok {
7410+ t .Skip ("only test with client frontend" )
7411+ }
7412+
7413+ payloadStats := & inputsPayloadStats {}
7414+ c , err := client .New (sb .Context (), sb .Address (), client .WithGRPCDialOption (grpc .WithStatsHandler (payloadStats )))
7415+ require .NoError (t , err )
7416+ defer c .Close ()
7417+
7418+ payload := bytes .Repeat ([]byte ("a" ), 64 << 10 )
7419+ input := llb .Scratch ().File (llb .Mkfile ("/payload" , 0600 , payload ))
7420+
7421+ destDir := t .TempDir ()
7422+ dockerfile := []byte (`
7423+ FROM scratch
7424+ COPY payload payload
7425+ ` )
7426+ dir := integration .Tmpdir (
7427+ t ,
7428+ fstest .CreateFile ("Dockerfile" , dockerfile , 0600 ),
7429+ )
7430+
7431+ _ , err = f .Solve (sb .Context (), c , client.SolveOpt {
7432+ Exports : []client.ExportEntry {
7433+ {
7434+ Type : client .ExporterLocal ,
7435+ OutputDir : destDir ,
7436+ },
7437+ },
7438+ LocalMounts : map [string ]fsutil.FS {
7439+ dockerui .DefaultLocalNameDockerfile : dir ,
7440+ },
7441+ FrontendInputs : map [string ]llb.State {
7442+ dockerui .DefaultLocalNameContext : input ,
7443+ },
7444+ }, nil )
7445+ require .NoError (t , err )
7446+
7447+ actual , err := os .ReadFile (filepath .Join (destDir , "payload" ))
7448+ require .NoError (t , err )
7449+ require .Equal (t , payload , actual )
7450+
7451+ inCompressions , outCompressions := payloadStats .inputsCompressions ()
7452+ require .Contains (t , outCompressions , grpcgzip .Name , "expected Inputs request to use gzip compression" )
7453+ require .Contains (t , inCompressions , grpcgzip .Name , "expected Inputs response to use gzip compression" )
7454+ require .NotZero (t , payloadStats .inputsPayloadCount (), "expected an Inputs response payload" )
7455+ }
7456+
73437457func testFrontendSubrequests (t * testing.T , sb integration.Sandbox ) {
73447458 f := getFrontend (t , sb )
73457459 if _ , ok := f .(* clientFrontend ); ! ok {
0 commit comments