Skip to content

Commit

Permalink
Add Windows CircleCI test job
Browse files Browse the repository at this point in the history
Summary: We will use S2MS for testing on Windows

Test Plan: https://app.circleci.com/pipelines/github/memsql/SingleStoreNETConnector/142/workflows/2edb516a-e9a0-4325-bf66-bdc340c80a59

Reviewers: mshcherbina-ua, okramarenko-ua

Reviewed By: mshcherbina-ua

Subscribers: engineering-list

JIRA Issues: PLAT-6093

Differential Revision: https://grizzly.internal.memcompute.com/D54941
  • Loading branch information
Pavlo committed Feb 17, 2022
1 parent 787e720 commit 39b090b
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .circleci/SideBySide/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"Data": {
"ConnectionString": "server=127.0.0.1;user id=SQL_USER_NAME;password=SQL_USER_PASSWORD;port=3306;database=singlestoretest",
"ConnectionString": "server=SINGLESTORE_HOST;user id=SQL_USER_NAME;password=SQL_USER_PASSWORD;port=3306;database=singlestoretest",
"UnsupportedFeatures": "CachingSha2Password,Ed25519,QueryAttributes,Tls11,Tls13,UuidToBin,UnixDomainSocket,Sha256Password,GlobalLog"
}
}
59 changes: 55 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ parameters:
type: string
default: "1.0.0"

orbs:
win: circleci/[email protected]

commands:
setup-environment-ubuntu:
description: "Setup the linux environment"
description: Setup Linux environment
steps:
- run:
name: Install .NET Core 6.0
Expand All @@ -20,8 +23,52 @@ commands:
sudo apt-get install -y mariadb-client-core-10.3
sudo apt-get install -y dotnet-sdk-6.0
dotnet --info
run-tests-win:
description: Run tests on Windows
parameters:
target_framework:
type: string
steps:
- run:
name: Unit tests
command: |
cd tests\MySqlConnector.Tests
dotnet.exe test -f << parameters.target_framework >> -c Release --no-build
cd ..\..
- run:
name: Conformance tests
command: |
cd tests/Conformance.Tests/
dotnet.exe test -f << parameters.target_framework >> -c Release --no-build
cd ..\..
- run:
name: SideBySide tests
command: |
cd tests\SideBySide
dotnet.exe test -f << parameters.target_framework >> -c Release --no-build
cd ..\..
jobs:
test-windows:
executor: win/default
steps:
- checkout
- run:
name: Build project binaries
command: |
choco upgrade dotnet-sdk
dotnet.exe build -c Release
- run:
name: Start SingleStore for SideBySide tests
command: |
pip install pymysql
python.exe .circleci\s2ms_cluster.py start singlestoretest
- run:
name: Fill test config
command: python.exe .circleci\fill_test_config.py
- run-tests-win:
target_framework: net6.0

test-ubuntu:
parameters:
singlestore_image:
Expand All @@ -46,25 +93,27 @@ jobs:
name: Copy config file for SideBySide tests
command: |
cp ./.circleci/SideBySide/config.json tests/SideBySide/config.json
sed -i "s|SINGLESTORE_HOST|127.0.0.1|g" tests/SideBySide/config.json
sed -i "s|SQL_USER_PASSWORD|${SQL_USER_PASSWORD}|g" tests/SideBySide/config.json
sed -i "s|SQL_USER_NAME|root|g" tests/SideBySide/config.json
cp tests/SideBySide/config.json tests/SideBySide/bin/Release/net6.0/config.json
- run:
name: Unit tests
command: |
cd tests/MySqlConnector.Tests
dotnet test -f net6.0 -c Release
dotnet test -f net6.0 -c Release --no-build
cd ../../
- run:
name: Conformance tests
command: |
cd tests/Conformance.Tests/
dotnet test -f net6.0 -c Release
dotnet test -f net6.0 -c Release --no-build
cd ../../
- run:
name: SideBySide tests
command: |
cd tests/SideBySide
dotnet test -f net6.0 -c Release
dotnet test -f net6.0 -c Release --no-build
cd ../../
workflows:
Expand All @@ -89,3 +138,5 @@ workflows:
parameters:
singlestore_image:
- singlestore/cluster-in-a-box:centos-7.3.13-761e3259b3-3.2.11-1.11.9
- test-windows:
name: Test S2MS on Windows
35 changes: 35 additions & 0 deletions .circleci/fill_test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
import os


CLUSTER_ID_FILE = "CLUSTER_ID"
HOSTNAME_TMPL = "svc-{}-ddl.aws-frankfurt-1.svc.singlestore.com"

NET_FRAMEWORKS = ["net452", "net461", "net472", "netcoreapp3.1", "net5.0", "net6.0"]


if __name__ == "__main__":

home_dir = os.getenv("HOMEPATH")
if home_dir is None:
home_dir = os.getenv("HOME")

with open(CLUSTER_ID_FILE, "r") as f:
cluster_id = f.read().strip()

hostname = HOSTNAME_TMPL.format(cluster_id)
password = os.getenv("SQL_USER_PASSWORD")

with open("./.circleci/SideBySide/config.json", "r") as f_in:
config_content = f_in.read()

config_content = config_content.replace("SINGLESTORE_HOST", hostname, 1)
config_content = config_content.replace("SQL_USER_PASSWORD", password, 1)
config_content = config_content.replace("SQL_USER_NAME", "admin", 1)

for target_framework in NET_FRAMEWORKS:
with open(f"tests/SideBySide/bin/Release/{target_framework}/config.json", "w") as f_out:
f_out.write(config_content)

with open(os.path.join(home_dir, "CONNECTION_STRING"), "w") as f_conn:
f_conn.write(json.loads(config_content)["Data"]["ConnectionString"])
134 changes: 134 additions & 0 deletions .circleci/s2ms_cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import json
import os
import pymysql
import requests
from requests.adapters import HTTPAdapter
import sys
from time import sleep
from typing import Dict, Optional
from urllib3 import Retry

BASE_URL = "https://api.singlestore.com"
CLUSTERS_PATH = "/v0beta/clusters"

SQL_USER_PASSWORD = os.getenv("SQL_USER_PASSWORD") # project UI env-var reference
S2MS_API_KEY = os.getenv("S2MS_API_KEY") # project UI env-var reference

HEADERS = {
"Authorization": f"Bearer {S2MS_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json"
}

CLUSTER_NAME = ".NET-connector-ci-test-cluster"
AWS_EU_CENTRAL_REGION = "7e7ffd27-20f7-44b6-87e6-e72828a81ac7"
AUTO_TERMINATE_MINUTES = 30

PAYLOAD_FOR_CREATE = {
"name": CLUSTER_NAME,
"regionID": AWS_EU_CENTRAL_REGION,
"adminPassword": SQL_USER_PASSWORD,
"expiresAt": f"{AUTO_TERMINATE_MINUTES}m",
"firewallRanges": [
"0.0.0.0/0"
],
"size": "S-00"
}
HOSTNAME_TMPL = "svc-{}-ddl.aws-frankfurt-1.svc.singlestore.com"
CLUSTER_ID_FILE = "CLUSTER_ID"

TOTAL_RETRIES = 5
S2MS_REQUEST_TIMEOUT = 60


def request_with_retry(request_method, url, data=None, headers=HEADERS):
try:
with requests.Session() as s:
retries = Retry(
total=TOTAL_RETRIES,
backoff_factor=0.2,
status_forcelist=[500, 502, 503, 504])

s.mount('http://', HTTPAdapter(max_retries=retries))
s.mount('https://', HTTPAdapter(max_retries=retries))

return s.request(request_method, url, data=data, headers=headers, timeout=S2MS_REQUEST_TIMEOUT)
except requests.exceptions.RequestException as e:
raise SystemExit(e)


def create_cluster() -> str:
cl_id = request_with_retry("POST", BASE_URL + CLUSTERS_PATH, data=json.dumps(PAYLOAD_FOR_CREATE))
return cl_id.json()["clusterID"]


def get_cluster_info(cluster_id: str) -> Dict:
cl_id = request_with_retry("GET", BASE_URL + CLUSTERS_PATH + f"/{cluster_id}")
return cl_id.json()


def is_cluster_active(cluster_id: str) -> bool:
cl_info = get_cluster_info(cluster_id)
return cl_info["state"] == "Active"


def wait_start(cluster_id: str) -> None:
print(f"Waiting for cluster {cluster_id} to be available for connection..", end="", flush=True)
time_wait = 0
while (not is_cluster_active(cluster_id) and time_wait < 600):
print(".", end="", flush=True)
sleep(5)
time_wait += 5
if time_wait < 600:
print("\nCluster is active!")
else:
print(f"\nTimeout error: can't connect to {cluster_id} for more than 10 minutes!")


def terminate_cluster(cluster_id: str) -> None:
request_with_retry("DELETE", BASE_URL + CLUSTERS_PATH + f"/{cluster_id}")


def check_connection(cluster_id: str, create_db: Optional[str] = None):
conn = pymysql.connect(
user="admin",
password=SQL_USER_PASSWORD,
host=HOSTNAME_TMPL.format(cluster_id),
port=3306)

cur = conn.cursor()
try:
cur.execute("SELECT NOW():>TEXT")
res = cur.fetchall()
print(f"Successfully connected to {cluster_id} at {res[0][0]}")

if create_db is not None:
cur.execute(f"DROP DATABASE IF EXISTS {create_db}")
cur.execute(f"CREATE DATABASE {create_db}")
finally:
cur.close()
conn.close()


if __name__ == "__main__":
if len(sys.argv) < 2:
print("Not enough arguments to start/terminate cluster!")
exit(1)
command = sys.argv[1]
db_name = None
if len(sys.argv) > 2:
db_name = sys.argv[2]

if command == "start":
new_cl_id = create_cluster()
with open(CLUSTER_ID_FILE, "w") as f:
f.write(new_cl_id)
wait_start(new_cl_id)
check_connection(new_cl_id, db_name)
exit(0)

if command == "terminate":
with open(CLUSTER_ID_FILE, "r") as f:
cl_id = f.read()
terminate_cluster(cl_id)
exit(0)
1 change: 1 addition & 0 deletions .circleci/setup_cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ singlestore-wait-start() {
echo -n "."
sleep 0.2
done
mysql -u root -h 127.0.0.1 -P 3306 -p"${SQL_USER_PASSWORD}" -e "create database if not exists singlestoretest" >/dev/null 2>/dev/null
echo ". Success!"
}

Expand Down
16 changes: 15 additions & 1 deletion tests/Conformance.Tests/DbFactoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,21 @@ public class DbFactoryFixture : IDbFactoryFixture
public DbFactoryFixture()
{
String sqlUserPassword = Environment.GetEnvironmentVariable("SQL_USER_PASSWORD") ?? "pass";
ConnectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING") ?? String.Format("Server=localhost;User Id=root;Password={0};SSL Mode=None", sqlUserPassword);

String home = Environment.GetEnvironmentVariable("HOMEPATH") ?? "~";
String connectionStringFile = System.IO.Path.Join(home, "CONNECTION_STRING");

string connectionString;
try
{
connectionString = System.IO.File.ReadAllText(connectionStringFile);
}
catch (System.Exception)
{
connectionString = "";
}

ConnectionString = connectionString.Length > 0 ? connectionString : String.Format("Server=localhost;Port=3306;User Id=root;Password={0};SSL Mode=None", sqlUserPassword);
}

public string ConnectionString { get; }
Expand Down

0 comments on commit 39b090b

Please sign in to comment.