2017-02-11 00:48:15 +08:00
package buildah
import (
2018-04-12 22:20:36 +08:00
"context"
2017-02-11 00:48:15 +08:00
"fmt"
2017-11-17 03:48:21 +08:00
"os"
2017-03-24 04:18:40 +08:00
"strings"
2017-02-11 00:48:15 +08:00
2018-06-02 02:52:16 +08:00
"github.com/containers/image/pkg/sysregistries"
2017-02-11 00:48:15 +08:00
is "github.com/containers/image/storage"
2017-07-29 05:29:37 +08:00
"github.com/containers/image/transports"
"github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
2017-05-17 23:53:28 +08:00
"github.com/containers/storage"
2017-10-20 05:47:15 +08:00
"github.com/opencontainers/selinux/go-selinux"
"github.com/opencontainers/selinux/go-selinux/label"
2017-03-22 04:57:07 +08:00
"github.com/openshift/imagebuilder"
2017-06-02 03:23:02 +08:00
"github.com/pkg/errors"
2017-06-29 05:07:58 +08:00
"github.com/projectatomic/buildah/util"
2017-10-10 03:05:56 +08:00
"github.com/sirupsen/logrus"
2017-02-11 00:48:15 +08:00
)
const (
2017-02-11 03:45:06 +08:00
// BaseImageFakeName is the "name" of a source image which we interpret
// as "no image".
2017-03-22 04:57:07 +08:00
BaseImageFakeName = imagebuilder . NoBaseImageSpecifier
2017-08-23 18:47:13 +08:00
// DefaultTransport is a prefix that we apply to an image name if we
// can't find one in the local Store, in order to generate a source
// reference for the image that we can then copy to the local Store.
DefaultTransport = "docker://"
2017-06-29 05:07:58 +08:00
// minimumTruncatedIDLength is the minimum length of an identifier that
// we'll accept as possibly being a truncated image ID.
minimumTruncatedIDLength = 3
2017-02-11 00:48:15 +08:00
)
2017-10-20 05:47:15 +08:00
func reserveSELinuxLabels ( store storage . Store , id string ) error {
if selinux . GetEnabled ( ) {
containers , err := store . Containers ( )
if err != nil {
return err
}
for _ , c := range containers {
if id == c . ID {
continue
} else {
b , err := OpenBuilder ( store , c . ID )
if err != nil {
2017-11-17 03:48:21 +08:00
if os . IsNotExist ( err ) {
// Ignore not exist errors since containers probably created by other tool
// TODO, we need to read other containers json data to reserve their SELinux labels
2017-10-20 05:47:15 +08:00
continue
}
return err
}
2018-04-28 02:09:32 +08:00
// Prevent different containers from using same MCS label
2017-10-20 05:47:15 +08:00
if err := label . ReserveLabel ( b . ProcessLabel ) ; err != nil {
return err
}
}
}
}
return nil
}
2018-04-12 22:20:36 +08:00
func pullAndFindImage ( ctx context . Context , store storage . Store , imageName string , options BuilderOptions , sc * types . SystemContext ) ( * storage . Image , types . ImageReference , error ) {
ref , err := pullImage ( ctx , store , imageName , options , sc )
2017-06-29 05:07:58 +08:00
if err != nil {
logrus . Debugf ( "error pulling image %q: %v" , imageName , err )
return nil , nil , err
}
img , err := is . Transport . GetStoreImage ( store , ref )
if err != nil {
logrus . Debugf ( "error reading pulled image %q: %v" , imageName , err )
return nil , nil , err
}
return img , ref , nil
}
2018-01-18 20:04:09 +08:00
func getImageName ( name string , img * storage . Image ) string {
imageName := name
if len ( img . Names ) > 0 {
imageName = img . Names [ 0 ]
// When the image used by the container is a tagged image
// the container name might be set to the original image instead of
// the image given in the "form" command line.
// This loop is supposed to fix this.
for _ , n := range img . Names {
if strings . Contains ( n , name ) {
imageName = n
break
}
}
}
return imageName
}
2017-06-29 05:07:58 +08:00
func imageNamePrefix ( imageName string ) string {
prefix := imageName
s := strings . Split ( imageName , "/" )
if len ( s ) > 0 {
prefix = s [ len ( s ) - 1 ]
}
s = strings . Split ( prefix , ":" )
if len ( s ) > 0 {
prefix = s [ 0 ]
}
s = strings . Split ( prefix , "@" )
if len ( s ) > 0 {
prefix = s [ 0 ]
}
return prefix
}
2018-03-13 01:53:12 +08:00
func newContainerIDMappingOptions ( idmapOptions * IDMappingOptions ) storage . IDMappingOptions {
var options storage . IDMappingOptions
if idmapOptions != nil {
options . HostUIDMapping = idmapOptions . HostUIDMapping
options . HostGIDMapping = idmapOptions . HostGIDMapping
uidmap , gidmap := convertRuntimeIDMaps ( idmapOptions . UIDMap , idmapOptions . GIDMap )
if len ( uidmap ) > 0 && len ( gidmap ) > 0 {
options . UIDMap = uidmap
options . GIDMap = gidmap
} else {
options . HostUIDMapping = true
options . HostGIDMapping = true
}
}
return options
}
2018-06-02 02:52:16 +08:00
func resolveImage ( ctx context . Context , systemContext * types . SystemContext , store storage . Store , options BuilderOptions ) ( types . ImageReference , * storage . Image , error ) {
2017-07-29 05:29:37 +08:00
var ref types . ImageReference
2017-02-11 00:48:15 +08:00
var img * storage . Image
2017-06-29 05:07:58 +08:00
for _ , image := range util . ResolveName ( options . FromImage , options . Registry , systemContext , store ) {
2018-06-02 02:52:16 +08:00
var err error
2017-06-29 05:07:58 +08:00
if len ( image ) >= minimumTruncatedIDLength {
if img , err = store . Image ( image ) ; err == nil && img != nil && strings . HasPrefix ( img . ID , image ) {
if ref , err = is . Transport . ParseStoreReference ( store , img . ID ) ; err != nil {
2018-06-02 02:52:16 +08:00
return nil , nil , errors . Wrapf ( err , "error parsing reference to image %q" , img . ID )
2017-06-29 05:07:58 +08:00
}
break
}
}
2017-02-11 00:48:15 +08:00
2017-04-11 02:15:30 +08:00
if options . PullPolicy == PullAlways {
2018-06-02 02:52:16 +08:00
pulledImg , pulledReference , err := pullAndFindImage ( ctx , store , image , options , systemContext )
if err != nil {
2018-06-02 07:33:32 +08:00
logrus . Debugf ( "unable to pull and read image %q: %v" , image , err )
2017-06-29 05:07:58 +08:00
continue
2017-02-11 00:48:15 +08:00
}
2017-07-29 05:29:37 +08:00
ref = pulledReference
2017-06-29 05:07:58 +08:00
img = pulledImg
break
2017-02-11 00:48:15 +08:00
}
2017-07-29 05:29:37 +08:00
2018-06-02 02:52:16 +08:00
srcRef , err := alltransports . ParseImageName ( image )
if err != nil {
2017-06-29 05:07:58 +08:00
if options . Transport == "" {
2018-06-02 02:52:16 +08:00
logrus . Debugf ( "error parsing image name %q: %v" , image , err )
2017-06-29 05:07:58 +08:00
continue
2017-07-29 05:29:37 +08:00
}
2018-03-19 10:16:47 +08:00
transport := options . Transport
if transport != DefaultTransport {
transport = transport + ":"
}
2018-06-02 02:52:16 +08:00
srcRef2 , err := alltransports . ParseImageName ( transport + image )
if err != nil {
logrus . Debugf ( "error parsing image name %q: %v" , image , err )
2017-06-29 05:07:58 +08:00
continue
2017-07-29 05:29:37 +08:00
}
2017-06-29 05:07:58 +08:00
srcRef = srcRef2
}
2017-07-29 05:29:37 +08:00
2018-06-02 02:52:16 +08:00
destImage , err := localImageNameForReference ( ctx , store , srcRef , options . FromImage )
if err != nil {
return nil , nil , errors . Wrapf ( err , "error computing local image name for %q" , transports . ImageName ( srcRef ) )
2017-06-29 05:07:58 +08:00
}
if destImage == "" {
2018-06-02 02:52:16 +08:00
return nil , nil , errors . Errorf ( "error computing local image name for %q" , transports . ImageName ( srcRef ) )
2017-06-29 05:07:58 +08:00
}
2017-07-29 05:29:37 +08:00
2017-06-29 05:07:58 +08:00
ref , err = is . Transport . ParseStoreReference ( store , destImage )
if err != nil {
2018-06-02 02:52:16 +08:00
return nil , nil , errors . Wrapf ( err , "error parsing reference to image %q" , destImage )
2017-02-11 00:48:15 +08:00
}
img , err = is . Transport . GetStoreImage ( store , ref )
if err != nil {
2017-07-29 05:29:37 +08:00
if errors . Cause ( err ) == storage . ErrImageUnknown && options . PullPolicy != PullIfMissing {
2017-06-29 05:07:58 +08:00
logrus . Debugf ( "no such image %q: %v" , transports . ImageName ( ref ) , err )
continue
2017-02-11 00:48:15 +08:00
}
2018-06-02 02:52:16 +08:00
pulledImg , pulledReference , err := pullAndFindImage ( ctx , store , image , options , systemContext )
if err != nil {
2018-06-02 07:33:32 +08:00
logrus . Debugf ( "unable to pull and read image %q: %v" , image , err )
2017-06-29 05:07:58 +08:00
continue
2017-02-11 00:48:15 +08:00
}
2017-06-29 05:07:58 +08:00
ref = pulledReference
img = pulledImg
2017-02-11 00:48:15 +08:00
}
2017-06-29 05:07:58 +08:00
break
}
2018-06-02 02:52:16 +08:00
return ref , img , nil
}
2017-06-29 05:07:58 +08:00
2018-06-02 02:52:16 +08:00
func newBuilder ( ctx context . Context , store storage . Store , options BuilderOptions ) ( * Builder , error ) {
var ref types . ImageReference
var img * storage . Image
var err error
if options . FromImage == BaseImageFakeName {
options . FromImage = ""
}
if options . Transport == "" {
options . Transport = DefaultTransport
}
systemContext := getSystemContext ( options . SystemContext , options . SignaturePolicyPath )
if options . FromImage != "scratch" {
ref , img , err = resolveImage ( ctx , systemContext , store , options )
if err != nil {
return nil , err
}
if options . FromImage != "" && ( ref == nil || img == nil ) {
// If options.FromImage is set but we ended up
// with nil in ref or in img then there was an error that
// we should return.
return nil , errors . Wrapf ( storage . ErrImageUnknown , "image %q not found in %s registries" , options . FromImage , sysregistries . RegistriesConfPath ( systemContext ) )
}
2017-06-29 05:07:58 +08:00
}
image := options . FromImage
imageID := ""
2018-06-09 00:55:46 +08:00
topLayer := ""
2017-06-29 05:07:58 +08:00
if img != nil {
2018-01-29 09:08:06 +08:00
image = getImageName ( imageNamePrefix ( image ) , img )
2017-03-16 05:19:29 +08:00
imageID = img . ID
2018-06-09 00:55:46 +08:00
topLayer = img . TopLayer
2017-06-29 05:07:58 +08:00
}
2018-06-12 06:31:04 +08:00
var src types . ImageCloser
if ref != nil {
src , err = ref . NewImage ( ctx , systemContext )
if err != nil {
return nil , errors . Wrapf ( err , "error instantiating image for %q" , transports . ImageName ( ref ) )
}
defer src . Close ( )
}
2017-07-29 05:29:37 +08:00
name := "working-container"
if options . Container != "" {
name = options . Container
} else {
var err2 error
if image != "" {
2017-06-29 05:07:58 +08:00
name = imageNamePrefix ( image ) + "-" + name
2017-07-29 05:29:37 +08:00
}
suffix := 1
tmpName := name
for errors . Cause ( err2 ) != storage . ErrContainerUnknown {
_ , err2 = store . Container ( tmpName )
if err2 == nil {
suffix ++
tmpName = fmt . Sprintf ( "%s-%d" , name , suffix )
}
}
name = tmpName
}
2017-06-29 05:07:58 +08:00
2017-02-11 00:48:15 +08:00
coptions := storage . ContainerOptions { }
2018-03-13 01:53:12 +08:00
coptions . IDMappingOptions = newContainerIDMappingOptions ( options . IDMappingOptions )
2017-03-16 05:19:29 +08:00
container , err := store . CreateContainer ( "" , [ ] string { name } , imageID , "" , "" , & coptions )
2017-02-11 00:48:15 +08:00
if err != nil {
2017-06-02 03:23:02 +08:00
return nil , errors . Wrapf ( err , "error creating container" )
2017-02-11 00:48:15 +08:00
}
defer func ( ) {
if err != nil {
if err2 := store . DeleteContainer ( container . ID ) ; err != nil {
logrus . Errorf ( "error deleting container %q: %v" , container . ID , err2 )
}
}
} ( )
2017-06-29 05:07:58 +08:00
if err = reserveSELinuxLabels ( store , container . ID ) ; err != nil {
2017-10-20 05:47:15 +08:00
return nil , err
}
2018-02-14 03:58:56 +08:00
processLabel , mountLabel , err := label . InitLabels ( options . CommonBuildOpts . LabelOpts )
2017-10-20 05:47:15 +08:00
if err != nil {
return nil , err
}
2018-03-08 07:11:43 +08:00
uidmap , gidmap := convertStorageIDMaps ( container . UIDMap , container . GIDMap )
namespaceOptions := DefaultNamespaceOptions ( )
namespaceOptions . AddOrReplace ( options . NamespaceOptions ... )
2017-10-20 05:47:15 +08:00
2017-02-11 00:48:15 +08:00
builder := & Builder {
2017-11-08 06:44:24 +08:00
store : store ,
Type : containerType ,
FromImage : image ,
FromImageID : imageID ,
Container : name ,
ContainerID : container . ID ,
ImageAnnotations : map [ string ] string { } ,
ImageCreatedBy : "" ,
ProcessLabel : processLabel ,
MountLabel : mountLabel ,
DefaultMountsFilePath : options . DefaultMountsFilePath ,
2018-03-08 07:11:43 +08:00
NamespaceOptions : namespaceOptions ,
2018-04-14 06:20:25 +08:00
ConfigureNetwork : options . ConfigureNetwork ,
CNIPluginPath : options . CNIPluginPath ,
CNIConfigDir : options . CNIConfigDir ,
2018-03-08 07:11:43 +08:00
IDMappingOptions : IDMappingOptions {
HostUIDMapping : len ( uidmap ) == 0 ,
HostGIDMapping : len ( uidmap ) == 0 ,
UIDMap : uidmap ,
GIDMap : gidmap ,
} ,
2018-06-05 05:36:26 +08:00
AddCapabilities : copyStringSlice ( options . AddCapabilities ) ,
DropCapabilities : copyStringSlice ( options . DropCapabilities ) ,
CommonBuildOpts : options . CommonBuildOpts ,
TopLayer : topLayer ,
2017-02-11 00:48:15 +08:00
}
if options . Mount {
2017-10-20 05:47:15 +08:00
_ , err = builder . Mount ( mountLabel )
2017-02-11 00:48:15 +08:00
if err != nil {
2017-06-02 03:23:02 +08:00
return nil , errors . Wrapf ( err , "error mounting build container" )
2017-02-11 00:48:15 +08:00
}
}
2018-06-12 06:41:11 +08:00
if err := builder . initConfig ( ctx , src ) ; err != nil {
2018-06-12 05:43:21 +08:00
return nil , errors . Wrapf ( err , "error preparing image configuration" )
}
2017-02-11 00:48:15 +08:00
err = builder . Save ( )
if err != nil {
2017-06-02 03:23:02 +08:00
return nil , errors . Wrapf ( err , "error saving builder state" )
2017-02-11 00:48:15 +08:00
}
return builder , nil
}