{"id":31751,"date":"2021-01-13T00:00:00","date_gmt":"2021-01-12T23:00:00","guid":{"rendered":"https:\/\/blexin.com\/?p=31751"},"modified":"2021-01-13T09:39:52","modified_gmt":"2021-01-13T08:39:52","slug":"test-di-integrazione-con-pulumi-ed-azure-kubernetes-service","status":"publish","type":"post","link":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/","title":{"rendered":"Test di integrazione con Pulumi ed Azure Kubernetes Service"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"608\" data-attachment-id=\"31755\" data-permalink=\"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/attachment\/1_21_1105x656-blog-c\/\" data-orig-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656&amp;ssl=1\" data-orig-size=\"1105,656\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"1_21_1105x656-blog-C-\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1024%2C608&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?resize=1024%2C608&#038;ssl=1\" alt=\"\" class=\"wp-image-31755\" srcset=\"https:\/\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C--1024x608.png 1024w, https:\/\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C--980x582.png 980w, https:\/\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C--480x285.png 480w\" sizes=\"auto, (min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Per un nostro cliente stiamo svolgendo delle attivit\u00e0 di svecchiamento su alcuni prodotti, migrando gradualmente verso architetture a microservizi. Uno dei prodotti in questione ha  seguito per molto tempo la politica del &#8220;non toccare ci\u00f2 che funziona\u201d, con il risultato di diventare ingestibile con il passare del tempo. Alcune classi che rappresentano la logica business sono cresciute a dismisura, diventando molto complesse da modificare, con una copertura dei test insufficiente e con una logica di business condivisa tra i vari domini che convivono nella stessa codebase.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tutti questi fattori hanno reso difficile manutenere il sistema ed \u00e8 stato quindi deciso di evolvere gradualmente il codice in diversi servizi. Purtroppo per\u00f2 la logica business \u00e8 rimasta condivisa tra i domini e, attualmente, questa condivisione rappresenta il problema pi\u00f9 importante.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Per suddividere correttamente la logica tra vari (micro)servizi e continuare a deployare valore senza introdurre regressioni o malfunzionamenti inaspettati, c&#8217;\u00e8 bisogno di assicurarci che le modifiche apportate non cambino il comportamento atteso del software e, come sempre, la migliore arma \u00e8 avere una codebase ben coperta da test.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Gli <strong>Unit Test<\/strong> sono molto utili allo scopo, ma non bastano a renderci confidenti sulle modifiche alla logica. Ci serve di poter testare anche le interazioni tra due o pi\u00f9 (micro)servizi generati, effettuandoli in un ambiente&nbsp; temporaneo quanto pi\u00f9 simile possibile a quello di produzione, chiamato <strong>Ephemeral Environment<\/strong>. Ci serve cio\u00e8 una suite di <strong>Test di integrazione<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Per riuscire nel nostro intento \u00e8 molto utile il concetto di <strong>Infrastructure as Code<\/strong> (IaC), che consiste nel rappresentare l&#8217;infrastruttura (network, virtual machine, load balancer, e connection) in modo descrittivo, attraverso il codice. Seguendo il principio per cui lo stesso codice genera sempre la stessa libreria, lo stesso modello di IaC genera sempre lo stesso environment ogni volta che viene applicato. In questo modo, descrivendo&nbsp; il nostro ambiente target, possiamo condividere tra team sviluppo e operation la stessa configurazione.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Quello che abbiamo fatto \u00e8 stato utilizzare un framework di IaC in modo da ricreare un Ephemeral Environment, in cui i nostri servizi potessero girare simulando l&#8217;ambiente di produzione. Questo ci ha consentito anche di isolare&nbsp; tutte le dipendenze esterne fuori dallo scope della nostra logica. Ricreato l&#8217;ambiente, abbiamo costruito una suite di integration test che potesse validare la logica e semplificare l&#8217;aggiunta di nuove feature al prodotto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tra i tanto framework IaC sul mercato, \u00e8 stato scelto <a rel=\"noreferrer noopener\" href=\"https:\/\/www.pulumi.com\" target=\"_blank\"><strong>Pulumi<\/strong><\/a>, che mette a disposizione una CLI, un runtime, librerie e un servizio hostato che lavorano insieme. Consente di fare delivery, provisioning, updating e gestione di infrastrutture in Cloud, supportando diversi linguaggi, tra cui TypeScript, JavaScript, Python, Go, e.NET, e i loro tool nativi, librerie e package manager. Pulumi lavora su tutti o quasi i cloud provider o in alternativa anche su un&#8217;istanza di <strong>Kubernetes <\/strong>(k8s), volendo testare in locale possiamo quindi utilizzare anche <a href=\"https:\/\/minikube.sigs.k8s.io\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>minikube<\/strong><\/a>\u00a0 o <a rel=\"noreferrer noopener\" href=\"https:\/\/kind.sigs.k8s.io\/\" target=\"_blank\"><strong>kind<\/strong><\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un programma Pulumi presenta i seguenti componenti:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Program: un insieme di file scritti in uno dei linguaggi a scelta.<\/li><li>Project: una directory contenente il programma con i metadati associati.<\/li><li>Stack: rappresenta un&#8217;istanza del progetto. Spesso ogni stack corrisponde a differenti cloud environment.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Vediamo come eseguire il deploy con Pulumi su <strong>Azure Kubernetes Service<\/strong> (AKS) e come lanciare una suite di test nell&#8217;ambiente creato. Il codice completo lo potete <a href=\"https:\/\/github.com\/ntonjeta\/pulumi_integration_examples\" target=\"_blank\" rel=\"noreferrer noopener\">trovare qui<\/a>. Ho leggermente modificato il classico esempio di guestbook da deployare in Kubernetes, partendo da quello che <a href=\"https:\/\/kubernetes.io\/docs\/tutorials\/stateless-application\/guestbook\/\" target=\"_blank\" rel=\"noreferrer noopener\">trovate qui<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Creiamo il progetto Pulumi con il comando:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n`pulumi new kubernetes-typescript`\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">In questo modo, viene creato per noi il template di un progetto TypeScript che pu\u00f2 essere deployato su un cluster Kubernetes. Bisogna ora installare i componenti con il package manager del linguaggio selezionato, nel nostro caso npm, e inizializzare uno stack.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n```\nnpm install .\n\npulumi stack init azure\n```\n\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Nel template generato, troviamo il file `Pulumi.yaml` in cui sono settate le configurazioni generali.&nbsp; \u00c8 inoltre possibile avere delle configurazioni specifiche per stack e per settare una configurazione basta dare il comando:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n```\npulumi config set password &lt;password&gt; --secret\n```\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Prima di vedere come configurare il deploy diamo un occhio all&#8217;applicazione. Si tratta di un semplice guestbook che utilizza un cluster di Redis per memorizzare i messaggi lasciati dagli ospiti.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Per sviluppare il guestbook possiamo utilizzare un servizio Redis locale, magari deployato con Docker, ma per isolare completamente l\u2019ambiente ho preferito utilizzare un mock di Redis:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n```\nvar Redis = require(&#039;ioredis-mock&#039;)\nvar redis = new Redis({\n  data: {\n\t&#039;messages&#039;: &#x5B;message]\n  }\n})\n```\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Sviluppato il guestbook, ne realizziamo una immagine docker che andremo a caricare su un <strong>Azure Container Registry<\/strong> (ACR) collegato al Resource Group su cui \u00e8 presente anche l&#8217;istanza di <strong>AKS<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vediamo il DockerFile:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n```\nFROM node:slim\n\nWORKDIR \/app\n\n## Copy package.json and package-lock.json before copy other files for better build caching\nCOPY &#x5B;&quot;.\/package.json&quot;, &quot;.\/package-lock.json&quot;, &quot;\/app\/&quot;]\nRUN npm install\n\nCOPY &#x5B; &quot;.&quot;, &quot;\/app\/&quot; ]\nEXPOSE 3000\nCMD &#x5B;&quot;npm&quot;, &quot;start&quot;]\n```\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Eseguiamo la build del container e andiamo a caricarlo su ACR.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n```\ndocker build . --tag=guestbook:v1\ndocker tag guestbook:v1 &lt;my-acr-name&gt;.azurecr.io\/guestbook\ndocker push &lt;my-acr-name&gt;.azurecr.io\/guestbook\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Andiamo ora a vedere come Pulumi ci consente di descrivere l&#8217;architettura in modo programmatico. Nel progetto all&#8217;interno della cartella cloud, troviamo due file: index.ts e k8sjs.ts.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nindex.js\n\n```\nimport * as pulumi from &quot;@pulumi\/pulumi&quot;;\nimport * as k8sjs from &quot;.\/k8sjs&quot;;\n\nconst config = new pulumi.Config();\n\nconst redisLeader = new k8sjs.ServiceDeployment(&quot;redis-leader&quot;, {\n\timage: &quot;redis&quot;,\n\tports: &#x5B;6379],\n});\n\nconst redisReplica = new k8sjs.ServiceDeployment(&quot;redis-replica&quot;, {\n\timage: &quot;pulumi\/guestbook-redis-replica&quot;,\n\tports: &#x5B;6379],\n});\n\nconst frontend = new k8sjs.ServiceDeployment(&quot;frontend&quot;, {\n\treplicas: 3,\n\timage: &quot;&lt;my-acr-name&gt;.azurecr.io\/guestbook:latest&quot;,\n\tports: &#x5B;3000],\n\tallocateIpAddress: true,\n});\n\nexport let frontendIp = frontend.ipAddress;\n```\n\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Vediamo che index.ts descrive l&#8217;infrastruttura ad alto livello, sfruttando la classe <em>ServiceDeployment<\/em> creata in k8sjs.ts. Nel codice sono definite due istanze di Redis, di cui una leader e una replica. Inoltre, \u00e8 specificato un deployment della nostra applicazione, con replica impostato a 3 in modo da avere ridondanza in caso di fallimenti.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vediamo la classe <em>ServiceDeployment<\/em> :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n```\nimport * as k8s from &quot;@pulumi\/kubernetes&quot;;\nimport * as k8stypes from &quot;@pulumi\/kubernetes\/types\/input&quot;;\nimport * as pulumi from &quot;@pulumi\/pulumi&quot;;\n\n\/**\n * ServiceDeployment is an example abstraction that uses a class to fold together the common pattern of a Kubernetes Deployment and its associated Service object.\n *\/\nexport class ServiceDeployment extends pulumi.ComponentResource {\n\tpublic readonly deployment: k8s.apps.v1.Deployment;\n\tpublic readonly service: k8s.core.v1.Service;\n\tpublic readonly ipAddress?: pulumi.Output&lt;string&gt;;\n\n\tconstructor(name: string, args: ServiceDeploymentArgs, opts?: pulumi.ComponentResourceOptions) {\n    \tsuper(&quot;k8sjs:service:ServiceDeployment&quot;, name, {}, opts);\n\n    \tconst labels = { app: name };\n    \tconst container: k8stypes.core.v1.Container = {\n        \tname,\n        \timage: args.image,\n        \tresources: args.resources || { requests: { cpu: &quot;100m&quot;, memory: &quot;100Mi&quot; } },\n        \tenv: &#x5B;{ name: &quot;GET_HOSTS_FROM&quot;, value: &quot;dns&quot; }],\n        \tports: args.ports &amp;&amp; args.ports.map(p =&gt; ({ containerPort: p })),\n    \t};\n    \tthis.deployment = new k8s.apps.v1.Deployment(name, {\n        \tspec: {\n            \tselector: { matchLabels: labels },\n            \treplicas: args.replicas || 1,\n            \ttemplate: {\n                \tmetadata: { labels: labels },\n                \tspec: { containers: &#x5B; container ] },\n            \t},\n        \t},\n    \t}, { parent: this });\n\n    \tthis.service = new k8s.core.v1.Service(name, {\n        \tmetadata: {\n            \tname: name,\n            \tlabels: this.deployment.metadata.labels,\n        \t},\n        \tspec: {\n            \tports: args.ports &amp;&amp; args.ports.map(p =&gt; ({ port: p, targetPort: p })),\n            \tselector: this.deployment.spec.template.metadata.labels,\n            \ttype: args.allocateIpAddress ? ( &quot;LoadBalancer&quot;) : undefined,\n        \t},\n    \t}, { parent: this });\n\n    \tif (args.allocateIpAddress) {\n            \tthis.ipAddress = this.service.status.loadBalancer.ingress&#x5B;0].ip;\n           \t \n    \t}\n\t}\n}\n\nexport interface ServiceDeploymentArgs {\n\timage: string;\n\tresources?: k8stypes.core.v1.ResourceRequirements;\n\treplicas?: number;\n\tports?: number&#x5B;];\n\tallocateIpAddress?: boolean;\n}\n```\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">In questo codice \u00e8 presente una classe che utilizza le librerie di Pulumi per unire due concetti di k8s: il deployment e il service.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nel deployment, se passato, viene specificato il numero di repliche di default impostato a 1. Se specificato il flag <em>allocateIpAddress<\/em>, il service verr\u00e0 configurato come &#8220;LoadBalancer&#8221; e l&#8217;indirizzo IP assegnato sar\u00e0 esposto tra i campi della classe. In caso contrario, il tipo impostato ad <em>undefined<\/em> espone semplicemente l&#8217;indirizzo IP all&#8217;interno del cluster k8s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Con Pulumi \u00e8 quindi possibile sviluppare l&#8217;applicazione e definire il design dell&#8217;infrastruttura utilizzando lo stesso linguaggio, in questo caso JavaScript\/TypeScript.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Andiamo ora a deployare la nostra applicazione su Azure, operazione che richiede un unico comando da terminale: <strong>pulumi up<\/strong>. \u00c8 inoltre possibile avere una preview prima di eseguire il deploy con il comando <strong>pulumi preview<\/strong>, che ci mostra le modifiche che verranno apportate senza modificare l&#8217;attuale deploy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pulumi offre inoltre una suite di integration test in Go: andiamo a vederne un esempio di utilizzo in cui creiamo un ephemeral environment ed eseguiamo test.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n```\npackage examples\n\nimport (\n    &quot;bytes&quot;\n    &quot;encoding\/json&quot;\n    &quot;fmt&quot;\n    &quot;io&quot;\n    &quot;io\/ioutil&quot;\n    &quot;net\/http&quot;\n    &quot;os&quot;\n    &quot;path&quot;\n    &quot;testing&quot;\n    &quot;time&quot;\n\n    &quot;github.com\/pulumi\/pulumi\/pkg\/v2\/testing\/integration&quot;\n    &quot;github.com\/stretchr\/testify\/assert&quot;\n)\n\nfunc TestGuestbook(t *testing.T) {\n    cwd, err := os.Getwd()\n    if err != nil {\n   \t t.FailNow()\n    }\n\n    test := integration.ProgramTestOptions{\n   \t Dir:     \tpath.Join(cwd, &quot;cloud&quot;),\n   \t Quick:   \ttrue,\n   \t SkipRefresh: true,\n   \t ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {\n   \t\t var frontend = stack.Outputs&#x5B;&quot;frontendIp&quot;].(string)\n   \t\t checkHTTPResult(t, frontend)\n   \t\t checkMessageEndpoint(t, frontend)\n   \t },\n    }\n    integration.ProgramTest(t, &amp;test)\n}\n\nfunc checkHTTPResult(t *testing.T, output interface{}) bool {\n    hostname := &quot;http:\/\/&quot; + output.(string) + &quot;:3000&quot;\n    body := doGet(t, hostname, 5*time.Minute)\n    if !assert.Contains(t, body, &quot;&lt;html&gt;&quot;) {\n   \t return false\n    }\n    return true\n}\n\ntype dataMessage struct {\n    messages &#x5B;]string\n}\n\nfunc checkMessageEndpoint(t *testing.T, output interface{}) bool {\n    hostname := &quot;http:\/\/&quot; + output.(string) + &quot;:3000\/messages&quot;\n\n    message := dataMessage{\n   \t messages: &#x5B;]string{&quot;a message&quot;},\n    }\n\n    request, err := json.Marshal(message)\n    if !assert.Nil(t, err) {\n   \t return false\n    }\n\n    body := doPost(t, hostname, bytes.NewBuffer(request), 5*time.Minute)\n\n    body = doGet(t, hostname, 5*time.Minute)\n    if !assert.JSONEq(t, &quot;{\\&quot;messages\\&quot;: &#x5B;]}&quot;, body) {\n   \t return false\n    }\n    return true\n}\n\n```\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Dando semplicemente il comando <strong>go test<\/strong> da riga di comando si riesce a tirare su l&#8217;ambiente opportunamente configurato, eseguire i nostri test definiti nella option <em>ExtraRuntimeValidation<\/em> e infine tirare gi\u00f9 l&#8217;ambiente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Abbiamo visto come, utilizzando Pulumi, \u00e8 possibile tirare su un&#8217;infrastruttura semplicemente scrivendo codice JavaScript\/TypeScript e come eseguire test di integrazione utilizzando gli strumenti messi a disposizione dallo stesso framework.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Spero di avervi incuriosito.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Al prossimo articolo!<\/p>\n\n\n\n\n","protected":false},"excerpt":{"rendered":"<p>Vediamo come sfruttare Infrastructure as Code per il provisioning e il testing in un ambiente di integrazione<\/p>\n","protected":false},"author":196716249,"featured_media":31755,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"off","_et_pb_old_content":"<!-- wp:image {\"id\":31755,\"sizeSlug\":\"large\",\"linkDestination\":\"none\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https:\/\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C--1024x608.png\" alt=\"\" class=\"wp-image-31755\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Per un nostro cliente stiamo svolgendo delle attivit\u00e0 di svecchiamento su alcuni prodotti, migrando gradualmente verso architetture a microservizi. Uno dei prodotti in questione ha  seguito per molto tempo la politica del \"non toccare ci\u00f2 che funziona\u201d, con il risultato di diventare ingestibile con il passare del tempo. Alcune classi che rappresentano la logica business sono cresciute a dismisura, diventando molto complesse da modificare, con una copertura dei test insufficiente e con una logica di business condivisa tra i vari domini che convivono nella stessa codebase.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Tutti questi fattori hanno reso difficile manutenere il sistema ed \u00e8 stato quindi deciso di evolvere gradualmente il codice in diversi servizi. Purtroppo per\u00f2 la logica business \u00e8 rimasta condivisa tra i domini e, attualmente, questa condivisione rappresenta il problema pi\u00f9 importante.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Per suddividere correttamente la logica tra vari (micro)servizi e continuare a deployare valore senza introdurre regressioni o malfunzionamenti inaspettati, c'\u00e8 bisogno di assicurarci che le modifiche apportate non cambino il comportamento atteso del software e, come sempre, la migliore arma \u00e8 avere una codebase ben coperta da test.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Gli <strong>Unit Test<\/strong> sono molto utili allo scopo, ma non bastano a renderci confidenti sulle modifiche alla logica. Ci serve di poter testare anche le interazioni tra due o pi\u00f9 (micro)servizi generati, effettuandoli in un ambiente&nbsp; temporaneo quanto pi\u00f9 simile possibile a quello di produzione, chiamato <strong>Ephemeral Environment<\/strong>. Ci serve cio\u00e8 una suite di <strong>Test di integrazione<\/strong>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Per riuscire nel nostro intento \u00e8 molto utile il concetto di <strong>Infrastructure as Code<\/strong> (IaC), che consiste nel rappresentare l'infrastruttura (network, virtual machine, load balancer, e connection) in modo descrittivo, attraverso il codice. Seguendo il principio per cui lo stesso codice genera sempre la stessa libreria, lo stesso modello di IaC genera sempre lo stesso environment ogni volta che viene applicato. In questo modo, descrivendo&nbsp; il nostro ambiente target, possiamo condividere tra team sviluppo e operation la stessa configurazione.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Quello che abbiamo fatto \u00e8 stato utilizzare un framework di IaC in modo da ricreare un Ephemeral Environment, in cui i nostri servizi potessero girare simulando l'ambiente di produzione. Questo ci ha consentito anche di isolare&nbsp; tutte le dipendenze esterne fuori dallo scope della nostra logica. Ricreato l'ambiente, abbiamo costruito una suite di integration test che potesse validare la logica e semplificare l'aggiunta di nuove feature al prodotto.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Tra i tanto framework IaC sul mercato, \u00e8 stato scelto <a href=\"https:\/\/www.pulumi.com\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Pulumi<\/strong><\/a>, che mette a disposizione una CLI, un runtime, librerie e un servizio hostato che lavorano insieme. Consente di fare delivery, provisioning, updating e gestione di infrastrutture in Cloud, supportando diversi linguaggi, tra cui TypeScript, JavaScript, Python, Go, e.NET, e i loro tool nativi, librerie e package manager. Pulumi lavora su tutti o quasi i cloud provider o in alternativa anche su un'istanza di <strong>Kubernetes <\/strong>(k8s), volendo testare in locale possiamo quindi utilizzare anche <a href=\"https:\/\/kind.sigs.k8s.io\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>minikube<\/strong><\/a>&nbsp; o <a href=\"https:\/\/kind.sigs.k8s.io\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>kind<\/strong><\/a>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Un programma Pulumi presenta i seguenti componenti:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>Program: un insieme di file scritti in uno dei linguaggi a scelta.<\/li><li>Project: una directory contenente il programma con i metadati associati.<\/li><li>Stack: rappresenta un'istanza del progetto. Spesso ogni stack corrisponde a differenti cloud environment.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Vediamo come eseguire il deploy con Pulumi su <strong>Azure Kubernetes Service<\/strong> (AKS) e come lanciare una suite di test nell'ambiente creato. Il codice completo lo potete <a href=\"https:\/\/github.com\/ntonjeta\/pulumi_integration_examples\" target=\"_blank\" rel=\"noreferrer noopener\">trovare qui<\/a>. Ho leggermente modificato il classico esempio di guestbook da deployare in Kubernetes, partendo da quello che <a href=\"https:\/\/kubernetes.io\/docs\/tutorials\/stateless-application\/guestbook\/\" target=\"_blank\" rel=\"noreferrer noopener\">trovate qui<\/a>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Creiamo il progetto Pulumi con il comando:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code -->\n<pre class=\"wp-block-syntaxhighlighter-code\">`pulumi new kubernetes-typescript`<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>In questo modo, viene creato per noi il template di un progetto TypeScript che pu\u00f2 essere deployato su un cluster Kubernetes. Bisogna ora installare i componenti con il package manager del linguaggio selezionato, nel nostro caso npm, e inizializzare uno stack.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">```\nnpm install .\n\npulumi stack init azure\n```\n<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>Nel template generato, troviamo il file `Pulumi.yaml` in cui sono settate le configurazioni generali.&nbsp; \u00c8 inoltre possibile avere delle configurazioni specifiche per stack e per settare una configurazione basta dare il comando:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">```\npulumi config set password &lt;password> --secret\n```<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>Prima di vedere come configurare il deploy diamo un occhio all'applicazione. Si tratta di un semplice guestbook che utilizza un cluster di Redis per memorizzare i messaggi lasciati dagli ospiti.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Per sviluppare il guestbook possiamo utilizzare un servizio Redis locale, magari deployato con Docker, ma per isolare completamente l\u2019ambiente ho preferito utilizzare un mock di Redis:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">```\nvar Redis = require('ioredis-mock')\nvar redis = new Redis({\n  data: {\n\t'messages': [message]\n  }\n})\n```<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>Sviluppato il guestbook, ne realizziamo una immagine docker che andremo a caricare su un <strong>Azure Container Registry<\/strong> (ACR) collegato al Resource Group su cui \u00e8 presente anche l'istanza di <strong>AKS<\/strong>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Vediamo il DockerFile:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">```\nFROM node:slim\n\nWORKDIR \/app\n\n## Copy package.json and package-lock.json before copy other files for better build caching\nCOPY [\".\/package.json\", \".\/package-lock.json\", \"\/app\/\"]\nRUN npm install\n\nCOPY [ \".\", \"\/app\/\" ]\nEXPOSE 3000\nCMD [\"npm\", \"start\"]\n```<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>Eseguiamo la build del container e andiamo a caricarlo su ACR.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">```\ndocker build . --tag=guestbook:v1\ndocker tag guestbook:v1 &lt;my-acr-name>.azurecr.io\/guestbook\ndocker push &lt;my-acr-name>.azurecr.io\/guestbook<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>Andiamo ora a vedere come Pulumi ci consente di descrivere l'architettura in modo programmatico. Nel progetto all'interno della cartella cloud, troviamo due file: index.ts e k8sjs.ts.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">index.js\n\n```\nimport * as pulumi from \"@pulumi\/pulumi\";\nimport * as k8sjs from \".\/k8sjs\";\n\nconst config = new pulumi.Config();\n\nconst redisLeader = new k8sjs.ServiceDeployment(\"redis-leader\", {\n\timage: \"redis\",\n\tports: [6379],\n});\n\nconst redisReplica = new k8sjs.ServiceDeployment(\"redis-replica\", {\n\timage: \"pulumi\/guestbook-redis-replica\",\n\tports: [6379],\n});\n\nconst frontend = new k8sjs.ServiceDeployment(\"frontend\", {\n\treplicas: 3,\n\timage: \"&lt;my-acr-name>.azurecr.io\/guestbook:latest\",\n\tports: [3000],\n\tallocateIpAddress: true,\n});\n\nexport let frontendIp = frontend.ipAddress;\n```\n<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>Vediamo che index.ts descrive l'infrastruttura ad alto livello, sfruttando la classe <em>ServiceDeployment<\/em> creata in k8sjs.ts. Nel codice sono definite due istanze di Redis, di cui una leader e una replica. Inoltre, \u00e8 specificato un deployment della nostra applicazione, con replica impostato a 3 in modo da avere ridondanza in caso di fallimenti.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Vediamo la classe <em>ServiceDeployment<\/em> :<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">```\nimport * as k8s from \"@pulumi\/kubernetes\";\nimport * as k8stypes from \"@pulumi\/kubernetes\/types\/input\";\nimport * as pulumi from \"@pulumi\/pulumi\";\n\n\/**\n * ServiceDeployment is an example abstraction that uses a class to fold together the common pattern of a Kubernetes Deployment and its associated Service object.\n *\/\nexport class ServiceDeployment extends pulumi.ComponentResource {\n\tpublic readonly deployment: k8s.apps.v1.Deployment;\n\tpublic readonly service: k8s.core.v1.Service;\n\tpublic readonly ipAddress?: pulumi.Output&lt;string>;\n\n\tconstructor(name: string, args: ServiceDeploymentArgs, opts?: pulumi.ComponentResourceOptions) {\n    \tsuper(\"k8sjs:service:ServiceDeployment\", name, {}, opts);\n\n    \tconst labels = { app: name };\n    \tconst container: k8stypes.core.v1.Container = {\n        \tname,\n        \timage: args.image,\n        \tresources: args.resources || { requests: { cpu: \"100m\", memory: \"100Mi\" } },\n        \tenv: [{ name: \"GET_HOSTS_FROM\", value: \"dns\" }],\n        \tports: args.ports &amp;&amp; args.ports.map(p => ({ containerPort: p })),\n    \t};\n    \tthis.deployment = new k8s.apps.v1.Deployment(name, {\n        \tspec: {\n            \tselector: { matchLabels: labels },\n            \treplicas: args.replicas || 1,\n            \ttemplate: {\n                \tmetadata: { labels: labels },\n                \tspec: { containers: [ container ] },\n            \t},\n        \t},\n    \t}, { parent: this });\n\n    \tthis.service = new k8s.core.v1.Service(name, {\n        \tmetadata: {\n            \tname: name,\n            \tlabels: this.deployment.metadata.labels,\n        \t},\n        \tspec: {\n            \tports: args.ports &amp;&amp; args.ports.map(p => ({ port: p, targetPort: p })),\n            \tselector: this.deployment.spec.template.metadata.labels,\n            \ttype: args.allocateIpAddress ? ( \"LoadBalancer\") : undefined,\n        \t},\n    \t}, { parent: this });\n\n    \tif (args.allocateIpAddress) {\n            \tthis.ipAddress = this.service.status.loadBalancer.ingress[0].ip;\n           \t \n    \t}\n\t}\n}\n\nexport interface ServiceDeploymentArgs {\n\timage: string;\n\tresources?: k8stypes.core.v1.ResourceRequirements;\n\treplicas?: number;\n\tports?: number[];\n\tallocateIpAddress?: boolean;\n}\n```<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>In questo codice \u00e8 presente una classe che utilizza le librerie di Pulumi per unire due concetti di k8s: il deployment e il service.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Nel deployment, se passato, viene specificato il numero di repliche di default impostato a 1. Se specificato il flag <em>allocateIpAddress<\/em>, il service verr\u00e0 configurato come \"LoadBalancer\" e l'indirizzo IP assegnato sar\u00e0 esposto tra i campi della classe. In caso contrario, il tipo impostato ad <em>undefined<\/em> espone semplicemente l'indirizzo IP all'interno del cluster k8s.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Con Pulumi \u00e8 quindi possibile sviluppare l'applicazione e definire il design dell'infrastruttura utilizzando lo stesso linguaggio, in questo caso JavaScript\/TypeScript.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Andiamo ora a deployare la nostra applicazione su Azure, operazione che richiede un unico comando da terminale: <strong>pulumi up<\/strong>. \u00c8 inoltre possibile avere una preview prima di eseguire il deploy con il comando <strong>pulumi preview<\/strong>, che ci mostra le modifiche che verranno apportate senza modificare l'attuale deploy.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Pulumi offre inoltre una suite di integration test in Go: andiamo a vederne un esempio di utilizzo in cui creiamo un ephemeral environment ed eseguiamo test.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:syntaxhighlighter\/code {\"language\":\"csharp\"} -->\n<pre class=\"wp-block-syntaxhighlighter-code\">```\npackage examples\n\nimport (\n    \"bytes\"\n    \"encoding\/json\"\n    \"fmt\"\n    \"io\"\n    \"io\/ioutil\"\n    \"net\/http\"\n    \"os\"\n    \"path\"\n    \"testing\"\n    \"time\"\n\n    \"github.com\/pulumi\/pulumi\/pkg\/v2\/testing\/integration\"\n    \"github.com\/stretchr\/testify\/assert\"\n)\n\nfunc TestGuestbook(t *testing.T) {\n    cwd, err := os.Getwd()\n    if err != nil {\n   \t t.FailNow()\n    }\n\n    test := integration.ProgramTestOptions{\n   \t Dir:     \tpath.Join(cwd, \"cloud\"),\n   \t Quick:   \ttrue,\n   \t SkipRefresh: true,\n   \t ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {\n   \t\t var frontend = stack.Outputs[\"frontendIp\"].(string)\n   \t\t checkHTTPResult(t, frontend)\n   \t\t checkMessageEndpoint(t, frontend)\n   \t },\n    }\n    integration.ProgramTest(t, &amp;test)\n}\n\nfunc checkHTTPResult(t *testing.T, output interface{}) bool {\n    hostname := \"http:\/\/\" + output.(string) + \":3000\"\n    body := doGet(t, hostname, 5*time.Minute)\n    if !assert.Contains(t, body, \"&lt;html>\") {\n   \t return false\n    }\n    return true\n}\n\ntype dataMessage struct {\n    messages []string\n}\n\nfunc checkMessageEndpoint(t *testing.T, output interface{}) bool {\n    hostname := \"http:\/\/\" + output.(string) + \":3000\/messages\"\n\n    message := dataMessage{\n   \t messages: []string{\"a message\"},\n    }\n\n    request, err := json.Marshal(message)\n    if !assert.Nil(t, err) {\n   \t return false\n    }\n\n    body := doPost(t, hostname, bytes.NewBuffer(request), 5*time.Minute)\n\n    body = doGet(t, hostname, 5*time.Minute)\n    if !assert.JSONEq(t, \"{\\\"messages\\\": []}\", body) {\n   \t return false\n    }\n    return true\n}\n\n```<\/pre>\n<!-- \/wp:syntaxhighlighter\/code -->\n\n<!-- wp:paragraph -->\n<p>Dando semplicemente il comando <strong>go test<\/strong> da riga di comando si riesce a tirare su l'ambiente opportunamente configurato, eseguire i nostri test definiti nella option <em>ExtraRuntimeValidation<\/em> e infine tirare gi\u00f9 l'ambiente.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Abbiamo visto come, utilizzando Pulumi, \u00e8 possibile tirare su un'infrastruttura semplicemente scrivendo codice JavaScript\/TypeScript e come eseguire test di integrazione utilizzando gli strumenti messi a disposizione dallo stesso framework.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Spero di avervi incuriosito.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Al prossimo articolo!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:block {\"ref\":26349} \/-->\n\n<!-- wp:block {\"ref\":26545} \/-->","_et_gb_content_width":"","_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","_crdt_document":"","inline_featured_image":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_wpas_customize_per_network":false,"jetpack_post_was_ever_published":false},"categories":[688637374],"tags":[688637377,688637409],"class_list":["post-31751","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-azure","tag-testing"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Test di integrazione con Pulumi ed Azure Kubernetes Service - Blexin<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Test di integrazione con Pulumi ed Azure Kubernetes Service - Blexin\" \/>\n<meta property=\"og:description\" content=\"Vediamo come sfruttare Infrastructure as Code per il provisioning e il testing in un ambiente di integrazione\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/\" \/>\n<meta property=\"og:site_name\" content=\"Blexin\" \/>\n<meta property=\"article:published_time\" content=\"2021-01-12T23:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-01-13T08:39:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/i1.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656\" \/>\n\t<meta property=\"og:image:width\" content=\"1105\" \/>\n\t<meta property=\"og:image:height\" content=\"656\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Antonio Tammaro\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Scritto da\" \/>\n\t<meta name=\"twitter:data1\" content=\"Antonio Tammaro\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo di lettura stimato\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minuti\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/\"},\"author\":{\"name\":\"Antonio Tammaro\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#\\\/schema\\\/person\\\/6e574a2e13d47c9167c5cfb475d9bfc5\"},\"headline\":\"Test di integrazione con Pulumi ed Azure Kubernetes Service\",\"datePublished\":\"2021-01-12T23:00:00+00:00\",\"dateModified\":\"2021-01-13T08:39:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/\"},\"wordCount\":1089,\"image\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1\",\"keywords\":[\"Azure\",\"Testing\"],\"articleSection\":[\"Blog\"],\"inLanguage\":\"it-IT\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/\",\"url\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/\",\"name\":\"Test di integrazione con Pulumi ed Azure Kubernetes Service - Blexin\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1\",\"datePublished\":\"2021-01-12T23:00:00+00:00\",\"dateModified\":\"2021-01-13T08:39:52+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#\\\/schema\\\/person\\\/6e574a2e13d47c9167c5cfb475d9bfc5\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/#primaryimage\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1\",\"width\":1105,\"height\":656},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blexin.com\\\/it\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Test di integrazione con Pulumi ed Azure Kubernetes Service\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#website\",\"url\":\"https:\\\/\\\/blexin.com\\\/it\\\/\",\"name\":\"Blexin\",\"description\":\"Con noi \u00e8 semplice\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blexin.com\\\/it\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"it-IT\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#\\\/schema\\\/person\\\/6e574a2e13d47c9167c5cfb475d9bfc5\",\"name\":\"Antonio Tammaro\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ddb88ac3dfb067ae6f1592d451eab3587e41bbbe98325961161cc286844e8d0d?s=96&d=identicon&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ddb88ac3dfb067ae6f1592d451eab3587e41bbbe98325961161cc286844e8d0d?s=96&d=identicon&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ddb88ac3dfb067ae6f1592d451eab3587e41bbbe98325961161cc286844e8d0d?s=96&d=identicon&r=g\",\"caption\":\"Antonio Tammaro\"},\"url\":\"https:\\\/\\\/blexin.com\\\/it\\\/author\\\/antonio-tammaroblexin-com\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Test di integrazione con Pulumi ed Azure Kubernetes Service - Blexin","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/","og_locale":"it_IT","og_type":"article","og_title":"Test di integrazione con Pulumi ed Azure Kubernetes Service - Blexin","og_description":"Vediamo come sfruttare Infrastructure as Code per il provisioning e il testing in un ambiente di integrazione","og_url":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/","og_site_name":"Blexin","article_published_time":"2021-01-12T23:00:00+00:00","article_modified_time":"2021-01-13T08:39:52+00:00","og_image":[{"width":1105,"height":656,"url":"https:\/\/i1.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656","type":"image\/png"}],"author":"Antonio Tammaro","twitter_card":"summary_large_image","twitter_misc":{"Scritto da":"Antonio Tammaro","Tempo di lettura stimato":"8 minuti"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/#article","isPartOf":{"@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/"},"author":{"name":"Antonio Tammaro","@id":"https:\/\/blexin.com\/it\/#\/schema\/person\/6e574a2e13d47c9167c5cfb475d9bfc5"},"headline":"Test di integrazione con Pulumi ed Azure Kubernetes Service","datePublished":"2021-01-12T23:00:00+00:00","dateModified":"2021-01-13T08:39:52+00:00","mainEntityOfPage":{"@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/"},"wordCount":1089,"image":{"@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/#primaryimage"},"thumbnailUrl":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1","keywords":["Azure","Testing"],"articleSection":["Blog"],"inLanguage":"it-IT"},{"@type":"WebPage","@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/","url":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/","name":"Test di integrazione con Pulumi ed Azure Kubernetes Service - Blexin","isPartOf":{"@id":"https:\/\/blexin.com\/it\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/#primaryimage"},"image":{"@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/#primaryimage"},"thumbnailUrl":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1","datePublished":"2021-01-12T23:00:00+00:00","dateModified":"2021-01-13T08:39:52+00:00","author":{"@id":"https:\/\/blexin.com\/it\/#\/schema\/person\/6e574a2e13d47c9167c5cfb475d9bfc5"},"breadcrumb":{"@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/#primaryimage","url":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1","contentUrl":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1","width":1105,"height":656},{"@type":"BreadcrumbList","@id":"https:\/\/blexin.com\/it\/blog\/test-di-integrazione-con-pulumi-ed-azure-kubernetes-service\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blexin.com\/it\/"},{"@type":"ListItem","position":2,"name":"Test di integrazione con Pulumi ed Azure Kubernetes Service"}]},{"@type":"WebSite","@id":"https:\/\/blexin.com\/it\/#website","url":"https:\/\/blexin.com\/it\/","name":"Blexin","description":"Con noi \u00e8 semplice","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blexin.com\/it\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"it-IT"},{"@type":"Person","@id":"https:\/\/blexin.com\/it\/#\/schema\/person\/6e574a2e13d47c9167c5cfb475d9bfc5","name":"Antonio Tammaro","image":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/secure.gravatar.com\/avatar\/ddb88ac3dfb067ae6f1592d451eab3587e41bbbe98325961161cc286844e8d0d?s=96&d=identicon&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ddb88ac3dfb067ae6f1592d451eab3587e41bbbe98325961161cc286844e8d0d?s=96&d=identicon&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ddb88ac3dfb067ae6f1592d451eab3587e41bbbe98325961161cc286844e8d0d?s=96&d=identicon&r=g","caption":"Antonio Tammaro"},"url":"https:\/\/blexin.com\/it\/author\/antonio-tammaroblexin-com\/"}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2021\/01\/1_21_1105x656-blog-C-.png?fit=1105%2C656&ssl=1","jetpack_shortlink":"https:\/\/wp.me\/pcyUBx-8g7","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts\/31751","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/users\/196716249"}],"replies":[{"embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/comments?post=31751"}],"version-history":[{"count":23,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts\/31751\/revisions"}],"predecessor-version":[{"id":31923,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts\/31751\/revisions\/31923"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/media\/31755"}],"wp:attachment":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/media?parent=31751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/categories?post=31751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/tags?post=31751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}