Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: add smoke test for nydusd hot upgrade #1533

Merged
merged 1 commit into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ jobs:
export PERFORMANCE_TEST_MODE=${{ matrix.mode }}
sudo -E make smoke-performance

failover-test:
takeover-test:
runs-on: ubuntu-latest
needs: [contrib-build, nydus-build]
steps:
Expand All @@ -304,7 +304,8 @@ jobs:
path: contrib/nydusify/cmd
- name: Prepare Nydus Container Environment
run: |
sudo bash misc/failover/prepare.sh
- name: Failover Test
sudo bash misc/takeover/prepare.sh
- name: Takeover Test
run: |
sudo -E make smoke-failover
export NEW_NYDUSD_BINARY_PATH=target/release/nydusd
sudo -E make smoke-takeover
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ smoke-performance:
smoke-benchmark:
make -C smoke test-benchmark

smoke-failover:
make -C smoke test-failover
smoke-takeover:
make -C smoke test-takeover

smoke: release smoke-only

Expand Down
2 changes: 1 addition & 1 deletion misc/failover/prepare.sh → misc/takeover/prepare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ sudo tar -xzvf cni-plugins-linux-amd64-v$CNI_PLUGINS_VERSION.tgz -C /opt/cni/bin
sudo install -D misc/performance/containerd_config.toml /etc/containerd/config.toml
sudo systemctl restart containerd
sudo install -D misc/performance/nydusd_config.json /etc/nydus/nydusd-config.fusedev.json
sudo install -D misc/failover/snapshotter_config.toml /etc/nydus/config.toml
sudo install -D misc/takeover/snapshotter_config.toml /etc/nydus/config.toml
sudo install -D misc/performance/nydus-snapshotter.service /etc/systemd/system/nydus-snapshotter.service
sudo systemctl start nydus-snapshotter
7 changes: 4 additions & 3 deletions smoke/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ test-compatibility: build

# SNAPSHOTTER_SYSTEM_SOCK=/run/containerd-nydus/system.sock
# SNAPSHOTTER=nydus
# FAILOVER_TEST_IMAGE=wordpress
test-failover: build
FAILOVER_TEST=true sudo -E ./smoke.test -test.v -test.timeout 10m -test.parallel=1 -test.run=TestFailover
# TAKEOVER_TEST_IMAGE=wordpress
# NEW_NYDUSD_BINARY_PATH=target/release/nydusd
test-takeover: build
TAKEOVER_TEST=true sudo -E ./smoke.test -test.v -test.timeout 10m -test.parallel=1 -test.run=TestTakeover
80 changes: 0 additions & 80 deletions smoke/tests/failover_test.go

This file was deleted.

163 changes: 163 additions & 0 deletions smoke/tests/takeover_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright 2023 Nydus Developers. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0

package tests

import (
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"time"

"github.com/dragonflyoss/nydus/smoke/tests/tool"
"github.com/dragonflyoss/nydus/smoke/tests/tool/test"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)

// Environment Requirement: Containerd, nerdctl >= 0.22, nydus-snapshotter, nydusd.
// Prepare: setup nydus for containerd, reference: https://github.com/dragonflyoss/nydus/blob/master/docs/containerd-env-setup.md.

var (
snapshotter string
takeoverTestImage string
snapshotterSystemSock string
)

type TakeoverTestSuit struct {
t *testing.T
ctx *tool.Context
testImage string
snapshotterCli *tool.SnapshotterClient
}

func NewTakeoverTestSuit(t *testing.T) *TakeoverTestSuit {
snapshotterCli := tool.NewSnapshotterClient(snapshotterSystemSock)
ctx := tool.DefaultContext(t)

// prepare and convert image
sourceImage := tool.PrepareImage(t, takeoverTestImage)
imageName := fmt.Sprintf("%s:nydus", sourceImage)
tool.ConvertImage(t, ctx, sourceImage, imageName)

return &TakeoverTestSuit{
t: t,
ctx: ctx,
testImage: imageName,
snapshotterCli: snapshotterCli,
}
}

func (f *TakeoverTestSuit) clear() {
tool.RunWithoutOutput(f.t, fmt.Sprintf("sudo nerdctl --snapshotter %s image rm %s", snapshotter, f.testImage))
}

func (f *TakeoverTestSuit) rmContainer(conatinerName string) {
tool.RunWithoutOutput(f.t, fmt.Sprintf("sudo nerdctl --snapshotter %s rm -f %s", snapshotter, conatinerName))
}

func (f *TakeoverTestSuit) TestFailover(t *testing.T) {
imageName := f.testImage

containerName := uuid.NewString()
tool.RunContainerSimple(t, imageName, snapshotter, containerName, false)
defer f.rmContainer(containerName)

daemons, err := f.snapshotterCli.GetNydusDaemonInfos()
require.NoError(t, err, "get nydus daemon infos")

// kill the nydus daemons
for _, daemon := range daemons {
killCmd := fmt.Sprintf("kill -9 %d", daemon.Pid)
tool.Run(t, killCmd)
}

// wait for the nydus daemons recover
time.Sleep(5 * time.Second)

// check the container by requesting its wait url
runArgs := tool.GetRunArgs(t, imageName)
resp, err := http.Get(runArgs.WaitURL)
Desiki-high marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err, "access to the wait url of the recoverd container")
defer resp.Body.Close()
if resp.StatusCode/100 != 2 {
t.Fatalf("Failed to access the wait url of the recoverd container")
}
}

func (f *TakeoverTestSuit) TestHotUpgrade(t *testing.T) {
imageName := f.testImage

containerName := uuid.NewString()
tool.RunContainerSimple(t, imageName, snapshotter, containerName, false)
defer f.rmContainer(containerName)

// hot upgrade nydusd
newNydusdPath := os.Getenv("NEW_NYDUSD_BINARY_PATH")
if newNydusdPath == "" {
newNydusdPath = "target/release/nydusd"
}
nydusdPath, err := filepath.Abs("../" + newNydusdPath)
require.NoErrorf(t, err, "get the abs path of new nydusd path (%s)", newNydusdPath)
err = os.Chmod(nydusdPath, 0755)
require.NoErrorf(t, err, "chmod nydusd binary file (%s)", nydusdPath)

upgradeReq := &tool.UpgradeRequest{
NydusdPath: nydusdPath,
Version: getNydusdVersion(nydusdPath),
Policy: "rolling",
}
err = f.snapshotterCli.Upgrade(upgradeReq)
require.NoError(t, err, "call the snapshotter to upgrade nydus daemons")

// wait for the nydus daemons recover
time.Sleep(5 * time.Second)

// check the container by requesting its wait url
runArgs := tool.GetRunArgs(t, imageName)
resp, err := http.Get(runArgs.WaitURL)
Desiki-high marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err, "access to the wait url of the recoverd container")
defer resp.Body.Close()
if resp.StatusCode/100 != 2 {
t.Fatalf("Failed to access the wait url of the recoverd container")
}
}

func getNydusdVersion(nydusdPath string) string {
versionOutput := tool.RunWithOutput(fmt.Sprintf("%s --version", nydusdPath))
lines := strings.Split(versionOutput, "\n")
version := ""
for _, line := range lines {
if strings.HasPrefix(line, "Version:") {
fields := strings.Fields(line)
version = strings.TrimSpace(fields[1])
}
}
return version
}

func TestTakeover(t *testing.T) {
if v, ok := os.LookupEnv("TAKEOVER_TEST"); !ok || v != "true" {
t.Skip("skipping takeover test")
}
snapshotter = os.Getenv("SNAPSHOTTER")
if snapshotter == "" {
snapshotter = defaultSnapshotter
}
takeoverTestImage = os.Getenv("TAKEOVER_TEST_IMAGE")
if takeoverTestImage == "" {
takeoverTestImage = "wordpress"
}
snapshotterSystemSock = os.Getenv("SNAPSHOTTER_SYSTEM_SOCK")
if snapshotterSystemSock == "" {
snapshotterSystemSock = defaultSnapshotterSystemSock
}
suite := NewTakeoverTestSuit(t)
defer suite.clear()

test.Run(t, suite, test.Sync)
}
50 changes: 46 additions & 4 deletions smoke/tests/tool/snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
package tool

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"time"

"github.com/pkg/errors"
)

// SnapshotterClient commnicates with nydus-snapshotter via
Expand Down Expand Up @@ -41,6 +44,12 @@ type rafsInstanceInfo struct {
ImageID string `json:"image_id"`
}

type UpgradeRequest struct {
NydusdPath string `json:"nydusd_path"`
Version string `json:"version"`
Policy string `json:"policy"`
}

func NewSnapshotterClient(sock string) *SnapshotterClient {
transport := &http.Transport{
MaxIdleConns: 10,
Expand All @@ -64,14 +73,42 @@ func NewSnapshotterClient(sock string) *SnapshotterClient {
}
}

func (cli *SnapshotterClient) GetNydusDaemonInfos() ([]*DaemonInfoFromSnapshotter, error) {
resp, err := cli.client.Get(fmt.Sprintf("http://unix%s", "/api/v1/daemons"))
func (cli *SnapshotterClient) request(method, urlSuffix string, body any) (respBody []byte, err error) {
var reqBody io.Reader
if body != nil {
reqJSON, err := json.Marshal(body)
if err != nil {
return nil, errors.Wrap(err, "marshal request body")
}

reqBody = bytes.NewBuffer(reqJSON)
}

url := fmt.Sprintf("http://unix%s", urlSuffix)
req, err := http.NewRequest(method, url, reqBody)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "build request")
}
resp, err := cli.client.Do(req)
if err != nil {
return nil, errors.Wrap(err, "do request")
}
Desiki-high marked this conversation as resolved.
Show resolved Hide resolved
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
respBody, err = io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "read response body")
}

if resp.StatusCode/100 != 2 {
return nil, fmt.Errorf("faild to do request(%v), got status %s, and resp %s", req, resp.Status, respBody)
}

return
}

func (cli *SnapshotterClient) GetNydusDaemonInfos() ([]*DaemonInfoFromSnapshotter, error) {
body, err := cli.request("GET", "/api/v1/daemons", nil)
if err != nil {
return nil, err
}
Expand All @@ -83,3 +120,8 @@ func (cli *SnapshotterClient) GetNydusDaemonInfos() ([]*DaemonInfoFromSnapshotte

return infos, nil
}

func (cli *SnapshotterClient) Upgrade(req *UpgradeRequest) error {
_, err := cli.request("PUT", "/api/v1/daemons/upgrade", req)
return err
}