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  }