mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
cmd/swarm: fix manifest subcommands and add tests
This commit is contained in:
parent
10780e8a00
commit
d84dc3e2fc
3 changed files with 538 additions and 125 deletions
|
|
@ -317,23 +317,23 @@ Downloads a swarm bzz uri to the given dir. When no dir is provided, working dir
|
||||||
Description: "Updates a MANIFEST by adding/removing/updating the hash of a path.\nCOMMAND could be: add, update, remove",
|
Description: "Updates a MANIFEST by adding/removing/updating the hash of a path.\nCOMMAND could be: add, update, remove",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
{
|
{
|
||||||
Action: add,
|
Action: manifestAdd,
|
||||||
CustomHelpTemplate: helpTemplate,
|
CustomHelpTemplate: helpTemplate,
|
||||||
Name: "add",
|
Name: "add",
|
||||||
Usage: "add a new path to the manifest",
|
Usage: "add a new path to the manifest",
|
||||||
ArgsUsage: "<MANIFEST> <path> <hash> [<content-type>]",
|
ArgsUsage: "<MANIFEST> <path> <hash>",
|
||||||
Description: "Adds a new path to the manifest",
|
Description: "Adds a new path to the manifest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: update,
|
Action: manifestUpdate,
|
||||||
CustomHelpTemplate: helpTemplate,
|
CustomHelpTemplate: helpTemplate,
|
||||||
Name: "update",
|
Name: "update",
|
||||||
Usage: "update the hash for an already existing path in the manifest",
|
Usage: "update the hash for an already existing path in the manifest",
|
||||||
ArgsUsage: "<MANIFEST> <path> <newhash> [<newcontent-type>]",
|
ArgsUsage: "<MANIFEST> <path> <newhash>",
|
||||||
Description: "Update the hash for an already existing path in the manifest",
|
Description: "Update the hash for an already existing path in the manifest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Action: remove,
|
Action: manifestRemove,
|
||||||
CustomHelpTemplate: helpTemplate,
|
CustomHelpTemplate: helpTemplate,
|
||||||
Name: "remove",
|
Name: "remove",
|
||||||
Usage: "removes a path from the manifest",
|
Usage: "removes a path from the manifest",
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
|
@ -30,127 +27,112 @@ import (
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const bzzManifestJSON = "application/bzz-manifest+json"
|
// manifestAdd adds a new entry to the manifest at the given path.
|
||||||
|
// New entry hash, the last argument, must be the hash of a manifest
|
||||||
func add(ctx *cli.Context) {
|
// with only one entry, which meta-data will be added to the original manifest.
|
||||||
|
// On success, this function will print new (updated) manifest's hash.
|
||||||
|
func manifestAdd(ctx *cli.Context) {
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) < 3 {
|
if len(args) != 3 {
|
||||||
utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH> [<content-type>]")
|
utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mhash = args[0]
|
mhash = args[0]
|
||||||
path = args[1]
|
path = args[1]
|
||||||
hash = args[2]
|
hash = args[2]
|
||||||
|
|
||||||
ctype string
|
|
||||||
wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
|
|
||||||
mroot api.Manifest
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(args) > 3 {
|
bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||||
ctype = args[3]
|
client := swarm.NewClient(bzzapi)
|
||||||
} else {
|
|
||||||
ctype = mime.TypeByExtension(filepath.Ext(path))
|
m, _, err := client.DownloadManifest(hash)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error downloading manifest to add: %v", err)
|
||||||
|
}
|
||||||
|
l := len(m.Entries)
|
||||||
|
if l == 0 {
|
||||||
|
utils.Fatalf("No entries in manifest %s", hash)
|
||||||
|
} else if l > 1 {
|
||||||
|
utils.Fatalf("Too many entries in manifest %s", hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
newManifest := addEntryToManifest(ctx, mhash, path, hash, ctype)
|
newManifest := addEntryToManifest(client, mhash, path, m.Entries[0])
|
||||||
fmt.Println(newManifest)
|
fmt.Println(newManifest)
|
||||||
|
|
||||||
if !wantManifest {
|
|
||||||
// Print the manifest. This is the only output to stdout.
|
|
||||||
mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
|
|
||||||
fmt.Println(string(mrootJSON))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(ctx *cli.Context) {
|
// manifestUpdate replaces an existing entry of the manifest at the given path.
|
||||||
|
// New entry hash, the last argument, must be the hash of a manifest
|
||||||
|
// with only one entry, which meta-data will be added to the original manifest.
|
||||||
|
// On success, this function will print hash of the updated manifest.
|
||||||
|
func manifestUpdate(ctx *cli.Context) {
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) < 3 {
|
if len(args) != 3 {
|
||||||
utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH>")
|
utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mhash = args[0]
|
mhash = args[0]
|
||||||
path = args[1]
|
path = args[1]
|
||||||
hash = args[2]
|
hash = args[2]
|
||||||
|
|
||||||
ctype string
|
|
||||||
wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
|
|
||||||
mroot api.Manifest
|
|
||||||
)
|
)
|
||||||
if len(args) > 3 {
|
|
||||||
ctype = args[3]
|
bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||||
} else {
|
client := swarm.NewClient(bzzapi)
|
||||||
ctype = mime.TypeByExtension(filepath.Ext(path))
|
|
||||||
|
m, _, err := client.DownloadManifest(hash)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error downloading manifest to update: %v", err)
|
||||||
|
}
|
||||||
|
l := len(m.Entries)
|
||||||
|
if l == 0 {
|
||||||
|
utils.Fatalf("No entries in manifest %s", hash)
|
||||||
|
} else if l > 1 {
|
||||||
|
utils.Fatalf("Too many entries in manifest %s", hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
newManifest := updateEntryInManifest(ctx, mhash, path, hash, ctype)
|
newManifest := updateEntryInManifest(client, mhash, path, m.Entries[0])
|
||||||
fmt.Println(newManifest)
|
fmt.Println(newManifest)
|
||||||
|
|
||||||
if !wantManifest {
|
|
||||||
// Print the manifest. This is the only output to stdout.
|
|
||||||
mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
|
|
||||||
fmt.Println(string(mrootJSON))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove(ctx *cli.Context) {
|
// manifestRemove removes an existing entry of the manifest at the given path.
|
||||||
|
// On success, this function will print hash of the manifest which does not
|
||||||
|
// contain the path.
|
||||||
|
func manifestRemove(ctx *cli.Context) {
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) < 2 {
|
if len(args) != 2 {
|
||||||
utils.Fatalf("Need at least two arguments <MHASH> <path>")
|
utils.Fatalf("Need exactly two arguments <MHASH> <path>")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mhash = args[0]
|
mhash = args[0]
|
||||||
path = args[1]
|
path = args[1]
|
||||||
|
|
||||||
wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
|
|
||||||
mroot api.Manifest
|
|
||||||
)
|
)
|
||||||
|
|
||||||
newManifest := removeEntryFromManifest(ctx, mhash, path)
|
bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||||
|
client := swarm.NewClient(bzzapi)
|
||||||
|
|
||||||
|
newManifest := removeEntryFromManifest(client, mhash, path)
|
||||||
fmt.Println(newManifest)
|
fmt.Println(newManifest)
|
||||||
|
|
||||||
if !wantManifest {
|
|
||||||
// Print the manifest. This is the only output to stdout.
|
|
||||||
mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
|
|
||||||
fmt.Println(string(mrootJSON))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
|
func addEntryToManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry) string {
|
||||||
|
var longestPathEntry = api.ManifestEntry{}
|
||||||
var (
|
|
||||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
|
||||||
client = swarm.NewClient(bzzapi)
|
|
||||||
longestPathEntry = api.ManifestEntry{}
|
|
||||||
)
|
|
||||||
|
|
||||||
mroot, isEncrypted, err := client.DownloadManifest(mhash)
|
mroot, isEncrypted, err := client.DownloadManifest(mhash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Manifest download failed: %v", err)
|
utils.Fatalf("Manifest download failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check if the "hash" to add is valid and present in swarm
|
|
||||||
_, _, err = client.DownloadManifest(hash)
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Hash to add is not present: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if we path is in this Manifest or do we have to dig deeper
|
// See if we path is in this Manifest or do we have to dig deeper
|
||||||
for _, entry := range mroot.Entries {
|
for _, e := range mroot.Entries {
|
||||||
if path == entry.Path {
|
if path == e.Path {
|
||||||
utils.Fatalf("Path %s already present, not adding anything", path)
|
utils.Fatalf("Path %s already present, not adding anything", path)
|
||||||
} else {
|
} else {
|
||||||
if entry.ContentType == bzzManifestJSON {
|
if e.ContentType == api.ManifestType {
|
||||||
prfxlen := strings.HasPrefix(path, entry.Path)
|
prfxlen := strings.HasPrefix(path, e.Path)
|
||||||
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
||||||
longestPathEntry = entry
|
longestPathEntry = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,25 +141,21 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin
|
||||||
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 := addEntryToManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
newHash := addEntryToManifest(client, longestPathEntry.Hash, newPath, entry)
|
||||||
|
|
||||||
// Replace the hash for parent Manifests
|
// Replace the hash for parent Manifests
|
||||||
newMRoot := &api.Manifest{}
|
newMRoot := &api.Manifest{}
|
||||||
for _, entry := range mroot.Entries {
|
for _, e := range mroot.Entries {
|
||||||
if longestPathEntry.Path == entry.Path {
|
if longestPathEntry.Path == e.Path {
|
||||||
entry.Hash = newHash
|
e.Hash = newHash
|
||||||
}
|
}
|
||||||
newMRoot.Entries = append(newMRoot.Entries, entry)
|
newMRoot.Entries = append(newMRoot.Entries, e)
|
||||||
}
|
}
|
||||||
mroot = newMRoot
|
mroot = newMRoot
|
||||||
} else {
|
} else {
|
||||||
// Add the entry in the leaf Manifest
|
// Add the entry in the leaf Manifest
|
||||||
newEntry := api.ManifestEntry{
|
entry.Path = path
|
||||||
Hash: hash,
|
mroot.Entries = append(mroot.Entries, entry)
|
||||||
Path: path,
|
|
||||||
ContentType: ctype,
|
|
||||||
}
|
|
||||||
mroot.Entries = append(mroot.Entries, newEntry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
|
newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
|
||||||
|
|
@ -185,14 +163,10 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin
|
||||||
utils.Fatalf("Manifest upload failed: %v", err)
|
utils.Fatalf("Manifest upload failed: %v", err)
|
||||||
}
|
}
|
||||||
return newManifestHash
|
return newManifestHash
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
|
func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry) string {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
|
||||||
client = swarm.NewClient(bzzapi)
|
|
||||||
newEntry = api.ManifestEntry{}
|
newEntry = api.ManifestEntry{}
|
||||||
longestPathEntry = api.ManifestEntry{}
|
longestPathEntry = api.ManifestEntry{}
|
||||||
)
|
)
|
||||||
|
|
@ -202,17 +176,15 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
||||||
utils.Fatalf("Manifest download failed: %v", err)
|
utils.Fatalf("Manifest download failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check if the "hash" with which to update is valid and present in swarm
|
|
||||||
|
|
||||||
// See if we path is in this Manifest or do we have to dig deeper
|
// See if we path is in this Manifest or do we have to dig deeper
|
||||||
for _, entry := range mroot.Entries {
|
for _, e := range mroot.Entries {
|
||||||
if path == entry.Path {
|
if path == e.Path {
|
||||||
newEntry = entry
|
newEntry = e
|
||||||
} else {
|
} else {
|
||||||
if entry.ContentType == bzzManifestJSON {
|
if e.ContentType == api.ManifestType {
|
||||||
prfxlen := strings.HasPrefix(path, entry.Path)
|
prfxlen := strings.HasPrefix(path, e.Path)
|
||||||
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
||||||
longestPathEntry = entry
|
longestPathEntry = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,15 +197,15 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
||||||
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(ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
newHash := updateEntryInManifest(client, longestPathEntry.Hash, newPath, entry)
|
||||||
|
|
||||||
// Replace the hash for parent Manifests
|
// Replace the hash for parent Manifests
|
||||||
newMRoot := &api.Manifest{}
|
newMRoot := &api.Manifest{}
|
||||||
for _, entry := range mroot.Entries {
|
for _, e := range mroot.Entries {
|
||||||
if longestPathEntry.Path == entry.Path {
|
if longestPathEntry.Path == e.Path {
|
||||||
entry.Hash = newHash
|
e.Hash = newHash
|
||||||
}
|
}
|
||||||
newMRoot.Entries = append(newMRoot.Entries, entry)
|
newMRoot.Entries = append(newMRoot.Entries, e)
|
||||||
|
|
||||||
}
|
}
|
||||||
mroot = newMRoot
|
mroot = newMRoot
|
||||||
|
|
@ -242,16 +214,12 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
||||||
if newEntry.Path != "" {
|
if newEntry.Path != "" {
|
||||||
// Replace the hash for leaf Manifest
|
// Replace the hash for leaf Manifest
|
||||||
newMRoot := &api.Manifest{}
|
newMRoot := &api.Manifest{}
|
||||||
for _, entry := range mroot.Entries {
|
for _, e := range mroot.Entries {
|
||||||
if newEntry.Path == entry.Path {
|
if newEntry.Path == e.Path {
|
||||||
myEntry := api.ManifestEntry{
|
entry.Path = e.Path
|
||||||
Hash: hash,
|
|
||||||
Path: entry.Path,
|
|
||||||
ContentType: ctype,
|
|
||||||
}
|
|
||||||
newMRoot.Entries = append(newMRoot.Entries, myEntry)
|
|
||||||
} else {
|
|
||||||
newMRoot.Entries = append(newMRoot.Entries, entry)
|
newMRoot.Entries = append(newMRoot.Entries, entry)
|
||||||
|
} else {
|
||||||
|
newMRoot.Entries = append(newMRoot.Entries, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mroot = newMRoot
|
mroot = newMRoot
|
||||||
|
|
@ -264,11 +232,8 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
||||||
return newManifestHash
|
return newManifestHash
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
func removeEntryFromManifest(client *swarm.Client, mhash, path string) string {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
|
||||||
client = swarm.NewClient(bzzapi)
|
|
||||||
entryToRemove = api.ManifestEntry{}
|
entryToRemove = api.ManifestEntry{}
|
||||||
longestPathEntry = api.ManifestEntry{}
|
longestPathEntry = api.ManifestEntry{}
|
||||||
)
|
)
|
||||||
|
|
@ -283,7 +248,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
||||||
if path == entry.Path {
|
if path == entry.Path {
|
||||||
entryToRemove = entry
|
entryToRemove = entry
|
||||||
} else {
|
} else {
|
||||||
if entry.ContentType == bzzManifestJSON {
|
if entry.ContentType == api.ManifestType {
|
||||||
prfxlen := strings.HasPrefix(path, entry.Path)
|
prfxlen := strings.HasPrefix(path, entry.Path)
|
||||||
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
||||||
longestPathEntry = entry
|
longestPathEntry = entry
|
||||||
|
|
@ -299,7 +264,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
||||||
if longestPathEntry.Path != "" {
|
if longestPathEntry.Path != "" {
|
||||||
// Load the child Manifest remove the entry there
|
// Load the child Manifest remove the entry there
|
||||||
newPath := path[len(longestPathEntry.Path):]
|
newPath := path[len(longestPathEntry.Path):]
|
||||||
newHash := removeEntryFromManifest(ctx, longestPathEntry.Hash, newPath)
|
newHash := removeEntryFromManifest(client, longestPathEntry.Hash, newPath)
|
||||||
|
|
||||||
// Replace the hash for parent Manifests
|
// Replace the hash for parent Manifests
|
||||||
newMRoot := &api.Manifest{}
|
newMRoot := &api.Manifest{}
|
||||||
|
|
|
||||||
448
cmd/swarm/manifest_test.go
Normal file
448
cmd/swarm/manifest_test.go
Normal file
|
|
@ -0,0 +1,448 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/api"
|
||||||
|
swarm "github.com/ethereum/go-ethereum/swarm/api/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestManifestChange tests manifest add, update and remove
|
||||||
|
// cli commands without encryption.
|
||||||
|
func TestManifestChange(t *testing.T) {
|
||||||
|
testManifestChange(t, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestManifestChange tests manifest add, update and remove
|
||||||
|
// cli commands with encryption enabled.
|
||||||
|
func TestManifestChangeEncrypted(t *testing.T) {
|
||||||
|
testManifestChange(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testManifestChange performs cli commands:
|
||||||
|
// - manifest add
|
||||||
|
// - manifest update
|
||||||
|
// - manifest remove
|
||||||
|
// on a manifest, testing the functionality of this
|
||||||
|
// comands on paths that are in root manifest or a nested one.
|
||||||
|
// Argument encrypt controls whether to use encryption or not.
|
||||||
|
func testManifestChange(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
indexDataFilename := filepath.Join(origDir, "index.html")
|
||||||
|
err = ioutil.WriteFile(indexDataFilename, []byte("<h1>Test</h1>"), 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Files paths robots.txt and robots.html share the same prefix "robots."
|
||||||
|
// which will result a manifest with a nested manifest under path "robots.".
|
||||||
|
// This will allow testing manifest changes on both root and nested manifest.
|
||||||
|
err = ioutil.WriteFile(filepath.Join(origDir, "robots.txt"), []byte("Disallow: /"), 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(origDir, "robots.html"), []byte("<strong>No Robots Allowed</strong>"), 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(origDir, "mutants.txt"), []byte("Frank\nMarcus"), 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)
|
||||||
|
|
||||||
|
// upload a new file and use its manifest to add it the original manifest.
|
||||||
|
t.Run("add", func(t *testing.T) {
|
||||||
|
humansData := []byte("Ann\nBob")
|
||||||
|
humansDataFilename := filepath.Join(tmp, "humans.txt")
|
||||||
|
err = ioutil.WriteFile(humansDataFilename, humansData, 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
humansManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"up",
|
||||||
|
humansDataFilename,
|
||||||
|
)
|
||||||
|
|
||||||
|
newManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"manifest",
|
||||||
|
"add",
|
||||||
|
origManifestHash,
|
||||||
|
"humans.txt",
|
||||||
|
humansManifestHash,
|
||||||
|
)
|
||||||
|
|
||||||
|
checkHashLength(t, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
newManifest := downloadManifest(t, client, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
for _, e := range newManifest.Entries {
|
||||||
|
if e.Path == "humans.txt" {
|
||||||
|
found = true
|
||||||
|
if e.Size != int64(len(humansData)) {
|
||||||
|
t.Errorf("expected humans.txt size %v, got %v", len(humansData), e.Size)
|
||||||
|
}
|
||||||
|
if e.ModTime.IsZero() {
|
||||||
|
t.Errorf("got zero mod time for humans.txt")
|
||||||
|
}
|
||||||
|
ct := "text/plain; charset=utf-8"
|
||||||
|
if e.ContentType != ct {
|
||||||
|
t.Errorf("expected content type %q, got %q", ct, e.ContentType)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatal("no humans.txt in new manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFile(t, client, newManifestHash, "humans.txt", humansData)
|
||||||
|
})
|
||||||
|
|
||||||
|
// upload a new file and use its manifest to add it the original manifest,
|
||||||
|
// but ensure that the file will be in the nested manifest of the original one.
|
||||||
|
t.Run("add nested", func(t *testing.T) {
|
||||||
|
robotsData := []byte(`{"disallow": "/"}`)
|
||||||
|
robotsDataFilename := filepath.Join(tmp, "robots.json")
|
||||||
|
err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
robotsManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"up",
|
||||||
|
robotsDataFilename,
|
||||||
|
)
|
||||||
|
|
||||||
|
newManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"manifest",
|
||||||
|
"add",
|
||||||
|
origManifestHash,
|
||||||
|
"robots.json",
|
||||||
|
robotsManifestHash,
|
||||||
|
)
|
||||||
|
|
||||||
|
checkHashLength(t, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
newManifest := downloadManifest(t, client, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
loop:
|
||||||
|
for _, e := range newManifest.Entries {
|
||||||
|
if e.Path == "robots." {
|
||||||
|
nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
|
||||||
|
for _, e := range nestedManifest.Entries {
|
||||||
|
if e.Path == "json" {
|
||||||
|
found = true
|
||||||
|
if e.Size != int64(len(robotsData)) {
|
||||||
|
t.Errorf("expected robots.json size %v, got %v", len(robotsData), e.Size)
|
||||||
|
}
|
||||||
|
if e.ModTime.IsZero() {
|
||||||
|
t.Errorf("got zero mod time for robots.json")
|
||||||
|
}
|
||||||
|
ct := "application/json"
|
||||||
|
if e.ContentType != ct {
|
||||||
|
t.Errorf("expected content type %q, got %q", ct, e.ContentType)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatal("no robots.json in new manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFile(t, client, newManifestHash, "robots.json", robotsData)
|
||||||
|
})
|
||||||
|
|
||||||
|
// upload a new file and use its manifest to change the file it the original manifest.
|
||||||
|
t.Run("update", func(t *testing.T) {
|
||||||
|
indexData := []byte("<h1>Ethereum Swarm</h1>")
|
||||||
|
indexDataFilename := filepath.Join(tmp, "index.html")
|
||||||
|
err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
humansManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"up",
|
||||||
|
indexDataFilename,
|
||||||
|
)
|
||||||
|
|
||||||
|
newManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"manifest",
|
||||||
|
"update",
|
||||||
|
origManifestHash,
|
||||||
|
"index.html",
|
||||||
|
humansManifestHash,
|
||||||
|
)
|
||||||
|
|
||||||
|
checkHashLength(t, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
newManifest := downloadManifest(t, client, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
for _, e := range newManifest.Entries {
|
||||||
|
if e.Path == "index.html" {
|
||||||
|
found = true
|
||||||
|
if e.Size != int64(len(indexData)) {
|
||||||
|
t.Errorf("expected index.html size %v, got %v", len(indexData), 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 index.html in new manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFile(t, client, newManifestHash, "index.html", indexData)
|
||||||
|
})
|
||||||
|
|
||||||
|
// upload a new file and use its manifest to change the file it the original manifest,
|
||||||
|
// but ensure that the file is in the nested manifest of the original one.
|
||||||
|
t.Run("update nested", func(t *testing.T) {
|
||||||
|
robotsData := []byte(`<string>Only humans allowed!!!</strong>`)
|
||||||
|
robotsDataFilename := filepath.Join(tmp, "robots.html")
|
||||||
|
err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
humansManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"up",
|
||||||
|
robotsDataFilename,
|
||||||
|
)
|
||||||
|
|
||||||
|
newManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"manifest",
|
||||||
|
"update",
|
||||||
|
origManifestHash,
|
||||||
|
"robots.html",
|
||||||
|
humansManifestHash,
|
||||||
|
)
|
||||||
|
|
||||||
|
checkHashLength(t, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
newManifest := downloadManifest(t, client, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
loop:
|
||||||
|
for _, e := range newManifest.Entries {
|
||||||
|
if e.Path == "robots." {
|
||||||
|
nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
|
||||||
|
for _, e := range nestedManifest.Entries {
|
||||||
|
if e.Path == "html" {
|
||||||
|
found = true
|
||||||
|
if e.Size != int64(len(robotsData)) {
|
||||||
|
t.Errorf("expected robots.html size %v, got %v", len(robotsData), e.Size)
|
||||||
|
}
|
||||||
|
if e.ModTime.IsZero() {
|
||||||
|
t.Errorf("got zero mod time for robots.html")
|
||||||
|
}
|
||||||
|
ct := "text/html; charset=utf-8"
|
||||||
|
if e.ContentType != ct {
|
||||||
|
t.Errorf("expected content type %q, got %q", ct, e.ContentType)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatal("no robots.html in new manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFile(t, client, newManifestHash, "robots.html", robotsData)
|
||||||
|
})
|
||||||
|
|
||||||
|
// remove a file from the manifest.
|
||||||
|
t.Run("remove", func(t *testing.T) {
|
||||||
|
newManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"manifest",
|
||||||
|
"remove",
|
||||||
|
origManifestHash,
|
||||||
|
"mutants.txt",
|
||||||
|
)
|
||||||
|
|
||||||
|
checkHashLength(t, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
newManifest := downloadManifest(t, client, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
for _, e := range newManifest.Entries {
|
||||||
|
if e.Path == "mutants.txt" {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
t.Fatal("mutants.txt is not removed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// remove a file from the manifest, but ensure that the file is in
|
||||||
|
// the nested manifest of the original one.
|
||||||
|
t.Run("remove nested", func(t *testing.T) {
|
||||||
|
newManifestHash := runSwarmExpectHash(t,
|
||||||
|
"--bzzapi",
|
||||||
|
cluster.Nodes[0].URL,
|
||||||
|
"manifest",
|
||||||
|
"remove",
|
||||||
|
origManifestHash,
|
||||||
|
"robots.html",
|
||||||
|
)
|
||||||
|
|
||||||
|
checkHashLength(t, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
newManifest := downloadManifest(t, client, newManifestHash, encrypt)
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
loop:
|
||||||
|
for _, e := range newManifest.Entries {
|
||||||
|
if e.Path == "robots." {
|
||||||
|
nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
|
||||||
|
for _, e := range nestedManifest.Entries {
|
||||||
|
if e.Path == "html" {
|
||||||
|
found = true
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
t.Fatal("robots.html in not removed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSwarmExpectHash(t *testing.T, args ...string) (hash string) {
|
||||||
|
t.Helper()
|
||||||
|
hashRegexp := `[a-f\d]{64,128}`
|
||||||
|
up := runSwarm(t, args...)
|
||||||
|
_, matches := up.ExpectRegexp(hashRegexp)
|
||||||
|
up.ExpectExit()
|
||||||
|
|
||||||
|
if len(matches) < 1 {
|
||||||
|
t.Fatal("no matches found")
|
||||||
|
}
|
||||||
|
return matches[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHashLength(t *testing.T, hash string, encrypted bool) {
|
||||||
|
t.Helper()
|
||||||
|
l := len(hash)
|
||||||
|
if encrypted && l != 128 {
|
||||||
|
t.Errorf("expected hash length 128, got %v", l)
|
||||||
|
}
|
||||||
|
if !encrypted && l != 64 {
|
||||||
|
t.Errorf("expected hash length 64, got %v", l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadManifest(t *testing.T, client *swarm.Client, hash string, encrypted bool) (manifest *api.Manifest) {
|
||||||
|
t.Helper()
|
||||||
|
m, isEncrypted, err := client.DownloadManifest(hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if encrypted != isEncrypted {
|
||||||
|
t.Error("new manifest encryption flag is not correct")
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFile(t *testing.T, client *swarm.Client, hash, path string, expected []byte) {
|
||||||
|
t.Helper()
|
||||||
|
f, err := client.Download(hash, path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(got, expected) {
|
||||||
|
t.Errorf("expected file content %q, got %q", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue