/oci/casext/map_test.go

https://github.com/openSUSE/umoci · Go · 275 lines · 219 code · 19 blank · 37 comment · 14 complexity · 67c60a1b1e76c5f804dbdb33e1cdbf0b MD5 · raw file

  1. /*
  2. * umoci: Umoci Modifies Open Containers' Images
  3. * Copyright (C) 2016-2019 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. package casext
  18. import (
  19. crand "crypto/rand"
  20. "io"
  21. "math/rand"
  22. "reflect"
  23. "testing"
  24. "github.com/mohae/deepcopy"
  25. "github.com/opencontainers/go-digest"
  26. ispec "github.com/opencontainers/image-spec/specs-go/v1"
  27. )
  28. func descriptorPtr(d ispec.Descriptor) *ispec.Descriptor { return &d }
  29. func randomDescriptor(t *testing.T) ispec.Descriptor {
  30. var descriptor ispec.Descriptor
  31. // Generate a random digest and length.
  32. descriptor.Size = int64(rand.Intn(512 * 1024))
  33. digester := digest.SHA256.Digester()
  34. io.CopyN(digester.Hash(), crand.Reader, descriptor.Size)
  35. descriptor.Digest = digester.Digest()
  36. // Generate a random number of annotations, with random key/values.
  37. descriptor.Annotations = map[string]string{}
  38. n := rand.Intn(32)
  39. for i := 0; i < n; i++ {
  40. descriptor.Annotations[randomString(32)] = randomString(32)
  41. }
  42. return descriptor
  43. }
  44. // Make sure that an identity mapping doesn't change the struct, and that it
  45. // actually does visit all of the descriptors once.
  46. func TestMapDescriptors_Identity(t *testing.T) {
  47. // List of interfaces to use MapDescriptors on, as well as how many
  48. // *unique* descriptors they contain.
  49. ociList := []struct {
  50. num int
  51. obj interface{}
  52. }{
  53. // Make sure that "base" types work.
  54. {
  55. num: 0,
  56. obj: nil,
  57. },
  58. {
  59. num: 1,
  60. obj: randomDescriptor(t),
  61. },
  62. {
  63. num: 1,
  64. obj: descriptorPtr(randomDescriptor(t)),
  65. },
  66. {
  67. num: 3,
  68. obj: []ispec.Descriptor{
  69. randomDescriptor(t),
  70. randomDescriptor(t),
  71. randomDescriptor(t),
  72. },
  73. },
  74. {
  75. num: 7,
  76. obj: []*ispec.Descriptor{
  77. descriptorPtr(randomDescriptor(t)),
  78. descriptorPtr(randomDescriptor(t)),
  79. descriptorPtr(randomDescriptor(t)),
  80. descriptorPtr(randomDescriptor(t)),
  81. descriptorPtr(randomDescriptor(t)),
  82. descriptorPtr(randomDescriptor(t)),
  83. descriptorPtr(randomDescriptor(t)),
  84. },
  85. },
  86. // Make sure official OCI structs work.
  87. {
  88. num: 7,
  89. obj: ispec.Manifest{
  90. Config: randomDescriptor(t),
  91. Layers: []ispec.Descriptor{
  92. randomDescriptor(t),
  93. randomDescriptor(t),
  94. randomDescriptor(t),
  95. randomDescriptor(t),
  96. randomDescriptor(t),
  97. randomDescriptor(t),
  98. },
  99. },
  100. },
  101. {
  102. num: 2,
  103. obj: ispec.Index{
  104. Manifests: []ispec.Descriptor{
  105. randomDescriptor(t),
  106. randomDescriptor(t),
  107. },
  108. },
  109. },
  110. // Check that pointers also work.
  111. {
  112. num: 5,
  113. obj: &ispec.Manifest{
  114. Config: randomDescriptor(t),
  115. Layers: []ispec.Descriptor{
  116. randomDescriptor(t),
  117. randomDescriptor(t),
  118. randomDescriptor(t),
  119. randomDescriptor(t),
  120. },
  121. },
  122. },
  123. {
  124. num: 9,
  125. obj: &ispec.Index{
  126. Manifests: []ispec.Descriptor{
  127. randomDescriptor(t),
  128. randomDescriptor(t),
  129. randomDescriptor(t),
  130. randomDescriptor(t),
  131. randomDescriptor(t),
  132. randomDescriptor(t),
  133. randomDescriptor(t),
  134. randomDescriptor(t),
  135. randomDescriptor(t),
  136. },
  137. },
  138. },
  139. // Make sure that an empty []ispec.Descriptor works properly.
  140. {
  141. num: 0,
  142. obj: []ispec.Descriptor{},
  143. },
  144. {
  145. num: 1,
  146. obj: ispec.Manifest{
  147. Config: randomDescriptor(t),
  148. Layers: nil,
  149. },
  150. },
  151. {
  152. num: 0,
  153. obj: ispec.Index{
  154. Manifests: []ispec.Descriptor{},
  155. },
  156. },
  157. // TODO: Add support for descending into maps.
  158. }
  159. for idx, test := range ociList {
  160. // Make a copy for later comparison.
  161. original := deepcopy.Copy(test.obj)
  162. foundSet := map[digest.Digest]int{}
  163. if err := MapDescriptors(test.obj, func(descriptor ispec.Descriptor) ispec.Descriptor {
  164. foundSet[descriptor.Digest]++
  165. return descriptor
  166. }); err != nil {
  167. t.Errorf("MapDescriptors(%d) unexpected error: %v", idx, err)
  168. continue
  169. }
  170. // Make sure that we hit everything uniquely.
  171. found := 0
  172. for d, n := range foundSet {
  173. found++
  174. if n != 1 {
  175. t.Errorf("MapDescriptors(%d) hit a descriptor more than once: %#v hit %d times", idx, d, n)
  176. }
  177. }
  178. if found != test.num {
  179. t.Errorf("MapDescriptors(%d) didn't hit the right number, expected %d got %d", idx, test.num, found)
  180. }
  181. if !reflect.DeepEqual(original, test.obj) {
  182. t.Errorf("MapDescriptors(%d) descriptors were modified with identity mapping, expected %#v got %#v", idx, original, test.obj)
  183. }
  184. }
  185. }
  186. // Make sure that it is possible to modify a variety of different interfaces.
  187. func TestMapDescriptors_ModifyOCI(t *testing.T) {
  188. // List of interfaces to use MapDescriptors on.
  189. ociList := []struct {
  190. obj interface{}
  191. }{
  192. // Make sure that "base" types work.
  193. {
  194. obj: descriptorPtr(randomDescriptor(t)),
  195. },
  196. {
  197. obj: []ispec.Descriptor{
  198. randomDescriptor(t),
  199. randomDescriptor(t),
  200. randomDescriptor(t),
  201. },
  202. },
  203. {
  204. obj: []*ispec.Descriptor{
  205. descriptorPtr(randomDescriptor(t)),
  206. descriptorPtr(randomDescriptor(t)),
  207. },
  208. },
  209. // TODO: Add the ability to mutate map keys and values.
  210. // Make sure official OCI structs work.
  211. {
  212. obj: &ispec.Manifest{
  213. Config: randomDescriptor(t),
  214. Layers: []ispec.Descriptor{
  215. randomDescriptor(t),
  216. randomDescriptor(t),
  217. randomDescriptor(t),
  218. randomDescriptor(t),
  219. randomDescriptor(t),
  220. randomDescriptor(t),
  221. },
  222. },
  223. },
  224. {
  225. obj: ispec.Index{
  226. Manifests: []ispec.Descriptor{
  227. randomDescriptor(t),
  228. randomDescriptor(t),
  229. },
  230. },
  231. },
  232. {
  233. obj: &ispec.Index{
  234. Manifests: []ispec.Descriptor{
  235. randomDescriptor(t),
  236. randomDescriptor(t),
  237. },
  238. },
  239. },
  240. }
  241. for idx, test := range ociList {
  242. // Make a copy for later comparison.
  243. original := deepcopy.Copy(test.obj)
  244. if err := MapDescriptors(&test.obj, func(descriptor ispec.Descriptor) ispec.Descriptor {
  245. // Create an entirely new descriptor.
  246. return randomDescriptor(t)
  247. }); err != nil {
  248. t.Errorf("MapDescriptors(%d) unexpected error: %v", idx, err)
  249. continue
  250. }
  251. if reflect.DeepEqual(original, test.obj) {
  252. t.Errorf("MapDescriptors(%d) descriptor was unmodified when replacing with a random descriptor!", idx)
  253. }
  254. }
  255. }
  256. // TODO: We should be able to rewrite non-OCI structs in the future.