github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/oci/casext/map_test.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016-2020 SUSE LLC 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package casext 19 20 import ( 21 crand "crypto/rand" 22 "io" 23 "math/rand" 24 "reflect" 25 "testing" 26 27 "github.com/mohae/deepcopy" 28 "github.com/opencontainers/go-digest" 29 ispec "github.com/opencontainers/image-spec/specs-go/v1" 30 "github.com/opencontainers/umoci/pkg/testutils" 31 ) 32 33 func descriptorPtr(d ispec.Descriptor) *ispec.Descriptor { return &d } 34 35 func randomDescriptor(t *testing.T) ispec.Descriptor { 36 var descriptor ispec.Descriptor 37 38 // Generate a random digest and length. 39 descriptor.Size = int64(rand.Intn(512 * 1024)) 40 digester := digest.SHA256.Digester() 41 io.CopyN(digester.Hash(), crand.Reader, descriptor.Size) 42 descriptor.Digest = digester.Digest() 43 44 // Generate a random number of annotations, with random key/values. 45 descriptor.Annotations = map[string]string{} 46 n := rand.Intn(32) 47 for i := 0; i < n; i++ { 48 descriptor.Annotations[testutils.RandomString(32)] = testutils.RandomString(32) 49 } 50 51 return descriptor 52 } 53 54 // Make sure that an identity mapping doesn't change the struct, and that it 55 // actually does visit all of the descriptors once. 56 func TestMapDescriptors_Identity(t *testing.T) { 57 // List of interfaces to use MapDescriptors on, as well as how many 58 // *unique* descriptors they contain. 59 ociList := []struct { 60 num int 61 obj interface{} 62 }{ 63 // Make sure that "base" types work. 64 { 65 num: 0, 66 obj: nil, 67 }, 68 { 69 num: 1, 70 obj: randomDescriptor(t), 71 }, 72 { 73 num: 1, 74 obj: descriptorPtr(randomDescriptor(t)), 75 }, 76 { 77 num: 3, 78 obj: []ispec.Descriptor{ 79 randomDescriptor(t), 80 randomDescriptor(t), 81 randomDescriptor(t), 82 }, 83 }, 84 { 85 num: 7, 86 obj: []*ispec.Descriptor{ 87 descriptorPtr(randomDescriptor(t)), 88 descriptorPtr(randomDescriptor(t)), 89 descriptorPtr(randomDescriptor(t)), 90 descriptorPtr(randomDescriptor(t)), 91 descriptorPtr(randomDescriptor(t)), 92 descriptorPtr(randomDescriptor(t)), 93 descriptorPtr(randomDescriptor(t)), 94 }, 95 }, 96 // Make sure official OCI structs work. 97 { 98 num: 7, 99 obj: ispec.Manifest{ 100 MediaType: ispec.MediaTypeImageManifest, 101 Config: randomDescriptor(t), 102 Layers: []ispec.Descriptor{ 103 randomDescriptor(t), 104 randomDescriptor(t), 105 randomDescriptor(t), 106 randomDescriptor(t), 107 randomDescriptor(t), 108 randomDescriptor(t), 109 }, 110 }, 111 }, 112 { 113 num: 2, 114 obj: ispec.Index{ 115 MediaType: ispec.MediaTypeImageIndex, 116 Manifests: []ispec.Descriptor{ 117 randomDescriptor(t), 118 randomDescriptor(t), 119 }, 120 }, 121 }, 122 // Check that pointers also work. 123 { 124 num: 5, 125 obj: &ispec.Manifest{ 126 MediaType: ispec.MediaTypeImageManifest, 127 Config: randomDescriptor(t), 128 Layers: []ispec.Descriptor{ 129 randomDescriptor(t), 130 randomDescriptor(t), 131 randomDescriptor(t), 132 randomDescriptor(t), 133 }, 134 }, 135 }, 136 { 137 num: 9, 138 obj: &ispec.Index{ 139 MediaType: ispec.MediaTypeImageIndex, 140 Manifests: []ispec.Descriptor{ 141 randomDescriptor(t), 142 randomDescriptor(t), 143 randomDescriptor(t), 144 randomDescriptor(t), 145 randomDescriptor(t), 146 randomDescriptor(t), 147 randomDescriptor(t), 148 randomDescriptor(t), 149 randomDescriptor(t), 150 }, 151 }, 152 }, 153 // Make sure that an empty []ispec.Descriptor works properly. 154 { 155 num: 0, 156 obj: []ispec.Descriptor{}, 157 }, 158 { 159 num: 1, 160 obj: ispec.Manifest{ 161 MediaType: ispec.MediaTypeImageManifest, 162 Config: randomDescriptor(t), 163 Layers: nil, 164 }, 165 }, 166 { 167 num: 0, 168 obj: ispec.Index{ 169 MediaType: ispec.MediaTypeImageIndex, 170 Manifests: []ispec.Descriptor{}, 171 }, 172 }, 173 // TODO: Add support for descending into maps. 174 } 175 176 for idx, test := range ociList { 177 // Make a copy for later comparison. 178 original := deepcopy.Copy(test.obj) 179 180 foundSet := map[digest.Digest]int{} 181 182 if err := MapDescriptors(test.obj, func(descriptor ispec.Descriptor) ispec.Descriptor { 183 foundSet[descriptor.Digest]++ 184 return descriptor 185 }); err != nil { 186 t.Errorf("MapDescriptors(%d) unexpected error: %v", idx, err) 187 continue 188 } 189 190 // Make sure that we hit everything uniquely. 191 found := 0 192 for d, n := range foundSet { 193 found++ 194 if n != 1 { 195 t.Errorf("MapDescriptors(%d) hit a descriptor more than once: %#v hit %d times", idx, d, n) 196 } 197 } 198 if found != test.num { 199 t.Errorf("MapDescriptors(%d) didn't hit the right number, expected %d got %d", idx, test.num, found) 200 } 201 202 if !reflect.DeepEqual(original, test.obj) { 203 t.Errorf("MapDescriptors(%d) descriptors were modified with identity mapping, expected %#v got %#v", idx, original, test.obj) 204 } 205 } 206 } 207 208 // Make sure that it is possible to modify a variety of different interfaces. 209 func TestMapDescriptors_ModifyOCI(t *testing.T) { 210 // List of interfaces to use MapDescriptors on. 211 ociList := []struct { 212 obj interface{} 213 }{ 214 // Make sure that "base" types work. 215 { 216 obj: descriptorPtr(randomDescriptor(t)), 217 }, 218 { 219 obj: []ispec.Descriptor{ 220 randomDescriptor(t), 221 randomDescriptor(t), 222 randomDescriptor(t), 223 }, 224 }, 225 { 226 obj: []*ispec.Descriptor{ 227 descriptorPtr(randomDescriptor(t)), 228 descriptorPtr(randomDescriptor(t)), 229 }, 230 }, 231 // TODO: Add the ability to mutate map keys and values. 232 // Make sure official OCI structs work. 233 { 234 obj: &ispec.Manifest{ 235 MediaType: ispec.MediaTypeImageManifest, 236 Config: randomDescriptor(t), 237 Layers: []ispec.Descriptor{ 238 randomDescriptor(t), 239 randomDescriptor(t), 240 randomDescriptor(t), 241 randomDescriptor(t), 242 randomDescriptor(t), 243 randomDescriptor(t), 244 }, 245 }, 246 }, 247 { 248 obj: ispec.Index{ 249 MediaType: ispec.MediaTypeImageIndex, 250 Manifests: []ispec.Descriptor{ 251 randomDescriptor(t), 252 randomDescriptor(t), 253 }, 254 }, 255 }, 256 { 257 obj: &ispec.Index{ 258 MediaType: ispec.MediaTypeImageIndex, 259 Manifests: []ispec.Descriptor{ 260 randomDescriptor(t), 261 randomDescriptor(t), 262 }, 263 }, 264 }, 265 } 266 267 for idx, test := range ociList { 268 // Make a copy for later comparison. 269 original := deepcopy.Copy(test.obj) 270 271 if err := MapDescriptors(&test.obj, func(descriptor ispec.Descriptor) ispec.Descriptor { 272 // Create an entirely new descriptor. 273 return randomDescriptor(t) 274 }); err != nil { 275 t.Errorf("MapDescriptors(%d) unexpected error: %v", idx, err) 276 continue 277 } 278 279 if reflect.DeepEqual(original, test.obj) { 280 t.Errorf("MapDescriptors(%d) descriptor was unmodified when replacing with a random descriptor!", idx) 281 } 282 } 283 } 284 285 // TODO: We should be able to rewrite non-OCI structs in the future.