Skip to content

Commit 1cff633

Browse files
committed
git: add resolvercache support to git source
Make sure remote ref does not change to different commit if git repo changes in the middle of the build. Signed-off-by: Tonis Tiigi <[email protected]>
1 parent df4d2ae commit 1cff633

File tree

3 files changed

+159
-4
lines changed

3 files changed

+159
-4
lines changed

client/client_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
248248
testHTTPPruneAfterResolveMeta,
249249
testHTTPResolveMetaReuse,
250250
testHTTPResolveMultiBuild,
251+
testGitResolveMutatedSource,
251252
}
252253

253254
func TestIntegration(t *testing.T) {
@@ -12413,6 +12414,124 @@ func testHTTPResolveMultiBuild(t *testing.T, sb integration.Sandbox) {
1241312414
require.Equal(t, "content2", string(dt))
1241412415
}
1241512416

12417+
func testGitResolveMutatedSource(t *testing.T, sb integration.Sandbox) {
12418+
integration.SkipOnPlatform(t, "windows")
12419+
ctx := sb.Context()
12420+
c, err := New(ctx, sb.Address())
12421+
require.NoError(t, err)
12422+
defer c.Close()
12423+
12424+
gitDir := t.TempDir()
12425+
gitCommands := []string{
12426+
"git init",
12427+
"git config --local user.email test",
12428+
"git config --local user.name test",
12429+
"echo a > a",
12430+
"git add a",
12431+
"git commit -m a",
12432+
"git tag -a v0.1 -m v0.1",
12433+
"echo b > b",
12434+
"git add b",
12435+
"git commit -m b",
12436+
"git checkout -B v2",
12437+
"git update-server-info",
12438+
}
12439+
err = runInDir(gitDir, gitCommands...)
12440+
require.NoError(t, err)
12441+
12442+
// cmd := exec.Command("git", "rev-parse", "HEAD")
12443+
// cmd.Dir = gitDir
12444+
// out, err := cmd.Output()
12445+
// require.NoError(t, err)
12446+
// commitHEAD := strings.TrimSpace(string(out))
12447+
12448+
cmd := exec.Command("git", "rev-parse", "v0.1")
12449+
cmd.Dir = gitDir
12450+
out, err := cmd.Output()
12451+
require.NoError(t, err)
12452+
commitTag := strings.TrimSpace(string(out))
12453+
12454+
cmd = exec.Command("git", "rev-parse", "v0.1^{commit}")
12455+
cmd.Dir = gitDir
12456+
out, err = cmd.Output()
12457+
require.NoError(t, err)
12458+
commitTagCommit := strings.TrimSpace(string(out))
12459+
12460+
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Clean(gitDir))))
12461+
defer server.Close()
12462+
12463+
dest := t.TempDir()
12464+
12465+
_, err = c.Build(ctx, SolveOpt{
12466+
Exports: []ExportEntry{
12467+
{
12468+
Type: ExporterLocal,
12469+
OutputDir: dest,
12470+
},
12471+
},
12472+
}, "test", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
12473+
id := "git://" + strings.TrimPrefix(server.URL, "http://") + "/.git#v0.1"
12474+
md, err := c.ResolveSourceMetadata(ctx, &pb.SourceOp{
12475+
Identifier: id,
12476+
Attrs: map[string]string{
12477+
"git.fullurl": server.URL + "/.git",
12478+
},
12479+
}, sourceresolver.Opt{})
12480+
if err != nil {
12481+
return nil, err
12482+
}
12483+
require.NotNil(t, md.Git)
12484+
require.Equal(t, "refs/tags/v0.1", md.Git.Ref)
12485+
require.Equal(t, commitTag, md.Git.Checksum)
12486+
require.Equal(t, commitTagCommit, md.Git.CommitChecksum)
12487+
require.Equal(t, id, md.Op.Identifier)
12488+
require.Equal(t, server.URL+"/.git", md.Op.Attrs["git.fullurl"])
12489+
12490+
// update the tag to point to a different commit
12491+
err = runInDir(gitDir, []string{
12492+
"git tag -f v0.1",
12493+
"git update-server-info",
12494+
}...)
12495+
require.NoError(t, err)
12496+
12497+
md, err = c.ResolveSourceMetadata(ctx, &pb.SourceOp{
12498+
Identifier: id,
12499+
Attrs: map[string]string{
12500+
"git.fullurl": server.URL + "/.git",
12501+
},
12502+
}, sourceresolver.Opt{})
12503+
if err != nil {
12504+
return nil, err
12505+
}
12506+
require.NotNil(t, md.Git)
12507+
require.Equal(t, "refs/tags/v0.1", md.Git.Ref)
12508+
require.Equal(t, commitTag, md.Git.Checksum)
12509+
require.Equal(t, commitTagCommit, md.Git.CommitChecksum)
12510+
require.Equal(t, id, md.Op.Identifier)
12511+
require.Equal(t, server.URL+"/.git", md.Op.Attrs["git.fullurl"])
12512+
12513+
st := llb.Git(server.URL+"/.git", "", llb.GitRef("v0.1"))
12514+
def, err := st.Marshal(sb.Context())
12515+
if err != nil {
12516+
return nil, err
12517+
}
12518+
return c.Solve(ctx, gateway.SolveRequest{
12519+
Definition: def.ToPB(),
12520+
})
12521+
}, nil)
12522+
require.NoError(t, err)
12523+
12524+
_, err = os.ReadFile(filepath.Join(dest, "b"))
12525+
require.Error(t, err)
12526+
require.True(t, os.IsNotExist(err), "expected file b to not exist")
12527+
12528+
dt, err := os.ReadFile(filepath.Join(dest, "a"))
12529+
require.NoError(t, err)
12530+
require.Equal(t, "a\n", string(dt))
12531+
12532+
checkAllReleasable(t, c, sb, false)
12533+
}
12534+
1241612535
func runInDir(dir string, cmds ...string) error {
1241712536
for _, args := range cmds {
1241812537
var cmd *exec.Cmd

solver/types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ type JobContext interface {
189189
type ResolverCache interface {
190190
// Lock locks a key until the returned release function is called.
191191
// Release function can return value that will be returned to next callers.
192-
// Lock can return multiple values because two steps can be merged together after
193-
// they both already completed resolve independently.
192+
// Lock can return multiple values because two steps can be merged once
193+
// both have independently completed their resolution.
194194
Lock(key any) (values []any, release func(any) error, err error)
195195
}
196196

source/git/source.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,11 @@ func (gs *gitSourceHandler) mountKnownHosts() (string, func() error, error) {
384384
return knownHosts.Name(), cleanup, nil
385385
}
386386

387-
func (gs *gitSourceHandler) resolveMetadata(ctx context.Context, jobCtx solver.JobContext) (*Metadata, error) {
387+
func (gs *gitSourceHandler) remoteKey() string {
388+
return gs.src.Remote + "#" + gs.src.Ref
389+
}
390+
391+
func (gs *gitSourceHandler) resolveMetadata(ctx context.Context, jobCtx solver.JobContext) (md *Metadata, retErr error) {
388392
remote := gs.src.Remote
389393
gs.locker.Lock(remote)
390394
defer gs.locker.Unlock(remote)
@@ -397,6 +401,9 @@ func (gs *gitSourceHandler) resolveMetadata(ctx context.Context, jobCtx solver.J
397401
}
398402

399403
if gitutil.IsCommitSHA(gs.src.Ref) {
404+
if gs.src.Checksum != "" && !strings.HasPrefix(gs.src.Ref, gs.src.Checksum) {
405+
return nil, errors.Errorf("expected checksum to match %s, got %s", gs.src.Checksum, gs.src.Ref)
406+
}
400407
return &Metadata{
401408
Ref: gs.src.Ref,
402409
Checksum: gs.src.Ref,
@@ -406,6 +413,35 @@ func (gs *gitSourceHandler) resolveMetadata(ctx context.Context, jobCtx solver.J
406413
var g session.Group
407414
if jobCtx != nil {
408415
g = jobCtx.Session()
416+
417+
if rc := jobCtx.ResolverCache(); rc != nil {
418+
values, release, err := rc.Lock(gs.remoteKey())
419+
if err != nil {
420+
return nil, err
421+
}
422+
saveResolved := true
423+
defer func() {
424+
v := md
425+
if retErr != nil || !saveResolved {
426+
v = nil
427+
}
428+
if err := release(v); err != nil {
429+
bklog.G(ctx).Warnf("failed to release resolver cache lock for %s: %v", gs.remoteKey(), err)
430+
}
431+
}()
432+
for _, v := range values {
433+
v2, ok := v.(*Metadata)
434+
if !ok {
435+
return nil, errors.Errorf("invalid resolver cache value for %s: %T", gs.remoteKey(), v)
436+
}
437+
if gs.src.Checksum != "" && !strings.HasPrefix(v2.Checksum, gs.src.Checksum) {
438+
continue
439+
}
440+
saveResolved = false
441+
clone := *v2
442+
return &clone, nil
443+
}
444+
}
409445
}
410446

411447
gs.getAuthToken(ctx, g)
@@ -483,7 +519,7 @@ func (gs *gitSourceHandler) resolveMetadata(ctx context.Context, jobCtx solver.J
483519
return nil, errors.Errorf("expected checksum to match %s, got %s", gs.src.Checksum, exp)
484520
}
485521
}
486-
md := &Metadata{
522+
md = &Metadata{
487523
Ref: usedRef,
488524
Checksum: sha,
489525
}

0 commit comments

Comments
 (0)