Skip to content

Commit

Permalink
test: add basic sanity test for upgrade
Browse files Browse the repository at this point in the history
Signed-off-by: Niladri Halder <[email protected]>
  • Loading branch information
niladrih committed Nov 7, 2024
1 parent 4a19268 commit 4ad5e5b
Show file tree
Hide file tree
Showing 12 changed files with 518 additions and 10 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/k8s-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@ jobs:
id: build
run: |
TAG=$(nix-shell ./shell.nix --run './scripts/python/generate-test-tag.sh')
BIN=$(mktemp -p . -d -t test-bin-XXXXXX)
TEST_DIR=$(realpath $(mktemp -d ./test-dir-XXXXXX))
nix-shell ./shell.nix --run "./scripts/python/tag-chart.sh $TAG"
RUSTFLAGS="-C debuginfo=0 -C strip=debuginfo" ./scripts/release.sh --tag $TAG --build-bins --build-binary-out $BIN --no-static-linking --skip-publish --debug
RUSTFLAGS="-C debuginfo=0 -C strip=debuginfo" ./scripts/release.sh --tag $TAG --build-binary-out $TEST_DIR --no-static-linking --skip-publish --debug
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "bin=$BIN" >> $GITHUB_OUTPUT
echo "bin=$TEST_DIR" >> $GITHUB_OUTPUT
- name: BootStrap k8s cluster
run: |
nix-shell ./scripts/k8s/shell.nix --run "./scripts/k8s/deployer.sh start --label"
- name: Load images to Kind cluster
run: nix-shell ./scripts/k8s/shell.nix --run "./scripts/k8s/load-images-to-kind.sh --tag ${{ steps.build.outputs.tag }} --trim-debug-suffix"
- name: Run Pytests
run: nix-shell ./shell.nix --run './scripts/python/test.sh'
run: |
export UPGRADE_TARGET_VERSION=${{ steps.build.outputs.tag }}
export TEST_DIR=${{ steps.build.outputs.bin }}
nix-shell ./shell.nix --run "./scripts/python/test.sh"
- name: The job has failed
if: ${{ failure() }}
run: |
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ __pycache__
/kubectl-plugin

# Pytest assets
/test-bin-*
/test-dir-*
tests/bdd/venv
pytest.log
7 changes: 7 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[pytest]
log_cli = true
log_cli_level = INFO
log_cli_format = %(asctime)s [%(levelname)s] %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = pytest.log
log_file_level = DEBUG
4 changes: 0 additions & 4 deletions scripts/helm/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ die() {
exit "${_return}"
}

nvme_ana_check() {
cat /sys/module/nvme_core/parameters/multipath
}

while [ "$#" -gt 0 ]; do
case $1 in
-h|--help)
Expand Down
3 changes: 3 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ in
mkShell {
name = "extensions-shell";
buildInputs = [
autoflake
black
cacert
cargo-expand
cargo-udeps
Expand All @@ -27,6 +29,7 @@ mkShell {
cowsay
git
helm-docs
isort
kubectl
kubernetes-helm-wrapped
llvmPackages.libclang
Expand Down
17 changes: 17 additions & 0 deletions tests/bdd/common/environment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import logging
import os

logger = logging.getLogger(__name__)


def get_env(variable: str):
try:
value = os.getenv(variable)
if len(value) == 0:
raise ValueError("Env {variable} is empty")
logger.info(f"Found env {variable}={value}")
return value

except Exception as e:
logger.error(f"Failed to get env {variable}: {e}")
return None
305 changes: 305 additions & 0 deletions tests/bdd/common/helm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
import json
import logging
import os
import subprocess
from enum import Enum
from shutil import which

from common import repo
from common.environment import get_env

logger = logging.getLogger(__name__)

helm_bin = which("helm")


def repo_ls():
try:
result = subprocess.run(
[helm_bin, "repo", "ls", "-o", "json"],
capture_output=True,
check=True,
text=True,
)
return json.loads(result.stdout.strip())

except subprocess.CalledProcessError as e:
logger.error(
f"Error: command 'helm repo ls -o json' failed with exit code {e.returncode}"
)
logger.error(f"Error Output: {e.stderr}")
return None

except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None


def repo_add_mayastor():
repos = repo_ls()
if repos is not None:
for r in repos:
if r["url"] == "https://openebs.github.io/mayastor-extensions":
return r["name"]

try:
repo_name = "mayastor"
subprocess.run(
[
helm_bin,
"repo",
"add",
repo_name,
"https://openebs.github.io/mayastor-extensions",
],
capture_output=True,
check=True,
text=True,
)

subprocess.run(
[
helm_bin,
"repo",
"update",
],
capture_output=True,
check=True,
text=True,
)
return repo_name

except subprocess.CalledProcessError as e:
logger.error(
f"Error: command 'helm repo add mayastor https://openebs.github.io/mayastor-extensions' failed with exit code {e.returncode}"
)
logger.error(f"Error Output: {e.stderr}")
return None

except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None


def latest_chart_so_far(version=None):
if version is None:
v = get_env("UPGRADE_TARGET_VERSION")
if v is None:
version = generate_test_tag()
else:
version = v

repo_name = repo_add_mayastor()
assert repo_name is not None

helm_search_command = [
helm_bin,
"search",
"repo",
repo_name + "/mayastor",
"--version",
"<" + version,
"-o",
"json",
]
try:
result = subprocess.run(
helm_search_command,
capture_output=True,
check=True,
text=True,
)
result_chart_info = json.loads(result.stdout.strip())
return result_chart_info[0]["version"]

except subprocess.CalledProcessError as e:
logger.error(
f"Error: command {helm_search_command} failed with exit code {e.returncode}"
)
logger.error(f"Error Output: {e.stderr}")
return None

except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None


class ChartSource(Enum):
HOSTED = "mayastor"
LOCAL = [
"/bin/bash",
"-c",
os.path.join(repo.root_dir(), "scripts/helm/install.sh")
+ " --dep-update --wait",
]


class HelmReleaseClient:
"""
A client for interacting with Helm releases in a specified Kubernetes namespace.
Attributes:
namespace (str): The Kubernetes namespace where the Helm releases are managed.
"""

def __init__(self):
"""
Initializes the HelmReleaseClient.
"""
self.namespace = "mayastor"

def get_metadata_mayastor(self):
command = [
helm_bin,
"get",
"metadata",
"mayastor",
"-n",
self.namespace,
"-o",
"json",
]
try:
result = subprocess.run(
command,
capture_output=True,
check=True,
text=True,
)
return json.loads(result.stdout.strip())

except subprocess.CalledProcessError as e:
logger.error(
f"Error: command '{command}' failed with exit code {e.returncode}"
)
logger.error(f"Error Output: {e.stderr}")
return None

except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None

def list(self):
"""
Lists the deployed Helm releases in the specified namespace.
Executes the 'helm ls' command to retrieve a list of deployed releases.
Returns:
str: A newline-separated string of deployed release names, or None if an error occurs.
"""
try:
result = subprocess.run(
[
helm_bin,
"ls",
"-n",
self.namespace,
"--deployed",
"--short",
],
capture_output=True,
check=True,
text=True,
)
return result.stdout.strip()

except subprocess.CalledProcessError as e:
logger.error(
f"Error: command 'helm ls -n {self.namespace} --deployed --short' failed with exit code {e.returncode}"
)
logger.error(f"Error Output: {e.stderr}")
return None

except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None

def release_is_deployed(self, release_name: str):
releases = self.list()
if releases is not None:
for release in releases:
if release == release_name:
return True
return False

def install_mayastor(self, source: ChartSource, version=None):
if self.release_is_deployed("mayastor"):
logger.error(
f"WARN: Helm release 'mayastor' already exists in the 'mayastor' namespace."
)
return

install_command = []
if source == ChartSource.HOSTED:
repo_name = repo_add_mayastor()
assert repo_name is not None

install_command += [
helm_bin,
"install",
"mayastor",
repo_name + "/" + source.value,
"--namespace=" + self.namespace,
"--create-namespace",
"--set",
"obs.callhome.sendReport=false,localpv-provisioner.analytics.enabled=false,etcd.livenessProbe.initialDelaySeconds=5,etcd.readinessProbe.initialDelaySeconds=5,etcd.replicaCount=1,eventing.enabled=false",
]
if version is not None:
install_command += ["--version=" + version]
install_command += ["--wait"]
logger.info(
f"Installing mayastor helm chart from hosted registry, version='{version}'"
)

if source == ChartSource.LOCAL:
install_command = source.value
logger.info("Installing mayastor helm chart from local directory")

try:
result = subprocess.run(
install_command,
capture_output=True,
check=True,
text=True,
)
logger.info("Installation succeeded")
return result.stdout.strip()

except subprocess.CalledProcessError as e:
logger.error(
f"Error: command {install_command} failed with exit code {e.returncode}"
)
logger.error(f"Error Output: {e.stderr}")
return None

except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None


def generate_test_tag():
generate_test_tag_script = [
"/bin/bash",
"-c",
os.path.join(repo.root_dir(), "scripts/python/generate-test-tag.sh"),
]
try:
result = subprocess.run(
generate_test_tag_script,
capture_output=True,
check=True,
text=True,
)
return result.stdout.strip()

except subprocess.CalledProcessError as e:
logger.error(
f"Error: command {generate_test_tag_script} failed with exit code {e.returncode}"
)
logger.error(f"Error Output: {e.stderr}")
return None

except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None
Loading

0 comments on commit 4ad5e5b

Please sign in to comment.