Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
Coder deployment for INF110
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Théo Zimmermann
Coder deployment for INF110
Commits
84c6063d
Unverified
Commit
84c6063d
authored
3 months ago
by
Théo Zimmermann
Browse files
Options
Downloads
Patches
Plain Diff
Convert part of the Jupyter notebook to use requests instead of HTTPie.
parent
7c4ce217
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
.gitignore
+1
-0
1 addition, 0 deletions
.gitignore
Coder-management.ipynb
+71
-61
71 additions, 61 deletions
Coder-management.ipynb
README.md
+2
-1
2 additions, 1 deletion
README.md
flake.nix
+1
-0
1 addition, 0 deletions
flake.nix
with
75 additions
and
62 deletions
.gitignore
+
1
−
0
View file @
84c6063d
openrc
users.csv
.direnv
.ipynb_checkpoints
result
...
...
This diff is collapsed.
Click to expand it.
Coder-management.ipynb
+
71
−
61
View file @
84c6063d
...
...
@@ -16,7 +16,8 @@
"outputs": [],
"source": [
"import csv\n",
"import json"
"import json\n",
"import requests"
]
},
{
...
...
@@ -47,7 +48,11 @@
"metadata": {},
"outputs": [],
"source": [
"!https GET \"tp-inf110.r2.enst.fr/api/v2/buildinfo\" | jq '{ coder_version: .version, coder_url: .dashboard_url }'"
"response = requests.get(\"https://tp-inf110.r2.enst.fr/api/v2/buildinfo\")\n",
"response.raise_for_status()\n",
"buildinfo = response.json()\n",
"print(f\"Version: {buildinfo['version']}\")\n",
"print(f\"URL: {buildinfo['dashboard_url']}\")"
]
},
{
...
...
@@ -68,55 +73,11 @@
"outputs": [],
"source": [
"user=\"Zimmi48\"\n",
"organization_id,=!https GET \"tp-inf110.r2.enst.fr/api/v2/users/{user}/organizations\" \"Coder-Session-Token:{token}\" | jq -r .[0].id"
]
},
{
"cell_type": "markdown",
"id": "e953643e-a910-4b1a-bead-70caf4e2ebdd",
"metadata": {
"jp-MarkdownHeadingCollapsed": true
},
"source": [
"## Create organization\n",
"\n",
"An organization is always created by default, so there is no point in creating another one for now."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eaf47ee5-f83c-4ebd-8828-a5c4c89db924",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"#name=\"INF110\"\n",
"#!https POST \"tp-inf110.r2.enst.fr/api/v2/organizations\" \"Coder-Session-Token:{token}\" \"name={name}\""
]
},
{
"cell_type": "markdown",
"id": "5fe6ded1-6fa1-48b4-b4bd-fc8eee714816",
"metadata": {
"jp-MarkdownHeadingCollapsed": true
},
"source": [
"## Delete organization"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f5dbf307-3352-498a-a175-93ad6ccd1155",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"#name=\"INF110\"\n",
"#!https DELETE \"tp-inf110.r2.enst.fr/api/v2/organizations/{name}\" \"Coder-Session-Token:{token}\""
"response = requests.get(f\"https://tp-inf110.r2.enst.fr/api/v2/users/{user}/organizations\", headers={\"Coder-Session-Token\": token})\n",
"response.raise_for_status()\n",
"organizations = response.json()\n",
"print(f\"Organizations: {organizations}\")\n",
"organization_id = organizations[0]['id']"
]
},
{
...
...
@@ -136,18 +97,64 @@
"metadata": {},
"outputs": [],
"source": [
"template_id,template_version=!https GET \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates\" \"Coder-Session-Token:{token}\" | jq -r '.[0] | .id, .active_version_id'\n",
"if template_id == 'null':\n",
"# Test if template exists\n",
"response = requests.get(f\"https://tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates\", headers={\"Coder-Session-Token\": token})\n",
"response.raise_for_status()\n",
"templates = response.json()\n",
"if len(templates) == 1:\n",
" template_id = templates[0]['id']\n",
" template_version = templates[0]['active_version_id']\n",
" print(f\"Template found: {template_id} (version {template_version})\")\n",
"elif len(templates) == 0:\n",
" print(\"No template found. Creating one...\")\n",
" # Create zip file to upload\n",
" !cd inf110-workspace && zip \"template.zip\" \"main.tf\" \"README.md\"\n",
" # Upload template files\n",
" file_hash=!cd inf110-workspace && zip \"template.zip\" \"main.tf\" \"README.md\" && https POST \"tp-inf110.r2.enst.fr/api/v2/files\" \"Coder-Session-Token:{token}\" \"Content-Type:application/zip\" < \"template.zip\" | jq -r '.hash'\n",
" with open(\"inf110-workspace/template.zip\", \"rb\") as f:\n",
" response = requests.post(\n",
" \"https://tp-inf110.r2.enst.fr/api/v2/files\",\n",
" headers={\n",
" \"Coder-Session-Token\": token,\n",
" \"Content-Type\": \"application/zip\"\n",
" },\n",
" data=f\n",
" )\n",
" response.raise_for_status()\n",
" file_hash = response.json()['hash']\n",
" # Create template version\n",
" version_name='INF110-2024'\n",
" template_version=!https POST \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templateversions\" \"Coder-Session-Token:{token}\" \"name={version_name}\" \"file_id={file_hash}\" \"storage_method=file\" \"provisioner=terraform\" | jq -r '.id'\n",
" response = requests.post(\n",
" f\"https://tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templateversions\",\n",
" headers={\"Coder-Session-Token\": token},\n",
" json={\n",
" \"name\": version_name,\n",
" \"file_id\": file_hash,\n",
" \"storage_method\": \"file\",\n",
" \"provisioner\": \"terraform\"\n",
" }\n",
" )\n",
" response.raise_for_status()\n",
" template_version = response.json()['id']\n",
" # Create template\n",
" template_name='INF110'\n",
" display_name='TP INF110'\n",
" description=\"Espace de travail pour les TP d'INF110\"\n",
" template_id=!https POST \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates\" \"Coder-Session-Token:{token}\" \"name={template_name}\" \"display_name={display_name}\" \"description={description}\" \"icon=/emojis/1f4d0.png\" \"template_version_id={template_version}\" | jq -r '.id'"
" response = requests.post(\n",
" f\"https://tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates\",\n",
" headers={\"Coder-Session-Token\": token},\n",
" json={\n",
" \"name\": template_name,\n",
" \"display_name\": display_name,\n",
" \"description\": description,\n",
" \"icon\": \"/emojis/1f4d0.png\",\n",
" \"template_version_id\": template_version\n",
" }\n",
" )\n",
" response.raise_for_status()\n",
" template_id = response.json()['id']\n",
" print(f\"Template created: {template_id} (version {template_version})\")\n",
"else:\n",
" raise ValueError(\"Multiple templates found\")"
]
},
{
...
...
@@ -167,7 +174,10 @@
},
"outputs": [],
"source": [
"!https GET \"tp-inf110.r2.enst.fr/api/v2/templateversions/{template_version}\" \"Coder-Session-Token:{token}\" | jq '.job.status'"
"response = requests.get(f\"https://tp-inf110.r2.enst.fr/api/v2/templateversions/{template_version}\", headers={\"Coder-Session-Token\": token})\n",
"response.raise_for_status()\n",
"template_version = response.json()\n",
"print(f\"Template version: {template_version['job']['status']}\")"
]
},
{
...
...
@@ -191,7 +201,7 @@
" !echo \"{email},{username},{password}\" >> users.csv && \\\n",
" https POST \"tp-inf110.r2.enst.fr/api/v2/users\" \"Coder-Session-Token:{token}\" \"email={email}\" \"login_type=password\" \"password={password}\" \"username={username}\"\n",
"\n",
"#create_user(\"
jean.leneutre@telecom-paris.fr\", \"jleneutre
\")"
"#create_user(\"
\", \"
\")"
]
},
{
...
...
@@ -257,7 +267,7 @@
"def create_workspace(username, name='tp'):\n",
" !https POST \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/members/{username}/workspaces\" \"Coder-Session-Token:{token}\" \"name={name}\" \"template_id={template_id}\"\n",
"\n",
"
#
create_workspace(\"Zimmi48\")"
"create_workspace(\"Zimmi48\")"
]
},
{
...
...
@@ -277,8 +287,8 @@
},
"outputs": [],
"source": [
"
#
for i in range(0,99):\n",
"
#
create_workspace('Zimmi48', 'autoscaling-test-%d' % i)"
"for i in range(0,99):\n",
" create_workspace('Zimmi48', 'autoscaling-test-%d' % i)"
]
},
{
...
...
%% Cell type:markdown id:6d924ae8-e462-4c47-86d9-8560ecedf78f tags:
# Coder management
%% Cell type:code id:bb33cb2d-8256-4d65-9442-153a5eb920a6 tags:
```
python
import
csv
import
json
import
requests
```
%% Cell type:code id:991fc1f2-61d2-443f-901b-df6acf094287 tags:
```
python
cluster_name
=
'
inf110
'
proxyJump
=
'
ubuntu@137.194.210.143
'
```
%% Cell type:code id:ae9a0955-2a17-499a-827b-8dfe8be56d65 tags:
```
python
token
,
=
!
source
openrc
&&
echo
$
CODER_TOKEN
```
%% Cell type:code id:904c792f-2229-44cb-a98b-7714af05e163 tags:
```
python
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/buildinfo
"
|
jq
'
{ coder_version: .version, coder_url: .dashboard_url }
'
response
=
requests
.
get
(
"
https://tp-inf110.r2.enst.fr/api/v2/buildinfo
"
)
response
.
raise_for_status
()
buildinfo
=
response
.
json
()
print
(
f
"
Version:
{
buildinfo
[
'
version
'
]
}
"
)
print
(
f
"
URL:
{
buildinfo
[
'
dashboard_url
'
]
}
"
)
```
%% Cell type:markdown id:463b7432-7861-44f6-8a42-36f453bf0a24 tags:
To retrieve the ID of the default organization, we can list the organizations of the admin user.
%% Cell type:code id:3d2d2fe7-c20b-4e7d-a5da-e0c8021cb6f8 tags:
```
python
user
=
"
Zimmi48
"
organization_id
,
=
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/users/{user}/organizations
"
"
Coder-Session-Token:{token}
"
|
jq
-
r
.[
0
].
id
```
%% Cell type:markdown id:e953643e-a910-4b1a-bead-70caf4e2ebdd tags:
## Create organization
An organization is always created by default, so there is no point in creating another one for now.
%% Cell type:code id:eaf47ee5-f83c-4ebd-8828-a5c4c89db924 tags:
```
python
#name="INF110"
#!https POST "tp-inf110.r2.enst.fr/api/v2/organizations" "Coder-Session-Token:{token}" "name={name}"
```
%% Cell type:markdown id:5fe6ded1-6fa1-48b4-b4bd-fc8eee714816 tags:
## Delete organization
%% Cell type:code id:f5dbf307-3352-498a-a175-93ad6ccd1155 tags:
```
python
#name="INF110"
#!https DELETE "tp-inf110.r2.enst.fr/api/v2/organizations/{name}" "Coder-Session-Token:{token}"
response
=
requests
.
get
(
f
"
https://tp-inf110.r2.enst.fr/api/v2/users/
{
user
}
/organizations
"
,
headers
=
{
"
Coder-Session-Token
"
:
token
})
response
.
raise_for_status
()
organizations
=
response
.
json
()
print
(
f
"
Organizations:
{
organizations
}
"
)
organization_id
=
organizations
[
0
][
'
id
'
]
```
%% Cell type:markdown id:19a552b0-8bf8-4cd8-86a1-135b7e2253ed tags:
## Create workspace template
If it doesn't exist yet. Otherwise, retrieve the template and template version IDs.
%% Cell type:code id:b4db687e-3e52-448d-b4d7-8c812cdaa2f7 tags:
```
python
template_id
,
template_version
=
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates
"
"
Coder-Session-Token:{token}
"
|
jq
-
r
'
.[0] | .id, .active_version_id
'
if
template_id
==
'
null
'
:
# Test if template exists
response
=
requests
.
get
(
f
"
https://tp-inf110.r2.enst.fr/api/v2/organizations/
{
organization_id
}
/templates
"
,
headers
=
{
"
Coder-Session-Token
"
:
token
})
response
.
raise_for_status
()
templates
=
response
.
json
()
if
len
(
templates
)
==
1
:
template_id
=
templates
[
0
][
'
id
'
]
template_version
=
templates
[
0
][
'
active_version_id
'
]
print
(
f
"
Template found:
{
template_id
}
(version
{
template_version
}
)
"
)
elif
len
(
templates
)
==
0
:
print
(
"
No template found. Creating one...
"
)
# Create zip file to upload
!
cd
inf110
-
workspace
&&
zip
"
template.zip
"
"
main.tf
"
"
README.md
"
# Upload template files
file_hash
=
!
cd
inf110
-
workspace
&&
zip
"
template.zip
"
"
main.tf
"
"
README.md
"
&&
https
POST
"
tp-inf110.r2.enst.fr/api/v2/files
"
"
Coder-Session-Token:{token}
"
"
Content-Type:application/zip
"
<
"
template.zip
"
|
jq
-
r
'
.hash
'
with
open
(
"
inf110-workspace/template.zip
"
,
"
rb
"
)
as
f
:
response
=
requests
.
post
(
"
https://tp-inf110.r2.enst.fr/api/v2/files
"
,
headers
=
{
"
Coder-Session-Token
"
:
token
,
"
Content-Type
"
:
"
application/zip
"
},
data
=
f
)
response
.
raise_for_status
()
file_hash
=
response
.
json
()[
'
hash
'
]
# Create template version
version_name
=
'
INF110-2024
'
template_version
=
!
https
POST
"
tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templateversions
"
"
Coder-Session-Token:{token}
"
"
name={version_name}
"
"
file_id={file_hash}
"
"
storage_method=file
"
"
provisioner=terraform
"
|
jq
-
r
'
.id
'
response
=
requests
.
post
(
f
"
https://tp-inf110.r2.enst.fr/api/v2/organizations/
{
organization_id
}
/templateversions
"
,
headers
=
{
"
Coder-Session-Token
"
:
token
},
json
=
{
"
name
"
:
version_name
,
"
file_id
"
:
file_hash
,
"
storage_method
"
:
"
file
"
,
"
provisioner
"
:
"
terraform
"
}
)
response
.
raise_for_status
()
template_version
=
response
.
json
()[
'
id
'
]
# Create template
template_name
=
'
INF110
'
display_name
=
'
TP INF110
'
description
=
"
Espace de travail pour les TP d
'
INF110
"
template_id
=
!
https
POST
"
tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates
"
"
Coder-Session-Token:{token}
"
"
name={template_name}
"
"
display_name={display_name}
"
"
description={description}
"
"
icon=/emojis/1f4d0.png
"
"
template_version_id={template_version}
"
|
jq
-
r
'
.id
'
response
=
requests
.
post
(
f
"
https://tp-inf110.r2.enst.fr/api/v2/organizations/
{
organization_id
}
/templates
"
,
headers
=
{
"
Coder-Session-Token
"
:
token
},
json
=
{
"
name
"
:
template_name
,
"
display_name
"
:
display_name
,
"
description
"
:
description
,
"
icon
"
:
"
/emojis/1f4d0.png
"
,
"
template_version_id
"
:
template_version
}
)
response
.
raise_for_status
()
template_id
=
response
.
json
()[
'
id
'
]
print
(
f
"
Template created:
{
template_id
}
(version
{
template_version
}
)
"
)
else
:
raise
ValueError
(
"
Multiple templates found
"
)
```
%% Cell type:markdown id:e20dfefb-0359-421a-92c6-ab403de529dc tags:
The query below can be used to check that the template creation was successful.
%% Cell type:code id:1ae74b43-8065-4e5f-9976-e00a879d7c99 tags:
```
python
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/templateversions/{template_version}
"
"
Coder-Session-Token:{token}
"
|
jq
'
.job.status
'
response
=
requests
.
get
(
f
"
https://tp-inf110.r2.enst.fr/api/v2/templateversions/
{
template_version
}
"
,
headers
=
{
"
Coder-Session-Token
"
:
token
})
response
.
raise_for_status
()
template_version
=
response
.
json
()
print
(
f
"
Template version:
{
template_version
[
'
job
'
][
'
status
'
]
}
"
)
```
%% Cell type:markdown id:5d900272-a3ee-461c-9cdd-df08b17b1d2a tags:
## Create users
%% Cell type:code id:9f05d75f-c300-49ca-9faf-19a1a24dd6d8 tags:
```
python
def
create_user
(
email
,
username
):
passwords
=
!
pwgen
-
s
10
-
1
password
=
passwords
[
0
]
!
echo
"
{email},{username},{password}
"
>>
users
.
csv
&&
\
https
POST
"
tp-inf110.r2.enst.fr/api/v2/users
"
"
Coder-Session-Token:{token}
"
"
email={email}
"
"
login_type=password
"
"
password={password}
"
"
username={username}
"
#create_user("
jean.leneutre@telecom-paris.fr", "jleneutre
")
#create_user("
", "
")
```
%% Cell type:code id:96595327-81a0-4a50-b090-a79d75a3134d tags:
```
python
def
create_student_accounts
(
csv_filename
):
with
open
(
csv_filename
,
mode
=
'
r
'
)
as
csv_file
:
csv_reader
=
csv
.
DictReader
(
csv_file
)
for
student
in
csv_reader
:
create_user
(
student
[
'
mail
'
],
student
[
'
login
'
])
#create_student_accounts('inf110-2023-B.csv')
#create_student_accounts('inf110-2023-D.csv')
#create_student_accounts('MITRO202.csv')
```
%% Cell type:markdown id:9868d906-ca8c-4fda-ad96-ec00bea5fadc tags:
## Delete users
(Requires having deleted their workspaces first.)
%% Cell type:code id:30a56dca-7f8f-4839-94e7-36340f5f199f tags:
```
python
def
delete_user
(
username
):
!
https
DELETE
"
tp-inf110.r2.enst.fr/api/v2/users/{username}
"
"
Coder-Session-Token:{token}
"
#delete_user("bbinder")
```
%% Cell type:markdown id:d6d20c1f-e231-488c-90ef-2daa811e89a2 tags:
## Create workspaces
%% Cell type:code id:80ff8f4e-fa5f-4308-a885-e3d382e2a171 tags:
```
python
def
create_workspace
(
username
,
name
=
'
tp
'
):
!
https
POST
"
tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/members/{username}/workspaces
"
"
Coder-Session-Token:{token}
"
"
name={name}
"
"
template_id={template_id}
"
#
create_workspace("Zimmi48")
create_workspace
(
"
Zimmi48
"
)
```
%% Cell type:markdown id:d2cea793-4b57-4718-bd17-7cbda610d5bd tags:
Test auto-scaling by creating many workspaces at once.
%% Cell type:code id:e3d06f0c-3183-47e4-a63e-254a6c08612a tags:
```
python
#
for i in range(0,99):
#
create_workspace('Zimmi48', 'autoscaling-test-%d' % i)
for
i
in
range
(
0
,
99
):
create_workspace
(
'
Zimmi48
'
,
'
autoscaling-test-%d
'
%
i
)
```
%% Cell type:code id:11d58691-691b-41a8-bdbf-51d3b313b64f tags:
```
python
def
create_student_workspaces
(
csv_filename
):
with
open
(
csv_filename
,
mode
=
'
r
'
)
as
csv_file
:
csv_reader
=
csv
.
DictReader
(
csv_file
)
for
student
in
csv_reader
:
create_workspace
(
student
[
'
login
'
])
#create_student_workspaces('inf110-2023-B.csv')
#create_student_workspaces('inf110-2023-D.csv')
```
%% Cell type:markdown id:1b405071-7dd5-4082-80f2-dbd60ea46262 tags:
## Start / stop all workspaces from a student group
%% Cell type:code id:e9be7a12-c858-4baf-9ac7-53827330798a tags:
```
python
def
get_student_username_list
(
csv_filename
):
students
=
[]
with
open
(
csv_filename
,
mode
=
'
r
'
)
as
csv_file
:
csv_reader
=
csv
.
DictReader
(
csv_file
)
for
student
in
csv_reader
:
students
+=
[
student
[
'
login
'
]]
return
students
#groupB = get_student_username_list('inf110-2023-B.csv')
#groupD = get_student_username_list('inf110-2023-D.csv')
```
%% Cell type:code id:1640ad41-d82c-4519-88d3-ef2c2cfc2950 tags:
```
python
len
(
groupB
)
```
%% Cell type:code id:6b0b126a-b7e4-43c5-9aab-2c27931ebf91 tags:
```
python
len
(
groupD
)
```
%% Cell type:code id:b495de0f-d085-4db6-a8a4-9a022f345251 tags:
```
python
def
stop_workspaces_from_group
(
group
):
workspaces
=
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/workspaces
"
"
Coder-Session-Token:{token}
"
"
limit==100
"
"
q==status:running
"
-
b
|
jq
-
cr
'
.workspaces | .[]
'
workspaces
=
list
(
map
(
json
.
loads
,
workspaces
))
for
workspace
in
[
workspace
[
'
id
'
]
for
workspace
in
workspaces
if
workspace
[
'
owner_name
'
]
in
group
]:
!
https
POST
"
tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace}/builds
"
"
Coder-Session-Token:{token}
"
"
transition=stop
"
stop_workspaces_from_group
([
'
Zimmi48
'
])
```
%% Cell type:code id:13f05a6e-f591-4bcd-9857-63f5a21f1028 tags:
```
python
def
start_workspaces_from_group
(
group
):
workspaces
=
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/workspaces
"
"
Coder-Session-Token:{token}
"
"
limit==100
"
"
q==status:stopped
"
-
b
|
jq
-
cr
'
.workspaces | .[]
'
workspaces
=
list
(
map
(
json
.
loads
,
workspaces
))
for
workspace
in
[
workspace
[
'
id
'
]
for
workspace
in
workspaces
if
workspace
[
'
owner_name
'
]
in
group
]:
!
https
POST
"
tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace}/builds
"
"
Coder-Session-Token:{token}
"
"
transition=start
"
"
template_version_id={template_version}
"
start_workspaces_from_group
(
mitro
)
```
%% Cell type:markdown id:e8e88940-8c7c-4b3c-8923-aaf0b813f524 tags:
## Delete all workspaces and users from a student group
%% Cell type:code id:5c22d1b3-923f-45fd-ac04-9f404e5002b6 tags:
```
python
def
delete_workspaces_from_group
(
group
):
workspaces
=
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/workspaces
"
"
Coder-Session-Token:{token}
"
"
limit==100
"
"
q==status:stopped
"
-
b
|
jq
-
cr
'
.workspaces | .[]
'
workspaces
=
list
(
map
(
json
.
loads
,
workspaces
))
for
workspace
in
[
workspace
[
'
id
'
]
for
workspace
in
workspaces
if
workspace
[
'
owner_name
'
]
in
group
]:
!
https
POST
"
tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace}/builds
"
"
Coder-Session-Token:{token}
"
"
transition=delete
"
delete_workspaces_from_group
([
'
Zimmi48
'
])
```
%% Cell type:code id:eafaba21-fdf9-46e9-ba8f-bc1f25de96a4 tags:
```
python
def
delete_students
(
group
):
for
student
in
group
:
delete_user
(
student
)
delete_students
(
groupD
)
```
%% Cell type:markdown id:12c57f21-23fe-4752-ad28-b05bd33ff08f tags:
## Stop all unhealthy workspaces
%% Cell type:code id:73ce34f9-b0ad-4434-b448-6e2fdcfed6bd tags:
```
python
def
stop_unhealthy_workspaces
():
workspaces
=
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/workspaces
"
"
Coder-Session-Token:{token}
"
"
limit==100
"
"
q==status:running
"
-
b
|
jq
-
cr
'
.workspaces | .[]
'
workspaces
=
list
(
map
(
json
.
loads
,
workspaces
))
for
workspace
in
[
workspace
for
workspace
in
workspaces
if
not
workspace
[
'
health
'
][
'
healthy
'
]]:
!
https
POST
"
tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace[
'
id
'
]}/builds
"
"
Coder-Session-Token:{token}
"
"
transition=stop
"
stop_unhealthy_workspaces
()
```
%% Cell type:markdown id:acd9cb0e-9596-4091-ae35-82a34ff9f0b4 tags:
## Check health of running workspaces
%% Cell type:code id:6c4beb11-717a-4d89-b9a4-e9b033b3e6a4 tags:
```
python
servers
=
{}
```
%% Cell type:code id:9bcdc633-0b28-46e4-aa54-97dc20f7d091 tags:
```
python
healthy_workspaces
=
[]
unhealthy_workspaces
=
[]
workspaces
=
!
https
GET
"
tp-inf110.r2.enst.fr/api/v2/workspaces
"
"
Coder-Session-Token:{token}
"
"
limit==100
"
"
q==status:running
"
-
b
|
jq
-
cr
'
.workspaces | .[]
'
workspaces
=
list
(
map
(
json
.
loads
,
workspaces
))
for
workspace
in
workspaces
:
if
workspace
[
'
health
'
][
'
healthy
'
]:
healthy_workspaces
+=
[
workspace
[
'
owner_name
'
].
lower
()]
else
:
unhealthy_workspaces
+=
[
workspace
[
'
owner_name
'
].
lower
()]
```
%% Cell type:code id:ffb92e49-aa8b-4371-bade-a06104020e32 tags:
```
python
print
(
"
Healthy workspaces: %d
"
%
len
(
healthy_workspaces
))
print
(
"
Unhealthy workspaces: %d
"
%
len
(
unhealthy_workspaces
))
```
%% Cell type:code id:08d53bc7-fd70-4a7f-97d3-be5e737cdbaa tags:
```
python
volumes
=
!
source
openrc
&&
openstack
volume
list
--
long
-
f
json
|
jq
-
c
'
.[] | { id: .Name, name: .Properties.
"
csi.storage.k8s.io/pvc/name
"
, status: .Status, location: .Name, attached_to: .
"
Attached to
"
} | select(.name != null) | select(.name | startswith(
"
coder-pvc-
"
) and contains(
"
-tp-inf110
"
)) | { name: .id, username: .name | split(
"
-
"
) | .[2], status: .status, server_id: .attached_to[0].server_id, location: (
"
/var/lib/kubelet/plugins/kubernetes.io/csi/pv/
"
+ .location +
"
/globalmount
"
) }
'
volumes
=
list
(
map
(
json
.
loads
,
volumes
))
```
%% Cell type:code id:c183542f-6abf-4fbe-8b64-386da87a2c95 tags:
```
python
[
volume
for
volume
in
volumes
if
volume
[
'
server_id
'
]
is
not
None
and
not
volume
[
'
username
'
]
in
healthy_workspaces
]
```
%% Cell type:code id:db003235-11d9-4386-b8fe-944955e5e257 tags:
```
python
for
server
in
servers
:
servers
[
server
][
'
users
'
]
=
[]
for
volume
in
volumes
:
if
volume
[
'
server_id
'
]
is
not
None
:
if
not
volume
[
'
server_id
'
]
in
servers
:
ip
,
=
!
source
openrc
&&
openstack
server
show
"
{volume[
'
server_id
'
]}
"
-
f
json
|
jq
-
r
'
.addresses.
"
{cluster_name}
"
[0]
'
servers
[
volume
[
'
server_id
'
]]
=
{
'
ip
'
:
ip
,
'
users
'
:
[
volume
[
'
username
'
]]
}
else
:
servers
[
volume
[
'
server_id
'
]][
'
users
'
]
+=
[
volume
[
'
username
'
]]
```
%% Cell type:code id:8cee7a31-4e22-4ff4-9b06-5c6b014ddea8 tags:
```
python
for
server
in
servers
:
print
(
"
Server:
"
+
servers
[
server
][
'
ip
'
])
for
user
in
servers
[
server
][
'
users
'
]:
print
(
'
User:
'
+
user
,
end
=
''
)
if
user
in
healthy_workspaces
:
print
(
'
(healthy)
'
)
elif
user
in
unhealthy_workspaces
:
print
(
'
(unhealthy)
'
)
else
:
print
(
'
(unknown)
'
)
print
()
print
()
```
%% Cell type:markdown id:79725809-695a-4ab4-9e58-79bb747b8b05 tags:
## Clean workspaces
%% Cell type:code id:1a29e39d-2abb-4b2a-b663-3455f956b46d tags:
```
python
def
remove_lost_found
():
for
volume
in
volumes
:
if
volume
[
'
server_id
'
]
is
not
None
:
machine_ip
=
servers
[
volume
[
'
server_id
'
]][
'
ip
'
]
lost_found
=
volume
[
'
location
'
]
+
"
/lost+found
"
command
=
"
set -xe;
"
command
+=
"
if [ -d
"
+
lost_found
+
"
]; then rmdir
"
+
lost_found
+
"
; fi;
"
!
ssh
-
oStrictHostKeyChecking
=
no
-
J
core
@
{
public_ip
}
core
@
{
machine_ip
}
sudo
sh
-
c
"
\"
{command}
\"
"
remove_lost_found
()
```
%% Cell type:markdown id:75661986-3bb2-42b0-abe6-1a5f18ea9615 tags:
## Copy / update files in workspace
%% Cell type:code id:13d7c970-4863-4fac-bb58-722bb9c27f34 tags:
```
python
past_versions
=
{
'
tp1.ipynb
'
:
[
'
e6f11284e2ba7d4a01350a1f2c0ab208
'
,
'
c208201a6aaf72f102fbd2cf46b62e5b
'
,
],
'
tp2.mv
'
:
[
'
b3b2a3e7a8dde6ed8848300158d89e5d
'
,
'
60a26b6a7d07579f63f6054c24b87ea2
'
,
'
190845731e26037dcbc7e3a74d239b1b
'
,
'
d010ae9ab0188b4cc77a3d5a45df2f1a
'
,
],
'
tp3.mv
'
:
[
'
91621d47a84e460e567dc51dfe1fcf45
'
,
],
'
tp4.mv
'
:
[
'
a819a3d31373750cfcb8473a27cd5c3f
'
,
],
'
tp5.mv
'
:
[],
'
settings.json
'
:
[
'
aca81e70023acea605dd44e829d04339
'
,
# Autogenerated by coq-lsp
'
7378b13908523bfde3692518f61939cf
'
,
'
2c2178abd6661d10ae492bf6fdc0454b
'
,
'
350ac51d8fea466958d9986c96c84842
'
,
'
ded5f984f385aa61ef30e01fef43f3e4
'
,
],
"
_CoqProject
"
:
[],
}
```
%% Cell type:code id:876c1a46-9dd4-4f4c-8a4e-185dc361d5a1 tags:
```
python
current_version
=
{
'
tp1.ipynb
'
:
'
009c42561f1853af966f95e9dcee9c24
'
,
'
tp2.mv
'
:
'
78139922e6b6a531e7e70acc98c1019c
'
,
'
tp3.mv
'
:
'
0a0253e4825146da6f08eff073b10444
'
,
'
tp4.mv
'
:
'
203ce16afa53d60cfd8390fb3fb7d898
'
,
'
tp5.mv
'
:
'
3ec8626aa544cb075af1f78719a1fb48
'
,
'
settings.json
'
:
'
3ddc1036917034cec13503b74d3b051e
'
,
'
_CoqProject
'
:
'
b1d60da8dd98106e00006d99428d1403
'
,
}
```
%% Cell type:code id:a8f0c5e5-f995-43bd-9cb8-08099e6ea8fa tags:
```
python
def
copy_update_file
(
filename
):
file_hash
,
=
!
md5sum
{
"
../INF110/
"
+
filename
}
|
cut
-
f1
-
d
'
'
if
file_hash
!=
current_version
[
filename
]:
print
(
"
Hash of
"
+
filename
+
"
has changed. Please update past_versions and current_version. New hash is
"
+
file_hash
)
return
for
server
in
servers
:
!
scp
-
o
StrictHostKeyChecking
=
no
-
J
{
proxyJump
}
{
"
../INF110/
"
+
filename
}
core
@
{
servers
[
server
][
'
ip
'
]}:
/
tmp
for
volume
in
volumes
:
if
volume
[
'
server_id
'
]
is
not
None
:
machine_ip
=
servers
[
volume
[
'
server_id
'
]][
'
ip
'
]
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
cp
-
nv
/
tmp
/
{
filename
}
{
volume
[
'
location
'
]}
file_hash
,
=
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
md5sum
{
volume
[
'
location
'
]
+
"
/
"
+
filename
}
|
cut
-
f1
-
d
'
'
if
file_hash
in
past_versions
[
filename
]:
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
cp
-
v
/
tmp
/
{
filename
}
{
volume
[
'
location
'
]}
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
chmod
go
+
rw
{
volume
[
'
location
'
]
+
"
/
"
+
filename
}
copy_update_file
(
"
tp1.ipynb
"
)
copy_update_file
(
"
_CoqProject
"
)
copy_update_file
(
"
tp2.mv
"
)
copy_update_file
(
"
tp3.mv
"
)
copy_update_file
(
"
tp4.mv
"
)
copy_update_file
(
"
tp5.mv
"
)
```
%% Cell type:code id:13608dc8-78f6-41d3-9cc3-f96c603a9437 tags:
```
python
def
copy_read_only_file
(
filename
):
for
server
in
servers
:
!
scp
-
J
{
proxyJump
}
{
"
../INF110/
"
+
filename
}
core
@
{
servers
[
server
][
'
ip
'
]}:
/
tmp
for
volume
in
volumes
:
if
volume
[
'
server_id
'
]
is
not
None
:
machine_ip
=
servers
[
volume
[
'
server_id
'
]][
'
ip
'
]
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
cp
-
v
/
tmp
/
{
filename
}
{
volume
[
'
location
'
]}
copy_read_only_file
(
'
README.md
'
)
copy_read_only_file
(
"
tp1-mysterious-tm.png
"
)
```
%% Cell type:code id:4cc929db-08fe-4b7b-9293-d86b7124f7cf tags:
```
python
def
copy_vscode_settings
():
file_hash
,
=
!
md5sum
{
"
../INF110/.vscode/settings.json
"
}
|
cut
-
f1
-
d
'
'
if
file_hash
!=
current_version
[
'
settings.json
'
]:
print
(
"
Hash of settings.json has changed. Please update past_versions and current_version. New hash is
"
+
file_hash
)
return
for
server
in
servers
:
!
scp
-
J
{
proxyJump
}
-
o
StrictHostKeyChecking
=
no
"
../INF110/.vscode/settings.json
"
core
@
{
servers
[
server
][
'
ip
'
]}:
/
tmp
for
volume
in
volumes
:
if
volume
[
'
server_id
'
]
is
not
None
:
machine_ip
=
servers
[
volume
[
'
server_id
'
]][
'
ip
'
]
vscode
=
volume
[
'
location
'
]
+
"
/.vscode
"
command
=
"
set -xe;
"
command
+=
"
if [ ! -d
"
+
vscode
+
"
]; then mkdir
"
+
vscode
+
"
; fi;
"
command
+=
"
cp -nv /tmp/settings.json
"
+
vscode
+
"
/settings.json;
"
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
sh
-
c
"
\"
{command}
\"
"
file_hash
,
=
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
md5sum
{
vscode
+
"
/settings.json
"
}
|
cut
-
f1
-
d
'
'
if
file_hash
in
past_versions
[
'
settings.json
'
]:
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
cp
-
v
/
tmp
/
settings
.
json
{
vscode
}
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
chmod
go
+
rw
{
vscode
+
"
/settings.json
"
}
copy_vscode_settings
()
```
%% Cell type:markdown id:55bd39c3-794e-43f2-8bf0-342361d61b64 tags:
## Save snapshots of modified files
%% Cell type:code id:db436f12-abb7-4b48-9e66-2f6ada19bdff tags:
```
python
def
snapshot
(
filename
,
snapshot_name
):
!
mkdir
-
p
{
snapshot_name
}
for
volume
in
volumes
:
if
volume
[
'
server_id
'
]
is
not
None
:
machine_ip
=
servers
[
volume
[
'
server_id
'
]][
'
ip
'
]
file_hash
,
=
!
ssh
-
o
StrictHostKeyChecking
=
no
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
md5sum
{
volume
[
'
location
'
]
+
"
/
"
+
filename
}
|
cut
-
f1
-
d
'
'
if
not
file_hash
==
current_version
[
filename
]
and
not
file_hash
in
past_versions
[
filename
]:
snapshot_filename
=
"
/tmp/
"
+
volume
[
'
username
'
]
+
"
-
"
+
snapshot_name
+
"
-
"
+
filename
!
ssh
-
J
{
proxyJump
}
core
@
{
machine_ip
}
sudo
cp
{
volume
[
'
location
'
]
+
"
/
"
+
filename
}
{
snapshot_filename
}
!
scp
-
J
{
proxyJump
}
core
@
{
machine_ip
}:{
snapshot_filename
}
{
snapshot_name
+
"
/
"
+
volume
[
'
username
'
]
+
"
_
"
+
filename
}
#snapshot('tp1.ipynb', '2023-12-04-post-tp')
#snapshot('tp2.mv', '2023-12-13-post-tp')
#snapshot('tp3.mv', '2023-12-13-post-tp')
#snapshot('tp4.mv', '2024-01-12-post-tp')
#snapshot('tp5.mv', '2024-01-22-post-tp')
```
%% Cell type:code id:35af9e82-e79c-4ad9-a843-3ebaab9f4d62 tags:
```
python
```
...
...
This diff is collapsed.
Click to expand it.
README.md
+
2
−
1
View file @
84c6063d
...
...
@@ -111,7 +111,7 @@ The reverse proxy will be used to access Coder via HTTPS.
```
bash
openstack server create
\
--flavor
smi-worker
\
--image
ubuntu-
jammy-19
-0
9
-202
3
\
--image
ubuntu-
noble-21
-0
8
-202
4
\
--network
inf110
\
--security-group
default
\
--security-group
ssh_icmp
\
...
...
@@ -228,6 +228,7 @@ Install the reverse proxy on the reverse proxy VM.
```
bash
ssh ubuntu@137.194.210.143
sudo
-i
apt update
apt
install
nginx python3-certbot-nginx
certbot
--nginx
-d
tp-inf110.r2.enst.fr
```
...
...
This diff is collapsed.
Click to expand it.
flake.nix
+
1
−
0
View file @
84c6063d
...
...
@@ -19,6 +19,7 @@
python-magnumclient
python-octaviaclient
httpie
requests
]))
pwgen
jq
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment