github.com/tilt-dev/tilt@v0.36.0/cmd/buildkitapi/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "flag" 7 "io" 8 "log" 9 "net" 10 "os" 11 "path/filepath" 12 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/client" 15 "github.com/docker/docker/pkg/jsonmessage" 16 controlapi "github.com/moby/buildkit/api/services/control" 17 "github.com/moby/buildkit/identity" 18 "github.com/moby/buildkit/session" 19 "github.com/moby/buildkit/session/filesync" 20 "github.com/pkg/errors" 21 "github.com/tonistiigi/fsutil" 22 fsutiltypes "github.com/tonistiigi/fsutil/types" 23 ) 24 25 var useCache bool 26 var useLegacyAPI bool 27 var contextDir string 28 29 // A small utility for running Buildkit on the dockerfile 30 // in the current directory printing out all the buildkit api 31 // response protobufs. 32 func main() { 33 flag.BoolVar(&useCache, "cache", false, "Enable docker caching") 34 flag.BoolVar(&useLegacyAPI, "legacy", false, "Print legacy build events") 35 flag.StringVar(&contextDir, "context", "", "Context directory") 36 flag.Parse() 37 38 err := run() 39 if err != nil { 40 log.Fatal(err) 41 } 42 } 43 44 func run() error { 45 ctx := context.Background() 46 d, err := client.NewEnvClient() 47 if err != nil { 48 return err 49 } 50 51 d.NegotiateAPIVersion(ctx) 52 53 session, err := session.NewSession(ctx, identity.NewID()) 54 if err != nil { 55 return err 56 } 57 58 fileMap := func(path string, s *fsutiltypes.Stat) fsutil.MapResult { 59 s.Uid = 0 60 s.Gid = 0 61 return fsutil.MapResultKeep 62 } 63 64 dir, _ := os.Getwd() 65 if contextDir == "" { 66 contextDir = dir 67 } 68 if !filepath.IsAbs(contextDir) { 69 contextDir = filepath.Join(dir, contextDir) 70 } 71 72 contextFS, err := fsutil.NewFS(contextDir) 73 if err != nil { 74 return err 75 } 76 77 contextFS, err = fsutil.NewFilterFS(contextFS, &fsutil.FilterOpt{ 78 Map: fileMap, 79 }) 80 if err != nil { 81 return err 82 } 83 84 dockerfileFS, err := fsutil.NewFS(dir) 85 if err != nil { 86 return err 87 } 88 89 session.Allow(filesync.NewFSSyncProvider(filesync.StaticDirSource{ 90 "context": contextFS, 91 "dockerfile": dockerfileFS, 92 })) 93 94 go func() { 95 defer func() { 96 _ = session.Close() 97 }() 98 99 // Start the server 100 dialSession := func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { 101 return d.DialHijack(ctx, "/session", proto, meta) 102 } 103 _ = session.Run(ctx, dialSession) 104 }() 105 106 opts := types.ImageBuildOptions{} 107 opts.Version = types.BuilderBuildKit 108 opts.Dockerfile = "Dockerfile" 109 opts.RemoteContext = "client-session" 110 opts.SessionID = session.ID() 111 if !useCache { 112 opts.NoCache = true 113 } 114 defer session.Close() 115 116 response, err := d.ImageBuild(ctx, nil, opts) 117 if err != nil { 118 return err 119 } 120 defer func() { 121 _ = response.Body.Close() 122 }() 123 124 return readDockerOutput(ctx, response.Body) 125 } 126 127 func readDockerOutput(ctx context.Context, reader io.Reader) error { 128 decoder := json.NewDecoder(reader) 129 130 for decoder.More() { 131 message := jsonmessage.JSONMessage{} 132 err := decoder.Decode(&message) 133 if err != nil { 134 return errors.Wrap(err, "decoding docker output") 135 } 136 137 isFromBuildkit := messageIsFromBuildkit(message) 138 if isFromBuildkit && !useLegacyAPI { 139 err := writeBuildkitStatus(message.Aux) 140 if err != nil { 141 return err 142 } 143 } else if !isFromBuildkit && useLegacyAPI { 144 err := json.NewEncoder(os.Stdout).Encode(message) 145 if err != nil { 146 return err 147 } 148 } 149 } 150 return nil 151 } 152 153 func writeBuildkitStatus(aux *json.RawMessage) error { 154 var resp controlapi.StatusResponse 155 var dt []byte 156 if err := json.Unmarshal(*aux, &dt); err != nil { 157 return err 158 } 159 if err := (&resp).UnmarshalVT(dt); err != nil { 160 return err 161 } 162 163 return json.NewEncoder(os.Stdout).Encode(&resp) 164 } 165 166 func messageIsFromBuildkit(msg jsonmessage.JSONMessage) bool { 167 return msg.ID == "moby.buildkit.trace" 168 }