...

Source file src/github.com/theupdateframework/go-tuf/client/interop_test.go

Documentation: github.com/theupdateframework/go-tuf/client

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"net/http"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/theupdateframework/go-tuf/util"
    13  	. "gopkg.in/check.v1"
    14  
    15  	goTufGenerator "github.com/theupdateframework/go-tuf/client/testdata/go-tuf/generator"
    16  )
    17  
    18  type InteropSuite struct{}
    19  
    20  var _ = Suite(&InteropSuite{})
    21  
    22  func (InteropSuite) TestGoClientIdentityConsistentSnapshotFalse(c *C) {
    23  	checkGoIdentity(c, false)
    24  }
    25  
    26  func (InteropSuite) TestGoClientIdentityConsistentSnapshotTrue(c *C) {
    27  	checkGoIdentity(c, true)
    28  }
    29  
    30  func checkGoIdentity(c *C, consistentSnapshot bool) {
    31  	cwd, err := os.Getwd()
    32  	c.Assert(err, IsNil)
    33  	testDataDir := filepath.Join(cwd, "testdata")
    34  
    35  	tempDir, err := os.MkdirTemp("", "")
    36  	c.Assert(err, IsNil)
    37  	defer os.RemoveAll(tempDir)
    38  
    39  	// Generate the metadata and compute hashes for all the files.
    40  	goTufGenerator.Generate(tempDir, filepath.Join(testDataDir, "keys.json"), consistentSnapshot)
    41  	hashes := computeHashes(c, tempDir)
    42  
    43  	snapshotDir := filepath.Join(testDataDir, "go-tuf", fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot))
    44  	snapshotHashes := computeHashes(c, snapshotDir)
    45  
    46  	c.Assert(hashes, DeepEquals, snapshotHashes, Commentf("metadata out of date, regenerate by running client/testdata/go-tuf/regenerate-metadata.sh"))
    47  }
    48  
    49  func computeHashes(c *C, dir string) map[string]string {
    50  	hashes := make(map[string]string)
    51  
    52  	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
    53  		if err != nil {
    54  			return err
    55  		}
    56  
    57  		if info.IsDir() {
    58  			return nil
    59  		}
    60  
    61  		bytes, err := os.ReadFile(path)
    62  		if err != nil {
    63  			return err
    64  		}
    65  
    66  		path, err = filepath.Rel(dir, path)
    67  		if err != nil {
    68  			return err
    69  		}
    70  		hashes[path] = string(bytes)
    71  
    72  		return nil
    73  	})
    74  	c.Assert(err, IsNil)
    75  
    76  	return hashes
    77  }
    78  
    79  func (InteropSuite) TestGoClientCompatibility(c *C) {
    80  	names := []string{
    81  		"go-tuf",
    82  		"go-tuf-transition-M3",
    83  		"go-tuf-transition-M4",
    84  	}
    85  	options := &HTTPRemoteOptions{MetadataPath: "", TargetsPath: "targets"}
    86  
    87  	for _, name := range names {
    88  		for _, consistentSnapshot := range []bool{false, true} {
    89  			t := newTestCase(c, name, consistentSnapshot, options)
    90  			t.run(c)
    91  		}
    92  	}
    93  }
    94  
    95  type testCase struct {
    96  	name               string
    97  	consistentSnapshot bool
    98  	options            *HTTPRemoteOptions
    99  	local              LocalStore
   100  	targets            map[string][]byte
   101  	testDir            string
   102  	testSteps          []string
   103  }
   104  
   105  func newTestCase(c *C, name string, consistentSnapshot bool, options *HTTPRemoteOptions) testCase {
   106  	cwd, err := os.Getwd()
   107  	c.Assert(err, IsNil)
   108  	testDir := filepath.Join(cwd, "testdata", name, fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot))
   109  
   110  	dirEntries, err := os.ReadDir(testDir)
   111  	c.Assert(err, IsNil)
   112  	c.Assert(dirEntries, Not(HasLen), 0)
   113  
   114  	testSteps := []string{}
   115  	for _, dirEntry := range dirEntries {
   116  		if dirEntry.IsDir() {
   117  			testSteps = append(testSteps, dirEntry.Name())
   118  		}
   119  	}
   120  
   121  	return testCase{
   122  		name:               name,
   123  		consistentSnapshot: consistentSnapshot,
   124  		options:            options,
   125  		local:              MemoryLocalStore(),
   126  		targets:            make(map[string][]byte),
   127  		testDir:            testDir,
   128  		testSteps:          testSteps,
   129  	}
   130  }
   131  
   132  func (t *testCase) run(c *C) {
   133  	c.Logf("test case: %s consistent-snapshot: %t", t.name, t.consistentSnapshot)
   134  
   135  	for _, stepName := range t.testSteps {
   136  		t.runStep(c, stepName)
   137  	}
   138  }
   139  
   140  func (t *testCase) runStep(c *C, stepName string) {
   141  	c.Logf("step: %s", stepName)
   142  
   143  	addr, cleanup := startFileServer(c, t.testDir)
   144  	defer cleanup()
   145  
   146  	remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/%s/repository", addr, stepName), t.options, nil)
   147  	c.Assert(err, IsNil)
   148  
   149  	client := NewClient(t.local, remote)
   150  	// initiate a client with the root metadata
   151  	ioReader, _, err := remote.GetMeta("root.json")
   152  	c.Assert(err, IsNil)
   153  	rootJsonBytes, err := io.ReadAll(ioReader)
   154  	c.Assert(err, IsNil)
   155  	c.Assert(client.Init(rootJsonBytes), IsNil)
   156  
   157  	// check update returns the correct updated targets
   158  	files, err := client.Update()
   159  	c.Assert(err, IsNil)
   160  	if stepName != "2" {
   161  		// The rest of the test cases add one target file at a time for each cycle, so this is why we expect that
   162  		// the number of updated targets returned by Update() should equals to 1
   163  		c.Assert(files, HasLen, 1)
   164  	} else {
   165  		// The following test case (#2) verifies that when a targets key has been rotated in the latest 3.root.json,
   166  		// the local targets.json meta is indeed ignored since it's signed with a key that has been now changed.
   167  		// The reason we check for 3 here is that the updated targets corresponds to all target files listed in the
   168  		// targets.json for test case #2
   169  		c.Assert(files, HasLen, 3)
   170  	}
   171  	targetName := stepName
   172  	t.targets[targetName] = []byte(targetName)
   173  
   174  	file, ok := files[targetName]
   175  	if !ok {
   176  		c.Fatalf("expected updated targets to contain %s", targetName)
   177  	}
   178  
   179  	data := t.targets[targetName]
   180  	meta, err := util.GenerateTargetFileMeta(bytes.NewReader(data), file.HashAlgorithms()...)
   181  	c.Assert(err, IsNil)
   182  	c.Assert(util.TargetFileMetaEqual(file, meta), IsNil)
   183  
   184  	// download the files and check they have the correct content
   185  	for name, data := range t.targets {
   186  		for _, prefix := range []string{"", "/"} {
   187  			var dest testDestination
   188  			c.Assert(client.Download(prefix+name, &dest), IsNil)
   189  			c.Assert(dest.deleted, Equals, false)
   190  			c.Assert(dest.String(), Equals, string(data))
   191  		}
   192  	}
   193  }
   194  
   195  func startFileServer(c *C, dir string) (string, func() error) {
   196  	l, err := net.Listen("tcp", "127.0.0.1:0")
   197  	c.Assert(err, IsNil)
   198  	addr := l.Addr().String()
   199  	go http.Serve(l, http.FileServer(http.Dir(dir)))
   200  	return addr, l.Close
   201  }
   202  

View as plain text