sts-sponsors team mailing list archive
-
sts-sponsors team
-
Mailing list archive
-
Message #06240
[Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
Thorsten Merten has proposed merging ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main.
Commit message:
feat: add test data and import tools
docs: add documentation wrt test data
* how to clear db
* how to add test data
feat: make datamodel reflect input from UX
* change machine status properties
* remove identifier
* add country
* change timezone to decimal (3 char string is ambiguous)
* add users table
* rename tables
Requested reviews:
MAAS Committers (maas-committers)
For more details, see:
https://code.launchpad.net/~thorsten-merten/maas-site-manager/+git/maas-site-manager/+merge/439266
--
Your team MAAS Committers is requested to review the proposed merge of ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main.
diff --git a/backend/README.md b/backend/README.md
index 9bf524c..966db8c 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -86,3 +86,49 @@ The application can be run via
```
tox -e run run
```
+
+## Delete and recreate the database
+
+There is not yet functionality to migrate the database with MAAS site manager.
+However a database is created for you on application startup. If the schema changes
+or you just want a fresh start, you can do the following.
+
+### Using docker
+
+To recreate the database stop the app (e.g. `docker compose down`) and run
+
+```
+docker volume rm maas-site-manager_postgres-data
+```
+
+A new database will be created for you on the next `docker-compose up`.
+
+### Any postgresql database
+
+(This also works if you use docker. In that case make sure to connect with the correct credentials)
+
+If you are using your own postgres you can usually do the following to empty the database:
+
+- Connect to your database
+```
+psql
+\c <database_name>
+```
+
+- Execute the following to empty and recreate the database.
+```
+DROP SCHEMA public CASCADE;
+CREATE SCHEMA public;
+
+GRANT ALL ON SCHEMA public TO postgres;
+GRANT ALL ON SCHEMA public TO public;
+```
+
+### Importing test data to the database
+
+There are CSV files available that can be used as test data.
+
+If you are running the app in docker you can easily load those
+
+- go to the test data directory `cd ../testdata`
+- run the loading script `./import.sh`
diff --git a/backend/msm/db/_tables.py b/backend/msm/db/_tables.py
index 98d1e42..6d25ecf 100644
--- a/backend/msm/db/_tables.py
+++ b/backend/msm/db/_tables.py
@@ -1,11 +1,12 @@
from uuid import uuid4
from sqlalchemy import (
+ Boolean,
Column,
- DECIMAL,
ForeignKey,
Integer,
MetaData,
+ Numeric,
String,
Table,
Text,
@@ -16,43 +17,58 @@ from sqlalchemy.types import DateTime
METADATA = MetaData()
Site = Table(
- "site",
+ "sites",
METADATA,
- Column("identifier", String(250), unique=True),
- Column("city", String(250)),
Column("id", Integer, primary_key=True, index=True),
+ Column("city", String(250)),
+ # ISO 3166 Alpha2
+ Column("country", String(2)),
# Decimal(8/6)/(9/6) = 16cm precision
- Column("latitude", DECIMAL(precision=8, scale=6)),
- Column("longitude", DECIMAL(precision=9, scale=6)),
- Column("name", String(250)),
+ Column("latitude", Numeric(precision=8, scale=6)),
+ Column("longitude", Numeric(precision=9, scale=6)),
+ Column("name", String(250), unique=True),
Column("note", Text),
Column("region", String(250)),
Column("street", String(250)),
- Column("timezone", String(3)),
+ # Timezones need be up to x.25 accuracy
+ Column("timezone", Numeric(precision=3, scale=2)),
Column("url", String(2048)),
)
+User = Table(
+ "users",
+ METADATA,
+ Column("id", Integer, primary_key=True, index=True),
+ Column("email", String(250), unique=True, index=True),
+ Column("full_name", String),
+ # this is the hashed password
+ Column("password", String(100)),
+ Column("disabled", Boolean),
+)
+
+
Token = Table(
- "token",
+ "tokens",
METADATA,
Column("id", Integer, primary_key=True, index=True),
- Column("site_id", Integer, ForeignKey("site.id"), index=True),
+ Column("site_id", Integer, ForeignKey("sites.id"), index=True),
Column(
"value", UUID(as_uuid=True), nullable=False, index=True, default=uuid4
),
Column("expiration", DateTime, nullable=False),
)
+
SiteData = Table(
"site_data",
METADATA,
Column("id", Integer, primary_key=True, index=True),
Column(
- "site_id", Integer, ForeignKey("site.id"), index=True, nullable=False
+ "site_id", Integer, ForeignKey("sites.id"), index=True, nullable=False
),
- Column("total_machines", Integer),
- Column("occupied_machines", Integer),
+ Column("allocated_machines", Integer),
+ Column("deployed_machines", Integer),
Column("ready_machines", Integer),
Column("error_machines", Integer),
Column("last_seen", DateTime),
diff --git a/backend/msm/db/queries.py b/backend/msm/db/queries.py
index a2d4dbc..a18ba36 100644
--- a/backend/msm/db/queries.py
+++ b/backend/msm/db/queries.py
@@ -86,7 +86,6 @@ async def get_filtered_sites(
stmt = select(
Site.c.id,
Site.c.name,
- Site.c.identifier,
Site.c.city,
Site.c.latitude,
Site.c.longitude,
@@ -106,7 +105,6 @@ async def get_sites(session: AsyncSession) -> Iterable[SiteSchema]:
stmt = select(
Site.c.id,
Site.c.name,
- Site.c.identifier,
Site.c.city,
Site.c.latitude,
Site.c.longitude,
diff --git a/backend/msm/schema.py b/backend/msm/schema.py
index 38a4ad5..10c726d 100644
--- a/backend/msm/schema.py
+++ b/backend/msm/schema.py
@@ -2,9 +2,42 @@ from datetime import (
datetime,
timedelta,
)
+from decimal import Decimal
from uuid import UUID
-from pydantic import BaseModel
+from pydantic import (
+ BaseModel,
+ SecretStr,
+)
+from pydantic.fields import Field
+
+# see discussion at
+# https://stackabuse.com/python-validate-email-address-with-regular-expressions-regex/
+valid_email = r"""
+ ([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|
+ \"([]!#-[^-~ \t]|(\\[\t -~]))+\")
+ @
+ ([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*])"""
+
+
+class CreateUser(BaseModel):
+ """
+ A MAAS Site Manager User
+ """
+
+ email: str = Field(
+ title="email@xxxxxxxxxxx",
+ max_length=250,
+ min_length=3,
+ regex=valid_email,
+ )
+ full_name: str
+ password: SecretStr = Field(min_length=8, max_length=50)
+ disabled: bool
+
+
+class User(CreateUser):
+ id: int
class CreateSite(BaseModel):
@@ -13,14 +46,13 @@ class CreateSite(BaseModel):
"""
name: str
- identifier: str
city: str | None
- latitude: str | None
- longitude: str | None
+ latitude: str | Decimal | None
+ longitude: str | Decimal | None
note: str | None
region: str | None
street: str | None
- timezone: str | None
+ timezone: str | Decimal | None
url: str
# TODO: we will need to add tags
@@ -39,9 +71,8 @@ class CreateSiteData(BaseModel):
"""
site_id: int
- total_machines: int
- # TODO: might include more states in future
- occupied_machines: int # TODO: name might change
+ allocated_machines: int
+ deployed_machines: int
ready_machines: int
error_machines: int
last_seen: datetime
diff --git a/backend/msm/user_api/tests/test_handlers.py b/backend/msm/user_api/tests/test_handlers.py
index 20f9b5e..4ffe3e9 100644
--- a/backend/msm/user_api/tests/test_handlers.py
+++ b/backend/msm/user_api/tests/test_handlers.py
@@ -22,22 +22,21 @@ async def test_list_sites(
site1 = {
"id": 1,
"name": "LondonHQ",
- "identifier": "site one",
"city": "London",
"latitude": "51.509865",
"longitude": "-0.118092",
"note": "the first site",
"region": "Blue Fin Bldg",
"street": "110 Southwark St",
- "timezone": "GMT",
+ "timezone": "0.00",
"url": "https://londoncalling.example.com",
}
site2 = site1.copy()
site2["id"] = 2
- site2["identifier"] = "site two"
site2["name"] = "BerlinHQ"
+ site2["timezone"] = "-7.00"
site2["city"] = "Berlin"
- await fixture.create("site", [site1, site2])
+ await fixture.create("sites", [site1, site2])
response = user_app_client.get("/sites?city=onDo") # vs London
assert response.status_code == 200
assert response.json() == [site1]
@@ -76,7 +75,7 @@ async def test_list_tokens(
"expiration": datetime.fromisoformat("2023-02-23T11:28:54.382456"),
},
]
- await fixture.create("token", tokens)
+ await fixture.create("tokens", tokens)
response = user_app_client.get("/tokens")
assert response.status_code == 200
assert len(response.json()) == 2
diff --git a/mocks/import.sh b/mocks/import.sh
new file mode 100755
index 0000000..592405c
--- /dev/null
+++ b/mocks/import.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# This script imports the testdata from this directory into the postgres database
+#
+psql=psql
+envfile="../.env.dev"
+
+if ! command -v psql > /dev/null
+then
+ echo "The ${psql} command was not found. It is need it to import the data. Try:"
+ echo "apt-get install postgresql-client"
+ exit 1
+fi
+
+if ! test -s ${envfile}
+then
+ echo "Cannot source dev environment. Make sure ../.env.dev exists."
+ exit 1
+fi
+. ${envfile}
+
+docker compose cp users.csv postgres:/
+docker compose cp sites.csv postgres:/
+docker compose cp tokens.csv postgres:/
+docker compose cp site_data.csv postgres:/
+
+${psql} postgresql://"${POSTGRES_USER}":"${POSTGRES_PASSWORD}"@localhost:"${POSTGRES_PORT}"/"${POSTGRES_DB}" -f import.sql
diff --git a/mocks/import.sql b/mocks/import.sql
new file mode 100644
index 0000000..a7198af
--- /dev/null
+++ b/mocks/import.sql
@@ -0,0 +1,23 @@
+COPY sites(id, city, country, latitude, longitude, name, note, region, street, timezone, url)
+FROM '/sites.csv'
+DELIMITER ','
+QUOTE '"'
+CSV HEADER;
+
+COPY tokens(site_id, value, expiration)
+FROM '/tokens.csv'
+DELIMITER ','
+QUOTE '"'
+CSV HEADER;
+
+COPY users(email, full_name, disabled, password)
+FROM '/users.csv'
+DELIMITER ','
+QUOTE '"'
+CSV HEADER;
+
+COPY site_data(site_id, allocated_machines, deployed_machines, ready_machines, error_machines, last_seen)
+FROM '/site_data.csv'
+DELIMITER ','
+QUOTE '"'
+CSV HEADER;
diff --git a/mocks/site_data.csv b/mocks/site_data.csv
new file mode 100644
index 0000000..e6a4960
--- /dev/null
+++ b/mocks/site_data.csv
@@ -0,0 +1,7 @@
+site_id, allocated_machines, deployed_machines, ready_machines, error_machines, last_seen
+1,10,1,8,1,"01-01-2023 08:00"
+2,11,0,8,3,"01-01-2023 09:00"
+3,12,2,4,6,"01-01-2023 10:00"
+4,13,0,13,0,"01-01-2023 11:00"
+5,14,13,1,0,"01-01-2023 12:00"
+6,15,2,10,3,"01-01-2023 13:00"
diff --git a/mocks/sites.csv b/mocks/sites.csv
new file mode 100644
index 0000000..1218db3
--- /dev/null
+++ b/mocks/sites.csv
@@ -0,0 +1,9 @@
+id,city, country, latitude, longitude, name, note, region, street, timezone, url
+1,London,GB,51.501990,-0.092200,Canonical Group Limited,4th Floor,,201 Borough High Street,0.0,https://london.canonical.example.com
+2,Austin,US,30.269612,-97.741057,Canonical USA Inc.,Perry Brooks Building - Suite 300,,720 Brazos Street,-5.0,https://austin.canonical.example.com
+3,Boston,US,42.358859,-71.059615,Canonical USA Inc. 001,Suite 210,,18 Tremont Street,-4.0,https://boston.canonical.example.com
+4,Shanghai,CN,31.187270,121.436829,Canonical China,上海市漕溪北路331号12楼1246室,,No. 331 North Caoxi Road,+8.0,https://shanghai.canonical.example.com
+5,Beijing,CN,39.908447,116.448690,Canonical China 001,China World Office 1; 北京市朝阳区建国门外大街1号国贸写字楼1座11层1118-19室 100004,Chaoyang District,1 Jianguomenwai Avenue,+8.0,https://shanghai.canonical.example.com
+6,Taipei City,TW,25.058098,121.543406,Canonical Group Limited - Taiwan Branch,105402 台北市松山區民生東路三段100號12樓,Songshan Dist.,"12F.,No. 100,Sec. 3,Minsheng E. Rd.",+8.0,https://taiwan.canonical.example.com
+7,Douglas,IM,54.153072,-4.481012,Canonical Limited,2nd Floor - Clarendon House,,Victoria Street,0,https://canonical.example.com
+8,Tokyo,JP,35.673242,139.740669,Canonical Japan K.K,3rd Floor - Sanno Park Tower,,2-11-1 Nagata-cho Chiyoda-ku,9.0,https://japan.canonical.example.com
diff --git a/mocks/tokens.csv b/mocks/tokens.csv
new file mode 100644
index 0000000..0a4848b
--- /dev/null
+++ b/mocks/tokens.csv
@@ -0,0 +1,12 @@
+site_id,value,expiration
+1,c96abba0-9962-41e5-9f40-6e2b55578c06,2023-03-01 00:00:00
+2,8960d8a1-7c5e-4331-8f58-591cf145a313,2023-03-01 00:00:00
+3,dffa13fe-d2c5-43b4-81e1-1efde6eeec76,2023-03-01 00:00:00
+4,a31a7408-09ee-4403-98c1-5c59ca3534c2,2023-03-01 00:00:00
+5,4d015359-3093-4216-ac87-6dfa29d0d6c5,2023-03-01 00:00:00
+6,9f404751-a17b-456c-8fa9-46b7258cb471,2023-03-01 00:00:00
+7,269773b7-b47d-46e8-9825-eb25eda99771,2023-03-01 00:00:00
+8,fce21bd4-3a92-42af-adb4-6b46dc0aefd4,2023-03-01 00:00:00
+,0e846493-fde9-4d15-844c-2ca0341d1e84,2024-01-01 00:00:00
+,e15a7d3c-9df8-40c7-b81b-ed4796e777bc,2024-01-01 00:00:00
+,87a62d9a-7645-43b5-9dd4-eaf53e768c4a,2024-01-01 00:00:00
diff --git a/mocks/users.csv b/mocks/users.csv
new file mode 100644
index 0000000..4cc531b
--- /dev/null
+++ b/mocks/users.csv
@@ -0,0 +1,3 @@
+email, full_name, disabled, password (admin)
+admin@xxxxxxxxxxx,MAAS Admin,false,$2b$12$F5sgrhRNtWAOehcoVO.XK.oSvupmcg8.0T2jCHOTg15M8N8LrpRwS
+disabled@xxxxxxxxxxx,Obsolete Admin,true,$2b$12$iEPLFcNocyeUDgu2ywDVGeFHyrksI89bzSvdAwvU1N4zYFtofme3S
Follow ups
-
[Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: MAAS Lander, 2023-03-23
-
Re: [Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: Thorsten Merten, 2023-03-23
-
[Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: Thorsten Merten, 2023-03-23
-
Re: [Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: Peter Makowski, 2023-03-23
-
Re: [Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: Adam Collard, 2023-03-22
-
Re: [UNITTESTS] -b MAASENG-1486-add-testdata lp:~thorsten-merten/maas-site-manager/+git/maas-site-manager into -b main lp:~maas-committers/maas-site-manager - TESTS PASS
From: MAAS Lander, 2023-03-22
-
Re: [Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: Thorsten Merten, 2023-03-22
-
Re: [Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: Thorsten Merten, 2023-03-21
-
Re: [Merge] ~thorsten-merten/maas-site-manager:MAASENG-1486-add-testdata into maas-site-manager:main
From: Adam Collard, 2023-03-20
-
Re: [UNITTESTS] -b MAASENG-1486-add-testdata lp:~thorsten-merten/maas-site-manager/+git/maas-site-manager into -b main lp:~maas-committers/maas-site-manager - TESTS PASS
From: MAAS Lander, 2023-03-20