Skip to content

Commit 4525ced

Browse files
authored
Merge pull request #54 from isabelizimm/dev-docker-rsconnect
add local connect testing
2 parents de06543 + 718c984 commit 4525ced

10 files changed

Lines changed: 179 additions & 2 deletions

File tree

.github/workflows/tests.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,32 @@ jobs:
3535
python -m pip install -e .[dev,torch]
3636
- name: Run Tests
3737
run: |
38-
pytest --cov --cov-report xml .
38+
pytest -m 'not rsc_test' --cov --cov-report xml
3939
- name: Upload coverage
4040
uses: codecov/codecov-action@v2
41+
42+
test-rsconnect:
43+
name: "Test RSConnect"
44+
runs-on: ubuntu-latest
45+
if: ${{ !github.event.pull_request.head.repo.fork }}
46+
steps:
47+
- uses: actions/checkout@v2
48+
- uses: actions/setup-python@v2
49+
with:
50+
python-version: 3.8
51+
- name: Install dependencies
52+
run: |
53+
python -m pip install --upgrade pip
54+
python -m pip install -e .[dev,torch]
55+
- name: run RStudio Connect
56+
run: |
57+
docker-compose up --build -d
58+
make dev
59+
env:
60+
RSC_LICENSE: ${{ secrets.RSC_LICENSE }}
61+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
62+
63+
# NOTE: edited to run checks for python package
64+
- name: Run tests
65+
run: |
66+
pytest vetiver -m 'rsc_test'

Makefile

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
.PHONY: clean-pyc clean-build clean docs
22
UNAME := $(shell uname)
33

4+
SPHINX_BUILDARGS=
5+
# Note that these are keys generated by the docker rsconnect service, so are
6+
# not really secrets. They are saved to json to make it easy to use rsconnect
7+
# as multiple users from the tests
8+
RSC_API_KEYS=vetiver/tests/rsconnect_api_keys.json
9+
410
ifeq ($(UNAME), Darwin)
511
BROWSER := open
612
else
@@ -20,7 +26,9 @@ help:
2026
@echo "release - package and upload a release"
2127
@echo "dist - package"
2228
@echo "install - install the package to the active Python's site-packages"
23-
@echo "develop - install the package in development mode"
29+
@echo "dev - generate RStudio Connect API keys"
30+
@echo "dev-start - start up development RStudio Connect in Docker"
31+
@echo "dev-stop - stop RStudio Connect dev container"
2432

2533
clean: clean-build clean-pyc clean-test docs-clean
2634

@@ -73,3 +81,19 @@ install: clean
7381

7482
develop: clean-pyc
7583
python setup.py develop
84+
85+
dev: vetiver/tests/rsconnect_api_keys.json
86+
87+
dev-start:
88+
docker-compose up -d
89+
docker-compose exec -T rsconnect bash < script/setup-rsconnect/add-users.sh
90+
# curl fails with error 52 without a short sleep....
91+
sleep 5
92+
curl -s --retry 10 --retry-connrefused http://localhost:3939
93+
94+
dev-stop:
95+
docker-compose down
96+
rm -f $(RSC_API_KEYS)
97+
98+
$(RSC_API_KEYS): dev-start
99+
python script/setup-rsconnect/dump_api_keys.py $@

docker-compose.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
version: '3.1'
2+
3+
services:
4+
5+
rsconnect:
6+
image: rstudio/rstudio-connect:2021.12.1
7+
restart: always
8+
ports:
9+
- 3939:3939
10+
volumes:
11+
- $PWD/script/setup-rsconnect/users.txt:/etc/users.txt
12+
- $PWD/script/setup-rsconnect/rstudio-connect.gcfg:/etc/rstudio-connect/rstudio-connect.gcfg
13+
# by default, mysql rounds to 4 decimals, but tests require more precision
14+
privileged: true
15+
environment:
16+
RSTUDIO_CONNECT_HASTE: "enabled"
17+
RSC_LICENSE: ${RSC_LICENSE}

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
[build-system]
22
requires = ["setuptools>=45", "wheel"]
33
build-backend = "setuptools.build_meta"
4+
5+
[tool.pytest.ini_options]
6+
testpaths = ["vetiver"]
7+
addopts = "--doctest-modules"
8+
doctest_optionflags = "NORMALIZE_WHITESPACE"
9+
markers = [
10+
"rsc_test: tests for rstudio connect",
11+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
awk ' { system("useradd -m -s /bin/bash "$1); system("echo \""$1":"$2"\" | chpasswd"); system("id "$1) } ' /etc/users.txt
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import json
2+
import sys
3+
4+
from pins.rsconnect.api import _HackyConnect
5+
6+
OUT_FILE = sys.argv[1]
7+
8+
9+
def get_api_key(user, password, email):
10+
rsc = _HackyConnect("http://localhost:3939")
11+
12+
return rsc.create_first_admin(user, password, email).api_key
13+
14+
15+
api_keys = {
16+
"admin": get_api_key("admin", "admin0", "admin@example.com"),
17+
"susan": get_api_key("susan", "susan", "susan@example.com"),
18+
"derek": get_api_key("derek", "derek", "derek@example.com"),
19+
}
20+
21+
json.dump(api_keys, open(OUT_FILE, "w"))
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[Server]
2+
DataDir = /data
3+
Address = http://localhost:3939
4+
5+
[HTTP]
6+
Listen = :3939
7+
8+
[Authentication]
9+
Provider = pam
10+
11+
[Authorization]
12+
DefaultUserRole = publisher
13+
14+
[Python]
15+
Enabled = false
16+
17+
[RPackageRepository "CRAN"]
18+
URL = https://packagemanager.rstudio.com/cran/__linux__/bionic/latest
19+
20+
[RPackageRepository "RSPM"]
21+
URL = https://packagemanager.rstudio.com/cran/__linux__/bionic/latest

script/setup-rsconnect/users.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
admin admin0
2+
test test
3+
susan susan
4+
derek derek
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"admin": "iv6yzMB4GwhQIXjI3lOWG2T21N02C9SU", "susan": "llQqii6bHqV5R9eMN7qpTvJqr3xtnRmy", "derek": "FkakZvdVk2ApxDVkbEn6u2g0eSul4tE6"}

vetiver/tests/test_rsconnect.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import pytest
2+
import json
3+
from pins.rsconnect.fs import RsConnectFs
4+
from pins.boards import BoardRsConnect
5+
6+
RSC_SERVER_URL = "http://localhost:3939"
7+
RSC_KEYS_FNAME = "vetiver/tests/rsconnect_api_keys.json"
8+
9+
pytestmark = pytest.mark.rsc_test # noqa
10+
11+
def rsc_from_key(name):
12+
from pins.rsconnect.api import RsConnectApi
13+
14+
with open(RSC_KEYS_FNAME) as f:
15+
api_key = json.load(f)[name]
16+
return RsConnectApi(RSC_SERVER_URL, api_key)
17+
18+
def rsc_fs_from_key(name):
19+
from pins.rsconnect.fs import RsConnectFs
20+
21+
rsc = rsc_from_key(name)
22+
23+
return RsConnectFs(rsc)
24+
25+
26+
def rsc_delete_user_content(rsc):
27+
guid = rsc.get_user()["guid"]
28+
content = rsc.get_content(owner_guid=guid)
29+
for entry in content:
30+
rsc.delete_content_item(entry["guid"])
31+
32+
@pytest.fixture(scope="function")
33+
def rsc_short():
34+
# tears down content after each test
35+
fs_susan = rsc_fs_from_key("susan")
36+
37+
# delete any content that might already exist
38+
rsc_delete_user_content(fs_susan.api)
39+
40+
yield BoardRsConnect("", fs_susan, allow_pickle_read=True) #fs_susan.ls to list content
41+
42+
rsc_delete_user_content(fs_susan.api)
43+
44+
from vetiver import VetiverModel, vetiver_pin_write, mock
45+
import sklearn
46+
# Load data, model
47+
X_df, y = mock.get_mock_data()
48+
model = mock.get_mock_model().fit(X_df, y)
49+
50+
def test_board_pin_write(rsc_short):
51+
v = VetiverModel(model=model, ptype_data=X_df,
52+
model_name="susan/model", versioned=None)
53+
vetiver_pin_write(board=rsc_short, model=v)
54+
assert isinstance(rsc_short.pin_read("susan/model"), sklearn.dummy.DummyRegressor)

0 commit comments

Comments
 (0)