| 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: |
| 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) |
| } |
| } |