2017-06-19 20:39:00 +00:00
package client
import (
2017-12-05 23:32:52 +00:00
"archive/tar"
2017-06-19 20:39:00 +00:00
"context"
2018-09-06 21:39:56 +00:00
"crypto/rand"
"crypto/rsa"
2018-09-06 22:25:45 +00:00
"crypto/x509"
2017-12-05 23:32:52 +00:00
"encoding/json"
2018-09-06 22:25:45 +00:00
"encoding/pem"
2018-07-06 12:03:53 +00:00
"fmt"
2018-08-16 10:44:25 +00:00
"io"
2017-12-02 02:17:40 +00:00
"io/ioutil"
2018-08-01 23:59:32 +00:00
"net"
2017-12-04 03:38:37 +00:00
"net/http"
2017-12-02 02:17:40 +00:00
"os"
"path/filepath"
2017-07-12 05:08:53 +00:00
"runtime"
2017-12-05 23:32:52 +00:00
"strings"
2017-06-19 20:39:00 +00:00
"testing"
2017-12-04 03:38:37 +00:00
"time"
2017-06-19 20:39:00 +00:00
2017-12-05 23:32:52 +00:00
"github.com/containerd/containerd"
"github.com/containerd/containerd/content"
2017-12-28 23:03:49 +00:00
"github.com/containerd/containerd/images"
2017-12-05 23:32:52 +00:00
"github.com/containerd/containerd/namespaces"
2017-12-28 23:03:49 +00:00
"github.com/containerd/containerd/snapshots"
2018-06-04 21:08:29 +00:00
"github.com/containerd/continuity/fs/fstest"
2017-06-22 20:15:46 +00:00
"github.com/moby/buildkit/client/llb"
2018-08-16 10:44:25 +00:00
gateway "github.com/moby/buildkit/frontend/gateway/client"
2017-12-02 02:17:40 +00:00
"github.com/moby/buildkit/identity"
2018-07-20 00:14:28 +00:00
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/secrets/secretsprovider"
2018-09-06 21:39:56 +00:00
"github.com/moby/buildkit/session/sshforward/sshprovider"
2018-07-17 22:17:51 +00:00
"github.com/moby/buildkit/util/testutil"
2017-12-02 02:17:40 +00:00
"github.com/moby/buildkit/util/testutil/httpserver"
2017-11-27 22:24:29 +00:00
"github.com/moby/buildkit/util/testutil/integration"
2017-12-05 23:32:52 +00:00
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
2017-12-02 02:17:40 +00:00
"github.com/stretchr/testify/require"
2018-09-06 21:39:56 +00:00
"golang.org/x/crypto/ssh/agent"
2018-07-06 12:03:53 +00:00
"golang.org/x/sync/errgroup"
2017-06-19 20:39:00 +00:00
)
2018-08-16 10:44:25 +00:00
type nopWriteCloser struct {
io . Writer
}
func ( nopWriteCloser ) Close ( ) error { return nil }
2017-11-27 22:24:29 +00:00
func TestClientIntegration ( t * testing . T ) {
2017-11-30 02:49:04 +00:00
integration . Run ( t , [ ] integration . Test {
2018-05-22 00:07:08 +00:00
testRelativeWorkDir ,
2017-11-30 02:49:04 +00:00
testCallDiskUsage ,
testBuildMultiMount ,
2017-12-02 02:17:40 +00:00
testBuildHTTPSource ,
2017-12-05 23:32:52 +00:00
testBuildPushAndValidate ,
2017-12-11 02:18:18 +00:00
testResolveAndHosts ,
2017-12-11 21:17:07 +00:00
testUser ,
2017-12-13 05:55:16 +00:00
testOCIExporter ,
2018-02-09 19:03:12 +00:00
testWhiteoutParentDir ,
2018-08-30 13:52:19 +00:00
testFrontendImageNaming ,
2018-02-13 19:18:18 +00:00
testDuplicateWhiteouts ,
2018-03-02 16:33:08 +00:00
testSchema1Image ,
2018-03-21 17:04:43 +00:00
testMountWithNoSource ,
2018-03-29 05:27:52 +00:00
testInvalidExporter ,
2018-03-26 13:51:08 +00:00
testReadonlyRootFS ,
2018-09-11 08:02:46 +00:00
testBasicRegistryCacheImportExport ,
testBasicLocalCacheImportExport ,
2018-05-15 19:09:09 +00:00
testCachedMounts ,
2018-05-20 21:48:35 +00:00
testProxyEnv ,
2018-06-04 21:08:29 +00:00
testLocalSymlinkEscape ,
2018-06-08 05:28:52 +00:00
testTmpfsMounts ,
2018-06-18 20:57:36 +00:00
testSharedCacheMounts ,
testLockedCacheMounts ,
testDuplicateCacheMount ,
2018-07-09 22:42:34 +00:00
testParallelLocalBuilds ,
2018-07-20 00:14:28 +00:00
testSecretMounts ,
2018-08-01 23:59:32 +00:00
testExtraHosts ,
2018-08-04 19:42:01 +00:00
testNetworkMode ,
2018-08-16 10:44:25 +00:00
testFrontendMetadataReturn ,
2018-09-06 21:39:56 +00:00
testSSHMount ,
2018-09-11 21:21:40 +00:00
testStdinClosed ,
2018-10-03 00:25:33 +00:00
testHostnameLookup ,
2019-01-18 22:49:08 +00:00
testBasicInlineCacheImportExport ,
2018-09-12 23:13:06 +00:00
} ,
integration . WithMirroredImages ( integration . OfficialImages ( "busybox:latest" , "alpine:latest" ) ) ,
)
2017-07-12 05:08:53 +00:00
}
2018-08-23 07:59:41 +00:00
func newContainerd ( cdAddress string ) ( * containerd . Client , error ) {
return containerd . New ( cdAddress , containerd . WithTimeout ( 60 * time . Second ) )
}
2018-10-03 00:25:33 +00:00
func testHostnameLookup ( t * testing . T , sb integration . Sandbox ) {
if sb . Rootless ( ) {
t . SkipNow ( )
}
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "busybox:latest" ) . Run ( llb . Shlex ( ` sh -c "ping -c 1 $(hostname)" ` ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
}
2018-09-11 21:21:40 +00:00
// moby/buildkit#614
func testStdinClosed ( t * testing . T , sb integration . Sandbox ) {
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "busybox:latest" ) . Run ( llb . Shlex ( "cat" ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
}
2018-09-06 21:39:56 +00:00
func testSSHMount ( t * testing . T , sb integration . Sandbox ) {
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
a := agent . NewKeyring ( )
k , err := rsa . GenerateKey ( rand . Reader , 2048 )
require . NoError ( t , err )
err = a . Add ( agent . AddedKey { PrivateKey : k } )
require . NoError ( t , err )
sockPath , clean , err := makeSSHAgentSock ( a )
require . NoError ( t , err )
defer clean ( )
ssh , err := sshprovider . NewSSHAgentProvider ( [ ] sshprovider . AgentConfig { {
2018-09-06 22:25:45 +00:00
Paths : [ ] string { sockPath } ,
2018-09-06 21:39:56 +00:00
} } )
require . NoError ( t , err )
// no ssh exposed
st := llb . Image ( "busybox:latest" ) . Run ( llb . Shlex ( ` nosuchcmd ` ) , llb . AddSSHSocket ( ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . Error ( t , err )
2018-10-05 22:50:07 +00:00
require . Contains ( t , err . Error ( ) , "no SSH key " )
2018-09-06 21:39:56 +00:00
// custom ID not exposed
st = llb . Image ( "busybox:latest" ) . Run ( llb . Shlex ( ` nosuchcmd ` ) , llb . AddSSHSocket ( llb . SSHID ( "customID" ) ) )
def , err = st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Session : [ ] session . Attachable { ssh } ,
} , nil )
require . Error ( t , err )
require . Contains ( t , err . Error ( ) , "unset ssh forward key customID" )
// missing custom ID ignored on optional
st = llb . Image ( "busybox:latest" ) . Run ( llb . Shlex ( ` ls ` ) , llb . AddSSHSocket ( llb . SSHID ( "customID" ) , llb . SSHOptional ) )
def , err = st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Session : [ ] session . Attachable { ssh } ,
} , nil )
require . NoError ( t , err )
// valid socket
st = llb . Image ( "alpine:latest" ) .
Run ( llb . Shlex ( ` apk add --no-cache openssh ` ) ) .
Run ( llb . Shlex ( ` sh -c 'echo -n $SSH_AUTH_SOCK > /out/sock && ssh-add -l > /out/out' ` ) ,
llb . AddSSHSocket ( ) )
out := st . AddMount ( "/out" , llb . Scratch ( ) )
def , err = out . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
Session : [ ] session . Attachable { ssh } ,
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "sock" ) )
require . NoError ( t , err )
require . Equal ( t , "/run/buildkit/ssh_agent.0" , string ( dt ) )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "out" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "2048" )
require . Contains ( t , string ( dt ) , "(RSA)" )
// forbidden command
st = llb . Image ( "alpine:latest" ) .
Run ( llb . Shlex ( ` apk add --no-cache openssh ` ) ) .
Run ( llb . Shlex ( ` sh -c 'ssh-keygen -f /tmp/key -N "" && ssh-add -k /tmp/key 2> /out/out || true' ` ) ,
llb . AddSSHSocket ( ) )
out = st . AddMount ( "/out" , llb . Scratch ( ) )
def , err = out . Marshal ( )
require . NoError ( t , err )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
Session : [ ] session . Attachable { ssh } ,
} , nil )
require . NoError ( t , err )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "out" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "agent refused operation" )
2018-09-06 22:25:45 +00:00
// valid socket from key on disk
st = llb . Image ( "alpine:latest" ) .
Run ( llb . Shlex ( ` apk add --no-cache openssh ` ) ) .
Run ( llb . Shlex ( ` sh -c 'ssh-add -l > /out/out' ` ) ,
llb . AddSSHSocket ( ) )
out = st . AddMount ( "/out" , llb . Scratch ( ) )
def , err = out . Marshal ( )
require . NoError ( t , err )
k , err = rsa . GenerateKey ( rand . Reader , 1024 )
require . NoError ( t , err )
dt = pem . EncodeToMemory (
& pem . Block {
Type : "RSA PRIVATE KEY" ,
Bytes : x509 . MarshalPKCS1PrivateKey ( k ) ,
} ,
)
tmpDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( tmpDir )
err = ioutil . WriteFile ( filepath . Join ( tmpDir , "key" ) , dt , 0600 )
require . NoError ( t , err )
ssh , err = sshprovider . NewSSHAgentProvider ( [ ] sshprovider . AgentConfig { {
Paths : [ ] string { filepath . Join ( tmpDir , "key" ) } ,
} } )
require . NoError ( t , err )
destDir , err = ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
Session : [ ] session . Attachable { ssh } ,
} , nil )
require . NoError ( t , err )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "out" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "1024" )
require . Contains ( t , string ( dt ) , "(RSA)" )
2018-09-06 21:39:56 +00:00
}
2018-08-01 23:59:32 +00:00
func testExtraHosts ( t * testing . T , sb integration . Sandbox ) {
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` sh -c 'cat /etc/hosts | grep myhost | grep 1.2.3.4' ` ) , llb . AddExtraHost ( "myhost" , net . ParseIP ( "1.2.3.4" ) ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
}
2018-08-04 19:42:01 +00:00
func testNetworkMode ( t * testing . T , sb integration . Sandbox ) {
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` sh -c 'wget https://example.com 2>&1 | grep "wget: bad address"' ` ) , llb . Network ( llb . NetModeNone ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
st2 := llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` ifconfig ` ) , llb . Network ( llb . NetModeHost ) )
def , err = st2 . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
// Currently disabled globally by default
// AllowedEntitlements: []entitlements.Entitlement{entitlements.EntitlementNetworkHost},
} , nil )
require . Error ( t , err )
require . Contains ( t , err . Error ( ) , "network.host is not allowed" )
}
2018-08-30 13:52:19 +00:00
func testFrontendImageNaming ( t * testing . T , sb integration . Sandbox ) {
2018-08-15 15:11:37 +00:00
requiresLinux ( t )
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
registry , err := sb . NewRegistry ( )
if errors . Cause ( err ) == integration . ErrorRequirements {
t . Skip ( err . Error ( ) )
}
require . NoError ( t , err )
2018-08-31 09:54:41 +00:00
checkImageName := map [ string ] func ( out , imageName string , exporterResponse map [ string ] string ) {
ExporterOCI : func ( out , imageName string , exporterResponse map [ string ] string ) {
2018-08-15 15:11:37 +00:00
// Nothing to check
return
} ,
2018-08-31 09:54:41 +00:00
ExporterDocker : func ( out , imageName string , exporterResponse map [ string ] string ) {
require . Contains ( t , exporterResponse , "image.name" )
require . Equal ( t , exporterResponse [ "image.name" ] , "docker.io/library/" + imageName )
2018-08-15 15:11:37 +00:00
dt , err := ioutil . ReadFile ( out )
require . NoError ( t , err )
m , err := testutil . ReadTarToMap ( dt , false )
require . NoError ( t , err )
_ , ok := m [ "oci-layout" ]
require . True ( t , ok )
var index ocispec . Index
err = json . Unmarshal ( m [ "index.json" ] . Data , & index )
require . NoError ( t , err )
require . Equal ( t , 2 , index . SchemaVersion )
require . Equal ( t , 1 , len ( index . Manifests ) )
var dockerMfst [ ] struct {
RepoTags [ ] string
}
err = json . Unmarshal ( m [ "manifest.json" ] . Data , & dockerMfst )
require . NoError ( t , err )
require . Equal ( t , 1 , len ( dockerMfst ) )
require . Equal ( t , 1 , len ( dockerMfst [ 0 ] . RepoTags ) )
require . Equal ( t , "docker.io/library/" + imageName , dockerMfst [ 0 ] . RepoTags [ 0 ] )
} ,
2018-08-31 09:54:41 +00:00
ExporterImage : func ( _ , imageName string , exporterResponse map [ string ] string ) {
require . Contains ( t , exporterResponse , "image.name" )
require . Equal ( t , exporterResponse [ "image.name" ] , imageName )
2018-08-15 15:11:37 +00:00
// check if we can pull (requires containerd)
var cdAddress string
if cd , ok := sb . ( interface {
ContainerdAddress ( ) string
} ) ; ! ok {
return
} else {
cdAddress = cd . ContainerdAddress ( )
}
// TODO: make public pull helper function so this can be checked for standalone as well
client , err := containerd . New ( cdAddress )
require . NoError ( t , err )
defer client . Close ( )
ctx := namespaces . WithNamespace ( context . Background ( ) , "buildkit" )
// check image in containerd
_ , err = client . ImageService ( ) . Get ( ctx , imageName )
require . NoError ( t , err )
// deleting image should release all content
err = client . ImageService ( ) . Delete ( ctx , imageName , images . SynchronousDelete ( ) )
require . NoError ( t , err )
checkAllReleasable ( t , c , sb , true )
_ , err = client . Pull ( ctx , imageName )
require . NoError ( t , err )
err = client . ImageService ( ) . Delete ( ctx , imageName , images . SynchronousDelete ( ) )
require . NoError ( t , err )
} ,
}
2018-08-30 14:59:12 +00:00
// A caller provided name takes precedence over one returned by the frontend. Iterate over both options.
for _ , winner := range [ ] string { "frontend" , "caller" } {
2018-08-30 15:27:04 +00:00
winner := winner // capture loop variable.
// The double layer of `t.Run` here is required so
// that the inner-most tests (with the actual
// functionality) have definitely completed before the
// sandbox and registry cleanups (defered above) are run.
t . Run ( winner , func ( t * testing . T ) {
for _ , exp := range [ ] string { ExporterOCI , ExporterDocker , ExporterImage } {
exp := exp // capture loop variable.
t . Run ( exp , func ( t * testing . T ) {
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
so := SolveOpt {
Exporter : exp ,
ExporterAttrs : map [ string ] string { } ,
}
out := filepath . Join ( destDir , "out.tar" )
imageName := "image-" + exp + "-fe:latest"
switch exp {
2018-08-31 10:19:04 +00:00
case ExporterOCI :
t . Skip ( "oci exporter does not support named images" )
case ExporterDocker :
2018-08-30 15:27:04 +00:00
outW , err := os . Create ( out )
require . NoError ( t , err )
so . ExporterOutput = outW
case ExporterImage :
imageName = registry + "/" + imageName
so . ExporterAttrs [ "push" ] = "true"
}
feName := imageName
switch winner {
case "caller" :
feName = "loser:latest"
so . ExporterAttrs [ "name" ] = imageName
case "frontend" :
2018-08-31 16:34:49 +00:00
so . ExporterAttrs [ "name" ] = "*"
2018-08-30 15:27:04 +00:00
}
frontend := func ( ctx context . Context , c gateway . Client ) ( * gateway . Result , error ) {
res := gateway . NewResult ( )
res . AddMeta ( "image.name" , [ ] byte ( feName ) )
return res , nil
}
2018-08-31 09:54:41 +00:00
resp , err := c . Build ( context . TODO ( ) , so , "" , frontend , nil )
2018-08-30 15:27:04 +00:00
require . NoError ( t , err )
2018-08-31 09:54:41 +00:00
checkImageName [ exp ] ( out , imageName , resp . ExporterResponse )
2018-08-30 15:27:04 +00:00
} )
2018-08-15 15:11:37 +00:00
}
2018-08-30 15:27:04 +00:00
} )
2018-08-15 15:11:37 +00:00
}
checkAllReleasable ( t , c , sb , true )
}
2018-07-20 00:14:28 +00:00
func testSecretMounts ( t * testing . T , sb integration . Sandbox ) {
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` sh -c 'mount | grep mysecret | grep "type tmpfs" && [ "$(cat /run/secrets/mysecret)" = 'foo-secret' ]' ` ) , llb . AddSecret ( "/run/secrets/mysecret" ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Session : [ ] session . Attachable { secretsprovider . FromMap ( map [ string ] [ ] byte {
"/run/secrets/mysecret" : [ ] byte ( "foo-secret" ) ,
} ) } ,
} , nil )
require . NoError ( t , err )
// test optional
st = llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` echo secret2 ` ) , llb . AddSecret ( "/run/secrets/mysecret2" , llb . SecretOptional ) )
def , err = st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Session : [ ] session . Attachable { secretsprovider . FromMap ( map [ string ] [ ] byte { } ) } ,
} , nil )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
st = llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` echo secret3 ` ) , llb . AddSecret ( "/run/secrets/mysecret3" ) )
def , err = st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Session : [ ] session . Attachable { secretsprovider . FromMap ( map [ string ] [ ] byte { } ) } ,
} , nil )
require . Error ( t , err )
// test id,perm,uid
st = llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` sh -c '[ "$(stat -c "%%u %%g %%f" /run/secrets/mysecret4)" = "1 1 81ff" ]' ` ) , llb . AddSecret ( "/run/secrets/mysecret4" , llb . SecretID ( "mysecret" ) , llb . SecretFileOpt ( 1 , 1 , 0777 ) ) )
def , err = st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Session : [ ] session . Attachable { secretsprovider . FromMap ( map [ string ] [ ] byte {
"mysecret" : [ ] byte ( "pw" ) ,
} ) } ,
} , nil )
require . NoError ( t , err )
}
2018-06-08 05:28:52 +00:00
func testTmpfsMounts ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-06-08 05:28:52 +00:00
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` sh -c 'mount | grep /foobar | grep "type tmpfs" && touch /foobar/test' ` ) , llb . AddMount ( "/foobar" , llb . Scratch ( ) , llb . Tmpfs ( ) ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
}
2018-06-04 21:08:29 +00:00
func testLocalSymlinkEscape ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-06-04 21:08:29 +00:00
require . NoError ( t , err )
defer c . Close ( )
2018-06-10 16:21:24 +00:00
test := [ ] byte ( ` set - ex
2018-06-04 21:08:29 +00:00
[ [ - L / mount / foo ] ]
[ [ - L / mount / sub / bar ] ]
[ [ - L / mount / bax ] ]
[ [ - f / mount / bay ] ]
2018-06-10 16:21:24 +00:00
[ [ - f / mount / sub / sub2 / file ] ]
2018-06-04 21:08:29 +00:00
[ [ ! - f / mount / baz ] ]
[ [ ! - f / mount / etc / passwd ] ]
[ [ ! - f / mount / etc / group ] ]
[ [ $ ( readlink / mount / foo ) == "/etc/passwd" ] ]
[ [ $ ( readlink / mount / sub / bar ) == "../../../etc/group" ] ]
` )
dir , err := tmpdir (
// point to absolute path that is not part of dir
fstest . Symlink ( "/etc/passwd" , "foo" ) ,
fstest . CreateDir ( "sub" , 0700 ) ,
// point outside of the dir
fstest . Symlink ( "../../../etc/group" , "sub/bar" ) ,
// regular valid symlink
fstest . Symlink ( "bay" , "bax" ) ,
// target for symlink (not requested)
fstest . CreateFile ( "bay" , [ ] byte { } , 0600 ) ,
2018-06-10 16:21:24 +00:00
// file with many subdirs
fstest . CreateDir ( "sub/sub2" , 0700 ) ,
fstest . CreateFile ( "sub/sub2/file" , [ ] byte { } , 0600 ) ,
2018-06-04 21:08:29 +00:00
// unused file that shouldn't be included
fstest . CreateFile ( "baz" , [ ] byte { } , 0600 ) ,
fstest . CreateFile ( "test.sh" , test , 0700 ) ,
)
require . NoError ( t , err )
defer os . RemoveAll ( dir )
local := llb . Local ( "mylocal" , llb . FollowPaths ( [ ] string {
2018-06-10 16:21:24 +00:00
"test.sh" , "foo" , "sub/bar" , "bax" , "sub/sub2/file" ,
2018-06-04 21:08:29 +00:00
} ) )
st := llb . Image ( "busybox:latest" ) .
Run ( llb . Shlex ( ` sh /mount/test.sh ` ) , llb . AddMount ( "/mount" , local , llb . Readonly ) )
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
LocalDirs : map [ string ] string {
"mylocal" : dir ,
} ,
} , nil )
require . NoError ( t , err )
}
2018-05-22 00:07:08 +00:00
func testRelativeWorkDir ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-05-22 00:07:08 +00:00
require . NoError ( t , err )
defer c . Close ( )
pwd := llb . Image ( "docker.io/library/busybox:latest" ) .
Dir ( "test1" ) .
Dir ( "test2" ) .
Run ( llb . Shlex ( ` sh -c "pwd > /out/pwd" ` ) ) .
AddMount ( "/out" , llb . Scratch ( ) )
def , err := pwd . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "pwd" ) )
require . NoError ( t , err )
require . Equal ( t , [ ] byte ( "/test1/test2\n" ) , dt )
}
2017-11-27 22:24:29 +00:00
func testCallDiskUsage ( t * testing . T , sb integration . Sandbox ) {
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2017-12-02 02:17:40 +00:00
require . NoError ( t , err )
defer c . Close ( )
2017-06-19 20:39:00 +00:00
_ , err = c . DiskUsage ( context . TODO ( ) )
2017-12-02 02:17:40 +00:00
require . NoError ( t , err )
2017-06-19 20:39:00 +00:00
}
2017-11-27 22:24:29 +00:00
func testBuildMultiMount ( t * testing . T , sb integration . Sandbox ) {
2017-12-09 02:19:08 +00:00
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2017-12-02 02:17:40 +00:00
require . NoError ( t , err )
defer c . Close ( )
2017-06-19 20:39:00 +00:00
alpine := llb . Image ( "docker.io/library/alpine:latest" )
2017-06-21 21:48:21 +00:00
ls := alpine . Run ( llb . Shlex ( "/bin/ls -l" ) )
2017-06-19 20:39:00 +00:00
busybox := llb . Image ( "docker.io/library/busybox:latest" )
2017-06-21 21:48:21 +00:00
cp := ls . Run ( llb . Shlex ( "/bin/cp -a /busybox/etc/passwd baz" ) )
2017-06-19 20:39:00 +00:00
cp . AddMount ( "/busybox" , busybox )
2017-10-02 04:59:34 +00:00
def , err := cp . Marshal ( )
2017-12-02 02:17:40 +00:00
require . NoError ( t , err )
2017-06-19 20:39:00 +00:00
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
2017-12-02 02:17:40 +00:00
require . NoError ( t , err )
2017-12-28 23:03:49 +00:00
checkAllReleasable ( t , c , sb , true )
2017-12-02 02:17:40 +00:00
}
func testBuildHTTPSource ( t * testing . T , sb integration . Sandbox ) {
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2017-12-02 02:17:40 +00:00
require . NoError ( t , err )
defer c . Close ( )
2017-12-04 03:38:37 +00:00
modTime := time . Now ( ) . Add ( - 24 * time . Hour ) // avoid falso positive with current time
2017-12-02 02:17:40 +00:00
resp := httpserver . Response {
2017-12-04 03:38:37 +00:00
Etag : identity . NewID ( ) ,
Content : [ ] byte ( "content1" ) ,
LastModified : & modTime ,
2017-12-02 02:17:40 +00:00
}
server := httpserver . NewTestServer ( map [ string ] httpserver . Response {
"/foo" : resp ,
} )
defer server . Close ( )
// invalid URL first
st := llb . HTTP ( server . URL + "/bar" )
def , err := st . Marshal ( )
require . NoError ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
2017-12-02 02:17:40 +00:00
require . Error ( t , err )
require . Contains ( t , err . Error ( ) , "invalid response status 404" )
// first correct request
st = llb . HTTP ( server . URL + "/foo" )
def , err = st . Marshal ( )
require . NoError ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
2017-12-02 02:17:40 +00:00
require . NoError ( t , err )
require . Equal ( t , server . Stats ( "/foo" ) . AllRequests , 1 )
require . Equal ( t , server . Stats ( "/foo" ) . CachedRequests , 0 )
tmpdir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( tmpdir )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-27 04:16:32 +00:00
Exporter : ExporterLocal ,
ExporterOutputDir : tmpdir ,
2017-12-02 02:17:40 +00:00
} , nil )
require . NoError ( t , err )
require . Equal ( t , server . Stats ( "/foo" ) . AllRequests , 2 )
require . Equal ( t , server . Stats ( "/foo" ) . CachedRequests , 1 )
dt , err := ioutil . ReadFile ( filepath . Join ( tmpdir , "foo" ) )
require . NoError ( t , err )
require . Equal ( t , [ ] byte ( "content1" ) , dt )
2017-12-04 03:38:37 +00:00
// test extra options
st = llb . HTTP ( server . URL + "/foo" , llb . Filename ( "bar" ) , llb . Chmod ( 0741 ) , llb . Chown ( 1000 , 1000 ) )
def , err = st . Marshal ( )
require . NoError ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-27 04:16:32 +00:00
Exporter : ExporterLocal ,
ExporterOutputDir : tmpdir ,
2017-12-04 03:38:37 +00:00
} , nil )
require . NoError ( t , err )
require . Equal ( t , server . Stats ( "/foo" ) . AllRequests , 3 )
require . Equal ( t , server . Stats ( "/foo" ) . CachedRequests , 1 )
dt , err = ioutil . ReadFile ( filepath . Join ( tmpdir , "bar" ) )
require . NoError ( t , err )
require . Equal ( t , [ ] byte ( "content1" ) , dt )
fi , err := os . Stat ( filepath . Join ( tmpdir , "bar" ) )
require . NoError ( t , err )
require . Equal ( t , fi . ModTime ( ) . Format ( http . TimeFormat ) , modTime . Format ( http . TimeFormat ) )
require . Equal ( t , int ( fi . Mode ( ) & 0777 ) , 0741 )
2017-12-28 23:03:49 +00:00
checkAllReleasable ( t , c , sb , true )
2017-12-02 02:17:40 +00:00
// TODO: check that second request was marked as cached
2017-06-19 20:39:00 +00:00
}
2017-11-27 22:24:29 +00:00
2017-12-11 02:18:18 +00:00
func testResolveAndHosts ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2017-12-11 02:18:18 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := llb . Scratch ( )
run := func ( cmd string ) {
st = busybox . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) ) . AddMount ( "/wd" , st )
}
run ( ` sh -c "cp /etc/resolv.conf ." ` )
run ( ` sh -c "cp /etc/hosts ." ` )
def , err := st . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-27 04:16:32 +00:00
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
2017-12-11 02:18:18 +00:00
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "resolv.conf" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "nameserver" )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "hosts" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "127.0.0.1 localhost" )
}
2017-12-11 21:17:07 +00:00
func testUser ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2017-12-11 21:17:07 +00:00
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "busybox:latest" ) . Run ( llb . Shlex ( ` sh -c "mkdir -m 0777 /wd" ` ) )
run := func ( user , cmd string ) {
st = st . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) , llb . User ( user ) )
}
run ( "daemon" , ` sh -c "id -nu > user" ` )
run ( "daemon:daemon" , ` sh -c "id -ng > group" ` )
run ( "daemon:nogroup" , ` sh -c "id -ng > nogroup" ` )
run ( "1:1" , ` sh -c "id -g > userone" ` )
st = st . Run ( llb . Shlex ( "cp -a /wd/. /out/" ) )
out := st . AddMount ( "/out" , llb . Scratch ( ) )
def , err := out . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-27 04:16:32 +00:00
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
2017-12-11 21:17:07 +00:00
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "user" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "daemon" )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "group" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "daemon" )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "nogroup" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "nogroup" )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "userone" ) )
require . NoError ( t , err )
require . Contains ( t , string ( dt ) , "1" )
2017-12-28 23:03:49 +00:00
checkAllReleasable ( t , c , sb , true )
2017-12-11 21:17:07 +00:00
}
2017-12-13 05:55:16 +00:00
func testOCIExporter ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2017-12-13 05:55:16 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := llb . Scratch ( )
run := func ( cmd string ) {
st = busybox . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) ) . AddMount ( "/wd" , st )
}
run ( ` sh -c "echo -n first > foo" ` )
run ( ` sh -c "echo -n second > bar" ` )
def , err := st . Marshal ( )
require . NoError ( t , err )
2017-12-18 00:20:19 +00:00
for _ , exp := range [ ] string { ExporterOCI , ExporterDocker } {
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
out := filepath . Join ( destDir , "out.tar" )
2018-03-27 04:16:32 +00:00
outW , err := os . Create ( out )
require . NoError ( t , err )
2017-12-18 00:20:19 +00:00
target := "example.com/buildkit/testoci:latest"
2018-08-31 10:19:04 +00:00
attrs := map [ string ] string { }
if exp == ExporterDocker {
attrs [ "name" ] = target
}
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-08-31 10:19:04 +00:00
Exporter : exp ,
ExporterAttrs : attrs ,
2018-03-27 04:16:32 +00:00
ExporterOutput : outW ,
2017-12-18 00:20:19 +00:00
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( out )
require . NoError ( t , err )
2018-07-17 22:17:51 +00:00
m , err := testutil . ReadTarToMap ( dt , false )
2017-12-18 00:20:19 +00:00
require . NoError ( t , err )
_ , ok := m [ "oci-layout" ]
require . True ( t , ok )
var index ocispec . Index
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "index.json" ] . Data , & index )
2017-12-18 00:20:19 +00:00
require . NoError ( t , err )
require . Equal ( t , 2 , index . SchemaVersion )
require . Equal ( t , 1 , len ( index . Manifests ) )
var mfst ocispec . Manifest
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "blobs/sha256/" + index . Manifests [ 0 ] . Digest . Hex ( ) ] . Data , & mfst )
2017-12-18 00:20:19 +00:00
require . NoError ( t , err )
require . Equal ( t , 2 , len ( mfst . Layers ) )
var ociimg ocispec . Image
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "blobs/sha256/" + mfst . Config . Digest . Hex ( ) ] . Data , & ociimg )
2017-12-18 00:20:19 +00:00
require . NoError ( t , err )
require . Equal ( t , "layers" , ociimg . RootFS . Type )
require . Equal ( t , 2 , len ( ociimg . RootFS . DiffIDs ) )
_ , ok = m [ "blobs/sha256/" + mfst . Layers [ 0 ] . Digest . Hex ( ) ]
require . True ( t , ok )
_ , ok = m [ "blobs/sha256/" + mfst . Layers [ 1 ] . Digest . Hex ( ) ]
require . True ( t , ok )
if exp != ExporterDocker {
continue
}
var dockerMfst [ ] struct {
Config string
RepoTags [ ] string
Layers [ ] string
}
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "manifest.json" ] . Data , & dockerMfst )
2017-12-18 00:20:19 +00:00
require . NoError ( t , err )
require . Equal ( t , 1 , len ( dockerMfst ) )
_ , ok = m [ dockerMfst [ 0 ] . Config ]
require . True ( t , ok )
require . Equal ( t , 2 , len ( dockerMfst [ 0 ] . Layers ) )
require . Equal ( t , 1 , len ( dockerMfst [ 0 ] . RepoTags ) )
require . Equal ( t , target , dockerMfst [ 0 ] . RepoTags [ 0 ] )
for _ , l := range dockerMfst [ 0 ] . Layers {
_ , ok := m [ l ]
require . True ( t , ok )
}
}
2017-12-28 23:03:49 +00:00
checkAllReleasable ( t , c , sb , true )
2017-12-13 05:55:16 +00:00
}
2018-08-16 10:44:25 +00:00
func testFrontendMetadataReturn ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
frontend := func ( ctx context . Context , c gateway . Client ) ( * gateway . Result , error ) {
res := gateway . NewResult ( )
res . AddMeta ( "frontend.returned" , [ ] byte ( "true" ) )
res . AddMeta ( "not-frontend.not-returned" , [ ] byte ( "false" ) )
res . AddMeta ( "frontendnot.returned.either" , [ ] byte ( "false" ) )
return res , nil
}
res , err := c . Build ( context . TODO ( ) , SolveOpt {
Exporter : ExporterOCI ,
ExporterAttrs : map [ string ] string { } ,
ExporterOutput : nopWriteCloser { ioutil . Discard } ,
} , "" , frontend , nil )
require . NoError ( t , err )
require . Contains ( t , res . ExporterResponse , "frontend.returned" )
require . Equal ( t , res . ExporterResponse [ "frontend.returned" ] , "true" )
require . NotContains ( t , res . ExporterResponse , "not-frontend.not-returned" )
require . NotContains ( t , res . ExporterResponse , "frontendnot.returned.either" )
checkAllReleasable ( t , c , sb , true )
}
2017-12-05 23:32:52 +00:00
func testBuildPushAndValidate ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := llb . Scratch ( )
run := func ( cmd string ) {
st = busybox . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) ) . AddMount ( "/wd" , st )
}
2018-06-18 20:57:36 +00:00
run ( ` sh -e -c "mkdir -p foo/sub; echo -n first > foo/sub/bar; chmod 0741 foo;" ` )
2017-12-08 22:55:23 +00:00
run ( ` true ` ) // this doesn't create a layer
2017-12-05 23:32:52 +00:00
run ( ` sh -c "echo -n second > foo/sub/baz" ` )
def , err := st . Marshal ( )
require . NoError ( t , err )
registry , err := sb . NewRegistry ( )
if errors . Cause ( err ) == integration . ErrorRequirements {
t . Skip ( err . Error ( ) )
}
require . NoError ( t , err )
target := registry + "/buildkit/testpush:latest"
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2017-12-05 23:32:52 +00:00
Exporter : ExporterImage ,
ExporterAttrs : map [ string ] string {
"name" : target ,
"push" : "true" ,
} ,
} , nil )
require . NoError ( t , err )
// test existence of the image with next build
firstBuild := llb . Image ( target )
def , err = firstBuild . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
2017-12-13 05:55:16 +00:00
defer os . RemoveAll ( destDir )
2017-12-05 23:32:52 +00:00
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-27 04:16:32 +00:00
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
2017-12-05 23:32:52 +00:00
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "foo/sub/bar" ) )
require . NoError ( t , err )
require . Equal ( t , dt , [ ] byte ( "first" ) )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "foo/sub/baz" ) )
require . NoError ( t , err )
require . Equal ( t , dt , [ ] byte ( "second" ) )
fi , err := os . Stat ( filepath . Join ( destDir , "foo" ) )
require . NoError ( t , err )
require . Equal ( t , 0741 , int ( fi . Mode ( ) & 0777 ) )
2017-12-28 23:03:49 +00:00
checkAllReleasable ( t , c , sb , false )
2017-12-05 23:32:52 +00:00
// examine contents of exported tars (requires containerd)
var cdAddress string
if cd , ok := sb . ( interface {
ContainerdAddress ( ) string
} ) ; ! ok {
return
} else {
cdAddress = cd . ContainerdAddress ( )
}
// TODO: make public pull helper function so this can be checked for standalone as well
2018-08-23 07:59:41 +00:00
client , err := newContainerd ( cdAddress )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
defer client . Close ( )
ctx := namespaces . WithNamespace ( context . Background ( ) , "buildkit" )
2017-12-28 23:03:49 +00:00
// check image in containerd
_ , err = client . ImageService ( ) . Get ( ctx , target )
require . NoError ( t , err )
// deleting image should release all content
err = client . ImageService ( ) . Delete ( ctx , target , images . SynchronousDelete ( ) )
require . NoError ( t , err )
checkAllReleasable ( t , c , sb , true )
2017-12-05 23:32:52 +00:00
img , err := client . Pull ( ctx , target )
require . NoError ( t , err )
desc , err := img . Config ( ctx )
require . NoError ( t , err )
2018-06-06 08:02:01 +00:00
dt , err = content . ReadBlob ( ctx , img . ContentStore ( ) , desc )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
var ociimg ocispec . Image
err = json . Unmarshal ( dt , & ociimg )
require . NoError ( t , err )
require . NotEqual ( t , "" , ociimg . OS )
require . NotEqual ( t , "" , ociimg . Architecture )
require . NotEqual ( t , "" , ociimg . Config . WorkingDir )
require . Equal ( t , "layers" , ociimg . RootFS . Type )
require . Equal ( t , 2 , len ( ociimg . RootFS . DiffIDs ) )
2018-01-05 18:51:32 +00:00
require . NotNil ( t , ociimg . Created )
require . True ( t , time . Since ( * ociimg . Created ) < 2 * time . Minute )
2017-12-05 23:32:52 +00:00
require . Condition ( t , func ( ) bool {
for _ , env := range ociimg . Config . Env {
if strings . HasPrefix ( env , "PATH=" ) {
return true
}
}
return false
} )
2017-12-08 22:55:23 +00:00
require . Equal ( t , 3 , len ( ociimg . History ) )
require . Contains ( t , ociimg . History [ 0 ] . CreatedBy , "foo/sub/bar" )
require . Contains ( t , ociimg . History [ 1 ] . CreatedBy , "true" )
require . Contains ( t , ociimg . History [ 2 ] . CreatedBy , "foo/sub/baz" )
require . False ( t , ociimg . History [ 0 ] . EmptyLayer )
require . True ( t , ociimg . History [ 1 ] . EmptyLayer )
require . False ( t , ociimg . History [ 2 ] . EmptyLayer )
2018-06-06 08:02:01 +00:00
dt , err = content . ReadBlob ( ctx , img . ContentStore ( ) , img . Target ( ) )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
2018-06-26 22:24:33 +00:00
var mfst = struct {
MediaType string ` json:"mediaType,omitempty" `
ocispec . Manifest
} { }
2017-12-05 23:32:52 +00:00
err = json . Unmarshal ( dt , & mfst )
require . NoError ( t , err )
2018-06-26 22:24:33 +00:00
require . Equal ( t , images . MediaTypeDockerSchema2Manifest , mfst . MediaType )
2017-12-05 23:32:52 +00:00
require . Equal ( t , 2 , len ( mfst . Layers ) )
2018-06-06 08:02:01 +00:00
dt , err = content . ReadBlob ( ctx , img . ContentStore ( ) , ocispec . Descriptor { Digest : mfst . Layers [ 0 ] . Digest } )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
2018-07-17 22:17:51 +00:00
m , err := testutil . ReadTarToMap ( dt , true )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
item , ok := m [ "foo/" ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
require . Equal ( t , int32 ( item . Header . Typeflag ) , tar . TypeDir )
require . Equal ( t , 0741 , int ( item . Header . Mode & 0777 ) )
2017-12-05 23:32:52 +00:00
item , ok = m [ "foo/sub/" ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
require . Equal ( t , int32 ( item . Header . Typeflag ) , tar . TypeDir )
2017-12-05 23:32:52 +00:00
item , ok = m [ "foo/sub/bar" ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
require . Equal ( t , int32 ( item . Header . Typeflag ) , tar . TypeReg )
require . Equal ( t , [ ] byte ( "first" ) , item . Data )
2017-12-05 23:32:52 +00:00
_ , ok = m [ "foo/sub/baz" ]
require . False ( t , ok )
2018-06-06 08:02:01 +00:00
dt , err = content . ReadBlob ( ctx , img . ContentStore ( ) , ocispec . Descriptor { Digest : mfst . Layers [ 1 ] . Digest } )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
2018-07-17 22:17:51 +00:00
m , err = testutil . ReadTarToMap ( dt , true )
2017-12-05 23:32:52 +00:00
require . NoError ( t , err )
item , ok = m [ "foo/sub/baz" ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
require . Equal ( t , int32 ( item . Header . Typeflag ) , tar . TypeReg )
require . Equal ( t , [ ] byte ( "second" ) , item . Data )
2017-12-05 23:32:52 +00:00
2017-12-20 21:54:48 +00:00
item , ok = m [ "foo/" ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
require . Equal ( t , int32 ( item . Header . Typeflag ) , tar . TypeDir )
require . Equal ( t , 0741 , int ( item . Header . Mode & 0777 ) )
2017-12-20 21:54:48 +00:00
item , ok = m [ "foo/sub/" ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
require . Equal ( t , int32 ( item . Header . Typeflag ) , tar . TypeDir )
2017-12-20 21:54:48 +00:00
_ , ok = m [ "foo/sub/bar" ]
require . False ( t , ok )
2017-12-05 23:32:52 +00:00
}
2018-09-11 08:02:46 +00:00
func testBasicCacheImportExport ( t * testing . T , sb integration . Sandbox , cacheOptionsEntry CacheOptionsEntry ) {
2018-04-26 00:43:17 +00:00
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-04-26 00:43:17 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := llb . Scratch ( )
run := func ( cmd string ) {
st = busybox . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) ) . AddMount ( "/wd" , st )
}
run ( ` sh -c "echo -n foobar > const" ` )
run ( ` sh -c "cat /dev/urandom | head -c 100 | sha256sum > unique" ` )
def , err := st . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
2018-04-25 17:49:15 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-04-26 00:43:17 +00:00
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
2018-09-11 08:02:46 +00:00
CacheExports : [ ] CacheOptionsEntry {
cacheOptionsEntry ,
} ,
2018-04-26 00:43:17 +00:00
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "const" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt ) , "foobar" )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "unique" ) )
require . NoError ( t , err )
2018-07-26 22:31:35 +00:00
err = c . Prune ( context . TODO ( ) , nil , PruneAll )
2018-04-26 00:43:17 +00:00
require . NoError ( t , err )
checkAllRemoved ( t , c , sb )
destDir , err = ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
2018-04-25 17:49:15 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-04-26 00:43:17 +00:00
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
2018-09-11 08:02:46 +00:00
CacheImports : [ ] CacheOptionsEntry {
cacheOptionsEntry ,
} ,
2018-04-26 00:43:17 +00:00
} , nil )
require . NoError ( t , err )
dt2 , err := ioutil . ReadFile ( filepath . Join ( destDir , "const" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt2 ) , "foobar" )
dt2 , err = ioutil . ReadFile ( filepath . Join ( destDir , "unique" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt ) , string ( dt2 ) )
}
2018-09-11 08:02:46 +00:00
func testBasicRegistryCacheImportExport ( t * testing . T , sb integration . Sandbox ) {
registry , err := sb . NewRegistry ( )
if errors . Cause ( err ) == integration . ErrorRequirements {
t . Skip ( err . Error ( ) )
}
require . NoError ( t , err )
target := registry + "/buildkit/testexport:latest"
o := CacheOptionsEntry {
Type : "registry" ,
Attrs : map [ string ] string {
"ref" : target ,
} ,
}
testBasicCacheImportExport ( t , sb , o )
}
func testBasicLocalCacheImportExport ( t * testing . T , sb integration . Sandbox ) {
dir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( dir )
o := CacheOptionsEntry {
Type : "local" ,
Attrs : map [ string ] string {
"store" : dir ,
} ,
}
testBasicCacheImportExport ( t , sb , o )
}
2019-01-18 22:49:08 +00:00
func testBasicInlineCacheImportExport ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
registry , err := sb . NewRegistry ( )
if errors . Cause ( err ) == integration . ErrorRequirements {
t . Skip ( err . Error ( ) )
}
require . NoError ( t , err )
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := llb . Scratch ( )
run := func ( cmd string ) {
st = busybox . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) ) . AddMount ( "/wd" , st )
}
run ( ` sh -c "echo -n foobar > const" ` )
run ( ` sh -c "cat /dev/urandom | head -c 100 | sha256sum > unique" ` )
def , err := st . Marshal ( )
require . NoError ( t , err )
target := registry + "/buildkit/testexportinline:latest"
resp , err := c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterImage ,
ExporterAttrs : map [ string ] string {
"name" : target ,
"push" : "true" ,
} ,
CacheExports : [ ] CacheOptionsEntry {
{
Type : "inline" ,
} ,
} ,
} , nil )
require . NoError ( t , err )
dgst , ok := resp . ExporterResponse [ "containerimage.digest" ]
require . Equal ( t , ok , true )
err = c . Prune ( context . TODO ( ) , nil , PruneAll )
require . NoError ( t , err )
checkAllRemoved ( t , c , sb )
resp , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterImage ,
CacheExports : [ ] CacheOptionsEntry {
{
Type : "inline" ,
} ,
} ,
CacheImports : [ ] CacheOptionsEntry {
{
Type : "registry" ,
Attrs : map [ string ] string {
"ref" : target ,
} ,
} ,
} ,
} , nil )
require . NoError ( t , err )
dgst2 , ok := resp . ExporterResponse [ "containerimage.digest" ]
require . Equal ( t , ok , true )
require . Equal ( t , dgst , dgst2 )
}
2018-05-15 19:09:09 +00:00
func testCachedMounts ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-05-15 19:09:09 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
// setup base for one of the cache sources
st := busybox . Run ( llb . Shlex ( ` sh -c "echo -n base > baz" ` ) , llb . Dir ( "/wd" ) )
base := st . AddMount ( "/wd" , llb . Scratch ( ) )
st = busybox . Run ( llb . Shlex ( ` sh -c "echo -n first > foo" ` ) , llb . Dir ( "/wd" ) )
2018-06-18 20:57:36 +00:00
st . AddMount ( "/wd" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountShared ) )
2018-05-15 19:09:09 +00:00
st = st . Run ( llb . Shlex ( ` sh -c "cat foo && echo -n second > /wd2/bar" ` ) , llb . Dir ( "/wd" ) )
2018-06-18 20:57:36 +00:00
st . AddMount ( "/wd" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountShared ) )
st . AddMount ( "/wd2" , base , llb . AsPersistentCacheDir ( "mycache2" , llb . CacheMountShared ) )
2018-05-15 19:09:09 +00:00
def , err := st . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
// repeat to make sure cache works
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
// second build using cache directories
st = busybox . Run ( llb . Shlex ( ` sh -c "cp /src0/foo . && cp /src1/bar . && cp /src1/baz ." ` ) , llb . Dir ( "/wd" ) )
out := st . AddMount ( "/wd" , llb . Scratch ( ) )
2018-06-18 20:57:36 +00:00
st . AddMount ( "/src0" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountShared ) )
st . AddMount ( "/src1" , base , llb . AsPersistentCacheDir ( "mycache2" , llb . CacheMountShared ) )
2018-05-15 19:09:09 +00:00
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
def , err = out . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "foo" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt ) , "first" )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "bar" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt ) , "second" )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "baz" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt ) , "base" )
checkAllReleasable ( t , c , sb , true )
}
2018-06-18 20:57:36 +00:00
func testSharedCacheMounts ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := busybox . Run ( llb . Shlex ( ` sh -e -c "touch one; while [[ ! -f two ]]; do ls -l; usleep 500000; done" ` ) , llb . Dir ( "/wd" ) )
st . AddMount ( "/wd" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountShared ) )
st2 := busybox . Run ( llb . Shlex ( ` sh -e -c "touch two; while [[ ! -f one ]]; do ls -l; usleep 500000; done" ` ) , llb . Dir ( "/wd" ) )
st2 . AddMount ( "/wd" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountShared ) )
out := busybox . Run ( llb . Shlex ( "true" ) )
out . AddMount ( "/m1" , st . Root ( ) )
out . AddMount ( "/m2" , st2 . Root ( ) )
def , err := out . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
}
func testLockedCacheMounts ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := busybox . Run ( llb . Shlex ( ` sh -e -c "touch one; if [[ -f two ]]; then exit 0; fi; for i in $(seq 10); do if [[ -f two ]]; then exit 1; fi; usleep 200000; done" ` ) , llb . Dir ( "/wd" ) )
st . AddMount ( "/wd" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountLocked ) )
st2 := busybox . Run ( llb . Shlex ( ` sh -e -c "touch two; if [[ -f one ]]; then exit 0; fi; for i in $(seq 10); do if [[ -f one ]]; then exit 1; fi; usleep 200000; done" ` ) , llb . Dir ( "/wd" ) )
st2 . AddMount ( "/wd" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountLocked ) )
out := busybox . Run ( llb . Shlex ( "true" ) )
out . AddMount ( "/m1" , st . Root ( ) )
out . AddMount ( "/m2" , st2 . Root ( ) )
def , err := out . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
}
func testDuplicateCacheMount ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
c , err := New ( context . TODO ( ) , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
out := busybox . Run ( llb . Shlex ( ` sh -e -c "[[ ! -f /m2/foo ]]; touch /m1/foo; [[ -f /m2/foo ]];" ` ) )
out . AddMount ( "/m1" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountLocked ) )
out . AddMount ( "/m2" , llb . Scratch ( ) , llb . AsPersistentCacheDir ( "mycache1" , llb . CacheMountLocked ) )
def , err := out . Marshal ( )
require . NoError ( t , err )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
require . NoError ( t , err )
}
2018-02-13 19:18:18 +00:00
// containerd/containerd#2119
func testDuplicateWhiteouts ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-02-13 19:18:18 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := llb . Scratch ( )
run := func ( cmd string ) {
st = busybox . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) ) . AddMount ( "/wd" , st )
}
2018-06-18 20:57:36 +00:00
run ( ` sh -e -c "mkdir -p d0 d1; echo -n first > d1/bar;" ` )
2018-02-13 19:18:18 +00:00
run ( ` sh -c "rm -rf d0 d1" ` )
def , err := st . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
out := filepath . Join ( destDir , "out.tar" )
2018-03-27 04:16:32 +00:00
outW , err := os . Create ( out )
require . NoError ( t , err )
2018-02-13 19:18:18 +00:00
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-27 04:16:32 +00:00
Exporter : ExporterOCI ,
ExporterOutput : outW ,
2018-02-13 19:18:18 +00:00
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( out )
require . NoError ( t , err )
2018-07-17 22:17:51 +00:00
m , err := testutil . ReadTarToMap ( dt , false )
2018-02-13 19:18:18 +00:00
require . NoError ( t , err )
var index ocispec . Index
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "index.json" ] . Data , & index )
2018-02-13 19:18:18 +00:00
require . NoError ( t , err )
var mfst ocispec . Manifest
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "blobs/sha256/" + index . Manifests [ 0 ] . Digest . Hex ( ) ] . Data , & mfst )
2018-02-13 19:18:18 +00:00
require . NoError ( t , err )
lastLayer := mfst . Layers [ len ( mfst . Layers ) - 1 ]
layer , ok := m [ "blobs/sha256/" + lastLayer . Digest . Hex ( ) ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
m , err = testutil . ReadTarToMap ( layer . Data , true )
2018-02-13 19:18:18 +00:00
require . NoError ( t , err )
_ , ok = m [ ".wh.d0" ]
require . True ( t , ok )
_ , ok = m [ ".wh.d1" ]
require . True ( t , ok )
// check for a bug that added whiteout for subfile
_ , ok = m [ "d1/.wh.bar" ]
require . True ( t , ! ok )
}
2018-02-09 19:03:12 +00:00
// #276
func testWhiteoutParentDir ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-02-09 19:03:12 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "busybox:latest" )
st := llb . Scratch ( )
run := func ( cmd string ) {
st = busybox . Run ( llb . Shlex ( cmd ) , llb . Dir ( "/wd" ) ) . AddMount ( "/wd" , st )
}
run ( ` sh -c "mkdir -p foo; echo -n first > foo/bar;" ` )
run ( ` rm foo/bar ` )
def , err := st . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
out := filepath . Join ( destDir , "out.tar" )
2018-03-27 04:16:32 +00:00
outW , err := os . Create ( out )
require . NoError ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-27 04:16:32 +00:00
Exporter : ExporterOCI ,
ExporterOutput : outW ,
2018-02-09 19:03:12 +00:00
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( out )
require . NoError ( t , err )
2018-07-17 22:17:51 +00:00
m , err := testutil . ReadTarToMap ( dt , false )
2018-02-09 19:03:12 +00:00
require . NoError ( t , err )
var index ocispec . Index
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "index.json" ] . Data , & index )
2018-02-09 19:03:12 +00:00
require . NoError ( t , err )
var mfst ocispec . Manifest
2018-07-17 22:17:51 +00:00
err = json . Unmarshal ( m [ "blobs/sha256/" + index . Manifests [ 0 ] . Digest . Hex ( ) ] . Data , & mfst )
2018-02-09 19:03:12 +00:00
require . NoError ( t , err )
lastLayer := mfst . Layers [ len ( mfst . Layers ) - 1 ]
layer , ok := m [ "blobs/sha256/" + lastLayer . Digest . Hex ( ) ]
require . True ( t , ok )
2018-07-17 22:17:51 +00:00
m , err = testutil . ReadTarToMap ( layer . Data , true )
2018-02-09 19:03:12 +00:00
require . NoError ( t , err )
_ , ok = m [ "foo/.wh.bar" ]
require . True ( t , ok )
_ , ok = m [ "foo/" ]
require . True ( t , ok )
}
2018-03-02 16:33:08 +00:00
// #296
func testSchema1Image ( t * testing . T , sb integration . Sandbox ) {
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-03-02 16:33:08 +00:00
require . NoError ( t , err )
defer c . Close ( )
st := llb . Image ( "gcr.io/google_containers/pause:3.0@sha256:0d093c962a6c2dd8bb8727b661e2b5f13e9df884af9945b4cc7088d9350cd3ee" )
def , err := st . Marshal ( )
require . NoError ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
2018-03-02 16:33:08 +00:00
require . NoError ( t , err )
checkAllReleasable ( t , c , sb , true )
}
2018-03-21 17:04:43 +00:00
// #319
func testMountWithNoSource ( t * testing . T , sb integration . Sandbox ) {
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-03-21 17:04:43 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "docker.io/library/busybox:latest" )
st := llb . Scratch ( )
var nilState llb . State
// This should never actually be run, but we want to succeed
// if it was, because we expect an error below, or a daemon
// panic if the issue has regressed.
run := busybox . Run (
llb . Args ( [ ] string { "/bin/true" } ) ,
llb . AddMount ( "/nil" , nilState , llb . SourcePath ( "/" ) , llb . Readonly ) )
st = run . AddMount ( "/mnt" , st )
def , err := st . Marshal ( )
require . NoError ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
2018-06-07 20:54:04 +00:00
require . NoError ( t , err )
2018-03-21 17:04:43 +00:00
checkAllReleasable ( t , c , sb , true )
}
2018-03-26 13:51:08 +00:00
// #324
func testReadonlyRootFS ( t * testing . T , sb integration . Sandbox ) {
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-03-26 13:51:08 +00:00
require . NoError ( t , err )
defer c . Close ( )
busybox := llb . Image ( "docker.io/library/busybox:latest" )
st := llb . Scratch ( )
// The path /foo should be unwriteable.
run := busybox . Run (
llb . ReadonlyRootFS ( ) ,
llb . Args ( [ ] string { "/bin/touch" , "/foo" } ) )
st = run . AddMount ( "/mnt" , st )
def , err := st . Marshal ( )
require . NoError ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt { } , nil )
2018-03-26 13:51:08 +00:00
require . Error ( t , err )
// Would prefer to detect more specifically "Read-only file
// system" but that isn't exposed here (it is on the stdio
// which we don't see).
require . Contains ( t , err . Error ( ) , "executor failed running [/bin/touch /foo]:" )
checkAllReleasable ( t , c , sb , true )
}
2018-05-20 21:48:35 +00:00
func testProxyEnv ( t * testing . T , sb integration . Sandbox ) {
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-05-20 21:48:35 +00:00
require . NoError ( t , err )
defer c . Close ( )
base := llb . Image ( "docker.io/library/busybox:latest" ) . Dir ( "/out" )
cmd := ` sh -c "echo -n $HTTP_PROXY-$HTTPS_PROXY-$NO_PROXY-$no_proxy > env" `
st := base . Run ( llb . Shlex ( cmd ) , llb . WithProxy ( llb . ProxyEnv {
HttpProxy : "httpvalue" ,
HttpsProxy : "httpsvalue" ,
NoProxy : "noproxyvalue" ,
} ) )
out := st . AddMount ( "/out" , llb . Scratch ( ) )
def , err := out . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
} , nil )
require . NoError ( t , err )
dt , err := ioutil . ReadFile ( filepath . Join ( destDir , "env" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt ) , "httpvalue-httpsvalue-noproxyvalue-noproxyvalue" )
// repeat to make sure proxy doesn't change cache
st = base . Run ( llb . Shlex ( cmd ) , llb . WithProxy ( llb . ProxyEnv {
HttpsProxy : "httpsvalue2" ,
NoProxy : "noproxyvalue2" ,
} ) )
out = st . AddMount ( "/out" , llb . Scratch ( ) )
def , err = out . Marshal ( )
require . NoError ( t , err )
destDir , err = ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
} , nil )
require . NoError ( t , err )
dt , err = ioutil . ReadFile ( filepath . Join ( destDir , "env" ) )
require . NoError ( t , err )
require . Equal ( t , string ( dt ) , "httpvalue-httpsvalue-noproxyvalue-noproxyvalue" )
}
2017-11-27 22:24:29 +00:00
func requiresLinux ( t * testing . T ) {
if runtime . GOOS != "linux" {
t . Skipf ( "unsupported GOOS: %s" , runtime . GOOS )
}
}
2017-12-05 23:32:52 +00:00
2018-04-26 00:43:17 +00:00
func checkAllRemoved ( t * testing . T , c * Client , sb integration . Sandbox ) {
retries := 0
for {
require . True ( t , 20 > retries )
retries ++
du , err := c . DiskUsage ( context . TODO ( ) )
require . NoError ( t , err )
if len ( du ) > 0 {
time . Sleep ( 500 * time . Millisecond )
2018-04-27 19:02:07 +00:00
continue
2018-04-26 00:43:17 +00:00
}
break
}
}
2017-12-28 23:03:49 +00:00
func checkAllReleasable ( t * testing . T , c * Client , sb integration . Sandbox , checkContent bool ) {
2018-01-07 21:34:10 +00:00
retries := 0
loop0 :
for {
require . True ( t , 20 > retries )
retries ++
du , err := c . DiskUsage ( context . TODO ( ) )
require . NoError ( t , err )
for _ , d := range du {
if d . InUse {
time . Sleep ( 500 * time . Millisecond )
continue loop0
}
}
break
}
2018-07-26 22:31:35 +00:00
err := c . Prune ( context . TODO ( ) , nil , PruneAll )
2017-12-28 23:03:49 +00:00
require . NoError ( t , err )
du , err := c . DiskUsage ( context . TODO ( ) )
require . NoError ( t , err )
require . Equal ( t , 0 , len ( du ) )
// examine contents of exported tars (requires containerd)
var cdAddress string
if cd , ok := sb . ( interface {
ContainerdAddress ( ) string
} ) ; ! ok {
return
} else {
cdAddress = cd . ContainerdAddress ( )
}
// TODO: make public pull helper function so this can be checked for standalone as well
2018-08-23 07:59:41 +00:00
client , err := newContainerd ( cdAddress )
2017-12-28 23:03:49 +00:00
require . NoError ( t , err )
defer client . Close ( )
ctx := namespaces . WithNamespace ( context . Background ( ) , "buildkit" )
snapshotService := client . SnapshotService ( "overlayfs" )
2018-01-07 21:34:10 +00:00
retries = 0
2017-12-28 23:03:49 +00:00
for {
count := 0
err = snapshotService . Walk ( ctx , func ( context . Context , snapshots . Info ) error {
count ++
return nil
} )
require . NoError ( t , err )
if count == 0 {
break
}
require . True ( t , 20 > retries )
retries ++
time . Sleep ( 500 * time . Millisecond )
}
if ! checkContent {
return
}
retries = 0
for {
count := 0
err = client . ContentStore ( ) . Walk ( ctx , func ( content . Info ) error {
count ++
return nil
} )
require . NoError ( t , err )
if count == 0 {
break
}
require . True ( t , 20 > retries )
retries ++
time . Sleep ( 500 * time . Millisecond )
}
}
2018-03-29 05:27:52 +00:00
func testInvalidExporter ( t * testing . T , sb integration . Sandbox ) {
requiresLinux ( t )
2018-06-06 10:52:53 +00:00
c , err := New ( context . TODO ( ) , sb . Address ( ) )
2018-03-29 05:27:52 +00:00
require . NoError ( t , err )
defer c . Close ( )
def , err := llb . Image ( "busybox:latest" ) . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
target := "example.com/buildkit/testoci:latest"
attrs := map [ string ] string {
"name" : target ,
}
for _ , exp := range [ ] string { ExporterOCI , ExporterDocker } {
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-29 05:27:52 +00:00
Exporter : exp ,
ExporterAttrs : attrs ,
} , nil )
// output file writer is required
require . Error ( t , err )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-29 05:27:52 +00:00
Exporter : exp ,
ExporterAttrs : attrs ,
ExporterOutputDir : destDir ,
} , nil )
// output directory is not supported
require . Error ( t , err )
}
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-29 05:27:52 +00:00
Exporter : ExporterLocal ,
ExporterAttrs : attrs ,
} , nil )
// output directory is required
require . Error ( t , err )
f , err := os . Create ( filepath . Join ( destDir , "a" ) )
require . NoError ( t , err )
defer f . Close ( )
2018-05-03 00:35:07 +00:00
_ , err = c . Solve ( context . TODO ( ) , def , SolveOpt {
2018-03-29 05:27:52 +00:00
Exporter : ExporterLocal ,
ExporterAttrs : attrs ,
ExporterOutput : f ,
} , nil )
// output file writer is not supported
require . Error ( t , err )
checkAllReleasable ( t , c , sb , true )
}
2018-06-04 21:08:29 +00:00
2018-07-09 22:42:34 +00:00
// moby/buildkit#492
func testParallelLocalBuilds ( t * testing . T , sb integration . Sandbox ) {
2018-07-06 12:03:53 +00:00
ctx , cancel := context . WithCancel ( context . TODO ( ) )
defer cancel ( )
c , err := New ( ctx , sb . Address ( ) )
require . NoError ( t , err )
defer c . Close ( )
eg , ctx := errgroup . WithContext ( ctx )
for i := 0 ; i < 3 ; i ++ {
2018-07-09 22:42:34 +00:00
func ( i int ) {
eg . Go ( func ( ) error {
fn := fmt . Sprintf ( "test%d" , i )
srcDir , err := tmpdir (
fstest . CreateFile ( fn , [ ] byte ( "contents" ) , 0600 ) ,
)
require . NoError ( t , err )
defer os . RemoveAll ( srcDir )
def , err := llb . Local ( "source" ) . Marshal ( )
require . NoError ( t , err )
destDir , err := ioutil . TempDir ( "" , "buildkit" )
require . NoError ( t , err )
defer os . RemoveAll ( destDir )
_ , err = c . Solve ( ctx , def , SolveOpt {
Exporter : ExporterLocal ,
ExporterOutputDir : destDir ,
LocalDirs : map [ string ] string {
"source" : srcDir ,
} ,
} , nil )
require . NoError ( t , err )
act , err := ioutil . ReadFile ( filepath . Join ( destDir , fn ) )
require . NoError ( t , err )
require . Equal ( t , "contents" , string ( act ) )
return nil
} )
} ( i )
2018-07-06 12:03:53 +00:00
}
err = eg . Wait ( )
require . NoError ( t , err )
}
2018-07-09 22:42:34 +00:00
func tmpdir ( appliers ... fstest . Applier ) ( string , error ) {
tmpdir , err := ioutil . TempDir ( "" , "buildkit-client" )
if err != nil {
return "" , err
}
if err := fstest . Apply ( appliers ... ) . Apply ( tmpdir ) ; err != nil {
return "" , err
}
return tmpdir , nil
}
2018-09-06 21:39:56 +00:00
func makeSSHAgentSock ( agent agent . Agent ) ( p string , cleanup func ( ) error , err error ) {
tmpDir , err := ioutil . TempDir ( "" , "buildkit" )
if err != nil {
return "" , nil , err
}
defer func ( ) {
if err != nil {
os . RemoveAll ( tmpDir )
}
} ( )
sockPath := filepath . Join ( tmpDir , "ssh_auth_sock" )
l , err := net . Listen ( "unix" , sockPath )
if err != nil {
return "" , nil , err
}
s := & server { l : l }
go s . run ( agent )
return sockPath , func ( ) error {
l . Close ( )
return os . RemoveAll ( tmpDir )
} , nil
}
type server struct {
l net . Listener
}
func ( s * server ) run ( a agent . Agent ) error {
for {
c , err := s . l . Accept ( )
if err != nil {
return err
}
go agent . ServeAgent ( a , c )
}
}