github.com/google/osv-scalibr@v0.4.1/artifact/image/layerscanning/testing/fakelayerbuilder/fakelayerbuilder.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package fakelayerbuilder uses a yaml file with custom syntax to build up fake layers for testing 16 // 17 // Example: 18 // 19 // ```yml 20 // 21 // layers: 22 // 23 // # Add foo.txt lockfile 24 // - files: 25 // foo.txt: 26 // # With the package foo 27 // - foo 28 // bar.txt: 29 // - bar 30 // # Delete the bar lockfile 31 // - files: 32 // !bar.txt: 33 // - files: 34 // baz.txt: 35 // - baz 36 // # Readd bar 37 // - files: 38 // bar.txt: 39 // - bar 40 // 41 // ``` 42 package fakelayerbuilder 43 44 import ( 45 "fmt" 46 "maps" 47 "os" 48 "path/filepath" 49 "strings" 50 "testing" 51 52 "github.com/google/osv-scalibr/artifact/image/layerscanning/testing/fakechainlayer" 53 "github.com/google/osv-scalibr/artifact/image/layerscanning/testing/fakelayer" 54 "github.com/google/osv-scalibr/artifact/image/whiteout" 55 "github.com/opencontainers/go-digest" 56 "gopkg.in/yaml.v3" 57 ) 58 59 func parseFakeLayerFileFromPath(path string) (FakeTestLayers, error) { 60 layers := FakeTestLayers{} 61 62 data, err := os.ReadFile(path) 63 if err != nil { 64 return FakeTestLayers{}, err 65 } 66 67 err = yaml.Unmarshal(data, &layers) 68 if err != nil { 69 return FakeTestLayers{}, err 70 } 71 72 return layers, nil 73 } 74 75 // BuildFakeChainLayersFromPath builds a slice of fake chain layers from a yaml file as defined at the top of this file. 76 func BuildFakeChainLayersFromPath(t *testing.T, testDir string, layerInfoPath string) []*fakechainlayer.FakeChainLayer { 77 t.Helper() 78 79 layers, err := parseFakeLayerFileFromPath(layerInfoPath) 80 if err != nil { 81 t.Fatalf("Failed to parse fake layer file %q: %q", layerInfoPath, err) 82 } 83 84 output := []*fakechainlayer.FakeChainLayer{} 85 86 // chainLayerContents is edited every loop with the diffs of that layer 87 chainLayerContents := map[string]string{} 88 89 for index, layer := range layers.Layers { 90 diffID := digest.NewDigestFromEncoded(digest.SHA256, fmt.Sprintf("diff-id-%d", index)) 91 chainID := digest.NewDigestFromEncoded(digest.SHA256, fmt.Sprintf("chain-id-%d", index)) 92 command := fmt.Sprintf("command-%d", index) 93 94 // Build and edit layer contents 95 layerContents := map[string]string{} 96 for key, file := range layer.Files { 97 if after, ok := strings.CutPrefix(key, "~"); ok { 98 key = after 99 layerContents[whiteout.ToWhiteout(key)] = "" 100 delete(chainLayerContents, key) 101 102 continue 103 } 104 layerContents[key] = strings.Join(file, "\n") 105 chainLayerContents[key] = layerContents[key] 106 } 107 108 // Create fake layer and chainLayer 109 fakeLayer, err := fakelayer.New(filepath.Join(testDir, fmt.Sprintf("layer-%d", index)), diffID, command, layerContents, false) 110 if err != nil { 111 t.Fatalf("fakelayer.New(%q, %q, %q, %v, %v) failed: %v", testDir, diffID, command, layerContents, false, err) 112 } 113 114 chainLayerContentsClone := make(map[string]string, len(chainLayerContents)) 115 maps.Copy(chainLayerContentsClone, chainLayerContents) 116 cfg := &fakechainlayer.Config{ 117 TestDir: filepath.Join(testDir, fmt.Sprintf("chainlayer-%d", index)), 118 Index: index, 119 DiffID: diffID, 120 ChainID: chainID, 121 Command: command, 122 Layer: fakeLayer, 123 Files: chainLayerContentsClone, 124 FilesAlreadyExist: false, 125 } 126 127 chainLayer, err := fakechainlayer.New(cfg) 128 if err != nil { 129 t.Fatalf("fakechainlayer.New(%d, %q, %q, %v, %v) failed: %v", index, diffID, command, layer, chainLayerContentsClone, err) 130 } 131 132 output = append(output, chainLayer) 133 } 134 135 return output 136 }