From 085987ff2cb1695881a3d0abeebbc21a4c51a2de Mon Sep 17 00:00:00 2001 From: Zahoor Mohamed Date: Mon, 13 Feb 2017 08:03:05 +0530 Subject: cmd/swarm: manifest manipulation commands (#3645) --- cmd/swarm/main.go | 37 ++++++ cmd/swarm/manifest.go | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/swarm/upload.go | 26 ++++ 3 files changed, 423 insertions(+) create mode 100644 cmd/swarm/manifest.go (limited to 'cmd') diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 34b3f71c4..546c646f1 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -154,6 +154,43 @@ The output of this command is supposed to be machine-readable. Prints the swarm hash of file or directory. `, }, + { + Name: "manifest", + Usage: "update a MANIFEST", + ArgsUsage: "manifest COMMAND", + Description: ` +Updates a MANIFEST by adding/removing/updating the hash of a path. +`, + Subcommands: []cli.Command{ + { + Action: add, + Name: "add", + Usage: "add a new path to the manifest", + ArgsUsage: " []", + Description: ` +Adds a new path to the manifest +`, + }, + { + Action: update, + Name: "update", + Usage: "update the hash for an already existing path in the manifest", + ArgsUsage: " []", + Description: ` +Update the hash for an already existing path in the manifest +`, + }, + { + Action: remove, + Name: "remove", + Usage: "removes a path from the manifest", + ArgsUsage: " ", + Description: ` +Removes a path from the manifest +`, + }, + }, + }, } app.Flags = []cli.Flag{ diff --git a/cmd/swarm/manifest.go b/cmd/swarm/manifest.go new file mode 100644 index 000000000..0de0d69bb --- /dev/null +++ b/cmd/swarm/manifest.go @@ -0,0 +1,360 @@ +// Copyright 2016 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 . + +// Command MANIFEST update +package main + +import ( + "gopkg.in/urfave/cli.v1" + "log" + "mime" + "path/filepath" + "strings" + "fmt" + "encoding/json" +) + +func add(ctx *cli.Context) { + + args := ctx.Args() + if len(args) < 3 { + log.Fatal("need atleast three arguments []") + } + + var ( + mhash = args[0] + path = args[1] + hash = args[2] + + ctype string + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + mroot manifest + ) + + + if len(args) > 3 { + ctype = args[3] + } else { + ctype = mime.TypeByExtension(filepath.Ext(path)) + } + + newManifest := addEntryToManifest (ctx, mhash, path, hash, ctype) + 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) { + + args := ctx.Args() + if len(args) < 3 { + log.Fatal("need atleast three arguments ") + } + + var ( + mhash = args[0] + path = args[1] + hash = args[2] + + ctype string + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + mroot manifest + ) + if len(args) > 3 { + ctype = args[3] + } else { + ctype = mime.TypeByExtension(filepath.Ext(path)) + } + + newManifest := updateEntryInManifest (ctx, mhash, path, hash, ctype) + 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) { + args := ctx.Args() + if len(args) < 2 { + log.Fatal("need atleast two arguments ") + } + + var ( + mhash = args[0] + path = args[1] + + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + mroot manifest + ) + + newManifest := removeEntryFromManifest (ctx, mhash, path) + 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 { + + var ( + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = &client{api: bzzapi} + longestPathEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + ) + + mroot, err := client.downloadManifest(mhash) + if err != nil { + log.Fatalln("manifest download failed:", err) + } + + //TODO: check if the "hash" to add is valid and present in swarm + _, err = client.downloadManifest(hash) + if err != nil { + log.Fatalln("hash to add is not present:", err) + } + + + // See if we path is in this Manifest or do we have to dig deeper + for _, entry := range mroot.Entries { + if path == entry.Path { + log.Fatal(path, "Already present, not adding anything") + }else { + if entry.ContentType == "application/bzz-manifest+json" { + prfxlen := strings.HasPrefix(path, entry.Path) + if prfxlen && len(path) > len(longestPathEntry.Path) { + longestPathEntry = entry + } + } + } + } + + if longestPathEntry.Path != "" { + // Load the child Manifest add the entry there + newPath := path[len(longestPathEntry.Path):] + newHash := addEntryToManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype) + + // Replace the hash for parent Manifests + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if longestPathEntry.Path == entry.Path { + entry.Hash = newHash + } + newMRoot.Entries = append(newMRoot.Entries, entry) + } + mroot = newMRoot + } else { + // Add the entry in the leaf Manifest + newEntry := manifestEntry{ + Path: path, + Hash: hash, + ContentType: ctype, + } + mroot.Entries = append(mroot.Entries, newEntry) + } + + + newManifestHash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + return newManifestHash + + + +} + +func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string) string { + + var ( + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = &client{api: bzzapi} + newEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + longestPathEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + ) + + mroot, err := client.downloadManifest(mhash) + if err != nil { + log.Fatalln("manifest download failed:", 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 + for _, entry := range mroot.Entries { + if path == entry.Path { + newEntry = entry + }else { + if entry.ContentType == "application/bzz-manifest+json" { + prfxlen := strings.HasPrefix(path, entry.Path) + if prfxlen && len(path) > len(longestPathEntry.Path) { + longestPathEntry = entry + } + } + } + } + + if longestPathEntry.Path == "" && newEntry.Path == "" { + log.Fatal(path, " Path not present in the Manifest, not setting anything") + } + + if longestPathEntry.Path != "" { + // Load the child Manifest add the entry there + newPath := path[len(longestPathEntry.Path):] + newHash := updateEntryInManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype) + + // Replace the hash for parent Manifests + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if longestPathEntry.Path == entry.Path { + entry.Hash = newHash + } + newMRoot.Entries = append(newMRoot.Entries, entry) + + } + mroot = newMRoot + } + + if newEntry.Path != "" { + // Replace the hash for leaf Manifest + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if newEntry.Path == entry.Path { + myEntry := manifestEntry{ + Path: entry.Path, + Hash: hash, + ContentType: ctype, + } + newMRoot.Entries = append(newMRoot.Entries, myEntry) + } else { + newMRoot.Entries = append(newMRoot.Entries, entry) + } + } + mroot = newMRoot + } + + + newManifestHash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + return newManifestHash +} + +func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string { + + var ( + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = &client{api: bzzapi} + entryToRemove = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + longestPathEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + ) + + mroot, err := client.downloadManifest(mhash) + if err != nil { + log.Fatalln("manifest download failed:", err) + } + + + + // See if we path is in this Manifest or do we have to dig deeper + for _, entry := range mroot.Entries { + if path == entry.Path { + entryToRemove = entry + }else { + if entry.ContentType == "application/bzz-manifest+json" { + prfxlen := strings.HasPrefix(path, entry.Path) + if prfxlen && len(path) > len(longestPathEntry.Path) { + longestPathEntry = entry + } + } + } + } + + if longestPathEntry.Path == "" && entryToRemove.Path == "" { + log.Fatal(path, "Path not present in the Manifest, not removing anything") + } + + if longestPathEntry.Path != "" { + // Load the child Manifest remove the entry there + newPath := path[len(longestPathEntry.Path):] + newHash := removeEntryFromManifest (ctx, longestPathEntry.Hash, newPath) + + // Replace the hash for parent Manifests + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if longestPathEntry.Path == entry.Path { + entry.Hash = newHash + } + newMRoot.Entries = append(newMRoot.Entries, entry) + } + mroot = newMRoot + } + + if entryToRemove.Path != "" { + // remove the entry in this Manifest + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if entryToRemove.Path != entry.Path { + newMRoot.Entries = append(newMRoot.Entries, entry) + } + } + mroot = newMRoot + } + + + newManifestHash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + return newManifestHash + + +} + diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go index d8039d45b..871713b2d 100644 --- a/cmd/swarm/upload.go +++ b/cmd/swarm/upload.go @@ -229,3 +229,29 @@ func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (strin content, err := ioutil.ReadAll(resp.Body) return string(content), err } + +func (c *client) downloadManifest(mhash string) (manifest, error) { + + mroot := manifest{} + req, err := http.NewRequest("GET", c.api + "/bzzr:/" + mhash, nil) + if err != nil { + return mroot, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return mroot, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return mroot, fmt.Errorf("bad status: %s", resp.Status) + + } + content, err := ioutil.ReadAll(resp.Body) + + err = json.Unmarshal(content, &mroot) + if err != nil { + return mroot, fmt.Errorf("Manifest %v is malformed: %v", mhash, err) + } + return mroot, err +} \ No newline at end of file -- cgit v1.2.3