cmd/swarm: manifest update: update default entry for non-encrypted uploads

This commit is contained in:
Janos Guljas 2018-07-30 18:09:54 +02:00
parent d84dc3e2fc
commit 46d5ba723d
2 changed files with 162 additions and 8 deletions

View file

@ -19,6 +19,7 @@ package main
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
@ -91,7 +92,13 @@ func manifestUpdate(ctx *cli.Context) {
utils.Fatalf("Too many entries in manifest %s", hash) utils.Fatalf("Too many entries in manifest %s", hash)
} }
newManifest := updateEntryInManifest(client, mhash, path, m.Entries[0]) newManifest, _, defaultEntryUpdated := updateEntryInManifest(client, mhash, path, m.Entries[0], true)
if defaultEntryUpdated {
// Print informational message to stderr
// allowing the user to get the new manifest hash from stdout
// without the need to parse the complete output.
fmt.Fprintln(os.Stderr, "Manifest default entry is updated, too")
}
fmt.Println(newManifest) fmt.Println(newManifest)
} }
@ -165,7 +172,13 @@ func addEntryToManifest(client *swarm.Client, mhash, path string, entry api.Mani
return newManifestHash return newManifestHash
} }
func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry) string { // updateEntryInManifest updates an existing entry o path with a new one in the manifest with provided mhash
// finding the path recursively through all nested manifests. Argument isRoot is used for default
// entry update detection. If the updated entry has the same hash as the default entry, then the
// default entry in root manifest will be updated too.
// Returned values are the new manifest hash, hash of the entry that was replaced by the new entry and
// a a bool that is true if default entry is updated.
func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry, isRoot bool) (newManifestHash, oldHash string, defaultEntryUpdated bool) {
var ( var (
newEntry = api.ManifestEntry{} newEntry = api.ManifestEntry{}
longestPathEntry = api.ManifestEntry{} longestPathEntry = api.ManifestEntry{}
@ -180,6 +193,9 @@ func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.M
for _, e := range mroot.Entries { for _, e := range mroot.Entries {
if path == e.Path { if path == e.Path {
newEntry = e newEntry = e
// keep the reference of the hash of the entry that should be replaced
// for default entry detection
oldHash = e.Hash
} else { } else {
if e.ContentType == api.ManifestType { if e.ContentType == api.ManifestType {
prfxlen := strings.HasPrefix(path, e.Path) prfxlen := strings.HasPrefix(path, e.Path)
@ -197,7 +213,8 @@ func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.M
if longestPathEntry.Path != "" { if longestPathEntry.Path != "" {
// Load the child Manifest add the entry there // Load the child Manifest add the entry there
newPath := path[len(longestPathEntry.Path):] newPath := path[len(longestPathEntry.Path):]
newHash := updateEntryInManifest(client, longestPathEntry.Hash, newPath, entry) var newHash string
newHash, oldHash, _ = updateEntryInManifest(client, longestPathEntry.Hash, newPath, entry, false)
// Replace the hash for parent Manifests // Replace the hash for parent Manifests
newMRoot := &api.Manifest{} newMRoot := &api.Manifest{}
@ -211,13 +228,19 @@ func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.M
mroot = newMRoot mroot = newMRoot
} }
if newEntry.Path != "" { // update the manifest if the new entry is found and
// check if default entry should be updated
if newEntry.Path != "" || isRoot {
// Replace the hash for leaf Manifest // Replace the hash for leaf Manifest
newMRoot := &api.Manifest{} newMRoot := &api.Manifest{}
for _, e := range mroot.Entries { for _, e := range mroot.Entries {
if newEntry.Path == e.Path { if newEntry.Path == e.Path {
entry.Path = e.Path entry.Path = e.Path
newMRoot.Entries = append(newMRoot.Entries, entry) newMRoot.Entries = append(newMRoot.Entries, entry)
} else if isRoot && e.Path == "" && e.Hash == oldHash {
entry.Path = e.Path
newMRoot.Entries = append(newMRoot.Entries, entry)
defaultEntryUpdated = true
} else { } else {
newMRoot.Entries = append(newMRoot.Entries, e) newMRoot.Entries = append(newMRoot.Entries, e)
} }
@ -225,11 +248,11 @@ func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.M
mroot = newMRoot mroot = newMRoot
} }
newManifestHash, err := client.UploadManifest(mroot, isEncrypted) newManifestHash, err = client.UploadManifest(mroot, isEncrypted)
if err != nil { if err != nil {
utils.Fatalf("Manifest upload failed: %v", err) utils.Fatalf("Manifest upload failed: %v", err)
} }
return newManifestHash return newManifestHash, oldHash, defaultEntryUpdated
} }
func removeEntryFromManifest(client *swarm.Client, mhash, path string) string { func removeEntryFromManifest(client *swarm.Client, mhash, path string) string {

View file

@ -226,7 +226,7 @@ func testManifestChange(t *testing.T, encrypt bool) {
t.Fatal(err) t.Fatal(err)
} }
humansManifestHash := runSwarmExpectHash(t, indexManifestHash := runSwarmExpectHash(t,
"--bzzapi", "--bzzapi",
cluster.Nodes[0].URL, cluster.Nodes[0].URL,
"up", "up",
@ -240,7 +240,7 @@ func testManifestChange(t *testing.T, encrypt bool) {
"update", "update",
origManifestHash, origManifestHash,
"index.html", "index.html",
humansManifestHash, indexManifestHash,
) )
checkHashLength(t, newManifestHash, encrypt) checkHashLength(t, newManifestHash, encrypt)
@ -269,6 +269,14 @@ func testManifestChange(t *testing.T, encrypt bool) {
} }
checkFile(t, client, newManifestHash, "index.html", indexData) checkFile(t, client, newManifestHash, "index.html", indexData)
// check default entry change
// TODO: encrypted uploads generate manifest with default entry
// with different hash compared to the same file that is used
// for default entry.
if !encrypt {
checkFile(t, client, newManifestHash, "", indexData)
}
}) })
// upload a new file and use its manifest to change the file it the original manifest, // upload a new file and use its manifest to change the file it the original manifest,
@ -394,6 +402,129 @@ func testManifestChange(t *testing.T, encrypt bool) {
}) })
} }
// TestNestedDefaultEntryUpdate tests if the default entry is updated
// if the file in nested manifest used for it is also updated.
// TODO: create a similar test for encrypted uploads when it becomes
// functional.
func TestNestedDefaultEntryUpdate(t *testing.T) {
testNestedDefaultEntryUpdate(t, false)
}
func testNestedDefaultEntryUpdate(t *testing.T, encrypt bool) {
t.Parallel()
cluster := newTestCluster(t, 1)
defer cluster.Shutdown()
tmp, err := ioutil.TempDir("", "swarm-manifest-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
origDir := filepath.Join(tmp, "orig")
if err := os.Mkdir(origDir, 0777); err != nil {
t.Fatal(err)
}
indexData := []byte("<h1>Test</h1>")
indexDataFilename := filepath.Join(origDir, "index.html")
err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
if err != nil {
t.Fatal(err)
}
// Add another file with common prefix as the default entry to test updates of
// default entry with nested manifests.
err = ioutil.WriteFile(filepath.Join(origDir, "index.txt"), []byte("Test"), 0666)
if err != nil {
t.Fatal(err)
}
args := []string{
"--bzzapi",
cluster.Nodes[0].URL,
"--recursive",
"--defaultpath",
indexDataFilename,
"up",
origDir,
}
if encrypt {
args = append(args, "--encrypt")
}
origManifestHash := runSwarmExpectHash(t, args...)
checkHashLength(t, origManifestHash, encrypt)
client := swarm.NewClient(cluster.Nodes[0].URL)
newIndexData := []byte("<h1>Ethereum Swarm</h1>")
newIndexDataFilename := filepath.Join(tmp, "index.html")
err = ioutil.WriteFile(newIndexDataFilename, newIndexData, 0666)
if err != nil {
t.Fatal(err)
}
newIndexManifestHash := runSwarmExpectHash(t,
"--bzzapi",
cluster.Nodes[0].URL,
"up",
newIndexDataFilename,
)
newManifestHash := runSwarmExpectHash(t,
"--bzzapi",
cluster.Nodes[0].URL,
"manifest",
"update",
origManifestHash,
"index.html",
newIndexManifestHash,
)
checkHashLength(t, newManifestHash, encrypt)
newManifest := downloadManifest(t, client, newManifestHash, encrypt)
var found bool
for _, e := range newManifest.Entries {
if e.Path == "index." {
found = true
newManifest = downloadManifest(t, client, e.Hash, encrypt)
break
}
}
if !found {
t.Fatal("no index. path in new manifest")
}
found = false
for _, e := range newManifest.Entries {
if e.Path == "html" {
found = true
if e.Size != int64(len(newIndexData)) {
t.Errorf("expected index.html size %v, got %v", len(newIndexData), e.Size)
}
if e.ModTime.IsZero() {
t.Errorf("got zero mod time for index.html")
}
ct := "text/html; charset=utf-8"
if e.ContentType != ct {
t.Errorf("expected content type %q, got %q", ct, e.ContentType)
}
break
}
}
if !found {
t.Fatal("no html in new manifest")
}
checkFile(t, client, newManifestHash, "index.html", newIndexData)
// check default entry change
checkFile(t, client, newManifestHash, "", newIndexData)
}
func runSwarmExpectHash(t *testing.T, args ...string) (hash string) { func runSwarmExpectHash(t *testing.T, args ...string) (hash string) {
t.Helper() t.Helper()
hashRegexp := `[a-f\d]{64,128}` hashRegexp := `[a-f\d]{64,128}`