Skip to content

Commit

Permalink
chore: enable range feature gate in e2e (#1059)
Browse files Browse the repository at this point in the history
Signed-off-by: Jim Ma <majinjing3@gmail.com>
  • Loading branch information
jim3ma committed Feb 14, 2022
1 parent a6423ce commit ffd71e6
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 22 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/e2e.yml
Expand Up @@ -69,7 +69,9 @@ jobs:
kubectl wait po proxy-2 --namespace dragonfly-e2e --for=condition=ready --timeout=10m
- name: Run E2E test
run: make actions-e2e-test-coverage
run: |
ginkgo -v -r --race --fail-fast --cover --trace --progress test/e2e -- --feature-gates=dfget-range=true
cat coverprofile.out >> coverage.txt
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -209,7 +209,7 @@ test-coverage:

# Run github actions E2E tests with coverage
actions-e2e-test-coverage:
@ginkgo -v -r --race --fail-fast -cover --trace --progress test/e2e
@ginkgo -v -r --race --fail-fast --cover --trace --progress test/e2e
@cat coverprofile.out >> coverage.txt
.PHONY: actions-e2e-test-coverage

Expand Down
38 changes: 20 additions & 18 deletions test/e2e/dfget_test.go
Expand Up @@ -85,18 +85,22 @@ func singleDfgetTest(name, ns, label, podNamePrefix, container string) {
fmt.Println("test in pod: " + podName)
Expect(strings.HasPrefix(podName, podNamePrefix)).Should(BeTrue())
pod := e2eutil.NewPodExec(ns, podName, container)
// install curl
_, err = pod.Command("apk", "add", "-U", "curl").CombinedOutput()
Expect(err).NotTo(HaveOccurred())

for path, size := range getFileDetails() {
url1 := e2eutil.GetFileURL(path)
url2 := e2eutil.GetNoContentLengthFileURL(path)
downloadSingleFile(ns, pod, path, url1, size, nil)
downloadSingleFile(ns, pod, path, url2, size, nil)

// make ranged requests to invoke prefetch feature
if featureGates.Enabled(featureGateRange) {
rg := getRandomRange(size)
downloadSingleFile(ns, pod, path, url1, size, rg)
downloadSingleFile(ns, pod, path, url2, size, rg)
}
downloadSingleFile(ns, pod, path, url1, size, nil)
downloadSingleFile(ns, pod, path, url2, size, nil)
}
})
}
Expand All @@ -105,23 +109,20 @@ func downloadSingleFile(ns string, pod *e2eutil.PodExec, path, url string, size
var (
sha256sum []string
dfget []string
wget []string
curl []string
)

if rg == nil {
sha256sum = append(sha256sum, "sha256sum", path)
dfget = append(dfget, "dfget", "-O", "/tmp/d7y.out", url)
wget = append(wget, "sh", "-c", fmt.Sprintf(`
export http_proxy=http://127.0.0.1:65001
wget -O /tmp/wget.out %s`, url))
sha256sum = append(sha256sum, "/usr/bin/sha256sum", path)
dfget = append(dfget, "/opt/dragonfly/bin/dfget", "-O", "/tmp/d7y.out", url)
curl = append(curl, "/usr/bin/curl", "-x", "http://127.0.0.1:65001", "-s", "--dump-header", "-", "-o", "/tmp/curl.out", url)
} else {
sha256sum = append(sha256sum, "sh", "-c",
fmt.Sprintf("dd if=%s ibs=1 skip=%d count=%d 2> /dev/null | sha256sum", path, rg.Start, rg.Length))
dfget = append(dfget, "dfget", "-O", "/tmp/d7y.out", "-H",
fmt.Sprintf("dd if=%s ibs=1 skip=%d count=%d 2> /dev/null | /usr/bin/sha256sum", path, rg.Start, rg.Length))
dfget = append(dfget, "/opt/dragonfly/bin/dfget", "-O", "/tmp/d7y.out", "-H",
fmt.Sprintf("Range: bytes=%d-%d", rg.Start, rg.Start+rg.Length-1), url)
wget = append(wget, "sh", "-c", fmt.Sprintf(`
export http_proxy=http://127.0.0.1:65001
wget -O /tmp/wget.out "Range: bytes=%d-%d" %s`, rg.Start, rg.Start+rg.Length-1, url))
curl = append(curl, "/usr/bin/curl", "-x", "http://127.0.0.1:65001", "-s", "--dump-header", "-", "-o", "/tmp/curl.out",
"--header", fmt.Sprintf("Range: bytes=%d-%d", rg.Start, rg.Start+rg.Length-1), url)
}

fmt.Printf("--------------------------------------------------------------------------------\n\n")
Expand Down Expand Up @@ -149,7 +150,7 @@ func downloadSingleFile(ns string, pod *e2eutil.PodExec, path, url string, size
Expect(err).NotTo(HaveOccurred())

// get dfget downloaded file digest
out, err = pod.Command("sha256sum", "/tmp/d7y.out").CombinedOutput()
out, err = pod.Command("/usr/bin/sha256sum", "/tmp/d7y.out").CombinedOutput()
fmt.Println("dfget sha256sum: " + string(out))
Expect(err).NotTo(HaveOccurred())
sha256sum2 := strings.Split(string(out), " ")[0]
Expand All @@ -160,18 +161,19 @@ func downloadSingleFile(ns string, pod *e2eutil.PodExec, path, url string, size

// skip dfdaemon
if ns == dragonflyNamespace {
fmt.Println("skip " + dragonflyNamespace + " namespace proxy tests")
return
}
// download file via proxy
start = time.Now()
out, err = pod.Command(wget...).CombinedOutput()
out, err = pod.Command(curl...).CombinedOutput()
end = time.Now()
fmt.Println(string(out))
fmt.Print(string(out))
Expect(err).NotTo(HaveOccurred())

// get proxy downloaded file digest
out, err = pod.Command("sha256sum", "/tmp/wget.out").CombinedOutput()
fmt.Println("wget sha256sum: " + string(out))
out, err = pod.Command("/usr/bin/sha256sum", "/tmp/curl.out").CombinedOutput()
fmt.Println("curl sha256sum: " + string(out))
Expect(err).NotTo(HaveOccurred())
sha256sum3 := strings.Split(string(out), " ")[0]
Expect(sha256sum1).To(Equal(sha256sum3))
Expand Down
6 changes: 5 additions & 1 deletion test/e2e/e2eutil/exec.go
Expand Up @@ -20,15 +20,18 @@ import (
"encoding/json"
"fmt"
"os/exec"
"strings"
)

const (
kindDockerContainer = "kind-control-plane"
)

func DockerCommand(arg ...string) *exec.Cmd {
extArgs := []string{"exec", "-i", kindDockerContainer}
container := kindDockerContainer
extArgs := []string{"exec", "-i", container}
extArgs = append(extArgs, arg...)
fmt.Println(fmt.Sprintf(`docker %s exec: "%s"`, container, strings.Join(arg, `" "`)))
return exec.Command("docker", extArgs...)
}

Expand Down Expand Up @@ -70,6 +73,7 @@ func (p *PodExec) Command(arg ...string) *exec.Cmd {
extArgs = []string{"-n", p.namespace, "exec", "-c", p.container, p.name, "--"}
}
extArgs = append(extArgs, arg...)
fmt.Println(fmt.Sprintf(`pod %s/%s exec: "%s"`, p.namespace, p.name, strings.Join(arg, `" "`)))
return KubeCtlCommand(extArgs...)
}

Expand Down
113 changes: 112 additions & 1 deletion test/tools/no-content-length/main.go
Expand Up @@ -17,13 +17,17 @@
package main

import (
"errors"
"flag"
"fmt"
"io"
"math"
"net/http"
"net/textproto"
"os"
"path"
"path/filepath"
"strconv"
"strings"
)

Expand All @@ -44,6 +48,21 @@ type fileHandler struct {
}

func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var rg *httpRange
if s := r.Header.Get("Range"); s != "" {
rgs, err := parseRange(s, math.MaxInt)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(fmt.Sprintf("wrong range format")))
return
}
if len(rgs) > 1 || len(rgs) == 0 {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(fmt.Sprintf("unsupport range format")))
return
}
rg = &rgs[0]
}
upath := filepath.Clean(r.URL.Path)
if !strings.HasPrefix(upath, "/") {
upath = "/" + upath
Expand Down Expand Up @@ -79,9 +98,101 @@ func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
defer file.Close()
var rd io.Reader
if rg == nil {
rd = file
} else {
_, _ = file.Seek(rg.start, io.SeekStart)
rd = io.LimitReader(file, rg.length)
}

if f.enableContentLength {
w.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
}
_, _ = io.Copy(w, file)
_, _ = io.Copy(w, rd)
}

// httpRange specifies the byte range to be sent to the client.
type httpRange struct {
start, length int64
}

// errNoOverlap is returned by serveContent's parseRange if first-byte-pos of
// all of the byte-range-spec values is greater than the content size.
var errNoOverlap = errors.New("invalid range: failed to overlap")

// parseRange parses a Range header string as per RFC 7233.
// errNoOverlap is returned if none of the ranges overlap.
func parseRange(s string, size int64) ([]httpRange, error) {
if s == "" {
return nil, nil // header not present
}
const b = "bytes="
if !strings.HasPrefix(s, b) {
return nil, errors.New("invalid range")
}
var ranges []httpRange
noOverlap := false
for _, ra := range strings.Split(s[len(b):], ",") {
ra = textproto.TrimString(ra)
if ra == "" {
continue
}
i := strings.Index(ra, "-")
if i < 0 {
return nil, errors.New("invalid range")
}
start, end := textproto.TrimString(ra[:i]), textproto.TrimString(ra[i+1:])
var r httpRange
if start == "" {
// If no start is specified, end specifies the
// range start relative to the end of the file,
// and we are dealing with <suffix-length>
// which has to be a non-negative integer as per
// RFC 7233 Section 2.1 "Byte-Ranges".
if end == "" || end[0] == '-' {
return nil, errors.New("invalid range")
}
i, err := strconv.ParseInt(end, 10, 64)
if i < 0 || err != nil {
return nil, errors.New("invalid range")
}
if i > size {
i = size
}
r.start = size - i
r.length = size - r.start
} else {
i, err := strconv.ParseInt(start, 10, 64)
if err != nil || i < 0 {
return nil, errors.New("invalid range")
}
if i >= size {
// If the range begins after the size of the content,
// then it does not overlap.
noOverlap = true
continue
}
r.start = i
if end == "" {
// If no end is specified, range extends to end of the file.
r.length = size - r.start
} else {
i, err := strconv.ParseInt(end, 10, 64)
if err != nil || r.start > i {
return nil, errors.New("invalid range")
}
if i >= size {
i = size - 1
}
r.length = i - r.start + 1
}
}
ranges = append(ranges, r)
}
if noOverlap && len(ranges) == 0 {
// The specified ranges did not overlap with the content.
return nil, errNoOverlap
}
return ranges, nil
}

0 comments on commit ffd71e6

Please sign in to comment.