mirror of
https://github.com/moby/buildkit.git
synced 2026-08-09 17:18:11 +00:00
This breaking api change refactors the LLB API to prevent reference mangling and demangling throughout OCI access. Once the session and store IDs have been determined in the dockerfile frontend, we keep them the same, and attach them as additional properties. This has the additional effect of making the actual reference used in the image resolution arbitrary, since we only parse and access the digest. The rest of the name can be selected to optimize for log readability. Signed-off-by: Justin Chadwell <me@jedevc.com>
353 lines
8.2 KiB
Go
353 lines
8.2 KiB
Go
package source
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/containerd/containerd/reference"
|
|
"github.com/moby/buildkit/client"
|
|
"github.com/moby/buildkit/solver/pb"
|
|
srctypes "github.com/moby/buildkit/source/types"
|
|
digest "github.com/opencontainers/go-digest"
|
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
"github.com/tonistiigi/fsutil"
|
|
)
|
|
|
|
var (
|
|
errInvalid = errors.New("invalid")
|
|
errNotFound = errors.New("not found")
|
|
)
|
|
|
|
type ResolveMode int
|
|
|
|
const (
|
|
ResolveModeDefault ResolveMode = iota
|
|
ResolveModeForcePull
|
|
ResolveModePreferLocal
|
|
)
|
|
|
|
type Identifier interface {
|
|
ID() string // until sources are in process this string comparison could be avoided
|
|
}
|
|
|
|
func FromString(s string) (Identifier, error) {
|
|
// TODO: improve this
|
|
parts := strings.SplitN(s, "://", 2)
|
|
if len(parts) != 2 {
|
|
return nil, errors.Wrapf(errInvalid, "failed to parse %s", s)
|
|
}
|
|
|
|
switch parts[0] {
|
|
case srctypes.DockerImageScheme:
|
|
return NewImageIdentifier(parts[1])
|
|
case srctypes.GitScheme:
|
|
return NewGitIdentifier(parts[1])
|
|
case srctypes.LocalScheme:
|
|
return NewLocalIdentifier(parts[1])
|
|
case srctypes.HTTPSScheme:
|
|
return NewHTTPIdentifier(parts[1], true)
|
|
case srctypes.HTTPScheme:
|
|
return NewHTTPIdentifier(parts[1], false)
|
|
case srctypes.OCIScheme:
|
|
return NewOCIIdentifier(parts[1])
|
|
default:
|
|
return nil, errors.Wrapf(errNotFound, "unknown schema %s", parts[0])
|
|
}
|
|
}
|
|
|
|
func FromLLB(op *pb.Op_Source, platform *pb.Platform) (Identifier, error) {
|
|
id, err := FromString(op.Source.Identifier)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if id, ok := id.(*ImageIdentifier); ok {
|
|
if platform != nil {
|
|
id.Platform = &ocispecs.Platform{
|
|
OS: platform.OS,
|
|
Architecture: platform.Architecture,
|
|
Variant: platform.Variant,
|
|
OSVersion: platform.OSVersion,
|
|
OSFeatures: platform.OSFeatures,
|
|
}
|
|
}
|
|
for k, v := range op.Source.Attrs {
|
|
switch k {
|
|
case pb.AttrImageResolveMode:
|
|
rm, err := ParseImageResolveMode(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id.ResolveMode = rm
|
|
case pb.AttrImageRecordType:
|
|
rt, err := parseImageRecordType(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id.RecordType = rt
|
|
case pb.AttrImageLayerLimit:
|
|
l, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "invalid layer limit %s", v)
|
|
}
|
|
if l <= 0 {
|
|
return nil, errors.Errorf("invalid layer limit %s", v)
|
|
}
|
|
id.LayerLimit = &l
|
|
}
|
|
}
|
|
}
|
|
if id, ok := id.(*GitIdentifier); ok {
|
|
for k, v := range op.Source.Attrs {
|
|
switch k {
|
|
case pb.AttrKeepGitDir:
|
|
if v == "true" {
|
|
id.KeepGitDir = true
|
|
}
|
|
case pb.AttrFullRemoteURL:
|
|
if !isGitTransport(v) {
|
|
v = "https://" + v
|
|
}
|
|
id.Remote = v
|
|
case pb.AttrAuthHeaderSecret:
|
|
id.AuthHeaderSecret = v
|
|
case pb.AttrAuthTokenSecret:
|
|
id.AuthTokenSecret = v
|
|
case pb.AttrKnownSSHHosts:
|
|
id.KnownSSHHosts = v
|
|
case pb.AttrMountSSHSock:
|
|
id.MountSSHSock = v
|
|
}
|
|
}
|
|
}
|
|
if id, ok := id.(*LocalIdentifier); ok {
|
|
for k, v := range op.Source.Attrs {
|
|
switch k {
|
|
case pb.AttrLocalSessionID:
|
|
id.SessionID = v
|
|
if p := strings.SplitN(v, ":", 2); len(p) == 2 {
|
|
id.Name = p[0] + "-" + id.Name
|
|
id.SessionID = p[1]
|
|
}
|
|
case pb.AttrIncludePatterns:
|
|
var patterns []string
|
|
if err := json.Unmarshal([]byte(v), &patterns); err != nil {
|
|
return nil, err
|
|
}
|
|
id.IncludePatterns = patterns
|
|
case pb.AttrExcludePatterns:
|
|
var patterns []string
|
|
if err := json.Unmarshal([]byte(v), &patterns); err != nil {
|
|
return nil, err
|
|
}
|
|
id.ExcludePatterns = patterns
|
|
case pb.AttrFollowPaths:
|
|
var paths []string
|
|
if err := json.Unmarshal([]byte(v), &paths); err != nil {
|
|
return nil, err
|
|
}
|
|
id.FollowPaths = paths
|
|
case pb.AttrSharedKeyHint:
|
|
id.SharedKeyHint = v
|
|
case pb.AttrLocalDiffer:
|
|
switch v {
|
|
case pb.AttrLocalDifferMetadata, "":
|
|
id.Differ = fsutil.DiffMetadata
|
|
case pb.AttrLocalDifferNone:
|
|
id.Differ = fsutil.DiffNone
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if id, ok := id.(*HTTPIdentifier); ok {
|
|
for k, v := range op.Source.Attrs {
|
|
switch k {
|
|
case pb.AttrHTTPChecksum:
|
|
dgst, err := digest.Parse(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id.Checksum = dgst
|
|
case pb.AttrHTTPFilename:
|
|
id.Filename = v
|
|
case pb.AttrHTTPPerm:
|
|
i, err := strconv.ParseInt(v, 0, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id.Perm = int(i)
|
|
case pb.AttrHTTPUID:
|
|
i, err := strconv.ParseInt(v, 0, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id.UID = int(i)
|
|
case pb.AttrHTTPGID:
|
|
i, err := strconv.ParseInt(v, 0, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id.GID = int(i)
|
|
}
|
|
}
|
|
}
|
|
if id, ok := id.(*OCIIdentifier); ok {
|
|
if platform != nil {
|
|
id.Platform = &ocispecs.Platform{
|
|
OS: platform.OS,
|
|
Architecture: platform.Architecture,
|
|
Variant: platform.Variant,
|
|
OSVersion: platform.OSVersion,
|
|
OSFeatures: platform.OSFeatures,
|
|
}
|
|
}
|
|
for k, v := range op.Source.Attrs {
|
|
switch k {
|
|
case pb.AttrOCILayoutSessionID:
|
|
id.SessionID = v
|
|
case pb.AttrOCILayoutStoreID:
|
|
id.StoreID = v
|
|
case pb.AttrOCILayoutLayerLimit:
|
|
l, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "invalid layer limit %s", v)
|
|
}
|
|
if l <= 0 {
|
|
return nil, errors.Errorf("invalid layer limit %s", v)
|
|
}
|
|
id.LayerLimit = &l
|
|
}
|
|
}
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
type ImageIdentifier struct {
|
|
Reference reference.Spec
|
|
Platform *ocispecs.Platform
|
|
ResolveMode ResolveMode
|
|
RecordType client.UsageRecordType
|
|
LayerLimit *int
|
|
}
|
|
|
|
func NewImageIdentifier(str string) (*ImageIdentifier, error) {
|
|
ref, err := reference.Parse(str)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
if ref.Object == "" {
|
|
return nil, errors.WithStack(reference.ErrObjectRequired)
|
|
}
|
|
return &ImageIdentifier{Reference: ref}, nil
|
|
}
|
|
|
|
func (*ImageIdentifier) ID() string {
|
|
return srctypes.DockerImageScheme
|
|
}
|
|
|
|
type LocalIdentifier struct {
|
|
Name string
|
|
SessionID string
|
|
IncludePatterns []string
|
|
ExcludePatterns []string
|
|
FollowPaths []string
|
|
SharedKeyHint string
|
|
Differ fsutil.DiffType
|
|
}
|
|
|
|
func NewLocalIdentifier(str string) (*LocalIdentifier, error) {
|
|
return &LocalIdentifier{Name: str}, nil
|
|
}
|
|
|
|
func (*LocalIdentifier) ID() string {
|
|
return srctypes.LocalScheme
|
|
}
|
|
|
|
func NewHTTPIdentifier(str string, tls bool) (*HTTPIdentifier, error) {
|
|
proto := "https://"
|
|
if !tls {
|
|
proto = "http://"
|
|
}
|
|
return &HTTPIdentifier{TLS: tls, URL: proto + str}, nil
|
|
}
|
|
|
|
type HTTPIdentifier struct {
|
|
TLS bool
|
|
URL string
|
|
Checksum digest.Digest
|
|
Filename string
|
|
Perm int
|
|
UID int
|
|
GID int
|
|
}
|
|
|
|
func (*HTTPIdentifier) ID() string {
|
|
return srctypes.HTTPSScheme
|
|
}
|
|
|
|
type OCIIdentifier struct {
|
|
Reference reference.Spec
|
|
Platform *ocispecs.Platform
|
|
SessionID string
|
|
StoreID string
|
|
LayerLimit *int
|
|
}
|
|
|
|
func NewOCIIdentifier(str string) (*OCIIdentifier, error) {
|
|
ref, err := reference.Parse(str)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
if ref.Object == "" {
|
|
return nil, errors.WithStack(reference.ErrObjectRequired)
|
|
}
|
|
return &OCIIdentifier{Reference: ref}, nil
|
|
}
|
|
|
|
func (*OCIIdentifier) ID() string {
|
|
return srctypes.OCIScheme
|
|
}
|
|
|
|
func (r ResolveMode) String() string {
|
|
switch r {
|
|
case ResolveModeDefault:
|
|
return pb.AttrImageResolveModeDefault
|
|
case ResolveModeForcePull:
|
|
return pb.AttrImageResolveModeForcePull
|
|
case ResolveModePreferLocal:
|
|
return pb.AttrImageResolveModePreferLocal
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func ParseImageResolveMode(v string) (ResolveMode, error) {
|
|
switch v {
|
|
case pb.AttrImageResolveModeDefault, "":
|
|
return ResolveModeDefault, nil
|
|
case pb.AttrImageResolveModeForcePull:
|
|
return ResolveModeForcePull, nil
|
|
case pb.AttrImageResolveModePreferLocal:
|
|
return ResolveModePreferLocal, nil
|
|
default:
|
|
return 0, errors.Errorf("invalid resolvemode: %s", v)
|
|
}
|
|
}
|
|
|
|
func parseImageRecordType(v string) (client.UsageRecordType, error) {
|
|
switch client.UsageRecordType(v) {
|
|
case "", client.UsageRecordTypeRegular:
|
|
return client.UsageRecordTypeRegular, nil
|
|
case client.UsageRecordTypeInternal:
|
|
return client.UsageRecordTypeInternal, nil
|
|
case client.UsageRecordTypeFrontend:
|
|
return client.UsageRecordTypeFrontend, nil
|
|
default:
|
|
return "", errors.Errorf("invalid record type %s", v)
|
|
}
|
|
}
|