287 lines
6.5 KiB
Go
287 lines
6.5 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"
|
|
digest "github.com/opencontainers/go-digest"
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
errInvalid = errors.New("invalid")
|
|
errNotFound = errors.New("not found")
|
|
)
|
|
|
|
type ResolveMode int
|
|
|
|
const (
|
|
ResolveModeDefault ResolveMode = iota
|
|
ResolveModeForcePull
|
|
ResolveModePreferLocal
|
|
)
|
|
|
|
const (
|
|
DockerImageScheme = "docker-image"
|
|
GitScheme = "git"
|
|
LocalScheme = "local"
|
|
HTTPScheme = "http"
|
|
HTTPSScheme = "https"
|
|
)
|
|
|
|
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 DockerImageScheme:
|
|
return NewImageIdentifier(parts[1])
|
|
case GitScheme:
|
|
return NewGitIdentifier(parts[1])
|
|
case LocalScheme:
|
|
return NewLocalIdentifier(parts[1])
|
|
case HTTPSScheme:
|
|
return NewHTTPIdentifier(parts[1], true)
|
|
case HTTPScheme:
|
|
return NewHTTPIdentifier(parts[1], false)
|
|
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 = &specs.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
|
|
}
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
type ImageIdentifier struct {
|
|
Reference reference.Spec
|
|
Platform *specs.Platform
|
|
ResolveMode ResolveMode
|
|
RecordType client.UsageRecordType
|
|
}
|
|
|
|
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 DockerImageScheme
|
|
}
|
|
|
|
type LocalIdentifier struct {
|
|
Name string
|
|
SessionID string
|
|
IncludePatterns []string
|
|
ExcludePatterns []string
|
|
FollowPaths []string
|
|
SharedKeyHint string
|
|
}
|
|
|
|
func NewLocalIdentifier(str string) (*LocalIdentifier, error) {
|
|
return &LocalIdentifier{Name: str}, nil
|
|
}
|
|
|
|
func (*LocalIdentifier) ID() string {
|
|
return 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 HTTPSScheme
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|