Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ checks:
max-pathname-length: 64
postage-amount: 1000
postage-depth: 17
timeout: 5m
timeout: 30m
type: manifest
networkavailability:
options:
Expand Down
2 changes: 1 addition & 1 deletion config/local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ checks:
max-pathname-length: 64
postage-amount: 1000
postage-depth: 17
timeout: 5m
timeout: 30m
type: manifest
ci-pingpong:
options:
Expand Down
2 changes: 1 addition & 1 deletion config/public-testnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ checks:
max-pathname-length: 64
postage-amount: 140000000
postage-depth: 17
timeout: 5m
timeout: 30m
type: manifest
pt-pss:
options:
Expand Down
6 changes: 6 additions & 0 deletions pkg/bee/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const (
swarmSocSignatureHeader = "Swarm-Soc-Signature"
swarmFeedIndexHeader = "Swarm-Feed-Index"
swarmFeedIndexNextHeader = "Swarm-Feed-Index-Next"
swarmIndexDocumentHeader = "Swarm-Index-Document"
swarmErrorDocumentHeader = "Swarm-Error-Document"
)

var userAgent = "beekeeper/" + beekeeper.Version
Expand Down Expand Up @@ -340,6 +342,10 @@ type UploadOptions struct {
BatchID string
Direct bool
ActHistoryAddress swarm.Address

// Dirs
IndexDocument string
ErrorDocument string
}

type DownloadOptions struct {
Expand Down
7 changes: 7 additions & 0 deletions pkg/bee/api/dirs.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ func (s *DirsService) Upload(ctx context.Context, data io.Reader, size int64, o
header.Set("swarm-collection", "True")
header.Set(postageStampBatchHeader, o.BatchID)

if o.IndexDocument != "" {
header.Set(swarmIndexDocumentHeader, o.IndexDocument)
}
if o.ErrorDocument != "" {
header.Set(swarmErrorDocumentHeader, o.ErrorDocument)
}

err = s.client.requestWithHeader(ctx, http.MethodPost, "/"+apiVersion+"/bzz", header, data, &resp)

return
Expand Down
191 changes: 163 additions & 28 deletions pkg/check/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"math/rand"
"time"

"github.com/ethersphere/bee/v2/pkg/crypto"
"github.com/ethersphere/bee/v2/pkg/swarm"
"github.com/ethersphere/beekeeper/pkg/bee"
"github.com/ethersphere/beekeeper/pkg/bee/api"
"github.com/ethersphere/beekeeper/pkg/beekeeper"
Expand Down Expand Up @@ -67,14 +69,34 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts int
}

rnd := random.PseudoGenerator(o.Seed)
names := cluster.FullNodeNames()
perm := rnd.Perm(len(names))

c.logger.Infof("Seed: %d", o.Seed)
if len(names) < 2 {
return fmt.Errorf("not enough nodes to run feed check")
}

overlays, err := cluster.FlattenOverlays(ctx)
clients, err := cluster.NodesClients(ctx)
if err != nil {
return err
}
upClient := clients[names[perm[0]]]
downClient := clients[names[perm[1]]]

err = c.checkWithoutSubDirs(ctx, rnd, o, upClient, downClient)
if err != nil {
return fmt.Errorf("check without subdirs: %w", err)
}

err = c.checkWithSubDirs(ctx, rnd, o, upClient, downClient)
if err != nil {
return fmt.Errorf("check with subdirs: %w", err)
}

return nil
}

func (c *Check) checkWithoutSubDirs(ctx context.Context, rnd *rand.Rand, o Options, upClient *bee.Client, downClient *bee.Client) error {
files, err := generateFiles(rnd, o.FilesInCollection, o.MaxPathnameLength)
if err != nil {
return err
Expand All @@ -86,57 +108,170 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts int
}

tarFile := bee.NewBufferFile("", tarReader)
clients, err := cluster.NodesClients(ctx)
batchID, err := upClient.GetOrCreateMutableBatch(ctx, o.PostageAmount, o.PostageDepth, o.PostageLabel)
if err != nil {
return fmt.Errorf("node %s: batch id %w", upClient.Name(), err)
}
c.logger.Infof("node %s: batch id %s", upClient.Name(), batchID)

if err := upClient.UploadCollection(ctx, &tarFile, api.UploadOptions{BatchID: batchID}); err != nil {
return fmt.Errorf("node %d: %w", 0, err)
}

for _, file := range files {
if err := c.download(downClient, tarFile.Address(), &file, bee.File{}); err != nil {
return err
}
}
return nil
}

func (c *Check) checkWithSubDirs(ctx context.Context, rnd *rand.Rand, o Options, upClient *bee.Client, downClient *bee.Client) error {
privKey, err := crypto.GenerateSecp256k1Key()
if err != nil {
return err
}

sortedNodes := cluster.FullNodeNames()
node := sortedNodes[0]
signer := crypto.NewDefaultSigner(privKey)
topic, err := crypto.LegacyKeccak256([]byte("my-website"))
if err != nil {
return err
}

client := clients[node]
batchID, err := upClient.GetOrCreateMutableBatch(ctx, o.PostageAmount, o.PostageDepth, o.PostageLabel)
if err != nil {
return fmt.Errorf("node %s: batch id %w", upClient.Name(), err)
}
c.logger.Infof("node %s: batch id %s", upClient.Name(), batchID)

batchID, err := client.GetOrCreateMutableBatch(ctx, o.PostageAmount, o.PostageDepth, o.PostageLabel)
rootFeedRef, err := upClient.CreateRootFeedManifest(ctx, signer, topic, api.UploadOptions{BatchID: batchID})
if err != nil {
return fmt.Errorf("node %s: batch id %w", node, err)
return err
}
c.logger.Infof("node %s: batch id %s", node, batchID)
c.logger.Infof("root feed reference: %s", rootFeedRef.Reference)
time.Sleep(3 * time.Second)

if err := client.UploadCollection(ctx, &tarFile, api.UploadOptions{BatchID: batchID}); err != nil {
return fmt.Errorf("node %d: %w", 0, err)
paths := []string{"index.html", "assets/styles/styles.css", "assets/styles/images/image.png", "error.html"}
files, err := generateFilesWithPaths(rnd, paths, int(o.MaxPathnameLength))
if err != nil {
return err
}

lastNode := sortedNodes[len(sortedNodes)-1]
try := 0
tarReader, err := tarFiles(files)
if err != nil {
return err
}
tarFile := bee.NewBufferFile("", tarReader)
if err := upClient.UploadCollection(ctx, &tarFile, api.UploadOptions{BatchID: batchID, IndexDocument: "index.html"}); err != nil {
return err
}
c.logger.Infof("collection uploaded: %s", tarFile.Address())
time.Sleep(3 * time.Second)

DOWNLOAD:
time.Sleep(5 * time.Second)
try++
if try > 5 {
return errors.New("failed getting manifest files after too many retries")
// push first version of website to the feed
ref, err := upClient.UpdateFeedWithReference(ctx, signer, topic, 0, tarFile.Address(), api.UploadOptions{BatchID: batchID})
if err != nil {
return err
}
c.logger.Infof("feed updated: %s", ref.Reference)

for i, file := range files {
node := clients[lastNode]
// download root (index.html) from the feed
err = c.download(downClient, rootFeedRef.Reference, nil, files[0])
if err != nil {
return err
}

// update website files
files, err = generateFilesWithPaths(rnd, paths, int(o.MaxPathnameLength))
if err != nil {
return err
}

tarReader, err = tarFiles(files)
if err != nil {
return err
}
tarFile = bee.NewBufferFile("", tarReader)
if err := upClient.UploadCollection(ctx, &tarFile, api.UploadOptions{BatchID: batchID, IndexDocument: "index.html"}); err != nil {
return err
}
time.Sleep(3 * time.Second)

// push 2nd version of website to the feed
ref, err = upClient.UpdateFeedWithReference(ctx, signer, topic, 1, tarFile.Address(), api.UploadOptions{BatchID: batchID})
if err != nil {
return err
}
c.logger.Infof("feed updated: %s", ref.Reference)

// download updated index.html from the feed
err = c.download(downClient, rootFeedRef.Reference, nil, files[0])
if err != nil {
return err
}

size, hash, err := node.DownloadManifestFile(ctx, tarFile.Address(), file.Name())
// download other paths and compare
for i := 0; i < len(files); i++ {
err = c.download(downClient, tarFile.Address(), &files[i], files[0])
if err != nil {
c.logger.Infof("Node %s. Error retrieving file: %v", lastNode, err)
goto DOWNLOAD
return err
}
}
return nil
}

// download retrieves a file from the given address using the specified client.
// If the file parameter is nil, it downloads the index file in the collection.
func (c *Check) download(client *bee.Client, address swarm.Address, file *bee.File, indexFile bee.File) error {
fName := ""
if file != nil {
fName = file.Name()
}
c.logger.Infof("downloading file: %s/%s", address, fName)

try := 0
DOWNLOAD:
time.Sleep(5 * time.Second)
try++
if try > 5 {
return fmt.Errorf("failed getting manifest files after too many retries")
}
_, hash, err := client.DownloadManifestFile(context.Background(), address, fName)
if err != nil {
c.logger.Infof("node %s. Error retrieving file: %s", client.Name(), err.Error())
goto DOWNLOAD
}

if file != nil {
if !bytes.Equal(file.Hash(), hash) {
c.logger.Infof("Node %s. File %d not retrieved successfully. Uploaded size: %d Downloaded size: %d Node: %s File: %s/%s", lastNode, i, file.Size(), size, overlays[lastNode].String(), tarFile.Address().String(), file.Name())
c.logger.Infof("node %s. File hash does not match", client.Name())
return errManifest
}
} else {
if !bytes.Equal(indexFile.Hash(), hash) {
c.logger.Infof("node %s. Index hash does not match", client.Name())
return errManifest
}

c.logger.Infof("Node %s. File %d retrieved successfully. Node: %s File: %s/%s", lastNode, i, overlays[lastNode].String(), tarFile.Address().String(), file.Name())
try = 0 // reset the retry counter for the next file
}

c.logger.Infof("node %s. File retrieved successfully", client.Name())
return nil
}

func generateFilesWithPaths(r *rand.Rand, paths []string, maxSize int) ([]bee.File, error) {
files := make([]bee.File, len(paths))
for i := 0; i < len(paths); i++ {
path := paths[i]
size := int64(r.Intn(maxSize)) + 1
file := bee.NewRandomFile(r, path, size)
err := file.CalculateHash()
if err != nil {
return nil, err
}
files[i] = file
}
return files, nil
}

func generateFiles(r *rand.Rand, filesCount int, maxPathnameLength int32) ([]bee.File, error) {
files := make([]bee.File, filesCount)

Expand Down
Loading