Skip to content

Commit b0c1c88

Browse files
committed
"pull up" images when creating them, too
We previously started "pulling up" images when we changed their names, and started denying the presence of images in read-only stores which shared their ID with an image in the read-write store, so that it would be possible to "remove" names from an image in read-only storage. We forgot about the Flags field, so start pulling that up, too. Do all of the above when we're asked to create an image, since denying the presence of images with the same ID in read-only stores would prevent us from finding the image by any of the names that it "had" just a moment before we created the new record. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
1 parent 945f872 commit b0c1c88

File tree

5 files changed

+103
-26
lines changed

5 files changed

+103
-26
lines changed

containers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ func (r *containerStore) create(id string, names []string, image, layer string,
666666
if _, idInUse := r.byid[id]; idInUse {
667667
return nil, ErrDuplicateID
668668
}
669-
names = dedupeNames(names)
669+
names = dedupeStrings(names)
670670
for _, name := range names {
671671
if _, nameInUse := r.byname[name]; nameInUse {
672672
return nil, fmt.Errorf("the container name %q is already in use by %s. You have to remove that container to be able to reuse that name: %w", name, r.byname[name].ID, ErrDuplicateName)

images.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ func (r *imageStore) create(id string, names []string, layer string, options Ima
703703
if _, idInUse := r.byid[id]; idInUse {
704704
return nil, fmt.Errorf("an image with ID %q already exists: %w", id, ErrDuplicateID)
705705
}
706-
names = dedupeNames(names)
706+
names = dedupeStrings(names)
707707
for _, name := range names {
708708
if image, nameInUse := r.byname[name]; nameInUse {
709709
return nil, fmt.Errorf("image name %q is already associated with image %q: %w", name, image.ID, ErrDuplicateName)
@@ -712,7 +712,7 @@ func (r *imageStore) create(id string, names []string, layer string, options Ima
712712
image = &Image{
713713
ID: id,
714714
Digest: options.Digest,
715-
Digests: copyDigestSlice(options.Digests),
715+
Digests: dedupeDigests(options.Digests),
716716
Names: names,
717717
NamesHistory: copyStringSlice(options.NamesHistory),
718718
TopLayer: layer,
@@ -820,7 +820,7 @@ func (r *imageStore) removeName(image *Image, name string) {
820820

821821
// The caller must hold r.inProcessLock for writing.
822822
func (i *Image) addNameToHistory(name string) {
823-
i.NamesHistory = dedupeNames(append([]string{name}, i.NamesHistory...))
823+
i.NamesHistory = dedupeStrings(append([]string{name}, i.NamesHistory...))
824824
}
825825

826826
// Requires startWriting.

layers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1230,7 +1230,7 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
12301230
if duplicateLayer, idInUse := r.byid[id]; idInUse {
12311231
return duplicateLayer, -1, ErrDuplicateID
12321232
}
1233-
names = dedupeNames(names)
1233+
names = dedupeStrings(names)
12341234
for _, name := range names {
12351235
if _, nameInUse := r.byname[name]; nameInUse {
12361236
return nil, -1, ErrDuplicateName

store.go

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,26 +1384,90 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, i
13841384
layer = ilayer.ID
13851385
}
13861386

1387-
var res *Image
1388-
err := s.writeToImageStore(func() error {
1389-
var options ImageOptions
1387+
var options ImageOptions
1388+
var namesToAddAfterCreating []string
1389+
1390+
if err := s.imageStore.startWriting(); err != nil {
1391+
return nil, err
1392+
}
1393+
defer s.imageStore.stopWriting()
1394+
1395+
// Check if the ID refers to an image in a read-only store -- we want
1396+
// to allow images in read-only stores to have their names changed, so
1397+
// if we find one, merge the new values in with what we know about the
1398+
// image that's already there.
1399+
if id != "" {
1400+
for _, is := range s.roImageStores {
1401+
store := is
1402+
if err := store.startReading(); err != nil {
1403+
return nil, err
1404+
}
1405+
defer store.stopReading()
1406+
if i, err := store.Get(id); err == nil {
1407+
// set information about this image in "options"
1408+
options = ImageOptions{
1409+
Metadata: i.Metadata,
1410+
CreationDate: i.Created,
1411+
Digest: i.Digest,
1412+
Digests: copyDigestSlice(i.Digests),
1413+
NamesHistory: copyStringSlice(i.NamesHistory),
1414+
}
1415+
for _, key := range i.BigDataNames {
1416+
data, err := store.BigData(id, key)
1417+
if err != nil {
1418+
return nil, err
1419+
}
1420+
dataDigest, err := store.BigDataDigest(id, key)
1421+
if err != nil {
1422+
return nil, err
1423+
}
1424+
options.BigData = append(options.BigData, ImageBigDataOption{
1425+
Key: key,
1426+
Data: data,
1427+
Digest: dataDigest,
1428+
})
1429+
}
1430+
namesToAddAfterCreating = dedupeStrings(append(append([]string{}, i.Names...), names...))
1431+
break
1432+
}
1433+
}
1434+
}
13901435

1391-
if iOptions != nil {
1392-
options = *iOptions
1393-
options.Digests = copyDigestSlice(iOptions.Digests)
1394-
options.BigData = copyImageBigDataOptionSlice(iOptions.BigData)
1395-
options.NamesHistory = copyStringSlice(iOptions.NamesHistory)
1396-
options.Flags = copyStringInterfaceMap(iOptions.Flags)
1436+
// merge any passed-in options into "options" as best we can
1437+
if iOptions != nil {
1438+
if !iOptions.CreationDate.IsZero() {
1439+
options.CreationDate = iOptions.CreationDate
13971440
}
1398-
if options.CreationDate.IsZero() {
1399-
options.CreationDate = time.Now().UTC()
1441+
if iOptions.Digest != "" {
1442+
options.Digest = iOptions.Digest
14001443
}
1444+
options.Digests = append(options.Digests, copyDigestSlice(iOptions.Digests)...)
1445+
if iOptions.Metadata != "" {
1446+
options.Metadata = iOptions.Metadata
1447+
}
1448+
options.BigData = append(options.BigData, copyImageBigDataOptionSlice(iOptions.BigData)...)
1449+
options.NamesHistory = append(options.NamesHistory, copyStringSlice(iOptions.NamesHistory)...)
1450+
if options.Flags == nil {
1451+
options.Flags = make(map[string]interface{})
1452+
}
1453+
for k, v := range iOptions.Flags {
1454+
options.Flags[k] = v
1455+
}
1456+
}
1457+
1458+
if options.CreationDate.IsZero() {
1459+
options.CreationDate = time.Now().UTC()
1460+
}
1461+
if metadata != "" {
14011462
options.Metadata = metadata
1463+
}
14021464

1403-
var err error
1404-
res, err = s.imageStore.create(id, names, layer, options)
1405-
return err
1406-
})
1465+
res, err := s.imageStore.create(id, names, layer, options)
1466+
if err == nil && len(namesToAddAfterCreating) > 0 {
1467+
// set any names we pulled up from an additional image store, now that we won't be
1468+
// triggering a duplicate names error
1469+
err = s.imageStore.updateNames(res.ID, namesToAddAfterCreating, addNames)
1470+
}
14071471
return res, err
14081472
}
14091473

@@ -2130,18 +2194,30 @@ func (s *store) Exists(id string) bool {
21302194
return s.containerStore.Exists(id)
21312195
}
21322196

2133-
func dedupeNames(names []string) []string {
2134-
seen := make(map[string]bool)
2197+
func dedupeStrings(names []string) []string {
2198+
seen := make(map[string]struct{})
21352199
deduped := make([]string, 0, len(names))
21362200
for _, name := range names {
21372201
if _, wasSeen := seen[name]; !wasSeen {
2138-
seen[name] = true
2202+
seen[name] = struct{}{}
21392203
deduped = append(deduped, name)
21402204
}
21412205
}
21422206
return deduped
21432207
}
21442208

2209+
func dedupeDigests(digests []digest.Digest) []digest.Digest {
2210+
seen := make(map[digest.Digest]struct{})
2211+
deduped := make([]digest.Digest, 0, len(digests))
2212+
for _, d := range digests {
2213+
if _, wasSeen := seen[d]; !wasSeen {
2214+
seen[d] = struct{}{}
2215+
deduped = append(deduped, d)
2216+
}
2217+
}
2218+
return deduped
2219+
}
2220+
21452221
// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`.
21462222
func (s *store) SetNames(id string, names []string) error {
21472223
return s.updateNames(id, names, setNames)
@@ -2156,7 +2232,7 @@ func (s *store) RemoveNames(id string, names []string) error {
21562232
}
21572233

21582234
func (s *store) updateNames(id string, names []string, op updateNameOperation) error {
2159-
deduped := dedupeNames(names)
2235+
deduped := dedupeStrings(names)
21602236

21612237
layerFound := false
21622238
if err := s.writeToLayerStore(func(rlstore rwLayerStore) error {
@@ -2188,11 +2264,12 @@ func (s *store) updateNames(id string, names []string, op updateNameOperation) e
21882264
if i, err := store.Get(id); err == nil {
21892265
// "pull up" the image so that we can change its names list
21902266
options := ImageOptions{
2191-
Metadata: i.Metadata,
21922267
CreationDate: i.Created,
21932268
Digest: i.Digest,
21942269
Digests: copyDigestSlice(i.Digests),
2270+
Metadata: i.Metadata,
21952271
NamesHistory: copyStringSlice(i.NamesHistory),
2272+
Flags: copyStringInterfaceMap(i.Flags),
21962273
}
21972274
for _, key := range i.BigDataNames {
21982275
data, err := store.BigData(id, key)

utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,5 @@ func applyNameOperation(oldNames []string, opParameters []string, op updateNameO
7070
default:
7171
return result, errInvalidUpdateNameOperation
7272
}
73-
return dedupeNames(result), nil
73+
return dedupeStrings(result), nil
7474
}

0 commit comments

Comments
 (0)