+
+
+We can than deploy our infrastructure with the followings commands
+
+```sh
+# Enter a bash compliant shell
+
+# Create workspace
+terraform workspace new test_terraform
+
+# Create .env file using .template.env and https://api.ovh.com/createToken
+
+# Source .env
+. .env
+# Source OpenRC variable from openrc.sh
+source ./openrc.sh
+
+# Init terraform project
+terraform init
+terraform plan
+
+# Create infra
+terraform apply
+
+# Delete infra (Optional)
+terraform destroy
+
+# Print available Openstack Images
+openstack image list --public
+
+# Print available Openstack Flavors (virtual machine type)
+openstack flavor list
+
+# Generate Ansible inventory
+terraform output -json | jq .ansible_inventory.value | sed 's/^"\|"$//g' | { echo -e "$(cat)"; } > ../ansible/environments/production/hosts
+```
+
+# Systems Configuration using Ansible
+
+We use [Ansible](https://github.com/ansible/ansible) to configure our systems. We use it to configure our machines.
+
+We have 3 types of roles given to our machines:
+- Common: for all the machines
+- Master: for the master node
+- Worker: for the worker nodes
+
+And we created a production infrastructure.
+
+To deploy our configuration to all the instances we use the following command:
+```sh
+ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i ansible/environments/production/hosts ansible/playbook.yml
+```
+
+This will install containerd, Kubernetes and configure the cluster. It will then deploy the application kubernetes configuration.
+
+# Building the doodle application using Nix
+
+We started to use nix to build the application reproducibly. Thanks to nix we managed to build the application and a a docker images that can be deployed on a kubernetes cluster or a docker isntances. We made 2 docker images one for the backend and one for the frontend.
+
+# Kubernetes
+
+We managed to deploy our application on a kubernetes cluster locally using minikube. you can find the relevant files and command in the kube/ folder.
+
+# Configuration Pitfalls
+
+We did some experimentation with creating virtual machine for each services of the application. But getting internet on those vm without edit the host network ocnfiguration was not possible. But we got the application in a "working state". But we were thinking that we could easly install teh vms natively on the instances. But we found out that we couldn't use a custom os images on OVH Cloud. But we sucessfuly managed to "corrupt" the image of the instance to get nixos installed on them (see terraform/nixos_deploy.fr).
+
+Since we where short in time and we already had a working local kubernetes cluster. We deciced that it will be easier to just install kubernetes on the instances and deploy the application on it for the configuration using Ansible since we knew how to use it.
+
+After more than 8 hours of trying to get kubernetes to work on the isntances it kept crashing. Not our services but the kube-system pods. We could not even use kubectl. We tried using containerd and docker-cri but still nothings worked. We decided to give up.
+
+Deployment should only be done using the command in this files.
+
+# Conclusion
+
+Appart from kubernetes not working we manged to get the application working on the local cluster. And to deploy isntances with terraform without issues. So we decided to consider the project as working. Even if is not really. Since configuration on the ubuntu instances using ansible is not working. We might really consider using nix-anywhere to deploy the application. We have some starting point configuration of kubernetes master and worker nodes. in the nix/systems/ directory.
+
+The teraform configuration is working really well. And Openstack is really a nice isntance provider over OVH Cloud and others. It is really realiable.
+
+# Future Work
+
+- Use nix to configure kubernetes master and worker nodes. It might work better. or jsut fix the ansible configuration but really error and documentation are not helpful.
+- Manage secrets using vault. Either sops-nix if using nix or ansible-vault if using ansible.
+- Handle SSL certificate using nginx in the kubernetes cluster. Would need IngressController and cert-manager. That is complex to setup without using helm. Helm seem to be the only way to setup easly cert-manager and nginx as an IngressController for the whole cluster.
+
+# PS:
+
+After some work (its +7hour after the deadline) we managed to install and deploy correctly kubernetes using nixos. We managed to corrupt the ovh vm. But since it's late we can't really fix things now.
+
+
+
+# Annexes
+
+## The Doodle App
+
+The doodle application use multiple services. We have an SQL Server, etherpad and a mail server.
+
+Here is the description of doodle app architecture and dependancies.
+- Doodle Frontend (Angular 10/Typescript)
+ - Static files served by a httpd server.
+ - Port 3000
+ - Call by user (Must be publicly available):
+ - Doodle Backend with `http://doodle-api:8080` endpoint
+ - Etherpad with `http://etherpad:9001`
+- Doodle Back (Quarkus/Java JDK 11)
+ - Port 8080
+ - Call directly (Can be intern):
+ - Database with `jdbc:mysql://mysql:3306` endpoint
+ - Mail Server with `http://mail:2525`
+- Database (MariaDB)
+ - Port 3306
+- Etherpad 1.8.6
+ - Port 9001
+- Mail Server
+ - Port 2525
+
+# Services Deployment using Kubernetes
\ No newline at end of file
diff --git a/ansible/environments/production/hosts b/ansible/environments/production/hosts
new file mode 100644
index 0000000..2078a9e
--- /dev/null
+++ b/ansible/environments/production/hosts
@@ -0,0 +1,6 @@
+[master]
+162.19.100.84 ansible_user=ubuntu ansible_ssh_private_key_file=/home/titouan/.ssh/Olenixen_id_ed25519
+[workers]
+51.75.185.185 ansible_user=ubuntu ansible_ssh_private_key_file=/home/titouan/.ssh/Olenixen_id_ed25519
+51.75.185.127 ansible_user=ubuntu ansible_ssh_private_key_file=/home/titouan/.ssh/Olenixen_id_ed25519
+
diff --git a/ansible/playbook.yml b/ansible/playbook.yml
new file mode 100644
index 0000000..43f51a1
--- /dev/null
+++ b/ansible/playbook.yml
@@ -0,0 +1,23 @@
+- name: Kubernetes cluster setup
+ hosts: all
+ become: yes
+ roles:
+ - common
+
+- name: Kubernetes master init
+ hosts: master
+ become: yes
+ roles:
+ - master
+
+- name: Kubernetes worker join
+ hosts: workers
+ become: yes
+ roles:
+ - worker
+
+- name: Kubernetes deploy yaml
+ hosts: master
+ become: yes
+ roles:
+ - master-deploy
\ No newline at end of file
diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml
new file mode 100644
index 0000000..f23c79f
--- /dev/null
+++ b/ansible/roles/common/tasks/main.yml
@@ -0,0 +1,101 @@
+- name: Install Common packages
+ apt:
+ name:
+ - apt-transport-https
+ - ca-certificates
+ - curl
+ - gnupg
+ - software-properties-common
+ - python3-pip
+ - python3-setuptools
+ state: present
+ update_cache: yes
+
+- name: Use the k8s apt key
+ get_url:
+ url: https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key
+ dest: /etc/apt/keyrings/kubernetes-apt-keyring.asc
+ mode: "0644"
+
+- name: Install k8s apt sources
+ apt_repository:
+ repo: deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.asc] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /
+ state: present
+
+- name: Install Kubernetes packages
+ apt:
+ name:
+ - kubelet
+ - kubeadm
+ - kubectl
+ state: present
+ update_cache: yes
+
+- name: Use the docker apt key
+ get_url:
+ url: https://download.docker.com/linux/ubuntu/gpg
+ dest: /etc/apt/keyrings/docker-apt-keyring.asc
+ mode: "0644"
+
+- name: Install docker apt sources
+ apt_repository:
+ repo: deb [signed-by=/etc/apt/keyrings/docker-apt-keyring.asc] https://download.docker.com/linux/ubuntu oracular stable
+ state: present
+
+- name: Update apt and install docker-ce
+ apt:
+ name: docker-ce
+ state: latest
+ update_cache: true
+
+- name: add modules required by containerd
+ modprobe:
+ name: "{{ item }}"
+ state: present
+ persistent: present
+ loop:
+ - overlay
+ - br_netfilter
+
+- name: install containerd
+ apt:
+ name: containerd
+
+- name: create containerd directory
+ file:
+ path: /etc/containerd
+ state: directory
+
+- name: create containerd config
+ shell: containerd config default > /etc/containerd/config.toml
+
+- name: Copy front docker images to remote
+ ansible.builtin.copy:
+ src: "{{ playbook_dir }}/../kube/containerd_config.toml"
+ dest: "/home/ubuntu/images/doodle-front-docker"
+ mode: '0644'
+
+- name: enable containerd
+ service:
+ name: containerd
+ enabled: yes
+
+- name: configure kubernetes networking
+ sysctl:
+ sysctl_file: /etc/sysctl.d/99-kubernetes-cri.conf
+ name: "{{ item.name }}"
+ value: "{{ item.value }}"
+ loop:
+ - { name: 'net.ipv4.ip_forward', value: '1'}
+ - { name: 'net.bridge.bridge-nf-call-iptables', value: '1'}
+ - { name: 'net.bridge.bridge-nf-call-ip6tables', value: '1'}
+
+- name: enable kubelet
+ service:
+ name: kubelet
+ enabled: yes
+
+- name: restart containerd
+ service:
+ name: containerd
+ state: restarted
\ No newline at end of file
diff --git a/ansible/roles/master-deploy/tasks/main.yml b/ansible/roles/master-deploy/tasks/main.yml
new file mode 100644
index 0000000..82280d3
--- /dev/null
+++ b/ansible/roles/master-deploy/tasks/main.yml
@@ -0,0 +1,64 @@
+- name: Ensure /home/ubuntu/images directory exists
+ file:
+ path: /home/ubuntu/images
+ state: directory
+ mode: '0744'
+ owner: ubuntu
+
+- name: Generate front docker images
+ delegate_to: localhost
+ register: gen_output
+ become: false # ensure no sudo
+ ansible.builtin.command: |
+ nix build {{ playbook_dir }}/../nix/.#doodle-front-docker --print-out-paths
+
+- name: Copy front docker images to remote
+ ansible.builtin.copy:
+ src: "{{ gen_output.stdout }}"
+ dest: "/home/ubuntu/images/doodle-front-docker"
+ mode: '0644'
+
+- name: Load front docker images image into containerd with ctr
+ ansible.builtin.command:
+ cmd: |
+ ctr -n k8s.io images import /home/ubuntu/images/doodle-front-docker
+ ctr -n k8s.io images tag docker.io/library/doodle-front:latest doordle.ovh/prod/doodle-front:latest
+
+- name: Generate api docker images
+ delegate_to: localhost
+ register: gen_output
+ become: false # ensure no sudo
+ ansible.builtin.command: |
+ nix build {{ playbook_dir }}/../nix/.#doodle-api-docker --print-out-paths
+
+- name: Copy api docker images to remote
+ ansible.builtin.copy:
+ src: "{{ gen_output.stdout }}"
+ dest: "/home/ubuntu/images/doodle-api-docker"
+ mode: '0644'
+
+- name: Load api docker images image into containerd with ctr
+ ansible.builtin.command:
+ cmd: |
+ ctr -n k8s.io images import /home/ubuntu/images/doodle-api-docker
+ ctr -n k8s.io images tag docker.io/library/doodle-api:latest doordle.ovh/prod/doodle-api:latest
+
+- name: Copy kubernetes yaml
+ ansible.builtin.copy:
+ src: ../../../../kube/
+ dest: /home/ubuntu/kube
+ owner: ubuntu
+ group: ubuntu
+ mode: '0644'
+
+- name: Set up kubeconfig
+ become_user: ubuntu
+ shell: |
+ kubectl create namespace doodle
+ kubectl apply -f kube/doodle-api.yaml
+ # kubectl create secret generic etherpad-apikey --from-file=APIKEY.txt=kube/APIKEY.txt -n doodle
+ # kubectl apply -f kube/etherpad.yaml
+ # kubectl apply -f kube/mysql.yaml
+ # kubectl apply -f kube/nginx.yaml
+ # kubectl apply -f kube/doodle-frontend.yaml
+
diff --git a/ansible/roles/master/tasks/main.yml b/ansible/roles/master/tasks/main.yml
new file mode 100644
index 0000000..9b8393e
--- /dev/null
+++ b/ansible/roles/master/tasks/main.yml
@@ -0,0 +1,22 @@
+- name: Initialize master
+ shell: kubeadm init --pod-network-cidr=10.244.0.0/16
+ register: kubeadm_output
+ args:
+ creates: /etc/kubernetes/admin.conf
+
+- name: Save join command
+ shell: |
+ kubeadm token create --print-join-command > /tmp/kubeadm_join.sh
+ args:
+ executable: /bin/bash
+
+- name: Set up kubeconfig
+ shell: |
+ mkdir -p /home/ubuntu/.kube
+ cp -i /etc/kubernetes/admin.conf /home/ubuntu/.kube/config
+ chown 1000:1000 /home/ubuntu/.kube/config
+
+- name: Install Flannel CNI
+ become_user: ubuntu
+ shell: |
+ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
diff --git a/ansible/roles/worker/tasks/main.yml b/ansible/roles/worker/tasks/main.yml
new file mode 100644
index 0000000..ed84a5f
--- /dev/null
+++ b/ansible/roles/worker/tasks/main.yml
@@ -0,0 +1,16 @@
+- name: Fetch join command from master
+ fetch:
+ src: /tmp/kubeadm_join.sh
+ dest: /tmp/kubeadm_join.sh
+ flat: yes
+ delegate_to: "{{ groups['master'][0] }}"
+ run_once: true
+
+- name: Copy join script to worker
+ copy:
+ src: /tmp/kubeadm_join.sh
+ dest: /tmp/kubeadm_join.sh
+ mode: 0755
+
+- name: Join the cluster
+ command: bash /tmp/kubeadm_join.sh
\ No newline at end of file
diff --git a/assets/AnsibleNixosMeme.png b/assets/AnsibleNixosMeme.png
new file mode 100644
index 0000000..11a0c65
Binary files /dev/null and b/assets/AnsibleNixosMeme.png differ
diff --git a/assets/ovhKey.png b/assets/ovhKey.png
new file mode 100644
index 0000000..069d990
Binary files /dev/null and b/assets/ovhKey.png differ
diff --git a/kube/APIKEY.txt b/kube/APIKEY.txt
new file mode 100644
index 0000000..5b5ce46
--- /dev/null
+++ b/kube/APIKEY.txt
@@ -0,0 +1 @@
+19d89ca52bc0fa4f19d6325464d9d7a032649b9fa68c111514627081e2784b4a
\ No newline at end of file
diff --git a/kube/README.md b/kube/README.md
new file mode 100644
index 0000000..2f7d388
--- /dev/null
+++ b/kube/README.md
@@ -0,0 +1,78 @@
+# How to run the project using minikube
+
+This documentation is for deploying the project using minikube (local kubernetes cluster).
+
+# Prerequisites
+- [minikube](https://minikube.sigs.k8s.io/docs/start/) to create the cluster and run the docker images
+- [nix](https://nixos.org/download.html) to build the images
+
+# Drawback
+
+This does not expose any other services than a nginx gateways to the services. So for developement this will not be highly useful. Since you can't access the services from outside the cluster (database, frontend, backend).
+It also force some domain name inside the project configuration like (frontend, backend, mysql, etherpad). So in a developement environment you will need to change the domain or use a proxy like nginx to redirect the traffic to the correct service or dns reddirect.
+
+# Starting using minikube
+```sh
+# Start minikube cluster
+minikube start
+
+# Start metric server to get information on the clusters
+minikube addons enable metrics-server
+
+# Launch minikube GUI
+minikube dashboard
+```
+
+# Import Images
+There is a imagePullPolicy: Never in the deployment yaml. So we need to load the images manually.
+
+```sh
+# Load minkube docker env
+eval $(minikube docker-env)
+
+# Build doodle frontend image
+nix build nix/.#doodle-front-docker
+# Push doodle front docker image
+docker load < result
+
+# Build doodle backend image
+nix build nix/.#doodle-api-docker
+# Push doodle api docker image
+docker load < result
+```
+
+# Deploy yamls
+
+```sh
+# Create namespace
+minikube kubectl -- create namespace doodle
+
+# Create etherpad secrets
+minikube kubectl -- create secret generic etherpad-apikey \
+ --from-file=APIKEY.txt=nix/doodle/api/APIKEY.txt \
+ -n doodle
+
+# Add etherpad service/deployment
+minikube kubectl -- apply -f kube/etherpad.yaml
+
+# Add mysql service/deployment
+minikube kubectl -- apply -f kube/mysql.yaml
+
+# Add frontend service/deployment
+minikube kubectl -- apply -f kube/doodle-frontend.yaml
+
+# Add backend service/deployment
+minikube kubectl -- apply -f kube/doodle-api.yaml
+
+# Add cert-manager & nginx
+# minikube kubectl -- apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml # Need to wait for cert-manager to be ready
+# minikube kubectl -- apply -f kube/cert-manager.yaml
+minikube kubectl -- apply -f kube/nginx.yaml
+```
+
+# Cleanup
+
+```sh
+minikube stop
+minikube delete
+```
\ No newline at end of file
diff --git a/kube/cert-manager.yaml b/kube/cert-manager.yaml
new file mode 100644
index 0000000..8f6ef70
--- /dev/null
+++ b/kube/cert-manager.yaml
@@ -0,0 +1,34 @@
+# First, install cert-manager (apply this separately)
+apiVersion: cert-manager.io/v1
+kind: ClusterIssuer
+metadata:
+ name: letsencrypt-prod
+spec:
+ acme:
+ # The ACME server URL
+ server: https://acme-v02.api.letsencrypt.org/directory
+ # Email address used for ACME registration
+ email: titouan.le.dilavrec@gmail.com
+ # Name of a secret used to store the ACME account private key
+ privateKeySecretRef:
+ name: letsencrypt-prod
+ # Enable the HTTP-01 challenge provider
+ solvers:
+ - http01:
+ ingress:
+ class: nginx
+---
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: nginx-tls-secret
+ namespace: doodle
+spec:
+ secretName: nginx-tls-secret
+ issuerRef:
+ name: letsencrypt-prod
+ kind: ClusterIssuer
+ # Replace with your domain(s)
+ dnsNames:
+ - doordle.ovh
+ - www.doordle.ovh
\ No newline at end of file
diff --git a/kube/containerd_config.toml b/kube/containerd_config.toml
new file mode 100644
index 0000000..454fabd
--- /dev/null
+++ b/kube/containerd_config.toml
@@ -0,0 +1,239 @@
+version = 3
+root = '/var/lib/containerd'
+state = '/run/containerd'
+temp = ''
+plugin_dir = ''
+disabled_plugins = []
+required_plugins = []
+oom_score = 0
+imports = []
+
+[grpc]
+ address = '/run/containerd/containerd.sock'
+ tcp_address = ''
+ tcp_tls_ca = ''
+ tcp_tls_cert = ''
+ tcp_tls_key = ''
+ uid = 0
+ gid = 0
+ max_recv_message_size = 16777216
+ max_send_message_size = 16777216
+
+[ttrpc]
+ address = ''
+ uid = 0
+ gid = 0
+
+[debug]
+ address = ''
+ uid = 0
+ gid = 0
+ level = ''
+ format = ''
+
+[metrics]
+ address = ''
+ grpc_histogram = false
+
+[plugins]
+ [plugins.'io.containerd.cri.v1.images']
+ snapshotter = 'overlayfs'
+ disable_snapshot_annotations = true
+ discard_unpacked_layers = false
+ max_concurrent_downloads = 3
+ image_pull_progress_timeout = '5m0s'
+ image_pull_with_sync_fs = false
+ stats_collect_period = 10
+
+ [plugins.'io.containerd.cri.v1.images'.pinned_images]
+ sandbox = 'registry.k8s.io/pause:3.10'
+
+ [plugins.'io.containerd.cri.v1.images'.registry]
+ config_path = ''
+
+ [plugins.'io.containerd.cri.v1.images'.image_decryption]
+ key_model = 'node'
+
+ [plugins.'io.containerd.cri.v1.runtime']
+ enable_selinux = false
+ selinux_category_range = 1024
+ max_container_log_line_size = 16384
+ disable_cgroup = false
+ disable_apparmor = false
+ restrict_oom_score_adj = false
+ disable_proc_mount = false
+ unset_seccomp_profile = ''
+ tolerate_missing_hugetlb_controller = true
+ disable_hugetlb_controller = true
+ device_ownership_from_security_context = false
+ ignore_image_defined_volumes = false
+ netns_mounts_under_state_dir = false
+ enable_unprivileged_ports = true
+ enable_unprivileged_icmp = true
+ enable_cdi = true
+ cdi_spec_dirs = ['/etc/cdi', '/var/run/cdi']
+ drain_exec_sync_io_timeout = '0s'
+ ignore_deprecation_warnings = []
+
+ [plugins.'io.containerd.cri.v1.runtime'.containerd]
+ default_runtime_name = 'runc'
+ ignore_blockio_not_enabled_errors = false
+ ignore_rdt_not_enabled_errors = false
+
+ [plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes]
+ [plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc]
+ runtime_type = 'io.containerd.runc.v2'
+ runtime_path = ''
+ pod_annotations = []
+ container_annotations = []
+ privileged_without_host_devices = false
+ privileged_without_host_devices_all_devices_allowed = false
+ base_runtime_spec = ''
+ cni_conf_dir = ''
+ cni_max_conf_num = 0
+ snapshotter = ''
+ sandboxer = 'podsandbox'
+ io_type = ''
+
+ [plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
+ BinaryName = ''
+ CriuImagePath = ''
+ CriuWorkPath = ''
+ IoGid = 0
+ IoUid = 0
+ NoNewKeyring = false
+ Root = ''
+ ShimCgroup = ''
+
+ [plugins.'io.containerd.cri.v1.runtime'.cni]
+ bin_dir = '/opt/cni/bin'
+ conf_dir = '/etc/cni/net.d'
+ max_conf_num = 1
+ setup_serially = false
+ conf_template = ''
+ ip_pref = ''
+ use_internal_loopback = false
+
+ [plugins.'io.containerd.gc.v1.scheduler']
+ pause_threshold = 0.02
+ deletion_threshold = 0
+ mutation_threshold = 100
+ schedule_delay = '0s'
+ startup_delay = '100ms'
+
+ [plugins.'io.containerd.grpc.v1.cri']
+ disable_tcp_service = true
+ stream_server_address = '127.0.0.1'
+ stream_server_port = '0'
+ stream_idle_timeout = '4h0m0s'
+ enable_tls_streaming = false
+
+ [plugins.'io.containerd.grpc.v1.cri'.x509_key_pair_streaming]
+ tls_cert_file = ''
+ tls_key_file = ''
+
+ [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
+ SystemdCgroup = true
+
+ [plugins.'io.containerd.image-verifier.v1.bindir']
+ bin_dir = '/opt/containerd/image-verifier/bin'
+ max_verifiers = 10
+ per_verifier_timeout = '10s'
+
+ [plugins.'io.containerd.internal.v1.opt']
+ path = '/opt/containerd'
+
+ [plugins.'io.containerd.internal.v1.tracing']
+
+ [plugins.'io.containerd.metadata.v1.bolt']
+ content_sharing_policy = 'shared'
+
+ [plugins.'io.containerd.monitor.container.v1.restart']
+ interval = '10s'
+
+ [plugins.'io.containerd.monitor.task.v1.cgroups']
+ no_prometheus = false
+
+ [plugins.'io.containerd.nri.v1.nri']
+ disable = false
+ socket_path = '/var/run/nri/nri.sock'
+ plugin_path = '/opt/nri/plugins'
+ plugin_config_path = '/etc/nri/conf.d'
+ plugin_registration_timeout = '5s'
+ plugin_request_timeout = '2s'
+ disable_connections = false
+
+ [plugins.'io.containerd.runtime.v2.task']
+ platforms = ['linux/amd64']
+
+ [plugins.'io.containerd.service.v1.diff-service']
+ default = ['walking']
+
+ [plugins.'io.containerd.service.v1.tasks-service']
+ blockio_config_file = ''
+ rdt_config_file = ''
+
+ [plugins.'io.containerd.shim.v1.manager']
+ env = []
+
+ [plugins.'io.containerd.snapshotter.v1.blockfile']
+ root_path = ''
+ scratch_file = ''
+ fs_type = ''
+ mount_options = []
+ recreate_scratch = false
+
+ [plugins.'io.containerd.snapshotter.v1.btrfs']
+ root_path = ''
+
+ [plugins.'io.containerd.snapshotter.v1.devmapper']
+ root_path = ''
+ pool_name = ''
+ base_image_size = ''
+ async_remove = false
+ discard_blocks = false
+ fs_type = ''
+ fs_options = ''
+
+ [plugins.'io.containerd.snapshotter.v1.native']
+ root_path = ''
+
+ [plugins.'io.containerd.snapshotter.v1.overlayfs']
+ root_path = ''
+ upperdir_label = false
+ sync_remove = false
+ slow_chown = false
+ mount_options = []
+
+ [plugins.'io.containerd.tracing.processor.v1.otlp']
+
+ [plugins.'io.containerd.transfer.v1.local']
+ max_concurrent_downloads = 3
+ max_concurrent_uploaded_layers = 3
+ config_path = ''
+
+[cgroup]
+ path = ''
+
+[timeouts]
+ 'io.containerd.timeout.bolt.open' = '0s'
+ 'io.containerd.timeout.metrics.shimstats' = '2s'
+ 'io.containerd.timeout.shim.cleanup' = '5s'
+ 'io.containerd.timeout.shim.load' = '5s'
+ 'io.containerd.timeout.shim.shutdown' = '3s'
+ 'io.containerd.timeout.task.state' = '2s'
+
+[stream_processors]
+ [stream_processors.'io.containerd.ocicrypt.decoder.v1.tar']
+ accepts = ['application/vnd.oci.image.layer.v1.tar+encrypted']
+ returns = 'application/vnd.oci.image.layer.v1.tar'
+ path = 'ctd-decoder'
+ args = ['--decryption-keys-path', '/etc/containerd/ocicrypt/keys']
+ env = ['OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf']
+
+ [stream_processors.'io.containerd.ocicrypt.decoder.v1.tar.gzip']
+ accepts = ['application/vnd.oci.image.layer.v1.tar+gzip+encrypted']
+ returns = 'application/vnd.oci.image.layer.v1.tar+gzip'
+ path = 'ctd-decoder'
+ args = ['--decryption-keys-path', '/etc/containerd/ocicrypt/keys']
+ env = ['OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf']
\ No newline at end of file
diff --git a/kube/doodle-api.yaml b/kube/doodle-api.yaml
new file mode 100644
index 0000000..39b42f4
--- /dev/null
+++ b/kube/doodle-api.yaml
@@ -0,0 +1,47 @@
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: api
+ namespace: doodle
+ labels:
+ k8s-app: api
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: api
+ template:
+ metadata:
+ name: api
+ labels:
+ k8s-app: api
+ spec:
+ containers:
+ - name: api
+ image: doordle.ovh/prod/doodle-api:latest
+ imagePullPolicy: Never
+ securityContext:
+ privileged: false
+ restartPolicy: Always
+ dnsPolicy: ClusterFirst
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 25%
+ maxSurge: 25%
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: api
+ namespace: doodle
+ labels:
+ k8s-app: api
+spec:
+ ports:
+ - name: tcp-8080-8080-api
+ protocol: TCP
+ port: 8080
+ targetPort: 8080
+ selector:
+ k8s-app: api
\ No newline at end of file
diff --git a/kube/doodle-frontend.yaml b/kube/doodle-frontend.yaml
new file mode 100644
index 0000000..d4adc03
--- /dev/null
+++ b/kube/doodle-frontend.yaml
@@ -0,0 +1,47 @@
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: front
+ namespace: doodle
+ labels:
+ k8s-app: front
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: front
+ template:
+ metadata:
+ name: front
+ labels:
+ k8s-app: front
+ spec:
+ containers:
+ - name: front
+ image: doordle.ovh/prod/doodle-api:latest
+ imagePullPolicy: Never
+ securityContext:
+ privileged: false
+ restartPolicy: Always
+ dnsPolicy: ClusterFirst
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 25%
+ maxSurge: 25%
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: front
+ namespace: doodle
+ labels:
+ k8s-app: front
+spec:
+ ports:
+ - name: tcp-80-3000-front
+ protocol: TCP
+ port: 80
+ targetPort: 3000
+ selector:
+ k8s-app: front
\ No newline at end of file
diff --git a/kube/etherpad.yaml b/kube/etherpad.yaml
new file mode 100644
index 0000000..de89c6c
--- /dev/null
+++ b/kube/etherpad.yaml
@@ -0,0 +1,55 @@
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: etherpad
+ namespace: doodle
+ labels:
+ k8s-app: etherpad
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: etherpad
+ template:
+ metadata:
+ name: etherpad
+ labels:
+ k8s-app: etherpad
+ spec:
+ containers:
+ - name: etherpad
+ image: etherpad/etherpad:1.8.6
+ securityContext:
+ privileged: false
+ volumeMounts:
+ - name: apikey-volume
+ mountPath: /opt/etherpad-lite/APIKEY.txt
+ subPath: APIKEY.txt
+ volumes:
+ - name: apikey-volume
+ secret:
+ secretName: etherpad-apikey
+ restartPolicy: Always
+ terminationGracePeriodSeconds: 30
+ dnsPolicy: ClusterFirst
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 25%
+ maxSurge: 25%
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: etherpad
+ namespace: doodle
+ labels:
+ k8s-app: etherpad
+spec:
+ ports:
+ - name: tcp-9001-9001-etherpad
+ protocol: TCP
+ port: 9001
+ targetPort: 9001
+ selector:
+ k8s-app: etherpad
\ No newline at end of file
diff --git a/kube/kubeadm-config.yaml b/kube/kubeadm-config.yaml
new file mode 100644
index 0000000..f0c9d3c
--- /dev/null
+++ b/kube/kubeadm-config.yaml
@@ -0,0 +1,11 @@
+apiVersion: kubeadm.k8s.io/v1beta3
+kind: InitConfiguration
+nodeRegistration:
+ criSocket: "unix:///var/run/containerd/containerd.sock" # or docker: "unix:///var/run/docker.sock"
+ kubeletExtraArgs:
+ cgroup-driver: "systemd"
+---
+apiVersion: kubeadm.k8s.io/v1beta3
+kind: ClusterConfiguration
+networking:
+ podSubnet: "10.244.0.0/16" # Flannel-compatible CIDR
\ No newline at end of file
diff --git a/kube/mail.yaml b/kube/mail.yaml
new file mode 100644
index 0000000..20e0112
--- /dev/null
+++ b/kube/mail.yaml
@@ -0,0 +1,47 @@
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: mail
+ namespace: doodle
+ labels:
+ k8s-app: mail
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: mail
+ template:
+ metadata:
+ name: mail
+ labels:
+ k8s-app: mail
+ spec:
+ containers:
+ - name: mail
+ image: bytemark/smtp:latest
+ imagePullPolicy: Always
+ securityContext:
+ privileged: false
+ restartPolicy: Always
+ dnsPolicy: ClusterFirst
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 25%
+ maxSurge: 25%
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: mail
+ namespace: doodle
+ labels:
+ k8s-app: mail
+spec:
+ ports:
+ - name: tcp-2525-25-smtp
+ protocol: TCP
+ port: 2525
+ targetPort: 25
+ selector:
+ k8s-app: mail
diff --git a/kube/mysql.yaml b/kube/mysql.yaml
new file mode 100644
index 0000000..b495590
--- /dev/null
+++ b/kube/mysql.yaml
@@ -0,0 +1,56 @@
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: mysql
+ namespace: doodle
+ labels:
+ k8s-app: mysql
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: mysql
+ template:
+ metadata:
+ name: mysql
+ labels:
+ k8s-app: mysql
+ spec:
+ containers:
+ - name: mysql
+ image: mysql
+ ports:
+ - containerPort: 3306
+ env:
+ - name: MYSQL_ROOT_PASSWORD
+ value: "root"
+ - name: MYSQL_DATABASE
+ value: "tlc"
+ - name: MYSQL_USER
+ value: "tlc"
+ - name: MYSQL_PASSWORD
+ value: "tlc"
+ volumeMounts:
+ - name: mysql-data
+ mountPath: /var/lib/mysql
+ volumes:
+ - name: mysql-data
+ emptyDir: {} # TODO Use a PersistentVolumeClaim in production
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: mysql
+ namespace: doodle
+ labels:
+ k8s-app: mysql
+spec:
+ type: NodePort
+ ports:
+ - name: tcp-3306-3306-mysql
+ protocol: TCP
+ port: 3306
+ targetPort: 3306
+ nodePort: 32306
+ selector:
+ k8s-app: mysql
\ No newline at end of file
diff --git a/kube/nginx.yaml b/kube/nginx.yaml
new file mode 100644
index 0000000..ceb3aff
--- /dev/null
+++ b/kube/nginx.yaml
@@ -0,0 +1,146 @@
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: nginx-config
+ namespace: doodle
+data:
+ nginx.conf: |
+ user nginx;
+ worker_processes auto;
+ error_log /var/log/nginx/error.log warn;
+ pid /var/run/nginx.pid;
+
+ events {
+ worker_connections 1024;
+ }
+
+ http {
+ include /etc/nginx/mime.types;
+ sendfile on;
+ keepalive_timeout 65;
+
+ # HTTP server - redirects to HTTPS
+ # server {
+ # listen 80;
+ # server_name _;
+
+ # # Redirect all HTTP requests to HTTPS
+ # location / {
+ # return 301 https://$host$request_uri;
+ # }
+ # }
+
+ # HTTPS server
+ server {
+ listen 80;
+ # listen 443 ssl;
+ # server_name _;
+
+ # # TLS configuration
+ # ssl_certificate /etc/nginx/ssl/tls.crt;
+ # ssl_certificate_key /etc/nginx/ssl/tls.key;
+ # ssl_protocols TLSv1.2 TLSv1.3;
+ # ssl_ciphers HIGH:!aNULL:!MD5;
+ # ssl_prefer_server_ciphers on;
+
+ # Frontend route
+ location / {
+ proxy_pass http://front:80;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # API route
+ location /api/ {
+ proxy_pass http://api:8080/api/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # Etherpad route
+ location /etherpad/ {
+ proxy_pass http://etherpad:9001/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ }
+ }
+---
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: nginx-gateway
+ namespace: doodle
+ labels:
+ k8s-app: nginx-gateway
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: nginx-gateway
+ template:
+ metadata:
+ name: nginx-gateway
+ labels:
+ k8s-app: nginx-gateway
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.21
+ ports:
+ - containerPort: 80
+ - containerPort: 443
+ volumeMounts:
+ - name: nginx-config
+ mountPath: /etc/nginx/nginx.conf
+ subPath: nginx.conf
+ # - name: tls-certs
+ # mountPath: /etc/nginx/ssl
+ securityContext:
+ privileged: false
+ volumes:
+ - name: nginx-config
+ configMap:
+ name: nginx-config
+ # - name: tls-certs
+ # secret:
+ # secretName: nginx-tls-secret
+ restartPolicy: Always
+ dnsPolicy: ClusterFirst
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 25%
+ maxSurge: 25%
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: nginx-gateway
+ namespace: doodle
+ labels:
+ k8s-app: nginx-gateway
+spec:
+ type: NodePort
+ ports:
+ - name: http
+ protocol: TCP
+ port: 80
+ targetPort: 80
+ nodePort: 30081
+ - name: https
+ protocol: TCP
+ port: 443
+ targetPort: 443
+ nodePort: 30443
+ selector:
+ k8s-app: nginx-gateway
diff --git a/nix/.envrc b/nix/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/nix/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/nix/.gitignore b/nix/.gitignore
new file mode 100644
index 0000000..752360e
--- /dev/null
+++ b/nix/.gitignore
@@ -0,0 +1 @@
+/nixos.qcow2
\ No newline at end of file
diff --git a/nix/doodle/README.french.md b/nix/doodle/README.french.md
new file mode 100644
index 0000000..ae8a4f8
--- /dev/null
+++ b/nix/doodle/README.french.md
@@ -0,0 +1,35 @@
+# Doodle in quarkus
+
+Ce repository est une application de type doodle développée avec quarkus.io pour le back et angular pour le front.
+
+Elle initialise automatiquement un pad pour la réunion et un salon de discussion.
+
+Le but est de faire travailler les étudiants sur la partie déploiement de ce type d'application dite cloud native.
+
+Votre mission est de mettre en production une telle application en permettant
+- qu'à chaque commit sur ce repository, si les tests passent, alors nous déployons automatiquement une nouvelle version dans un contexte (Continuous Deployement)
+- que l'application doit être monitorer finement.
+- que l'application redémarre automatiquement en cas de crash du serveur ou de crash d'un des services de l'application.
+- que Les accès doivent http doivent utiliser https.
+
+
+Une démo de l'application est accessible [ici](https://doodle.diverse-team.fr).
+
+- Voici une petite [vidéo](https://drive.google.com/file/d/1GQbdgq2CHcddTlcoHqM5Zc8Dw5o_eeLg/preview) de présentation des fonctionnalités de l'application.
+- Voici une petite [vidéo](https://drive.google.com/file/d/1l5UAsU5_q-oshwEW6edZ4UvQjN3-tzwi/preview) de présentation de l'architecture de l'application.
+- Voici une petite [vidéo](https://drive.google.com/file/d/1jxYNfJdtd4r_pDbOthra360ei8Z17tX_/preview) de revue de code de l'application.
+
+Un descriptif du cours, des TPs et des étapes du projet est lui accessible [ici](https://hackmd.diverse-team.fr/s/SJqu5DjSD)
+
+## Prérequis
+
+Vérifiez que vous avez installé sur votre machine de développement :
+
+- Java (JDK) 11+, e.g. [Oracle JSE](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) (avec la variable d'environnement JAVA_HOME bien configurée)
+- [Maven](http://maven.apache.org/install.html)
+- [Git](https://git-scm.com/download/)
+- [Docker](https://docs.docker.com/engine/install/) (au moins version 19.03.0, 20.10 préféré)
+- Docker compose ([Compose V2](https://docs.docker.com/compose/cli-command/#installing-compose-v2) préféré, nécésité de lancer des configuration compose en version 3.8)
+- [Node](https://nodejs.org/en/) au moins version 16
+- npm au moins version 8 (installé avec Node)
+- Un IDE Java (Eclipse, IntelliJ IDEA, NetBeans, VS Code, Xcode, etc.)
\ No newline at end of file
diff --git a/nix/doodle/Readme.md b/nix/doodle/Readme.md
new file mode 100644
index 0000000..5764cc5
--- /dev/null
+++ b/nix/doodle/Readme.md
@@ -0,0 +1,31 @@
+# Remote meetings planning
+
+This project is used in a course on the *ops* part at the [University of Rennes](https://www.univ-rennes1.fr/), France. It is a kind of doodle clone developed in so-called "cloud-native" technologies in order to allow students to work on a continuous deployment chain in a containerized environment. Among the feature, the application automatically initializes a pad for the meeting and a chat room for the meeting participants.
+
+- The [back](https://github.com/barais/doodlestudent/tree/main/api) is developed using the [quarkus.io](https://quarkus.io/) framework.
+- The [front](https://github.com/barais/doodlestudent/tree/main/front) is developed in [angular](https://angular.io/) using the [primeng](https://www.primefaces.org/primeng/) angular UI component library and the [fullcalendar](https://fullcalendar.io/) graphical component.
+
+A demo of the application is available [here](https://doodle.diverse-team.fr/).
+
+Three videos (in french) are available. They present:
+- the [main application feature](https://drive.google.com/file/d/1GQbdgq2CHcddTlcoHqM5Zc8Dw5o_eeLg/preview),
+- its [architecture](https://drive.google.com/file/d/1l5UAsU5_q-oshwEW6edZ4UvQjN3-tzwi/preview)
+- and a [short code review](https://drive.google.com/file/d/1jxYNfJdtd4r_pDbOthra360ei8Z17tX_/preview) .
+
+For french native speaker that wants to follow the course. The course web page is available [here](https://hackmd.diverse-team.fr/s/SJqu5DjSD).
+
+## Requirments
+
+Verify that these are installed on your computer :
+
+- Java (JDK) 11+, e.g. [Oracle JSE](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) (with the JAVA_HOME environment variable correctly set)
+- [Maven](http://maven.apache.org/install.html)
+- [Git](https://git-scm.com/download/)
+- [Docker](https://docs.docker.com/engine/install/) (at least version 19.03.0, 20.10 preferred)
+- Docker compose ([Compose V2](https://docs.docker.com/compose/cli-command/#installing-compose-v2) preferred, should be able to run 3.8 compose files)
+- [Node](https://nodejs.org/en/) at least version 16
+- npm at least version 8 (installed with Node)
+- A Java IDE (Eclipse, IntelliJ IDEA, NetBeans, VS Code, Xcode, etc.)
+
+If you are on Windows, Docker can not mount files outside your user folder (Unless an absolute path is provided).
+Please, clone the doodle in the user folder or change the compose file to correctly mount the etherpad APIKEY.txt
diff --git a/nix/doodle/api/.dockerignore b/nix/doodle/api/.dockerignore
new file mode 100644
index 0000000..94810d0
--- /dev/null
+++ b/nix/doodle/api/.dockerignore
@@ -0,0 +1,5 @@
+*
+!target/*-runner
+!target/*-runner.jar
+!target/lib/*
+!target/quarkus-app/*
\ No newline at end of file
diff --git a/nix/doodle/api/.gitignore b/nix/doodle/api/.gitignore
new file mode 100644
index 0000000..092178b
--- /dev/null
+++ b/nix/doodle/api/.gitignore
@@ -0,0 +1,37 @@
+#Maven
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+release.properties
+.mvn/wrapper/maven-wrapper.jar
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+.factorypath
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
diff --git a/nix/doodle/api/.mvn/wrapper/MavenWrapperDownloader.java b/nix/doodle/api/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..b901097
--- /dev/null
+++ b/nix/doodle/api/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if(mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if(mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if(!outputFile.getParentFile().exists()) {
+ if(!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/nix/doodle/api/.mvn/wrapper/maven-wrapper.properties b/nix/doodle/api/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..642d572
--- /dev/null
+++ b/nix/doodle/api/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/nix/doodle/api/APIKEY.txt b/nix/doodle/api/APIKEY.txt
new file mode 100644
index 0000000..5b5ce46
--- /dev/null
+++ b/nix/doodle/api/APIKEY.txt
@@ -0,0 +1 @@
+19d89ca52bc0fa4f19d6325464d9d7a032649b9fa68c111514627081e2784b4a
\ No newline at end of file
diff --git a/nix/doodle/api/README.md b/nix/doodle/api/README.md
new file mode 100644
index 0000000..29a73a8
--- /dev/null
+++ b/nix/doodle/api/README.md
@@ -0,0 +1,53 @@
+# code-with-quarkus project
+
+This project uses Quarusk, the Supersonic Subatomic Java Framework.
+
+If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
+
+## Running the application in dev mode
+
+You can run your application in dev mode that enables live coding using:
+```shell script
+docker-compose up --detach
+# Wait the correct start of the docker services and then
+./mvnw compile quarkus:dev
+```
+
+To stop the application and its dependencies, type `ctrl+c` in the bash session and run `docker-compose down`.
+
+## Packaging and running the application
+
+The application can be packaged using:
+```shell script
+./mvnw package
+```
+It produces the `code-with-quarkus-1.0.0-SNAPSHOT-runner.jar` file in the `/target` directory.
+Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory.
+If you want to build an _über-jar_, execute the following command:
+```shell script
+./mvnw clean package -Dquarkus.package.type=uber-jar
+```
+
+The application is now runnable using `java -jar target/code-with-quarkus-1.0.0-SNAPSHOT-runner.jar`.
+
+## Creating a native executable
+
+You can create a native executable using:
+```shell script
+./mvnw package -Pnative
+```
+
+Or, if you don't have GraalVM installed, you can run the native executable build in a container using:
+```shell script
+./mvnw package -Pnative -Dquarkus.native.container-build=true
+```
+
+You can then execute your native executable with: `./target/code-with-quarkus-1.0.0-SNAPSHOT-runner`
+
+If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.html.
+
+# RESTEasy JAX-RS
+
+Guide: https://quarkus.io/guides/rest-json
+
+
diff --git a/nix/doodle/api/docker-compose.nix b/nix/doodle/api/docker-compose.nix
new file mode 100644
index 0000000..387ae15
--- /dev/null
+++ b/nix/doodle/api/docker-compose.nix
@@ -0,0 +1,134 @@
+# Auto-generated using compose2nix v0.3.1.
+{ pkgs, lib, ... }:
+
+{
+ # Runtime
+ virtualisation.docker = {
+ enable = true;
+ autoPrune.enable = true;
+ };
+ virtualisation.oci-containers.backend = "docker";
+
+ # Containers
+ virtualisation.oci-containers.containers."doodle-services-db" = {
+ image = "mysql";
+ environment = {
+ "MYSQL_DATABASE" = "tlc";
+ "MYSQL_PASSWORD" = "tlc";
+ "MYSQL_ROOT_PASSWORD" = "root";
+ "MYSQL_USER" = "tlc";
+ };
+ ports = [
+ "3306:3306/tcp"
+ ];
+ log-driver = "journald";
+ extraOptions = [
+ "--network-alias=db"
+ "--network=doodle-services_default"
+ ];
+ };
+ systemd.services."docker-doodle-services-db" = {
+ serviceConfig = {
+ Restart = lib.mkOverride 90 "no";
+ };
+ after = [
+ "docker-network-doodle-services_default.service"
+ ];
+ requires = [
+ "docker-network-doodle-services_default.service"
+ ];
+ partOf = [
+ "docker-compose-doodle-services-root.target"
+ ];
+ wantedBy = [
+ "docker-compose-doodle-services-root.target"
+ ];
+ };
+ virtualisation.oci-containers.containers."doodle-services-etherpad" = {
+ image = "etherpad/etherpad:latest";
+ volumes = [
+ "/home/titouan/Documents/ESIR/S8/DevOps/devops/nix/doodle/api/APIKEY.txt:/opt/etherpad-lite/APIKEY.txt:rw"
+ ];
+ ports = [
+ "9001:9001/tcp"
+ ];
+ log-driver = "journald";
+ extraOptions = [
+ "--network-alias=etherpad"
+ "--network=doodle-services_default"
+ ];
+ };
+ systemd.services."docker-doodle-services-etherpad" = {
+ serviceConfig = {
+ Restart = lib.mkOverride 90 "no";
+ };
+ after = [
+ "docker-network-doodle-services_default.service"
+ ];
+ requires = [
+ "docker-network-doodle-services_default.service"
+ ];
+ partOf = [
+ "docker-compose-doodle-services-root.target"
+ ];
+ wantedBy = [
+ "docker-compose-doodle-services-root.target"
+ ];
+ };
+ virtualisation.oci-containers.containers."doodle-services-mail" = {
+ image = "bytemark/smtp";
+ ports = [
+ "2525:25/tcp"
+ ];
+ log-driver = "journald";
+ extraOptions = [
+ "--network-alias=mail"
+ "--network=doodle-services_default"
+ ];
+ };
+ systemd.services."docker-doodle-services-mail" = {
+ serviceConfig = {
+ Restart = lib.mkOverride 90 "always";
+ RestartMaxDelaySec = lib.mkOverride 90 "1m";
+ RestartSec = lib.mkOverride 90 "100ms";
+ RestartSteps = lib.mkOverride 90 9;
+ };
+ after = [
+ "docker-network-doodle-services_default.service"
+ ];
+ requires = [
+ "docker-network-doodle-services_default.service"
+ ];
+ partOf = [
+ "docker-compose-doodle-services-root.target"
+ ];
+ wantedBy = [
+ "docker-compose-doodle-services-root.target"
+ ];
+ };
+
+ # Networks
+ systemd.services."docker-network-doodle-services_default" = {
+ path = [ pkgs.docker ];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ ExecStop = "docker network rm -f doodle-services_default";
+ };
+ script = ''
+ docker network inspect doodle-services_default || docker network create doodle-services_default
+ '';
+ partOf = [ "docker-compose-doodle-services-root.target" ];
+ wantedBy = [ "docker-compose-doodle-services-root.target" ];
+ };
+
+ # Root service
+ # When started, this will automatically create all resources and start
+ # the containers. When stopped, this will teardown all resources.
+ systemd.targets."docker-compose-doodle-services-root" = {
+ unitConfig = {
+ Description = "Root target generated by compose2nix.";
+ };
+ wantedBy = [ "multi-user.target" ];
+ };
+}
diff --git a/nix/doodle/api/docker-compose.yaml b/nix/doodle/api/docker-compose.yaml
new file mode 100644
index 0000000..6c8f8cd
--- /dev/null
+++ b/nix/doodle/api/docker-compose.yaml
@@ -0,0 +1,22 @@
+version: "3.8"
+services:
+ db:
+ image: mysql
+ ports:
+ - "3306:3306"
+ environment:
+ - MYSQL_ROOT_PASSWORD=root
+ - MYSQL_DATABASE=tlc
+ - MYSQL_USER=tlc
+ - MYSQL_PASSWORD=tlc
+ etherpad:
+ image: etherpad/etherpad:latest
+ ports:
+ - "9001:9001"
+ volumes:
+ - ./APIKEY.txt:/opt/etherpad-lite/APIKEY.txt
+ mail:
+ image: bytemark/smtp
+ restart: always
+ ports:
+ - "2525:25"
diff --git a/nix/doodle/api/mvnw b/nix/doodle/api/mvnw
new file mode 100755
index 0000000..41c0f0c
--- /dev/null
+++ b/nix/doodle/api/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/nix/doodle/api/mvnw.cmd b/nix/doodle/api/mvnw.cmd
new file mode 100644
index 0000000..8611571
--- /dev/null
+++ b/nix/doodle/api/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/nix/doodle/api/pom.xml b/nix/doodle/api/pom.xml
new file mode 100644
index 0000000..069e43c
--- /dev/null
+++ b/nix/doodle/api/pom.xml
@@ -0,0 +1,249 @@
+
+