mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-22 03:00:12 +00:00
test(agentcompat): add integration scenarios
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
//go:build linux
|
||||
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/client"
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/contract"
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/fixture"
|
||||
)
|
||||
|
||||
func (execution transferExecution) upload(ctx context.Context, path fixture.AgentPath, payload fixture.Payload, digest fixture.PayloadDigest, fault contract.Fault) (transferPathEvidence, client.TransferURL, error) {
|
||||
transferURL, err := client.RequestUploadURL(ctx, execution.client, client.UploadURLRequest{
|
||||
ServerID: execution.serverID, Path: path.String(), TTLSeconds: 60, Mode: "0640", CreateDirs: true,
|
||||
})
|
||||
if err != nil {
|
||||
return transferPathEvidence{}, client.TransferURL{}, err
|
||||
}
|
||||
transferClient, err := clientForTransferURL(transferURL)
|
||||
if err != nil {
|
||||
return transferPathEvidence{}, client.TransferURL{}, err
|
||||
}
|
||||
defer transferClient.Close()
|
||||
measured := fixture.NewMeasuredReader(payload.Reader())
|
||||
expectedSHA := digest.Hex()
|
||||
if fault.String() == "transfer-hash" {
|
||||
expectedSHA = strings.Repeat("0", 64)
|
||||
}
|
||||
started := time.Now()
|
||||
result, err := transferClient.UploadTransfer(ctx, transferURL, client.UploadTransfer{
|
||||
Body: measured, ContentLength: int64(contract.TransferBytes), SHA256: expectedSHA,
|
||||
})
|
||||
duration := time.Since(started)
|
||||
measurement := measured.Measurement()
|
||||
if err != nil {
|
||||
return transferPathEvidence{bytes: measurement.Digest.Bytes, sha256: measurement.Digest.Hex(), chunks: measurement.Chunks, duration: duration}, transferURL, err
|
||||
}
|
||||
fileDigest, info, err := verifyUploadedTransfer(path)
|
||||
if err != nil {
|
||||
return transferPathEvidence{}, transferURL, err
|
||||
}
|
||||
if result.Size != info.Size() || result.SHA256 != fileDigest.Hex() {
|
||||
return transferPathEvidence{}, transferURL, errors.New("upload result differs from Agent file")
|
||||
}
|
||||
return transferPathEvidence{bytes: uint64(result.Size), sha256: result.SHA256, chunks: measurement.Chunks, duration: duration, mode: info.Mode()}, transferURL, nil
|
||||
}
|
||||
|
||||
func verifyUploadedTransfer(path fixture.AgentPath) (digest fixture.PayloadDigest, info os.FileInfo, err error) {
|
||||
file, err := os.Open(path.String())
|
||||
if err != nil {
|
||||
return fixture.PayloadDigest{}, nil, fmt.Errorf("open uploaded transfer: %w", err)
|
||||
}
|
||||
defer func() { err = errors.Join(err, file.Close()) }()
|
||||
digest, err = fixture.VerifyPayload(file, contract.TransferBytes)
|
||||
if err != nil {
|
||||
return fixture.PayloadDigest{}, nil, err
|
||||
}
|
||||
info, err = file.Stat()
|
||||
if err != nil {
|
||||
return fixture.PayloadDigest{}, nil, fmt.Errorf("stat uploaded transfer: %w", err)
|
||||
}
|
||||
return digest, info, nil
|
||||
}
|
||||
|
||||
func (execution transferExecution) download(ctx context.Context, path fixture.AgentPath) (transferPathEvidence, client.TransferURL, error) {
|
||||
transferURL, err := client.RequestDownloadURL(ctx, execution.client, client.DownloadURLRequest{
|
||||
ServerID: execution.serverID, Path: path.String(), TTLSeconds: 60,
|
||||
})
|
||||
if err != nil {
|
||||
return transferPathEvidence{}, client.TransferURL{}, err
|
||||
}
|
||||
transferClient, err := clientForTransferURL(transferURL)
|
||||
if err != nil {
|
||||
return transferPathEvidence{}, client.TransferURL{}, err
|
||||
}
|
||||
defer transferClient.Close()
|
||||
measured := fixture.NewMeasuredWriter()
|
||||
started := time.Now()
|
||||
written, err := transferClient.DownloadTransfer(ctx, transferURL, measured)
|
||||
duration := time.Since(started)
|
||||
measurement := measured.Measurement()
|
||||
if err != nil {
|
||||
return transferPathEvidence{}, transferURL, err
|
||||
}
|
||||
if written != int64(measurement.Digest.Bytes) {
|
||||
return transferPathEvidence{}, transferURL, errors.New("download byte count differs from measured digest")
|
||||
}
|
||||
return transferPathEvidence{bytes: measurement.Digest.Bytes, sha256: measurement.Digest.Hex(), chunks: measurement.Chunks, duration: duration}, transferURL, nil
|
||||
}
|
||||
|
||||
func (execution transferExecution) replayUpload(ctx context.Context, transferURL client.TransferURL) error {
|
||||
transferClient, err := clientForTransferURL(transferURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer transferClient.Close()
|
||||
payload, err := fixture.NewPayload(contract.DefaultSeed, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
digest, err := fixture.VerifyPayload(payload.Reader(), 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = transferClient.UploadTransfer(ctx, transferURL, client.UploadTransfer{Body: payload.Reader(), ContentLength: 1, SHA256: digest.Hex()})
|
||||
return err
|
||||
}
|
||||
|
||||
func (execution transferExecution) replayDownload(ctx context.Context, transferURL client.TransferURL) error {
|
||||
transferClient, err := clientForTransferURL(transferURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer transferClient.Close()
|
||||
_, err = transferClient.DownloadTransfer(ctx, transferURL, io.Discard)
|
||||
return err
|
||||
}
|
||||
|
||||
func (execution transferExecution) probeOversize(ctx context.Context) error {
|
||||
path, err := execution.root.Path("oversize/rejected.bin")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transferURL, err := client.RequestUploadURL(ctx, execution.client, client.UploadURLRequest{
|
||||
ServerID: execution.serverID, Path: path.String(), TTLSeconds: 60, CreateDirs: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transferClient, err := clientForTransferURL(transferURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer transferClient.Close()
|
||||
return transferClient.ProbeOversizeUpload(ctx, transferURL, client.OversizeUploadProbe{
|
||||
Body: zeroReader{}, ContentLength: int64(contract.TransferBytes) + 1,
|
||||
})
|
||||
}
|
||||
|
||||
type ownedTransferClient struct {
|
||||
*client.Client
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
func clientForTransferURL(transferURL client.TransferURL) (*ownedTransferClient, error) {
|
||||
parsed, err := url.Parse(transferURL.URL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse transfer URL origin: %w", err)
|
||||
}
|
||||
defaultTransport, ok := http.DefaultTransport.(*http.Transport)
|
||||
if !ok {
|
||||
return nil, errors.New("default HTTP transport is not cloneable")
|
||||
}
|
||||
transport := defaultTransport.Clone()
|
||||
transferClient, err := client.New(client.Config{
|
||||
BaseURL: parsed.Scheme + "://" + parsed.Host, HTTPClient: &http.Client{Transport: transport}, TransferTimeout: 5 * time.Minute,
|
||||
})
|
||||
if err != nil {
|
||||
transport.CloseIdleConnections()
|
||||
return nil, err
|
||||
}
|
||||
return &ownedTransferClient{Client: transferClient, transport: transport}, nil
|
||||
}
|
||||
|
||||
func (client *ownedTransferClient) Close() {
|
||||
client.transport.CloseIdleConnections()
|
||||
}
|
||||
|
||||
type zeroReader struct{}
|
||||
|
||||
func (zeroReader) Read(destination []byte) (int, error) {
|
||||
clear(destination)
|
||||
return len(destination), nil
|
||||
}
|
||||
Reference in New Issue
Block a user