2017-05-27 06:12:13 +00:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
2018-01-16 22:30:10 +00:00
|
|
|
"context"
|
2019-09-18 00:18:32 +00:00
|
|
|
"fmt"
|
2019-07-29 18:00:10 +00:00
|
|
|
"strings"
|
2017-05-27 06:12:13 +00:00
|
|
|
"sync"
|
2019-07-22 21:43:16 +00:00
|
|
|
"time"
|
2017-05-27 06:12:13 +00:00
|
|
|
|
2021-03-30 12:59:03 +00:00
|
|
|
"github.com/containerd/containerd/content"
|
2019-09-18 00:18:32 +00:00
|
|
|
"github.com/containerd/containerd/errdefs"
|
2019-07-22 21:43:16 +00:00
|
|
|
"github.com/containerd/containerd/leases"
|
2017-05-27 06:12:13 +00:00
|
|
|
"github.com/containerd/containerd/mount"
|
2019-09-18 00:18:32 +00:00
|
|
|
"github.com/containerd/containerd/snapshots"
|
2019-03-20 21:54:20 +00:00
|
|
|
"github.com/docker/docker/pkg/idtools"
|
2021-10-28 19:38:08 +00:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2017-06-30 22:54:51 +00:00
|
|
|
"github.com/moby/buildkit/identity"
|
2020-10-27 06:13:39 +00:00
|
|
|
"github.com/moby/buildkit/session"
|
2018-04-16 22:23:10 +00:00
|
|
|
"github.com/moby/buildkit/snapshot"
|
2020-05-28 20:46:33 +00:00
|
|
|
"github.com/moby/buildkit/solver"
|
|
|
|
"github.com/moby/buildkit/util/compression"
|
2017-06-22 20:15:46 +00:00
|
|
|
"github.com/moby/buildkit/util/flightcontrol"
|
2019-10-03 21:11:54 +00:00
|
|
|
"github.com/moby/buildkit/util/leaseutil"
|
2020-05-28 20:46:33 +00:00
|
|
|
"github.com/moby/buildkit/util/winlayers"
|
2020-05-13 15:37:27 +00:00
|
|
|
digest "github.com/opencontainers/go-digest"
|
2021-07-26 08:53:30 +00:00
|
|
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
2017-05-27 06:12:13 +00:00
|
|
|
"github.com/pkg/errors"
|
2017-09-28 05:38:54 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2020-05-28 20:46:33 +00:00
|
|
|
"golang.org/x/sync/errgroup"
|
2017-05-27 06:12:13 +00:00
|
|
|
)
|
|
|
|
|
2017-11-06 06:30:16 +00:00
|
|
|
// Ref is a reference to cacheable objects.
|
|
|
|
type Ref interface {
|
2017-05-27 06:12:13 +00:00
|
|
|
Mountable
|
2021-07-09 00:09:35 +00:00
|
|
|
RefMetadata
|
2017-06-13 23:01:47 +00:00
|
|
|
Release(context.Context) error
|
2019-03-20 21:54:20 +00:00
|
|
|
IdentityMapping() *idtools.IdentityMapping
|
2021-10-08 21:23:53 +00:00
|
|
|
DescHandler(digest.Digest) *DescHandler
|
2017-11-06 06:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ImmutableRef interface {
|
|
|
|
Ref
|
2018-05-10 04:52:05 +00:00
|
|
|
Clone() ImmutableRef
|
2021-10-25 17:51:30 +00:00
|
|
|
// Finalize commits the snapshot to the driver if it's not already.
|
|
|
|
// This means the snapshot can no longer be mounted as mutable.
|
|
|
|
Finalize(context.Context) error
|
2019-07-22 21:43:16 +00:00
|
|
|
|
2020-10-27 06:13:39 +00:00
|
|
|
Extract(ctx context.Context, s session.Group) error // +progress
|
2021-10-05 06:43:22 +00:00
|
|
|
GetRemotes(ctx context.Context, createIfNeeded bool, compressionopt solver.CompressionOpt, all bool, s session.Group) ([]*solver.Remote, error)
|
2021-10-28 19:38:08 +00:00
|
|
|
LayerChain() RefList
|
2019-07-22 21:43:16 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 06:12:13 +00:00
|
|
|
type MutableRef interface {
|
2017-11-06 06:30:16 +00:00
|
|
|
Ref
|
2017-07-14 18:59:31 +00:00
|
|
|
Commit(context.Context) (ImmutableRef, error)
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Mountable interface {
|
2020-10-27 06:13:39 +00:00
|
|
|
Mount(ctx context.Context, readonly bool, s session.Group) (snapshot.Mountable, error)
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 20:35:41 +00:00
|
|
|
type ref interface {
|
2021-07-09 00:09:35 +00:00
|
|
|
shouldUpdateLastUsed() bool
|
2018-09-14 20:35:41 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 06:12:13 +00:00
|
|
|
type cacheRecord struct {
|
2017-12-26 19:42:14 +00:00
|
|
|
cm *cacheManager
|
|
|
|
mu *sync.Mutex // the mutex is shared by records sharing data
|
|
|
|
|
|
|
|
mutable bool
|
2018-09-14 20:35:41 +00:00
|
|
|
refs map[ref]struct{}
|
2021-08-03 01:57:39 +00:00
|
|
|
parentRefs
|
2021-07-09 00:09:35 +00:00
|
|
|
*cacheMetadata
|
2017-12-26 19:42:14 +00:00
|
|
|
|
|
|
|
// dead means record is marked as deleted
|
|
|
|
dead bool
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
mountCache snapshot.Mountable
|
2017-05-31 23:45:04 +00:00
|
|
|
|
|
|
|
sizeG flightcontrol.Group
|
2017-07-14 18:59:31 +00:00
|
|
|
|
|
|
|
// these are filled if multiple refs point to same data
|
|
|
|
equalMutable *mutableRef
|
|
|
|
equalImmutable *immutableRef
|
2019-09-20 21:49:29 +00:00
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
layerDigestChainCache []digest.Digest
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
2017-12-26 19:42:14 +00:00
|
|
|
// hold ref lock before calling
|
2020-05-28 20:46:33 +00:00
|
|
|
func (cr *cacheRecord) ref(triggerLastUsed bool, descHandlers DescHandlers) *immutableRef {
|
|
|
|
ref := &immutableRef{
|
|
|
|
cacheRecord: cr,
|
|
|
|
triggerLastUsed: triggerLastUsed,
|
|
|
|
descHandlers: descHandlers,
|
|
|
|
}
|
2017-06-01 22:24:23 +00:00
|
|
|
cr.refs[ref] = struct{}{}
|
|
|
|
return ref
|
|
|
|
}
|
|
|
|
|
2017-12-26 19:42:14 +00:00
|
|
|
// hold ref lock before calling
|
2020-05-28 20:46:33 +00:00
|
|
|
func (cr *cacheRecord) mref(triggerLastUsed bool, descHandlers DescHandlers) *mutableRef {
|
|
|
|
ref := &mutableRef{
|
|
|
|
cacheRecord: cr,
|
|
|
|
triggerLastUsed: triggerLastUsed,
|
|
|
|
descHandlers: descHandlers,
|
|
|
|
}
|
2017-05-27 06:12:13 +00:00
|
|
|
cr.refs[ref] = struct{}{}
|
|
|
|
return ref
|
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
// parentRefs is a disjoint union type that holds either a single layerParent for this record, a list
|
|
|
|
// of parents if this is a merged record or all nil fields if this record has no parents. At most one
|
|
|
|
// field should be non-nil at a time.
|
|
|
|
type parentRefs struct {
|
|
|
|
layerParent *immutableRef
|
|
|
|
mergeParents []*immutableRef
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
diffParents *diffParents
|
|
|
|
}
|
|
|
|
|
|
|
|
type diffParents struct {
|
|
|
|
lower *immutableRef
|
|
|
|
upper *immutableRef
|
2021-08-03 01:57:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// caller must hold cacheManager.mu
|
|
|
|
func (p parentRefs) release(ctx context.Context) (rerr error) {
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
switch {
|
|
|
|
case p.layerParent != nil:
|
2021-08-03 01:57:39 +00:00
|
|
|
p.layerParent.mu.Lock()
|
|
|
|
defer p.layerParent.mu.Unlock()
|
|
|
|
rerr = p.layerParent.release(ctx)
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case len(p.mergeParents) > 0:
|
2021-08-03 01:57:39 +00:00
|
|
|
for i, parent := range p.mergeParents {
|
|
|
|
if parent == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
parent.mu.Lock()
|
|
|
|
if err := parent.release(ctx); err != nil {
|
|
|
|
rerr = multierror.Append(rerr, err).ErrorOrNil()
|
|
|
|
} else {
|
|
|
|
p.mergeParents[i] = nil
|
|
|
|
}
|
|
|
|
parent.mu.Unlock()
|
|
|
|
}
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case p.diffParents != nil:
|
|
|
|
if p.diffParents.lower != nil {
|
|
|
|
p.diffParents.lower.mu.Lock()
|
|
|
|
defer p.diffParents.lower.mu.Unlock()
|
|
|
|
if err := p.diffParents.lower.release(ctx); err != nil {
|
|
|
|
rerr = multierror.Append(rerr, err).ErrorOrNil()
|
|
|
|
} else {
|
|
|
|
p.diffParents.lower = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if p.diffParents.upper != nil {
|
|
|
|
p.diffParents.upper.mu.Lock()
|
|
|
|
defer p.diffParents.upper.mu.Unlock()
|
|
|
|
if err := p.diffParents.upper.release(ctx); err != nil {
|
|
|
|
rerr = multierror.Append(rerr, err).ErrorOrNil()
|
|
|
|
} else {
|
|
|
|
p.diffParents.upper = nil
|
|
|
|
}
|
|
|
|
}
|
2019-09-20 21:49:29 +00:00
|
|
|
}
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
return rerr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p parentRefs) clone() parentRefs {
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
switch {
|
|
|
|
case p.layerParent != nil:
|
2021-08-03 01:57:39 +00:00
|
|
|
p.layerParent = p.layerParent.clone()
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case len(p.mergeParents) > 0:
|
2021-08-03 01:57:39 +00:00
|
|
|
newParents := make([]*immutableRef, len(p.mergeParents))
|
|
|
|
for i, p := range p.mergeParents {
|
|
|
|
newParents[i] = p.clone()
|
|
|
|
}
|
|
|
|
p.mergeParents = newParents
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case p.diffParents != nil:
|
|
|
|
newDiffParents := &diffParents{}
|
|
|
|
if p.diffParents.lower != nil {
|
|
|
|
newDiffParents.lower = p.diffParents.lower.clone()
|
|
|
|
}
|
|
|
|
if p.diffParents.upper != nil {
|
|
|
|
newDiffParents.upper = p.diffParents.upper.clone()
|
|
|
|
}
|
|
|
|
p.diffParents = newDiffParents
|
2019-09-20 21:49:29 +00:00
|
|
|
}
|
2021-08-03 01:57:39 +00:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
type refKind int
|
|
|
|
|
|
|
|
const (
|
|
|
|
BaseLayer refKind = iota
|
|
|
|
Layer
|
|
|
|
Merge
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
Diff
|
2021-08-03 01:57:39 +00:00
|
|
|
)
|
2019-09-20 21:49:29 +00:00
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
func (cr *cacheRecord) kind() refKind {
|
|
|
|
if len(cr.mergeParents) > 0 {
|
|
|
|
return Merge
|
2019-09-20 21:49:29 +00:00
|
|
|
}
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
if cr.diffParents != nil {
|
|
|
|
return Diff
|
|
|
|
}
|
2021-08-03 01:57:39 +00:00
|
|
|
if cr.layerParent != nil {
|
|
|
|
return Layer
|
|
|
|
}
|
|
|
|
return BaseLayer
|
2019-09-20 21:49:29 +00:00
|
|
|
}
|
|
|
|
|
2017-12-26 19:42:14 +00:00
|
|
|
// hold ref lock before calling
|
|
|
|
func (cr *cacheRecord) isDead() bool {
|
|
|
|
return cr.dead || (cr.equalImmutable != nil && cr.equalImmutable.dead) || (cr.equalMutable != nil && cr.equalMutable.dead)
|
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
var errSkipWalk = errors.New("skip")
|
|
|
|
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
// walkAncestors calls the provided func on cr and each of its ancestors, counting layer,
|
|
|
|
// diff, and merge parents. It starts at cr and does a depth-first walk to parents. It will visit
|
2021-08-03 01:57:39 +00:00
|
|
|
// a record and its parents multiple times if encountered more than once. It will only skip
|
|
|
|
// visiting parents of a record if errSkipWalk is returned. If any other error is returned,
|
|
|
|
// the walk will stop and return the error to the caller.
|
|
|
|
func (cr *cacheRecord) walkAncestors(f func(*cacheRecord) error) error {
|
|
|
|
curs := []*cacheRecord{cr}
|
|
|
|
for len(curs) > 0 {
|
|
|
|
cur := curs[len(curs)-1]
|
|
|
|
curs = curs[:len(curs)-1]
|
|
|
|
if err := f(cur); err != nil {
|
|
|
|
if errors.Is(err, errSkipWalk) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
switch cur.kind() {
|
|
|
|
case Layer:
|
|
|
|
curs = append(curs, cur.layerParent.cacheRecord)
|
|
|
|
case Merge:
|
|
|
|
for _, p := range cur.mergeParents {
|
|
|
|
curs = append(curs, p.cacheRecord)
|
|
|
|
}
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case Diff:
|
|
|
|
if cur.diffParents.lower != nil {
|
|
|
|
curs = append(curs, cur.diffParents.lower.cacheRecord)
|
|
|
|
}
|
|
|
|
if cur.diffParents.upper != nil {
|
|
|
|
curs = append(curs, cur.diffParents.upper.cacheRecord)
|
|
|
|
}
|
2021-08-03 01:57:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// walkUniqueAncestors calls walkAncestors but skips a record if it's already been visited.
|
|
|
|
func (cr *cacheRecord) walkUniqueAncestors(f func(*cacheRecord) error) error {
|
|
|
|
memo := make(map[*cacheRecord]struct{})
|
|
|
|
return cr.walkAncestors(func(cr *cacheRecord) error {
|
|
|
|
if _, ok := memo[cr]; ok {
|
|
|
|
return errSkipWalk
|
|
|
|
}
|
|
|
|
memo[cr] = struct{}{}
|
|
|
|
return f(cr)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-05-28 20:46:33 +00:00
|
|
|
func (cr *cacheRecord) isLazy(ctx context.Context) (bool, error) {
|
2021-07-09 00:09:35 +00:00
|
|
|
if !cr.getBlobOnly() {
|
2020-05-28 20:46:33 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
dgst := cr.getBlob()
|
2020-11-12 23:36:16 +00:00
|
|
|
// special case for moby where there is no compressed blob (empty digest)
|
|
|
|
if dgst == "" {
|
|
|
|
return false, nil
|
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
_, err := cr.cm.ContentStore.Info(ctx, dgst)
|
2020-05-28 20:46:33 +00:00
|
|
|
if errors.Is(err, errdefs.ErrNotFound) {
|
|
|
|
return true, nil
|
2020-10-11 12:56:00 +00:00
|
|
|
} else if err != nil {
|
|
|
|
return false, err
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
2020-10-11 12:56:00 +00:00
|
|
|
|
|
|
|
// If the snapshot is a remote snapshot, this layer is lazy.
|
2021-07-09 00:09:35 +00:00
|
|
|
if info, err := cr.cm.Snapshotter.Stat(ctx, cr.getSnapshotID()); err == nil {
|
2020-10-11 12:56:00 +00:00
|
|
|
if _, ok := info.Labels["containerd.io/snapshot/remote"]; ok {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
|
2019-03-20 21:54:20 +00:00
|
|
|
func (cr *cacheRecord) IdentityMapping() *idtools.IdentityMapping {
|
|
|
|
return cr.cm.IdentityMapping()
|
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
func (cr *cacheRecord) viewLeaseID() string {
|
|
|
|
return cr.ID() + "-view"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cr *cacheRecord) viewSnapshotID() string {
|
|
|
|
return cr.getSnapshotID() + "-view"
|
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
func (cr *cacheRecord) size(ctx context.Context) (int64, error) {
|
2017-05-31 23:45:04 +00:00
|
|
|
// this expects that usage() is implemented lazily
|
2017-07-03 23:08:20 +00:00
|
|
|
s, err := cr.sizeG.Do(ctx, cr.ID(), func(ctx context.Context) (interface{}, error) {
|
2017-05-31 23:45:04 +00:00
|
|
|
cr.mu.Lock()
|
2021-07-09 00:09:35 +00:00
|
|
|
s := cr.getSize()
|
2017-05-31 23:45:04 +00:00
|
|
|
if s != sizeUnknown {
|
2017-07-14 18:59:31 +00:00
|
|
|
cr.mu.Unlock()
|
2017-05-31 23:45:04 +00:00
|
|
|
return s, nil
|
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
driverID := cr.getSnapshotID()
|
2017-07-14 18:59:31 +00:00
|
|
|
if cr.equalMutable != nil {
|
2021-07-09 00:09:35 +00:00
|
|
|
driverID = cr.equalMutable.getSnapshotID()
|
2017-07-14 18:59:31 +00:00
|
|
|
}
|
|
|
|
cr.mu.Unlock()
|
2019-09-18 00:18:32 +00:00
|
|
|
var usage snapshots.Usage
|
2021-07-09 00:09:35 +00:00
|
|
|
if !cr.getBlobOnly() {
|
2019-09-18 00:18:32 +00:00
|
|
|
var err error
|
2021-08-03 01:57:39 +00:00
|
|
|
usage, err = cr.cm.Snapshotter.Usage(ctx, driverID)
|
2019-09-18 00:18:32 +00:00
|
|
|
if err != nil {
|
|
|
|
cr.mu.Lock()
|
|
|
|
isDead := cr.isDead()
|
|
|
|
cr.mu.Unlock()
|
|
|
|
if isDead {
|
|
|
|
return int64(0), nil
|
|
|
|
}
|
2020-04-19 05:17:47 +00:00
|
|
|
if !errors.Is(err, errdefs.ErrNotFound) {
|
2019-09-18 00:18:32 +00:00
|
|
|
return s, errors.Wrapf(err, "failed to get usage for %s", cr.ID())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
if dgst := cr.getBlob(); dgst != "" {
|
2019-09-18 00:18:32 +00:00
|
|
|
info, err := cr.cm.ContentStore.Info(ctx, digest.Digest(dgst))
|
|
|
|
if err == nil {
|
|
|
|
usage.Size += info.Size
|
2017-12-27 01:22:50 +00:00
|
|
|
}
|
2021-03-30 12:59:03 +00:00
|
|
|
for k, v := range info.Labels {
|
|
|
|
// accumulate size of compression variant blobs
|
|
|
|
if strings.HasPrefix(k, compressionVariantDigestLabelPrefix) {
|
|
|
|
if cdgst, err := digest.Parse(v); err == nil {
|
|
|
|
if digest.Digest(dgst) == cdgst {
|
|
|
|
// do not double count if the label points to this content itself.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if info, err := cr.cm.ContentStore.Info(ctx, cdgst); err == nil {
|
|
|
|
usage.Size += info.Size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-31 23:45:04 +00:00
|
|
|
}
|
|
|
|
cr.mu.Lock()
|
2021-07-09 00:09:35 +00:00
|
|
|
cr.queueSize(usage.Size)
|
|
|
|
if err := cr.commitMetadata(); err != nil {
|
2018-04-30 17:38:47 +00:00
|
|
|
cr.mu.Unlock()
|
2017-07-03 23:08:20 +00:00
|
|
|
return s, err
|
|
|
|
}
|
2017-05-31 23:45:04 +00:00
|
|
|
cr.mu.Unlock()
|
|
|
|
return usage.Size, nil
|
|
|
|
})
|
2020-05-27 21:58:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return s.(int64), nil
|
2017-05-31 23:45:04 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
// caller must hold cr.mu
|
|
|
|
func (cr *cacheRecord) mount(ctx context.Context, s session.Group) (_ snapshot.Mountable, rerr error) {
|
|
|
|
if cr.mountCache != nil {
|
|
|
|
return cr.mountCache, nil
|
2017-07-18 06:08:22 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
var mountSnapshotID string
|
|
|
|
if cr.mutable {
|
|
|
|
mountSnapshotID = cr.getSnapshotID()
|
|
|
|
} else if cr.equalMutable != nil {
|
|
|
|
mountSnapshotID = cr.equalMutable.getSnapshotID()
|
|
|
|
} else {
|
|
|
|
mountSnapshotID = cr.viewSnapshotID()
|
|
|
|
if _, err := cr.cm.LeaseManager.Create(ctx, func(l *leases.Lease) error {
|
|
|
|
l.ID = cr.viewLeaseID()
|
2019-09-19 20:08:36 +00:00
|
|
|
l.Labels = map[string]string{
|
2019-10-03 21:11:54 +00:00
|
|
|
"containerd.io/gc.flat": time.Now().UTC().Format(time.RFC3339Nano),
|
2019-09-19 20:08:36 +00:00
|
|
|
}
|
|
|
|
return nil
|
2021-10-28 19:59:26 +00:00
|
|
|
}, leaseutil.MakeTemporary); err != nil && !errdefs.IsAlreadyExists(err) {
|
2019-09-19 20:08:36 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
defer func() {
|
|
|
|
if rerr != nil {
|
|
|
|
cr.cm.LeaseManager.Delete(context.TODO(), leases.Lease{ID: cr.viewLeaseID()})
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if err := cr.cm.LeaseManager.AddResource(ctx, leases.Lease{ID: cr.viewLeaseID()}, leases.Resource{
|
|
|
|
ID: mountSnapshotID,
|
|
|
|
Type: "snapshots/" + cr.cm.Snapshotter.Name(),
|
|
|
|
}); err != nil && !errdefs.IsAlreadyExists(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Return the mount direct from View rather than setting it using the Mounts call below.
|
|
|
|
// The two are equivalent for containerd snapshotters but the moby snapshotter requires
|
|
|
|
// the use of the mountable returned by View in this case.
|
|
|
|
mnts, err := cr.cm.Snapshotter.View(ctx, mountSnapshotID, cr.getSnapshotID())
|
|
|
|
if err != nil && !errdefs.IsAlreadyExists(err) {
|
|
|
|
return nil, err
|
2017-05-30 21:17:04 +00:00
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
cr.mountCache = mnts
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
|
|
|
|
if cr.mountCache != nil {
|
|
|
|
return cr.mountCache, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
mnts, err := cr.cm.Snapshotter.Mounts(ctx, mountSnapshotID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cr.mountCache = mnts
|
|
|
|
return cr.mountCache, nil
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
2017-12-27 01:22:50 +00:00
|
|
|
// call when holding the manager lock
|
2017-07-14 18:59:31 +00:00
|
|
|
func (cr *cacheRecord) remove(ctx context.Context, removeSnapshot bool) error {
|
2017-07-19 23:39:32 +00:00
|
|
|
delete(cr.cm.records, cr.ID())
|
2017-07-14 18:59:31 +00:00
|
|
|
if removeSnapshot {
|
2021-10-28 19:59:26 +00:00
|
|
|
if err := cr.cm.LeaseManager.Delete(ctx, leases.Lease{
|
|
|
|
ID: cr.ID(),
|
|
|
|
}); err != nil && !errdefs.IsNotFound(err) {
|
|
|
|
return errors.Wrapf(err, "failed to delete lease for %s", cr.ID())
|
2017-07-14 18:59:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
if err := cr.cm.MetadataStore.Clear(cr.ID()); err != nil {
|
2021-08-03 01:57:39 +00:00
|
|
|
return errors.Wrapf(err, "failed to delete metadata of %s", cr.ID())
|
|
|
|
}
|
|
|
|
if err := cr.parentRefs.release(ctx); err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to release parents of %s", cr.ID())
|
2017-12-27 01:22:50 +00:00
|
|
|
}
|
2017-07-14 18:59:31 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
type immutableRef struct {
|
|
|
|
*cacheRecord
|
2018-09-14 20:35:41 +00:00
|
|
|
triggerLastUsed bool
|
2020-05-28 20:46:33 +00:00
|
|
|
descHandlers DescHandlers
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
// Order is from parent->child, sr will be at end of slice. Refs should not
|
|
|
|
// be released as they are used internally in the underlying cacheRecords.
|
|
|
|
func (sr *immutableRef) layerChain() []*immutableRef {
|
|
|
|
var count int
|
|
|
|
sr.layerWalk(func(*immutableRef) {
|
|
|
|
count++
|
|
|
|
})
|
|
|
|
layers := make([]*immutableRef, count)
|
|
|
|
var index int
|
|
|
|
sr.layerWalk(func(sr *immutableRef) {
|
|
|
|
layers[index] = sr
|
|
|
|
index++
|
|
|
|
})
|
|
|
|
return layers
|
|
|
|
}
|
|
|
|
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
// returns the set of cache record IDs for each layer in sr's layer chain
|
|
|
|
func (sr *immutableRef) layerSet() map[string]struct{} {
|
|
|
|
var count int
|
|
|
|
sr.layerWalk(func(*immutableRef) {
|
|
|
|
count++
|
|
|
|
})
|
|
|
|
set := make(map[string]struct{}, count)
|
|
|
|
sr.layerWalk(func(sr *immutableRef) {
|
|
|
|
set[sr.ID()] = struct{}{}
|
|
|
|
})
|
|
|
|
return set
|
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
// layerWalk visits each ref representing an actual layer in the chain for
|
|
|
|
// sr (including sr). The layers are visited from lowest->highest as ordered
|
|
|
|
// in the remote for the ref.
|
|
|
|
func (sr *immutableRef) layerWalk(f func(*immutableRef)) {
|
|
|
|
switch sr.kind() {
|
|
|
|
case Merge:
|
|
|
|
for _, parent := range sr.mergeParents {
|
|
|
|
parent.layerWalk(f)
|
|
|
|
}
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case Diff:
|
|
|
|
lower := sr.diffParents.lower
|
|
|
|
upper := sr.diffParents.upper
|
|
|
|
// If upper is only one blob different from lower, then re-use that blob
|
|
|
|
switch {
|
|
|
|
case upper != nil && lower == nil && upper.kind() == BaseLayer:
|
|
|
|
// upper is a single layer being diffed with scratch
|
|
|
|
f(upper)
|
|
|
|
case upper != nil && lower != nil && upper.kind() == Layer && upper.layerParent.ID() == lower.ID():
|
|
|
|
// upper is a single layer on top of lower
|
|
|
|
f(upper)
|
|
|
|
default:
|
|
|
|
// otherwise, the diff will be computed and turned into its own single blob
|
|
|
|
f(sr)
|
|
|
|
}
|
2021-08-03 01:57:39 +00:00
|
|
|
case Layer:
|
|
|
|
sr.layerParent.layerWalk(f)
|
|
|
|
fallthrough
|
|
|
|
case BaseLayer:
|
|
|
|
f(sr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// hold cacheRecord.mu lock before calling
|
|
|
|
func (cr *cacheRecord) layerDigestChain() []digest.Digest {
|
|
|
|
if cr.layerDigestChainCache != nil {
|
|
|
|
return cr.layerDigestChainCache
|
|
|
|
}
|
|
|
|
switch cr.kind() {
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case Diff:
|
|
|
|
if cr.getBlob() == "" {
|
|
|
|
// this diff just reuses the upper blob
|
|
|
|
cr.layerDigestChainCache = cr.diffParents.upper.layerDigestChain()
|
|
|
|
} else {
|
|
|
|
cr.layerDigestChainCache = append(cr.layerDigestChainCache, cr.getBlob())
|
|
|
|
}
|
2021-08-03 01:57:39 +00:00
|
|
|
case Merge:
|
|
|
|
for _, parent := range cr.mergeParents {
|
|
|
|
cr.layerDigestChainCache = append(cr.layerDigestChainCache, parent.layerDigestChain()...)
|
|
|
|
}
|
|
|
|
case Layer:
|
|
|
|
cr.layerDigestChainCache = append(cr.layerDigestChainCache, cr.layerParent.layerDigestChain()...)
|
|
|
|
fallthrough
|
|
|
|
case BaseLayer:
|
|
|
|
cr.layerDigestChainCache = append(cr.layerDigestChainCache, cr.getBlob())
|
|
|
|
}
|
|
|
|
return cr.layerDigestChainCache
|
|
|
|
}
|
|
|
|
|
2021-10-28 19:38:08 +00:00
|
|
|
type RefList []ImmutableRef
|
|
|
|
|
|
|
|
func (l RefList) Release(ctx context.Context) (rerr error) {
|
|
|
|
for i, r := range l {
|
|
|
|
if r == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := r.Release(ctx); err != nil {
|
|
|
|
rerr = multierror.Append(rerr, err).ErrorOrNil()
|
|
|
|
} else {
|
|
|
|
l[i] = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rerr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *immutableRef) LayerChain() RefList {
|
2021-08-03 01:57:39 +00:00
|
|
|
chain := sr.layerChain()
|
2021-10-28 19:38:08 +00:00
|
|
|
l := RefList(make([]ImmutableRef, len(chain)))
|
|
|
|
for i, p := range chain {
|
|
|
|
l[i] = p.Clone()
|
|
|
|
}
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
2021-10-08 21:23:53 +00:00
|
|
|
func (sr *immutableRef) DescHandler(dgst digest.Digest) *DescHandler {
|
|
|
|
return sr.descHandlers[dgst]
|
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
type mutableRef struct {
|
|
|
|
*cacheRecord
|
2018-09-14 20:35:41 +00:00
|
|
|
triggerLastUsed bool
|
2020-05-28 20:46:33 +00:00
|
|
|
descHandlers DescHandlers
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2021-10-08 21:23:53 +00:00
|
|
|
func (sr *mutableRef) DescHandler(dgst digest.Digest) *DescHandler {
|
|
|
|
return sr.descHandlers[dgst]
|
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
func (sr *immutableRef) clone() *immutableRef {
|
2018-05-10 04:52:05 +00:00
|
|
|
sr.mu.Lock()
|
2020-05-28 20:46:33 +00:00
|
|
|
ref := sr.ref(false, sr.descHandlers)
|
2018-05-10 04:52:05 +00:00
|
|
|
sr.mu.Unlock()
|
|
|
|
return ref
|
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
func (sr *immutableRef) Clone() ImmutableRef {
|
|
|
|
return sr.clone()
|
|
|
|
}
|
|
|
|
|
2021-07-26 02:00:48 +00:00
|
|
|
func (sr *immutableRef) ociDesc(ctx context.Context, dhs DescHandlers) (ocispecs.Descriptor, error) {
|
2021-10-28 19:59:26 +00:00
|
|
|
dgst := sr.getBlob()
|
|
|
|
if dgst == "" {
|
|
|
|
return ocispecs.Descriptor{}, errors.Errorf("no blob set for cache record %s", sr.ID())
|
|
|
|
}
|
|
|
|
|
2021-07-26 08:53:30 +00:00
|
|
|
desc := ocispecs.Descriptor{
|
2021-07-09 00:09:35 +00:00
|
|
|
Digest: sr.getBlob(),
|
|
|
|
Size: sr.getBlobSize(),
|
|
|
|
MediaType: sr.getMediaType(),
|
2020-05-28 20:46:33 +00:00
|
|
|
Annotations: make(map[string]string),
|
|
|
|
}
|
|
|
|
|
2021-07-26 02:00:48 +00:00
|
|
|
if blobDesc, err := getBlobDesc(ctx, sr.cm.ContentStore, desc.Digest); err == nil {
|
|
|
|
if blobDesc.Annotations != nil {
|
|
|
|
desc.Annotations = blobDesc.Annotations
|
|
|
|
}
|
|
|
|
} else if dh, ok := dhs[desc.Digest]; ok {
|
|
|
|
// No blob metadtata is stored in the content store. Try to get annotations from desc handlers.
|
|
|
|
for k, v := range filterAnnotationsForSave(dh.Annotations) {
|
|
|
|
desc.Annotations[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
diffID := sr.getDiffID()
|
2020-05-28 20:46:33 +00:00
|
|
|
if diffID != "" {
|
2021-07-09 00:09:35 +00:00
|
|
|
desc.Annotations["containerd.io/uncompressed"] = string(diffID)
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
createdAt := sr.GetCreatedAt()
|
2020-05-28 20:46:33 +00:00
|
|
|
if !createdAt.IsZero() {
|
|
|
|
createdAt, err := createdAt.MarshalText()
|
|
|
|
if err != nil {
|
2021-07-26 08:53:30 +00:00
|
|
|
return ocispecs.Descriptor{}, err
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
desc.Annotations["buildkit/createdat"] = string(createdAt)
|
|
|
|
}
|
|
|
|
|
|
|
|
return desc, nil
|
|
|
|
}
|
|
|
|
|
2021-07-26 02:00:48 +00:00
|
|
|
const (
|
|
|
|
compressionVariantDigestLabelPrefix = "buildkit.io/compression/digest."
|
|
|
|
compressionVariantAnnotationsLabelPrefix = "buildkit.io/compression/annotation."
|
|
|
|
compressionVariantMediaTypeLabel = "buildkit.io/compression/mediatype"
|
|
|
|
)
|
2021-03-30 12:59:03 +00:00
|
|
|
|
|
|
|
func compressionVariantDigestLabel(compressionType compression.Type) string {
|
|
|
|
return compressionVariantDigestLabelPrefix + compressionType.String()
|
|
|
|
}
|
|
|
|
|
2021-10-05 06:43:22 +00:00
|
|
|
func getCompressionVariants(ctx context.Context, cs content.Store, dgst digest.Digest) (res []compression.Type, _ error) {
|
|
|
|
info, err := cs.Info(ctx, dgst)
|
|
|
|
if errors.Is(err, errdefs.ErrNotFound) {
|
|
|
|
return nil, nil
|
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for k := range info.Labels {
|
|
|
|
if strings.HasPrefix(k, compressionVariantDigestLabelPrefix) {
|
|
|
|
if t := compression.Parse(strings.TrimPrefix(k, compressionVariantDigestLabelPrefix)); t != compression.UnknownCompression {
|
|
|
|
res = append(res, t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-26 02:00:48 +00:00
|
|
|
func (sr *immutableRef) getCompressionBlob(ctx context.Context, compressionType compression.Type) (ocispecs.Descriptor, error) {
|
2021-10-05 06:43:22 +00:00
|
|
|
return getCompressionVariantBlob(ctx, sr.cm.ContentStore, sr.getBlob(), compressionType)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCompressionVariantBlob(ctx context.Context, cs content.Store, dgst digest.Digest, compressionType compression.Type) (ocispecs.Descriptor, error) {
|
|
|
|
info, err := cs.Info(ctx, dgst)
|
2021-03-30 12:59:03 +00:00
|
|
|
if err != nil {
|
2021-07-26 02:00:48 +00:00
|
|
|
return ocispecs.Descriptor{}, err
|
2021-03-30 12:59:03 +00:00
|
|
|
}
|
|
|
|
dgstS, ok := info.Labels[compressionVariantDigestLabel(compressionType)]
|
|
|
|
if ok {
|
|
|
|
dgst, err := digest.Parse(dgstS)
|
|
|
|
if err != nil {
|
2021-07-26 02:00:48 +00:00
|
|
|
return ocispecs.Descriptor{}, err
|
2021-07-21 22:53:16 +00:00
|
|
|
}
|
2021-07-26 02:00:48 +00:00
|
|
|
return getBlobDesc(ctx, cs, dgst)
|
2021-03-30 12:59:03 +00:00
|
|
|
}
|
2021-07-26 02:00:48 +00:00
|
|
|
return ocispecs.Descriptor{}, errdefs.ErrNotFound
|
2021-03-30 12:59:03 +00:00
|
|
|
}
|
|
|
|
|
2021-07-26 02:00:48 +00:00
|
|
|
func (sr *immutableRef) addCompressionBlob(ctx context.Context, desc ocispecs.Descriptor, compressionType compression.Type) error {
|
2021-03-30 12:59:03 +00:00
|
|
|
cs := sr.cm.ContentStore
|
2021-10-28 19:59:26 +00:00
|
|
|
if err := sr.cm.LeaseManager.AddResource(ctx, leases.Lease{ID: sr.ID()}, leases.Resource{
|
2021-07-26 02:00:48 +00:00
|
|
|
ID: desc.Digest.String(),
|
2021-03-30 12:59:03 +00:00
|
|
|
Type: "content",
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
info, err := cs.Info(ctx, sr.getBlob())
|
2021-03-30 12:59:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if info.Labels == nil {
|
|
|
|
info.Labels = make(map[string]string)
|
|
|
|
}
|
|
|
|
cachedVariantLabel := compressionVariantDigestLabel(compressionType)
|
2021-07-26 02:00:48 +00:00
|
|
|
info.Labels[cachedVariantLabel] = desc.Digest.String()
|
2021-03-30 12:59:03 +00:00
|
|
|
if _, err := cs.Update(ctx, info, "labels."+cachedVariantLabel); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-26 02:00:48 +00:00
|
|
|
|
|
|
|
info, err = cs.Info(ctx, desc.Digest)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var fields []string
|
|
|
|
info.Labels = map[string]string{
|
|
|
|
compressionVariantMediaTypeLabel: desc.MediaType,
|
|
|
|
}
|
|
|
|
fields = append(fields, "labels."+compressionVariantMediaTypeLabel)
|
|
|
|
for k, v := range filterAnnotationsForSave(desc.Annotations) {
|
|
|
|
k2 := compressionVariantAnnotationsLabelPrefix + k
|
|
|
|
info.Labels[k2] = v
|
|
|
|
fields = append(fields, "labels."+k2)
|
|
|
|
}
|
|
|
|
if _, err := cs.Update(ctx, info, fields...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-30 12:59:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-07-26 02:00:48 +00:00
|
|
|
func filterAnnotationsForSave(a map[string]string) (b map[string]string) {
|
|
|
|
if a == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for _, k := range append(eStargzAnnotations, containerdUncompressed) {
|
|
|
|
v, ok := a[k]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if b == nil {
|
|
|
|
b = make(map[string]string)
|
|
|
|
}
|
|
|
|
b[k] = v
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBlobDesc(ctx context.Context, cs content.Store, dgst digest.Digest) (ocispecs.Descriptor, error) {
|
|
|
|
info, err := cs.Info(ctx, dgst)
|
|
|
|
if err != nil {
|
|
|
|
return ocispecs.Descriptor{}, err
|
|
|
|
}
|
|
|
|
if info.Labels == nil {
|
|
|
|
return ocispecs.Descriptor{}, fmt.Errorf("no blob metadata is stored for %q", info.Digest)
|
|
|
|
}
|
|
|
|
mt, ok := info.Labels[compressionVariantMediaTypeLabel]
|
|
|
|
if !ok {
|
|
|
|
return ocispecs.Descriptor{}, fmt.Errorf("no media type is stored for %q", info.Digest)
|
|
|
|
}
|
|
|
|
desc := ocispecs.Descriptor{
|
|
|
|
Digest: info.Digest,
|
|
|
|
Size: info.Size,
|
|
|
|
MediaType: mt,
|
|
|
|
}
|
|
|
|
for k, v := range info.Labels {
|
|
|
|
if strings.HasPrefix(k, compressionVariantAnnotationsLabelPrefix) {
|
|
|
|
if desc.Annotations == nil {
|
|
|
|
desc.Annotations = make(map[string]string)
|
|
|
|
}
|
|
|
|
desc.Annotations[strings.TrimPrefix(k, compressionVariantAnnotationsLabelPrefix)] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return desc, nil
|
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
func (sr *immutableRef) Mount(ctx context.Context, readonly bool, s session.Group) (_ snapshot.Mountable, rerr error) {
|
|
|
|
if sr.equalMutable != nil && !readonly {
|
|
|
|
if err := sr.Finalize(ctx); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-31 03:55:33 +00:00
|
|
|
if err := sr.Extract(ctx, s); err != nil {
|
|
|
|
return nil, err
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sr.mu.Lock()
|
|
|
|
defer sr.mu.Unlock()
|
2020-10-11 12:56:00 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if sr.mountCache != nil {
|
2022-01-16 04:20:17 +00:00
|
|
|
if readonly {
|
|
|
|
return setReadonly(sr.mountCache), nil
|
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
return sr.mountCache, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var mnt snapshot.Mountable
|
2020-10-11 12:56:00 +00:00
|
|
|
if sr.cm.Snapshotter.Name() == "stargz" {
|
|
|
|
if err := sr.withRemoteSnapshotLabelsStargzMode(ctx, s, func() {
|
2021-10-28 19:59:26 +00:00
|
|
|
mnt, rerr = sr.mount(ctx, s)
|
2020-10-11 12:56:00 +00:00
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
} else {
|
|
|
|
mnt, rerr = sr.mount(ctx, s)
|
|
|
|
}
|
|
|
|
if rerr != nil {
|
|
|
|
return nil, rerr
|
2020-10-11 12:56:00 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if readonly {
|
|
|
|
mnt = setReadonly(mnt)
|
|
|
|
}
|
|
|
|
return mnt, nil
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-27 06:13:39 +00:00
|
|
|
func (sr *immutableRef) Extract(ctx context.Context, s session.Group) (rerr error) {
|
2021-08-03 01:57:39 +00:00
|
|
|
if (sr.kind() == Layer || sr.kind() == BaseLayer) && !sr.getBlobOnly() {
|
2021-10-28 19:59:26 +00:00
|
|
|
return nil
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 06:05:12 +00:00
|
|
|
if sr.cm.Snapshotter.Name() == "stargz" {
|
2020-10-11 12:56:00 +00:00
|
|
|
if err := sr.withRemoteSnapshotLabelsStargzMode(ctx, s, func() {
|
|
|
|
if rerr = sr.prepareRemoteSnapshotsStargzMode(ctx, s); rerr != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
rerr = sr.unlazy(ctx, sr.descHandlers, s)
|
2020-10-11 12:56:00 +00:00
|
|
|
}); err != nil {
|
2020-11-21 06:05:12 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-10-11 12:56:00 +00:00
|
|
|
return rerr
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
return sr.unlazy(ctx, sr.descHandlers, s)
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 12:56:00 +00:00
|
|
|
func (sr *immutableRef) withRemoteSnapshotLabelsStargzMode(ctx context.Context, s session.Group, f func()) error {
|
|
|
|
dhs := sr.descHandlers
|
2021-08-03 01:57:39 +00:00
|
|
|
for _, r := range sr.layerChain() {
|
2020-10-11 12:56:00 +00:00
|
|
|
r := r
|
2021-07-09 00:09:35 +00:00
|
|
|
info, err := r.cm.Snapshotter.Stat(ctx, r.getSnapshotID())
|
2020-10-11 12:56:00 +00:00
|
|
|
if err != nil && !errdefs.IsNotFound(err) {
|
|
|
|
return err
|
|
|
|
} else if errdefs.IsNotFound(err) {
|
|
|
|
continue // This snpashot doesn't exist; skip
|
|
|
|
} else if _, ok := info.Labels["containerd.io/snapshot/remote"]; !ok {
|
|
|
|
continue // This isn't a remote snapshot; skip
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
dh := dhs[digest.Digest(r.getBlob())]
|
2020-08-17 09:34:33 +00:00
|
|
|
if dh == nil {
|
2020-10-11 12:56:00 +00:00
|
|
|
continue // no info passed; skip
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 12:56:00 +00:00
|
|
|
// Append temporary labels (based on dh.SnapshotLabels) as hints for remote snapshots.
|
|
|
|
// For avoiding collosion among calls, keys of these tmp labels contain an unique ID.
|
|
|
|
flds, labels := makeTmpLabelsStargzMode(snapshots.FilterInheritedLabels(dh.SnapshotLabels), s)
|
|
|
|
info.Labels = labels
|
|
|
|
if _, err := r.cm.Snapshotter.Update(ctx, info, flds...); err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to add tmp remote labels for remote snapshot")
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
for k := range info.Labels {
|
|
|
|
info.Labels[k] = "" // Remove labels appended in this call
|
|
|
|
}
|
|
|
|
if _, err := r.cm.Snapshotter.Update(ctx, info, flds...); err != nil {
|
|
|
|
logrus.Warn(errors.Wrapf(err, "failed to remove tmp remote labels"))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
f()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *immutableRef) prepareRemoteSnapshotsStargzMode(ctx context.Context, s session.Group) error {
|
|
|
|
_, err := sr.sizeG.Do(ctx, sr.ID()+"-prepare-remote-snapshot", func(ctx context.Context) (_ interface{}, rerr error) {
|
|
|
|
dhs := sr.descHandlers
|
2021-08-03 01:57:39 +00:00
|
|
|
for _, r := range sr.layerChain() {
|
2020-10-11 12:56:00 +00:00
|
|
|
r := r
|
2021-07-09 00:09:35 +00:00
|
|
|
snapshotID := r.getSnapshotID()
|
2020-10-11 12:56:00 +00:00
|
|
|
if _, err := r.cm.Snapshotter.Stat(ctx, snapshotID); err == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
dh := dhs[digest.Digest(r.getBlob())]
|
2020-10-11 12:56:00 +00:00
|
|
|
if dh == nil {
|
|
|
|
// We cannot prepare remote snapshots without descHandler.
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// tmpLabels contains dh.SnapshotLabels + session IDs. All keys contain
|
|
|
|
// an unique ID for avoiding the collision among snapshotter API calls to
|
|
|
|
// this snapshot. tmpLabels will be removed at the end of this function.
|
|
|
|
defaultLabels := snapshots.FilterInheritedLabels(dh.SnapshotLabels)
|
|
|
|
if defaultLabels == nil {
|
|
|
|
defaultLabels = make(map[string]string)
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
2020-10-11 12:56:00 +00:00
|
|
|
tmpFields, tmpLabels := makeTmpLabelsStargzMode(defaultLabels, s)
|
|
|
|
defaultLabels["containerd.io/snapshot.ref"] = snapshotID
|
|
|
|
|
|
|
|
// Prepare remote snapshots
|
|
|
|
var (
|
2021-07-09 00:09:35 +00:00
|
|
|
key = fmt.Sprintf("tmp-%s %s", identity.NewID(), r.getChainID())
|
2020-10-11 12:56:00 +00:00
|
|
|
opts = []snapshots.Opt{
|
|
|
|
snapshots.WithLabels(defaultLabels),
|
|
|
|
snapshots.WithLabels(tmpLabels),
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
2020-10-11 12:56:00 +00:00
|
|
|
)
|
|
|
|
parentID := ""
|
2021-08-03 01:57:39 +00:00
|
|
|
if r.layerParent != nil {
|
|
|
|
parentID = r.layerParent.getSnapshotID()
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
2021-07-26 02:00:48 +00:00
|
|
|
if err := r.cm.Snapshotter.Prepare(ctx, key, parentID, opts...); err != nil {
|
2020-10-11 12:56:00 +00:00
|
|
|
if errdefs.IsAlreadyExists(err) {
|
|
|
|
// Check if the targeting snapshot ID has been prepared as
|
|
|
|
// a remote snapshot in the snapshotter.
|
|
|
|
info, err := r.cm.Snapshotter.Stat(ctx, snapshotID)
|
|
|
|
if err == nil { // usable as remote snapshot without unlazying.
|
|
|
|
defer func() {
|
|
|
|
// Remove tmp labels appended in this func
|
|
|
|
for k := range tmpLabels {
|
|
|
|
info.Labels[k] = ""
|
|
|
|
}
|
|
|
|
if _, err := r.cm.Snapshotter.Update(ctx, info, tmpFields...); err != nil {
|
|
|
|
logrus.Warn(errors.Wrapf(err,
|
|
|
|
"failed to remove tmp remote labels after prepare"))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Try the next layer as well.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This layer and all upper layers cannot be prepared without unlazying.
|
|
|
|
break
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 12:56:00 +00:00
|
|
|
return nil, nil
|
2020-08-17 09:34:33 +00:00
|
|
|
})
|
2020-10-11 12:56:00 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeTmpLabelsStargzMode(labels map[string]string, s session.Group) (fields []string, res map[string]string) {
|
|
|
|
res = make(map[string]string)
|
|
|
|
// Append unique ID to labels for avoiding collision of labels among calls
|
|
|
|
id := identity.NewID()
|
|
|
|
for k, v := range labels {
|
|
|
|
tmpKey := k + "." + id
|
|
|
|
fields = append(fields, "labels."+tmpKey)
|
|
|
|
res[tmpKey] = v
|
|
|
|
}
|
|
|
|
for i, sid := range session.AllSessionIDs(s) {
|
|
|
|
sidKey := "containerd.io/snapshot/remote/stargz.session." + fmt.Sprintf("%d", i) + "." + id
|
|
|
|
fields = append(fields, "labels."+sidKey)
|
|
|
|
res[sidKey] = sid
|
2020-11-21 04:35:26 +00:00
|
|
|
}
|
2020-10-11 12:56:00 +00:00
|
|
|
return
|
2020-08-17 09:34:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
func (sr *immutableRef) unlazy(ctx context.Context, dhs DescHandlers, s session.Group) error {
|
|
|
|
_, err := sr.sizeG.Do(ctx, sr.ID()+"-unlazy", func(ctx context.Context) (_ interface{}, rerr error) {
|
|
|
|
if _, err := sr.cm.Snapshotter.Stat(ctx, sr.getSnapshotID()); err == nil {
|
2020-08-17 09:34:33 +00:00
|
|
|
return nil, nil
|
2019-09-18 00:18:32 +00:00
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
switch sr.kind() {
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
case Merge, Diff:
|
|
|
|
return nil, sr.unlazyDiffMerge(ctx, dhs, s)
|
2021-08-03 01:57:39 +00:00
|
|
|
case Layer, BaseLayer:
|
|
|
|
return nil, sr.unlazyLayer(ctx, dhs, s)
|
|
|
|
}
|
|
|
|
return nil, nil
|
2021-10-28 19:59:26 +00:00
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
2020-11-12 23:36:16 +00:00
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
// should be called within sizeG.Do call for this ref's ID
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
func (sr *immutableRef) unlazyDiffMerge(ctx context.Context, dhs DescHandlers, s session.Group) error {
|
2021-08-03 01:57:39 +00:00
|
|
|
eg, egctx := errgroup.WithContext(ctx)
|
|
|
|
var diffs []snapshot.Diff
|
|
|
|
sr.layerWalk(func(sr *immutableRef) {
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
var diff snapshot.Diff
|
|
|
|
switch sr.kind() {
|
|
|
|
case Diff:
|
|
|
|
if sr.diffParents.lower != nil {
|
|
|
|
diff.Lower = sr.diffParents.lower.getSnapshotID()
|
|
|
|
eg.Go(func() error {
|
|
|
|
return sr.diffParents.lower.unlazy(egctx, dhs, s)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if sr.diffParents.upper != nil {
|
|
|
|
diff.Upper = sr.diffParents.upper.getSnapshotID()
|
|
|
|
eg.Go(func() error {
|
|
|
|
return sr.diffParents.upper.unlazy(egctx, dhs, s)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case Layer:
|
2021-08-03 01:57:39 +00:00
|
|
|
diff.Lower = sr.layerParent.getSnapshotID()
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
fallthrough
|
|
|
|
case BaseLayer:
|
|
|
|
diff.Upper = sr.getSnapshotID()
|
|
|
|
eg.Go(func() error {
|
|
|
|
return sr.unlazy(egctx, dhs, s)
|
|
|
|
})
|
2021-08-03 01:57:39 +00:00
|
|
|
}
|
|
|
|
diffs = append(diffs, diff)
|
|
|
|
})
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
if err := eg.Wait(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-08-03 01:57:39 +00:00
|
|
|
return sr.cm.Snapshotter.Merge(ctx, sr.getSnapshotID(), diffs)
|
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
// should be called within sizeG.Do call for this ref's ID
|
|
|
|
func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, s session.Group) (rerr error) {
|
|
|
|
if !sr.getBlobOnly() {
|
|
|
|
return nil
|
|
|
|
}
|
2020-05-28 20:46:33 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if sr.cm.Applier == nil {
|
|
|
|
return errors.New("unlazy requires an applier")
|
|
|
|
}
|
2019-09-18 00:18:32 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if _, ok := leases.FromContext(ctx); !ok {
|
|
|
|
leaseCtx, done, err := leaseutil.WithLease(ctx, sr.cm.LeaseManager, leaseutil.MakeTemporary)
|
2020-05-28 20:46:33 +00:00
|
|
|
if err != nil {
|
2021-10-28 19:59:26 +00:00
|
|
|
return err
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
defer done(leaseCtx)
|
|
|
|
ctx = leaseCtx
|
|
|
|
}
|
|
|
|
|
|
|
|
if sr.GetLayerType() == "windows" {
|
|
|
|
ctx = winlayers.UseWindowsLayerMode(ctx)
|
|
|
|
}
|
2020-05-28 20:46:33 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
eg, egctx := errgroup.WithContext(ctx)
|
|
|
|
|
|
|
|
parentID := ""
|
2021-08-03 01:57:39 +00:00
|
|
|
if sr.layerParent != nil {
|
2020-05-28 20:46:33 +00:00
|
|
|
eg.Go(func() error {
|
2021-08-03 01:57:39 +00:00
|
|
|
if err := sr.layerParent.unlazy(egctx, dhs, s); err != nil {
|
2021-10-28 19:59:26 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-08-03 01:57:39 +00:00
|
|
|
parentID = sr.layerParent.getSnapshotID()
|
2021-10-28 19:59:26 +00:00
|
|
|
return nil
|
2020-05-28 20:46:33 +00:00
|
|
|
})
|
2021-10-28 19:59:26 +00:00
|
|
|
}
|
2020-05-28 20:46:33 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
desc, err := sr.ociDesc(ctx, dhs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
dh := dhs[desc.Digest]
|
2020-05-28 20:46:33 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
eg.Go(func() error {
|
|
|
|
// unlazies if needed, otherwise a no-op
|
|
|
|
return lazyRefProvider{
|
|
|
|
ref: sr,
|
|
|
|
desc: desc,
|
|
|
|
dh: dh,
|
|
|
|
session: s,
|
|
|
|
}.Unlazy(egctx)
|
|
|
|
})
|
2020-05-28 20:46:33 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if err := eg.Wait(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-28 20:46:33 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if dh != nil && dh.Progress != nil {
|
|
|
|
_, stopProgress := dh.Progress.Start(ctx)
|
|
|
|
defer stopProgress(rerr)
|
|
|
|
statusDone := dh.Progress.Status("extracting "+desc.Digest.String(), "extracting")
|
|
|
|
defer statusDone()
|
|
|
|
}
|
2019-09-18 00:18:32 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
key := fmt.Sprintf("extract-%s %s", identity.NewID(), sr.getChainID())
|
2019-09-18 00:18:32 +00:00
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
err = sr.cm.Snapshotter.Prepare(ctx, key, parentID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
mountable, err := sr.cm.Snapshotter.Mounts(ctx, key)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mounts, unmount, err := mountable.Mount()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = sr.cm.Applier.Apply(ctx, desc, mounts)
|
|
|
|
if err != nil {
|
|
|
|
unmount()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := unmount(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := sr.cm.Snapshotter.Commit(ctx, sr.getSnapshotID(), key); err != nil {
|
|
|
|
if !errors.Is(err, errdefs.ErrAlreadyExists) {
|
|
|
|
return err
|
2019-09-18 00:18:32 +00:00
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
}
|
|
|
|
sr.queueBlobOnly(false)
|
|
|
|
sr.queueSize(sizeUnknown)
|
|
|
|
if err := sr.commitMetadata(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2019-09-18 00:18:32 +00:00
|
|
|
}
|
|
|
|
|
2017-06-13 23:01:47 +00:00
|
|
|
func (sr *immutableRef) Release(ctx context.Context) error {
|
2017-05-27 06:12:13 +00:00
|
|
|
sr.cm.mu.Lock()
|
|
|
|
defer sr.cm.mu.Unlock()
|
|
|
|
|
|
|
|
sr.mu.Lock()
|
|
|
|
defer sr.mu.Unlock()
|
|
|
|
|
2017-06-13 23:01:47 +00:00
|
|
|
return sr.release(ctx)
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
func (sr *immutableRef) shouldUpdateLastUsed() bool {
|
2018-09-14 20:35:41 +00:00
|
|
|
return sr.triggerLastUsed
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *immutableRef) updateLastUsedNow() bool {
|
|
|
|
if !sr.triggerLastUsed {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for r := range sr.refs {
|
2021-07-09 00:09:35 +00:00
|
|
|
if r.shouldUpdateLastUsed() {
|
2018-09-14 20:35:41 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-06-13 23:01:47 +00:00
|
|
|
func (sr *immutableRef) release(ctx context.Context) error {
|
2017-05-27 06:12:13 +00:00
|
|
|
delete(sr.refs, sr)
|
|
|
|
|
2018-09-14 20:35:41 +00:00
|
|
|
if sr.updateLastUsedNow() {
|
2021-07-09 00:09:35 +00:00
|
|
|
sr.updateLastUsed()
|
2018-09-14 20:35:41 +00:00
|
|
|
if sr.equalMutable != nil {
|
|
|
|
sr.equalMutable.triggerLastUsed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(sr.refs) == 0 {
|
2017-07-14 18:59:31 +00:00
|
|
|
if sr.equalMutable != nil {
|
|
|
|
sr.equalMutable.release(ctx)
|
2021-10-28 19:59:26 +00:00
|
|
|
} else {
|
|
|
|
if err := sr.cm.LeaseManager.Delete(ctx, leases.Lease{ID: sr.viewLeaseID()}); err != nil && !errdefs.IsNotFound(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sr.mountCache = nil
|
2017-07-14 18:59:31 +00:00
|
|
|
}
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-25 17:51:30 +00:00
|
|
|
func (sr *immutableRef) Finalize(ctx context.Context) error {
|
2017-05-27 06:12:13 +00:00
|
|
|
sr.mu.Lock()
|
|
|
|
defer sr.mu.Unlock()
|
2021-07-01 02:50:54 +00:00
|
|
|
return sr.finalize(ctx)
|
2017-07-14 18:59:31 +00:00
|
|
|
}
|
|
|
|
|
2021-07-01 02:50:54 +00:00
|
|
|
// caller must hold cacheRecord.mu
|
|
|
|
func (cr *cacheRecord) finalize(ctx context.Context) error {
|
2017-11-17 02:09:35 +00:00
|
|
|
mutable := cr.equalMutable
|
2017-07-14 18:59:31 +00:00
|
|
|
if mutable == nil {
|
|
|
|
return nil
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
2019-07-22 21:43:16 +00:00
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
_, err := cr.cm.LeaseManager.Create(ctx, func(l *leases.Lease) error {
|
2019-07-22 21:43:16 +00:00
|
|
|
l.ID = cr.ID()
|
|
|
|
l.Labels = map[string]string{
|
|
|
|
"containerd.io/gc.flat": time.Now().UTC().Format(time.RFC3339Nano),
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
2020-04-19 05:17:47 +00:00
|
|
|
if !errors.Is(err, errdefs.ErrAlreadyExists) { // migrator adds leases for everything
|
2019-09-30 23:38:02 +00:00
|
|
|
return errors.Wrap(err, "failed to create lease")
|
|
|
|
}
|
2019-07-22 21:43:16 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if err := cr.cm.LeaseManager.AddResource(ctx, leases.Lease{ID: cr.ID()}, leases.Resource{
|
|
|
|
ID: cr.getSnapshotID(),
|
|
|
|
Type: "snapshots/" + cr.cm.Snapshotter.Name(),
|
2019-07-22 21:43:16 +00:00
|
|
|
}); err != nil {
|
2019-09-24 01:02:17 +00:00
|
|
|
cr.cm.LeaseManager.Delete(context.TODO(), leases.Lease{ID: cr.ID()})
|
2021-10-28 19:59:26 +00:00
|
|
|
return errors.Wrapf(err, "failed to add snapshot %s to lease", cr.getSnapshotID())
|
2019-07-22 21:43:16 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if err := cr.cm.Snapshotter.Commit(ctx, cr.getSnapshotID(), mutable.getSnapshotID()); err != nil {
|
2019-09-19 21:55:10 +00:00
|
|
|
cr.cm.LeaseManager.Delete(context.TODO(), leases.Lease{ID: cr.ID()})
|
cache: add support for Diff refs.
This allows you to create refs that are single layers representing the
diff between any two arbitrary refs. The primary use case for this is
to allows users to extract the changes created by ops like Exec and
rebase them elsewhere through MergeOp. However, there is no restriction
on the inputs to DiffOp and the resulting ref's layer is simply the
layer created by running the differ on the two inputs refs
(specifically, the same differ used during exports).
A Diff ref can be mounted by itself, in which case it is defined as the
result of applying the diff to Scratch. Most use cases though will use
Diff refs as the input to a MergeOp, in which case the diff is just
applied on top of the lower merge inputs, as was the case before.
In cases like Diff(A, A->B->C) (i.e. cases where the diff is between two
refs where the lower is an ancestor of upper), the diff will be defined
as the layers separating the two refs. In other cases, the diff is just
a single layer, not re-used from the inputs, representing the diff
between the two refs (which can be defined as the layer "Diff(A,B)" that
satisfies "Merge(A, Diff(A,B)) == B").
Note that there is technically a meaningful difference between the
"unmerge" behavior of extracting the layers separating diffs and the
"simple diff" of just running the differ on the two refs. Namely, in the
case where there are "intermediate deletes" (i.e. deletes that only
exist in layers between A and B but not between A and B by themselves),
then the simple diff and unmerge can create different results when
plugged into a MergeOp. This is due to the fact that intermediate
deletes will apply to the merge when using the unmerge behavior, but not
when using the simple diff. This is on top of the fact that the simple
diff inherently has a "flattening" behavior where multiple layers are
squashed into a single one.
So, in the case where lower is an ancestor of upper, we choose to follow
the unmerge behavior, but it's possible users may prefer the simple diff
behavior. As of right now, they won't be able to do so, but if needed we
can add the ability to choose which behavior is followed in the future.
This could be done through a flag provided to DiffOp or possibly by
adapting llb.Copy to support this type of behavior with the same
efficiency as DiffOp.
Signed-off-by: Erik Sipsma <erik@sipsma.dev>
2021-11-26 21:43:24 +00:00
|
|
|
return errors.Wrapf(err, "failed to commit %s to %s during finalize", mutable.getSnapshotID(), cr.getSnapshotID())
|
2019-09-19 21:55:10 +00:00
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
cr.mountCache = nil
|
|
|
|
|
2019-09-19 21:55:10 +00:00
|
|
|
mutable.dead = true
|
|
|
|
go func() {
|
|
|
|
cr.cm.mu.Lock()
|
|
|
|
defer cr.cm.mu.Unlock()
|
|
|
|
if err := mutable.remove(context.TODO(), true); err != nil {
|
|
|
|
logrus.Error(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2017-11-17 02:09:35 +00:00
|
|
|
cr.equalMutable = nil
|
2021-07-09 00:09:35 +00:00
|
|
|
cr.clearEqualMutable()
|
|
|
|
return cr.commitMetadata()
|
2017-07-14 18:59:31 +00:00
|
|
|
}
|
2017-05-27 06:12:13 +00:00
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
func (sr *mutableRef) shouldUpdateLastUsed() bool {
|
2018-09-14 20:35:41 +00:00
|
|
|
return sr.triggerLastUsed
|
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
func (sr *mutableRef) commit(ctx context.Context) (_ *immutableRef, rerr error) {
|
2017-07-14 18:59:31 +00:00
|
|
|
if !sr.mutable || len(sr.refs) == 0 {
|
2019-02-27 22:40:45 +00:00
|
|
|
return nil, errors.Wrapf(errInvalid, "invalid mutable ref %p", sr)
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
id := identity.NewID()
|
2021-07-09 00:09:35 +00:00
|
|
|
md, _ := sr.cm.getMetadata(id)
|
2017-07-14 18:59:31 +00:00
|
|
|
rec := &cacheRecord{
|
2021-07-09 00:09:35 +00:00
|
|
|
mu: sr.mu,
|
|
|
|
cm: sr.cm,
|
2021-08-03 01:57:39 +00:00
|
|
|
parentRefs: sr.parentRefs.clone(),
|
2021-07-09 00:09:35 +00:00
|
|
|
equalMutable: sr,
|
|
|
|
refs: make(map[ref]struct{}),
|
|
|
|
cacheMetadata: md,
|
2017-07-14 18:59:31 +00:00
|
|
|
}
|
2017-06-01 22:24:23 +00:00
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
if descr := sr.GetDescription(); descr != "" {
|
|
|
|
if err := md.queueDescription(descr); err != nil {
|
2017-07-25 19:11:52 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-03 01:57:39 +00:00
|
|
|
if err := initializeMetadata(rec.cacheMetadata, rec.parentRefs); err != nil {
|
2017-07-25 19:11:52 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
sr.cm.records[id] = rec
|
2017-06-01 22:24:23 +00:00
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
if err := sr.commitMetadata(); err != nil {
|
2017-07-03 23:08:20 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-05-27 06:12:13 +00:00
|
|
|
|
2021-07-09 00:09:35 +00:00
|
|
|
md.queueCommitted(true)
|
|
|
|
md.queueSize(sizeUnknown)
|
2021-10-28 19:59:26 +00:00
|
|
|
md.queueSnapshotID(id)
|
2021-07-09 00:09:35 +00:00
|
|
|
md.setEqualMutable(sr.ID())
|
|
|
|
if err := md.commitMetadata(); err != nil {
|
2017-07-14 18:59:31 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-28 20:46:33 +00:00
|
|
|
ref := rec.ref(true, sr.descHandlers)
|
2017-07-14 18:59:31 +00:00
|
|
|
sr.equalImmutable = ref
|
|
|
|
return ref, nil
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
func (sr *mutableRef) Mount(ctx context.Context, readonly bool, s session.Group) (_ snapshot.Mountable, rerr error) {
|
2020-05-28 20:46:33 +00:00
|
|
|
sr.mu.Lock()
|
|
|
|
defer sr.mu.Unlock()
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if sr.mountCache != nil {
|
2022-01-16 04:20:17 +00:00
|
|
|
if readonly {
|
|
|
|
return setReadonly(sr.mountCache), nil
|
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
return sr.mountCache, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var mnt snapshot.Mountable
|
2021-08-03 01:57:39 +00:00
|
|
|
if sr.cm.Snapshotter.Name() == "stargz" && sr.layerParent != nil {
|
|
|
|
if err := sr.layerParent.withRemoteSnapshotLabelsStargzMode(ctx, s, func() {
|
2021-10-28 19:59:26 +00:00
|
|
|
mnt, rerr = sr.mount(ctx, s)
|
2020-10-11 12:56:00 +00:00
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-28 19:59:26 +00:00
|
|
|
} else {
|
|
|
|
mnt, rerr = sr.mount(ctx, s)
|
|
|
|
}
|
|
|
|
if rerr != nil {
|
|
|
|
return nil, rerr
|
2020-10-11 12:56:00 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 19:59:26 +00:00
|
|
|
if readonly {
|
|
|
|
mnt = setReadonly(mnt)
|
|
|
|
}
|
|
|
|
return mnt, nil
|
2020-05-28 20:46:33 +00:00
|
|
|
}
|
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
func (sr *mutableRef) Commit(ctx context.Context) (ImmutableRef, error) {
|
2017-05-27 06:12:13 +00:00
|
|
|
sr.cm.mu.Lock()
|
|
|
|
defer sr.cm.mu.Unlock()
|
|
|
|
|
|
|
|
sr.mu.Lock()
|
2017-07-14 18:59:31 +00:00
|
|
|
defer sr.mu.Unlock()
|
2017-05-27 06:12:13 +00:00
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
return sr.commit(ctx)
|
|
|
|
}
|
2017-07-03 23:08:20 +00:00
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
func (sr *mutableRef) Release(ctx context.Context) error {
|
|
|
|
sr.cm.mu.Lock()
|
|
|
|
defer sr.cm.mu.Unlock()
|
2017-05-27 06:12:13 +00:00
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
sr.mu.Lock()
|
|
|
|
defer sr.mu.Unlock()
|
2017-05-27 06:12:13 +00:00
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
return sr.release(ctx)
|
|
|
|
}
|
2017-05-27 06:12:13 +00:00
|
|
|
|
2017-07-14 18:59:31 +00:00
|
|
|
func (sr *mutableRef) release(ctx context.Context) error {
|
|
|
|
delete(sr.refs, sr)
|
2021-07-09 00:09:35 +00:00
|
|
|
if !sr.HasCachePolicyRetain() {
|
2017-07-19 23:39:32 +00:00
|
|
|
if sr.equalImmutable != nil {
|
2021-07-09 00:09:35 +00:00
|
|
|
if sr.equalImmutable.HasCachePolicyRetain() {
|
|
|
|
if sr.shouldUpdateLastUsed() {
|
|
|
|
sr.updateLastUsed()
|
2018-09-14 20:35:41 +00:00
|
|
|
sr.triggerLastUsed = false
|
|
|
|
}
|
2017-07-20 22:55:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
2017-07-19 23:39:32 +00:00
|
|
|
if err := sr.equalImmutable.remove(ctx, false); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sr.remove(ctx, true)
|
2020-07-18 16:11:39 +00:00
|
|
|
}
|
2021-07-09 00:09:35 +00:00
|
|
|
if sr.shouldUpdateLastUsed() {
|
|
|
|
sr.updateLastUsed()
|
2020-07-18 16:11:39 +00:00
|
|
|
sr.triggerLastUsed = false
|
2017-07-19 23:39:32 +00:00
|
|
|
}
|
2017-07-14 18:59:31 +00:00
|
|
|
return nil
|
2017-05-27 06:12:13 +00:00
|
|
|
}
|
2017-07-18 06:08:22 +00:00
|
|
|
|
2018-04-16 22:23:10 +00:00
|
|
|
func setReadonly(mounts snapshot.Mountable) snapshot.Mountable {
|
|
|
|
return &readOnlyMounter{mounts}
|
|
|
|
}
|
|
|
|
|
|
|
|
type readOnlyMounter struct {
|
|
|
|
snapshot.Mountable
|
|
|
|
}
|
|
|
|
|
2019-08-15 22:42:03 +00:00
|
|
|
func (m *readOnlyMounter) Mount() ([]mount.Mount, func() error, error) {
|
|
|
|
mounts, release, err := m.Mountable.Mount()
|
2018-04-16 22:23:10 +00:00
|
|
|
if err != nil {
|
2019-08-15 22:42:03 +00:00
|
|
|
return nil, nil, err
|
2018-04-16 22:23:10 +00:00
|
|
|
}
|
2017-07-18 06:08:22 +00:00
|
|
|
for i, m := range mounts {
|
2019-07-29 18:00:10 +00:00
|
|
|
if m.Type == "overlay" {
|
|
|
|
mounts[i].Options = readonlyOverlay(m.Options)
|
|
|
|
continue
|
|
|
|
}
|
2017-07-18 06:08:22 +00:00
|
|
|
opts := make([]string, 0, len(m.Options))
|
|
|
|
for _, opt := range m.Options {
|
|
|
|
if opt != "rw" {
|
|
|
|
opts = append(opts, opt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
opts = append(opts, "ro")
|
|
|
|
mounts[i].Options = opts
|
|
|
|
}
|
2019-08-15 22:42:03 +00:00
|
|
|
return mounts, release, nil
|
2017-07-18 06:08:22 +00:00
|
|
|
}
|
2019-07-29 18:00:10 +00:00
|
|
|
|
|
|
|
func readonlyOverlay(opt []string) []string {
|
|
|
|
out := make([]string, 0, len(opt))
|
|
|
|
upper := ""
|
|
|
|
for _, o := range opt {
|
|
|
|
if strings.HasPrefix(o, "upperdir=") {
|
|
|
|
upper = strings.TrimPrefix(o, "upperdir=")
|
|
|
|
} else if !strings.HasPrefix(o, "workdir=") {
|
|
|
|
out = append(out, o)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if upper != "" {
|
|
|
|
for i, o := range out {
|
|
|
|
if strings.HasPrefix(o, "lowerdir=") {
|
|
|
|
out[i] = "lowerdir=" + upper + ":" + strings.TrimPrefix(o, "lowerdir=")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|