Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion pkg/app/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,18 @@ To build and push the image to a registry:
Usage: "Force rebuild the image",
Value: false,
},
// https://github.com/urfave/cli/issues/1134#issuecomment-1191407527
&cli.StringFlag{
Name: "export-cache",
Usage: "Export the cache (e.g. type=registry,ref=<image>)",
Aliases: []string{"ec"},
},
&cli.StringFlag{
Name: "import-cache",
Usage: "Import the cache (e.g. type=registry,ref=<image>)",
Aliases: []string{"ic"},
},
},

Action: build,
}

Expand Down Expand Up @@ -122,6 +132,8 @@ func build(clicontext *cli.Context) error {
debug := clicontext.Bool("debug")
output := clicontext.String("output")
force := clicontext.Bool("force")
exportCache := clicontext.String("export-cache")
importCache := clicontext.String("import-cache")

opt := builder.Options{
ManifestFilePath: manifest,
Expand All @@ -132,6 +144,8 @@ func build(clicontext *cli.Context) error {
OutputOpts: output,
PubKeyPath: clicontext.Path("public-key"),
ProgressMode: "auto",
ExportCache: exportCache,
ImportCache: importCache,
}
if debug {
opt.ProgressMode = "plain"
Expand Down
15 changes: 15 additions & 0 deletions pkg/app/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ var CommandUp = &cli.Command{
Usage: "Force rebuild and run the container although the previous container is running",
Value: false,
},
// https://github.com/urfave/cli/issues/1134#issuecomment-1191407527
&cli.StringFlag{
Name: "export-cache",
Usage: "Export the cache (e.g. type=registry,ref=<image>)",
Aliases: []string{"ec"},
},
&cli.StringFlag{
Name: "import-cache",
Usage: "Import the cache (e.g. type=registry,ref=<image>)",
Aliases: []string{"ic"},
},
},

Action: up,
Expand Down Expand Up @@ -140,6 +151,8 @@ func up(clicontext *cli.Context) error {
debug := clicontext.Bool("debug")
force := clicontext.Bool("force")
output := ""
exportCache := clicontext.String("export-cache")
importCache := clicontext.String("import-cache")

opt := builder.Options{
ManifestFilePath: manifest,
Expand All @@ -150,6 +163,8 @@ func up(clicontext *cli.Context) error {
OutputOpts: output,
PubKeyPath: clicontext.Path("public-key"),
ProgressMode: "auto",
ExportCache: exportCache,
ImportCache: importCache,
}
if debug {
opt.ProgressMode = "plain"
Expand Down
12 changes: 10 additions & 2 deletions pkg/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,17 @@ func (b generalBuilder) BuildFunc() func(ctx context.Context, c client.Client) (
return nil, errors.Wrap(err, "failed to compile")
}

res, err := c.Solve(ctx, client.SolveRequest{
sreq := client.SolveRequest{
Definition: def.ToPB(),
})
}
if b.Options.ImportCache != "" {
ci, err := ParseImportCache([]string{b.Options.ImportCache})
if err != nil {
return nil, errors.Wrap(err, "failed to get the import cache")
}
sreq.CacheImports = ci
}
res, err := c.Solve(ctx, sreq)
if err != nil {
return nil, err
}
Expand Down
42 changes: 24 additions & 18 deletions pkg/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type Options struct {
PubKeyPath string
// OutputOpts is the output options.
OutputOpts string
// ExportCache is the options to export cache.
// e.g. type=registry,ref=docker.io/username/image
ExportCache string
// ImportCache is the options to import cache.
// e.g. type=registry,ref=docker.io/username/image
ImportCache string
}

type generalBuilder struct {
Expand Down Expand Up @@ -188,6 +194,11 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err
if err != nil {
return errors.Wrap(err, "failed to get labels")
}

ce, err := ParseExportCache([]string{b.ExportCache}, nil)
if err != nil {
return errors.Wrap(err, "failed to parse export cache")
}
// k := platforms.Format(platforms.DefaultSpec())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
Expand All @@ -199,6 +210,9 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err
for _, entry := range b.entries {
// Set up docker config auth.
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(os.Stderr)}
b.logger.WithFields(logrus.Fields{
"type": entry.Type,
}).Debug("build image with buildkit")
switch entry.Type {
// Create default build.
case client.ExporterDocker:
Expand All @@ -217,20 +231,15 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err
}
}
defer pipeW.Close()
_, err := b.Client.Build(ctx, client.SolveOpt{
Exports: []client.ExportEntry{entry},
solveOpt := client.SolveOpt{
CacheExports: ce,
Exports: []client.ExportEntry{entry},
LocalDirs: map[string]string{
// TODO(gaocegege): Move it to BuildFunc with the help
// of llb.Local
flag.FlagCacheDir: home.GetManager().CacheDir(),
},
Session: attachable,
// TODO(gaocegege): Use llb.WithProxy to implement it.
FrontendAttrs: map[string]string{
"build-arg:HTTPS_PROXY": os.Getenv("HTTPS_PROXY"),
},
}, "envd", b.BuildFunc(), pw.Status())

}
_, err := b.Client.Build(ctx, solveOpt, "envd", b.BuildFunc(), pw.Status())
if err != nil {
err = errors.Wrap(err, "failed to solve LLB")
return err
Expand All @@ -256,18 +265,15 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err
})
default:
eg.Go(func() error {
_, err := b.Client.Build(ctx, client.SolveOpt{
Exports: []client.ExportEntry{entry},
solveOpt := client.SolveOpt{
CacheExports: ce,
Exports: []client.ExportEntry{entry},
LocalDirs: map[string]string{
flag.FlagCacheDir: home.GetManager().CacheDir(),
},
Session: attachable,
// TODO(gaocegege): Use llb.WithProxy to implement it.
FrontendAttrs: map[string]string{
"build-arg:HTTPS_PROXY": os.Getenv("HTTPS_PROXY"),
},
}, "envd", b.BuildFunc(), pw.Status())

}
_, err := b.Client.Build(ctx, solveOpt, "envd", b.BuildFunc(), pw.Status())
if err != nil {
err = errors.Wrap(err, "failed to solve LLB")
return err
Expand Down
137 changes: 137 additions & 0 deletions pkg/builder/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/containerd/console"
"github.com/containerd/containerd/platforms"
"github.com/moby/buildkit/client"
gatewayclient "github.com/moby/buildkit/frontend/gateway/client"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -76,6 +77,142 @@ func DefaultPathEnv(os string) string {
return DefaultPathEnvUnix
}

func parseImportCacheCSV(s string) (gatewayclient.CacheOptionsEntry, error) {
im := gatewayclient.CacheOptionsEntry{
Type: "",
Attrs: map[string]string{},
}
csvReader := csv.NewReader(strings.NewReader(s))
fields, err := csvReader.Read()
if err != nil {
return im, err
}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
return im, errors.Errorf("invalid value %s", field)
}
key := strings.ToLower(parts[0])
value := parts[1]
switch key {
case "type":
im.Type = value
default:
im.Attrs[key] = value
}
}
if im.Type == "" {
return im, errors.New("--import-cache requires type=<type>")
}
return im, nil
}

// ParseImportCache parses --import-cache
func ParseImportCache(importCaches []string) ([]gatewayclient.CacheOptionsEntry, error) {
var imports []gatewayclient.CacheOptionsEntry
for _, importCache := range importCaches {
legacy := !strings.Contains(importCache, "type=")
if legacy {
logrus.Warn("--import-cache <ref> is deprecated. Please use --import-cache type=registry,ref=<ref>,<opt>=<optval>[,<opt>=<optval>] instead.")
imports = append(imports, gatewayclient.CacheOptionsEntry{
Type: "registry",
Attrs: map[string]string{"ref": importCache},
})
} else {
im, err := parseImportCacheCSV(importCache)
if err != nil {
return nil, err
}
imports = append(imports, im)
}
}
return imports, nil
}

// ParseExportCache parses --export-cache (and legacy --export-cache-opt)
// Refer to github.com/moby/buildkit/cmd/buildctl/build/exportcache.go
func ParseExportCache(exportCaches, legacyExportCacheOpts []string) ([]client.CacheOptionsEntry, error) {
var exports []client.CacheOptionsEntry
if len(legacyExportCacheOpts) > 0 {
if len(exportCaches) != 1 {
return nil, errors.New("--export-cache-opt requires exactly single --export-cache")
}
}
for _, exportCache := range exportCaches {
legacy := !strings.Contains(exportCache, "type=")
if legacy {
logrus.Warnf("--export-cache <ref> --export-cache-opt <opt>=<optval> is deprecated. Please use --export-cache type=registry,ref=<ref>,<opt>=<optval>[,<opt>=<optval>] instead")
attrs, err := attrMap(legacyExportCacheOpts)
if err != nil {
return nil, err
}
if _, ok := attrs["mode"]; !ok {
attrs["mode"] = "min"
}
attrs["ref"] = exportCache
exports = append(exports, client.CacheOptionsEntry{
Type: "registry",
Attrs: attrs,
})
} else {
if len(legacyExportCacheOpts) > 0 {
return nil, errors.New("--export-cache-opt is not supported for the specified --export-cache. Please use --export-cache type=<type>,<opt>=<optval>[,<opt>=<optval>] instead")
}
ex, err := parseExportCacheCSV(exportCache)
if err != nil {
return nil, err
}
exports = append(exports, ex)
}
}
return exports, nil
}

func attrMap(sl []string) (map[string]string, error) {
m := map[string]string{}
for _, v := range sl {
parts := strings.SplitN(v, "=", 2)
if len(parts) != 2 {
return nil, errors.Errorf("invalid value %s", v)
}
m[parts[0]] = parts[1]
}
return m, nil
}

func parseExportCacheCSV(s string) (client.CacheOptionsEntry, error) {
ex := client.CacheOptionsEntry{
Type: "",
Attrs: map[string]string{},
}
csvReader := csv.NewReader(strings.NewReader(s))
fields, err := csvReader.Read()
if err != nil {
return ex, err
}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
return ex, errors.Errorf("invalid value %s", field)
}
key := strings.ToLower(parts[0])
value := parts[1]
switch key {
case "type":
ex.Type = value
default:
ex.Attrs[key] = value
}
}
if ex.Type == "" {
return ex, errors.New("--export-cache requires type=<type>")
}
if _, ok := ex.Attrs["mode"]; !ok {
ex.Attrs["mode"] = "min"
}
return ex, nil
}

// parseOutput parses --output
// Refer to https://github.com/moby/buildkit/blob/master/cmd/buildctl/build/output.go#L56
func parseOutput(exports string) ([]client.ExportEntry, error) {
Expand Down
Loading