github.com/rish1988/moby@v25.0.2+incompatible/builder/builder-next/controller.go (about) 1 package buildkit 2 3 import ( 4 "context" 5 "net/http" 6 "os" 7 "path/filepath" 8 "runtime" 9 "time" 10 11 ctd "github.com/containerd/containerd" 12 "github.com/containerd/containerd/content/local" 13 ctdmetadata "github.com/containerd/containerd/metadata" 14 "github.com/containerd/containerd/platforms" 15 "github.com/containerd/containerd/snapshots" 16 "github.com/containerd/log" 17 "github.com/docker/docker/api/types" 18 "github.com/docker/docker/api/types/filters" 19 "github.com/docker/docker/builder/builder-next/adapters/containerimage" 20 "github.com/docker/docker/builder/builder-next/adapters/localinlinecache" 21 "github.com/docker/docker/builder/builder-next/adapters/snapshot" 22 "github.com/docker/docker/builder/builder-next/exporter/mobyexporter" 23 "github.com/docker/docker/builder/builder-next/imagerefchecker" 24 mobyworker "github.com/docker/docker/builder/builder-next/worker" 25 wlabel "github.com/docker/docker/builder/builder-next/worker/label" 26 "github.com/docker/docker/daemon/config" 27 "github.com/docker/docker/daemon/graphdriver" 28 units "github.com/docker/go-units" 29 "github.com/moby/buildkit/cache" 30 "github.com/moby/buildkit/cache/metadata" 31 "github.com/moby/buildkit/cache/remotecache" 32 "github.com/moby/buildkit/cache/remotecache/gha" 33 inlineremotecache "github.com/moby/buildkit/cache/remotecache/inline" 34 localremotecache "github.com/moby/buildkit/cache/remotecache/local" 35 registryremotecache "github.com/moby/buildkit/cache/remotecache/registry" 36 "github.com/moby/buildkit/client" 37 bkconfig "github.com/moby/buildkit/cmd/buildkitd/config" 38 "github.com/moby/buildkit/control" 39 "github.com/moby/buildkit/frontend" 40 dockerfile "github.com/moby/buildkit/frontend/dockerfile/builder" 41 "github.com/moby/buildkit/frontend/gateway" 42 "github.com/moby/buildkit/frontend/gateway/forwarder" 43 containerdsnapshot "github.com/moby/buildkit/snapshot/containerd" 44 "github.com/moby/buildkit/solver" 45 "github.com/moby/buildkit/solver/bboltcachestorage" 46 "github.com/moby/buildkit/util/archutil" 47 "github.com/moby/buildkit/util/entitlements" 48 "github.com/moby/buildkit/util/network/netproviders" 49 "github.com/moby/buildkit/util/tracing/detect" 50 "github.com/moby/buildkit/worker" 51 "github.com/moby/buildkit/worker/containerd" 52 "github.com/moby/buildkit/worker/label" 53 "github.com/pkg/errors" 54 "go.etcd.io/bbolt" 55 bolt "go.etcd.io/bbolt" 56 "go.opentelemetry.io/otel/sdk/trace" 57 58 "github.com/moby/buildkit/solver/pb" 59 "github.com/moby/buildkit/util/apicaps" 60 ) 61 62 func newController(ctx context.Context, rt http.RoundTripper, opt Opt) (*control.Controller, error) { 63 if opt.UseSnapshotter { 64 return newSnapshotterController(ctx, rt, opt) 65 } 66 return newGraphDriverController(ctx, rt, opt) 67 } 68 69 func getTraceExporter(ctx context.Context) trace.SpanExporter { 70 exp, err := detect.Exporter() 71 if err != nil { 72 log.G(ctx).WithError(err).Error("Failed to detect trace exporter for buildkit controller") 73 } 74 return exp 75 } 76 77 func newSnapshotterController(ctx context.Context, rt http.RoundTripper, opt Opt) (*control.Controller, error) { 78 if err := os.MkdirAll(opt.Root, 0o711); err != nil { 79 return nil, err 80 } 81 82 historyDB, historyConf, err := openHistoryDB(opt.Root, opt.BuilderConfig.History) 83 if err != nil { 84 return nil, err 85 } 86 87 cacheStorage, err := bboltcachestorage.NewStore(filepath.Join(opt.Root, "cache.db")) 88 if err != nil { 89 return nil, err 90 } 91 92 nc := netproviders.Opt{ 93 Mode: "host", 94 } 95 96 // HACK! Windows doesn't have 'host' mode networking. 97 if runtime.GOOS == "windows" { 98 nc = netproviders.Opt{ 99 Mode: "auto", 100 } 101 } 102 103 dns := getDNSConfig(opt.DNSConfig) 104 105 wo, err := containerd.NewWorkerOpt(opt.Root, opt.ContainerdAddress, opt.Snapshotter, opt.ContainerdNamespace, 106 opt.Rootless, map[string]string{ 107 label.Snapshotter: opt.Snapshotter, 108 }, dns, nc, opt.ApparmorProfile, false, nil, "", ctd.WithTimeout(60*time.Second)) 109 if err != nil { 110 return nil, err 111 } 112 113 policy, err := getGCPolicy(opt.BuilderConfig, opt.Root) 114 if err != nil { 115 return nil, err 116 } 117 118 // make sure platforms are normalized moby/buildkit#4391 119 for i, p := range wo.Platforms { 120 wo.Platforms[i] = platforms.Normalize(p) 121 } 122 123 wo.GCPolicy = policy 124 wo.RegistryHosts = opt.RegistryHosts 125 wo.Labels = getLabels(opt, wo.Labels) 126 127 exec, err := newExecutor(opt.Root, opt.DefaultCgroupParent, opt.NetworkController, dns, opt.Rootless, opt.IdentityMapping, opt.ApparmorProfile) 128 if err != nil { 129 return nil, err 130 } 131 wo.Executor = exec 132 133 w, err := mobyworker.NewContainerdWorker(ctx, wo) 134 if err != nil { 135 return nil, err 136 } 137 138 wc := &worker.Controller{} 139 140 err = wc.Add(w) 141 if err != nil { 142 return nil, err 143 } 144 frontends := map[string]frontend.Frontend{ 145 "dockerfile.v0": forwarder.NewGatewayForwarder(wc, dockerfile.Build), 146 "gateway.v0": gateway.NewGatewayFrontend(wc), 147 } 148 149 return control.NewController(control.Opt{ 150 SessionManager: opt.SessionManager, 151 WorkerController: wc, 152 Frontends: frontends, 153 CacheManager: solver.NewCacheManager(ctx, "local", cacheStorage, worker.NewCacheResultStorage(wc)), 154 CacheStore: cacheStorage, 155 ResolveCacheImporterFuncs: map[string]remotecache.ResolveCacheImporterFunc{ 156 "gha": gha.ResolveCacheImporterFunc(), 157 "local": localremotecache.ResolveCacheImporterFunc(opt.SessionManager), 158 "registry": registryremotecache.ResolveCacheImporterFunc(opt.SessionManager, wo.ContentStore, opt.RegistryHosts), 159 }, 160 ResolveCacheExporterFuncs: map[string]remotecache.ResolveCacheExporterFunc{ 161 "gha": gha.ResolveCacheExporterFunc(), 162 "inline": inlineremotecache.ResolveCacheExporterFunc(), 163 "local": localremotecache.ResolveCacheExporterFunc(opt.SessionManager), 164 "registry": registryremotecache.ResolveCacheExporterFunc(opt.SessionManager, opt.RegistryHosts), 165 }, 166 Entitlements: getEntitlements(opt.BuilderConfig), 167 HistoryDB: historyDB, 168 HistoryConfig: historyConf, 169 LeaseManager: wo.LeaseManager, 170 ContentStore: wo.ContentStore, 171 TraceCollector: getTraceExporter(ctx), 172 }) 173 } 174 175 func openHistoryDB(root string, cfg *config.BuilderHistoryConfig) (*bolt.DB, *bkconfig.HistoryConfig, error) { 176 db, err := bbolt.Open(filepath.Join(root, "history.db"), 0o600, nil) 177 if err != nil { 178 return nil, nil, err 179 } 180 181 var conf *bkconfig.HistoryConfig 182 if cfg != nil { 183 conf = &bkconfig.HistoryConfig{ 184 MaxAge: cfg.MaxAge, 185 MaxEntries: cfg.MaxEntries, 186 } 187 } 188 189 return db, conf, nil 190 } 191 192 func newGraphDriverController(ctx context.Context, rt http.RoundTripper, opt Opt) (*control.Controller, error) { 193 if err := os.MkdirAll(opt.Root, 0o711); err != nil { 194 return nil, err 195 } 196 197 dist := opt.Dist 198 root := opt.Root 199 200 pb.Caps.Init(apicaps.Cap{ 201 ID: pb.CapMergeOp, 202 Enabled: false, 203 DisabledReasonMsg: "only enabled with containerd image store backend", 204 }) 205 206 pb.Caps.Init(apicaps.Cap{ 207 ID: pb.CapDiffOp, 208 Enabled: false, 209 DisabledReasonMsg: "only enabled with containerd image store backend", 210 }) 211 212 var driver graphdriver.Driver 213 if ls, ok := dist.LayerStore.(interface { 214 Driver() graphdriver.Driver 215 }); ok { 216 driver = ls.Driver() 217 } else { 218 return nil, errors.Errorf("could not access graphdriver") 219 } 220 221 innerStore, err := local.NewStore(filepath.Join(root, "content")) 222 if err != nil { 223 return nil, err 224 } 225 226 db, err := bolt.Open(filepath.Join(root, "containerdmeta.db"), 0o644, nil) 227 if err != nil { 228 return nil, errors.WithStack(err) 229 } 230 231 mdb := ctdmetadata.NewDB(db, innerStore, map[string]snapshots.Snapshotter{}) 232 233 store := containerdsnapshot.NewContentStore(mdb.ContentStore(), "buildkit") 234 235 snapshotter, lm, err := snapshot.NewSnapshotter(snapshot.Opt{ 236 GraphDriver: driver, 237 LayerStore: dist.LayerStore, 238 Root: root, 239 IdentityMapping: opt.IdentityMapping, 240 }, ctdmetadata.NewLeaseManager(mdb), "buildkit") 241 if err != nil { 242 return nil, err 243 } 244 245 if err := cache.MigrateV2(context.Background(), filepath.Join(root, "metadata.db"), filepath.Join(root, "metadata_v2.db"), store, snapshotter, lm); err != nil { 246 return nil, err 247 } 248 249 md, err := metadata.NewStore(filepath.Join(root, "metadata_v2.db")) 250 if err != nil { 251 return nil, err 252 } 253 254 layerGetter, ok := snapshotter.(imagerefchecker.LayerGetter) 255 if !ok { 256 return nil, errors.Errorf("snapshotter does not implement layergetter") 257 } 258 259 refChecker := imagerefchecker.New(imagerefchecker.Opt{ 260 ImageStore: dist.ImageStore, 261 LayerGetter: layerGetter, 262 }) 263 264 cm, err := cache.NewManager(cache.ManagerOpt{ 265 Snapshotter: snapshotter, 266 MetadataStore: md, 267 PruneRefChecker: refChecker, 268 LeaseManager: lm, 269 ContentStore: store, 270 GarbageCollect: mdb.GarbageCollect, 271 }) 272 if err != nil { 273 return nil, err 274 } 275 276 src, err := containerimage.NewSource(containerimage.SourceOpt{ 277 CacheAccessor: cm, 278 ContentStore: store, 279 DownloadManager: dist.DownloadManager, 280 MetadataStore: dist.V2MetadataService, 281 ImageStore: dist.ImageStore, 282 ReferenceStore: dist.ReferenceStore, 283 RegistryHosts: opt.RegistryHosts, 284 LayerStore: dist.LayerStore, 285 LeaseManager: lm, 286 GarbageCollect: mdb.GarbageCollect, 287 }) 288 if err != nil { 289 return nil, err 290 } 291 292 dns := getDNSConfig(opt.DNSConfig) 293 294 exec, err := newExecutor(root, opt.DefaultCgroupParent, opt.NetworkController, dns, opt.Rootless, opt.IdentityMapping, opt.ApparmorProfile) 295 if err != nil { 296 return nil, err 297 } 298 299 differ, ok := snapshotter.(mobyexporter.Differ) 300 if !ok { 301 return nil, errors.Errorf("snapshotter doesn't support differ") 302 } 303 304 exp, err := mobyexporter.New(mobyexporter.Opt{ 305 ImageStore: dist.ImageStore, 306 Differ: differ, 307 ImageTagger: opt.ImageTagger, 308 }) 309 if err != nil { 310 return nil, err 311 } 312 313 cacheStorage, err := bboltcachestorage.NewStore(filepath.Join(opt.Root, "cache.db")) 314 if err != nil { 315 return nil, err 316 } 317 318 historyDB, historyConf, err := openHistoryDB(opt.Root, opt.BuilderConfig.History) 319 if err != nil { 320 return nil, err 321 } 322 323 gcPolicy, err := getGCPolicy(opt.BuilderConfig, root) 324 if err != nil { 325 return nil, errors.Wrap(err, "could not get builder GC policy") 326 } 327 328 layers, ok := snapshotter.(mobyworker.LayerAccess) 329 if !ok { 330 return nil, errors.Errorf("snapshotter doesn't support differ") 331 } 332 333 leases, err := lm.List(ctx, `labels."buildkit/lease.temporary"`) 334 if err != nil { 335 return nil, err 336 } 337 for _, l := range leases { 338 lm.Delete(ctx, l) 339 } 340 341 wopt := mobyworker.Opt{ 342 ID: opt.EngineID, 343 ContentStore: store, 344 CacheManager: cm, 345 GCPolicy: gcPolicy, 346 Snapshotter: snapshotter, 347 Executor: exec, 348 ImageSource: src, 349 DownloadManager: dist.DownloadManager, 350 V2MetadataService: dist.V2MetadataService, 351 Exporter: exp, 352 Transport: rt, 353 Layers: layers, 354 Platforms: archutil.SupportedPlatforms(true), 355 LeaseManager: lm, 356 Labels: getLabels(opt, nil), 357 } 358 359 wc := &worker.Controller{} 360 w, err := mobyworker.NewWorker(wopt) 361 if err != nil { 362 return nil, err 363 } 364 wc.Add(w) 365 366 frontends := map[string]frontend.Frontend{ 367 "dockerfile.v0": forwarder.NewGatewayForwarder(wc, dockerfile.Build), 368 "gateway.v0": gateway.NewGatewayFrontend(wc), 369 } 370 371 return control.NewController(control.Opt{ 372 SessionManager: opt.SessionManager, 373 WorkerController: wc, 374 Frontends: frontends, 375 CacheManager: solver.NewCacheManager(ctx, "local", cacheStorage, worker.NewCacheResultStorage(wc)), 376 CacheStore: cacheStorage, 377 ResolveCacheImporterFuncs: map[string]remotecache.ResolveCacheImporterFunc{ 378 "registry": localinlinecache.ResolveCacheImporterFunc(opt.SessionManager, opt.RegistryHosts, store, dist.ReferenceStore, dist.ImageStore), 379 "local": localremotecache.ResolveCacheImporterFunc(opt.SessionManager), 380 }, 381 ResolveCacheExporterFuncs: map[string]remotecache.ResolveCacheExporterFunc{ 382 "inline": inlineremotecache.ResolveCacheExporterFunc(), 383 }, 384 Entitlements: getEntitlements(opt.BuilderConfig), 385 LeaseManager: lm, 386 ContentStore: store, 387 HistoryDB: historyDB, 388 HistoryConfig: historyConf, 389 TraceCollector: getTraceExporter(ctx), 390 }) 391 } 392 393 func getGCPolicy(conf config.BuilderConfig, root string) ([]client.PruneInfo, error) { 394 var gcPolicy []client.PruneInfo 395 if conf.GC.Enabled { 396 var ( 397 defaultKeepStorage int64 398 err error 399 ) 400 401 if conf.GC.DefaultKeepStorage != "" { 402 defaultKeepStorage, err = units.RAMInBytes(conf.GC.DefaultKeepStorage) 403 if err != nil { 404 return nil, errors.Wrapf(err, "could not parse '%s' as Builder.GC.DefaultKeepStorage config", conf.GC.DefaultKeepStorage) 405 } 406 } 407 408 if conf.GC.Policy == nil { 409 gcPolicy = mobyworker.DefaultGCPolicy(root, defaultKeepStorage) 410 } else { 411 gcPolicy = make([]client.PruneInfo, len(conf.GC.Policy)) 412 for i, p := range conf.GC.Policy { 413 b, err := units.RAMInBytes(p.KeepStorage) 414 if err != nil { 415 return nil, err 416 } 417 if b == 0 { 418 b = defaultKeepStorage 419 } 420 gcPolicy[i], err = toBuildkitPruneInfo(types.BuildCachePruneOptions{ 421 All: p.All, 422 KeepStorage: b, 423 Filters: filters.Args(p.Filter), 424 }) 425 if err != nil { 426 return nil, err 427 } 428 } 429 } 430 } 431 return gcPolicy, nil 432 } 433 434 func getEntitlements(conf config.BuilderConfig) []string { 435 var ents []string 436 // Incase of no config settings, NetworkHost should be enabled & SecurityInsecure must be disabled. 437 if conf.Entitlements.NetworkHost == nil || *conf.Entitlements.NetworkHost { 438 ents = append(ents, string(entitlements.EntitlementNetworkHost)) 439 } 440 if conf.Entitlements.SecurityInsecure != nil && *conf.Entitlements.SecurityInsecure { 441 ents = append(ents, string(entitlements.EntitlementSecurityInsecure)) 442 } 443 return ents 444 } 445 446 func getLabels(opt Opt, labels map[string]string) map[string]string { 447 if labels == nil { 448 labels = make(map[string]string) 449 } 450 labels[wlabel.HostGatewayIP] = opt.DNSConfig.HostGatewayIP.String() 451 return labels 452 }