github.com/willmtemple/docker@v1.7.0-rc2/graph/manifest_test.go (about)

     1  package graph
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/docker/distribution/digest"
    12  	"github.com/docker/docker/image"
    13  	"github.com/docker/docker/pkg/tarsum"
    14  	"github.com/docker/docker/registry"
    15  	"github.com/docker/docker/runconfig"
    16  	"github.com/docker/docker/utils"
    17  	"github.com/docker/libtrust"
    18  )
    19  
    20  const (
    21  	testManifestImageName    = "testapp"
    22  	testManifestImageID      = "d821b739e8834ec89ac4469266c3d11515da88fdcbcbdddcbcddb636f54fdde9"
    23  	testManifestImageIDShort = "d821b739e883"
    24  	testManifestTag          = "manifesttest"
    25  )
    26  
    27  func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) {
    28  	manifest := &registry.ManifestData{
    29  		Name:          remoteName,
    30  		Tag:           tag,
    31  		SchemaVersion: 1,
    32  	}
    33  	localRepo, err := s.Get(localName)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	if localRepo == nil {
    38  		return nil, fmt.Errorf("Repo does not exist: %s", localName)
    39  	}
    40  
    41  	// Get the top-most layer id which the tag points to
    42  	layerId, exists := localRepo[tag]
    43  	if !exists {
    44  		return nil, fmt.Errorf("Tag does not exist for %s: %s", localName, tag)
    45  	}
    46  	layersSeen := make(map[string]bool)
    47  
    48  	layer, err := s.graph.Get(layerId)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	manifest.Architecture = layer.Architecture
    53  	manifest.FSLayers = make([]*registry.FSLayer, 0, 4)
    54  	manifest.History = make([]*registry.ManifestHistory, 0, 4)
    55  	var metadata runconfig.Config
    56  	if layer.Config != nil {
    57  		metadata = *layer.Config
    58  	}
    59  
    60  	for ; layer != nil; layer, err = layer.GetParent() {
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  
    65  		if layersSeen[layer.ID] {
    66  			break
    67  		}
    68  		if layer.Config != nil && metadata.Image != layer.ID {
    69  			err = runconfig.Merge(&metadata, layer.Config)
    70  			if err != nil {
    71  				return nil, err
    72  			}
    73  		}
    74  
    75  		checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID))
    76  		if err != nil {
    77  			return nil, fmt.Errorf("Error getting image checksum: %s", err)
    78  		}
    79  		if tarsum.VersionLabelForChecksum(checksum) != tarsum.Version1.String() {
    80  			archive, err := layer.TarLayer()
    81  			if err != nil {
    82  				return nil, err
    83  			}
    84  
    85  			defer archive.Close()
    86  
    87  			tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1)
    88  			if err != nil {
    89  				return nil, err
    90  			}
    91  			if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
    92  				return nil, err
    93  			}
    94  
    95  			checksum = tarSum.Sum(nil)
    96  
    97  			// Save checksum value
    98  			if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), checksum); err != nil {
    99  				return nil, err
   100  			}
   101  		}
   102  
   103  		jsonData, err := layer.RawJson()
   104  		if err != nil {
   105  			return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err)
   106  		}
   107  
   108  		manifest.FSLayers = append(manifest.FSLayers, &registry.FSLayer{BlobSum: checksum})
   109  
   110  		layersSeen[layer.ID] = true
   111  
   112  		manifest.History = append(manifest.History, &registry.ManifestHistory{V1Compatibility: string(jsonData)})
   113  	}
   114  
   115  	manifestBytes, err := json.MarshalIndent(manifest, "", "   ")
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	return manifestBytes, nil
   121  }
   122  
   123  func TestManifestTarsumCache(t *testing.T) {
   124  	tmp, err := utils.TestDirectory("")
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	defer os.RemoveAll(tmp)
   129  	store := mkTestTagStore(tmp, t)
   130  	defer store.graph.driver.Cleanup()
   131  
   132  	archive, err := fakeTar()
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  	img := &image.Image{ID: testManifestImageID}
   137  	if err := store.graph.Register(img, archive); err != nil {
   138  		t.Fatal(err)
   139  	}
   140  	if err := store.Tag(testManifestImageName, testManifestTag, testManifestImageID, false); err != nil {
   141  		t.Fatal(err)
   142  	}
   143  
   144  	if cs, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)); err != nil {
   145  		t.Fatal(err)
   146  	} else if cs != "" {
   147  		t.Fatalf("Non-empty checksum file after register")
   148  	}
   149  
   150  	// Generate manifest
   151  	payload, err := store.newManifest(testManifestImageName, testManifestImageName, testManifestTag)
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	manifestChecksum, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID))
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  
   161  	var manifest registry.ManifestData
   162  	if err := json.Unmarshal(payload, &manifest); err != nil {
   163  		t.Fatalf("error unmarshalling manifest: %s", err)
   164  	}
   165  
   166  	if len(manifest.FSLayers) != 1 {
   167  		t.Fatalf("Unexpected number of layers, expecting 1: %d", len(manifest.FSLayers))
   168  	}
   169  
   170  	if manifest.FSLayers[0].BlobSum != manifestChecksum {
   171  		t.Fatalf("Unexpected blob sum, expecting %q, got %q", manifestChecksum, manifest.FSLayers[0].BlobSum)
   172  	}
   173  
   174  	if len(manifest.History) != 1 {
   175  		t.Fatalf("Unexpected number of layer history, expecting 1: %d", len(manifest.History))
   176  	}
   177  
   178  	v1compat, err := img.RawJson()
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	}
   182  	if manifest.History[0].V1Compatibility != string(v1compat) {
   183  		t.Fatalf("Unexpected json value\nExpected:\n%s\nActual:\n%s", v1compat, manifest.History[0].V1Compatibility)
   184  	}
   185  }
   186  
   187  // TestManifestDigestCheck ensures that loadManifest properly verifies the
   188  // remote and local digest.
   189  func TestManifestDigestCheck(t *testing.T) {
   190  	tmp, err := utils.TestDirectory("")
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	defer os.RemoveAll(tmp)
   195  	store := mkTestTagStore(tmp, t)
   196  	defer store.graph.driver.Cleanup()
   197  
   198  	archive, err := fakeTar()
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	img := &image.Image{ID: testManifestImageID}
   203  	if err := store.graph.Register(img, archive); err != nil {
   204  		t.Fatal(err)
   205  	}
   206  	if err := store.Tag(testManifestImageName, testManifestTag, testManifestImageID, false); err != nil {
   207  		t.Fatal(err)
   208  	}
   209  
   210  	if cs, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)); err != nil {
   211  		t.Fatal(err)
   212  	} else if cs != "" {
   213  		t.Fatalf("Non-empty checksum file after register")
   214  	}
   215  
   216  	// Generate manifest
   217  	payload, err := store.newManifest(testManifestImageName, testManifestImageName, testManifestTag)
   218  	if err != nil {
   219  		t.Fatalf("unexpected error generating test manifest: %v", err)
   220  	}
   221  
   222  	pk, err := libtrust.GenerateECP256PrivateKey()
   223  	if err != nil {
   224  		t.Fatalf("unexpected error generating private key: %v", err)
   225  	}
   226  
   227  	sig, err := libtrust.NewJSONSignature(payload)
   228  	if err != nil {
   229  		t.Fatalf("error creating signature: %v", err)
   230  	}
   231  
   232  	if err := sig.Sign(pk); err != nil {
   233  		t.Fatalf("error signing manifest bytes: %v", err)
   234  	}
   235  
   236  	signedBytes, err := sig.PrettySignature("signatures")
   237  	if err != nil {
   238  		t.Fatalf("error getting signed bytes: %v", err)
   239  	}
   240  
   241  	dgst, err := digest.FromBytes(payload)
   242  	if err != nil {
   243  		t.Fatalf("error getting digest of manifest: %v", err)
   244  	}
   245  
   246  	// use this as the "bad" digest
   247  	zeroDigest, err := digest.FromBytes([]byte{})
   248  	if err != nil {
   249  		t.Fatalf("error making zero digest: %v", err)
   250  	}
   251  
   252  	// Remote and local match, everything should look good
   253  	local, _, _, err := store.loadManifest(signedBytes, dgst.String(), dgst)
   254  	if err != nil {
   255  		t.Fatalf("unexpected error verifying local and remote digest: %v", err)
   256  	}
   257  
   258  	if local != dgst {
   259  		t.Fatalf("local digest not correctly calculated: %v", err)
   260  	}
   261  
   262  	// remote and no local, since pulling by tag
   263  	local, _, _, err = store.loadManifest(signedBytes, "tag", dgst)
   264  	if err != nil {
   265  		t.Fatalf("unexpected error verifying tag pull and remote digest: %v", err)
   266  	}
   267  
   268  	if local != dgst {
   269  		t.Fatalf("local digest not correctly calculated: %v", err)
   270  	}
   271  
   272  	// remote and differing local, this is the most important to fail
   273  	local, _, _, err = store.loadManifest(signedBytes, zeroDigest.String(), dgst)
   274  	if err == nil {
   275  		t.Fatalf("error expected when verifying with differing local digest")
   276  	}
   277  
   278  	// no remote, no local (by tag)
   279  	local, _, _, err = store.loadManifest(signedBytes, "tag", "")
   280  	if err != nil {
   281  		t.Fatalf("unexpected error verifying manifest without remote digest: %v", err)
   282  	}
   283  
   284  	if local != dgst {
   285  		t.Fatalf("local digest not correctly calculated: %v", err)
   286  	}
   287  
   288  	// no remote, with local
   289  	local, _, _, err = store.loadManifest(signedBytes, dgst.String(), "")
   290  	if err != nil {
   291  		t.Fatalf("unexpected error verifying manifest without remote digest: %v", err)
   292  	}
   293  
   294  	if local != dgst {
   295  		t.Fatalf("local digest not correctly calculated: %v", err)
   296  	}
   297  
   298  	// bad remote, we fail the check.
   299  	local, _, _, err = store.loadManifest(signedBytes, dgst.String(), zeroDigest)
   300  	if err == nil {
   301  		t.Fatalf("error expected when verifying with differing remote digest")
   302  	}
   303  }