From 1705d197a13c929f85c264cbf4e5ca741cbb16a2 Mon Sep 17 00:00:00 2001 From: John McCann Cunniff Jr <36013983+wabscale@users.noreply.github.com> Date: Sun, 23 May 2021 14:50:38 -0400 Subject: [PATCH] V3.0.3 chg digitalocean move (#87) * CHG digital ocean move * CHG a bunch of changes for digital ocean * CHG replace old readme with design doc * CHG doc name in render.sh * CHG update design pdf * CHG update the language to reflect digital ocean on readme * CHG update design pdf * CHG fix a few things * CHG responsive question card feedback * CHG presort question responses by pool * FIX some stuff with question editing and assigning * CHG add zsh shell and optimize xv6 theia image layers * ADD github org url to course and fix question tests * CHG fix some things with the anubis cli and management IDE --- Makefile | 6 +- README.md | 705 +++++++++++++++++- api/anubis/models/__init__.py | 55 +- api/anubis/rpc/seed.py | 4 + api/anubis/rpc/theia.py | 72 +- api/anubis/utils/auth.py | 6 +- api/anubis/utils/data.py | 6 +- api/anubis/utils/lms/assignments.py | 2 +- api/anubis/utils/lms/questions.py | 81 +- api/anubis/utils/seed.py | 2 +- api/anubis/views/admin/assignments.py | 2 +- api/anubis/views/admin/autograde.py | 9 +- api/anubis/views/admin/ide.py | 18 +- api/anubis/views/admin/questions.py | 81 +- api/anubis/views/public/questions.py | 24 + api/anubis/views/public/webhook.py | 6 +- api/jobs/reaper.py | 1 - .../2af8f3918f87_add_github_org_url.py | 57 ++ .../31dc6a1a6759_add_autograde_tests_repo.py | 28 + ...2a_add_theia_options_to_assignment_and_.py | 2 +- api/tests/test_assignment_admin.py | 2 +- api/tests/test_ide_admin.py | 2 +- api/tests/test_questions_admin.py | 30 +- api/tests/test_questions_public.py | 2 +- docker-compose.yml | 20 +- docs/design.md | 698 ----------------- docs/design.pdf | Bin 1229944 -> 1069825 bytes docs/img/cluster.mmd.svg | 2 +- docs/img/rpc-queue-1.mmd.svg | 2 +- docs/img/rpc-queue-2.mmd.svg | 2 +- docs/img/rpc-queue-3.mmd.svg | 2 +- docs/img/submission-flow.mmd.svg | 2 +- docs/img/theia-pod.mmd.svg | 2 +- docs/render.sh | 4 +- k8s/.gitignore | 1 + k8s/Makefile | 42 ++ k8s/chart/templates/api.yml | 38 - k8s/chart/templates/ingress.yml | 22 +- k8s/chart/templates/logstash.yml | 61 +- k8s/chart/templates/migrate.yml | 34 + k8s/chart/templates/rpc.yml | 5 +- k8s/chart/values.yaml | 34 +- k8s/debug/provision.sh | 26 +- k8s/debug/restart.sh | 3 + k8s/deploy.sh | 51 +- k8s/prod/provision.sh | 84 +++ k8s/prod/traefik-values.yaml | 242 ++++++ theia/ide/admin/Dockerfile | 24 +- theia/ide/admin/autosave | 10 + theia/ide/admin/autosave-dump.sh | 3 + theia/ide/admin/cli/.editorconfig | 21 - theia/ide/admin/cli/Makefile | 89 --- theia/ide/admin/cli/README.rst | 25 - theia/ide/admin/cli/anubis/__init__.py | 3 +- .../ide/admin/cli/anubis/assignment/meta.yml | 32 +- theia/ide/admin/cli/anubis/assignment/test.sh | 2 +- theia/ide/admin/cli/anubis/cli.py | 17 +- theia/ide/admin/cli/docs/Makefile | 20 - theia/ide/admin/cli/docs/authors.rst | 1 - theia/ide/admin/cli/docs/conf.py | 162 ---- theia/ide/admin/cli/docs/contributing.rst | 1 - theia/ide/admin/cli/docs/history.rst | 1 - theia/ide/admin/cli/docs/index.rst | 20 - theia/ide/admin/cli/docs/installation.rst | 51 -- theia/ide/admin/cli/docs/make.bat | 36 - theia/ide/admin/cli/docs/readme.rst | 1 - theia/ide/admin/cli/docs/usage.rst | 7 - theia/ide/admin/cli/requirements.txt | 1 - theia/ide/admin/cli/requirements_dev.txt | 10 - theia/ide/admin/cli/setup.cfg | 22 - theia/ide/admin/cli/setup.py | 12 +- theia/ide/admin/cli/tests/__init__.py | 1 - theia/ide/admin/cli/tests/test_anubis.py | 33 - theia/ide/admin/cli/tox.ini | 20 - theia/ide/admin/supervisord.conf | 8 + theia/ide/xv6/Dockerfile | 202 ++--- theia/ide/xv6/autosave | 4 - theia/sidecar/autosave-loop.sh | 4 +- .../Admin/Assignment/AssignmentCard.jsx | 2 +- .../Admin/Assignment/QuestionCard.jsx | 87 ++- .../Admin/Assignment/QuestionControls.jsx | 68 +- .../Components/Admin/Course/CourseCard.jsx | 76 +- .../Admin/Course/CourseTasProfessors.jsx | 4 +- .../Admin/IDE/ManagementIDEDialog.jsx | 21 +- web/src/Components/Admin/Users/UserCard.jsx | 10 +- .../Admin/Visuals/AssignmentSundialPaper.jsx | 13 +- .../Admin/Visuals/AutogradeVisuals.jsx | 30 +- .../Components/Public/Courses/CourseCard.jsx | 10 +- web/src/Components/Public/IDE/IDEDialog.jsx | 4 +- .../Public/Questions/QuestionEditor.jsx | 5 + .../Public/Questions/QuestionsCard.jsx | 69 +- web/src/Pages/Admin/Assignment/Assignment.jsx | 2 +- web/src/Pages/Admin/Assignment/Questions.jsx | 29 +- web/src/Pages/Admin/Assignment/Tests.jsx | 4 +- web/src/Pages/Admin/Autograde/Assignments.jsx | 6 +- web/src/Pages/Admin/Autograde/Results.jsx | 32 +- web/src/Pages/Admin/Course.jsx | 62 +- web/src/Pages/Admin/Users.jsx | 2 +- web/src/Pages/Public/Courses.jsx | 5 +- web/src/Utils/datetime.js | 22 +- web/src/navconfig.jsx | 6 +- web/yarn.lock | 178 +++-- 102 files changed, 2091 insertions(+), 2079 deletions(-) create mode 100644 api/migrations/versions/2af8f3918f87_add_github_org_url.py create mode 100644 api/migrations/versions/31dc6a1a6759_add_autograde_tests_repo.py delete mode 100644 docs/design.md create mode 100644 k8s/.gitignore create mode 100644 k8s/Makefile create mode 100644 k8s/chart/templates/migrate.yml create mode 100755 k8s/prod/provision.sh create mode 100644 k8s/prod/traefik-values.yaml create mode 100755 theia/ide/admin/autosave create mode 100755 theia/ide/admin/autosave-dump.sh delete mode 100644 theia/ide/admin/cli/.editorconfig delete mode 100644 theia/ide/admin/cli/Makefile delete mode 100644 theia/ide/admin/cli/README.rst delete mode 100644 theia/ide/admin/cli/docs/Makefile delete mode 100644 theia/ide/admin/cli/docs/authors.rst delete mode 100755 theia/ide/admin/cli/docs/conf.py delete mode 100644 theia/ide/admin/cli/docs/contributing.rst delete mode 100644 theia/ide/admin/cli/docs/history.rst delete mode 100644 theia/ide/admin/cli/docs/index.rst delete mode 100644 theia/ide/admin/cli/docs/installation.rst delete mode 100644 theia/ide/admin/cli/docs/make.bat delete mode 100644 theia/ide/admin/cli/docs/readme.rst delete mode 100644 theia/ide/admin/cli/docs/usage.rst delete mode 100644 theia/ide/admin/cli/requirements_dev.txt delete mode 100644 theia/ide/admin/cli/setup.cfg delete mode 100644 theia/ide/admin/cli/tests/__init__.py delete mode 100644 theia/ide/admin/cli/tests/test_anubis.py delete mode 100644 theia/ide/admin/cli/tox.ini diff --git a/Makefile b/Makefile index e60c749a2..58d17c7d5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PERSISTENT_SERVICES := db traefik kibana elasticsearch-coordinating redis-master logstash RESTART_ALWAYS_SERVICES := api web-dev rpc-default rpc-theia rpc-regrade -PUSH_SERVICES := api web logstash theia-init theia-proxy theia-admin theia-xv6 +PUSH_SERVICES := api web theia-init theia-proxy theia-admin theia-xv6 @@ -35,7 +35,7 @@ deploy: build: docker-compose build --parallel --pull -.PHONY: push # Push images to registry.osiris.services (requires vpn) +.PHONY: push # Push images to registry.digitalocean.com (requires vpn) push: docker-compose build --parallel --pull $(PUSH_SERVICES) docker-compose push $(PUSH_SERVICES) @@ -65,7 +65,7 @@ mindebug: docker-compose up -d traefik db redis-master logstash docker-compose up \ -d --force-recreate \ - api web rpc-default rpc-theia + api web-dev rpc-default rpc-theia @echo 'Waiting a moment before running migrations' sleep 3 @echo 'running migrations' diff --git a/README.md b/README.md index e7f080466..4195657fa 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,696 @@ -# Anubis +# Anubis Design Doc -Anubis is a cluster of services that work together to automatically test and grade OS3224 -assignment submissions. +![](./docs/img/anubis-icon-1.png) -![alt img](docs/img/cluster.mmd.svg) +> Author: John Cunniff -## Overview -The basic flow of a submission (from the students prospective) is as follows: +> Version: v3.0.3 -1. A students use github classroom to get a template repo for an assignment. -2. Students make changes as necessary, then commit and push their changes to their github repo. -(triggering a webhook to Anubis) -3. The student can then go to the Anubis website in their browser and get live updates and feedback -of the tests for that assignment. -4. From the feedback they got from step 3 they should know if their code passes basic tests. If they -fail the tests, they should go back to step 2. +\pagebreak -This is quite simply a large scale CD/CI for OS3224 assignments with some other metrics, and tracking -built in. If you are unfamiliar with what CD/CI is, you can read up on it -[here](https://en.wikipedia.org/wiki/CI/CD). +# Contents +- [1. Overview](#1-overview) + - [1.1 Elevator Pitch](#11-elevator-pitch) + - [1.2 Motivations](#12-motivations) +- [2. Services](#2-services) + - [2.1 Traefik](#21-traefik) + - [2.2 API](#22-api) + - [2.2.1 Zones](#221-zones) + - [2.2.2 Responsibilities](#222-responsibilities) + - [2.2.3 SSO Authentication](#223-sso-authentication) + - [2.3 Submission Pipeline](#23-submission-pipeline) + - [2.3.1 Kube Job](#231-kube-job) + - [2.3.2 Submission State Reporting](#232-submission-state-reporting) + - [2.3.3 Stages](#233-stages) + - [Clone](#clone) + - [Build](#build) + - [Test](#test) + - [2.4 Web Frontend](#24-web-frontend) + - [2.4.1 Autograde Results](#241-autograde-results) + - [2.5 Anubis Cloud IDE](#25-anubis-cloud-ide) + - [2.5.1 IDE Frontend](#251-ide-frontend) + - [2.5.2 Theia Pod Design](#252-theia-pod-design) + - [2.6 Datastores](#26-datastores) + - [2.6.1 Mariadb](#261-mariadb) + - [2.6.2 Elasticsearch](#262-elasticsearch) + - [2.6.3 Kibana](#263-kibana) + - [2.6.4 Redis + RQWorker](#264-redis--rqworker) + - [2.7 Logging](#27-logging) + - [2.7.1 logstash](#271-logstash) +- [3. Deployment](#3-deployment) + - [3.1 Kubernetes](#31-kubernetes) + - [3.1.1 Helm Chart](#311-helm-chart) + - [3.1.2 Rolling Updates](#312-rolling-updates) + - [3.1.3 Longhorn](#313-longhorn) + - [2.1.4 Digital Ocean](#314-digital-ocean) + - [3.1.5 Nodes](#315-nodes) + - [3.1.6 Networking](#316-networking) + - [3.2 Github](#32-github) + - [3.2.1 Organization](#321-organization) + - [3.2.2 Classroom](#322-classroom) +- [4. CLI](#4-cli) + - [4.1 CLI in Management IDE](#41-cli-in-management-ide) + - [4.2 CLI Usage](#42-cli-usage) +- [5. Assignments](#5-assignments) + - [5.1 Creating a new Assignment](#51-creating-a-new-assignment) + - [5.2 Writing Tests](#52-writing-tests) + - [5.3 Uploading Tests](#53-uploading-tests) +\pagebreak -## Anubis Cloud IDE +## 1 Overview -![alt img](docs/img/20201217_235040.jpg) +### 1.1 Elevator Pitch +At its core, Anubis is a tool to give students live feedback from their homework assignments +while they are working on them and before the deadline. Instead of having students +submit a patch file, through github classrooms +each student will have their own private repo for every assignment. +The way students then submit their work +is simply by submitting before the deadline. Students can then +push, and therefore submit as many times as +they would like before the deadline. -![alt img](docs/img/theia-pod.mmd.svg) +When a student pushes to their assignment repo, a job is launched in the +Anubis cluster. That job will build +their repo, run tests on the results, and store the results in the [datastore](#26-datastores). + +Students can then navigate to the anubis website, where they will sign in through [NYU SSO](#223-sso-authentication). +From there, +they will be able to see all the current and past assignments, and submissions for their classes. They are able +to view all relevant data from their build and tests for a given submission. There they can request a regrade, +there by launching a new submission pipeline. While the submission still being processed, the frontend will poll +the [API](#22-api) for updates. In this, the frontend will be constantly updating while the submission is being +processed, giving a live and interactive feel to the frontend. Once a submission is processed Anubis will show +the students logs from their tests, and builds along with which tests passed and which failed. + +New in version v2.2.0, there is now the Anubis Cloud IDE. Using some kubernetes magic, we are able to +host [theia](https://theia-ide.org/) servers for individual students. These are essentially VSCode instances +that students can access in the browser. What makes these so powerful is that students can access a terminal +and type commands right into a bash shell which will be run in the remote container. With this setup students +have access to a fully insolated and prebuilt linux environment at a click of a button. + +### 1.2 Motivations + +The purpose of this paper is to both explain the design, and the features of the Anubis autograder. + +One of the main goals for Anubis from the beginning +was to simplify the experience for students. There should not need to +be a learning curve with a learning management system like Anubis just +for students to turn in their homework. Everywhere possible, Anubis +will fill in the blanks. All they need to do is click a button to get an +ide, do their work and check their test feedback if they would like. The +feedback the system gives to students is entirely up to the Professor and TAs. +You could use the Anubis system just for autograding, or just +for the cloud IDEs or both! It is up to you which features of Anubis you +would like to integrate into your curriculum. + +\pagebreak + +## 2 Services + +### 2.1 Traefik + +For our edge router, we use [traefik](https://docs.traefik.io/). Traefik will be what actually +listens on the servers external ports. All external traffic will be routed through Traefik. Above all else, +we will be using Traefik's routing features to handle the Ingress of requests. + +Traefik lets us do some spicy and powerful stuff. We can route requests to different services based off +predefined rules, and even change requests as they pass through. This makes it so that we can +have both the static store and api on the same domain. The rules are set up such that every request that +starts with a path of `/api` goes to the api service. + +By leveraging these features of Traefik, we can make it appear that the services work different when +being accessed externally. Namely, the basic authentication for certain paths (and therefore services). + +> One thing to note here is that when being accessed from within the cluster, none of these rules +> apply as we would be connecting directly to services. + +### 2.2 API + +The API is the backbone of anubis. It is where all the heavy lifting is done. The service relies on both +the [elasticsearch](#262-elasticsearch) and [mariadb](#261-mariadb) data stores to maintain state. + +#### 2.2.1 Zones + +The API is split into two distinct, and uniquely treated zones. There is a `public` and a `admin` zone. +All endpoints for Anubis fall within one of these zones. + +These zones are simply paths that are treated differently depending on where the request is external. Namely, +for the admin zone external requests will require you to be marked as an admin. By adding this simple level of +authentication, we can lock down a section of the more sensitive API to only those authenticated from the outside. + +#### 2.2.2 Responsibilities + +The Anubis API is responsible for handling most basic IO, and state managing that happens on the cluster. +Some of this includes: + +- Authenticating users +- Providing Class, Assignment, and Submission data to the frontend +- Handling github webhooks +- Handling reports from the submission pipeline cluster +- Handling regrade requests +- Initializing new IDE sessions + +#### 2.2.3 SSO Authentication + +To authenticate with the api, a token is required. The only way to get one of these tokens is through NYU +Single Sign On. By doing this, we are outsourcing our authentication. This saves a whole lot of headaches +while being arguably more secure than if we rolled our own. + +In implementation, the frontend loads it will attempt to authenticate with the API. If there is +a stale or broken token in the current cookies, the frontend will redirect users to the NYU login page. +Given that they authenticate there, they will be redirected back to the API, where we will provide them +with a token. From there, they will be logged into Anubis. + +All of this is about 20 lines on our end. All that is necessary are some keys from NYU IT. + +### 2.3 Submission Pipeline + +#### 2.3.1 Kube Job + +A given submission pipeline is of the form of a Kubernetes [Job](https://kubernetes.io/docs/concepts/workloads/controllers/job/). +These jobs have some built in assurances. A job is configurable to continue to launch new containers if a Pod fails. + +#### 2.3.2 Submission State Reporting + +At each and every stage of a submission pipeline, the job will report to the api with a state update. This state is +in the form of a string that describes what is currently happening at that moment. This data can then be passed along +to a user that is watching their pipeline be processed live. + +Each unique per-assignment pipeline is packaged in the form of a docker image. + +> See [Creating a new Assignment](#51-creating-a-new-assignment) + +> An error at any stage of the submissions pipeline will result in an error being reported to the API, and +> the container exiting. + +#### 2.3.3 Stages + +It is important to note that at each stage of the submission pipeline, we will be moving execution back and +forth between two users. There will be the entrypoint program managing the container as user `anubis`. The +`anubis` user will have much higher privileges than the `student` user. The `student` user will be used whenever +executing student code. It will not have any level of access to anything from the `anubis` user. + +![](./docs/img/submission-flow.mmd.png) + +##### Clone + +In this initial stage, we will pull the current repo down from github. After checking out the commit for +the current submission, we will also delete the `.git` directory as it is not needed. Lastly we will +`chown` the entire repo as `student:student`. This will then be the only place in the container that the +student user can read or write to (other than /tmp of course). + +##### Build + +At this stage we hand execution off to student code for the first time. We will be building the student +code _as the `student` user_. The command for building the container will be specified by on a per-assignment +basis. The stdout of the build will be captured by the `anubis` user. + +Once built, a build report will be sent to the API, along with a state update for the submission. If the +student is on the submission page, then they should be able to see the build info shortly thereafter. + +##### Test + +Tests will be defined on a per-assignment basis. Again we are executing student code, as the student user. + +After each test, we will return to the entrypoint program as the `anubis` user. We will then send a report +of the last test before continuing to the next. + +Once we reach the last test, we send off a separate notification to the API indicating the completion of +the pipeline. It is at that point that the API marks the submission as processed. + +### 2.4 Web Frontend + +The frontend is designed to be a simple reflection of the backend data. Once authenticated, users will +be able to see the classes they are a part of, current and past assignments, and all their submissions. +With few exceptions, the frontend is a near one to one translation of the API's data models. Most pages +will have a corresponding API endpoint. The data shown on that page will be in exactly the form of the +API response. + +The notable exceptions to this simplistic model would be the submission page, the regrade button, and +the find-missing button. + +The submission page will poll for new data if the submission is not marked as processed. As it sees +new data, it will update what is displayed. + +Located on the submission page, the purpose of the regrade button is to be a simple and easy way for +users to request regrades. When clicked, the frontend will hit a special endpoint for requesting regrades. +If successful, the submission will be re-enqueued in a submission pipeline. + +On the submissions page, the find-missing button will trigger a server side update of the submission data. +When the find-missing endpoint is hit, the API will use the graphql API for github to pull all the commits +for all the known repos for that user. If it sees commits that were not previously seen (likely though github +not delivering a webhook), then it will create and enqueue the new submissions. + + +#### 2.4.1 Autograde Results + +Getting the autograde results is as easy as searching a name in the autograde results panel. You will +be taken to a page like this where the calculated best submission is shown. The best submission is the +most recent submission that passed the most tests. + +The students see a reduced version of the panel showed to the TAs and professors. In the admin view, more +data is displayed about the student. The students can see the status of the submission, and the build and +test results. + +> *Calculating the best submissions requires heavy IO with the database. For this, the autograde results are +heavily cached. Results are updated hourly.* + +![Autograde Results](./docs/img/autograde-results.png) + + +### 2.5 Anubis Cloud IDE + +One of the more exciting new features is that of the cloud ide. Leveraging some magic that Kubernetes +and containers give us, we can provide a fully isolated IDE environment for all ~130 concurrently. + +The [Theia IDE](https://theia-ide.org/) is a basically a VSCode webserver. They have docker images for +specific languages. + +Something very special about theia is that we can actually build theia using a package.json. By doing this +we are able to compile in only the plugins we actually need/use. This is very useful because we can bring +the default theia-cpp image down from 4GiB to ~1.5GiB. This is still an annoyingly large image, but hey, it's +not 4GiB. + +#### 2.5.1 IDE Frontend + +Once students have created their repo, they will be able to launch a theia session for a given assignment. At that +time, we clone exactly what is in github into a theia pod. + +![Theia Launch Session](./docs/img/theia1.png) + +Once their session pod has been allocated, then they can click the goto session button. It is at this point +that their requests start to go through the theia proxy. Assuming all goes well with initializing the session, +and starting the websocket connection, then students will have the following theia IDE. + +![Theia Webview](./docs/img/theia2.png) + +Something to point out here is that this looks, feels, and acts exactly as VSCode, but it is accessed over the +internet. We can even load some VSCode plugins into the builds. + +#### 2.5.2 Theia Pod Design + +The pod design requires some distributed finesse. There are a couple of things about theia that make it so that +we need to do some rather fancy things in Kubernetes to make the setup work. + +Distributing and handling multiple theia severs and concurrent connections is the name of the game here. We +need to be able to have a setup that scales well that is able to handle many users on the system at once. + +The main thing that we need to handle is the fact that theia requires a websocket connection between the browser and +theia server instance. When the pods are allocated, we note the ClusterIP in the database. Then when we need to +initialize a client session, we use this saved ClusterIP to forward requests (both http and websockets) to the +pod. + +These pods are temporary. When the student is finished working (or after a timeout) we reclaim the resources by +deleting the containers and data. Because of this, we needed some form of autosave. Saving is pushing to github. +The issue we need to contend with is how do we have automatic commits and pushes to github without exposing a +password or api token to the users. We handle this by having a second container whose only role is committing and +pushing to github. The credentials live in that container, completely separate and inaccessible to the user who +may be working in the other theia server container. These two containers are connected by a shared longhorn volume. +This volume is relatively small (~50MiB). With this setup, we have autosave running in the background while being +completely hidden from the user. + +![Theia Pod Spec](./docs/img/theia-pod.mmd.png) + +With these lightweight containerized theia servers, we are able to support significantly more concurrent users than +if we had elected to implement a cloud vm solution. Because with containers, we do not need to virtualize hardware, +the resources on the system for each user is significantly less. Given the resources that we have on Digital Ocean, +we would be able to have maybe 20-30 concurrent users using cloud virtual machines. With our containerized theia, +we can handle all ~130 students at the same time with room to breath. + +#### 2.5.3 Reclaiming Theia Resources + +The theia sessions are often forgotten about. Students often create an IDE server, work on it for a bit, then forget +about it. Due to this, we have a time to live of 6 hours for each session. A cronjob runs every 5 minutes to look +for and schedule delete for stale resources. We do provide a button in the anubis panel as a way for students to manually +schedule their session for deletion. Even when we ask students to click this button when they are done, some still do not. +In the following graph we can see an interesting experiment in student behavior. It shows a cumulative graph +showing the duration of a theia session for the final exam. Just about 40% of sessions hit the 6-hour timeout. + +![Theia Cumulative Duration](./docs/img/theia3.png) + +#### 2.5.4 Management IDE + +Separate from the student xv6 IDE, there is also an admin build of theia that has some extra things. On the assignments +page of the admin panel, any TA or professor can launch their own admin IDE. In this IDE, the assignment-tests +repo is cloned instead of a student assignment repo. It has less networking restrictions, the anubis cli +and even has docker running right in the pod. With this, TA's and professors can create, modify, and deploy +assignments. + +Using some very clever authentication mechanics, the management IDEs will be initialized with a personal token +that will be used by the CLI within the container. The result of this is that you will be able to just use the +anubis CLI. It will be routed to the in cluster API instances. All the requests you make to the API will be +authenticated using that personal token that gets dropped into the pod. + +![Management IDE](./docs/img/theia4.png) + +### 2.6 Datastores + +#### 2.6.1 Mariadb + +Anubis uses the [bitnami MariaDB chart](https://hub.kubeapps.com/charts/bitnami/mariadb). It runs with 3 read-only +replication nodes, along with a main node that does read-write. The underlying MariaDB files are also +backed up with a [Longhorn](https://longhorn.io/) persistent volume that has 3x replication in the cluster. +That volume has daily snapshots. Redundancy is the name of the game here. + +The precise data model that we will be using is a simple relational database. From the API, we will interface +with Mariadb via [SQLAlchemy](https://www.sqlalchemy.org/). + +Here is an example of a minimal mariadb deployment installed through the bitnami chart. Obviously for production we would +want to change the password from anubis to something stronger. + +```shell +# Create a minimal mariadb deployment in a mariadb namespace. On +# prod, the mariadb is in a separate namespace, so we do the same +# here. +echo 'Adding mariadb' +kubectl create namespace mariadb +helm install mariadb \ + --set 'auth.rootPassword=anubis' \ + --set 'volumePermissions.enabled=true' \ + --set 'auth.username=anubis' \ + --set 'auth.database=anubis' \ + --set 'auth.password=anubis' \ + --set 'replication.enabled=false' \ + --namespace mariadb \ + bitnami/mariadb +``` + +#### 2.6.2 Elasticsearch + +Anubis uses elasticsearch as a search engine for logging, and event tracking. The +[elastic ecosystem](https://www.elastic.co/) has other tools like [kibana](#263-kibana) and [logstash](#271-logstash) +for visualizing data, and logging. Other than the visualization features, elastic allows us to simply start +throwing data at it without defining any meaningful shape. This allows us to just log events and data into +elastic to be retrieved, and visualized later. + +The Elasticsearch stack is pretty annoying to manage on its own. Installing through the bitnami chart is +much easier. + +```shell +# Install a minimal elasticsearch and kibana deployments +echo 'Adding elasticsearch + kibana' +kubectl create namespace anubis +helm install elasticsearch \ + --set name=elasticsearch \ + --set master.persistence.size=1Gi \ + --set data.persistence.size=1Gi \ + --set master.replicas=1 \ + --set coordinating.replicas=1 \ + --set data.replicas=1 \ + --set global.kibanaEnabled=true \ + --set fullnameOverride=elasticsearch \ + --set global.coordinating.name=coordinating \ + --namespace anubis \ + bitnami/elasticsearch +``` + +#### 2.6.3 Kibana + +The Anubis autograder generates a lot of data. We have intense logging, and event tracking on every service. +When something happens on the cluster, it will be indexed into elastic. [Kibana](https://www.elastic.co/kibana) +is elastic's data visualization tool. It runs as a website that interfaces with the internal elasticsearch. Through +kibana, we can stream live logs, view event data, and create meaningful visualizations. + +The API sees when students start their homeworks (create their github repo), and when they are submitting +(pushing to their repo). This data is indexed into elasticsearch, and visualized via kibana. In Anubis version +one we were able to show graphs of when students were starting vs finishing their assignments. To no ones surprise, +the majority of the class was starting very late, with a large influx of submissions in the few hours before each +deadline. Furthermore, we can show how long it takes for a student to start their assignment to when they have +their tests pass on average. We can also show which tests were causing students the most trouble. + +Here is one such visualization of assignment submissions over time. The peaks are the few days before a due date. + +![Submissions Over Time](./docs/img/submissions-over-time.png) + +This incredibly precise view into the actual data that can be generated on a platform such as Anubis is +something that sets it apart from its competitors. We can show meaningful statistics to the professors and TAs +on the platform about what went well with their assignment, and where students struggled. + +#### 2.6.4 Redis + RQWorker + +Redis has two purposes in Anubis. It is used by flask-caching to cache some endpoint and function +results. For most all the functions that are heavy on the database, the results are cached for a specified period +of time. The second purpose is as a rpc job broker. We use [python-rq](https://python-rq.org/) as it is super +simple and easy to configure and set up. Using rq, we can enqueue functions to be run by a deployment of rpc-worker +pods. Just about every time we need or want to do some work asynchronously, rq is used. + +The redis setup needed for anubis is quite minimal. Again we would obviously want to change the password +here in prod. + +```shell +# Install a minimal redis deployment +echo 'Adding redis' +helm install redis \ + --set fullnameOverride=redis \ + --set password=anubis \ + --set cluster.enabled=false \ + --namespace anubis \ + bitnami/redis +``` + +### 2.7 Logging + +> _When it doubt, just log it_ + +#### 2.7.1 Logstash + +Logstash itself is a service that your applications can ship their logs to before being indexed into elasticsearch. +Its purpose is to act as a natural buffer of log data. It is able to interface with elasticsearch, adjusting the +speed of log ingestion as needed. In addition to acting as a buffer, it also enriches the data that it sees. For +example, the logstash python client will not only ship the log message, but also the file that the log is coming +from, along with which node the log is coming from. + +This centralized, and persistent logging is indexed into elasticsearch, and accessed via kiaban. Anubis uses logstash +on its API and submission pipeline. + +\pagebreak + +## 3 Deployment + +### 3.1 Kubernetes + +The main goal with moving to Kubernetes from a simple single server docker-compose setup was scalability. We needed +to scale our load horizontally. No longer was adding more RAM and CPU cores to the VM viable. With more users, we +have a greater load. Kubernetes allows us to distribute this load across the servers in the clusters quite easily. + +In moving to Kube, we are also now able to make guarantees about availability. In theory, even if sections of +physical servers on the cluster go offline Anubis will still remain available. +[More on that later...](#314-digital-ocean) + + +#### 3.1.1 Helm Chart + +Anubis itself is packaged as a helm chart. Both mariadb and elasticsearch need to be setup separately. There +are several options for how to deploy anubis that can be specified when running the deploy script at `kube/deploy.sh`. +Notable options are deploying in debug mode, or restricting access to only the OSIRIS vpn. Options passed +to the deploy script will be then passed to helm. + + +```shell +# Deploy with anubis only being accessable from the OSIRIS vpn +./kube/deploy.sh --set vpnOnly=true + +# Deploy in debug mode +./kube/deploy.sh --set debug=true + +# Set the number of replicas for the api service to a specific value +./kube/deploy.sh --set api.replicas=5 + +# Disable rolling updates +./kube/deploy.sh --set rollingUpdates=false +``` + +A full list of options can be found in the values.yaml file at `kube/values.yaml`. + +#### 3.1.2 Rolling Updates + +In the decisions that were made when considering when expanding Anubis, availability was of the most important. +Specifically we needed to move to a platform that would allow us to do _zero_ downtime deploys. Kubernetes has +very powerful rolling update features. We can bring up a new version of the Anubis API, verify that this new +version is healthy, then bring down the old version. All the while, there will be no degradation in availability. + +Of all the features of Kubernetes that Anubis leverages, none are quite as important as rolling updates. The +Anubis API can be updated to a new version with _zero_ downtime. This means we can live patch the API with new +versions, with little to no degradation in service. + +#### 3.1.3 Longhorn + +The Kubernetes [StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/) that Anubis uses is Longhorn. It allows us to have ReadWriteMany volumes +for things like the Cloud IDEs. + +All important data is stored on 3x replicated Longhorn StorageVolumes. Those volumes all have at least daily +snapshots taken of them. At any given time, we can reset any stateful service to a previous snapshot from the +last seven days. + +For the things that are very important we have daily snapshots, and extra replication. Longhorn makes this +as simple as checking some boxes. You can see here our mariadb master image with triple replication +(two in Brooklyn one in Manhattan), with a 7 day snapshot. + +![Longhorn Volume Management](./docs/img/longhorn-mariadb.png) + +#### 3.1.4 Digital Ocean + +The official Anubis cluster is hosted on Digital Ocean. +Using their managed Kubernetes solution, the cluster can +balloon in resources when their are many students online. + +The main benifits of using a managed cluster instead of +self hosting is ease of mind! + +##### 3.1.5 Nodes + +The cluster is comprised of Digital Ocean droplets in their NYC-1 datacenter. +When Kubernetes reports things like memory, cpu or disk pressure the node pools +are automatically scaled up to handle the increased load. + +##### 3.1.6 Networking + +The Digital Ocean cluster uses a loadbalancer to distribute the networking +load across all existing nodes. Traefik CRD is the reverse proxy that exists on +the cluster to handle the ingress. + +### 3.2 Github + +#### 3.2.1 Organization + +Each class that will need a github organization to put all its repos under. The only members of that organization +should be the professor and TAs. + +The organization should be set up to have push event webhooks to anubis. The endpoint the webhook should push +to is `https://anubis.osiris.services/api/public/webhook/`. That endpoint will be hit when there is a push to a +repo in that organization. + +#### 3.2.2 Classroom + +[Github classroom](https://classroom.github.com/) is used to create, and distribute repos for assignments. +There you can create assignments that will pull from a template repo of your choosing. A link will be generated +that can be distributed to students. When clicked, that link will generate a new repo for the student within the +class organization. As the student is not a part of the organization, they will not be able to see any of the +other student repos (given the assignment was made using a private repo). + +The best place to put the template repo is within the class organization as a private repo. + +One very important thing to note here is that in order to be able to create private assignment repo's, the +classroom you create must be verified by github. This can take a few weeks, ad a professor needs to email +github asking for permission from a .edu email providing their title and whatnot. + +> Getting your github classroom / org approved will likely cause delays if not done at least a month +> before the semester starts. + + +## 4 CLI + +### 4.1 CLI in Management IDE + +In the Anubis Management IDEs there is a anubis cli installed. When a +Management IDE is started, it is provisioned with the necessary authentication +tokens to communicate with the Anubis API. + +The short version of this is that when you use an Anubis Management IDE it +is automatically pointed at the internal API and is authenticated to you. + +### 4.2 CLI Usage + +The main purposes of the CLI is for making it a bit easier for admins to make private API calls. In some +cases it will handle the input and output of files. + +``` +jc@aion ‹ master@61e1674 › : ~/nyu/os/assignment-tests/spring2021 +[0] % anubis assignment init new-assignment +Creating assignment directory... +Initializing the assignment with sample data... + +You now have an Anubis assignment initialized at new-assignment +cd into that directory and run the sync command to upload it to +Anubis. + +cd new-assignment +anubis assignment build --push +anubis assignment sync +``` + +Some use cases would be: +- Adding, Updating and Listing assignment data +- Adding, Updating and Listing class data +- Packaging a new assignment tests, and sending its data off to the cluster + +## 5 Assignments + +### 5.1 Creating a new Assignment + +Using the anubis cli, you can initialize a new assignment using `anubis assignment init ` + +### 5.2 Writing Tests + +All the files to build and run a complete anubis pipeline image will be dropped into the new directory. + +``` +new-assignment +|- assignment.py +|- Dockerfile +|- meta.yml +|- pipeline.py +|- test.sh +`- utils.py +``` + +The only thing you will ever need to edit is `assignment.py`. This is where you define your build and +test code. Just like all the other cool libraries out there, the anubis pipeline works through hooking +functions. Here is a minimal example of an assignment.py that will build and run a single simple test. + +```python +from utils import register_test, register_build, exec_as_student +from utils import ( + TestResult, BuildResult, Panic, DEBUG, + xv6_run, did_xv6_crash, verify_expected +) + +@register_build +def build(build_result: BuildResult): + stdout, retcode = exec_as_student('make xv6.img fs.img') + + build_result.stdout = stdout.decode() + build_result.passed = retcode == 0 + + +@register_test('test echo') +def test_1(test_result: TestResult): + test_result.stdout = "Testing echo 123\n" + + # Start xv6 and run command + stdout_lines = xv6_run("echo 123", test_result) + + # Run echo 123 as student user and capture output lines + expected_raw, _ = exec_as_student('echo 123') + expected = expected_raw.decode().strip().split('\n') + + # Attempt to detect crash + if did_xv6_crash(stdout_lines, test_result): + return + + # Test to see if the expected result was found + verify_expected(stdout_lines, expected, test_result) + +``` + +There are a couple functions to point out here. The `register_build` and `register_test` decorators are how you +tell anubis about your build and test. The `exec_as_student` is how you should call any and all student code. It +lowers the privileges way down so that even if the student pushes something malicious, they are still low privileged +enough where they can not do much. It also adds timeouts to their commands. Boxing student code in like this +is absolutely essential. Do not underestimate the creative and surprising ways students will find to break things. + +### 5.3 Uploading Tests + +Now you have your tests. That's great. The next thing you need to do is push the image to the docker registry and +upload the assignment data to anubis. This is as simple as running two commands: + +```shell +# sends assignment metadata to anubis +anubis assignment sync + +# builds then pushes the assignment +# pipeline image to the registry +anubis assignment build --push +``` diff --git a/api/anubis/models/__init__.py b/api/anubis/models/__init__.py index 2fb749a5b..b3f4b023f 100644 --- a/api/anubis/models/__init__.py +++ b/api/anubis/models/__init__.py @@ -7,7 +7,6 @@ from sqlalchemy_json import MutableJson from anubis.utils.data import rand -from anubis.utils.services.logger import logger db = SQLAlchemy() @@ -90,8 +89,11 @@ class Course(db.Model): semester = db.Column(db.TEXT, nullable=True) section = db.Column(db.TEXT, nullable=True) professor = db.Column(db.TEXT, nullable=False) - theia_default_image = db.Column(db.TEXT, nullable=False, default='registry.osiris.services/anubis/xv6') + autograde_tests_repo = db.Column(db.TEXT, nullable=False, + default='https://github.com/os3224/anubis-assignment-tests') + theia_default_image = db.Column(db.TEXT, nullable=False, default='registry.digitalocean.com/anubis/xv6') theia_default_options = db.Column(MutableJson, default=lambda: {"limits": {"cpu": "2", "memory": "500Mi"}}) + github_org_url = db.Column(db.TEXT, default='') @property def total_assignments(self): @@ -177,11 +179,11 @@ class Assignment(db.Model): course_id = db.Column(db.String(128), db.ForeignKey(Course.id), index=True) # Fields - name = db.Column(db.TEXT, nullable=False) + name = db.Column(db.TEXT, nullable=False, index=True) hidden = db.Column(db.Boolean, default=False) description = db.Column(db.TEXT, nullable=True) github_classroom_url = db.Column(db.TEXT, nullable=True, default=None) - pipeline_image = db.Column(db.TEXT, nullable=True) + pipeline_image = db.Column(db.TEXT, nullable=True, index=True) unique_code = db.Column( db.String(8), unique=True, @@ -191,7 +193,7 @@ class Assignment(db.Model): accept_late = db.Column(db.Boolean, default=True) autograde_enabled = db.Column(db.Boolean, default=True) theia_image = db.Column( - db.TEXT, default="registry.osiris.services/anubis/theia-xv6" + db.TEXT, default="registry.digitalocean.com/anubis/theia-xv6" ) theia_options = db.Column(MutableJson, default=lambda: {}) @@ -383,35 +385,27 @@ def data(self): :return: """ - response = AssignedQuestionResponse.query.filter( + from anubis.utils.lms.assignments import get_assignment_due_date + + response: AssignedQuestionResponse = AssignedQuestionResponse.query.filter( AssignedQuestionResponse.assigned_question_id == self.id, ).order_by(AssignedQuestionResponse.created.desc()).first() - raw_response = self.question.placeholder + response_data = {'submitted': None,'late': True, 'text': self.question.placeholder} if response is not None: - raw_response = response.response + response_data = response.data return { "id": self.id, - "response": raw_response, + "response": response_data, "question": self.question.data, } @property def full_data(self): - response = AssignedQuestionResponse.query.filter( - AssignedQuestionResponse.assigned_question_id == self.id, - ).order_by(AssignedQuestionResponse.created.desc()).first() - - raw_response = self.question.placeholder - if response is not None: - raw_response = response.response - - return { - "id": self.id, - "question": self.question.full_data, - "response": raw_response, - } + data = self.data + data['question'] = self.question.full_data + return data class AssignedQuestionResponse(db.Model): @@ -432,6 +426,16 @@ class AssignedQuestionResponse(db.Model): created = db.Column(db.DateTime, default=datetime.now) last_updated = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) + @property + def data(self): + from anubis.utils.lms.assignments import get_assignment_due_date + + return { + 'submitted': str(self.created), + 'late': get_assignment_due_date(self.question.owner, self.question.assignment) < self.created, + 'text': self.response, + } + class Submission(db.Model): __tablename__ = "submission" @@ -467,7 +471,7 @@ class Submission(db.Model): # Relationships owner = db.relationship(User) assignment = db.relationship(Assignment) - build = db.relationship("SubmissionBuild", cascade="all,delete", backref='submission') + build = db.relationship("SubmissionBuild", cascade="all,delete", uselist=False, backref='submission') test_results = db.relationship("SubmissionTestResult", cascade="all,delete", backref='submission') repo = db.relationship(AssignmentRepo, backref='submissions') @@ -658,7 +662,7 @@ class TheiaSession(db.Model): state = db.Column(db.TEXT) cluster_address = db.Column(db.TEXT, nullable=True, default=None) image = db.Column( - db.TEXT, default="registry.osiris.services/anubis/theia-xv6" + db.TEXT, default="registry.digitalocean.com/anubis/theia-xv6" ) options = db.Column(MutableJson, nullable=False, default=lambda: dict()) network_locked = db.Column(db.Boolean, default=True) @@ -766,6 +770,3 @@ def data(self): 'assignment_id': self.assignment_id, 'due_date': str(self.due_date) } - - - diff --git a/api/anubis/rpc/seed.py b/api/anubis/rpc/seed.py index 8eb7fc66b..06b8589f8 100644 --- a/api/anubis/rpc/seed.py +++ b/api/anubis/rpc/seed.py @@ -62,6 +62,8 @@ def seed(): intro_to_os_course = create_course( intro_to_os_students, name="Intro to OS", course_code="CS-UY 3224", section="A", professor="Gustavo", + autograde_tests_repo='https://github.com/os3224/anubis-assignment-tests', + github_org_url='https://github.com/os3224', ) os_assignment, _, os_submissions, _ = create_assignment(intro_to_os_course, intro_to_os_students) init_submissions(os_submissions) @@ -75,6 +77,8 @@ def seed(): mmds_course = create_course( mmds_students, name="Mining Massive Datasets", course_code="CS-UY 3843", section="A", professor="Gustavo", + autograde_tests_repo='https://github.com/os3224/anubis-assignment-tests', + github_org_url='https://github.com/os3224' ) mmds_assignment, _, mmds_submissions, _ = create_assignment(mmds_course, mmds_students) init_submissions(mmds_submissions) diff --git a/api/anubis/rpc/theia.py b/api/anubis/rpc/theia.py index cbd4e1ff7..c52e377ea 100644 --- a/api/anubis/rpc/theia.py +++ b/api/anubis/rpc/theia.py @@ -52,7 +52,7 @@ def create_theia_pod_obj(theia_session: TheiaSession): # Init container init_container = client.V1Container( name="theia-init-{}-{}".format(theia_session.owner.netid, theia_session.id), - image="registry.osiris.services/anubis/theia-init:latest", + image="registry.digitalocean.com/anubis/theia-init:latest", image_pull_policy=os.environ.get("IMAGE_PULL_POLICY", default="Always"), env=[ client.V1EnvVar(name="GIT_REPO", value=theia_session.repo_url), @@ -91,6 +91,14 @@ def create_theia_pod_obj(theia_session: TheiaSession): name='AUTOSAVE', value='ON' if autosave else 'OFF', ), + client.V1EnvVar( + name='COURSE_ID', + value=theia_session.course_id, + ), + client.V1EnvVar( + name='COURSE_CODE', + value=theia_session.course.course_code, + ), *extra_env, ], resources=client.V1ResourceRequirements( @@ -111,29 +119,32 @@ def create_theia_pod_obj(theia_session: TheiaSession): containers.append(theia_container) # Sidecar container - if autosave: - sidecar_container = client.V1Container( - name="sidecar", - image="registry.osiris.services/anubis/theia-sidecar:latest", - image_pull_policy=os.environ.get("IMAGE_PULL_POLICY", default="Always"), - env=[ - client.V1EnvVar( - name="GIT_CRED", - value_from=client.V1EnvVarSource( - secret_key_ref=client.V1SecretKeySelector( - name="git", key="credentials" - ) - ), + sidecar_container = client.V1Container( + name="sidecar", + image="registry.digitalocean.com/anubis/theia-sidecar:latest", + image_pull_policy=os.environ.get("IMAGE_PULL_POLICY", default="Always"), + env=[ + client.V1EnvVar( + name="GIT_CRED", + value_from=client.V1EnvVarSource( + secret_key_ref=client.V1SecretKeySelector( + name="git", key="credentials" + ) ), - ], - volume_mounts=[ - client.V1VolumeMount( - mount_path="/home/project", - name=volume_name, - ) - ], - ) - containers.append(sidecar_container) + ), + client.V1EnvVar( + name='AUTOSAVE', + value='ON' if autosave else 'OFF', + ), + ], + volume_mounts=[ + client.V1VolumeMount( + mount_path="/home/project", + name=volume_name, + ) + ], + ) + containers.append(sidecar_container) extra_labels = {} spec_extra = {} @@ -238,6 +249,8 @@ def initialize_theia_session(theia_session_id: str): name = get_theia_pod_name(theia_session) n = 10 while True: + + # Get the pod information from the kubernetes api pod: client.V1Pod = v1.read_namespaced_pod( name=name, namespace="anubis", @@ -245,6 +258,8 @@ def initialize_theia_session(theia_session_id: str): if pod.status.phase == "Pending": n += 1 + + # Wait at 60 iterations while it is pending before giving up if n > 60: logger.error( "Theia session took too long to initialize. Freeing worker." @@ -254,8 +269,17 @@ def initialize_theia_session(theia_session_id: str): time.sleep(1) if pod.status.phase == "Running": + # Set the cluster address and state theia_session.cluster_address = pod.status.pod_ip theia_session.state = "Running" + + # We need to introduce a small amount of time here + # to give the theia server a moment to actually run + # before the button on the web frontend to go to + # the session is available to the user + time.sleep(1) + + # Index the event esindex( "theia", body={ @@ -264,6 +288,8 @@ def initialize_theia_session(theia_session_id: str): "netid": theia_session.owner.netid, }, ) + + # Log the event logger.info("Theia session started {}".format(name)) break diff --git a/api/anubis/utils/auth.py b/api/anubis/utils/auth.py index ed79f5d31..f5e94513e 100644 --- a/api/anubis/utils/auth.py +++ b/api/anubis/utils/auth.py @@ -5,8 +5,7 @@ from typing import Union import jwt -from flask import g -from flask import request +from flask import g, request, has_request_context from anubis.config import config from anubis.models import User, TAForCourse, ProfessorForCourse @@ -74,6 +73,9 @@ def get_token() -> Union[str, None]: :return: """ + if not has_request_context(): + return None + return request.headers.get("token", default=None) or request.cookies.get( "token", default=None ) or request.args.get('token', default=None) diff --git a/api/anubis/utils/data.py b/api/anubis/utils/data.py index 427ea79cb..62e433643 100644 --- a/api/anubis/utils/data.py +++ b/api/anubis/utils/data.py @@ -326,8 +326,8 @@ def wrapper(*args, **kwargs): # Push an app context with app.app_context(): - - # Call the function within an app context - return function(*args, **kwargs) + with app.test_request_context(): + # Call the function within an app context + return function(*args, **kwargs) return wrapper diff --git a/api/anubis/utils/lms/assignments.py b/api/anubis/utils/lms/assignments.py index 356858890..099758e93 100644 --- a/api/anubis/utils/lms/assignments.py +++ b/api/anubis/utils/lms/assignments.py @@ -153,7 +153,7 @@ def assignment_sync(assignment_data: dict) -> Tuple[Union[dict, str], bool]: ) ).first() if course is None: - return "Unable to find class", False + return "Unable to find course", False assert_course_admin(course.id) diff --git a/api/anubis/utils/lms/questions.py b/api/anubis/utils/lms/questions.py index 71d4ee4a3..69bf28e12 100644 --- a/api/anubis/utils/lms/questions.py +++ b/api/anubis/utils/lms/questions.py @@ -6,11 +6,13 @@ Assignment, AssignmentQuestion, AssignedStudentQuestion, + AssignedQuestionResponse, User, InCourse, ) from anubis.utils.data import _verify_data_shape, is_debug from anubis.utils.services.cache import cache +from anubis.utils.lms.students import get_students def get_question_pool_mapping( @@ -41,28 +43,64 @@ def get_question_pool_mapping( return sequence_to_questions -def hard_reset_questions(assignment: Assignment): +def reset_question_assignments(assignment: Assignment, commit: bool = True): """ - Hard reset all questions for a given assignment. This - will delete question assignments and the questions - themselves. + Reset the question assignments for an assignment. This will + first delete all the existing question responses, then + the question assignments. The questions themselves will stay :param assignment: + :param commit: :return: """ - # Delete the student question assignments + # Get all the question assignments for this assignment + assigned_student_questions = AssignedStudentQuestion.query.filter( + AssignedStudentQuestion.assignment_id == assignment.id + ).all() + + # Break them down into ids + assigned_student_question_ids = list(map(lambda x: x.id, assigned_student_questions)) + + # Delete all responses for the assignment + AssignedQuestionResponse.query.filter( + AssignedQuestionResponse.assigned_question_id.in_( + assigned_student_question_ids + ), + ).delete() + + # Delete the question assignments AssignedStudentQuestion.query.filter( AssignedStudentQuestion.assignment_id == assignment.id ).delete() + # Commit the delete + if commit: + db.session.commit() + + +def hard_reset_questions(assignment: Assignment, commit: bool = True): + """ + Hard reset all questions for a given assignment. This + will delete question assignments and the questions + themselves. + + :param assignment: + :param commit: + :return: + """ + + # Delete the student responses, and question assignments + reset_question_assignments(assignment, commit=False) + # Delete the questions themselves AssignmentQuestion.query.filter( AssignmentQuestion.assignment_id == assignment.id ).delete() - # commit the delete - db.session.commit() + # Commit the delete + if commit: + db.session.commit() def assign_questions(assignment: Assignment): @@ -243,11 +281,36 @@ def get_assigned_questions(assignment_id: str, user_id: str, full: bool = False) """ # Get assigned questions - assigned_questions = AssignedStudentQuestion.query.filter( + assigned_questions = AssignedStudentQuestion.query.join(AssignmentQuestion).filter( AssignedStudentQuestion.assignment_id == assignment_id, AssignedStudentQuestion.owner_id == user_id, - ).all() + ).order_by(AssignmentQuestion.pool).all() if not full: return [assigned_question.data for assigned_question in assigned_questions] return [assigned_question.full_data for assigned_question in assigned_questions] + + +def get_question_assignments(assignment: Assignment): + """ + + :param assignment: + :return: + """ + + # Create a dictionary of question assignments + # netid -> get_assigned_questions(assignment, student, full=True) + assignments = {} + + # Get all the students in the course + students = get_students(assignment.course_id) + + for student in students: + assignments[student['netid']] = { + 'name': student['name'], + 'netid': student['netid'], + 'questions': get_assigned_questions(assignment.id, student['id'], full=True), + } + + return assignments + diff --git a/api/anubis/utils/seed.py b/api/anubis/utils/seed.py index b21d06549..7ed2a9005 100644 --- a/api/anubis/utils/seed.py +++ b/api/anubis/utils/seed.py @@ -74,7 +74,7 @@ def create_assignment(course, users): # Assignment 1 uniq assignment = Assignment( id=rand(), name=f"assignment {course.name}", unique_code=rand(8), hidden=False, - pipeline_image=f"registry.osiris.services/anubis/assignment/{rand(8)}", + pipeline_image=f"registry.digitalocean.com/anubis/assignment/{rand(8)}", github_classroom_url='http://localhost', release_date=datetime.now() - timedelta(hours=2), due_date=datetime.now() + timedelta(hours=12), diff --git a/api/anubis/views/admin/assignments.py b/api/anubis/views/admin/assignments.py index 10e265b84..eaf25704f 100644 --- a/api/anubis/views/admin/assignments.py +++ b/api/anubis/views/admin/assignments.py @@ -286,7 +286,7 @@ def private_assignment_sync(assignment: dict): "hidden": true, "github_classroom_url": "", "unique_code": "{code}", - "pipeline_image": "registry.osiris.services/anubis/assignment/{code}", + "pipeline_image": "registry.digitalocean.com/anubis/assignment/{code}", "date": { "release": "{now}", "due": "{week_from_now}", diff --git a/api/anubis/views/admin/autograde.py b/api/anubis/views/admin/autograde.py index 3615b9ef6..99da34b6e 100644 --- a/api/anubis/views/admin/autograde.py +++ b/api/anubis/views/admin/autograde.py @@ -1,6 +1,6 @@ from flask import Blueprint -from anubis.models import Submission, Assignment, User +from anubis.models import Submission, Assignment, User, InCourse from anubis.utils.auth import require_admin from anubis.utils.http.decorators import json_response from anubis.utils.http.https import success_response, error_response, get_number_arg @@ -91,9 +91,12 @@ def admin_autograde_assignment_assignment_id(assignment_id): # Get the (possibly cached) autograde calculations bests = bulk_autograde(assignment_id, limit=limit, offset=offset) + total = User.query.join(InCourse).filter( + InCourse.course_id == assignment.course_id, + ).count() # Pass back the results - return success_response({"stats": bests}) + return success_response({"stats": bests, "total": total}) @autograde_.route("/for//") @@ -139,7 +142,7 @@ def admin_autograde_for_assignment_id_user_id(assignment_id, user_id): @autograde_.route("/submission//") @require_admin() @log_endpoint("cli", lambda: "submission-stats") -@cache.memoize(timeout=60) +@cache.memoize(timeout=60, source_check=True) @json_response def private_submission_stats_id(assignment_id: str, netid: str): """ diff --git a/api/anubis/views/admin/ide.py b/api/anubis/views/admin/ide.py index 65cb58249..2cb625c9a 100644 --- a/api/anubis/views/admin/ide.py +++ b/api/anubis/views/admin/ide.py @@ -16,6 +16,22 @@ ide = Blueprint("admin-ide", __name__, url_prefix="/admin/ide") +@ide.route("/settings") +@require_admin() +@json_response +def admin_ide_admin_settings(): + # Get the current course context + course = get_course_context() + + return success_response({'settings': { + "privileged": True, + "network_locked": False, + "image": "registry.digitalocean.com/anubis/theia-admin", + "repo_url": course.autograde_tests_repo, + "options": '{"limits": {"cpu": "2", "memory": "2Gi"}, "autosave": true, "credentials": true}', + }}) + + @ide.route("/initialize", methods=["POST"]) @ide.route("/initialize-custom", methods=["POST"]) @require_admin() @@ -52,7 +68,7 @@ def admin_ide_initialize_custom(settings: dict, **_): # Read the options out of the posted data network_locked = settings.get('network_locked', False) privileged = settings.get('privileged', True) - image = settings.get('image', 'registry.osiris.services/anubis/theia-admin') + image = settings.get('image', 'registry.digitalocean.com/anubis/theia-admin') repo_url = settings.get('repo_url', 'https://github.com/os3224/anubis-assignment-tests') options_str = settings.get('options', '{"limits": {"cpu": "4", "memory": "4Gi"}}') diff --git a/api/anubis/views/admin/questions.py b/api/anubis/views/admin/questions.py index 9f3d34b0f..1f9808f1c 100644 --- a/api/anubis/views/admin/questions.py +++ b/api/anubis/views/admin/questions.py @@ -5,37 +5,37 @@ from anubis.utils.auth import require_admin from anubis.utils.http.decorators import json_response, json_endpoint from anubis.utils.http.https import error_response, success_response -from anubis.utils.services.elastic import log_endpoint from anubis.utils.lms.course import ( - assert_course_admin, assert_course_superuser, assert_course_context, - get_course_context, ) from anubis.utils.lms.questions import ( hard_reset_questions, get_all_questions, assign_questions, + reset_question_assignments, +get_question_assignments, ) +from anubis.utils.services.elastic import log_endpoint questions = Blueprint("admin-questions", __name__, url_prefix="/admin/questions") -@questions.route("/add/") +@questions.route("/add/") @require_admin() @log_endpoint("admin", lambda: "add new question") @json_response -def admin_questions_add_unique_code(unique_code: str): +def admin_questions_add_unique_code(assignment_id: str): """ Add a new blank question to the assignment. - :param unique_code: + :param assignment_id: :return: """ # Try to find assignment assignment: Assignment = Assignment.query.filter( - Assignment.unique_code == unique_code + Assignment.id == assignment_id ).first() # If the assignment does not exist, then stop @@ -113,11 +113,11 @@ def admin_questions_delete_question_id(assignment_question_id: str): }) -@questions.route("/hard-reset/") +@questions.route("/hard-reset/") @require_admin() @log_endpoint("admin", lambda: "question hard reset") @json_response -def private_questions_hard_reset_unique_code(unique_code: str): +def private_questions_hard_reset_unique_code(assignment_id: str): """ This endpoint should be used very sparingly. When this is hit, assuming the assignment exists, it will delete all questions @@ -134,13 +134,13 @@ def private_questions_hard_reset_unique_code(unique_code: str): ** Be careful with this one ** - :param unique_code: + :param assignment_id: :return: """ # Try to find assignment assignment: Assignment = Assignment.query.filter( - Assignment.unique_code == unique_code + Assignment.id == assignment_id ).first() # If the assignment does not exist, then stop @@ -163,11 +163,11 @@ def private_questions_hard_reset_unique_code(unique_code: str): }) -@questions.route("/reset-assignments/") +@questions.route("/reset-assignments/") @require_admin() @log_endpoint("admin", lambda: "reset question assignments") @json_response -def private_questions_reset_assignments_unique_code(unique_code: str): +def private_questions_reset_assignments_assignment_id(assignment_id: str): """ This endpoint should be used very sparingly. When this is hit, assuming the assignment exists, it will delete all questions @@ -184,13 +184,13 @@ def private_questions_reset_assignments_unique_code(unique_code: str): ** Be careful with this one ** - :param unique_code: + :param assignment_id: :return: """ # Try to find assignment assignment: Assignment = Assignment.query.filter( - Assignment.unique_code == unique_code + Assignment.id == assignment_id ).first() # Verify that the assignment exists @@ -203,13 +203,8 @@ def private_questions_reset_assignments_unique_code(unique_code: str): # Assert that the set course context matches the course of the assignment assert_course_context(assignment) - # Delete all the question assignments - AssignedStudentQuestion.query.filter( - AssignedStudentQuestion.assignment_id == assignment.id - ).delete() - - # Commit the delete - db.session.commit() + # Reset the question assignments + reset_question_assignments(assignment, commit=True) # Pass back the status return success_response({ @@ -248,7 +243,7 @@ def admin_questions_update(assignment_question_id: str, question: dict): db_assignment_question.solution = question['solution'] db_assignment_question.code_language = question['code_language'] db_assignment_question.code_question = question['code_question'] - db_assignment_question.sequence = question['sequence'] + db_assignment_question.pool = question['pool'] # Commit any changes db.session.commit() @@ -259,21 +254,21 @@ def admin_questions_update(assignment_question_id: str, question: dict): }) -@questions.route("/get-assignments/") +@questions.route("/get-assignments/") @require_admin() @log_endpoint("admin", lambda: "questions get") @json_response -def private_questions_get_assignments_unique_code(unique_code: str): +def private_questions_get_assignments_unique_code(assignment_id: str): """ Get all questions for the given assignment. - :param unique_code: + :param assignment_id: :return: """ # Try to find assignment assignment: Assignment = Assignment.query.filter( - Assignment.unique_code == unique_code + Assignment.id == assignment_id ).first() # If the assignment does not exist, then stop @@ -284,33 +279,28 @@ def private_questions_get_assignments_unique_code(unique_code: str): assert_course_context(assignment) # Get all the question assignments - assignment_questions = AssignmentQuestion.query.filter( - AssignmentQuestion.assignment_id == assignment.id, - ).order_by(AssignmentQuestion.pool, AssignmentQuestion.created.desc()).all() + question_assignments = get_question_assignments(assignment) return success_response({ - 'questions': [ - assignment_question.full_data - for assignment_question in assignment_questions - ] + 'assignments': question_assignments, }) -@questions.route("/get/") +@questions.route("/get/") @require_admin() @log_endpoint("admin", lambda: "get question assignments") @json_response -def private_questions_get_unique_code(unique_code: str): +def private_questions_get_unique_code(assignment_id: str): """ Get all questions for the given assignment. - :param unique_code: + :param assignment_id: :return: """ # Try to find assignment assignment: Assignment = Assignment.query.filter( - Assignment.unique_code == unique_code + Assignment.id == assignment_id ).first() # Verify that the assignment exists @@ -320,16 +310,23 @@ def private_questions_get_unique_code(unique_code: str): # Assert that the assignment is within the course context assert_course_context(assignment) + assigned_question_count = AssignedStudentQuestion.query.filter( + AssignedStudentQuestion.assignment_id == assignment.id + ).count() + return success_response({ - 'questions': get_all_questions(assignment) + 'assignment_name': assignment.name, + 'questions': get_all_questions(assignment), + 'questions_assigned': assigned_question_count > 0, + 'assigned_question_count': assigned_question_count, }) -@questions.route("/assign/") +@questions.route("/assign/") @require_admin() @log_endpoint("admin", lambda: "question assign") @json_response -def private_questions_assign_unique_code(unique_code: str): +def private_questions_assign_unique_code(assignment_id: str): """ Assign questions that have been created. This action will only run once. Once a question is assigned to a student, the only way to change it is @@ -342,7 +339,7 @@ def private_questions_assign_unique_code(unique_code: str): # Try to find assignment assignment: Assignment = Assignment.query.filter( - Assignment.unique_code == unique_code + Assignment.id == assignment_id ).first() # Verify that we got an assignment diff --git a/api/anubis/views/public/questions.py b/api/anubis/views/public/questions.py index 87eb98f86..47685ef65 100644 --- a/api/anubis/views/public/questions.py +++ b/api/anubis/views/public/questions.py @@ -1,4 +1,5 @@ from flask import Blueprint +from datetime import datetime from sqlalchemy.exc import IntegrityError, DataError from anubis.models import db, Assignment, AssignedStudentQuestion, AssignedQuestionResponse, User @@ -8,6 +9,7 @@ from anubis.utils.lms.course import is_course_admin from anubis.utils.lms.questions import get_assigned_questions from anubis.utils.services.elastic import log_endpoint +from anubis.utils.lms.assignments import get_assignment_due_date questions = Blueprint("public-questions", __name__, url_prefix="/public/questions") @@ -70,6 +72,27 @@ def public_questions_save(id: str, response: str): if not isinstance(response, str): return error_response('response must be a string') + # Get the assignment that this question exists for + assignment = assigned_question.assignment + + # If the assignment is set to not accept late submissions, + # then we need to do a quick check to make sure they are submitting + # on time. + if not assignment.accept_late: + # Calculate now + now = datetime.now() + + # Calculate the assignment due date for this student + due_date = get_assignment_due_date(user, assignment) + + # Make sure that the deadline has not passed. If it has, then + # we should give them an error saying that they can request a + # regrade from the Professor. + if due_date < now: + return error_response( + 'This assignment does not accept late submissions. You can request an extension from your Professor.' + ) + # Create a new response res = AssignedQuestionResponse( assigned_question_id=assigned_question.id, @@ -91,4 +114,5 @@ def public_questions_save(id: str, response: str): return success_response({ "status": "Response Saved", + "response": res.data, }) diff --git a/api/anubis/views/public/webhook.py b/api/anubis/views/public/webhook.py index ee394e7d9..4c65233af 100644 --- a/api/anubis/views/public/webhook.py +++ b/api/anubis/views/public/webhook.py @@ -18,8 +18,8 @@ from anubis.utils.lms.assignments import get_assignment_due_date from anubis.utils.lms.webhook import parse_webhook, guess_github_username, check_repo from anubis.utils.lms.submissions import get_submissions -from anubis.utils.lms.repos import get_repos from anubis.utils.services.elastic import log_endpoint, esindex +from anubis.utils.services.cache import cache from anubis.utils.services.logger import logger from anubis.utils.services.rpc import enqueue_autograde_pipeline from anubis.utils.lms.submissions import reject_late_submission, init_submission @@ -134,8 +134,8 @@ def public_webhook(): if not is_debug(): # Make sure that the repo we're about to process actually belongs to - # our organization - if not request.json["repository"]["full_name"].startswith("os3224/"): + # a github organization that matches a course. + if not repo_url.startswith(assignment.course.github_org_url): logger.error( "Invalid github organization in webhook.", extra={ diff --git a/api/jobs/reaper.py b/api/jobs/reaper.py index d45e91a68..b7854e48b 100644 --- a/api/jobs/reaper.py +++ b/api/jobs/reaper.py @@ -11,7 +11,6 @@ from anubis.utils.lms.submissions import init_submission from anubis.utils.lms.webhook import check_repo, guess_github_username from anubis.utils.services.rpc import enqueue_ide_reap_stale, enqueue_autograde_pipeline -rom anubis.utils.visuals.assignments import get_assignment_sundial def reap_stale_submissions(): diff --git a/api/migrations/versions/2af8f3918f87_add_github_org_url.py b/api/migrations/versions/2af8f3918f87_add_github_org_url.py new file mode 100644 index 000000000..da24e2617 --- /dev/null +++ b/api/migrations/versions/2af8f3918f87_add_github_org_url.py @@ -0,0 +1,57 @@ +"""ADD github org url + +Revision ID: 2af8f3918f87 +Revises: 31dc6a1a6759 +Create Date: 2021-05-23 10:42:43.278835 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = "2af8f3918f87" +down_revision = "31dc6a1a6759" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "course", sa.Column("github_org_url", sa.TEXT(), nullable=True) + ) + op.alter_column( + "static_file", + "course_id", + existing_type=mysql.VARCHAR(length=128), + nullable=False, + ) + op.alter_column( + "theia_session", + "course_id", + existing_type=mysql.VARCHAR(length=128), + nullable=False, + ) + conn = op.get_bind() + with conn.begin(): + conn.execute("update course set github_org_url = 'https://github.com/os3224';") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "theia_session", + "course_id", + existing_type=mysql.VARCHAR(length=128), + nullable=True, + ) + op.alter_column( + "static_file", + "course_id", + existing_type=mysql.VARCHAR(length=128), + nullable=True, + ) + op.drop_column("course", "github_org_url") + # ### end Alembic commands ### diff --git a/api/migrations/versions/31dc6a1a6759_add_autograde_tests_repo.py b/api/migrations/versions/31dc6a1a6759_add_autograde_tests_repo.py new file mode 100644 index 000000000..b760a325c --- /dev/null +++ b/api/migrations/versions/31dc6a1a6759_add_autograde_tests_repo.py @@ -0,0 +1,28 @@ +"""ADD autograde_tests_repo + +Revision ID: 31dc6a1a6759 +Revises: 716e3e14891d +Create Date: 2021-05-20 20:57:40.110207 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = "31dc6a1a6759" +down_revision = "716e3e14891d" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("course", sa.Column("autograde_tests_repo", sa.TEXT(), nullable=False, default="https://github.com/os3224/anubis-assignment-tests")) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("course", "autograde_tests_repo") + # ### end Alembic commands ### diff --git a/api/migrations/versions/4331be83342a_add_theia_options_to_assignment_and_.py b/api/migrations/versions/4331be83342a_add_theia_options_to_assignment_and_.py index 4f9f514c5..019195a84 100644 --- a/api/migrations/versions/4331be83342a_add_theia_options_to_assignment_and_.py +++ b/api/migrations/versions/4331be83342a_add_theia_options_to_assignment_and_.py @@ -29,7 +29,7 @@ def upgrade(): conn = op.get_bind() with conn.begin(): conn.execute("update assignment set theia_options = '{}';") - conn.execute("update course set theia_default_image = 'registry.osiris.services/anubis/theia-xv6';") + conn.execute("update course set theia_default_image = 'registry.digitalocean.com/anubis/theia-xv6';") conn.execute("update course set theia_default_options = '{}';") # ### end Alembic commands ### diff --git a/api/tests/test_assignment_admin.py b/api/tests/test_assignment_admin.py index a992d0f2e..530be275d 100644 --- a/api/tests/test_assignment_admin.py +++ b/api/tests/test_assignment_admin.py @@ -8,7 +8,7 @@ "hidden": True, "github_classroom_url": "", "unique_code": "aa11bb22", - "pipeline_image": "registry.osiris.services/anubis/assignment/aa11bb2233", + "pipeline_image": "registry.digitalocean.com/anubis/assignment/aa11bb2233", "date": { "release": str(datetime.now() - timedelta(hours=2)), "due": str(datetime.now() + timedelta(hours=12)), diff --git a/api/tests/test_ide_admin.py b/api/tests/test_ide_admin.py index 2f0fcb57f..dd93d4a9c 100644 --- a/api/tests/test_ide_admin.py +++ b/api/tests/test_ide_admin.py @@ -3,7 +3,7 @@ settings_sample = { 'network_locked': False, 'privileged': True, - 'image': 'registry.osiris.services/anubis/theia-admin', + 'image': 'registry.digitalocean.com/anubis/theia-admin', 'repo_url': 'https://github.com/os3224/anubis-assignment-tests', 'options': '{"limits": {"cpu": "4", "memory": "4Gi"}}', } diff --git a/api/tests/test_questions_admin.py b/api/tests/test_questions_admin.py index 5218868b7..01901fc66 100644 --- a/api/tests/test_questions_admin.py +++ b/api/tests/test_questions_admin.py @@ -5,7 +5,7 @@ 'solution': 'solution', 'code_language': 'markdown', 'code_question': True, - 'sequence': 0, + 'pool': 0, } @@ -15,15 +15,15 @@ def test_questions_admin(): student = Session('student') ta = Session('ta') - unique_code = superuser.get('/admin/assignments/list')['assignments'][0]['unique_code'] - superuser.get(f'/admin/questions/reset-assignments/{unique_code}') + assignment_id = superuser.get('/admin/assignments/list')['assignments'][0]['id'] + superuser.get(f'/admin/questions/reset-assignments/{assignment_id}') - permission_test(f'/admin/questions/get/{unique_code}') - permission_test(f'/admin/questions/get-assignments/{unique_code}') + permission_test(f'/admin/questions/get/{assignment_id}') + permission_test(f'/admin/questions/get-assignments/{assignment_id}') - permission_test(f'/admin/questions/add/{unique_code}') - permission_test(f'/admin/questions/add/{unique_code}') - questions = superuser.get(f'/admin/questions/get/{unique_code}')['questions'] + permission_test(f'/admin/questions/add/{assignment_id}') + permission_test(f'/admin/questions/add/{assignment_id}') + questions = superuser.get(f'/admin/questions/get/{assignment_id}')['questions'] print(questions) question_id = questions[0]['id'] permission_test(f'/admin/questions/update/{question_id}', method='post', json={'question': sample_question}) @@ -33,13 +33,13 @@ def test_questions_admin(): ta.get(f'/admin/questions/delete/{questions[2]["id"]}') student.get(f'/admin/questions/delete/{questions[3]["id"]}', should_fail=True) - permission_test(f'/admin/questions/add/{unique_code}') - superuser.get(f'/admin/questions/reset-assignments/{unique_code}') + permission_test(f'/admin/questions/add/{assignment_id}') + superuser.get(f'/admin/questions/reset-assignments/{assignment_id}') permission_test( - f'/admin/questions/assign/{unique_code}', - after=lambda: superuser.get(f'/admin/questions/reset-assignments/{unique_code}') + f'/admin/questions/assign/{assignment_id}', + after=lambda: superuser.get(f'/admin/questions/reset-assignments/{assignment_id}') ) - superuser.get(f'/admin/questions/reset-assignments/{unique_code}') - permission_test(f'/admin/questions/reset-assignments/{unique_code}', fail_for=['student', 'ta']) - permission_test(f'/admin/questions/hard-reset/{unique_code}', fail_for=['student', 'ta']) + superuser.get(f'/admin/questions/reset-assignments/{assignment_id}') + permission_test(f'/admin/questions/reset-assignments/{assignment_id}', fail_for=['student', 'ta']) + permission_test(f'/admin/questions/hard-reset/{assignment_id}', fail_for=['student', 'ta']) diff --git a/api/tests/test_questions_public.py b/api/tests/test_questions_public.py index 72e32728c..3e947b3e5 100644 --- a/api/tests/test_questions_public.py +++ b/api/tests/test_questions_public.py @@ -23,4 +23,4 @@ def test_questions_public(): json={'response': None}, should_fail=True, ) _questions = s.get(f'/public/questions/get/{assignment_id}')['questions'] - assert _questions[index]['response'] == 'test123' + assert _questions[index]['response']['text'] == 'test123' diff --git a/docker-compose.yml b/docker-compose.yml index ab7665406..240ec70d6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,7 +24,7 @@ services: - "traefik.enable=false" api: - image: registry.osiris.services/anubis/api:latest + image: registry.digitalocean.com/anubis/api:latest build: ./api command: sh -c "while true; do python3 dev.py; sleep 1; done" ports: @@ -49,7 +49,7 @@ services: pipeline-api: build: ./api - image: registry.osiris.services/anubis/api:latest + image: registry.digitalocean.com/anubis/api:latest environment: - "DEBUG=1" - "DB_HOST=db" @@ -71,7 +71,7 @@ services: volumes: - "./api:/opt/app" labels: - - "traefik.enable=false" + - "traefik.false=enable" rpc-theia: build: ./api @@ -96,7 +96,7 @@ services: - "traefik.enable=false" web: - image: registry.osiris.services/anubis/web:latest + image: registry.digitalocean.com/anubis/web:latest build: ./web web-dev: @@ -162,7 +162,7 @@ services: - "traefik.enable=false" logstash: - image: registry.osiris.services/anubis/logstash:latest + image: registry.digitalocean.com/anubis/logstash:latest build: ./logstash labels: - "traefik.enable=false" @@ -175,23 +175,23 @@ services: - "/usr/share/zoneinfo/America/New_York:/etc/localtime:ro" theia-xv6: - image: registry.osiris.services/anubis/theia-xv6:latest + image: registry.digitalocean.com/anubis/theia-xv6:latest build: ./theia/ide/xv6 theia-admin: - image: registry.osiris.services/anubis/theia-admin:latest + image: registry.digitalocean.com/anubis/theia-admin:latest build: ./theia/ide/admin theia-proxy: - image: registry.osiris.services/anubis/theia-proxy:latest + image: registry.digitalocean.com/anubis/theia-proxy:latest build: ./theia/proxy theia-init: - image: registry.osiris.services/anubis/theia-init:latest + image: registry.digitalocean.com/anubis/theia-init:latest build: ./theia/init theia-sidecar: - image: registry.osiris.services/anubis/theia-sidecar:latest + image: registry.digitalocean.com/anubis/theia-sidecar:latest build: ./theia/sidecar volumes: diff --git a/docs/design.md b/docs/design.md deleted file mode 100644 index 6167b7aec..000000000 --- a/docs/design.md +++ /dev/null @@ -1,698 +0,0 @@ -# Anubis Design Doc - -![](./img/anubis-icon-1.png) - -> Author: John Cunniff - -> Version: v2.2.5 - -\pagebreak - -# Contents -- [1. Overview](#1-overview) - - [1.1 Elevator Pitch](#11-elevator-pitch) - - [1.2 Motivations](#12-motivations) -- [2. Services](#2-services) - - [2.1 Traefik](#21-traefik) - - [2.2 API](#22-api) - - [2.2.1 Zones](#221-zones) - - [2.2.2 Responsibilities](#222-responsibilities) - - [2.2.3 SSO Authentication](#223-sso-authentication) - - [2.3 Submission Pipeline](#23-submission-pipeline) - - [2.3.1 Kube Job](#231-kube-job) - - [2.3.2 Submission State Reporting](#232-submission-state-reporting) - - [2.3.3 Stages](#233-stages) - - [Clone](#clone) - - [Build](#build) - - [Test](#test) - - [2.4 Web Frontend](#24-web-frontend) - - [2.4.1 Autograde Results](#241-autograde-results) - - [2.5 Anubis Cloud IDE](#25-anubis-cloud-ide) - - [2.5.1 IDE Frontend](#251-ide-frontend) - - [2.5.2 Theia Pod Design](#252-theia-pod-design) - - [2.6 Datastores](#26-datastores) - - [2.6.1 Mariadb](#261-mariadb) - - [2.6.2 Elasticsearch](#262-elasticsearch) - - [2.6.3 Kibana](#263-kibana) - - [2.6.4 Redis + RQWorker](#264-redis--rqworker) - - [2.7 Logging](#27-logging) - - [2.7.1 logstash](#271-logstash) -- [3. Deployment](#3-deployment) - - [3.1 Kubernetes](#31-kubernetes) - - [3.1.1 Helm Chart](#311-helm-chart) - - [3.1.2 Rolling Updates](#312-rolling-updates) - - [3.1.3 Longhorn](#313-longhorn) - - [2.1.4 OSIRIS Space Cluster](#314-the-osiris-space-cluster) - - [3.1.5 Nodes](#315-nodes) - - [3.1.6 Networking](#316-networking) - - [3.1.7 Shared Services](#317-shared-services) - - [3.2 Github](#32-github) - - [3.2.1 Organization](#321-organization) - - [3.2.2 Classroom](#322-classroom) -- [4. CLI](#4-cli) - - [4.1 CLI Install](#41-installing-the-cli) - - [4.2 CLI Usage](#42-cli-usage) -- [5. Assignments](#5-assignments) - - [5.1 Creating a new Assignment](#51-creating-a-new-assignment) - - [5.2 Writing Tests](#52-writing-tests) - - [5.3 Uploading Tests](#53-uploading-tests) - -\pagebreak - -## 1 Overview - -### 1.1 Elevator Pitch - -At its core, Anubis is a tool to give Intro to OS students live feedback from their homework assignments -while they are working on them. Instead of having students submit a patch file, through github classrooms -each student will have their own private repo for every assignment. The way students then submit their work -is simply by submitting before the deadline. Students can then push, and therefore submit as many times as -they would like before the deadline. - -When a student pushes to their assignment repo, a job is launched in the Anubis cluster. That job will build -their repo, run tests on the results, and store the results in the [datastore](#26-datastores). - -Students can then navigate to the anubis website, where they will sign in through [NYU SSO](#223-sso-authentication). -From there, -they will be able to see all the current and past assignments, and submissions for their classes. They are able -to view all relevant data from their build and tests for a given submission. There they can request a regrade, -there by launching a new submission pipeline. While the submission still being processed, the frontend will poll -the [API](#22-api) for updates. In this, the frontend will be constantly updating while the submission is being -processed, giving a live and interactive feel to the frontend. Once a submission is processed Anubis will show -the students logs from their tests, and builds along with which tests passed and which failed. - -New in version v2.2.0, there is now the Anubis Cloud IDE. Using some kubernetes magic, we are able to -host [theia](https://theia-ide.org/) servers for individual students. These are essentially VSCode instances -that students can access in the browser. What makes these so powerful is that students can access a terminal -and type commands right into a bash shell which will be run in the remote container. With this setup students -have access to a fully insolated and prebuilt linux environment at a click of a button. - -### 1.2 Motivations - -The purpose of this paper is to both explain the design, and the features of the Anubis autograder. - -\pagebreak - -## 2 Services - -### 2.1 Traefik - -For our edge router, we use [traefik](https://docs.traefik.io/). Traefik will be what actually -listens on the servers external ports. All external traffic will be routed through Traefik. Above all else, -we will be using Traefik's routing features to handle the Ingress of requests. - -Traefik lets us do some spicy and powerful stuff. We can route requests to different services based off -predefined rules, and even change requests as they pass through. This makes it so that we can -have both the static store and api on the same domain. The rules are set up such that every request that -starts with a path of `/api` goes to the api service. - -By leveraging these features of Traefik, we can make it appear that the services work different when -being accessed externally. Namely, the basic authentication for certain paths (and therefore services). - -> One thing to note here is that when being accessed from within the cluster, none of these rules -> apply as we would be connecting directly to services. - -### 2.2 API - -The API is the backbone of anubis. It is where all the heavy lifting is done. The service relies on both -the [elasticsearch](#262-elasticsearch) and [mariadb](#261-mariadb) data stores to maintain state. - -#### 2.2.1 Zones - -The API is split into two distinct, and uniquely treated zones. There is a `public` and a `admin` zone. -All endpoints for Anubis fall within one of these zones. - -These zones are simply paths that are treated differently depending on where the request is external. Namely, -for the admin zone external requests will require you to be marked as an admin. By adding this simple level of -authentication, we can lock down a section of the more sensitive API to only those authenticated from the outside. - -#### 2.2.2 Responsibilities - -The Anubis API is responsible for handling most basic IO, and state managing that happens on the cluster. -Some of this includes: - -- Authenticating users -- Providing Class, Assignment, and Submission data to the frontend -- Handling github webhooks -- Handling reports from the submission pipeline cluster -- Handling regrade requests -- Initializing new IDE sessions - -#### 2.2.3 SSO Authentication - -To authenticate with the api, a token is required. The only way to get one of these tokens is through NYU -Single Sign On. By doing this, we are outsourcing our authentication. This saves a whole lot of headaches -while being arguably more secure than if we rolled our own. - -In implementation, the frontend loads it will attempt to authenticate with the API. If there is -a stale or broken token in the current cookies, the frontend will redirect users to the NYU login page. -Given that they authenticate there, they will be redirected back to the API, where we will provide them -with a token. From there, they will be logged into Anubis. - -All of this is about 20 lines on our end. All that is necessary are some keys from NYU IT. - -### 2.3 Submission Pipeline - -#### 2.3.1 Kube Job - -A given submission pipeline is of the form of a Kubernetes [Job](https://kubernetes.io/docs/concepts/workloads/controllers/job/). -These jobs have some built in assurances. A job is configurable to continue to launch new containers if a Pod fails. - -#### 2.3.2 Submission State Reporting - -At each and every stage of a submission pipeline, the job will report to the api with a state update. This state is -in the form of a string that describes what is currently happening at that moment. This data can then be passed along -to a user that is watching their pipeline be processed live. - -Each unique per-assignment pipeline is packaged in the form of a docker image. - -> See [Creating a new Assignment](#51-creating-a-new-assignment) - -> An error at any stage of the submissions pipeline will result in an error being reported to the API, and -> the container exiting. - -#### 2.3.3 Stages - -It is important to note that at each stage of the submission pipeline, we will be moving execution back and -forth between two users. There will be the entrypoint program managing the container as user `anubis`. The -`anubis` user will have much higher privileges than the `student` user. The `student` user will be used whenever -executing student code. It will not have any level of access to anything from the `anubis` user. - -![](./img/submission-flow.mmd.png) - -##### Clone - -In this initial stage, we will pull the current repo down from github. After checking out the commit for -the current submission, we will also delete the `.git` directory as it is not needed. Lastly we will -`chown` the entire repo as `student:student`. This will then be the only place in the container that the -student user can read or write to (other than /tmp of course). - -##### Build - -At this stage we hand execution off to student code for the first time. We will be building the student -code _as the `student` user_. The command for building the container will be specified by on a per-assignment -basis. The stdout of the build will be captured by the `anubis` user. - -Once built, a build report will be sent to the API, along with a state update for the submission. If the -student is on the submission page, then they should be able to see the build info shortly thereafter. - -##### Test - -Tests will be defined on a per-assignment basis. Again we are executing student code, as the student user. - -After each test, we will return to the entrypoint program as the `anubis` user. We will then send a report -of the last test before continuing to the next. - -Once we reach the last test, we send off a separate notification to the API indicating the completion of -the pipeline. It is at that point that the API marks the submission as processed. - -### 2.4 Web Frontend - -The frontend is designed to be a simple reflection of the backend data. Once authenticated, users will -be able to see the classes they are a part of, current and past assignments, and all their submissions. -With few exceptions, the frontend is a near one to one translation of the API's data models. Most pages -will have a corresponding API endpoint. The data shown on that page will be in exactly the form of the -API response. - -The notable exceptions to this simplistic model would be the submission page, the regrade button, and -the find-missing button. - -The submission page will poll for new data if the submission is not marked as processed. As it sees -new data, it will update what is displayed. - -Located on the submission page, the purpose of the regrade button is to be a simple and easy way for -users to request regrades. When clicked, the frontend will hit a special endpoint for requesting regrades. -If successful, the submission will be re-enqueued in a submission pipeline. - -On the submissions page, the find-missing button will trigger a server side update of the submission data. -When the find-missing endpoint is hit, the API will use the graphql API for github to pull all the commits -for all the known repos for that user. If it sees commits that were not previously seen (likely though github -not delivering a webhook), then it will create and enqueue the new submissions. - - -#### 2.4.1 Autograde Results - -Getting the autograde results is as easy as searching a name in the autograde results panel. You will -be taken to a page like this where the calculated best submission is shown. The best submission is the -most recent submission that passed the most tests. - -The students see a reduced version of the panel showed to the TAs and professors. In the admin view, more -data is displayed about the student. The students can see the status of the submission, and the build and -test results. - -> *Calculating the best submissions requires heavy IO with the database. For this, the autograde results are -heavily cached. Results are updated hourly.* - -![Autograde Results](./img/autograde-results.png) - - -### 2.5 Anubis Cloud IDE - -One of the more exciting new features is that of the cloud ide. Leveraging some magic that Kubernetes -and containers give us, we can provide a fully isolated IDE environment for all ~130 concurrently. - -The [Theia IDE](https://theia-ide.org/) is a basically a VSCode webserver. They have docker images for -specific languages. - -Something very special about theia is that we can actually build theia using a package.json. By doing this -we are able to compile in only the plugins we actually need/use. This is very useful because we can bring -the default theia-cpp image down from 4GiB to ~1.5GiB. This is still an annoyingly large image, but hey, it's -not 4GiB. - -#### 2.5.1 IDE Frontend - -Once students have created their repo, they will be able to launch a theia session for a given assignment. At that -time, we clone exactly what is in github into a theia pod. - -![Theia Launch Session](./img/theia1.png) - -Once their session pod has been allocated, then they can click the goto session button. It is at this point -that their requests start to go through the theia proxy. Assuming all goes well with initializing the session, -and starting the websocket connection, then students will have the following theia IDE. - -![Theia Webview](./img/theia2.png) - -Something to point out here is that this looks, feels, and acts exactly as VSCode, but it is accessed over the -internet. We can even load some VSCode plugins into the builds. - -#### 2.5.2 Theia Pod Design - -The pod design requires some distributed finesse. There are a couple of things about theia that make it so that -we need to do some rather fancy things in Kubernetes to make the setup work. - -Distributing and handling multiple theia severs and concurrent connections is the name of the game here. We -need to be able to have a setup that scales well that is able to handle many users on the system at once. - -The main thing that we need to handle is the fact that theia requires a websocket connection between the browser and -theia server instance. When the pods are allocated, we note the ClusterIP in the database. Then when we need to -initialize a client session, we use this saved ClusterIP to forward requests (both http and websockets) to the -pod. - -These pods are temporary. When the student is finished working (or after a timeout) we reclaim the resources by -deleting the containers and data. Because of this, we needed some form of autosave. Saving is pushing to github. -The issue we need to contend with is how do we have automatic commits and pushes to github without exposing a -password or api token to the users. We handle this by having a second container whose only role is committing and -pushing to github. The credentials live in that container, completely separate and inaccessible to the user who -may be working in the other theia server container. These two containers are connected by a shared longhorn volume. -This volume is relatively small (~50MiB). With this setup, we have autosave running in the background while being -completely hidden from the user. - -![Theia Pod Spec](./img/theia-pod.mmd.png) - -With these lightweight containerized theia servers, we are able to support significantly more concurrent users than -if we had elected to implement a cloud vm solution. Because with containers, we do not need to virtualize hardware, -the resources on the system for each user is significantly less. Given the resources that we have in the -Space Cluster, -we would be able to have maybe 20-30 concurrent users using cloud virtual machines. With our containerized theia, -we can handle all ~130 students at the same time with room to breath. - -#### 2.5.3 Reclaiming Theia Resources - -The theia sessions are often forgotten about. Students often create an IDE server, work on it for a bit, then forget -about it. Due to this, we have a time to live of 6 hours for each session. A cronjob runs every 5 minutes to look -for and schedule delete for stale resources. We do provide a button in the anubis panel as a way for students to manually -schedule their session for deletion. Even when we ask students to click this button when they are done, some still do not. -In the following graph we can see an interesting experiment in student behavior. It shows a cumulative graph -showing the duration of a theia session for the final exam. Just about 40% of sessions hit the 6-hour timeout. - -![Theia Cumulative Duration](./img/theia3.png) - -#### 2.5.4 Management IDE - -Separate from the student xv6 IDE, there is also an admin build of theia that has some extra things. On the assignments -page of the admin panel, any TA or professor can launch their own admin IDE. In this IDE, the assignment-tests -repo is cloned instead of a student assignment repo. It has less networking restrictions, the anubis cli -and even has docker running right in the pod. With this, TA's and professors can create, modify, and deploy -assignments. - -Using some very clever authentication mechanics, the management IDEs will be initialized with a personal token -that will be used by the CLI within the container. The result of this is that you will be able to just use the -anubis CLI. It will be routed to the in cluster API instances. All the requests you make to the API will be -authenticated using that personal token that gets dropped into the pod. - -![Management IDE](./img/theia4.png) - -### 2.6 Datastores - -#### 2.6.1 Mariadb - -Anubis will use the OSIRIS Space Clusters existing MariaDB cluster. That cluster is made from -[bitnami's MariaDB chart](https://hub.kubeapps.com/charts/bitnami/mariadb). It runs with 3 read-only -replication nodes, along with a main node that does read-write. The underlying MariaDB files are also -backed up with a [Longhorn](https://longhorn.io/) persistent volume that has 3x replication in the cluster. -That volume has daily snapshots. Redundancy is the name of the game here. - -The precise data model that we will be using is a simple relational database. From the API, we will interface -with Mariadb via [SQLAlchemy](https://www.sqlalchemy.org/). - -Here is an example of a minimal mariadb deployment installed through the bitnami chart. Obviously for production we would -want to change the password from anubis to something stronger. - -```shell -# Create a minimal mariadb deployment in a mariadb namespace. On -# prod, the mariadb is in a separate namespace, so we do the same -# here. -echo 'Adding mariadb' -kubectl create namespace mariadb -helm install mariadb \ - --set 'auth.rootPassword=anubis' \ - --set 'volumePermissions.enabled=true' \ - --set 'auth.username=anubis' \ - --set 'auth.database=anubis' \ - --set 'auth.password=anubis' \ - --set 'replication.enabled=false' \ - --namespace mariadb \ - bitnami/mariadb -``` - -#### 2.6.2 Elasticsearch - -Anubis uses elasticsearch as a search engine for logging, and event tracking. The -[elastic ecosystem](https://www.elastic.co/) has other tools like [kibana](#263-kibana) and [logstash](#271-logstash) -for visualizing data, and logging. Other than the visualization features, elastic allows us to simply start -throwing data at it without defining any meaningful shape. This allows us to just log events and data into -elastic to be retrieved, and visualized later. - -The Elasticsearch stack is pretty annoying to manage on its own. Installing through the bitnami chart is -much easier. - -```shell -# Install a minimal elasticsearch and kibana deployments -echo 'Adding elasticsearch + kibana' -kubectl create namespace anubis -helm install elasticsearch \ - --set name=elasticsearch \ - --set master.persistence.size=1Gi \ - --set data.persistence.size=1Gi \ - --set master.replicas=1 \ - --set coordinating.replicas=1 \ - --set data.replicas=1 \ - --set global.kibanaEnabled=true \ - --set fullnameOverride=elasticsearch \ - --set global.coordinating.name=coordinating \ - --namespace anubis \ - bitnami/elasticsearch -``` - -#### 2.6.3 Kibana - -The Anubis autograder generates a lot of data. We have intense logging, and event tracking on every service. -When something happens on the cluster, it will be indexed into elastic. [Kibana](https://www.elastic.co/kibana) -is elastic's data visualization tool. It runs as a website that interfaces with the internal elasticsearch. Through -kibana, we can stream live logs, view event data, and create meaningful visualizations. - -The API sees when students start their homeworks (create their github repo), and when they are submitting -(pushing to their repo). This data is indexed into elasticsearch, and visualized via kibana. In Anubis version -one we were able to show graphs of when students were starting vs finishing their assignments. To no ones surprise, -the majority of the class was starting very late, with a large influx of submissions in the few hours before each -deadline. Furthermore, we can show how long it takes for a student to start their assignment to when they have -their tests pass on average. We can also show which tests were causing students the most trouble. - -Here is one such visualization of assignment submissions over time. The peaks are the few days before a due date. - -![Submissions Over Time](./img/submissions-over-time.png) - -This incredibly precise view into the actual data that can be generated on a platform such as Anubis is -something that sets it apart from its competitors. We can show meaningful statistics to the professors and TAs -on the platform about what went well with their assignment, and where students struggled. - -#### 2.6.4 Redis + RQWorker - -Redis has two purposes in Anubis. It is used by flask-caching to cache some endpoint and function -results. For most all the functions that are heavy on the database, the results are cached for a specified period -of time. The second purpose is as a rpc job broker. We use [python-rq](https://python-rq.org/) as it is super -simple and easy to configure and set up. Using rq, we can enqueue functions to be run by a deployment of rpc-worker -pods. Just about every time we need or want to do some work asynchronously, rq is used. - -The redis setup needed for anubis is quite minimal. Again we would obviously want to change the password -here in prod. - -```shell -# Install a minimal redis deployment -echo 'Adding redis' -helm install redis \ - --set fullnameOverride=redis \ - --set password=anubis \ - --set cluster.enabled=false \ - --namespace anubis \ - bitnami/redis -``` - -### 2.7 Logging - -> _When it doubt, just log it_ - -#### 2.7.1 Logstash - -Logstash itself is a service that your applications can ship their logs to before being indexed into elasticsearch. -Its purpose is to act as a natural buffer of log data. It is able to interface with elasticsearch, adjusting the -speed of log ingestion as needed. In addition to acting as a buffer, it also enriches the data that it sees. For -example, the logstash python client will not only ship the log message, but also the file that the log is coming -from, along with which node the log is coming from. - -This centralized, and persistent logging is indexed into elasticsearch, and accessed via kiaban. Anubis uses logstash -on its API and submission pipeline. - -\pagebreak - -## 3 Deployment - -### 3.1 Kubernetes - -The main goal with moving to Kubernetes from a simple single server docker-compose setup was scalability. We needed -to scale our load horizontally. No longer was adding more RAM and CPU cores to the VM viable. With more users, we -have a greater load. Kubernetes allows us to distribute this load across the servers in the clusters quite easily. - -In moving to Kube, we are also now able to make guarantees about availability. In theory, even if sections of -physical servers on the cluster go offline Anubis will still remain available. -[More on that later...](#314-the-osiris-space-cluster) - - -#### 3.1.1 Helm Chart - -Anubis itself is packaged as a helm chart. Both mariadb and elasticsearch need to be setup separately. There -are several options for how to deploy anubis that can be specified when running the deploy script at `kube/deploy.sh`. -Notable options are deploying in debug mode, or restricting access to only the OSIRIS vpn. Options passed -to the deploy script will be then passed to helm. - - -```shell -# Deploy with anubis only being accessable from the OSIRIS vpn -./kube/deploy.sh --set vpnOnly=true - -# Deploy in debug mode -./kube/deploy.sh --set debug=true - -# Set the number of replicas for the api service to a specific value -./kube/deploy.sh --set api.replicas=5 - -# Disable rolling updates -./kube/deploy.sh --set rollingUpdates=false -``` - -A full list of options can be found in the values.yaml file at `kube/values.yaml`. - -#### 3.1.2 Rolling Updates - -In the decisions that were made when considering when expanding Anubis, availability was of the most important. -Specifically we needed to move to a platform that would allow us to do _zero_ downtime deploys. Kubernetes has -very powerful rolling update features. We can bring up a new version of the Anubis API, verify that this new -version is healthy, then bring down the old version. All the while, there will be no degradation in availability. - -Of all the features of Kubernetes that Anubis leverages, none are quite as important as rolling updates. The -Anubis API can be updated to a new version with _zero_ downtime. This means we can live patch the API with new -versions, with little to no degradation in service. - -#### 3.1.3 Longhorn - -The Kubernetes [StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/) that the Space Cluster -supports is Longhorn. It allows us to have replicated data volumes with scheduled snapshots and backups. - -All persistent data is stored on 3x replicated Longhorn StorageVolumes. Those volumes all have at least daily -snapshots taken of them. At any given time, we can reset any stateful service to a previous snapshot from the -last seven days. - -For the things that are very important we have daily snapshots, and extra replication. Longhorn makes this -as simple as checking some boxes. You can see here our mariadb master image with triple replication -(two in Brooklyn one in Manhattan), with a 7 day snapshot. - -![Longhorn Volume Management](./img/longhorn-mariadb.png) - -#### 3.1.4 The OSIRIS Space Cluster - -The OSIRIS Space Cluster is a k3s Kubernetes cluster that is managed by the OSIRIS Lab at NYU Tandon. It is -primarily used for research and website hosting. It was designed by John Cunniff to be highly redundant, even -in the case of a networking outage / natural disaster. - -Its nodes are split between a datacenter in Manhattan and one in Brooklyn. A whole lot has to fail -before the cluster gets in a state where it cannot heal itself. - -##### 3.1.5 Nodes - -The cluster is comprised of servers that are evenly distributed between the South Data Center (SDC) in lower -Manhattan and MetroTech Center (MTC) in Rogers Hall in Brooklyn. The nodes are connected via the internal -OSIRIS network. - -##### 3.1.6 Networking - -There are multiple so called "Ingress Nodes" in both the MTC and SDC. These are nodes that have both public IP -addresses and internal OSIRIS IP addresses. These nodes handle all inbound traffic to the cluster via Traefik. - -Because there are multiple IP address for the space cluster, the main ingress DNS `space.osiris.services` will -always resolve to all the current IP address. This guarantees greater availability, as if one IP is down for -whatever reason another may be available. - -The public IP addresses that are used are made up of both NYU, and POLY IP addresses. This design is quite -purposeful. It will guarantee that even in the event of a network outage at Poly or NYU, the cluster will -remain able to handle ingress. - -##### 3.1.7 Shared Services - -The space cluster has a few shared internal services. Namely, there is a shared replicated, and backed up -MariaDB instance which Anubis will use a datastore. There is also an instance of -[Kubernetes-Dashboard](https://github.com/kubernetes/dashboard) running for viewing kubernetes resources. -The Space Cluster also provides Longhorn for persistent data. - -![Kubernetes Dashboard](./img/kube-dashboard1.png) - -### 3.2 Github - -#### 3.2.1 Organization - -Each class that will need a github organization to put all its repos under. The only members of that organization -should be the professor and TAs. - -The organization should be set up to have push event webhooks to anubis. The endpoint the webhook should push -to is `https://anubis.osiris.services/api/public/webhook/`. That endpoint will be hit when there is a push to a -repo in that organization. - -#### 3.2.2 Classroom - -[Github classroom](https://classroom.github.com/) is used to create, and distribute repos for assignments. -There you can create assignments that will pull from a template repo of your choosing. A link will be generated -that can be distributed to students. When clicked, that link will generate a new repo for the student within the -class organization. As the student is not a part of the organization, they will not be able to see any of the -other student repos (given the assignment was made using a private repo). - -The best place to put the template repo is within the class organization as a private repo. - -One very important thing to note here is that in order to be able to create private assignment repo's, the -classroom you create must be verified by github. This can take a few weeks, ad a professor needs to email -github asking for permission from a .edu email providing their title and whatnot. - -> Getting your github classroom / org approved will likely cause delays if not done at least a month -> before the semester starts. - - -## 4 CLI - -### 4.1 Installing the CLI - -The command line interface for anubis is packaged as a simple pip package. To install it, make sure -you have python3 and pip installed, then from the anubis repo run `make cli`. This will run the pip -install command necessary for installing the cli globally. If that was successful, then the `anubis` -command will be installed. - -### 4.2 CLI Usage - -The main purposes of the CLI is for making it a bit easier for admins to make private API calls. In some -cases it will handle the input and output of files. - -``` -jc@aion ‹ master@61e1674 › : ~/nyu/os/assignment-tests/spring2021 -[0] % anubis assignment init new-assignment -Creating assignment directory... -Initializing the assignment with sample data... - -You now have an Anubis assignment initialized at new-assignment -cd into that directory and run the sync command to upload it to -Anubis. - -cd new-assignment -anubis assignment build --push -anubis assignment sync -``` - -Some use cases would be: -- Adding, Updating and Listing assignment data -- Adding, Updating and Listing class data -- Packaging a new assignment tests, and sending its data off to the cluster - -## 5 Assignments - -### 5.1 Creating a new Assignment - -Using the anubis cli, you can initialize a new assignment using `anubis assignment init ` - -### 5.2 Writing Tests - -All the files to build and run a complete anubis pipeline image will be dropped into the new directory. - -``` -new-assignment -|- assignment.py -|- Dockerfile -|- meta.yml -|- pipeline.py -|- test.sh -`- utils.py -``` - -The only thing you will ever need to edit is `assignment.py`. This is where you define your build and -test code. Just like all the other cool libraries out there, the anubis pipeline works through hooking -functions. Here is a minimal example of an assignment.py that will build and run a single simple test. - -```python -from utils import register_test, register_build, exec_as_student -from utils import ( - TestResult, BuildResult, Panic, DEBUG, - xv6_run, did_xv6_crash, verify_expected -) - -@register_build -def build(build_result: BuildResult): - stdout, retcode = exec_as_student('make xv6.img fs.img') - - build_result.stdout = stdout.decode() - build_result.passed = retcode == 0 - - -@register_test('test echo') -def test_1(test_result: TestResult): - test_result.stdout = "Testing echo 123\n" - - # Start xv6 and run command - stdout_lines = xv6_run("echo 123", test_result) - - # Run echo 123 as student user and capture output lines - expected_raw, _ = exec_as_student('echo 123') - expected = expected_raw.decode().strip().split('\n') - - # Attempt to detect crash - if did_xv6_crash(stdout_lines, test_result): - return - - # Test to see if the expected result was found - verify_expected(stdout_lines, expected, test_result) - -``` - -There are a couple functions to point out here. The `register_build` and `register_test` decorators are how you -tell anubis about your build and test. The `exec_as_student` is how you should call any and all student code. It -lowers the privileges way down so that even if the student pushes something malicious, they are still low privileged -enough where they can not do much. It also adds timeouts to their commands. Boxing student code in like this -is absolutely essential. Do not underestimate the creative and surprising ways students will find to break things. - -### 5.3 Uploading Tests - -Now you have your tests. That's great. The next thing you need to do is push the image to the docker registry and -upload the assignment data to anubis. This is as simple as running two commands: - -```shell -# sends assignment metadata to anubis -anubis assignment sync - -# builds then pushes the assignment -# pipeline image to the registry -anubis assignment build --push -``` diff --git a/docs/design.pdf b/docs/design.pdf index c5f854c03a6e3de71b7c211920b74cdfd3611cb8..a33c86c2880f55d6549910ccdb302aae050a6360 100644 GIT binary patch delta 70976 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 z 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

(j+&DQD$4Kd)@u;zQHu-q`S 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�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 z

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

Anubis
users
Cluster
Traefik
Anubis Cloud IDE
API
Static
logging
State Stores
rpc_cluster
submission pipeline jobs
students
Admins
Ingress
Ingress
Ingress
Ingress
Ingress
Ingress
forward
forward
forward
create
create
create
log
log
log
cache or push rpc
cache or push rpc
cache or push rpc
log stream
push state updates
push state updates
push state updates
create
create
create
rpc poll
rpc poll
rpc poll
rpc poll
store
submission job 1337
submission job 1338
submission job 1339
pipeline API
RPC Worker
RPC Worker
RPC Worker
RPC Worker
Database
elasticsearch
redis cache
logstash
web static
web static
web static
API 1
API 1
API 1
theia proxy
theia proxy
theia session 1337
theia session 1338
theia session 1339
Traefik
TA
Professor
student
student
student
student
student
student
student
student
\ No newline at end of file +
Anubis
users
Cluster
Traefik
Anubis Cloud IDE
API
Static
logging
State Stores
rpc_cluster
submission pipeline jobs
students
Admins
Ingress
Ingress
Ingress
Ingress
Ingress
Ingress
forward
forward
forward
create
create
create
log
log
log
cache or push rpc
cache or push rpc
cache or push rpc
log stream
push state updates
push state updates
push state updates
create
create
create
rpc poll
rpc poll
rpc poll
rpc poll
store
submission job 1337
submission job 1338
submission job 1339
pipeline API
RPC Worker
RPC Worker
RPC Worker
RPC Worker
Database
elasticsearch
redis cache
logstash
web static
web static
web static
API 1
API 1
API 1
theia proxy
theia proxy
theia session 1337
theia session 1338
theia session 1339
Traefik
TA
Professor
student
student
student
student
student
student
student
student
\ No newline at end of file diff --git a/docs/img/rpc-queue-1.mmd.svg b/docs/img/rpc-queue-1.mmd.svg index 73c47e720..ebadc8f6b 100644 --- a/docs/img/rpc-queue-1.mmd.svg +++ b/docs/img/rpc-queue-1.mmd.svg @@ -1 +1 @@ -
Anubis
RPC Queue
RPC worker pool
enqueue
dequeue
API
worker
job1
...
job3
\ No newline at end of file +
Anubis
RPC Queue
RPC worker pool
enqueue
dequeue
API
worker
job1
...
job3
\ No newline at end of file diff --git a/docs/img/rpc-queue-2.mmd.svg b/docs/img/rpc-queue-2.mmd.svg index b65501760..a1c2a7d96 100644 --- a/docs/img/rpc-queue-2.mmd.svg +++ b/docs/img/rpc-queue-2.mmd.svg @@ -1 +1 @@ -
Anubis
RPC Queue
RPC worker pool
enqueue
dequeue
API
worker
New Theia Start Job
job1
...
job3
\ No newline at end of file +
Anubis
RPC Queue
RPC worker pool
enqueue
dequeue
API
worker
New Theia Start Job
job1
...
job3
\ No newline at end of file diff --git a/docs/img/rpc-queue-3.mmd.svg b/docs/img/rpc-queue-3.mmd.svg index 174ce7449..6017c54f5 100644 --- a/docs/img/rpc-queue-3.mmd.svg +++ b/docs/img/rpc-queue-3.mmd.svg @@ -1 +1 @@ -
Anubis
RPC default Queue
RPC theia Queue
RPC default worker pool
RPC theia worker pool
enqueue theia start
enqueue regrade
dequeue
dequeue
API
worker
worker
New Theia Start Job
job3
...
job1
\ No newline at end of file +
Anubis
RPC default Queue
RPC theia Queue
RPC default worker pool
RPC theia worker pool
enqueue theia start
enqueue regrade
dequeue
dequeue
API
worker
worker
New Theia Start Job
job3
...
job1
\ No newline at end of file diff --git a/docs/img/submission-flow.mmd.svg b/docs/img/submission-flow.mmd.svg index dc1da667a..7bb36c4ca 100644 --- a/docs/img/submission-flow.mmd.svg +++ b/docs/img/submission-flow.mmd.svg @@ -1 +1 @@ -
submission flow
github
anubis
rpc cluster
submision pipeline
processing
webhook
push job
kube job create
anubis api
database
pipeline api
report
test
build
clone
rpc worker
github
\ No newline at end of file +
submission flow
github
anubis
rpc cluster
submision pipeline
processing
webhook
push job
kube job create
anubis api
database
pipeline api
report
test
build
clone
rpc worker
github
\ No newline at end of file diff --git a/docs/img/theia-pod.mmd.svg b/docs/img/theia-pod.mmd.svg index 5399e557f..8636db857 100644 --- a/docs/img/theia-pod.mmd.svg +++ b/docs/img/theia-pod.mmd.svg @@ -1 +1 @@ -
pod
student
proxy
proxy instance
proxy instance
proxy instance
theia
highly replicated proxy deployment
autosave
pushes to github every 5 minutes
theia server container
volume
shared PV between containers with student repo
\ No newline at end of file +
pod
student
proxy
proxy instance
proxy instance
proxy instance
theia
highly replicated proxy deployment
autosave
pushes to github every 5 minutes
theia server container
volume
shared PV between containers with student repo
\ No newline at end of file diff --git a/docs/render.sh b/docs/render.sh index cb70e3878..68fc3b6ce 100755 --- a/docs/render.sh +++ b/docs/render.sh @@ -9,6 +9,6 @@ if ! [ -d node_modules ]; then yarn fi ./render.sh -cd .. -exec pandoc design.md -s -o design.pdf -f markdown-implicit_figures +cd ../../ +exec pandoc README.md -s -o docs/design.pdf -f markdown-implicit_figures diff --git a/k8s/.gitignore b/k8s/.gitignore new file mode 100644 index 000000000..e44e39dc3 --- /dev/null +++ b/k8s/.gitignore @@ -0,0 +1 @@ +init-secrets.sh diff --git a/k8s/Makefile b/k8s/Makefile new file mode 100644 index 000000000..091cc7202 --- /dev/null +++ b/k8s/Makefile @@ -0,0 +1,42 @@ +VERSION := 7.12.0 +NAMESPACE := elastic + + +install: check elastic kibana filebeat + +check: + if ! helm repo list | grep elastic &> /dev/null; then \ + helm repo add https://helm.elastic.co; \ + helm repo update; \ + fi + +elastic: + helm upgrade \ + --install elasticsearch elastic/elasticsearch \ + --create-namespace \ + --set replicas=1 \ + --set resources=null \ + --set volumeClaimTemplate.resources.requests.storage=1Gi \ + --set imageTag=$(VERSION) \ + --namespace $(NAMESPACE) + +kibana: + helm upgrade \ + --install kibana elastic/kibana \ + --create-namespace \ + --set resources=null \ + --set imageTag=$(VERSION) \ + --namespace $(NAMESPACE) + +filebeat: + helm upgrade \ + --install filebeat elastic/filebeat \ + --values filebeat-values.yaml \ + --set daemonset.resources=null \ + --set imageTag=$(VERSION) \ + --namespace kube-system + + +proxy-kibana: + @echo '-> http://localhost:5601/' + kubectl port-forward svc/kibana-kibana "5601:5601" -n $(NAMESPACE) diff --git a/k8s/chart/templates/api.yml b/k8s/chart/templates/api.yml index 21f06b09f..78d55eca9 100644 --- a/k8s/chart/templates/api.yml +++ b/k8s/chart/templates/api.yml @@ -28,10 +28,6 @@ spec: component: api spec: # dnsPolicy: Default - {{- if .Values.api.datacenter }} - nodeSelector: - datacenter: {{ .Values.api.datacenter }} - {{- end }} containers: - name: api image: {{ .Values.api.image }}:{{ .Values.api.tag }} @@ -93,40 +89,6 @@ spec: {{- end }} --- -apiVersion: batch/v1 -kind: Job -metadata: - name: db-migrate - namespace: {{ .Release.Namespace }} - labels: - job: db-migrate - component: db-migrate - heritage: {{ .Release.Service | quote }} - release: {{ .Release.Name | quote }} -spec: - backoffLimit: 4 - ttlSecondsAfterFinished: 30 - template: - spec: - restartPolicy: Never - containers: - - name: db-migrate - image: {{ .Values.api.image }}:{{ .Values.api.tag }} - imagePullPolicy: {{ .Values.imagePullPolicy }} - command: ["alembic", "upgrade", "head"] - env: - # sqlalchemy uri - - name: "DATABASE_URI" - valueFrom: - secretKeyRef: - name: api - key: database-uri - - name: "REDIS_PASS" - valueFrom: - secretKeyRef: - name: api - key: redis-password ---- apiVersion: v1 kind: Service metadata: diff --git a/k8s/chart/templates/ingress.yml b/k8s/chart/templates/ingress.yml index 0b553ae9e..62b892294 100644 --- a/k8s/chart/templates/ingress.yml +++ b/k8s/chart/templates/ingress.yml @@ -16,33 +16,27 @@ spec: - "/api" --- -# Public Ingress Route /api/public/* apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: ingress.route.anubis.api.public namespace: {{ .Release.Namespace }} labels: - app.kubernetes.io/name: anubis - component: api + app: api heritage: {{ .Release.Service | quote }} release: {{ .Release.Name | quote }} spec: - {{- if .Values.debug }} + {{- if not .Values.debug }} entryPoints: - - web + - websecure {{- else }} entryPoints: - - websecure + - web {{- end }} routes: - kind: Rule match: Host(`{{ .Values.domain }}`) && PathPrefix(`/api`) middlewares: - {{- if .Values.vpnOnly }} - - name: whitelist-vpn - namespace: traefik - {{- end }} - name: strip-api namespace: {{ .Release.Namespace }} services: @@ -50,7 +44,7 @@ spec: port: 5000 {{- if not .Values.debug }} tls: - certResolver: tls + certResolver: le {{- end }} --- @@ -86,7 +80,7 @@ spec: port: 3000 {{- if not .Values.debug }} tls: - certResolver: tls + certResolver: le {{- end }} --- @@ -118,7 +112,5 @@ spec: port: 5000 {{- if not .Values.debug }} tls: - certResolver: tls + certResolver: le {{- end }} - - diff --git a/k8s/chart/templates/logstash.yml b/k8s/chart/templates/logstash.yml index 3d5f0598e..dedc44f40 100644 --- a/k8s/chart/templates/logstash.yml +++ b/k8s/chart/templates/logstash.yml @@ -1,8 +1,3 @@ -### ELK stack -# - Elasticsearch -# - Logstash -# - Kibana - ## Logstash apiVersion: apps/v1 kind: Deployment @@ -10,41 +5,56 @@ metadata: name: logstash namespace: {{ .Release.Namespace }} labels: - app.kubernetes.io/name: logstash + app: logstash heritage: {{ .Release.Service | quote }} release: {{ .Release.Name | quote }} - spec: selector: matchLabels: - app.kubernetes.io/name: logstash + app: logstash template: metadata: labels: - app.kubernetes.io/name: logstash + app: logstash ecosystem: elk spec: containers: - name: logstash - image: {{ .Values.logstash.image }}:{{ .Values.logstash.tag }} - imagePullPolicy: {{ .Values.imagePullPolicy }} + image: docker.elastic.co/logstash/logstash:7.8.0 + imagePullPolicy: Always ports: - name: web containerPort: 5000 - name: api containerPort: 9600 + volumeMounts: + - name: config + mountPath: "/usr/share/logstash/pipeline/" + readOnly: true + volumes: + # You set volumes at the Pod level, then mount them into containers inside that Pod + - name: config + configMap: + # Provide the name of the ConfigMap you want to mount. + name: logstash + # An array of keys from the ConfigMap to create as files + items: + - key: "logstash.conf" + path: "logstash.conf" --- apiVersion: v1 kind: Service metadata: name: logstash - namespace: anubis + namespace: {{ .Release.Namespace }} labels: - app.kubernetes.io/name: logstash + app: logstash + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} spec: selector: - app.kubernetes.io/name: logstash + app: logstash ports: - name: logstash protocol: UDP @@ -55,3 +65,26 @@ spec: targetPort: 9600 --- + + +apiVersion: v1 +kind: ConfigMap +metadata: + name: logstash + namespace: {{ .Release.Namespace }} + labels: + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} +data: + logstash.conf: | + input { + udp { + port => 5000 + codec => json + } + } + output { + elasticsearch { + hosts => ["{{ .Values.elasticsearch.service }}:9200"] + } + } diff --git a/k8s/chart/templates/migrate.yml b/k8s/chart/templates/migrate.yml new file mode 100644 index 000000000..2702d0ca7 --- /dev/null +++ b/k8s/chart/templates/migrate.yml @@ -0,0 +1,34 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: db-migrate-{{ .Release.Revision }} + namespace: {{ .Release.Namespace }} + labels: + job: db-migrate-{{ .Release.Revision }} + component: db-migrate + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} +spec: + backoffLimit: 4 + ttlSecondsAfterFinished: 30 + template: + spec: + restartPolicy: Never + containers: + - name: db-migrate + image: {{ .Values.api.image }}:{{ .Values.api.tag }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: ["alembic", "upgrade", "head"] + env: + # sqlalchemy uri + - name: "DATABASE_URI" + valueFrom: + secretKeyRef: + name: api + key: database-uri + - name: "REDIS_PASS" + valueFrom: + secretKeyRef: + name: api + key: redis-password diff --git a/k8s/chart/templates/rpc.yml b/k8s/chart/templates/rpc.yml index ce3af8e80..6f5a24def 100644 --- a/k8s/chart/templates/rpc.yml +++ b/k8s/chart/templates/rpc.yml @@ -7,7 +7,10 @@ metadata: component: pipeline-rpc heritage: {{ .Release.Service | quote }} release: {{ .Release.Name | quote }} - +{{- if .Values.imagePullSecret }} +imagePullSecrets: + - name: {{ .Values.imagePullSecret }} +{{- end }} --- apiVersion: apps/v1 diff --git a/k8s/chart/values.yaml b/k8s/chart/values.yaml index 1cb25f28a..deb061a08 100644 --- a/k8s/chart/values.yaml +++ b/k8s/chart/values.yaml @@ -4,20 +4,15 @@ healthChecks: true domain: "anubis.osiris.services" vpnOnly: false imagePullPolicy: "Always" +imagePullSecret: "anubis" api: - replicas: 3 - workers: 4 + replicas: 2 + workers: 2 datacenter: "mtc" gunicorn_options: "--capture-output --enable-stdio-inheritance --preload --timeout 30" - image: "registry.osiris.services/anubis/api" - tag: "latest" - -static: - replicas: 3 - image: "registry.osiris.services/anubis/static" + image: "registry.digitalocean.com/anubis/api" tag: "latest" - healthCheck: true reaper: enable: true @@ -30,30 +25,33 @@ visuals: suspend: false pipeline_api: - replicas: 3 - workers: 2 + replicas: 1 + workers: 1 web: replicas: 2 - image: "registry.osiris.services/anubis/web" + image: "registry.digitalocean.com/anubis/web" tag: "latest" logstash: - image: "registry.osiris.services/anubis/logstash" + image: "registry.digitalocean.com/anubis/logstash" tag: "latest" rpc: default: - replicas: 5 + replicas: 3 theia: - replicas: 5 + replicas: 3 regrade: - replicas: 5 + replicas: 3 theia: enable: true proxy: - replicas: 10 + replicas: 2 domain: "ide.anubis.osiris.services" - image: "registry.osiris.services/anubis/theia-proxy" + image: "registry.digitalocean.com/anubis/theia-proxy" tag: "latest" + +elasticsearch: + service: elasticsearch-master.elastic.svc.cluster.local diff --git a/k8s/debug/provision.sh b/k8s/debug/provision.sh index 7f59b796a..9bdaf6a9b 100755 --- a/k8s/debug/provision.sh +++ b/k8s/debug/provision.sh @@ -45,6 +45,7 @@ fi minikube start \ --feature-gates=TTLAfterFinished=true \ --ports=80:80,443:443 \ + --network-plugin=cni \ --cpus=${CPUS} \ --memory=${MEM} \ --cni=calico @@ -87,23 +88,22 @@ helm upgrade --install mariadb bitnami/mariadb \ --set 'replication.enabled=false' \ --namespace mariadb + # Install a minimal elasticsearch and kibana deployments -echo 'Adding elasticsearch + kibana' -kubectl create namespace anubis -helm upgrade --install elasticsearch bitnami/elasticsearch \ - --set name=elasticsearch \ - --set master.persistence.size=1Gi \ - --set data.persistence.size=1Gi \ - --set master.replicas=1 \ - --set coordinating.replicas=1 \ - --set data.replicas=1 \ - --set global.kibanaEnabled=true \ - --set fullnameOverride=elasticsearch \ - --set global.coordinating.name=coordinating \ - --namespace anubis +echo 'Adding elasticsearch' +kubectl create namespace elastic +helm upgrade \ + --install elasticsearch elastic/elasticsearch \ + --create-namespace \ + --set replicas=1 \ + --set resources=null \ + --set volumeClaimTemplate.resources.requests.storage=1Gi \ + --set imageTag=7.12.0 \ + --namespace elastic # Install a minimal redis deployment echo 'Adding redis' +kubectl create namespace anubis helm upgrade --install redis bitnami/redis \ --set fullnameOverride=redis \ --set global.redis.password=anubis \ diff --git a/k8s/debug/restart.sh b/k8s/debug/restart.sh index 77bdc7a53..ca9f01d5a 100755 --- a/k8s/debug/restart.sh +++ b/k8s/debug/restart.sh @@ -51,3 +51,6 @@ kubectl rollout restart deployments.apps/rpc-theia -n anubis kubectl rollout restart deployments.apps/rpc-regrade -n anubis kubectl rollout restart deployments.apps/pipeline-api -n anubis kubectl rollout restart deployments.apps/theia-proxy -n anubis + +cd .. +make startup-links diff --git a/k8s/deploy.sh b/k8s/deploy.sh index 4a183a971..ef37f0e81 100755 --- a/k8s/deploy.sh +++ b/k8s/deploy.sh @@ -4,58 +4,11 @@ set -e cd $(dirname $(realpath $0)) -kubectl config use-context space - - -if ! kubectl get namespace | grep anubis &> /dev/null; then - kubectl create namespace anubis -fi - - -if ! kubectl get secrets -n anubis | grep api &> /dev/null; then - read -s -p "Anubis DB Password: " DB_PASS - read -s -p "Anubis REDIS Password: " REDIS_PASS - kubectl create secret generic api \ - --from-literal=database-uri=mysql+pymysql://anubis:${DB_PASS}@mariadb.mariadb.svc.cluster.local/anubis \ - --from-literal=database-password=${DB_PASS} \ - --from-literal=redis-password=${REDIS_PASS} \ - --from-literal=secret-key=$(head -c10 /dev/urandom | openssl sha1 -hex | awk '{print $2}') \ - -n anubis -fi - - -if ! helm list -n anubis | awk '{print $1}' | grep elasticsearch &> /dev/null; then - # Install a minimal elasticsearch and kibana deployments - echo 'Adding elasticsearch + kibana' - helm upgrade --install elasticsearch bitnami/elasticsearch \ - --set name=elasticsearch \ - --set master.persistence.size=4Gi \ - --set data.persistence.size=4Gi \ - --set master.replicas=2 \ - --set coordinating.replicas=2 \ - --set data.replicas=2 \ - --set global.kibanaEnabled=true \ - --set fullnameOverride=elasticsearch \ - --set global.coordinating.name=coordinating \ - --namespace anubis - - read -s -p "Anubis REDIS Password: " REDIS_PASS - # Install a minimal redis deployment - echo 'Adding redis' - helm upgrade --install redis bitnami/redis \ - --set fullnameOverride=redis \ - --set global.redis.password=${REDIS_PASS} \ - --set architecture=standalone \ - --set master.persistence.enabled=false \ - --namespace anubis -fi - - pushd .. -#if ! docker image ls | awk '{print $1}' | grep -w '^registry.osiris.services/anubis/theia-admin$' &>/dev/null; then +#if ! docker image ls | awk '{print $1}' | grep -w '^registry.digitalocean.com/anubis/theia-admin$' &>/dev/null; then # EXTRA_BUILD="theia-admin" #fi -#if ! docker image ls | awk '{print $1}' | grep -w '^registry.osiris.services/anubis/theia-xv6$' &>/dev/null; then +#if ! docker image ls | awk '{print $1}' | grep -w '^registry.digitalocean.com/anubis/theia-xv6$' &>/dev/null; then # EXTRA_BUILD="${EXTRA_BUILD} theia-xv6" #fi docker-compose build --parallel --pull api web logstash theia-proxy theia-init theia-sidecar ${EXTRA_BUILD} diff --git a/k8s/prod/provision.sh b/k8s/prod/provision.sh new file mode 100755 index 000000000..c8c144651 --- /dev/null +++ b/k8s/prod/provision.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +# Change into the directory that this script is in +cd $(dirname $0) + +# Add the bitnami helm charts +helm repo add traefik https://helm.traefik.io/traefik +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo add elastic https://helm.elastic.co +helm repo add longhorn https://charts.longhorn.io +helm repo update + +kubectl create ns traefik +kubectl create ns longhorn-system +kubectl create ns elastic +kubectl create ns anubis + + +SECRET_KEY="$(head -c10 /dev/urandom | openssl sha1 -hex | awk '{print $2}')" +DB_PASS="$(head -c10 /dev/urandom | openssl sha1 -hex | awk '{print $2}')" +REDIS_PASS="$(head -c10 /dev/urandom | openssl sha1 -hex | awk '{print $2}')" + +echo 'Adding traefik' +helm upgrade \ + --install traefik traefik/traefik \ + --namespace traefik \ + --create-namespace \ + --values traefik-values.yaml + +echo 'Adding longhorn' +helm install longhorn/longhorn \ + --name longhorn \ + --namespace longhorn-system + +# Create a minimal mariadb deployment in a mariadb namespace. On +# prod, the mariadb is in a seperate namespace, so we do the same +# here. +echo 'Adding mariadb' +kubectl create namespace mariadb +helm upgrade --install mariadb bitnami/mariadb \ + --set 'auth.rootPassword=anubis' \ + --set 'volumePermissions.enabled=true' \ + --set 'auth.username=anubis' \ + --set 'auth.database=anubis' \ + --set "auth.password=${DB_PASS}" \ + --set 'replication.enabled=false' \ + --namespace mariadb + +# Install a minimal elasticsearch and kibana deployments +echo 'Adding elasticsearch' +kubectl create namespace anubis +helm upgrade \ + --install elasticsearch elastic/elasticsearch \ + --create-namespace \ + --set replicas=1 \ + --set resources=null \ + --set volumeClaimTemplate.resources.requests.storage=1Gi \ + --set imageTag=7.12.0 \ + --namespace elastic + +# Install a minimal redis deployment +echo 'Adding redis' +helm upgrade --install redis bitnami/redis \ + --set fullnameOverride=redis \ + --set global.redis.password=${REDIS_PASS} \ + --set architecture=standalone \ + --set master.persistence.enabled=false \ + --namespace anubis + +# Create api secret +kubectl create secret generic api -n anubis \ + --from-literal=database-uri=mysql+pymysql://anubis:${DB_PASS}@mariadb.mariadb.svc.cluster.local/anubis \ + --from-literal=database-password=${DB_PASS} \ + --from-literal=redis-password=${REDIS_PASS} \ + --from-literal=secret-key=${SECRET_KEY} + + +if [ -f ./init-secrets.sh ]; then + bash ./init-secrets.sh +fi + +cd ../../ +docker-compose build --parallel --pull +docker-compose push api web theia-init theia-proxy theia-admin theia-xv6 diff --git a/k8s/prod/traefik-values.yaml b/k8s/prod/traefik-values.yaml new file mode 100644 index 000000000..aee2895d4 --- /dev/null +++ b/k8s/prod/traefik-values.yaml @@ -0,0 +1,242 @@ +globalArguments: [] +additionalArguments: + - "--log.level=INFO" + - "--accesslog" + - "--accesslog.filepath=/data/access.log" + - "--accesslog.format=json" + - "--providers.kubernetescrd=true" + - "--certificatesresolvers.le.acme.tlschallenge=true" + - "--certificatesresolvers.le.acme.email=osiris@osiris.cyber.nyu.edu" + - "--certificatesresolvers.le.acme.storage=/data/acme.json" + +# Use ingressClass. Ignored if Traefik version < 2.3 / kubernetes < 1.18.x +ingressClass: + # true is not unit-testable yet, pending https://github.com/rancher/helm-unittest/pull/12 + enabled: false + isDefaultClass: false + +# Create an IngressRoute for the dashboard +ingressRoute: + dashboard: + enabled: false + # Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) + annotations: {} + # Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) + labels: {} + +rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + +# +# Configure providers +# +providers: + kubernetesCRD: + enabled: true + namespaces: [] + # - "default" + kubernetesIngress: + enabled: false + namespaces: [] + # - "default" + # IP used for Kubernetes Ingress endpoints + publishedService: + enabled: false + # Published Kubernetes Service to copy status from. Format: namespace/servicename + # By default this Traefik service + # pathOverride: "" + +deployment: + enabled: true + # Can be either Deployment or DaemonSet + kind: Deployment + # Number of pods of the deployment (only applies when kind == Deployment) + replicas: 1 + # Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} + # Additional Pod labels (e.g. for filtering Pod by custom labels) + podLabels: {} + # Additional containers (e.g. for metric offloading sidecars) + additionalContainers: [] + # https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host + # - name: socat-proxy + # image: alpine/socat:1.0.5 + # args: ["-s", "-u", "udp-recv:8125", "unix-sendto:/socket/socket"] + # volumeMounts: + # - name: dsdsocket + # mountPath: /socket + # Additional volumes available for use with initContainers and additionalContainers + additionalVolumes: [] + # - name: dsdsocket + # hostPath: + # path: /var/run/statsd-exporter + # Additional initContainers (e.g. for setting file permission as shown below) + initContainers: + # The "volume-permissions" init container is required if you run into permission issues. + # Related issue: https://github.com/traefik/traefik/issues/6972 + - name: volume-permissions + image: busybox:1.31.1 + command: ["sh", "-c", "chmod -Rv 600 /data/*"] + volumeMounts: + - name: data + mountPath: /data + +persistence: + enabled: true + name: data + # existingClaim: "" + accessMode: ReadWriteOnce + size: 1Gi + # storageClass: "" + path: /data + annotations: {} + # subPath: "" # only mount a subpath of the Volume into the pod + + +ports: + # The name of this one can't be changed as it is used for the readiness and + # liveness probes, but you can adjust its config to your liking + traefik: + port: 9000 + # Use hostPort if set. + # hostPort: 9000 + # + # Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which + # means it's listening on all your interfaces and all your IPs. You may want + # to set this value if you need traefik to listen on specific interface + # only. + # hostIP: 192.168.100.10 + + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # + # You SHOULD NOT expose the traefik port on production deployments. + # If you want to access it from outside of your cluster, + # use `kubectl port-forward` or create a secure ingress + expose: false + # The exposed port for this service + exposedPort: 9000 + # The port protocol (TCP/UDP) + protocol: TCP + web: + port: 8000 + # hostPort: 8000 + expose: true + exposedPort: 80 + # The port protocol (TCP/UDP) + protocol: TCP + # Use nodeport if set. This is useful if you have configured Traefik in a + # LoadBalancer + # nodePort: 32080 + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection + redirectTo: websecure + websecure: + port: 8443 + # hostPort: 8443 + expose: true + exposedPort: 443 + # The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 + # Set TLS at the entrypoint + # https://doc.traefik.io/traefik/routing/entrypoints/#tls + tls: + enabled: true + # this is the name of a TLSOption definition + options: "" + certResolver: "le" + domains: + - main: anubis.osiris.services + sans: + - ide.anubis.osiris.services + +podSecurityContext: null +securityContext: null + +# +# Add volumes to the traefik pod. The volume name will be passed to tpl. +# This can be used to mount a cert pair or a configmap that holds a config.toml file. +# After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: +# additionalArguments: +# - "--providers.file.filename=/config/dynamic.toml" +volumes: [] +# - name: public-cert +# mountPath: "/certs" +# type: secret +# - name: '{{ printf "%s-configs" .Release.Name }}' +# mountPath: "/config" +# type: configMap + +# Additional volumeMounts to add to the Traefik container +additionalVolumeMounts: [] + # For instance when using a logshipper for access logs + # - name: traefik-logs + # mountPath: /var/log/traefik + +# Logs +# https://docs.traefik.io/observability/logs/ +logs: + # Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on). + general: + # By default, the logs use a text format (common), but you can + # also ask for the json format in the format option + # format: json + # By default, the level is set to ERROR. Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. + level: ERROR + access: + # To enable access logs + enabled: false + # By default, logs are written using the Common Log Format (CLF). + # To write logs in JSON, use json in the format option. + # If the given format is unsupported, the default (CLF) is used instead. + # format: json + # To write the logs in an asynchronous fashion, specify a bufferingSize option. + # This option represents the number of log lines Traefik will keep in memory before writing + # them to the selected output. In some cases, this option can greatly help performances. + # bufferingSize: 100 + # Filtering https://docs.traefik.io/observability/access-logs/#filtering + filters: {} + # statuscodes: "200,300-302" + # retryattempts: true + # minduration: 10ms + # Fields + # https://docs.traefik.io/observability/access-logs/#limiting-the-fieldsincluding-headers + fields: + general: + defaultmode: keep + names: {} + # Examples: + # ClientUsername: drop + headers: + defaultmode: drop + names: {} + # Examples: + # User-Agent: redact + # Authorization: drop + # Content-Type: keep + +# Environment variables to be passed to Traefik's binary +env: [] +# - name: SOME_VAR +# value: some-var-value +# - name: SOME_VAR_FROM_CONFIG_MAP +# valueFrom: +# configMapRef: +# name: configmap-name +# key: config-key +# - name: SOME_SECRET +# valueFrom: +# secretKeyRef: +# name: secret-name +# key: secret-key + +envFrom: [] +# - configMapRef: +# name: config-map-name +# - secretRef: +# name: secret-name diff --git a/theia/ide/admin/Dockerfile b/theia/ide/admin/Dockerfile index f09920595..7012321ab 100644 --- a/theia/ide/admin/Dockerfile +++ b/theia/ide/admin/Dockerfile @@ -33,7 +33,7 @@ RUN set -eux; apt-get update \ | tee /etc/apt/sources.list.d/docker.list > /dev/null \ && apt-get update \ && apt-get install -y --no-install-recommends \ - make build-essential libssl-dev wget curl llvm htop \ + make build-essential libssl-dev wget curl llvm htop zsh \ docker-ce docker-ce-cli containerd.io; \ rm -rf /var/lib/apt/lists/* \ && savedAptMark="$(apt-mark showmanual)" \ @@ -68,11 +68,21 @@ RUN set -eux; apt-get update \ && rm get-pip.py; \ pip3 install --upgrade --no-cache-dir pip \ && pip3 install --upgrade --no-cache-dir python-language-server flake8 autopep8 pylint supervisor \ - && rm -rf /tmp/* \ + && adduser --disabled-password --gecos '' --uid 1001 theia; \ + cd /home/theia; \ + git clone https://github.com/ohmyzsh/ohmyzsh.git .oh-my-zsh; \ + git clone https://github.com/zsh-users/zsh-syntax-highlighting.git \ + .oh-my-zsh/custom/plugins/zsh-syntax-highlighting; \ + cp .oh-my-zsh/templates/zshrc.zsh-template .zshrc; \ + sed -i 's/ZSH_THEME="robbyrussell"/ZSH_THEME="gnzh"/' .zshrc; \ + sed -i 's/plugins=(git)/plugins=(git zsh-syntax-highlighting)/' .zshrc; \ + mkdir -p /home/theia/.theia; \ + echo '{"terminal.integrated.shell.linux": "/usr/bin/zsh"}' \ + > /home/theia/.theia/settings.json; \ + rm -rf /tmp/* \ && rm -rf /usr/share/doc \ && rm -rf /root/.cache \ && rm -rf /home/theia/node_modules/.cache \ - && adduser --disabled-password --gecos '' --uid 1001 theia \ && mkdir -p /home/project \ && chmod g+rw /home \ && mkdir -p /home/project \ @@ -94,7 +104,10 @@ RUN set -eux; apt-get update \ | xargs -r apt-mark manual \ && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ && rm -rf /var/cache/apt/* \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/*; \ + find / -depth \ + \( -name .cache -o -name __pycache__ -o -name *.pyc -o -name *.a -o -name .git -o -name .github \) \ + -exec rm -rf {} + COPY --from=0 /home/theia /home/theia COPY cli /cli @@ -105,6 +118,7 @@ RUN pip3 install --no-cache-dir /cli \ \( -type d -a \( -name test -o -name tests -o -name idle_test -o -name __pycache__ \) \) \ -o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name '*.a' \) \) \ \) -exec rm -rf '{}' + -COPY supervisord.conf initialize-incluster.py / +COPY supervisord.conf initialize-incluster.py autosave-dump.sh / +COPY autosave /usr/local/bin/autosave ENTRYPOINT ["supervisord", "--nodaemon", "-c", "/supervisord.conf"] diff --git a/theia/ide/admin/autosave b/theia/ide/admin/autosave new file mode 100755 index 000000000..04f00e924 --- /dev/null +++ b/theia/ide/admin/autosave @@ -0,0 +1,10 @@ +#!/bin/bash + + +if git status | grep 'nothing to commit' &> /dev/null; then + echo 'Nothing to autosave just yet. Make a change you would like to save!' + exit 1 +fi + +exec curl http://localhost:5001/ \ + --data "message=${@}" diff --git a/theia/ide/admin/autosave-dump.sh b/theia/ide/admin/autosave-dump.sh new file mode 100755 index 000000000..3385318c3 --- /dev/null +++ b/theia/ide/admin/autosave-dump.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo ${AUTOSAVE} > /tmp/AUTOSAVE diff --git a/theia/ide/admin/cli/.editorconfig b/theia/ide/admin/cli/.editorconfig deleted file mode 100644 index d4a2c4405..000000000 --- a/theia/ide/admin/cli/.editorconfig +++ /dev/null @@ -1,21 +0,0 @@ -# http://editorconfig.org - -root = true - -[*] -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true -insert_final_newline = true -charset = utf-8 -end_of_line = lf - -[*.bat] -indent_style = tab -end_of_line = crlf - -[LICENSE] -insert_final_newline = false - -[Makefile] -indent_style = tab diff --git a/theia/ide/admin/cli/Makefile b/theia/ide/admin/cli/Makefile deleted file mode 100644 index 859ebb948..000000000 --- a/theia/ide/admin/cli/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -.PHONY: clean clean-test clean-pyc clean-build docs help -.DEFAULT_GOAL := help - -define BROWSER_PYSCRIPT -import os, webbrowser, sys - -from urllib.request import pathname2url - -webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) -endef -export BROWSER_PYSCRIPT - -define PRINT_HELP_PYSCRIPT -import re, sys - -for line in sys.stdin: - match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) - if match: - target, help = match.groups() - print("%-20s %s" % (target, help)) -endef -export PRINT_HELP_PYSCRIPT - -BROWSER := python -c "$$BROWSER_PYSCRIPT" - -help: - @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) - -clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts - -clean-build: ## remove build artifacts - rm -fr build/ - rm -fr dist/ - rm -fr .eggs/ - find . -name '*.egg-info' -exec rm -fr {} + - find . -name '*.egg' -exec rm -f {} + - -clean-pyc: ## remove Python file artifacts - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - find . -name '__pycache__' -exec rm -fr {} + - -clean-test: ## remove test and coverage artifacts - rm -fr .tox/ - rm -f .coverage - rm -fr htmlcov/ - rm -fr .pytest_cache - -lint: ## check style with flake8 - flake8 anubis tests - -test: ## run tests quickly with the default Python - python setup.py test - -test-all: ## run tests on every Python version with tox - tox - -coverage: ## check code coverage quickly with the default Python - coverage run --source anubis setup.py test - coverage report -m - coverage html - $(BROWSER) htmlcov/index.html - -docs: ## generate Sphinx HTML documentation, including API docs - rm -f docs/anubis.rst - rm -f docs/modules.rst - sphinx-apidoc -o docs/ anubis - $(MAKE) -C docs clean - $(MAKE) -C docs html - $(BROWSER) docs/_build/html/index.html - -servedocs: docs ## compile the docs watching for changes - watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . - -release: dist ## package and upload a release - twine upload dist/* - -dist: clean ## builds source and wheel package - python setup.py sdist - python setup.py bdist_wheel - ls -l dist - -install: clean ## install the package to the active Python's site-packages - python setup.py install - -venv: - virtualenv -p $(shell which python3) venv - ./venv/bin/pip install -r ./requirements.txt diff --git a/theia/ide/admin/cli/README.rst b/theia/ide/admin/cli/README.rst deleted file mode 100644 index 35cddf9fc..000000000 --- a/theia/ide/admin/cli/README.rst +++ /dev/null @@ -1,25 +0,0 @@ -========== -Anubis-cli -========== - - - - - - -CLI component to the Anubis autograder. - - - -Features --------- - -* TODO - -Credits -------- - -This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template. - -.. _Cookiecutter: https://github.com/audreyr/cookiecutter -.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage diff --git a/theia/ide/admin/cli/anubis/__init__.py b/theia/ide/admin/cli/anubis/__init__.py index 7627c872d..a06269ae6 100644 --- a/theia/ide/admin/cli/anubis/__init__.py +++ b/theia/ide/admin/cli/anubis/__init__.py @@ -1,5 +1,4 @@ """Top-level package for Anubis-cli.""" __author__ = """John McCann Cunniff Jr.""" -__email__ = 'johncunniff1248@gmail.com' -__version__ = 'v2.0.0' +__version__ = 'v3.0.0' diff --git a/theia/ide/admin/cli/anubis/assignment/meta.yml b/theia/ide/admin/cli/anubis/assignment/meta.yml index 9ec082359..bc0901706 100644 --- a/theia/ide/admin/cli/anubis/assignment/meta.yml +++ b/theia/ide/admin/cli/anubis/assignment/meta.yml @@ -1,16 +1,23 @@ assignment: name: "{name}" - class: "CS-UY 3224" + class: "{course_code}" hidden: true + # Optionally specify the github classroom link # for students to get their repo. + # + # !! Remember to set the Custom repository + # !! prefix to {name}-{unique_code} + # !! when creating the assignment on + # !! Github Classroom github_classroom_url: "" # Don't change these! - unique_code: "{code}" - pipeline_image: "registry.osiris.services/anubis/assignment/{code}" + unique_code: "{unique_code}" + pipeline_image: "registry.digitalocean.com/anubis/assignment/{unique_code}" - # Specify the importaint dates here + # Specify the important dates here + # * Remember! These are interpreted as America/New_York * date: release: "{now}" due: "{week_from_now}" @@ -19,20 +26,3 @@ assignment: # This description will be shown to the user on the Anubis website. description: | This is a very long description that encompases the entire assignment - - # Uncomment this section if you want to have question pools - # These questions are purely additive. To delete a question, you will - # need to manually edit the database. This is by design to prevent - # a question assigned to a student from being changed to prevent - # confusion. - questions: [] - # - pool: 1 - # questions: - # - q: "What is 3*4?" - # a: "12" - # - q: "What is 3*2" - # a: "6" - # - pool: 2 - # questions: - # - q: "What is sqrt(144)?" - # a: "12" diff --git a/theia/ide/admin/cli/anubis/assignment/test.sh b/theia/ide/admin/cli/anubis/assignment/test.sh index aac19d147..4ffba3831 100755 --- a/theia/ide/admin/cli/anubis/assignment/test.sh +++ b/theia/ide/admin/cli/anubis/assignment/test.sh @@ -19,5 +19,5 @@ sed -i 's/^COPY student \/student/# COPY student \/student/' Dockerfile docker run -it \ -e DEBUG=1 \ - registry.osiris.services/anubis/assignment/{unique_code} $@ + registry.digitalocean.com/anubis/assignment/{unique_code} $@ diff --git a/theia/ide/admin/cli/anubis/cli.py b/theia/ide/admin/cli/anubis/cli.py index dc190d54b..a81d5b39f 100644 --- a/theia/ide/admin/cli/anubis/cli.py +++ b/theia/ide/admin/cli/anubis/cli.py @@ -16,6 +16,8 @@ INCLUSTER = False API_URL = 'https://anubis.osiris.services/api' +COURSE_ID = os.environ.get('COURSE_ID', None) +COURSE_CODE = os.environ.get('COURSE_CODE', 'CS-UY 3224') conf_dir = os.path.join(os.environ.get("HOME"), ".anubis") conf_file = os.path.join(os.environ.get("HOME"), ".anubis/config.json") assignment_base = None @@ -182,16 +184,6 @@ def whoami(): click.echo(json.dumps(r.json(), indent=2)) -@questions.command() -def assign(): - if not os.path.exists('meta.yml'): - click.echo('no meta.yml found!') - return 1 - assignment_meta = yaml.safe_load(open('meta.yml').read()) - unique_code = assignment_meta['assignment']['unique_code'] - get_json('/private/questions/assign/{}'.format(unique_code)) - - @assignment.command() def sync(): if not os.path.exists('meta.yml'): @@ -230,9 +222,10 @@ def init(assignment_name): meta_path = os.path.join(safe_assignment_name, 'meta.yml') meta = open(meta_path).read().format( name=os.path.basename(safe_assignment_name), - code=unique_code, + unique_code=unique_code, now=now.strftime('%F %T'), - week_from_now=week_from_now.strftime('%F %T') + week_from_now=week_from_now.strftime('%F %T'), + course_code=COURSE_CODE, ) with open(meta_path, 'w') as f: f.write(meta) diff --git a/theia/ide/admin/cli/docs/Makefile b/theia/ide/admin/cli/docs/Makefile deleted file mode 100644 index 2e32e3825..000000000 --- a/theia/ide/admin/cli/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = python -msphinx -SPHINXPROJ = anubis -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/theia/ide/admin/cli/docs/authors.rst b/theia/ide/admin/cli/docs/authors.rst deleted file mode 100644 index e122f914a..000000000 --- a/theia/ide/admin/cli/docs/authors.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../AUTHORS.rst diff --git a/theia/ide/admin/cli/docs/conf.py b/theia/ide/admin/cli/docs/conf.py deleted file mode 100755 index a4277bde4..000000000 --- a/theia/ide/admin/cli/docs/conf.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python -# -# anubis documentation build configuration file, created by -# sphinx-quickstart on Fri Jun 9 13:47:02 2017. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another -# directory, add these directories to sys.path here. If the directory is -# relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. -# -import os -import sys -sys.path.insert(0, os.path.abspath('..')) - -import anubis - -# -- General configuration --------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'Anubis-cli' -copyright = "2020, John McCann Cunniff Jr." -author = "John McCann Cunniff Jr." - -# The version info for the project you're documenting, acts as replacement -# for |version| and |release|, also used in various other places throughout -# the built documents. -# -# The short X.Y version. -version = anubis.__version__ -# The full version, including alpha/beta/rc tags. -release = anubis.__version__ - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, `` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'alabaster' - -# Theme options are theme-specific and customize the look and feel of a -# theme further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - - -# -- Options for HTMLHelp output --------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'anubisdoc' - - -# -- Options for LaTeX output ------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'anubis.tex', - 'Anubis-cli Documentation', - 'John McCann Cunniff Jr.', 'manual'), -] - - -# -- Options for manual page output ------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'anubis', - 'Anubis-cli Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'anubis', - 'Anubis-cli Documentation', - author, - 'anubis', - 'One line description of project.', - 'Miscellaneous'), -] - - - diff --git a/theia/ide/admin/cli/docs/contributing.rst b/theia/ide/admin/cli/docs/contributing.rst deleted file mode 100644 index e582053ea..000000000 --- a/theia/ide/admin/cli/docs/contributing.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CONTRIBUTING.rst diff --git a/theia/ide/admin/cli/docs/history.rst b/theia/ide/admin/cli/docs/history.rst deleted file mode 100644 index 250649964..000000000 --- a/theia/ide/admin/cli/docs/history.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../HISTORY.rst diff --git a/theia/ide/admin/cli/docs/index.rst b/theia/ide/admin/cli/docs/index.rst deleted file mode 100644 index 0e4a824c9..000000000 --- a/theia/ide/admin/cli/docs/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -Welcome to Anubis-cli's documentation! -====================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - readme - installation - usage - modules - contributing - authors - history - -Indices and tables -================== -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/theia/ide/admin/cli/docs/installation.rst b/theia/ide/admin/cli/docs/installation.rst deleted file mode 100644 index a5b8f806a..000000000 --- a/theia/ide/admin/cli/docs/installation.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. highlight:: shell - -============ -Installation -============ - - -Stable release --------------- - -To install Anubis-cli, run this command in your terminal: - -.. code-block:: console - - $ pip install anubis - -This is the preferred method to install Anubis-cli, as it will always install the most recent stable release. - -If you don't have `pip`_ installed, this `Python installation guide`_ can guide -you through the process. - -.. _pip: https://pip.pypa.io -.. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ - - -From sources ------------- - -The sources for Anubis-cli can be downloaded from the `Github repo`_. - -You can either clone the public repository: - -.. code-block:: console - - $ git clone git://github.com/juan-punchman/anubis - -Or download the `tarball`_: - -.. code-block:: console - - $ curl -OJL https://github.com/juan-punchman/anubis/tarball/master - -Once you have a copy of the source, you can install it with: - -.. code-block:: console - - $ python setup.py install - - -.. _Github repo: https://github.com/juan-punchman/anubis -.. _tarball: https://github.com/juan-punchman/anubis/tarball/master diff --git a/theia/ide/admin/cli/docs/make.bat b/theia/ide/admin/cli/docs/make.bat deleted file mode 100644 index 221b250bb..000000000 --- a/theia/ide/admin/cli/docs/make.bat +++ /dev/null @@ -1,36 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=python -msphinx -) -set SOURCEDIR=. -set BUILDDIR=_build -set SPHINXPROJ=anubis - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The Sphinx module was not found. Make sure you have Sphinx installed, - echo.then set the SPHINXBUILD environment variable to point to the full - echo.path of the 'sphinx-build' executable. Alternatively you may add the - echo.Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/theia/ide/admin/cli/docs/readme.rst b/theia/ide/admin/cli/docs/readme.rst deleted file mode 100644 index 72a335581..000000000 --- a/theia/ide/admin/cli/docs/readme.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../README.rst diff --git a/theia/ide/admin/cli/docs/usage.rst b/theia/ide/admin/cli/docs/usage.rst deleted file mode 100644 index 8fa523d3d..000000000 --- a/theia/ide/admin/cli/docs/usage.rst +++ /dev/null @@ -1,7 +0,0 @@ -===== -Usage -===== - -To use Anubis-cli in a project:: - - import anubis diff --git a/theia/ide/admin/cli/requirements.txt b/theia/ide/admin/cli/requirements.txt index ad9f8a6d5..ed1572716 100644 --- a/theia/ide/admin/cli/requirements.txt +++ b/theia/ide/admin/cli/requirements.txt @@ -1,4 +1,3 @@ Click>=7.0 requests pyyaml -docker diff --git a/theia/ide/admin/cli/requirements_dev.txt b/theia/ide/admin/cli/requirements_dev.txt deleted file mode 100644 index 3439c4570..000000000 --- a/theia/ide/admin/cli/requirements_dev.txt +++ /dev/null @@ -1,10 +0,0 @@ -pip==19.2.3 -bump2version==0.5.11 -wheel==0.33.6 -watchdog==0.9.0 -flake8==3.7.8 -tox==3.14.0 -coverage==4.5.4 -Sphinx==1.8.5 -twine==1.14.0 -Click==7.0 diff --git a/theia/ide/admin/cli/setup.cfg b/theia/ide/admin/cli/setup.cfg deleted file mode 100644 index ffddc29a2..000000000 --- a/theia/ide/admin/cli/setup.cfg +++ /dev/null @@ -1,22 +0,0 @@ -[bumpversion] -current_version = v2.0.0 -commit = True -tag = True - -[bumpversion:file:setup.py] -search = version='{current_version}' -replace = version='{new_version}' - -[bumpversion:file:anubis/__init__.py] -search = __version__ = '{current_version}' -replace = __version__ = '{new_version}' - -[bdist_wheel] -universal = 1 - -[flake8] -exclude = docs - -[aliases] -# Define setup.py command aliases here - diff --git a/theia/ide/admin/cli/setup.py b/theia/ide/admin/cli/setup.py index bc3ef147b..8c510aa6f 100644 --- a/theia/ide/admin/cli/setup.py +++ b/theia/ide/admin/cli/setup.py @@ -4,18 +4,13 @@ from setuptools import setup, find_packages -with open('README.rst') as readme_file: - readme = readme_file.read() +requirements = ['Click>=7.0', 'requests', 'pyyaml'] -requirements = ['Click>=7.0', 'requests','pyyaml','docker'] - -setup_requirements = [ ] - -test_requirements = [ ] +setup_requirements = [] +test_requirements = [] setup( author="John McCann Cunniff Jr.", - author_email='johncunniff1248@gmail.com', python_requires='>=3.5', classifiers=[ 'Development Status :: 2 - Pre-Alpha', @@ -34,7 +29,6 @@ ], }, install_requires=requirements, - long_description=readme + '\n', include_package_data=True, keywords='anubis', name='anubis', diff --git a/theia/ide/admin/cli/tests/__init__.py b/theia/ide/admin/cli/tests/__init__.py deleted file mode 100644 index 1a85f511c..000000000 --- a/theia/ide/admin/cli/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Unit test package for anubis.""" diff --git a/theia/ide/admin/cli/tests/test_anubis.py b/theia/ide/admin/cli/tests/test_anubis.py deleted file mode 100644 index 49ec8fdd2..000000000 --- a/theia/ide/admin/cli/tests/test_anubis.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -"""Tests for `anubis` package.""" - - -import unittest -from click.testing import CliRunner - -from anubis import anubis -from anubis import cli - - -class TestAnubis(unittest.TestCase): - """Tests for `anubis` package.""" - - def setUp(self): - """Set up test fixtures, if any.""" - - def tearDown(self): - """Tear down test fixtures, if any.""" - - def test_000_something(self): - """Test something.""" - - def test_command_line_interface(self): - """Test the CLI.""" - runner = CliRunner() - result = runner.invoke(cli.main) - assert result.exit_code == 0 - assert 'anubis.cli.main' in result.output - help_result = runner.invoke(cli.main, ['--help']) - assert help_result.exit_code == 0 - assert '--help Show this message and exit.' in help_result.output diff --git a/theia/ide/admin/cli/tox.ini b/theia/ide/admin/cli/tox.ini deleted file mode 100644 index 4b1f52142..000000000 --- a/theia/ide/admin/cli/tox.ini +++ /dev/null @@ -1,20 +0,0 @@ -[tox] -envlist = py35, py36, py37, py38, flake8 - -[travis] -python = - 3.8: py38 - 3.7: py37 - 3.6: py36 - 3.5: py35 - -[testenv:flake8] -basepython = python -deps = flake8 -commands = flake8 anubis tests - -[testenv] -setenv = - PYTHONPATH = {toxinidir} - -commands = python setup.py test diff --git a/theia/ide/admin/supervisord.conf b/theia/ide/admin/supervisord.conf index 872c2f655..81994b6c1 100644 --- a/theia/ide/admin/supervisord.conf +++ b/theia/ide/admin/supervisord.conf @@ -23,3 +23,11 @@ command=/initialize-incluster.py autorestart=false stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 + +[program:autosave-dump] +directory=/ +user=theia +command=/autosave-dump.sh +autorestart=false +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 diff --git a/theia/ide/xv6/Dockerfile b/theia/ide/xv6/Dockerfile index 707b0651f..6c828c2ed 100644 --- a/theia/ide/xv6/Dockerfile +++ b/theia/ide/xv6/Dockerfile @@ -1,100 +1,12 @@ # https://github.com/theia-ide/theia-apps/tree/master/theia-cpp-docker -FROM ubuntu:20.04 as common - ARG NODE_VERSION=12.18.3 -ENV NODE_VERSION=$NODE_VERSION -ENV DEBIAN_FRONTEND=noninteractive - -# User account -RUN adduser --disabled-password --gecos '' --uid 1001 theia - -# Common deps -RUN apt-get update && \ - apt-get -y install build-essential \ - curl \ - git \ - gpg \ - python \ - wget \ - xz-utils && \ - rm -rf /var/lib/apt/lists/* - -# Install Node.js -# From: https://github.com/nodejs/docker-node/blob/6b8d86d6ad59e0d1e7a94cec2e909cad137a028f/8/Dockerfile - -# gpg keys listed at https://github.com/nodejs/node#release-keys -RUN set -ex \ - && for key in \ - 4ED778F539E3634C779C87C6D7062848A1AB005C \ - B9E2F5981AA6E0CD28160D9FF13993A75599653C \ - 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ - B9AE9905FFD7803F25714661B63B535A4C206CA9 \ - 77984A986EBC2AA786BC0F66B01FBB92821C587A \ - 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ - FD3A5288F042B6850C66B31F09FE44734EB7990E \ - 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \ - C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ - DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ - A48C2BEE680E841632CD4E44F07496B3EB3C1762 \ - ; do \ - gpg --batch --keyserver ipv4.pool.sks-keyservers.net --recv-keys "$key" || \ - gpg --batch --keyserver pool.sks-keyservers.net --recv-keys "$key" || \ - gpg --batch --keyserver pgp.mit.edu --recv-keys "$key" || \ - gpg --batch --keyserver keyserver.pgp.com --recv-keys "$key" || \ - gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" ; \ - done - -RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" \ - && case "${dpkgArch##*-}" in \ - amd64) ARCH='x64';; \ - ppc64el) ARCH='ppc64le';; \ - s390x) ARCH='s390x';; \ - arm64) ARCH='arm64';; \ - armhf) ARCH='armv7l';; \ - i386) ARCH='x86';; \ - *) echo "unsupported architecture"; exit 1 ;; \ - esac \ - && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \ - && curl -SLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ - && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ - && grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ - && tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \ - && rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \ - && ln -s /usr/local/bin/node /usr/local/bin/nodejs - -FROM common as theia - -# Use "latest" or "next" version for Theia packages -# Optionally build a striped Theia application with no map file or .ts sources. -# Makes image ~150MB smaller when enabled -ARG strip=true - -ENV DEBIAN_FRONTEND=noninteractive \ - YARN_VERSION=1.22.5 \ - strip=trye -# Install Yarn -# From: https://github.com/nodejs/docker-node/blob/6b8d86d6ad59e0d1e7a94cec2e909cad137a028f/8/Dockerfile +FROM node:${NODE_VERSION}-buster as theia -RUN set -ex \ - && for key in \ - 6A010C5166006599AA17F08146C2130DFD2497F5 \ - ; do \ - gpg --batch --keyserver ipv4.pool.sks-keyservers.net --recv-keys "$key" || \ - gpg --batch --keyserver pool.sks-keyservers.net --recv-keys "$key" || \ - gpg --batch --keyserver pgp.mit.edu --recv-keys "$key" || \ - gpg --batch --keyserver keyserver.pgp.com --recv-keys "$key" || \ - gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" ; \ - done \ - && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ - && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \ - && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \ - && mkdir -p /opt/yarn \ - && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/yarn --strip-components=1 \ - && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \ - && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarnpkg \ - && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz +ENV NODE_VERSION=$NODE_VERSION +ENV TERM=xterm-256color +ENV DEBIAN_FRONTEND=noninteractive # Theia application @@ -102,61 +14,65 @@ WORKDIR /home/theia COPY latest.package.json package.json -RUN yarn --pure-lockfile && \ - NODE_OPTIONS="--max_old_space_size=4096" yarn theia build && \ - yarn theia download:plugins && \ - yarn --production && \ - yarn autoclean --init && \ - echo *.ts >> .yarnclean && \ - echo *.ts.map >> .yarnclean && \ - echo *.spec.* >> .yarnclean && \ - yarn autoclean --force && \ - yarn cache clean - -FROM common - -ARG LLVM=10 -ARG CMAKE_VERSION=3.18.1 -# C/C++ Developer tools - -# Install clangd and clang-tidy from the public LLVM PPA (nightly build / development version) -# And also the GDB debugger from the Ubuntu repos -RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ - echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" > /etc/apt/sources.list.d/llvm.list && \ - apt-get update && \ - apt-get install -y \ - gcc-multilib \ - g++-multilib \ - python3 python3-pip \ - clangd-$LLVM \ - gdb \ - qemu-system-i386 && \ - pip3 install supervisor && \ - rm -rf /var/lib/apt/lists/* && \ - ln -s /usr/bin/clangd-$LLVM /usr/bin/clangd - -# Install latest stable CMake -RUN wget "https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.sh" && \ - chmod a+x cmake-$CMAKE_VERSION-Linux-x86_64.sh && \ - ./cmake-$CMAKE_VERSION-Linux-x86_64.sh --prefix=/usr/ --skip-license && \ - rm cmake-$CMAKE_VERSION-Linux-x86_64.sh - -RUN echo 'set auto-load safe-path /' > /home/theia/.gdbinit \ +RUN set -ex; \ + chown -R 1001:1001 /home/theia; \ + cp /etc/skel/.bash_logout /etc/skel/.bashrc /etc/skel/.profile /home/theia/; \ + adduser --disabled-password --gecos '' --uid 1001 theia; \ + yarn --pure-lockfile; \ + NODE_OPTIONS="--max_old_space_size=4096" yarn theia build; \ + yarn theia download:plugins; \ + yarn --production; \ + yarn autoclean --init; \ + echo *.ts >> .yarnclean; \ + echo *.ts.map >> .yarnclean; \ + echo *.spec.* >> .yarnclean; \ + yarn autoclean --force; \ + yarn cache clean; \ + apt update; \ + apt-get install -y --no-install-recommends \ + wget gpg apt-transport-https ca-certificates apt-utils; \ + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -; \ + echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-12 main" \ + > /etc/apt/sources.list.d/llvm.list; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + zsh gdb git build-essential cmake \ + gcc-multilib g++-multilib \ + python3 python3-pip \ + clangd-12 \ + qemu-system-i386; \ + pip3 install --no-cache-dir supervisor; \ + ln -s /usr/bin/clangd-12 /usr/bin/clangd; \ + echo 'set auto-load safe-path /' > /home/theia/.gdbinit \ && echo 'source /opt/pwndbg/gdbinit.py' >> /home/theia/.gdbinit \ && git clone https://github.com/pwndbg/pwndbg.git /opt/pwndbg \ && cd /opt/pwndbg \ - && ./setup.sh - -RUN chmod g+rw /home && \ - mkdir -p /home/project && \ - chown -R theia:theia /home/theia && \ - chown -R theia:theia /home/project; - -COPY --from=theia /home/theia /home/theia -WORKDIR /home/theia -COPY supervisord.conf /supervisord.conf + && ./setup.sh; \ + cd /home/theia; \ + git clone https://github.com/ohmyzsh/ohmyzsh.git .oh-my-zsh; \ + git clone https://github.com/zsh-users/zsh-syntax-highlighting.git \ + .oh-my-zsh/custom/plugins/zsh-syntax-highlighting; \ + cp .oh-my-zsh/templates/zshrc.zsh-template .zshrc; \ + sed -i 's/ZSH_THEME="robbyrussell"/ZSH_THEME="gnzh"/' .zshrc; \ + sed -i 's/plugins=(git)/plugins=(git zsh-syntax-highlighting)/' .zshrc; \ + mkdir -p /home/theia/.theia; \ + echo '{"terminal.integrated.shell.linux": "/usr/bin/zsh"}' \ + > /home/theia/.theia/settings.json; \ + chmod g+rw /home \ + && mkdir -p /home/project \ + && chown -R theia:theia /home/theia \ + && chown -R theia:theia /home/project; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /tmp/*; \ + rm -rf /usr/share/doc; \ + rm -rf /var/cache/apt/*; \ + rm -rf /var/lib/apt/lists/*; \ + find / -depth \ + \( -name .cache -o -name __pycache__ -o -name '*.pyc' -o -name '*.a' -o -name .git -o -name .github \) \ + -exec 'rm' '-rf' '{}' '+'; + +COPY supervisord.conf autosave-dump.sh / COPY autosave /usr/local/bin/autosave -COPY autosave-dump.sh /autosave-dump.sh USER theia ENTRYPOINT ["supervisord", "--nodaemon", "-c", "/supervisord.conf"] diff --git a/theia/ide/xv6/autosave b/theia/ide/xv6/autosave index f27f09ef6..04f00e924 100755 --- a/theia/ide/xv6/autosave +++ b/theia/ide/xv6/autosave @@ -1,9 +1,5 @@ #!/bin/bash -if [ "$(cat /tmp/AUTOSAVE)" = "OFF" ]; then - echo 'Autosave was disabled for this session.' - exit 1 -fi if git status | grep 'nothing to commit' &> /dev/null; then echo 'Nothing to autosave just yet. Make a change you would like to save!' diff --git a/theia/sidecar/autosave-loop.sh b/theia/sidecar/autosave-loop.sh index 0511eed56..0e9dfc815 100755 --- a/theia/sidecar/autosave-loop.sh +++ b/theia/sidecar/autosave-loop.sh @@ -12,6 +12,8 @@ fi set +e while true; do - /autosave.sh + if [ "${AUTOSAVE}" = "ON" ]; then + /autosave.sh + fi sleep "5m" done diff --git a/web/src/Components/Admin/Assignment/AssignmentCard.jsx b/web/src/Components/Admin/Assignment/AssignmentCard.jsx index bc86532f8..55daaa703 100644 --- a/web/src/Components/Admin/Assignment/AssignmentCard.jsx +++ b/web/src/Components/Admin/Assignment/AssignmentCard.jsx @@ -212,7 +212,7 @@ export default function AssignmentCard({assignment, editableFields, updateField, variant={'contained'} className={clsx(classes.buttonRight, classes.button)} component={Link} - to={`/admin/assignment/questions/${assignment.unique_code}`} + to={`/admin/assignment/questions/${assignment.id}`} startIcon={} > Edit Questions diff --git a/web/src/Components/Admin/Assignment/QuestionCard.jsx b/web/src/Components/Admin/Assignment/QuestionCard.jsx index f667bef2a..a00726ba5 100644 --- a/web/src/Components/Admin/Assignment/QuestionCard.jsx +++ b/web/src/Components/Admin/Assignment/QuestionCard.jsx @@ -18,6 +18,7 @@ import SaveIcon from '@material-ui/icons/Save'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Switch from '@material-ui/core/Switch'; import DeleteForeverIcon from '@material-ui/icons/DeleteForever'; +import clsx from 'clsx'; const useStyles = makeStyles((theme) => ({ @@ -42,12 +43,16 @@ const useStyles = makeStyles((theme) => ({ width: 42, marginLeft: theme.spacing(2), }, + code_lang: { + marginLeft: theme.spacing(1), + minWidth: 300, + }, })); export default function QuestionCard({assignmentQuestion, updateQuestion, saveQuestion, deleteQuestion, reload}) { const classes = useStyles(); const { - sequence = 0, + pool = 0, question = '', solution = '', code_language = '', @@ -59,20 +64,16 @@ export default function QuestionCard({assignmentQuestion, updateQuestion, saveQu - - - Question {sequence} - - - - Sequence + Question Pool updateQuestion({...assignmentQuestion, sequence: e.target.value})} + value={pool} + onChange={(e) => ( + updateQuestion({...assignmentQuestion, pool: e.target.value}) + )} margin="dense" inputProps={{ 'step': 1, @@ -83,33 +84,36 @@ export default function QuestionCard({assignmentQuestion, updateQuestion, saveQu - updateQuestion({...assignmentQuestion, code_question: !code_question})} - /> - } - label={'Code Question'} - labelPlacement="start" - /> +
+ updateQuestion({...assignmentQuestion, code_question: !code_question})} + /> + } + label={'Code Question'} + labelPlacement="start" + /> - updateQuestion({...assignmentQuestion, code_language: e.target.value})} - /> + ( + updateQuestion({...assignmentQuestion, code_language: e.target.value}) + )} + /> +
- - + + Question Editor - - - Question Preview + + + Markdown Preview - - + + Solution Editor + + Solutions only visible to TAs + - - - Solution Preview + + + Markdown Preview +

+ This will delete all question assignments. This action cannot be easily undone. + After assigning new questions, students may have a different set of assigned questions. + Consider downloading the question assignments before running this action. +

+

Do not use this if you are not sure what you are doing.

+ +); + +const hardResetWarning = ( +
+

+ This will delete all assignments and questions for the assignments. This action cannot be easily undone. + You will need to re-sync the assignment to get the questions back. + Consider downloading the question assignments before running this action. +

+

Do not use this if you are not sure what you are doing.

+
+); + +export default function QuestionControls({assignmentId, reload, questionsAssigned, assignmentName}) { const {enqueueSnackbar} = useSnackbar(); const [verify, setVerify] = useState(null); - const assignQuestions = () => { - axios.get(`/api/admin/questions/assign/${uniqueCode}`).then((response) => { + axios.get(`/api/admin/questions/assign/${assignmentId}`).then((response) => { standardStatusHandler(response, enqueueSnackbar); + reload(); }).catch(standardErrorHandler(enqueueSnackbar)); }; const hardResetQuestions = () => { - axios.get(`/api/admin/questions/hard-reset/${uniqueCode}`).then((response) => { + axios.get(`/api/admin/questions/hard-reset/${assignmentId}`).then((response) => { standardStatusHandler(response, enqueueSnackbar); + reload(); }).catch(standardErrorHandler(enqueueSnackbar)); }; const resetQuestionAssignments = () => { - axios.get(`/api/admin/questions/reset-assignments/${uniqueCode}`).then((response) => { + axios.get(`/api/admin/questions/reset-assignments/${assignmentId}`).then((response) => { standardStatusHandler(response, enqueueSnackbar); + reload(); }).catch(standardErrorHandler(enqueueSnackbar)); }; const downloadQuestionAssignments = () => { - axios.get(`/api/admin/questions/get-assignments/${uniqueCode}`).then((response) => { + axios.get(`/api/admin/questions/get-assignments/${assignmentId}`).then((response) => { const data = standardStatusHandler(response, enqueueSnackbar); - if (data.questions) { + if (data.assignments) { downloadTextFile( - `assignment-${uniqueCode}-question-assignments.json`, - JSON.stringify(data.questions), + `assignment-${assignmentName}-question-assignments.json`, + JSON.stringify(data.assignments), 'application/json', ); } @@ -67,7 +82,7 @@ export default function QuestionControls({uniqueCode, reload}) { }; const addQuestion = () => { - axios.get(`/api/admin/questions/add/${uniqueCode}`).then((response) => { + axios.get(`/api/admin/questions/add/${assignmentId}`).then((response) => { standardStatusHandler(response, enqueueSnackbar); reload(); }).catch(standardErrorHandler(enqueueSnackbar)); @@ -88,7 +103,9 @@ export default function QuestionControls({uniqueCode, reload}) { -