add latex thesis code

This commit is contained in:
Lorenzo Venerandi
2025-04-01 17:56:08 +02:00
parent c3b39b7284
commit 72c32427c4
69 changed files with 3208 additions and 0 deletions

241
latex/3_code_generation.tex Normal file
View File

@@ -0,0 +1,241 @@
In questo capitolo verrà approfondito il processo di generazione codice del progetto Go e del manifest wadm, che verranno impiegati poi per le fasi di Build e Deploy. Il primo passo è definire con precisione le struttura e la composizione dei file che fanno parte del progetto iniziale, quello che dovrà poi configurare l'utente finale che utilizzerà il framework, strutturato nel modo seguente:
\texttt{project/}
\dirtree{%
.1 workflow.yaml.
.1 tasks/.
.2 task1.go.
.1 gen/.
}
\section{Definizione Tasks}
In questa sezione ci si concentrerà sulle specifiche dei file task, cioè quelli situati nella cartella \texttt{tasks} del progetto e contenenti le funzioni che verranno eseguite dai moduli Wasm. Iniziamo fornendo la struttura base di ogni task file, mostrata nel Listing \ref{code:task_file}.
\begin{lstlisting}[language=Go, caption={Struttura base file task}, captionpos=b, label={code:task_file}]
package main
import (
...
)
...
func exec_task(arg string) string{
return ...
}
\end{lstlisting}
I requisiti da rispettare per la definizione della task sono:
\begin{itemize}
\item Il file deve avere un'estensione \texttt{.go}.
\item Il package deve essere impostato su main (stesso package del main fornito dal template).
\item Funzione di interfacciamento (cioè chiamata dal main) nominata \texttt{exec\_task}.
\item La funzione di interfacciamento deve accettare una stringa e restituire una stringa.
\end{itemize}
La funzione interfaccia accetta e restituisce una stringa per facilitare il più possibile l'utilizzo da parte di un utente finale, che non dovrà preoccuparsi di tradurre i tipi Go con i tipi Wasm specificati in WASI (cosa che viene gestita in ``background'' sul file main del progetto Go).\\
Una volta soddisfatti questi requisiti è possibile eseguire innumerevoli operazioni, come importare librerie, definire strutture e altre funzioni. Un esempio è mostrato nel Listing \ref{code:task_file_json}, in cui viene importata la libreria \texttt{encoding/json} e definita una struct \texttt{Request}.
\begin{lstlisting}[language=Go, caption={Task file ed integrazione con Json}, captionpos=b, label={code:task_file_json}, keepspaces=true]
package main
import (
"encoding/json"
)
type Request struct {
Data int
Name string
}
func exec_task(arg string) string{
// unmarshal the data
req := Request{}
json.Unmarshal([]byte(arg), &req)
// do some operations
...
// return the json string
json, _ := json.Marshal(req)
return string(json)
}
\end{lstlisting}
\section{Configurazione Workflow}
Passiamo ora alla codifica del file \texttt{workflow.yaml}, nel quale verranno effettivamente impostati i task e il loro comportamento.
\subsection{Specifica file workflow}
Il file deve essere correttamente formattato in Yaml e contenere i seguenti campi:
\begin{itemize}
\item \texttt{project\_name}: stringa contenente il nome del progetto, può contenere spazi e viene utilizzato principalmente nella CLI e nelle metriche.
\item \texttt{tasks}: lista contenente i vari componenti Wasm.
\end{itemize}
Andando nel dettaglio, ogni elemento della lista \texttt{tasks} deve contenere:
\begin{itemize}
\item \texttt{name}: stringa contenente il nome del componente, può contenere spazi e verrà impostato come nome dell'applicazione su wasmCloud.
\item \texttt{type}: stringa contenente il template da utilizzare come base.
\item \texttt{code}: stringa contenente il nome del file task da associare al componente Wasm in fase di generazione.
\item \texttt{targets}: lista contenente delle stringhe che rappresentano la label \texttt{host-type} associata ai nodi target di deployment dei componenti Wasm.
\item \texttt{source\_topic}: stringa contenente il topic da cui verranno ricevuti i dati.
\item \texttt{dest\_topic}: stringa contenente il topic a cui verranno inviati.
\item \texttt{component\_name}: stringa contenente il nome sintetico del componente, non può contenere spazi e funge da nome per l'OCI artifact associato.
\item \texttt{version}: versione del componente, specificata nel formato \texttt{x.x.x}. Insieme al component\_name conferisce il nome all'OCI artifact.
\end{itemize}
\subsection{Template}
I template sono dei progetti già sviluppati che forniscono certe funzionalità e fungono da base per il codice fornito dall'utente. Attualmente sono implementati due template:
\begin{itemize}
\item \texttt{processor\_nats}: comunicazione interamente basata su NATS: i dati arrivano da un topic e vengono inviati ad un topic.
\item \texttt{http\_producer\_nats}: i dati possono provenire sia da un topic NATS che da una richiesta POST effettuata al nodo in cui è deployata l'applicazione. Viene utilizzato infatti l'http provider per esporre un web server nella porta 8000.
\end{itemize}
Questo approccio consente una facile estendibilità del progetto: infatti potranno essere sviluppati ed aggiunti nuovi template per aumentare le funzionalità disponibili sul framework. Un altro template attualmente in sviluppo è quello per la scrittura dei dati su un DB relazionale.\\
Per comprendere meglio le modalità di utilizzo del file workflow analizziamo un esempio.
\begin{lstlisting}[language=yaml, caption={Esempio Workflow file con processor e producer}, captionpos=b, label={code:workflow_file}, keepspaces=true]
project_name: Temperature data analysis
tasks:
- name: Temp data conversion
type: http_producer_nats
code: convert.go
targets:
- edge
source_topic: living_room_celsius_data
dest_topic: living_room_kelvin_data
component_name: celsius_to_kelvin_conversion
version: 1.0.0
- name: Data filter
type: processor_nats
code: filter.go
targets:
- cloud
source_topic: living_room_kelvin_data
dest_topic: filtered_kelvin_data
component_name: temp_filter
version: 1.0.2
\end{lstlisting}
Nel file workflow riportato nel Listing \ref{code:workflow_file} vengono definiti due componenti:
\begin{itemize}
\item il primo si occupa di ricevere dati inviati da sensori ad un server http, convertire le temperature e pubblicarli in un topic NATS
\item il secondo filtra i risultati, magari rimuovendo errori di misurazione o aggregandoli
\end{itemize}
Questo semplice esempio vuole mostrare come con una configurazione ridotta sia possibile generare, compilare e distribuire un'applicazione che supporta casi d'uso applicabili al mondo IoT.
\section{Generazione codice}
Il codice che si occupa della generazione è situato sul package \texttt{code\_generator} ed è distribuito nei file \texttt{generator.py} e \texttt{template\_compiler.py}. All'interno del package sono presenti anche i template utilizzati come base per i progetti Go, situati nella cartella \texttt{templates}.
\texttt{code\_generator/}
\dirtree{%
.1 .
.1 generator.py.
.1 template\_compiler.py.
.1 templates/.
.2 processor\_nats/.
.2 producer\_nats/.
}
\subsection{Parsing Workflow}
Verrà ora analizzato nel dettaglio il processo di generazione del codice, partendo dalla fase di analisi del progetto fornito dall'utente. La prima operazione viene effettuata da \texttt{generator.py}, di cui riportiamo un estratto contenente il codice più rilevante.
\begin{lstlisting}[language=python, caption={Generazione del codice}, captionpos=b, label={code:code_gen}]
def generate(project_dir, registry_url, metrics, metrics_enabled):
...
# Parsing del file workflow.yaml
config = __parse_yaml(f"{project_dir}/workflow.yaml")
# Pulizia della cartella di output
__remove_dir_if_exists(output_dir)
os.makedirs(output_dir, exist_ok=True)
# Per ogni task all'interno del file workflow
for task in config['tasks']:
...
# Compilazione dei template
template_compiler.handle_task(task, output_dir)
# Copia del file task all'interno della cartella di output
shutil.copy2(f"{project_dir}/tasks/{task['code']}", f"{output_dir}/{task['component_name']}/{task['code']}")
if metrics_enabled:
gen_metrics['gen_time'] = '%.3f'%(end_time - start_time)
metrics['code_gen'] = gen_metrics
\end{lstlisting}
Come si può notare dal Listing \ref{code:code_gen} la funzione \texttt{generate} si occupa di parsare il file workflow, controllarne la validità e preparare la cartella di output.
\subsection{Compilazione template}
La generazione vera e propria del codice viene affidata a \texttt{template\_compiler} tramite la funzione \texttt{handle\_task}, la quale seleziona il template corretto in base a quello riportato nella configurazione, lo copia nella cartella di output e lo compila utilizzando Jinja.\\
Nei Listing \ref{code:jinja_after} e \ref{code:jinja_after} viene riportato un esempio di file (in questo caso una porzione di \texttt{wadm.yaml}) prima e dopo la compilazione del template tramite Jinja.\\
\noindent\begin{minipage}{.46\textwidth}
\begin{lstlisting}[caption=Template wadm.yaml, language=yaml, basicstyle=\scriptsize, frame=single, label={code:jinja_before}]
spec:
components:
- name: {{ component_name }}
type: component
properties:
image: "{{ registry_url }}/{{ component_name }}:{{ version }}"
traits:
- type: link
properties:
target: nats-processor
namespace: wasmcloud
package: messaging
interfaces: [consumer]
- type: spreadscaler
properties:
instances: 1
spread:
{%- set weight = (100 / (targets | length)) | int -%}
{% for target in targets %}
- name: {{ target }}
weight: {{ weight }}
requirements:
host-type: {{ target }}
{% endfor %}
\end{lstlisting}
\end{minipage}
\hfill
$\rightarrow$
\hfill
\begin{minipage}{.46\textwidth}
\begin{lstlisting}[caption=wadm.yaml compilato con Jinja, language=yaml, basicstyle=\scriptsize, frame=single, label={code:jinja_after}]
spec:
components:
- name: data_double_test1
type: component
properties:
image: "gitea.rebus.ninja/lore/data_double_test1:1.0.0"
traits:
- type: link
properties:
target: nats-processor
namespace: wasmcloud
package: messaging
interfaces: [consumer]
- type: spreadscaler
properties:
instances: 1
spread:
- name: cloud
weight: 100
requirements:
host-type: cloud
\end{lstlisting}
\end{minipage}
A questo punto viene copiato il file specificato sulla configurazione dalla cartella \texttt{task/} del progetto alla cartella di output. La funzione viene automaticamente agganciata all'handler specifico del file main del template (dato che appartengono allo stesso package).\\
L'intero processo di generazione è stato schematizzato nella Figura \ref{fig:code_gen_impl}:
\FloatBarrier
\begin{figure}[h]
\centering
\includegraphics[width=\textwidth]{img/schemi/schemi-implementazione-gen.drawio.pdf}
\caption{Processo generazione del codice}
\label{fig:code_gen_impl}
\end{figure}
\FloatBarrier