add support for building from cached images

Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
Avi Deitcher 2022-07-01 15:23:28 +03:00
parent 44dfac2725
commit a90ff542cd
31 changed files with 734 additions and 175 deletions

View File

@ -2,8 +2,10 @@ package cache
import ( import (
"fmt" "fmt"
"strings"
"github.com/google/go-containerregistry/pkg/v1" "github.com/containerd/containerd/reference"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/match" "github.com/google/go-containerregistry/pkg/v1/match"
"github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/partial"
) )
@ -48,20 +50,46 @@ func (p *Provider) findImage(imageName, architecture string) (v1.Image, error) {
return nil, fmt.Errorf("no image found for %s", imageName) return nil, fmt.Errorf("no image found for %s", imageName)
} }
// FindDescriptor get the first descriptor pointed to by the image name // FindDescriptor get the first descriptor pointed to by the image reference, whether tagged or digested
func (p *Provider) FindDescriptor(name string) (*v1.Descriptor, error) { func (p *Provider) FindDescriptor(ref *reference.Spec) (*v1.Descriptor, error) {
index, err := p.cache.ImageIndex() index, err := p.cache.ImageIndex()
// if there is no root index, we are broken // if there is no root index, we are broken
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid image cache: %v", err) return nil, fmt.Errorf("invalid image cache: %v", err)
} }
descs, err := partial.FindManifests(index, match.Name(name)) // parse the ref.Object to determine what it is, then search by that
dig := ref.Digest()
hash, err := v1.NewHash(dig.String())
// if we had a valid hash, search by that
if err == nil {
// we had a valid hash, so we should search by it
descs, err := partial.FindManifests(index, match.Digests(hash))
if err != nil {
return nil, err
}
if len(descs) > 0 {
return &descs[0], nil
}
// we had a valid hash, but didn't find any descriptors
return nil, nil
}
// no valid hash, try the tag
tag := ref.Object
// remove anything after an '@'
n := strings.LastIndex(tag, "@")
if n == 0 {
return nil, fmt.Errorf("invalid tag, was not digest, yet began with '@': %s", tag)
}
if n >= 0 {
tag = tag[:n]
}
descs, err := partial.FindManifests(index, match.Name(fmt.Sprintf("%s:%s", ref.Locator, tag)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(descs) < 1 { if len(descs) > 0 {
return nil, nil return &descs[0], nil
} }
return &descs[0], nil return nil, nil
} }

View File

@ -1,12 +1,15 @@
package cache package cache
import ( import (
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/content/local"
"github.com/google/go-containerregistry/pkg/v1/layout" "github.com/google/go-containerregistry/pkg/v1/layout"
) )
// Provider cache implementation of cacheProvider // Provider cache implementation of cacheProvider
type Provider struct { type Provider struct {
cache layout.Path cache layout.Path
store content.Store
} }
// NewProvider create a new CacheProvider based in the provided directory // NewProvider create a new CacheProvider based in the provided directory
@ -15,5 +18,9 @@ func NewProvider(dir string) (*Provider, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Provider{p}, nil store, err := local.NewStore(dir)
if err != nil {
return nil, err
}
return &Provider{p, store}, nil
} }

10
src/cmd/linuxkit/cache/store.go vendored Normal file
View File

@ -0,0 +1,10 @@
package cache
import (
"github.com/containerd/containerd/content"
)
// Store get content.Store referencing the cache
func (p *Provider) Store() (content.Store, error) {
return p.store, nil
}

View File

@ -35,14 +35,14 @@ func cacheExport(args []string) {
if err != nil { if err != nil {
log.Fatalf("unable to read a local cache: %v", err) log.Fatalf("unable to read a local cache: %v", err)
} }
desc, err := p.FindDescriptor(fullname)
if err != nil {
log.Fatalf("unable to find image named %s: %v", name, err)
}
ref, err := reference.Parse(fullname) ref, err := reference.Parse(fullname)
if err != nil { if err != nil {
log.Fatalf("invalid image name %s: %v", name, err) log.Fatalf("invalid image name %s: %v", name, err)
} }
desc, err := p.FindDescriptor(&ref)
if err != nil {
log.Fatalf("unable to find image named %s: %v", name, err)
}
src := p.NewSource(&ref, *arch, desc) src := p.NewSource(&ref, *arch, desc)
reader, err := src.V1TarReader() reader, err := src.V1TarReader()

View File

@ -26,11 +26,12 @@ require (
github.com/gophercloud/gophercloud v0.1.0 github.com/gophercloud/gophercloud v0.1.0
github.com/gophercloud/utils v0.0.0-20181029231510-34f5991525d1 github.com/gophercloud/utils v0.0.0-20181029231510-34f5991525d1
github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/go-version v1.2.0
github.com/moby/buildkit v0.10.1-0.20220614084718-b3d272e22672 github.com/moby/buildkit v0.10.1-0.20220617010517-a6a114a1a476
github.com/moby/hyperkit v0.0.0-20180416161519-d65b09c1c28a github.com/moby/hyperkit v0.0.0-20180416161519-d65b09c1c28a
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
github.com/moby/vpnkit v0.4.1-0.20200311130018-2ffc1dd8a84e github.com/moby/vpnkit v0.4.1-0.20200311130018-2ffc1dd8a84e
github.com/moul/gotty-client v1.7.1-0.20180526075433-e5589f6df359 github.com/moul/gotty-client v1.7.1-0.20180526075433-e5589f6df359
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/packethost/packngo v0.1.1-0.20171201154433-f1be085ecd6f github.com/packethost/packngo v0.1.1-0.20171201154433-f1be085ecd6f

View File

@ -1066,6 +1066,8 @@ github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlK
github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d/go.mod h1:WvwAZv8aRScHkqc/+X46cRC2CKMKpqcaX+pRvUTtPes= github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d/go.mod h1:WvwAZv8aRScHkqc/+X46cRC2CKMKpqcaX+pRvUTtPes=
github.com/moby/buildkit v0.10.1-0.20220614084718-b3d272e22672 h1:cDyvSdAMu2BSuEaodU0d9Jm7ki/wWibm/s8G/xRurYA= github.com/moby/buildkit v0.10.1-0.20220614084718-b3d272e22672 h1:cDyvSdAMu2BSuEaodU0d9Jm7ki/wWibm/s8G/xRurYA=
github.com/moby/buildkit v0.10.1-0.20220614084718-b3d272e22672/go.mod h1:yle9eiU1fiJ/WhC4VTLOaQ6rxFou1mc4AhwScHwysi0= github.com/moby/buildkit v0.10.1-0.20220614084718-b3d272e22672/go.mod h1:yle9eiU1fiJ/WhC4VTLOaQ6rxFou1mc4AhwScHwysi0=
github.com/moby/buildkit v0.10.1-0.20220617010517-a6a114a1a476 h1:quXwiNE0nC/0bHFJkQKcwetpk1jNHIwwzDpq9hVmxSM=
github.com/moby/buildkit v0.10.1-0.20220617010517-a6a114a1a476/go.mod h1:yle9eiU1fiJ/WhC4VTLOaQ6rxFou1mc4AhwScHwysi0=
github.com/moby/hyperkit v0.0.0-20180416161519-d65b09c1c28a h1:hExo7kltIidoitYnXsnqfvkJXG3YEMbHuQbf9nOMvow= github.com/moby/hyperkit v0.0.0-20180416161519-d65b09c1c28a h1:hExo7kltIidoitYnXsnqfvkJXG3YEMbHuQbf9nOMvow=
github.com/moby/hyperkit v0.0.0-20180416161519-d65b09c1c28a/go.mod h1:zGAVB/FkAf4ozkR8CCuj4LcVuErrNsj9APTDFvhOckw= github.com/moby/hyperkit v0.0.0-20180416161519-d65b09c1c28a/go.mod h1:zGAVB/FkAf4ozkR8CCuj4LcVuErrNsj9APTDFvhOckw=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=

View File

@ -12,8 +12,10 @@ import (
) )
const ( const (
buildersEnvVar = "LINUXKIT_BUILDERS" buildersEnvVar = "LINUXKIT_BUILDERS"
defaultBuilderImage = "moby/buildkit:v0.10.3" // this is the most recent manifest pointed to by moby/buildkit:master as of 2022-07-13, so it includes
// our required commit. Once there is a normal semver tag later than this, we should switch to it.
defaultBuilderImage = "moby/buildkit@sha256:e7b24395dff0280513cb795fe6dbd77d8f22e49ef9401eedc311c08b2b7fdd0b"
) )
func pkgBuild(args []string) { func pkgBuild(args []string) {
@ -35,6 +37,7 @@ func pkgBuildPush(args []string, withPush bool) {
} }
force := flags.Bool("force", false, "Force rebuild even if image is in local cache") force := flags.Bool("force", false, "Force rebuild even if image is in local cache")
ignoreCache := flags.Bool("ignore-cached", false, "Ignore cached intermediate images, always pulling from registry")
docker := flags.Bool("docker", false, "Store the built image in the docker image cache instead of the default linuxkit cache") docker := flags.Bool("docker", false, "Store the built image in the docker image cache instead of the default linuxkit cache")
platforms := flags.String("platforms", "", "Which platforms to build for, defaults to all of those for which the package can be built") platforms := flags.String("platforms", "", "Which platforms to build for, defaults to all of those for which the package can be built")
skipPlatforms := flags.String("skip-platforms", "", "Platforms that should be skipped, even if present in build.yml") skipPlatforms := flags.String("skip-platforms", "", "Platforms that should be skipped, even if present in build.yml")
@ -78,6 +81,9 @@ func pkgBuildPush(args []string, withPush bool) {
if *force { if *force {
opts = append(opts, pkglib.WithBuildForce()) opts = append(opts, pkglib.WithBuildForce())
} }
if *ignoreCache {
opts = append(opts, pkglib.WithBuildIgnoreCache())
}
opts = append(opts, pkglib.WithBuildCacheDir(*buildCacheDir)) opts = append(opts, pkglib.WithBuildCacheDir(*buildCacheDir))
if withPush { if withPush {

View File

@ -31,6 +31,7 @@ const (
type buildOpts struct { type buildOpts struct {
skipBuild bool skipBuild bool
force bool force bool
ignoreCache bool
push bool push bool
release string release string
manifest bool manifest bool
@ -161,6 +162,14 @@ func WithBuildBuilderRestart(restart bool) BuildOpt {
} }
} }
// WithBuildIgnoreCache when building an image, do not look in local cache for dependent images
func WithBuildIgnoreCache() BuildOpt {
return func(bo *buildOpts) error {
bo.ignoreCache = true
return nil
}
}
// Build builds the package // Build builds the package
func (p Pkg) Build(bos ...BuildOpt) error { func (p Pkg) Build(bos ...BuildOpt) error {
var bo buildOpts var bo buildOpts
@ -321,7 +330,7 @@ func (p Pkg) Build(bos ...BuildOpt) error {
} }
// get descriptor for root of manifest // get descriptor for root of manifest
desc, err := c.FindDescriptor(p.FullTag()) desc, err := c.FindDescriptor(&ref)
if err != nil { if err != nil {
return err return err
} }
@ -403,7 +412,7 @@ func (p Pkg) buildArch(ctx context.Context, d dockerRunner, c lktspec.CacheProvi
} }
if _, err := c.ImagePull(&ref, "", arch, false); err == nil { if _, err := c.ImagePull(&ref, "", arch, false); err == nil {
fmt.Fprintf(writer, "image already found %s for arch %s", ref, arch) fmt.Fprintf(writer, "image already found %s for arch %s", ref, arch)
desc, err := c.FindDescriptor(ref.String()) desc, err := c.FindDescriptor(&ref)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not find root descriptor for %s: %v", ref, err) return nil, fmt.Errorf("could not find root descriptor for %s: %v", ref, err)
} }
@ -452,7 +461,12 @@ func (p Pkg) buildArch(ctx context.Context, d dockerRunner, c lktspec.CacheProvi
buildCtx := &buildCtx{sources: p.sources} buildCtx := &buildCtx{sources: p.sources}
platform := fmt.Sprintf("linux/%s", arch) platform := fmt.Sprintf("linux/%s", arch)
if err := d.build(ctx, tagArch, p.path, builderName, builderImage, platform, restart, buildCtx.Reader(), stdout, imageBuildOpts); err != nil { // if we were told to ignore cached dependent images, pass it a nil cache so it cannot read anything
passCache := c
if bo.ignoreCache {
passCache = nil
}
if err := d.build(ctx, tagArch, p.path, builderName, builderImage, platform, restart, passCache, buildCtx.Reader(), stdout, imageBuildOpts); err != nil {
stdoutCloser() stdoutCloser()
if strings.Contains(err.Error(), "executor failed running [/dev/.buildkit_qemu_emulator") { if strings.Contains(err.Error(), "executor failed running [/dev/.buildkit_qemu_emulator") {
return nil, fmt.Errorf("buildkit was unable to emulate %s. check binfmt has been set up and works for this platform: %v", platform, err) return nil, fmt.Errorf("buildkit was unable to emulate %s. check binfmt has been set up and works for this platform: %v", platform, err)

View File

@ -13,10 +13,12 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/reference" "github.com/containerd/containerd/reference"
dockertypes "github.com/docker/docker/api/types" dockertypes "github.com/docker/docker/api/types"
registry "github.com/google/go-containerregistry/pkg/v1" registry "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types" "github.com/google/go-containerregistry/pkg/v1/types"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec" lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -52,7 +54,7 @@ func (d *dockerMocker) contextSupportCheck() error {
return errors.New("contexts not supported") return errors.New("contexts not supported")
} }
func (d *dockerMocker) build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, builderRestart bool, stdin io.Reader, stdout io.Writer, imageBuildOpts dockertypes.ImageBuildOptions) error { func (d *dockerMocker) build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, builderRestart bool, c spec.CacheProvider, r io.Reader, stdout io.Writer, imageBuildOpts dockertypes.ImageBuildOptions) error {
if !d.enableBuild { if !d.enableBuild {
return errors.New("build disabled") return errors.New("build disabled")
} }
@ -237,7 +239,8 @@ func (c *cacheMocker) DescriptorWrite(ref *reference.Spec, desc registry.Descrip
return c.NewSource(ref, "", &root), nil return c.NewSource(ref, "", &root), nil
} }
func (c *cacheMocker) FindDescriptor(name string) (*registry.Descriptor, error) { func (c *cacheMocker) FindDescriptor(ref *reference.Spec) (*registry.Descriptor, error) {
name := ref.String()
if desc, ok := c.images[name]; ok && len(desc) > 0 { if desc, ok := c.images[name]; ok && len(desc) > 0 {
return &desc[0], nil return &desc[0], nil
} }
@ -259,6 +262,11 @@ func (c *cacheMocker) appendImage(image string, root registry.Descriptor) {
c.images[image] = append(c.images[image], root) c.images[image] = append(c.images[image], root)
} }
// Store get content.Store referencing the cache
func (c *cacheMocker) Store() (content.Store, error) {
return nil, errors.New("unsupported")
}
type cacheMockerSource struct { type cacheMockerSource struct {
c *cacheMocker c *cacheMocker
ref *reference.Spec ref *reference.Spec

View File

@ -15,14 +15,19 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path"
"runtime" "runtime"
"strings" "strings"
"time" "time"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/reference"
"github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/progress"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
versioncompare "github.com/hashicorp/go-version" versioncompare "github.com/hashicorp/go-version"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/registry" "github.com/linuxkit/linuxkit/src/cmd/linuxkit/registry"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
buildkitClient "github.com/moby/buildkit/client" buildkitClient "github.com/moby/buildkit/client"
// golint requires comments on non-main(test) // golint requires comments on non-main(test)
@ -30,6 +35,8 @@ import (
_ "github.com/moby/buildkit/client/connhelper/dockercontainer" _ "github.com/moby/buildkit/client/connhelper/dockercontainer"
_ "github.com/moby/buildkit/client/connhelper/ssh" _ "github.com/moby/buildkit/client/connhelper/ssh"
"github.com/moby/buildkit/frontend/dockerfile/builder" "github.com/moby/buildkit/frontend/dockerfile/builder"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/moby/buildkit/session/upload/uploadprovider" "github.com/moby/buildkit/session/upload/uploadprovider"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -44,7 +51,7 @@ const (
type dockerRunner interface { type dockerRunner interface {
tag(ref, tag string) error tag(ref, tag string) error
build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, restart bool, stdin io.Reader, stdout io.Writer, imageBuildOpts types.ImageBuildOptions) error build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, restart bool, c spec.CacheProvider, r io.Reader, stdout io.Writer, imageBuildOpts types.ImageBuildOptions) error
save(tgt string, refs ...string) error save(tgt string, refs ...string) error
load(src io.Reader) error load(src io.Reader) error
pull(img string) (bool, error) pull(img string) (bool, error)
@ -367,7 +374,7 @@ func (dr *dockerRunnerImpl) tag(ref, tag string) error {
return dr.command(nil, nil, nil, "image", "tag", ref, tag) return dr.command(nil, nil, nil, "image", "tag", ref, tag)
} }
func (dr *dockerRunnerImpl) build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, restart bool, stdin io.Reader, stdout io.Writer, imageBuildOpts types.ImageBuildOptions) error { func (dr *dockerRunnerImpl) build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, restart bool, c spec.CacheProvider, stdin io.Reader, stdout io.Writer, imageBuildOpts types.ImageBuildOptions) error {
// ensure we have a builder // ensure we have a builder
client, err := dr.builder(ctx, dockerContext, builderImage, platform, restart) client, err := dr.builder(ctx, dockerContext, builderImage, platform, restart)
if err != nil { if err != nil {
@ -427,6 +434,59 @@ func (dr *dockerRunnerImpl) build(ctx context.Context, tag, pkg, dockerContext,
} }
} }
// go through the dockerfile to see if we have any provided images cached
if c != nil {
dockerfile := path.Join(pkg, "Dockerfile")
f, err := os.Open(dockerfile)
if err != nil {
return fmt.Errorf("error opening dockerfile %s: %v", dockerfile, err)
}
defer f.Close()
ast, err := parser.Parse(f)
if err != nil {
return fmt.Errorf("error parsing dockerfile from bytes into AST %s: %v", dockerfile, err)
}
stages, _, err := instructions.Parse(ast.AST)
if err != nil {
return fmt.Errorf("error parsing dockerfile from AST into stages %s: %v", dockerfile, err)
}
// go through each stage, get the basename of the image, see if we have it in the linuxkit cache
imageStores := map[string]string{}
for _, stage := range stages {
// see if the provided image name is tagged (docker.io/linuxkit/foo:latest) or digested (docker.io/linuxkit/foo@sha256:abcdefg)
// if neither, we have an error
ref, err := reference.Parse(util.ReferenceExpand(stage.BaseName))
if err != nil {
return fmt.Errorf("could not resolve references for image %s: %v", stage.BaseName, err)
}
gdesc, err := c.FindDescriptor(&ref)
if err != nil {
return fmt.Errorf("invalid name %s", stage.BaseName)
}
// not found, so nothing to look up
if gdesc == nil {
continue
}
hash := gdesc.Digest
imageStores[stage.BaseName] = hash.String()
}
if len(imageStores) > 0 {
// if we made it here, we found the reference
store, err := c.Store()
if err != nil {
return fmt.Errorf("unable to get content store from cache: %v", err)
}
if solveOpts.OCIStores == nil {
solveOpts.OCIStores = map[string]content.Store{}
}
solveOpts.OCIStores["linuxkit-cache"] = store
for image, hash := range imageStores {
solveOpts.FrontendAttrs["context:"+image] = fmt.Sprintf("oci-layout:%s@%s", "linuxkit-cache", hash)
}
}
}
ctx2, cancel := context.WithCancel(context.TODO()) ctx2, cancel := context.WithCancel(context.TODO())
defer cancel() defer cancel()
printer := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, "auto") printer := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, "auto")

View File

@ -3,14 +3,19 @@ package spec
import ( import (
"io" "io"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/reference" "github.com/containerd/containerd/reference"
v1 "github.com/google/go-containerregistry/pkg/v1" v1 "github.com/google/go-containerregistry/pkg/v1"
) )
// CacheProvider interface for a provide of a cache. // CacheProvider interface for a provide of a cache.
type CacheProvider interface { type CacheProvider interface {
// FindDescriptor get the first descriptor pointed to by the image name // FindDescriptor find the descriptor pointed to by the reference in the cache.
FindDescriptor(name string) (*v1.Descriptor, error) // ref is a valid reference, such as docker.io/library/alpine:3.15 or alpine@sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454
// If both tag and digest are provided, will use digest exclusively.
// Will expand to full names, so "alpine" becomes "docker.io/library/alpine:latest".
// If none is found, returns nil Descriptor and no error.
FindDescriptor(ref *reference.Spec) (*v1.Descriptor, error)
// ImagePull takes an image name and pulls it from a registry to the cache. It should be // ImagePull takes an image name and pulls it from a registry to the cache. It should be
// efficient and only write missing blobs, based on their content hash. If the ref already // efficient and only write missing blobs, based on their content hash. If the ref already
// exists in the cache, it should not pull anything, unless alwaysPull is set to true. // exists in the cache, it should not pull anything, unless alwaysPull is set to true.
@ -30,4 +35,6 @@ type CacheProvider interface {
Push(name string) error Push(name string) error
// NewSource return an ImageSource for a specific ref and architecture in the cache. // NewSource return an ImageSource for a specific ref and architecture in the cache.
NewSource(ref *reference.Spec, architecture string, descriptor *v1.Descriptor) ImageSource NewSource(ref *reference.Spec, architecture string, descriptor *v1.Descriptor) ImageSource
// Store get content.Store referencing the cache
Store() (content.Store, error)
} }

View File

@ -34,8 +34,17 @@ type ImageMetaResolver interface {
ResolveImageConfig(ctx context.Context, ref string, opt ResolveImageConfigOpt) (digest.Digest, []byte, error) ResolveImageConfig(ctx context.Context, ref string, opt ResolveImageConfigOpt) (digest.Digest, []byte, error)
} }
type ResolverType int
const (
ResolverTypeRegistry ResolverType = iota
ResolverTypeOCILayout
)
type ResolveImageConfigOpt struct { type ResolveImageConfigOpt struct {
Platform *ocispecs.Platform Platform *ocispecs.Platform
ResolveMode string ResolveMode string
LogName string LogName string
ResolverType // default is ResolverTypeRegistry
SessionID string
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
_ "crypto/sha256" // for opencontainers/go-digest _ "crypto/sha256" // for opencontainers/go-digest
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -132,8 +133,9 @@ func Image(ref string, opts ...ImageOption) State {
p = c.Platform p = c.Platform
} }
_, dt, err := info.metaResolver.ResolveImageConfig(ctx, ref, ResolveImageConfigOpt{ _, dt, err := info.metaResolver.ResolveImageConfig(ctx, ref, ResolveImageConfigOpt{
Platform: p, Platform: p,
ResolveMode: info.resolveMode.String(), ResolveMode: info.resolveMode.String(),
ResolverType: ResolverTypeRegistry,
}) })
if err != nil { if err != nil {
return State{}, err return State{}, err
@ -147,8 +149,9 @@ func Image(ref string, opts ...ImageOption) State {
p = c.Platform p = c.Platform
} }
dgst, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{ dgst, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{
Platform: p, Platform: p,
ResolveMode: info.resolveMode.String(), ResolveMode: info.resolveMode.String(),
ResolverType: ResolverTypeRegistry,
}) })
if err != nil { if err != nil {
return State{}, err return State{}, err
@ -452,6 +455,57 @@ func Differ(t DiffType, required bool) LocalOption {
}) })
} }
func OCILayout(contentStoreID string, dig digest.Digest, opts ...OCILayoutOption) State {
gi := &OCILayoutInfo{}
for _, o := range opts {
o.SetOCILayoutOption(gi)
}
attrs := map[string]string{}
if gi.sessionID != "" {
attrs[pb.AttrOCILayoutSessionID] = gi.sessionID
addCap(&gi.Constraints, pb.CapSourceOCILayoutSessionID)
}
if ll := gi.layerLimit; ll != nil {
attrs[pb.AttrOCILayoutLayerLimit] = strconv.FormatInt(int64(*ll), 10)
addCap(&gi.Constraints, pb.CapSourceOCILayoutLayerLimit)
}
addCap(&gi.Constraints, pb.CapSourceOCILayout)
source := NewSource(fmt.Sprintf("oci-layout://%s@%s", contentStoreID, dig), attrs, gi.Constraints)
return NewState(source.Output())
}
type OCILayoutOption interface {
SetOCILayoutOption(*OCILayoutInfo)
}
type ociLayoutOptionFunc func(*OCILayoutInfo)
func (fn ociLayoutOptionFunc) SetOCILayoutOption(li *OCILayoutInfo) {
fn(li)
}
func OCISessionID(id string) OCILayoutOption {
return ociLayoutOptionFunc(func(oi *OCILayoutInfo) {
oi.sessionID = id
})
}
func OCILayerLimit(limit int) OCILayoutOption {
return ociLayoutOptionFunc(func(oi *OCILayoutInfo) {
oi.layerLimit = &limit
})
}
type OCILayoutInfo struct {
constraintsWrapper
sessionID string
layerLimit *int
}
type DiffType string type DiffType string
const ( const (

View File

@ -455,6 +455,7 @@ type ConstraintsOpt interface {
HTTPOption HTTPOption
ImageOption ImageOption
GitOption GitOption
OCILayoutOption
} }
type constraintsOptFunc func(m *Constraints) type constraintsOptFunc func(m *Constraints)
@ -471,6 +472,10 @@ func (fn constraintsOptFunc) SetLocalOption(li *LocalInfo) {
li.applyConstraints(fn) li.applyConstraints(fn)
} }
func (fn constraintsOptFunc) SetOCILayoutOption(oi *OCILayoutInfo) {
oi.applyConstraints(fn)
}
func (fn constraintsOptFunc) SetHTTPOption(hi *HTTPInfo) { func (fn constraintsOptFunc) SetHTTPOption(hi *HTTPInfo) {
hi.applyConstraints(fn) hi.applyConstraints(fn)
} }

View File

@ -33,6 +33,7 @@ import (
type SolveOpt struct { type SolveOpt struct {
Exports []ExportEntry Exports []ExportEntry
LocalDirs map[string]string LocalDirs map[string]string
OCIStores map[string]content.Store
SharedKey string SharedKey string
Frontend string Frontend string
FrontendAttrs map[string]string FrontendAttrs map[string]string
@ -158,8 +159,27 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
} }
} }
if len(cacheOpt.contentStores) > 0 { // this is a new map that contains both cacheOpt stores and OCILayout stores
s.Allow(sessioncontent.NewAttachable(cacheOpt.contentStores)) contentStores := make(map[string]content.Store, len(cacheOpt.contentStores)+len(opt.OCIStores))
// copy over the stores references from cacheOpt
for key, store := range cacheOpt.contentStores {
contentStores[key] = store
}
// copy over the stores references from ociLayout opts
for key, store := range opt.OCIStores {
// conflicts are not allowed
if _, ok := contentStores[key]; ok {
// we probably should check if the store is identical, but given that
// https://pkg.go.dev/github.com/containerd/containerd/content#Store
// is just an interface, composing 4 others, that is rather hard to do.
// For a future iteration.
return nil, errors.Errorf("contentStore key %s exists in both cache and OCI layouts", key)
}
contentStores[key] = store
}
if len(contentStores) > 0 {
s.Allow(sessioncontent.NewAttachable(contentStores))
} }
eg.Go(func() error { eg.Go(func() error {

View File

@ -0,0 +1,115 @@
package exptypes
import (
"fmt"
"regexp"
"github.com/containerd/containerd/platforms"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
const (
AnnotationIndex = "index"
AnnotationIndexDescriptor = "index-descriptor"
AnnotationManifest = "manifest"
AnnotationManifestDescriptor = "manifest-descriptor"
)
var (
keyAnnotationRegexp = regexp.MustCompile(`^annotation(?:-([a-z-]+))?(?:\[([A-Za-z0-9_/-]+)\])?\.(\S+)$`)
)
type AnnotationKey struct {
Type string
Platform *ocispecs.Platform
Key string
}
func (k AnnotationKey) String() string {
prefix := "annotation"
switch k.Type {
case "":
case AnnotationManifest, AnnotationManifestDescriptor:
prefix += fmt.Sprintf("-%s", k.Type)
if p := k.PlatformString(); p != "" {
prefix += fmt.Sprintf("[%s]", p)
}
case AnnotationIndex, AnnotationIndexDescriptor:
prefix += "-" + k.Type
default:
panic("unknown annotation type")
}
return fmt.Sprintf("%s.%s", prefix, k.Key)
}
func (k AnnotationKey) PlatformString() string {
if k.Platform == nil {
return ""
}
return platforms.Format(*k.Platform)
}
func AnnotationIndexKey(key string) string {
return AnnotationKey{
Type: AnnotationIndex,
Key: key,
}.String()
}
func AnnotationIndexDescriptorKey(key string) string {
return AnnotationKey{
Type: AnnotationIndexDescriptor,
Key: key,
}.String()
}
func AnnotationManifestKey(p *ocispecs.Platform, key string) string {
return AnnotationKey{
Type: AnnotationManifest,
Platform: p,
Key: key,
}.String()
}
func AnnotationManifestDescriptorKey(p *ocispecs.Platform, key string) string {
return AnnotationKey{
Type: AnnotationManifestDescriptor,
Platform: p,
Key: key,
}.String()
}
func ParseAnnotationKey(result string) (AnnotationKey, bool, error) {
groups := keyAnnotationRegexp.FindStringSubmatch(result)
if groups == nil {
return AnnotationKey{}, false, nil
}
tp, platform, key := groups[1], groups[2], groups[3]
switch tp {
case AnnotationIndex, AnnotationIndexDescriptor, AnnotationManifest, AnnotationManifestDescriptor:
case "":
tp = AnnotationManifest
default:
return AnnotationKey{}, true, errors.Errorf("unrecognized annotation type %s", tp)
}
var ociPlatform *ocispecs.Platform
if platform != "" {
p, err := platforms.Parse(platform)
if err != nil {
return AnnotationKey{}, true, err
}
ociPlatform = &p
}
annotation := AnnotationKey{
Type: tp,
Platform: ociPlatform,
Key: key,
}
return annotation, true, nil
}

View File

@ -27,6 +27,7 @@ import (
"github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/solver/pb"
binfotypes "github.com/moby/buildkit/util/buildinfo/types" binfotypes "github.com/moby/buildkit/util/buildinfo/types"
digest "github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -455,7 +456,7 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) {
} }
c.Warn(ctx, defVtx, msg, warnOpts(sourceMap, location, detail, url)) c.Warn(ctx, defVtx, msg, warnOpts(sourceMap, location, detail, url))
}, },
ContextByName: contextByNameFunc(c, tp), ContextByName: contextByNameFunc(c, c.BuildOpts().SessionID, tp),
}) })
if err != nil { if err != nil {
@ -787,7 +788,7 @@ func warnOpts(sm *llb.SourceMap, r *parser.Range, detail [][]byte, url string) c
return opts return opts
} }
func contextByNameFunc(c client.Client, p *ocispecs.Platform) func(context.Context, string, string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) { func contextByNameFunc(c client.Client, sessionID string, p *ocispecs.Platform) func(context.Context, string, string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
return func(ctx context.Context, name, resolveMode string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) { return func(ctx context.Context, name, resolveMode string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
named, err := reference.ParseNormalizedNamed(name) named, err := reference.ParseNormalizedNamed(name)
if err != nil { if err != nil {
@ -801,7 +802,7 @@ func contextByNameFunc(c client.Client, p *ocispecs.Platform) func(context.Conte
} }
if p != nil { if p != nil {
name := name + "::" + platforms.Format(platforms.Normalize(*p)) name := name + "::" + platforms.Format(platforms.Normalize(*p))
st, img, bi, err := contextByName(ctx, c, name, p, resolveMode) st, img, bi, err := contextByName(ctx, c, sessionID, name, p, resolveMode)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -809,11 +810,11 @@ func contextByNameFunc(c client.Client, p *ocispecs.Platform) func(context.Conte
return st, img, bi, nil return st, img, bi, nil
} }
} }
return contextByName(ctx, c, name, p, resolveMode) return contextByName(ctx, c, sessionID, name, p, resolveMode)
} }
} }
func contextByName(ctx context.Context, c client.Client, name string, platform *ocispecs.Platform, resolveMode string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) { func contextByName(ctx context.Context, c client.Client, sessionID, name string, platform *ocispecs.Platform, resolveMode string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
opts := c.BuildOpts().Opts opts := c.BuildOpts().Opts
v, ok := opts["context:"+name] v, ok := opts["context:"+name]
if !ok { if !ok {
@ -846,9 +847,11 @@ func contextByName(ctx context.Context, c client.Client, name string, platform *
named = reference.TagNameOnly(named) named = reference.TagNameOnly(named)
_, data, err := c.ResolveImageConfig(ctx, named.String(), llb.ResolveImageConfigOpt{ _, data, err := c.ResolveImageConfig(ctx, named.String(), llb.ResolveImageConfigOpt{
Platform: platform, Platform: platform,
ResolveMode: resolveMode, ResolveMode: resolveMode,
LogName: fmt.Sprintf("[context %s] load metadata for %s", name, ref), LogName: fmt.Sprintf("[context %s] load metadata for %s", name, ref),
ResolverType: llb.ResolverTypeRegistry,
SessionID: sessionID,
}) })
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
@ -878,6 +881,49 @@ func contextByName(ctx context.Context, c client.Client, name string, platform *
st = &httpst st = &httpst
} }
return st, nil, nil, nil return st, nil, nil, nil
case "oci-layout":
ref := strings.TrimPrefix(vv[1], "//")
// expected format is storeID@hash
parts := strings.SplitN(ref, "@", 2)
if len(parts) != 2 {
return nil, nil, nil, errors.Errorf("invalid oci-layout format '%s', must be oci-layout:///content-store@sha256:digest", vv[1])
}
storeID := parts[0]
dig, err := digest.Parse(parts[1])
if err != nil {
return nil, nil, nil, errors.Errorf("invalid digest format '%s', must be oci-layout:///content-store@sha256:digest", vv[1])
}
// the ref now is "content-store@sha256:digest"
// ResolveImageConfig will try to treat that as a valid reference,
// to be parsed with https://pkg.go.dev/github.com/containerd/containerd@v1.6.6/reference#Parse
// That will fail, because it will think that "content-store@sha256" is a host and "digest" is a port.
// To get it to pass, we need to jury-rig it with a host, so that it is a legitimate ref and can
// be processed.
// A reasonable format is storeID as host, so
// storeID/image@digest
// We do not support any image lookup for now, so any image will do; it is ignored.
usableRef := fmt.Sprintf("%s/%s@%s", storeID, "image", dig)
_, data, err := c.ResolveImageConfig(ctx, usableRef, llb.ResolveImageConfigOpt{
Platform: platform,
ResolveMode: resolveMode,
LogName: fmt.Sprintf("[context %s] load metadata for %s", name, ref),
ResolverType: llb.ResolverTypeOCILayout,
})
if err != nil {
return nil, nil, nil, err
}
st := llb.OCILayout(storeID, dig,
llb.WithCustomName("[context "+name+"] OCI load from client"),
llb.OCISessionID(c.BuildOpts().SessionID),
)
st, err = st.WithImageConfig(data)
if err != nil {
return nil, nil, nil, err
}
return &st, nil, nil, nil
case "local": case "local":
st := llb.Local(vv[1], st := llb.Local(vv[1],
llb.SessionID(c.BuildOpts().SessionID), llb.SessionID(c.BuildOpts().SessionID),

View File

@ -332,9 +332,10 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
} }
prefix += "internal]" prefix += "internal]"
dgst, dt, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName, llb.ResolveImageConfigOpt{ dgst, dt, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName, llb.ResolveImageConfigOpt{
Platform: platform, Platform: platform,
ResolveMode: opt.ImageResolveMode.String(), ResolveMode: opt.ImageResolveMode.String(),
LogName: fmt.Sprintf("%s load metadata for %s", prefix, d.stage.BaseName), LogName: fmt.Sprintf("%s load metadata for %s", prefix, d.stage.BaseName),
ResolverType: llb.ResolverTypeRegistry,
}) })
if err != nil { if err != nil {
return suggest.WrapError(errors.Wrap(err, origName), origName, append(allStageNames, commonImageNames()...), true) return suggest.WrapError(errors.Wrap(err, origName), origName, append(allStageNames, commonImageNames()...), true)

View File

@ -459,7 +459,7 @@ func (c *grpcClient) ResolveImageConfig(ctx context.Context, ref string, opt llb
OSFeatures: platform.OSFeatures, OSFeatures: platform.OSFeatures,
} }
} }
resp, err := c.client.ResolveImageConfig(ctx, &pb.ResolveImageConfigRequest{Ref: ref, Platform: p, ResolveMode: opt.ResolveMode, LogName: opt.LogName}) resp, err := c.client.ResolveImageConfig(ctx, &pb.ResolveImageConfigRequest{Ref: ref, Platform: p, ResolveMode: opt.ResolveMode, LogName: opt.LogName, ResolverType: int32(opt.ResolverType), SessionID: opt.SessionID})
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }

View File

@ -489,6 +489,8 @@ type ResolveImageConfigRequest struct {
Platform *pb.Platform `protobuf:"bytes,2,opt,name=Platform,proto3" json:"Platform,omitempty"` Platform *pb.Platform `protobuf:"bytes,2,opt,name=Platform,proto3" json:"Platform,omitempty"`
ResolveMode string `protobuf:"bytes,3,opt,name=ResolveMode,proto3" json:"ResolveMode,omitempty"` ResolveMode string `protobuf:"bytes,3,opt,name=ResolveMode,proto3" json:"ResolveMode,omitempty"`
LogName string `protobuf:"bytes,4,opt,name=LogName,proto3" json:"LogName,omitempty"` LogName string `protobuf:"bytes,4,opt,name=LogName,proto3" json:"LogName,omitempty"`
ResolverType int32 `protobuf:"varint,5,opt,name=ResolverType,proto3" json:"ResolverType,omitempty"`
SessionID string `protobuf:"bytes,6,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -555,6 +557,20 @@ func (m *ResolveImageConfigRequest) GetLogName() string {
return "" return ""
} }
func (m *ResolveImageConfigRequest) GetResolverType() int32 {
if m != nil {
return m.ResolverType
}
return 0
}
func (m *ResolveImageConfigRequest) GetSessionID() string {
if m != nil {
return m.SessionID
}
return ""
}
type ResolveImageConfigResponse struct { type ResolveImageConfigResponse struct {
Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=Digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"Digest"` Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=Digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"Digest"`
Config []byte `protobuf:"bytes,2,opt,name=Config,proto3" json:"Config,omitempty"` Config []byte `protobuf:"bytes,2,opt,name=Config,proto3" json:"Config,omitempty"`
@ -2248,136 +2264,138 @@ func init() {
func init() { proto.RegisterFile("gateway.proto", fileDescriptor_f1a937782ebbded5) } func init() { proto.RegisterFile("gateway.proto", fileDescriptor_f1a937782ebbded5) }
var fileDescriptor_f1a937782ebbded5 = []byte{ var fileDescriptor_f1a937782ebbded5 = []byte{
// 2060 bytes of a gzipped FileDescriptorProto // 2086 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x6f, 0xe3, 0xc6, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x6f, 0xe3, 0xc6,
0x15, 0x37, 0x2d, 0xd9, 0x92, 0x9e, 0x3e, 0xd6, 0x99, 0xa4, 0x29, 0x43, 0x04, 0x1b, 0x87, 0x4d, 0x15, 0x37, 0xad, 0xef, 0xa7, 0x8f, 0x75, 0x26, 0x69, 0xca, 0x10, 0xc1, 0xc6, 0x61, 0x53, 0x57,
0x5d, 0xed, 0xc6, 0xa1, 0x52, 0x6f, 0x02, 0x6f, 0xbd, 0x45, 0xd2, 0xf5, 0x17, 0xec, 0xc4, 0xf6, 0xbb, 0x71, 0xa8, 0xd4, 0x9b, 0xc0, 0x5b, 0x6f, 0x91, 0x74, 0xfd, 0x05, 0x2b, 0xb1, 0xbd, 0xee,
0xba, 0xe3, 0x14, 0x0b, 0x04, 0x29, 0x50, 0x5a, 0x1c, 0x69, 0x89, 0xa5, 0x39, 0xec, 0x70, 0xb4, 0x38, 0xc5, 0x02, 0x41, 0x0a, 0x94, 0x16, 0x47, 0x5a, 0x62, 0x69, 0x92, 0x1d, 0x8e, 0xd6, 0xeb,
0x5e, 0x27, 0x97, 0xf6, 0xd6, 0x63, 0x81, 0x02, 0xbd, 0x16, 0xe8, 0x5f, 0xd0, 0x53, 0x8f, 0x3d, 0xe4, 0xd2, 0xde, 0x7a, 0xec, 0xa9, 0xd7, 0x02, 0xfd, 0x0b, 0x7a, 0xea, 0xb1, 0xe7, 0x1c, 0x7b,
0xe7, 0xd8, 0x63, 0xd1, 0x43, 0x50, 0xec, 0x1f, 0x51, 0xa0, 0xb7, 0xe2, 0xcd, 0x0c, 0x25, 0x4a, 0x29, 0x50, 0xf4, 0x10, 0x14, 0xfb, 0x47, 0x14, 0xe8, 0xad, 0x78, 0x33, 0x43, 0x89, 0x92, 0x65,
0x96, 0x29, 0x0b, 0x39, 0x69, 0xe6, 0xf1, 0xfd, 0xde, 0xbc, 0xaf, 0x79, 0xef, 0x8d, 0xa0, 0xd9, 0x4a, 0x42, 0x4e, 0x9a, 0x79, 0x7c, 0xbf, 0x37, 0xef, 0x6b, 0xde, 0x7b, 0x23, 0x68, 0x0e, 0x5c,
0xf7, 0x25, 0xbb, 0xf4, 0xaf, 0xbc, 0x44, 0x70, 0xc9, 0xc9, 0x5b, 0x17, 0xfc, 0xfc, 0xca, 0x3b, 0xc1, 0xae, 0xdc, 0x6b, 0x27, 0xe6, 0x91, 0x88, 0xc8, 0x5b, 0x97, 0xd1, 0xc5, 0xb5, 0x73, 0x31,
0x1f, 0x84, 0x51, 0xf0, 0x3c, 0x94, 0xde, 0x8b, 0x9f, 0x7a, 0x3d, 0xc1, 0x63, 0xc9, 0xe2, 0xc0, 0xf4, 0x03, 0xef, 0xb9, 0x2f, 0x9c, 0x17, 0x3f, 0x75, 0xfa, 0x3c, 0x0a, 0x05, 0x0b, 0x3d, 0xeb,
0xf9, 0xa0, 0x1f, 0xca, 0x67, 0x83, 0x73, 0xaf, 0xcb, 0x2f, 0x3a, 0x7d, 0xde, 0xe7, 0x1d, 0x85, 0x83, 0x81, 0x2f, 0x9e, 0x0d, 0x2f, 0x9c, 0x5e, 0x74, 0xd9, 0x19, 0x44, 0x83, 0xa8, 0x23, 0x11,
0x38, 0x1f, 0xf4, 0xd4, 0x4e, 0x6d, 0xd4, 0x4a, 0x4b, 0x72, 0x36, 0x26, 0xd9, 0xfb, 0x9c, 0xf7, 0x17, 0xc3, 0xbe, 0xdc, 0xc9, 0x8d, 0x5c, 0x29, 0x49, 0xd6, 0xd6, 0x34, 0xfb, 0x20, 0x8a, 0x06,
0x23, 0xe6, 0x27, 0x61, 0x6a, 0x96, 0x1d, 0x91, 0x74, 0x3b, 0xa9, 0xf4, 0xe5, 0x20, 0x35, 0x98, 0x01, 0x73, 0x63, 0x3f, 0xd1, 0xcb, 0x0e, 0x8f, 0x7b, 0x9d, 0x44, 0xb8, 0x62, 0x98, 0x68, 0xcc,
0xf5, 0x1c, 0x06, 0x15, 0xe9, 0x64, 0x8a, 0x74, 0x52, 0x1e, 0xbd, 0x60, 0xa2, 0x93, 0x9c, 0x77, 0x66, 0x06, 0x83, 0x8a, 0x74, 0x52, 0x45, 0x3a, 0x49, 0x14, 0xbc, 0x60, 0xbc, 0x13, 0x5f, 0x74,
0x78, 0x92, 0x71, 0x77, 0x6e, 0xe4, 0xf6, 0x93, 0xb0, 0x23, 0xaf, 0x12, 0x96, 0x76, 0x2e, 0xb9, 0xa2, 0x38, 0xe5, 0xee, 0xdc, 0xca, 0xed, 0xc6, 0x7e, 0x47, 0x5c, 0xc7, 0x2c, 0xe9, 0x5c, 0x45,
0x78, 0xce, 0x84, 0x01, 0x3c, 0xb8, 0x11, 0x30, 0x90, 0x61, 0x84, 0xa8, 0xae, 0x9f, 0xa4, 0x78, 0xfc, 0x39, 0xe3, 0x1a, 0xf0, 0xe0, 0x56, 0xc0, 0x50, 0xf8, 0x01, 0xa2, 0x7a, 0x6e, 0x9c, 0xe0,
0x08, 0xfe, 0x1a, 0x50, 0xde, 0x6c, 0xc9, 0xe3, 0x30, 0x95, 0x61, 0xd8, 0x0f, 0x3b, 0xbd, 0x54, 0x21, 0xf8, 0xab, 0x41, 0x59, 0xb3, 0x45, 0x14, 0xfa, 0x89, 0xf0, 0xfd, 0x81, 0xdf, 0xe9, 0x27,
0x61, 0xf4, 0x29, 0x68, 0x84, 0x66, 0x77, 0xff, 0x50, 0x82, 0x65, 0xca, 0xd2, 0x41, 0x24, 0xc9, 0x12, 0xa3, 0x4e, 0x41, 0x23, 0x14, 0xbb, 0xfd, 0x87, 0x02, 0x94, 0x29, 0x4b, 0x86, 0x81, 0x20,
0x1a, 0x34, 0x05, 0xeb, 0xed, 0xb2, 0x44, 0xb0, 0xae, 0x2f, 0x59, 0x60, 0x5b, 0xab, 0x56, 0xbb, 0x1b, 0xd0, 0xe4, 0xac, 0xbf, 0xcf, 0x62, 0xce, 0x7a, 0xae, 0x60, 0x9e, 0x69, 0xac, 0x1b, 0xed,
0x76, 0xb0, 0x40, 0xc7, 0xc9, 0xe4, 0x57, 0xd0, 0x12, 0xac, 0x97, 0xe6, 0x18, 0x17, 0x57, 0xad, 0xda, 0xd1, 0x0a, 0x9d, 0x24, 0x93, 0x5f, 0x41, 0x8b, 0xb3, 0x7e, 0x92, 0x61, 0x5c, 0x5d, 0x37,
0x76, 0x7d, 0xe3, 0x7d, 0xef, 0xc6, 0x60, 0x78, 0x94, 0xf5, 0x8e, 0xfd, 0x64, 0x04, 0x39, 0x58, 0xda, 0xf5, 0xad, 0xf7, 0x9d, 0x5b, 0x83, 0xe1, 0x50, 0xd6, 0x3f, 0x71, 0xe3, 0x31, 0xe4, 0x68,
0xa0, 0x13, 0x42, 0xc8, 0x06, 0x94, 0x04, 0xeb, 0xd9, 0x25, 0x25, 0xeb, 0x6e, 0xb1, 0xac, 0x83, 0x85, 0x4e, 0x09, 0x21, 0x5b, 0x50, 0xe0, 0xac, 0x6f, 0x16, 0xa4, 0xac, 0xbb, 0xf9, 0xb2, 0x8e,
0x05, 0x8a, 0xcc, 0x64, 0x13, 0xca, 0x28, 0xc5, 0x2e, 0x2b, 0xd0, 0xbb, 0x33, 0x15, 0x38, 0x58, 0x56, 0x28, 0x32, 0x93, 0x6d, 0x28, 0xa2, 0x14, 0xb3, 0x28, 0x41, 0xef, 0xce, 0x55, 0xe0, 0x68,
0xa0, 0x0a, 0x40, 0x3e, 0x87, 0xea, 0x05, 0x93, 0x7e, 0xe0, 0x4b, 0xdf, 0x86, 0xd5, 0x52, 0xbb, 0x85, 0x4a, 0x00, 0xf9, 0x1c, 0xaa, 0x97, 0x4c, 0xb8, 0x9e, 0x2b, 0x5c, 0x13, 0xd6, 0x0b, 0xed,
0xbe, 0xd1, 0x29, 0x04, 0xa3, 0x83, 0xbc, 0x63, 0x83, 0xd8, 0x8b, 0xa5, 0xb8, 0xa2, 0x43, 0x01, 0xfa, 0x56, 0x27, 0x17, 0x8c, 0x0e, 0x72, 0x4e, 0x34, 0xe2, 0x20, 0x14, 0xfc, 0x9a, 0x8e, 0x04,
0xce, 0x23, 0x68, 0x8e, 0x7d, 0x22, 0x2b, 0x50, 0x7a, 0xce, 0xae, 0xb4, 0xff, 0x28, 0x2e, 0xc9, 0x58, 0x8f, 0xa0, 0x39, 0xf1, 0x89, 0xac, 0x41, 0xe1, 0x39, 0xbb, 0x56, 0xfe, 0xa3, 0xb8, 0x24,
0x1b, 0xb0, 0xf4, 0xc2, 0x8f, 0x06, 0x4c, 0xb9, 0xaa, 0x41, 0xf5, 0x66, 0x6b, 0xf1, 0xa1, 0xb5, 0x6f, 0x40, 0xe9, 0x85, 0x1b, 0x0c, 0x99, 0x74, 0x55, 0x83, 0xaa, 0xcd, 0xce, 0xea, 0x43, 0x63,
0x5d, 0x85, 0x65, 0xa1, 0xc4, 0xbb, 0x7f, 0xb6, 0x60, 0x65, 0xd2, 0x4f, 0xe4, 0xd0, 0x58, 0x68, 0xb7, 0x0a, 0x65, 0x2e, 0xc5, 0xdb, 0x7f, 0x32, 0x60, 0x6d, 0xda, 0x4f, 0xa4, 0xab, 0x2d, 0x34,
0x29, 0x25, 0x3f, 0x9e, 0xc3, 0xc5, 0x48, 0x48, 0xb5, 0xaa, 0x4a, 0x84, 0xb3, 0x09, 0xb5, 0x21, 0xa4, 0x92, 0x1f, 0x2f, 0xe1, 0x62, 0x24, 0x24, 0x4a, 0x55, 0x29, 0xc2, 0xda, 0x86, 0xda, 0x88,
0x69, 0x96, 0x8a, 0xb5, 0x9c, 0x8a, 0xee, 0x26, 0x94, 0x28, 0xeb, 0x91, 0x16, 0x2c, 0x86, 0x26, 0x34, 0x4f, 0xc5, 0x5a, 0x46, 0x45, 0x7b, 0x1b, 0x0a, 0x94, 0xf5, 0x49, 0x0b, 0x56, 0x7d, 0x9d,
0x29, 0xe8, 0x62, 0x18, 0x90, 0x55, 0x28, 0x05, 0xac, 0x67, 0x82, 0xdf, 0xf2, 0x92, 0x73, 0x6f, 0x14, 0x74, 0xd5, 0xf7, 0xc8, 0x3a, 0x14, 0x3c, 0xd6, 0xd7, 0xc1, 0x6f, 0x39, 0xf1, 0x85, 0xb3,
0x97, 0xf5, 0xc2, 0x38, 0x94, 0x21, 0x8f, 0x29, 0x7e, 0x72, 0xff, 0x6a, 0x61, 0x72, 0xa1, 0x5a, 0xcf, 0xfa, 0x7e, 0xe8, 0x0b, 0x3f, 0x0a, 0x29, 0x7e, 0xb2, 0xff, 0x62, 0x60, 0x72, 0xa1, 0x5a,
0xe4, 0xd3, 0x31, 0x3b, 0x66, 0xa7, 0xca, 0x35, 0xed, 0x9f, 0x16, 0x6b, 0xff, 0x51, 0x5e, 0xfb, 0xe4, 0xd3, 0x09, 0x3b, 0xe6, 0xa7, 0xca, 0x0d, 0xed, 0x9f, 0xe6, 0x6b, 0xff, 0x51, 0x56, 0xfb,
0x99, 0xf9, 0x93, 0xb7, 0x4e, 0x42, 0x93, 0x32, 0x39, 0x10, 0x31, 0x65, 0xbf, 0x1d, 0xb0, 0x54, 0xb9, 0xf9, 0x93, 0xb5, 0x4e, 0x40, 0x93, 0x32, 0x31, 0xe4, 0x21, 0x65, 0xbf, 0x1d, 0xb2, 0x44,
0x92, 0x9f, 0x65, 0x11, 0x51, 0xf2, 0x67, 0xa5, 0x15, 0x32, 0x52, 0x03, 0x20, 0x6d, 0x58, 0x62, 0x90, 0x9f, 0xa5, 0x11, 0x91, 0xf2, 0xe7, 0xa5, 0x15, 0x32, 0x52, 0x0d, 0x20, 0x6d, 0x28, 0x31,
0x42, 0x70, 0x61, 0xb4, 0x20, 0x9e, 0xae, 0x1c, 0x9e, 0x48, 0xba, 0xde, 0x99, 0xaa, 0x1c, 0x54, 0xce, 0x23, 0xae, 0xb5, 0x20, 0x8e, 0xaa, 0x1c, 0x0e, 0x8f, 0x7b, 0xce, 0xb9, 0xac, 0x1c, 0x54,
0x33, 0xb8, 0x2b, 0xd0, 0xca, 0x4e, 0x4d, 0x13, 0x1e, 0xa7, 0xcc, 0xbd, 0x03, 0xcd, 0xc3, 0x38, 0x31, 0xd8, 0x6b, 0xd0, 0x4a, 0x4f, 0x4d, 0xe2, 0x28, 0x4c, 0x98, 0x7d, 0x07, 0x9a, 0xdd, 0x30,
0x19, 0xc8, 0xd4, 0xe8, 0xe1, 0xfe, 0xc3, 0x82, 0x56, 0x46, 0xd1, 0x3c, 0xe4, 0x2b, 0xa8, 0x8f, 0x1e, 0x8a, 0x44, 0xeb, 0x61, 0xff, 0xdd, 0x80, 0x56, 0x4a, 0x51, 0x3c, 0xe4, 0x2b, 0xa8, 0x8f,
0x7c, 0x9c, 0x39, 0x73, 0xab, 0x40, 0xbf, 0x71, 0x7c, 0x2e, 0x40, 0xc6, 0xb7, 0x79, 0x71, 0xce, 0x7d, 0x9c, 0x3a, 0x73, 0x27, 0x47, 0xbf, 0x49, 0x7c, 0x26, 0x40, 0xda, 0xb7, 0x59, 0x71, 0xd6,
0x09, 0xac, 0x4c, 0x32, 0x4c, 0xf1, 0xf4, 0x7b, 0xe3, 0x9e, 0x9e, 0x0c, 0x7c, 0xce, 0xb3, 0x7f, 0x29, 0xac, 0x4d, 0x33, 0xcc, 0xf0, 0xf4, 0x7b, 0x93, 0x9e, 0x9e, 0x0e, 0x7c, 0xc6, 0xb3, 0xff,
0xb2, 0xe0, 0x2d, 0xca, 0x54, 0x29, 0x3c, 0xbc, 0xf0, 0xfb, 0x6c, 0x87, 0xc7, 0xbd, 0xb0, 0x9f, 0x34, 0xe0, 0x2d, 0xca, 0x64, 0x29, 0xec, 0x5e, 0xba, 0x03, 0xb6, 0x17, 0x85, 0x7d, 0x7f, 0x90,
0xb9, 0x79, 0x45, 0x65, 0x55, 0x26, 0x19, 0x13, 0xac, 0x0d, 0xd5, 0xd3, 0xc8, 0x97, 0x3d, 0x2e, 0xba, 0x79, 0x4d, 0x66, 0x55, 0x2a, 0x19, 0x13, 0xac, 0x0d, 0xd5, 0xb3, 0xc0, 0x15, 0xfd, 0x88,
0x2e, 0x8c, 0xf0, 0x06, 0x0a, 0xcf, 0x68, 0x74, 0xf8, 0x95, 0xac, 0x42, 0xdd, 0x08, 0x3e, 0xe6, 0x5f, 0x6a, 0xe1, 0x0d, 0x14, 0x9e, 0xd2, 0xe8, 0xe8, 0x2b, 0x59, 0x87, 0xba, 0x16, 0x7c, 0x12,
0x01, 0x53, 0x35, 0xa3, 0x46, 0xf3, 0x24, 0x62, 0x43, 0xe5, 0x88, 0xf7, 0x4f, 0xfc, 0x0b, 0xa6, 0x79, 0x4c, 0xd6, 0x8c, 0x1a, 0xcd, 0x92, 0x88, 0x09, 0x95, 0xe3, 0x68, 0x70, 0xea, 0x5e, 0x32,
0x8a, 0x43, 0x8d, 0x66, 0x5b, 0xf7, 0x77, 0x16, 0x38, 0xd3, 0xb4, 0x32, 0x2e, 0xfe, 0x0c, 0x96, 0x59, 0x1c, 0x6a, 0x34, 0xdd, 0x12, 0x1b, 0x1a, 0x9a, 0x91, 0x7f, 0x71, 0x1d, 0x33, 0xb3, 0xb4,
0x77, 0xc3, 0x3e, 0x4b, 0x75, 0xf4, 0x6b, 0xdb, 0x1b, 0xdf, 0x7e, 0xf7, 0xce, 0xc2, 0xbf, 0xbf, 0x6e, 0xb4, 0x4b, 0x74, 0x82, 0x46, 0xde, 0x86, 0xda, 0x39, 0x4b, 0x12, 0x3f, 0x0a, 0xbb, 0xfb,
0x7b, 0xe7, 0x7e, 0xae, 0xae, 0xf2, 0x84, 0xc5, 0x5d, 0x1e, 0x4b, 0x3f, 0x8c, 0x99, 0xc0, 0xf6, 0x66, 0x59, 0xe2, 0xc7, 0x04, 0xfb, 0x77, 0x06, 0x58, 0xb3, 0xec, 0xd2, 0x41, 0xfa, 0x0c, 0xca,
0xf0, 0x41, 0xa0, 0x20, 0x9e, 0x46, 0x52, 0x23, 0x81, 0xbc, 0x09, 0xcb, 0x5a, 0xba, 0xb9, 0xf6, 0xfb, 0xfe, 0x80, 0x25, 0x2a, 0x7f, 0x6a, 0xbb, 0x5b, 0xdf, 0x7e, 0xf7, 0xce, 0xca, 0xbf, 0xbf,
0x66, 0xe7, 0xfe, 0x7d, 0x09, 0x1a, 0x67, 0xa8, 0x40, 0xe6, 0x0b, 0x0f, 0x60, 0xe4, 0x42, 0x93, 0x7b, 0xe7, 0x7e, 0xa6, 0x32, 0x47, 0x31, 0x0b, 0x7b, 0x51, 0x28, 0x5c, 0x3f, 0x64, 0x1c, 0x1b,
0x76, 0x93, 0x8e, 0xcd, 0x71, 0x10, 0x07, 0xaa, 0xfb, 0x26, 0xc4, 0xe6, 0xba, 0x0e, 0xf7, 0xe4, 0xcc, 0x07, 0x9e, 0x84, 0x38, 0x0a, 0x49, 0xb5, 0x04, 0xf2, 0x26, 0x94, 0x95, 0x74, 0x5d, 0x38,
0x4b, 0xa8, 0x67, 0xeb, 0x27, 0x89, 0xb4, 0x4b, 0x2a, 0x47, 0x1e, 0x16, 0xe4, 0x48, 0x5e, 0x13, 0xf4, 0xce, 0xfe, 0x5b, 0x09, 0x1a, 0xe7, 0xa8, 0x40, 0xea, 0x4d, 0x07, 0x60, 0x1c, 0x04, 0x9d,
0x2f, 0x07, 0x35, 0x19, 0x92, 0xa3, 0x90, 0x75, 0x78, 0xcd, 0x8f, 0x22, 0x7e, 0x69, 0xd2, 0x5e, 0xb8, 0xd3, 0xa1, 0xc9, 0x70, 0x10, 0x0b, 0xaa, 0x87, 0x3a, 0x49, 0xf4, 0x85, 0x1f, 0xed, 0xc9,
0x25, 0xb0, 0xbd, 0xb4, 0x6a, 0xb5, 0xab, 0xf4, 0xfa, 0x07, 0xf2, 0x21, 0xbc, 0x9e, 0x23, 0x3e, 0x97, 0x50, 0x4f, 0xd7, 0x4f, 0x62, 0x61, 0x16, 0x64, 0x96, 0x3d, 0xcc, 0xc9, 0xb2, 0xac, 0x26,
0x16, 0xc2, 0xbf, 0xc2, 0x88, 0x2f, 0x2b, 0xfe, 0x69, 0x9f, 0xb0, 0x06, 0xed, 0x87, 0xb1, 0x1f, 0x4e, 0x06, 0xaa, 0x73, 0x2c, 0x43, 0x21, 0x9b, 0xf0, 0x9a, 0x1b, 0x04, 0xd1, 0x95, 0xbe, 0x38,
0xd9, 0xa0, 0x78, 0xf4, 0x86, 0xb8, 0xd0, 0xd8, 0x7b, 0x99, 0x70, 0x21, 0x99, 0x78, 0x2c, 0xa5, 0xf2, 0x0a, 0xc8, 0x10, 0x54, 0xe9, 0xcd, 0x0f, 0xe4, 0x43, 0x78, 0x3d, 0x43, 0x7c, 0xcc, 0xb9,
0xb0, 0xeb, 0xca, 0x99, 0x63, 0x34, 0x72, 0x0a, 0x8d, 0x1d, 0xbf, 0xfb, 0x8c, 0x1d, 0x5e, 0x20, 0x7b, 0x8d, 0x39, 0x53, 0x96, 0xfc, 0xb3, 0x3e, 0x61, 0x15, 0x3b, 0xf4, 0x43, 0x37, 0x30, 0x41,
0x31, 0xb5, 0x1b, 0xca, 0xec, 0xf5, 0x02, 0xb3, 0x15, 0xfb, 0x93, 0x24, 0x77, 0x19, 0xc6, 0x24, 0xf2, 0xa8, 0x0d, 0xc6, 0xfc, 0xe0, 0x65, 0x1c, 0x71, 0xc1, 0xf8, 0x63, 0x21, 0xb8, 0x59, 0x97,
0x90, 0x2e, 0xb4, 0x32, 0xd3, 0xf5, 0x2d, 0xb2, 0x9b, 0x4a, 0xe6, 0xa3, 0x79, 0x5d, 0xa9, 0xd1, 0xce, 0x9c, 0xa0, 0x91, 0x33, 0x68, 0xec, 0xb9, 0xbd, 0x67, 0xac, 0x7b, 0x89, 0xc4, 0xc4, 0x6c,
0xfa, 0x88, 0x09, 0x91, 0x18, 0xc8, 0x3d, 0xbc, 0x30, 0xbe, 0x64, 0x76, 0x4b, 0xd9, 0x3c, 0xdc, 0x48, 0xb3, 0x37, 0x73, 0xcc, 0x96, 0xec, 0x4f, 0xe2, 0xcc, 0x75, 0x9a, 0x90, 0x40, 0x7a, 0xd0,
0x3b, 0x9f, 0xc0, 0xca, 0x64, 0x34, 0xe6, 0x29, 0xdb, 0xce, 0x2f, 0xe1, 0xf5, 0x29, 0x2a, 0x7c, 0x4a, 0x4d, 0x57, 0xf7, 0xd0, 0x6c, 0x4a, 0x99, 0x8f, 0x96, 0x75, 0xa5, 0x42, 0xab, 0x23, 0xa6,
0xaf, 0x1b, 0xfd, 0x37, 0x0b, 0x5e, 0xbb, 0xe6, 0x37, 0x42, 0xa0, 0xfc, 0xc5, 0x55, 0xc2, 0x8c, 0x44, 0x62, 0x20, 0x0f, 0xf0, 0xca, 0xb9, 0x82, 0x99, 0x2d, 0x69, 0xf3, 0x68, 0x6f, 0x7d, 0x02,
0x48, 0xb5, 0x26, 0xc7, 0xb0, 0x84, 0x71, 0x49, 0xed, 0x45, 0xe5, 0xb4, 0xcd, 0x79, 0x02, 0xe1, 0x6b, 0xd3, 0xd1, 0x58, 0xa6, 0xf0, 0x5b, 0xbf, 0x84, 0xd7, 0x67, 0xa8, 0xf0, 0xbd, 0x6a, 0xc2,
0x29, 0xa4, 0x76, 0x98, 0x96, 0xe2, 0x3c, 0x04, 0x18, 0x11, 0xe7, 0x6a, 0x5e, 0x5f, 0x41, 0xd3, 0x5f, 0x0d, 0x78, 0xed, 0x86, 0xdf, 0x08, 0x81, 0xa2, 0xbc, 0x8b, 0x4a, 0xa4, 0x5c, 0x93, 0x13,
0x44, 0xc5, 0x5c, 0xf0, 0x15, 0x3d, 0x67, 0x18, 0x30, 0x4e, 0x11, 0xa3, 0x82, 0x5f, 0x9a, 0xb3, 0x28, 0x61, 0x5c, 0x12, 0x73, 0x55, 0x3a, 0x6d, 0x7b, 0x99, 0x40, 0x38, 0x12, 0xa9, 0x1c, 0xa6,
0xe0, 0xbb, 0xdf, 0xc0, 0x1d, 0xca, 0xfc, 0x60, 0x3f, 0x8c, 0xd8, 0xcd, 0x75, 0x0d, 0x6f, 0x6b, 0xa4, 0x58, 0x0f, 0x01, 0xc6, 0xc4, 0xa5, 0xda, 0xdf, 0x57, 0xd0, 0xd4, 0x51, 0xd1, 0x17, 0x7c,
0x18, 0xb1, 0x53, 0x5f, 0x3e, 0x1b, 0xde, 0x56, 0xb3, 0x27, 0x5b, 0xb0, 0x44, 0xfd, 0xb8, 0xcf, 0x4d, 0x4d, 0x2a, 0x1a, 0x8c, 0x73, 0xc8, 0xb8, 0x65, 0x14, 0x96, 0x6c, 0x19, 0xf6, 0x37, 0x70,
0xcc, 0xd1, 0xef, 0x15, 0x1c, 0xad, 0x0e, 0x41, 0x5e, 0xaa, 0x21, 0xee, 0x23, 0xa8, 0x0d, 0x69, 0x87, 0x32, 0xd7, 0x3b, 0xf4, 0x03, 0x76, 0x7b, 0x65, 0xc4, 0xdb, 0xea, 0x07, 0xec, 0xcc, 0x15,
0x58, 0x6b, 0x9e, 0xf4, 0x7a, 0x29, 0xd3, 0x75, 0xab, 0x44, 0xcd, 0x0e, 0xe9, 0x47, 0x2c, 0xee, 0xcf, 0x46, 0xb7, 0x55, 0xef, 0xc9, 0x0e, 0x94, 0xa8, 0x1b, 0x0e, 0x98, 0x3e, 0xfa, 0xbd, 0x9c,
0x9b, 0xa3, 0x4b, 0xd4, 0xec, 0xdc, 0x35, 0x1c, 0x36, 0x32, 0xcd, 0x8d, 0x6b, 0x08, 0x94, 0x77, 0xa3, 0xe5, 0x21, 0xc8, 0x4b, 0x15, 0xc4, 0x7e, 0x04, 0xb5, 0x11, 0x0d, 0x6b, 0xcd, 0x93, 0x7e,
0x71, 0x22, 0xb2, 0xd4, 0x05, 0x53, 0x6b, 0x37, 0xc0, 0x46, 0xe5, 0x07, 0xbb, 0xa1, 0xb8, 0xd9, 0x3f, 0x61, 0xaa, 0x6e, 0x15, 0xa8, 0xde, 0x21, 0xfd, 0x98, 0x85, 0x03, 0x7d, 0x74, 0x81, 0xea,
0x40, 0x1b, 0x2a, 0xbb, 0xa1, 0xc8, 0xd9, 0x97, 0x6d, 0xc9, 0x1a, 0xb6, 0xb0, 0x6e, 0x34, 0x08, 0x9d, 0xbd, 0x81, 0xe3, 0x4a, 0xaa, 0xb9, 0x76, 0x0d, 0x81, 0xe2, 0x3e, 0xce, 0x54, 0x86, 0xbc,
0xd0, 0x5a, 0xc9, 0x44, 0x6c, 0x6a, 0xf5, 0x04, 0xd5, 0xfd, 0x54, 0xfb, 0x51, 0x9d, 0x62, 0x94, 0x60, 0x72, 0x6d, 0x7b, 0xd8, 0xea, 0x5c, 0x6f, 0xdf, 0xe7, 0xb7, 0x1b, 0x68, 0x42, 0x65, 0xdf,
0x59, 0x87, 0x0a, 0x8b, 0xa5, 0x08, 0x59, 0xd6, 0xe7, 0x88, 0xa7, 0x87, 0x58, 0x4f, 0x0d, 0xb1, 0xe7, 0x19, 0xfb, 0xd2, 0x2d, 0xd9, 0xc0, 0x26, 0xd8, 0x0b, 0x86, 0x1e, 0x5a, 0x2b, 0x18, 0x0f,
0xaa, 0x9f, 0xd2, 0x8c, 0xc5, 0xdd, 0x84, 0x3b, 0x48, 0x28, 0x0e, 0x04, 0x81, 0x72, 0x4e, 0x49, 0x75, 0xb5, 0x9f, 0xa2, 0xda, 0x9f, 0x2a, 0x3f, 0xca, 0x53, 0xb4, 0x32, 0x9b, 0x50, 0x61, 0xa1,
0xb5, 0x76, 0xb7, 0x60, 0x65, 0x04, 0x34, 0x47, 0xaf, 0x41, 0x19, 0x47, 0x64, 0x53, 0x88, 0xa7, 0xe0, 0x3e, 0x4b, 0x3b, 0x25, 0x71, 0xd4, 0x18, 0xec, 0xc8, 0x31, 0x58, 0x76, 0x64, 0x9a, 0xb2,
0x9d, 0xab, 0xbe, 0xbb, 0x4d, 0xa8, 0x9f, 0x86, 0x71, 0xd6, 0xd1, 0xdc, 0x57, 0x16, 0x34, 0x4e, 0xd8, 0xdb, 0x70, 0x07, 0x09, 0xf9, 0x81, 0x20, 0x50, 0xcc, 0x28, 0x29, 0xd7, 0xf6, 0x0e, 0xac,
0x79, 0x3c, 0xea, 0x25, 0xa7, 0x70, 0x27, 0xbb, 0x81, 0x8f, 0x4f, 0x0f, 0x77, 0xfc, 0x24, 0x33, 0x8d, 0x81, 0xfa, 0xe8, 0x0d, 0x28, 0xe2, 0x90, 0xad, 0x0b, 0xf1, 0xac, 0x73, 0xe5, 0x77, 0xbb,
0x65, 0xf5, 0x7a, 0x98, 0xcd, 0x34, 0xef, 0x69, 0xc6, 0xed, 0x32, 0xb6, 0x1d, 0x3a, 0x09, 0x27, 0x09, 0xf5, 0x33, 0x3f, 0x4c, 0x7b, 0xa2, 0xfd, 0xca, 0x80, 0xc6, 0x59, 0x14, 0x8e, 0x7b, 0xc9,
0xbf, 0x80, 0xca, 0xd1, 0xd1, 0xb6, 0x92, 0xb4, 0x38, 0x97, 0xa4, 0x0c, 0x46, 0x3e, 0x81, 0xca, 0x19, 0xdc, 0x49, 0x6f, 0xe0, 0xe3, 0xb3, 0xee, 0x9e, 0x1b, 0xa7, 0xa6, 0xac, 0xdf, 0x0c, 0xb3,
0x53, 0xf5, 0xc8, 0x48, 0x4d, 0x6b, 0x98, 0x92, 0x72, 0xda, 0x50, 0xcd, 0x46, 0x59, 0x97, 0x8b, 0x7e, 0x0f, 0x38, 0x8a, 0x71, 0xb7, 0x88, 0x6d, 0x87, 0x4e, 0xc3, 0xc9, 0x2f, 0xa0, 0x72, 0x7c,
0x80, 0x66, 0x20, 0xf7, 0xbf, 0x16, 0xd4, 0x9f, 0xfa, 0xa3, 0x69, 0xe9, 0x33, 0x58, 0x0e, 0xbe, 0xbc, 0x2b, 0x25, 0xad, 0x2e, 0x25, 0x29, 0x85, 0x91, 0x4f, 0xa0, 0xf2, 0x54, 0x3e, 0x53, 0x12,
0x77, 0xbf, 0xd4, 0x5b, 0xbc, 0xc5, 0x11, 0x7b, 0xc1, 0x22, 0x93, 0xaa, 0x7a, 0x83, 0xd4, 0xf4, 0xdd, 0x1a, 0x66, 0xa4, 0x9c, 0x32, 0x54, 0xb1, 0x51, 0xd6, 0x8b, 0xb8, 0x47, 0x53, 0x90, 0xfd,
0x19, 0x17, 0xfa, 0x76, 0x36, 0xa8, 0xde, 0x60, 0x5e, 0x07, 0x4c, 0xfa, 0x61, 0x64, 0x97, 0x57, 0x5f, 0x03, 0xea, 0x4f, 0xdd, 0xf1, 0xbc, 0xf5, 0x19, 0x94, 0xbd, 0xef, 0xdd, 0x2f, 0xd5, 0x16,
0x4b, 0xd8, 0x5b, 0xf5, 0x0e, 0xa3, 0x3e, 0x10, 0x91, 0x6a, 0x4a, 0x35, 0x8a, 0x4b, 0xe2, 0x42, 0x6f, 0x71, 0xc0, 0x5e, 0xb0, 0x40, 0xa7, 0xaa, 0xda, 0x20, 0x35, 0x79, 0x16, 0x71, 0x75, 0x3b,
0x39, 0x8c, 0x7b, 0x5c, 0xf5, 0x1d, 0x53, 0xdd, 0xce, 0xf8, 0x40, 0x74, 0xd9, 0x61, 0xdc, 0xe3, 0x1b, 0x54, 0x6d, 0x30, 0xaf, 0x3d, 0x26, 0x5c, 0x3f, 0x30, 0x8b, 0xeb, 0x05, 0xec, 0xad, 0x6a,
0x54, 0x7d, 0x23, 0xef, 0xc2, 0xb2, 0xc0, 0x6b, 0x94, 0xda, 0x15, 0xe5, 0x94, 0x1a, 0x72, 0xe9, 0x87, 0x51, 0x1f, 0xf2, 0x40, 0x36, 0xa5, 0x1a, 0xc5, 0x25, 0xb1, 0xa1, 0xe8, 0x87, 0xfd, 0x48,
0xcb, 0x66, 0x3e, 0xb8, 0x2d, 0x68, 0x68, 0xbb, 0xcd, 0xbc, 0xf6, 0xc7, 0x45, 0x78, 0xfd, 0x84, 0xf6, 0x1d, 0x5d, 0xdd, 0xce, 0xa3, 0x21, 0xef, 0xb1, 0x6e, 0xd8, 0x8f, 0xa8, 0xfc, 0x46, 0xde,
0x5d, 0xee, 0x64, 0x76, 0x65, 0x0e, 0x59, 0x85, 0xfa, 0x90, 0x76, 0xb8, 0x6b, 0xd2, 0x2f, 0x4f, 0x85, 0x32, 0xc7, 0x6b, 0x94, 0x98, 0x15, 0xe9, 0x94, 0x1a, 0x72, 0xa9, 0xcb, 0xa6, 0x3f, 0xd8,
0xc2, 0xc3, 0x8e, 0xf9, 0x20, 0x96, 0x59, 0x0c, 0xd5, 0x61, 0x8a, 0x42, 0xcd, 0x07, 0xf2, 0x63, 0x2d, 0x68, 0x28, 0xbb, 0xf5, 0xc4, 0xf7, 0xc7, 0x55, 0x78, 0xfd, 0x94, 0x5d, 0xed, 0xa5, 0x76,
0xa8, 0x9c, 0x30, 0x89, 0xaf, 0x41, 0x65, 0x75, 0x6b, 0xa3, 0x8e, 0x3c, 0x27, 0x4c, 0xe2, 0x70, 0xa5, 0x0e, 0x59, 0x87, 0xfa, 0x88, 0xd6, 0xdd, 0xd7, 0xe9, 0x97, 0x25, 0xe1, 0x61, 0x27, 0xd1,
0x43, 0xb3, 0x6f, 0x38, 0x31, 0x25, 0xd9, 0xc4, 0x54, 0x9e, 0x36, 0x31, 0x65, 0x5f, 0xc9, 0x26, 0x30, 0x14, 0x69, 0x0c, 0xe5, 0x61, 0x92, 0x42, 0xf5, 0x07, 0xf2, 0x63, 0xa8, 0x9c, 0x32, 0x81,
0xd4, 0xbb, 0x3c, 0x4e, 0xa5, 0xf0, 0x43, 0x3c, 0x78, 0x49, 0x31, 0xff, 0x00, 0x99, 0x75, 0x60, 0xef, 0x49, 0x69, 0x75, 0x6b, 0xab, 0x8e, 0x3c, 0xa7, 0x4c, 0xe0, 0x78, 0x44, 0xd3, 0x6f, 0x38,
0x77, 0x46, 0x1f, 0x69, 0x9e, 0x93, 0xdc, 0x07, 0x60, 0x2f, 0xa5, 0xf0, 0x0f, 0x78, 0x2a, 0x53, 0x73, 0xc5, 0xe9, 0xcc, 0x55, 0x9c, 0x35, 0x73, 0xa5, 0x5f, 0xc9, 0x36, 0xd4, 0x7b, 0x51, 0x98,
0x7b, 0x59, 0x29, 0x0c, 0x88, 0x43, 0xc2, 0xe1, 0x29, 0xcd, 0x7d, 0x75, 0xdf, 0x84, 0x37, 0xc6, 0x08, 0xee, 0xfa, 0x78, 0x70, 0x49, 0x32, 0xff, 0x00, 0x99, 0x55, 0x60, 0xf7, 0xc6, 0x1f, 0x69,
0x3d, 0x62, 0x5c, 0xf5, 0x08, 0x7e, 0x48, 0x59, 0xc4, 0xfc, 0x94, 0xcd, 0xef, 0x2d, 0xd7, 0x01, 0x96, 0x93, 0xdc, 0x07, 0x60, 0x2f, 0x05, 0x77, 0x8f, 0xa2, 0x44, 0x24, 0x66, 0x59, 0x2a, 0x0c,
0xfb, 0x3a, 0xd8, 0x08, 0xfe, 0x5f, 0x09, 0xea, 0x7b, 0x2f, 0x59, 0xf7, 0x98, 0xa5, 0xa9, 0xdf, 0x88, 0x43, 0x42, 0xf7, 0x8c, 0x66, 0xbe, 0xda, 0x6f, 0xc2, 0x1b, 0x93, 0x1e, 0xd1, 0xae, 0x7a,
0x67, 0xe4, 0x6d, 0xa8, 0x9d, 0x0a, 0xde, 0x65, 0x69, 0x3a, 0x94, 0x35, 0x22, 0x90, 0x9f, 0x43, 0x04, 0x3f, 0xa4, 0x2c, 0x60, 0x6e, 0xc2, 0x96, 0xf7, 0x96, 0x6d, 0x81, 0x79, 0x13, 0xac, 0x05,
0xf9, 0x30, 0x0e, 0xa5, 0x69, 0x73, 0x6b, 0x85, 0x63, 0x73, 0x28, 0x8d, 0x4c, 0x7c, 0x32, 0xe2, 0xff, 0xaf, 0x00, 0xf5, 0x83, 0x97, 0xac, 0x77, 0xc2, 0x92, 0xc4, 0x1d, 0xc8, 0xc9, 0xef, 0x8c,
0x96, 0x6c, 0x41, 0x19, 0x8b, 0xc4, 0x6d, 0x0a, 0x75, 0x90, 0xc3, 0x22, 0x86, 0x6c, 0xab, 0x47, 0x47, 0x3d, 0x96, 0x24, 0x23, 0x59, 0x63, 0x02, 0xf9, 0x39, 0x14, 0xbb, 0xa1, 0x2f, 0x74, 0x9b,
0x76, 0xf8, 0x35, 0x33, 0x51, 0x6a, 0x17, 0x77, 0x98, 0xf0, 0x6b, 0x36, 0x92, 0x60, 0x90, 0x64, 0xdb, 0xc8, 0x1d, 0xbc, 0x7d, 0xa1, 0x65, 0xe2, 0xa3, 0x13, 0xb7, 0x64, 0x07, 0x8a, 0x58, 0x24,
0x0f, 0x2a, 0x67, 0xd2, 0x17, 0xf8, 0xde, 0xd6, 0xd1, 0xbb, 0x57, 0x34, 0x88, 0x68, 0xce, 0x91, 0x16, 0x29, 0xd4, 0x5e, 0x06, 0x8b, 0x18, 0xb2, 0x2b, 0x9f, 0xe9, 0xfe, 0xd7, 0x4c, 0x47, 0xa9,
0x94, 0x0c, 0x8b, 0x4e, 0xd8, 0x7b, 0x19, 0x4a, 0x73, 0x1b, 0x8a, 0x9c, 0x80, 0x6c, 0x39, 0x43, 0x9d, 0xdf, 0x61, 0xfc, 0xaf, 0xd9, 0x58, 0x82, 0x46, 0x92, 0x03, 0xa8, 0x9c, 0x0b, 0x97, 0xe3,
0x70, 0x8b, 0xe8, 0x5d, 0x1e, 0x33, 0xbb, 0x32, 0x13, 0x8d, 0x6c, 0x39, 0x34, 0x6e, 0xd1, 0x0d, 0x8b, 0x5d, 0x45, 0xef, 0x5e, 0xde, 0x20, 0xa2, 0x38, 0xc7, 0x52, 0x52, 0x2c, 0x3a, 0xe1, 0xe0,
0x67, 0x61, 0x1f, 0xe7, 0xbb, 0xea, 0x4c, 0x37, 0x68, 0xc6, 0x9c, 0x1b, 0x34, 0x61, 0xbb, 0x02, 0xa5, 0x2f, 0xf4, 0x6d, 0xc8, 0x73, 0x02, 0xb2, 0x65, 0x0c, 0xc1, 0x2d, 0xa2, 0xf7, 0xa3, 0x90,
0x4b, 0x6a, 0x9a, 0x71, 0xff, 0x62, 0x41, 0x3d, 0x17, 0xa7, 0x5b, 0xdc, 0xbb, 0xb7, 0xa1, 0x8c, 0x99, 0x95, 0xb9, 0x68, 0x64, 0xcb, 0xa0, 0x71, 0x8b, 0x6e, 0x38, 0xf7, 0x07, 0x38, 0xdf, 0x55,
0xef, 0x74, 0x13, 0xff, 0xaa, 0xba, 0x75, 0x4c, 0xfa, 0x54, 0x51, 0xb1, 0x70, 0xec, 0x07, 0xba, 0xe7, 0xba, 0x41, 0x31, 0x66, 0xdc, 0xa0, 0x08, 0xbb, 0x15, 0x28, 0xc9, 0x69, 0xc6, 0xfe, 0xb3,
0x28, 0x36, 0x29, 0x2e, 0x91, 0xf2, 0x85, 0xbc, 0x52, 0x21, 0xab, 0x52, 0x5c, 0x92, 0x75, 0xa8, 0x01, 0xf5, 0x4c, 0x9c, 0x16, 0xb8, 0x77, 0x6f, 0x43, 0x11, 0x5f, 0xfa, 0x3a, 0xfe, 0x55, 0x79,
0x9e, 0xb1, 0xee, 0x40, 0x84, 0xf2, 0x4a, 0x05, 0xa1, 0xb5, 0xb1, 0xa2, 0xca, 0x89, 0xa1, 0xa9, 0xeb, 0x98, 0x70, 0xa9, 0xa4, 0x62, 0xe1, 0x38, 0xf4, 0x54, 0x51, 0x6c, 0x52, 0x5c, 0x22, 0xe5,
0xcb, 0x39, 0xe4, 0x70, 0x3f, 0xc7, 0xe4, 0x1c, 0x29, 0x48, 0xa0, 0xbc, 0x83, 0xaf, 0x15, 0xd4, 0x0b, 0x71, 0x2d, 0x43, 0x56, 0xa5, 0xb8, 0x24, 0x9b, 0x50, 0x3d, 0x67, 0xbd, 0x21, 0xf7, 0xc5,
0xac, 0x49, 0xd5, 0x1a, 0x1f, 0x8c, 0x7b, 0xb3, 0x1e, 0x8c, 0x7b, 0xd9, 0x83, 0x71, 0x3c, 0xa8, 0xb5, 0x0c, 0x42, 0x6b, 0x6b, 0x4d, 0x96, 0x13, 0x4d, 0x93, 0x97, 0x73, 0xc4, 0x61, 0x7f, 0x8e,
0xd8, 0x7d, 0x72, 0x4e, 0x76, 0x1f, 0x43, 0x6d, 0x98, 0x78, 0xf8, 0x56, 0xdf, 0x0f, 0xcc, 0x49, 0xc9, 0x39, 0x56, 0x90, 0x40, 0x71, 0x0f, 0xdf, 0x3b, 0xa8, 0x59, 0x93, 0xca, 0x35, 0x3e, 0x39,
0x8b, 0xfb, 0x01, 0x9a, 0xb2, 0xf7, 0x64, 0x5f, 0x9d, 0x52, 0xa5, 0xb8, 0x1c, 0xf6, 0xfa, 0x52, 0x0f, 0xe6, 0x3d, 0x39, 0x0f, 0xd2, 0x27, 0xe7, 0x64, 0x50, 0xb1, 0xfb, 0x64, 0x9c, 0x6c, 0x3f,
0xae, 0xd7, 0x6f, 0xe2, 0x53, 0x38, 0x97, 0x7d, 0xc8, 0x44, 0xf9, 0x65, 0x9a, 0xa9, 0x8c, 0x6b, 0x86, 0xda, 0x28, 0xf1, 0xf0, 0xb5, 0x7f, 0xe8, 0xe9, 0x93, 0x56, 0x0f, 0x3d, 0x34, 0xe5, 0xe0,
0x6d, 0x46, 0x94, 0x2a, 0x59, 0xca, 0x8c, 0x28, 0x75, 0x7f, 0x04, 0xcd, 0xb1, 0x78, 0x21, 0x93, 0xc9, 0xa1, 0x3c, 0xa5, 0x4a, 0x71, 0x39, 0xea, 0xf5, 0x85, 0x4c, 0xaf, 0xdf, 0xc6, 0xc7, 0x74,
0x7a, 0x7b, 0x99, 0x91, 0x10, 0xd7, 0x1b, 0xff, 0xaa, 0x41, 0xed, 0xe8, 0x68, 0x7b, 0x5b, 0x84, 0x26, 0xfb, 0x90, 0x89, 0x46, 0x57, 0x49, 0xaa, 0x32, 0xae, 0x95, 0x19, 0x41, 0x22, 0x65, 0x49,
0x41, 0x9f, 0x91, 0xdf, 0x5b, 0x40, 0xae, 0x3f, 0xc3, 0xc8, 0x47, 0xc5, 0x37, 0x63, 0xfa, 0x5b, 0x33, 0x82, 0xc4, 0xfe, 0x11, 0x34, 0x27, 0xe2, 0x85, 0x4c, 0xf2, 0xf5, 0xa6, 0x47, 0x42, 0x5c,
0xd2, 0xf9, 0x78, 0x4e, 0x94, 0xe9, 0xcf, 0x5f, 0xc2, 0x92, 0x9a, 0x0d, 0xc9, 0x4f, 0x6e, 0x39, 0x6f, 0xfd, 0xab, 0x06, 0xb5, 0xe3, 0xe3, 0xdd, 0x5d, 0xee, 0x7b, 0x03, 0x46, 0x7e, 0x6f, 0x00,
0xd3, 0x3b, 0xed, 0xd9, 0x8c, 0x46, 0x76, 0x17, 0xaa, 0xd9, 0x7c, 0x45, 0xee, 0x17, 0xaa, 0x37, 0xb9, 0xf9, 0x0c, 0x23, 0x1f, 0xe5, 0xdf, 0x8c, 0xd9, 0xaf, 0x51, 0xeb, 0xe3, 0x25, 0x51, 0xba,
0x36, 0x3e, 0x3a, 0xef, 0xdf, 0x8a, 0xd7, 0x1c, 0xf2, 0x1b, 0xa8, 0x98, 0xb1, 0x89, 0xdc, 0x9b, 0x3f, 0x7f, 0x09, 0x25, 0x39, 0x1b, 0x92, 0x9f, 0x2c, 0x38, 0xd3, 0x5b, 0xed, 0xf9, 0x8c, 0x5a,
0x81, 0x1b, 0x0d, 0x70, 0xce, 0xfd, 0xdb, 0xb0, 0x8e, 0xcc, 0xc8, 0xc6, 0xa3, 0x42, 0x33, 0x26, 0x76, 0x0f, 0xaa, 0xe9, 0x7c, 0x45, 0xee, 0xe7, 0xaa, 0x37, 0x31, 0x3e, 0x5a, 0xef, 0x2f, 0xc4,
0x86, 0xaf, 0x42, 0x33, 0xae, 0xcd, 0x5b, 0x4f, 0xa1, 0x8c, 0x73, 0x14, 0x29, 0xaa, 0x27, 0xb9, 0xab, 0x0f, 0xf9, 0x0d, 0x54, 0xf4, 0xd8, 0x44, 0xee, 0xcd, 0xc1, 0x8d, 0x07, 0x38, 0xeb, 0xfe,
0x41, 0xcb, 0x29, 0x0a, 0xd7, 0xd8, 0x00, 0xf6, 0x6b, 0xac, 0xbb, 0xea, 0x2d, 0x5a, 0x5c, 0x71, 0x22, 0xac, 0x63, 0x33, 0xd2, 0xf1, 0x28, 0xd7, 0x8c, 0xa9, 0xe1, 0x2b, 0xd7, 0x8c, 0x1b, 0xf3,
0x73, 0x7f, 0xff, 0x38, 0xf7, 0x6e, 0xc1, 0x39, 0x12, 0x6f, 0xde, 0x71, 0xed, 0x5b, 0xfc, 0x07, 0xd6, 0x53, 0x28, 0xe2, 0x1c, 0x45, 0xf2, 0xea, 0x49, 0x66, 0xd0, 0xb2, 0xf2, 0xc2, 0x35, 0x31,
0x33, 0x5b, 0xfc, 0xc4, 0xbf, 0x3d, 0x1c, 0x1a, 0xf9, 0x76, 0x4a, 0xbc, 0x02, 0xe8, 0x94, 0x49, 0x80, 0xfd, 0x1a, 0xeb, 0xae, 0x7c, 0x8b, 0xe6, 0x57, 0xdc, 0xcc, 0x1f, 0x48, 0xd6, 0xbd, 0x05,
0xc4, 0xe9, 0xdc, 0x9a, 0xdf, 0x1c, 0xf8, 0x0d, 0xbe, 0x09, 0xc6, 0x5b, 0x2d, 0xd9, 0x28, 0x74, 0x38, 0xc7, 0xe2, 0xf5, 0x3b, 0xae, 0xbd, 0xc0, 0xbf, 0x38, 0xf3, 0xc5, 0x4f, 0xfd, 0x5f, 0x14,
0xc7, 0xd4, 0xa6, 0xee, 0x3c, 0x98, 0x0b, 0x63, 0x0e, 0xf7, 0x75, 0x2b, 0x37, 0xed, 0x9a, 0x14, 0x41, 0x23, 0xdb, 0x4e, 0x89, 0x93, 0x03, 0x9d, 0x31, 0x89, 0x58, 0x9d, 0x85, 0xf9, 0xf5, 0x81,
0x77, 0xa6, 0x61, 0xcb, 0x77, 0x6e, 0xc9, 0xd7, 0xb6, 0x3e, 0xb4, 0x30, 0xcf, 0x70, 0x84, 0x2b, 0xdf, 0xe0, 0x9b, 0x60, 0xb2, 0xd5, 0x92, 0xad, 0x5c, 0x77, 0xcc, 0x6c, 0xea, 0xd6, 0x83, 0xa5,
0x94, 0x9d, 0x9b, 0x6d, 0x0b, 0xf3, 0x2c, 0x3f, 0x0b, 0x6e, 0x37, 0xbe, 0x7d, 0x75, 0xd7, 0xfa, 0x30, 0xfa, 0x70, 0x57, 0xb5, 0x72, 0xdd, 0xae, 0x49, 0x7e, 0x67, 0x1a, 0xb5, 0x7c, 0x6b, 0x41,
0xe7, 0xab, 0xbb, 0xd6, 0x7f, 0x5e, 0xdd, 0xb5, 0xce, 0x97, 0xd5, 0x5f, 0xeb, 0x0f, 0xfe, 0x1f, 0xbe, 0xb6, 0xf1, 0xa1, 0x81, 0x79, 0x86, 0x23, 0x5c, 0xae, 0xec, 0xcc, 0x6c, 0x9b, 0x9b, 0x67,
0x00, 0x00, 0xff, 0xff, 0xce, 0x5a, 0x92, 0x41, 0xac, 0x18, 0x00, 0x00, 0xd9, 0x59, 0x70, 0xb7, 0xf1, 0xed, 0xab, 0xbb, 0xc6, 0x3f, 0x5e, 0xdd, 0x35, 0xfe, 0xf3, 0xea,
0xae, 0x71, 0x51, 0x96, 0x7f, 0xce, 0x3f, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x29, 0xd9,
0x1c, 0x06, 0xee, 0x18, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -3364,6 +3382,18 @@ func (m *ResolveImageConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro
i -= len(m.XXX_unrecognized) i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized)
} }
if len(m.SessionID) > 0 {
i -= len(m.SessionID)
copy(dAtA[i:], m.SessionID)
i = encodeVarintGateway(dAtA, i, uint64(len(m.SessionID)))
i--
dAtA[i] = 0x32
}
if m.ResolverType != 0 {
i = encodeVarintGateway(dAtA, i, uint64(m.ResolverType))
i--
dAtA[i] = 0x28
}
if len(m.LogName) > 0 { if len(m.LogName) > 0 {
i -= len(m.LogName) i -= len(m.LogName)
copy(dAtA[i:], m.LogName) copy(dAtA[i:], m.LogName)
@ -5102,6 +5132,13 @@ func (m *ResolveImageConfigRequest) Size() (n int) {
if l > 0 { if l > 0 {
n += 1 + l + sovGateway(uint64(l)) n += 1 + l + sovGateway(uint64(l))
} }
if m.ResolverType != 0 {
n += 1 + sovGateway(uint64(m.ResolverType))
}
l = len(m.SessionID)
if l > 0 {
n += 1 + l + sovGateway(uint64(l))
}
if m.XXX_unrecognized != nil { if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized) n += len(m.XXX_unrecognized)
} }
@ -7147,6 +7184,57 @@ func (m *ResolveImageConfigRequest) Unmarshal(dAtA []byte) error {
} }
m.LogName = string(dAtA[iNdEx:postIndex]) m.LogName = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex iNdEx = postIndex
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ResolverType", wireType)
}
m.ResolverType = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGateway
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ResolverType |= int32(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SessionID", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGateway
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGateway
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGateway
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SessionID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipGateway(dAtA[iNdEx:]) skippy, err := skipGateway(dAtA[iNdEx:])

View File

@ -83,6 +83,8 @@ message ResolveImageConfigRequest {
pb.Platform Platform = 2; pb.Platform Platform = 2;
string ResolveMode = 3; string ResolveMode = 3;
string LogName = 4; string LogName = 4;
int32 ResolverType = 5;
string SessionID = 6;
} }
message ResolveImageConfigResponse { message ResolveImageConfigResponse {

View File

@ -28,6 +28,9 @@ const AttrImageResolveModePreferLocal = "local"
const AttrImageRecordType = "image.recordtype" const AttrImageRecordType = "image.recordtype"
const AttrImageLayerLimit = "image.layerlimit" const AttrImageLayerLimit = "image.layerlimit"
const AttrOCILayoutSessionID = "oci.session"
const AttrOCILayoutLayerLimit = "oci.layerlimit"
const AttrLocalDiffer = "local.differ" const AttrLocalDiffer = "local.differ"
const AttrLocalDifferNone = "none" const AttrLocalDifferNone = "none"
const AttrLocalDifferMetadata = "metadata" const AttrLocalDifferMetadata = "metadata"

View File

@ -35,6 +35,10 @@ const (
CapSourceHTTPPerm apicaps.CapID = "source.http.perm" CapSourceHTTPPerm apicaps.CapID = "source.http.perm"
CapSourceHTTPUIDGID apicaps.CapID = "soruce.http.uidgid" CapSourceHTTPUIDGID apicaps.CapID = "soruce.http.uidgid"
CapSourceOCILayout apicaps.CapID = "source.ocilayout"
CapSourceOCILayoutSessionID apicaps.CapID = "source.ocilayout.sessionid"
CapSourceOCILayoutLayerLimit apicaps.CapID = "source.ocilayout.layerlimit"
CapBuildOpLLBFileName apicaps.CapID = "source.buildop.llbfilename" CapBuildOpLLBFileName apicaps.CapID = "source.buildop.llbfilename"
CapExecMetaBase apicaps.CapID = "exec.meta.base" CapExecMetaBase apicaps.CapID = "exec.meta.base"
@ -74,6 +78,8 @@ const (
CapMergeOp apicaps.CapID = "mergeop" CapMergeOp apicaps.CapID = "mergeop"
CapDiffOp apicaps.CapID = "diffop" CapDiffOp apicaps.CapID = "diffop"
CapAnnotations apicaps.CapID = "exporter.image.annotations"
) )
func init() { func init() {
@ -203,6 +209,24 @@ func init() {
Status: apicaps.CapStatusExperimental, Status: apicaps.CapStatusExperimental,
}) })
Caps.Init(apicaps.Cap{
ID: CapSourceOCILayout,
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{
ID: CapSourceOCILayoutSessionID,
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{
ID: CapSourceOCILayoutLayerLimit,
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{ Caps.Init(apicaps.Cap{
ID: CapSourceHTTPUIDGID, ID: CapSourceHTTPUIDGID,
Enabled: true, Enabled: true,
@ -392,19 +416,28 @@ func init() {
Enabled: true, Enabled: true,
Status: apicaps.CapStatusExperimental, Status: apicaps.CapStatusExperimental,
}) })
Caps.Init(apicaps.Cap{ Caps.Init(apicaps.Cap{
ID: CapRemoteCacheS3, ID: CapRemoteCacheS3,
Enabled: true, Enabled: true,
Status: apicaps.CapStatusExperimental, Status: apicaps.CapStatusExperimental,
}) })
Caps.Init(apicaps.Cap{ Caps.Init(apicaps.Cap{
ID: CapMergeOp, ID: CapMergeOp,
Enabled: true, Enabled: true,
Status: apicaps.CapStatusExperimental, Status: apicaps.CapStatusExperimental,
}) })
Caps.Init(apicaps.Cap{ Caps.Init(apicaps.Cap{
ID: CapDiffOp, ID: CapDiffOp,
Enabled: true, Enabled: true,
Status: apicaps.CapStatusExperimental, Status: apicaps.CapStatusExperimental,
}) })
Caps.Init(apicaps.Cap{
ID: CapAnnotations,
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
} }

View File

@ -6,4 +6,5 @@ const (
LocalScheme = "local" LocalScheme = "local"
HTTPScheme = "http" HTTPScheme = "http"
HTTPSScheme = "https" HTTPSScheme = "https"
OCIScheme = "oci-layout"
) )

View File

@ -335,7 +335,7 @@ github.com/linuxkit/virtsock/pkg/vsock
github.com/matttproud/golang_protobuf_extensions/pbutil github.com/matttproud/golang_protobuf_extensions/pbutil
# github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b # github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b
github.com/mitchellh/go-ps github.com/mitchellh/go-ps
# github.com/moby/buildkit v0.10.1-0.20220614084718-b3d272e22672 # github.com/moby/buildkit v0.10.1-0.20220617010517-a6a114a1a476
## explicit ## explicit
github.com/moby/buildkit/api/services/control github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types github.com/moby/buildkit/api/types
@ -414,6 +414,7 @@ github.com/morikuni/aec
## explicit ## explicit
github.com/moul/gotty-client github.com/moul/gotty-client
# github.com/opencontainers/go-digest v1.0.0 # github.com/opencontainers/go-digest v1.0.0
## explicit
github.com/opencontainers/go-digest github.com/opencontainers/go-digest
# github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 # github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
## explicit ## explicit

View File

@ -0,0 +1,3 @@
FROM alpine:3.15
COPY foo /foo

View File

@ -0,0 +1,5 @@
image: chained-build-image1-test-does-not-exist-anywhere-else-123456789 # this must be used as FROM in the chained image
network: true
arches:
- amd64
- arm64

View File

@ -0,0 +1 @@
bar

View File

@ -0,0 +1,5 @@
# this FROM *must* be updated when changing ../build1; just run `lkt pkg show-tag ../build1` and place it here
FROM linuxkit/chained-build-image1-test-does-not-exist-anywhere-else-123456789:ad06eb29c48f0daab0cc2836a1351a053948100c as src
FROM alpine:3.15
COPY --from=src /foo /foo
RUN cat /foo

View File

@ -0,0 +1,5 @@
image: chained-build-test-second
network: true
arches:
- amd64
- arm64

View File

@ -0,0 +1,19 @@
#!/bin/sh
# SUMMARY: Check that chained builds get the output of one for the next
# LABELS:
# REPEAT:
set -ex
# Source libraries. Uncomment if needed/defined
#. "${RT_LIB}"
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
# Test code goes here
echo linuxkit is "$(which linuxkit)"
# build the first, use it to build the second
linuxkit pkg build --force ./build1
linuxkit pkg build --force ./build2
exit 0