From fc21bf280bac39377d3a236efa87f5c8cbadfb9f Mon Sep 17 00:00:00 2001
From: John Howard <jhoward@microsoft.com>
Date: Tue, 25 Apr 2017 09:37:29 -0700
Subject: [PATCH] LCOW: Adds platform to the layer store

Signed-off-by: John Howard <jhoward@microsoft.com>
---
 distribution/xfer/download_test.go |  5 +++++
 layer/empty.go                     |  4 ++++
 layer/filestore_unix.go            | 13 +++++++++++
 layer/filestore_windows.go         | 35 ++++++++++++++++++++++++++++++
 layer/layer.go                     | 13 +++++++++++
 layer/layer_store.go               |  6 +++++
 layer/ro_layer.go                  |  4 ++++
 layer/ro_layer_unix.go             |  7 ++++++
 layer/ro_layer_windows.go          |  7 ++++++
 migrate/v1/migratev1_test.go       |  4 ++++
 10 files changed, 98 insertions(+)
 create mode 100644 layer/filestore_unix.go
 create mode 100644 layer/filestore_windows.go
 create mode 100644 layer/ro_layer_unix.go

diff --git a/distribution/xfer/download_test.go b/distribution/xfer/download_test.go
index 69323bb869..e3434ae312 100644
--- a/distribution/xfer/download_test.go
+++ b/distribution/xfer/download_test.go
@@ -26,6 +26,7 @@ type mockLayer struct {
 	diffID    layer.DiffID
 	chainID   layer.ChainID
 	parent    layer.Layer
+	platform  layer.Platform
 }
 
 func (ml *mockLayer) TarStream() (io.ReadCloser, error) {
@@ -56,6 +57,10 @@ func (ml *mockLayer) DiffSize() (size int64, err error) {
 	return 0, nil
 }
 
+func (ml *mockLayer) Platform() layer.Platform {
+	return ml.platform
+}
+
 func (ml *mockLayer) Metadata() (map[string]string, error) {
 	return make(map[string]string), nil
 }
diff --git a/layer/empty.go b/layer/empty.go
index 80f2c12439..cf04aa12fe 100644
--- a/layer/empty.go
+++ b/layer/empty.go
@@ -55,6 +55,10 @@ func (el *emptyLayer) Metadata() (map[string]string, error) {
 	return make(map[string]string), nil
 }
 
+func (el *emptyLayer) Platform() Platform {
+	return ""
+}
+
 // IsEmpty returns true if the layer is an EmptyLayer
 func IsEmpty(diffID DiffID) bool {
 	return diffID == DigestSHA256EmptyTar
diff --git a/layer/filestore_unix.go b/layer/filestore_unix.go
new file mode 100644
index 0000000000..fe8a4f8b2a
--- /dev/null
+++ b/layer/filestore_unix.go
@@ -0,0 +1,13 @@
+// +build !windows
+
+package layer
+
+// SetPlatform writes the "platform" file to the layer filestore
+func (fm *fileMetadataTransaction) SetPlatform(platform Platform) error {
+	return nil
+}
+
+// GetPlatform reads the "platform" file from the layer filestore
+func (fms *fileMetadataStore) GetPlatform(layer ChainID) (Platform, error) {
+	return "", nil
+}
diff --git a/layer/filestore_windows.go b/layer/filestore_windows.go
new file mode 100644
index 0000000000..066456d8d2
--- /dev/null
+++ b/layer/filestore_windows.go
@@ -0,0 +1,35 @@
+package layer
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+)
+
+// SetPlatform writes the "platform" file to the layer filestore
+func (fm *fileMetadataTransaction) SetPlatform(platform Platform) error {
+	if platform == "" {
+		return nil
+	}
+	return fm.ws.WriteFile("platform", []byte(platform), 0644)
+}
+
+// GetPlatform reads the "platform" file from the layer filestore
+func (fms *fileMetadataStore) GetPlatform(layer ChainID) (Platform, error) {
+	contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "platform"))
+	if err != nil {
+		// For backwards compatibility, the platform file may not exist. Default to "windows" if missing.
+		if os.IsNotExist(err) {
+			return "windows", nil
+		}
+		return "", err
+	}
+	content := strings.TrimSpace(string(contentBytes))
+
+	if content != "windows" && content != "linux" {
+		return "", fmt.Errorf("invalid platform value: %s", content)
+	}
+
+	return Platform(content), nil
+}
diff --git a/layer/layer.go b/layer/layer.go
index 7b993ee4ad..2e5dcf9c85 100644
--- a/layer/layer.go
+++ b/layer/layer.go
@@ -64,6 +64,14 @@ func (id ChainID) String() string {
 	return string(id)
 }
 
+// Platform is the platform of a layer
+type Platform string
+
+// String returns a string rendition of layers target platform
+func (id Platform) String() string {
+	return string(id)
+}
+
 // DiffID is the hash of an individual layer tar.
 type DiffID digest.Digest
 
@@ -99,6 +107,9 @@ type Layer interface {
 	// Parent returns the next layer in the layer chain.
 	Parent() Layer
 
+	// Platform returns the platform of the layer
+	Platform() Platform
+
 	// Size returns the size of the entire layer chain. The size
 	// is calculated from the total size of all files in the layers.
 	Size() (int64, error)
@@ -208,6 +219,7 @@ type MetadataTransaction interface {
 	SetDiffID(DiffID) error
 	SetCacheID(string) error
 	SetDescriptor(distribution.Descriptor) error
+	SetPlatform(Platform) error
 	TarSplitWriter(compressInput bool) (io.WriteCloser, error)
 
 	Commit(ChainID) error
@@ -228,6 +240,7 @@ type MetadataStore interface {
 	GetDiffID(ChainID) (DiffID, error)
 	GetCacheID(ChainID) (string, error)
 	GetDescriptor(ChainID) (distribution.Descriptor, error)
+	GetPlatform(ChainID) (Platform, error)
 	TarSplitReader(ChainID) (io.ReadCloser, error)
 
 	SetMountID(string, string) error
diff --git a/layer/layer_store.go b/layer/layer_store.go
index 25861c6669..4e3594ed60 100644
--- a/layer/layer_store.go
+++ b/layer/layer_store.go
@@ -144,6 +144,11 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
 		return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err)
 	}
 
+	platform, err := ls.store.GetPlatform(layer)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get platform for %s: %s", layer, err)
+	}
+
 	cl = &roLayer{
 		chainID:    layer,
 		diffID:     diff,
@@ -152,6 +157,7 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
 		layerStore: ls,
 		references: map[Layer]struct{}{},
 		descriptor: descriptor,
+		platform:   platform,
 	}
 
 	if parent != "" {
diff --git a/layer/ro_layer.go b/layer/ro_layer.go
index 8b4cf8f0de..e03d78b4db 100644
--- a/layer/ro_layer.go
+++ b/layer/ro_layer.go
@@ -16,6 +16,7 @@ type roLayer struct {
 	size       int64
 	layerStore *layerStore
 	descriptor distribution.Descriptor
+	platform   Platform
 
 	referenceCount int
 	references     map[Layer]struct{}
@@ -142,6 +143,9 @@ func storeLayer(tx MetadataTransaction, layer *roLayer) error {
 			return err
 		}
 	}
+	if err := tx.SetPlatform(layer.platform); err != nil {
+		return err
+	}
 
 	return nil
 }
diff --git a/layer/ro_layer_unix.go b/layer/ro_layer_unix.go
new file mode 100644
index 0000000000..1b36856f9e
--- /dev/null
+++ b/layer/ro_layer_unix.go
@@ -0,0 +1,7 @@
+// +build !windows
+
+package layer
+
+func (rl *roLayer) Platform() Platform {
+	return ""
+}
diff --git a/layer/ro_layer_windows.go b/layer/ro_layer_windows.go
index 32bd7182a3..6679bdfe8f 100644
--- a/layer/ro_layer_windows.go
+++ b/layer/ro_layer_windows.go
@@ -7,3 +7,10 @@ var _ distribution.Describable = &roLayer{}
 func (rl *roLayer) Descriptor() distribution.Descriptor {
 	return rl.descriptor
 }
+
+func (rl *roLayer) Platform() Platform {
+	if rl.platform == "" {
+		return "windows"
+	}
+	return rl.platform
+}
diff --git a/migrate/v1/migratev1_test.go b/migrate/v1/migratev1_test.go
index 55898f12b4..852f5891fb 100644
--- a/migrate/v1/migratev1_test.go
+++ b/migrate/v1/migratev1_test.go
@@ -433,6 +433,10 @@ func (l *mockLayer) DiffSize() (int64, error) {
 	return 0, nil
 }
 
+func (l *mockLayer) Platform() layer.Platform {
+	return ""
+}
+
 func (l *mockLayer) Metadata() (map[string]string, error) {
 	return nil, nil
 }