اختبار البنية التحتية الخاصة بك كرمز مع Pulumi. الجزء 1

مساء الخير أيها الأصدقاء. تحسبا لبدء تدفق جديد في الدورة التدريبية "ممارسات وأدوات DevOps" ، فإننا نشارككم ترجمة جديدة. دعنا نذهب.



يوفر استخدام Pulumi ولغات البرمجة للأغراض العامة للبنية التحتية كرمز العديد من المزايا: امتلاك المهارات والمعرفة ، والقضاء على الكود في الشفرة من خلال التجريد ، والأدوات المألوفة لفريقك ، مثل IDEs و linters. جميع أدوات هندسة البرمجيات هذه لا تجعلنا أكثر إنتاجية فحسب ، بل أيضًا تحسين جودة الكود. لذلك ، من الطبيعي أن يتيح لك استخدام لغات البرمجة للأغراض العامة تقديم ممارسة مهمة أخرى في تطوير البرمجيات - الاختبار .

في هذه المقالة ، سننظر في كيفية مساعدة Pulumi في اختبار "البنية التحتية الخاصة بنا كرمز".



لماذا اختبار البنية التحتية؟


قبل الدخول في التفاصيل ، يجدر طرح السؤال: "لماذا نحتاج إلى اختبار البنية التحتية على الإطلاق؟" هناك العديد من الأسباب لهذا ، وهنا بعض منها:

  • وحدة اختبار الوظائف الفردية أو أجزاء من المنطق في البرنامج
  • تحقق من الحالة المطلوبة للبنية التحتية للتأكد من مطابقتها لقيود معينة.
  • الكشف عن الأخطاء الشائعة ، مثل عدم وجود تشفير دلو التخزين أو عدم الأمان ، يفتح الوصول من الإنترنت إلى الأجهزة الافتراضية.
  • التحقق من توفير البنية التحتية.
  • إجراء اختبار وقت التشغيل لمنطق التطبيق الذي يعمل داخل البنية التحتية "المبرمجة" للتحقق من الصحة بعد التزويد.
  • كما نرى ، هناك مجموعة واسعة من خيارات اختبار البنية التحتية. لدى Polumi آليات للاختبار في كل نقطة في هذا الطيف. لنبدأ ونرى كيف يعمل.

اختبار وحدة


يتم إنشاء برامج Pulumi بلغات برمجة للأغراض العامة مثل JavaScript أو Python أو TypeScript أو Go. لذلك ، تتوفر القوة الكاملة لهذه اللغات لهم ، بما في ذلك أدواتهم ومكتباتهم ، بما في ذلك أطر الاختبار. Pulumi عبارة عن سحابة متعددة ، مما يعني القدرة على استخدام أي موفري سحابة للاختبار.

(في هذه المقالة ، على الرغم من كونها متعددة اللغات ومتعددة السحابية ، فإننا نستخدم JavaScript و Mocha ونركز على AWS. يمكنك استخدام Python unittest أو إطار اختبار Go أو أي إطار اختبار آخر تريده. وبالطبع ، تعمل Pulumi بشكل رائع مع Azure أو Google Cloud ، Kubernetes).

كما رأينا ، هناك العديد من الأسباب التي قد تحتاج إلى اختبار رمز البنية التحتية الخاصة بك. واحد منهم هو اختبار الوحدة المعتادة. نظرًا لأن الكود قد يحتوي على وظائف - على سبيل المثال ، لحساب CIDR ، وحساب الأسماء والعلامات ديناميكيًا ، إلخ. - ربما تريد اختبارها. هذا هو نفسه كتابة اختبارات وحدة منتظمة للتطبيقات في لغة البرمجة المفضلة لديك.
إذا قمت بتعقيد الأمور قليلاً ، فيمكنك التحقق من كيفية تخصيص البرنامج للموارد. للتوضيح ، دعونا نتخيل أننا بحاجة إلى إنشاء خادم EC2 بسيط ونريد أن نتأكد مما يلي:

  • مثيلات لها علامة Name .
  • يجب ألا تستخدم المثيلات البرنامج النصي المضمّن userData - يجب أن نستخدم AMI (الصورة).
  • لا ينبغي أن يكون هناك SSH مفتوحة على شبكة الإنترنت.

هذا المثال مكتوب على أساس مثال aws-js-webserver :

index.js:


 "use strict"; let aws = require("@pulumi/aws"); let group = new aws.ec2.SecurityGroup("web-secgrp", { ingress: [ { protocol: "tcp", fromPort: 22, toPort: 22, cidrBlocks: ["0.0.0.0/0"] }, { protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] }, ], }); let userData = `#!/bin/bash echo "Hello, World!" > index.html nohup python -m SimpleHTTPServer 80 &`; let server = new aws.ec2.Instance("web-server-www", { instanceType: "t2.micro", securityGroups: [ group.name ], // reference the group object above ami: "ami-c55673a0" // AMI for us-east-2 (Ohio), userData: userData // start a simple web server }); exports.group = group; exports.server = server; exports.publicIp = server.publicIp; exports.publicHostName = server.publicDns; 

هذا هو برنامج Pulumi الأساسي: إنه يخصص ببساطة مجموعة أمان EC2 ومثيله. ومع ذلك ، تجدر الإشارة إلى أننا ننتهك هنا القواعد الثلاثة المذكورة أعلاه. دعنا نكتب الاختبارات!

اختبارات الكتابة


سيبدو الهيكل العام لاختباراتنا بمثابة اختبارات Mocha العادية:

ec2tests.js


 test.js: let assert = require("assert"); let mocha = require("mocha"); let pulumi = require("@pulumi/pulumi"); let infra = require("./index"); describe("Infrastructure", function() { let server = infra.server; describe("#server", function() { // TODO(check 1):    Name. // TODO(check 2):    inline- userData. }); let group = infra.group; describe("#group", function() { // TODO(check 3):    SSH,   . }); }); 

الآن دعنا نكتب اختبارنا الأول: تأكد من أن المثيلات لها علامة Name . للتحقق من ذلك ، نحن ببساطة نحصل على كائن مثيل EC2 ونتحقق من خاصية tags المقابلة:

  // check 1:    Name. it("must have a name tag", function(done) { pulumi.all([server.urn, server.tags]).apply(([urn, tags]) => { if (!tags || !tags["Name"]) { done(new Error(`Missing a name tag on server ${urn}`)); } else { done(); } }); }); 

يبدو كاختبار منتظم ، ولكن مع بعض الميزات التي تستحق الاهتمام:

  • نظرًا لأننا نطلب حالة المورد قبل النشر ، يتم تنفيذ اختباراتنا دائمًا في وضع "الخطة" (أو "المعاينة") ، وبالتالي ، هناك العديد من الخصائص التي لن يتم تلقي قيمها أو لن يتم تحديدها ، ويشمل ذلك جميع خصائص المخرجات المحسوبة مزود الخدمة السحابية الخاص بك.لإجراء اختباراتنا ، هذا أمر طبيعي - نحن نتحقق فقط من بيانات الإدخال ، وسنعود إلى هذه المشكلة لاحقًا عندما يتعلق الأمر باختبارات التكامل.
  • نظرًا لأن جميع خصائص موارد Pulumi هي "مخرجات" ، ويتم حساب الكثير منها بشكل غير متزامن ، نحتاج إلى استخدام طريقة التطبيق للوصول إلى القيم. هذا مشابه جدا للوعود وبعد then .
  • نظرًا لأننا نستخدم العديد من الخصائص لإظهار المورد URN في رسالة الخطأ ، نحتاج إلى استخدام الدالة pulumi.all .
  • أخيرًا ، نظرًا لأن هذه القيم يتم حسابها بشكل غير متزامن ، نحتاج إلى استخدام ميزة Mocha المدمجة غير المتزامنة مع رد اتصال تم done أو إرجاع وعد.

بعد أن نقوم بتكوين كل شيء ، سيكون لدينا إمكانية الوصول إلى بيانات الإدخال كقيم JavaScript بسيطة. خاصية tags هي خريطة (مجموعة اقتران) ، لذلك سنتأكد من أنها (1) ليست خاطئة ، و (2) هناك مفتاح Name . انها بسيطة جدا والآن يمكننا التحقق من أي شيء!

الآن دعنا نكتب الشيك الثاني. هذا أبسط من ذلك:

  // check 2:    inline- userData. it("must not use userData (use an AMI instead)", function(done) { pulumi.all([server.urn, server.userData]).apply(([urn, userData]) => { if (userData) { done(new Error(`Illegal use of userData on server ${urn}`)); } else { done(); } }); }); 


وأخيرا ، سوف نكتب الاختبار الثالث. سيكون هذا الأمر أكثر تعقيدًا قليلاً لأننا نبحث عن قواعد تسجيل الدخول المرتبطة بمجموعة أمان ، والتي قد تكون كثيرة ، ويتراوح نطاق CIDR في هذه القواعد ، والتي قد تكون كثيرة أيضًا. لكننا تمكنا:

  // check 3:    SSH,   . it("must not open port 22 (SSH) to the Internet", function(done) { pulumi.all([ group.urn, group.ingress ]).apply(([ urn, ingress ]) => { if (ingress.find(rule => rule.fromPort == 22 && rule.cidrBlocks.find(block => block === "0.0.0.0/0"))) { done(new Error(`Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0) on group ${urn}`)); } else { done(); } }); }); 

هذا كل شيء. الآن دعنا نجري الاختبارات!

تشغيل الاختبارات


في معظم الحالات ، يمكنك إجراء الاختبارات بالطريقة المعتادة باستخدام إطار الاختبار الذي تختاره. ولكن هناك ميزة واحدة Pulumi يجب الانتباه إليها.
عادةً ما يتم استخدام Pulimi CLI (واجهة سطر الأوامر ، واجهة سطر الأوامر) لبدء تشغيل برامج Pulumi ، التي تحدد وقت تشغيل اللغة ، وتتحكم في بدء تشغيل محرك Pulumi ، بحيث يمكن تسجيل العمليات بالموارد وإدراجها في الخطة ، إلخ. ومع ذلك ، هناك مشكلة واحدة. عندما يتم تشغيله تحت سيطرة إطار الاختبار الخاص بك ، لن يكون هناك اتصال بين CLI ومحرك Pulumi.

للتغلب على هذه المشكلة ، نحتاج فقط إلى تحديد ما يلي:

  • اسم المشروع ، الموجود في متغير البيئة PULUMI_NODEJS_PROJECT (أو ، بشكل أعم ، PULUMI__PROJECT ).
    اسم المكدس المحدد في متغير البيئة PULUMI_NODEJS_STACK (أو ، بشكل أعم ، PULUMI__ STACK).
    متغيرات التكوين المكدس الخاص بك. يمكن الحصول عليها باستخدام PULUMI_CONFIG البيئة PULUMI_CONFIG وتنسيقها هو خريطة JSON مع أزواج المفتاح / القيمة.

    سيصدر البرنامج تحذيرات تشير إلى أنه في وقت التشغيل ، لا يتوفر اتصال بـ CLI / المحرك. هذا أمر مهم ، لأنه في الواقع ، لن ينشر البرنامج أي شيء ، وقد يكون ذلك مفاجأة إذا لم يكن هذا ما تريد القيام به! لإعلام Pulumi أن هذا هو ما تحتاجه تمامًا ، يمكنك تعيين PULUMI_TEST_MODE على " true .

    تخيل أننا بحاجة إلى تحديد اسم المشروع في my-ws ، واسم رصة dev ، ومنطقة AWS us-west-2 . سيبدو سطر الأوامر لتشغيل اختبارات Mocha كما يلي:

     $ PULUMI_TEST_MODE=true \ PULUMI_NODEJS_STACK="my-ws" \ PULUMI_NODEJS_PROJECT="dev" \ PULUMI_CONFIG='{ "aws:region": "us-west-2" }' \ mocha tests.js 

    القيام بذلك ، كما هو متوقع ، سيظهر لنا أن لدينا ثلاثة اختبارات سقطت!

     Infrastructure #server 1) must have a name tag 2) must not use userData (use an AMI instead) #group 3) must not open port 22 (SSH) to the Internet 0 passing (17ms) 3 failing 1) Infrastructure #server must have a name tag: Error: Missing a name tag on server urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 2) Infrastructure #server must not use userData (use an AMI instead): Error: Illegal use of userData on server urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 3) Infrastructure #group must not open port 22 (SSH) to the Internet: Error: Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0) on group 

    دعونا إصلاح برنامجنا:

     "use strict"; let aws = require("@pulumi/aws"); let group = new aws.ec2.SecurityGroup("web-secgrp", { ingress: [ { protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] }, ], }); let server = new aws.ec2.Instance("web-server-www", { tags: { "Name": "web-server-www" }, instanceType: "t2.micro", securityGroups: [ group.name ], // reference the group object above ami: "ami-c55673a0" // AMI for us-east-2 (Ohio), }); exports.group = group; exports.server = server; exports.publicIp = server.publicIp; exports.publicHostName = server.publicDns; 

    ثم أعد تشغيل الاختبارات:

     Infrastructure #server ✓ must have a name tag ✓ must not use userData (use an AMI instead) #group ✓ must not open port 22 (SSH) to the Internet 3 passing (16ms) 

    كل شيء سار على ما يرام ... الصيحة! ✓ ✓ ✓

    هذا كل شيء لهذا اليوم ، لكننا سنتحدث عن اختبار النشر في الجزء الثاني من الترجمة ؛-)

Source: https://habr.com/ru/post/ar463001/


All Articles