рдкреБрд▓реБрдореАрд╕рд╣ рдХреЛрдб рдореНрд╣рдгреВрди рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛рдВрдЪреА рдЪрд╛рдЪрдгреА рдХрд░рдгреЗ. рднрд╛рдЧ 1

рд╢реБрдн рджреБрдкрд╛рд░ рдорд┐рддреНрд░рд╛рдВрдиреЛ. рджрд░рд╛рдиреЗ рдирд╡реАрди рдкреНрд░рд╡рд╛рд╣ рд╕реБрд░реВ рд╣реЛрдгреНрдпрд╛рдЪреНрдпрд╛ рдЕрдкреЗрдХреНрд╖реЗрдиреЗ "DevOps рд╕рд░рд╛рд╡ рдЖрдгрд┐ рд╕рд╛рдзрдиреЗ" рдЖрдореНрд╣реА рддреБрдордЪреНрдпрд╛рд╕реЛрдмрдд рдирд╡реАрди рдЕрдиреБрд╡рд╛рдж рд╢реЗрдЕрд░ рдХрд░рдд рдЖрд╣реЛрдд. рдЬрд╛.

рдкреБрд▓реБрдореАрд╕рд╣ рдХреЛрдб рдореНрд╣рдгреВрди рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛рдВрдЪреА рдЪрд╛рдЪрдгреА рдХрд░рдгреЗ. рднрд╛рдЧ 1

рдЗрдиреНрдлреНрд░рд╛рд╕реНрдЯреНрд░рдХреНрдЪрд░ рдХреЛрдбрд╕рд╛рдареА рдкреБрд▓реБрдореА рдЖрдгрд┐ рд╕рд╛рдорд╛рдиреНрдп-рдЙрджреНрджреЗрд╢реАрдп рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рднрд╛рд╖рд╛ рд╡рд╛рдкрд░рдгреЗ (рдХреЛрдб рдореНрд╣рдгреВрди рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛) рдЕрдиреЗрдХ рдлрд╛рдпрджреЗ рдкреНрд░рджрд╛рди рдХрд░рддрд╛рдд: рдХреМрд╢рд▓реНрдпреЗ рдЖрдгрд┐ рдЬреНрдЮрд╛рдирд╛рдЪреА рдЙрдкрд▓рдмреНрдзрддрд╛, рдЕреЕрдмреНрд╕реНрдЯреНрд░реЕрдХреНрд╢рдирджреНрд╡рд╛рд░реЗ рдХреЛрдбрдордзреАрд▓ рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХрд╛рдвреВрди рдЯрд╛рдХрдгреЗ, рддреБрдордЪреНрдпрд╛ рдЯреАрдорд▓рд╛ рдкрд░рд┐рдЪрд┐рдд рдЕрд╕рд▓реЗрд▓реА рд╕рд╛рдзрдиреЗ, рдЬрд╕реЗ рдХреА IDEs рдЖрдгрд┐ linters. рд╣реА рд╕рд░реНрд╡ рд╕реЙрдлреНрдЯрд╡реЗрдЕрд░ рдЕрднрд┐рдпрд╛рдВрддреНрд░рд┐рдХреА рд╕рд╛рдзрдиреЗ рдХреЗрд╡рд│ рдЖрдореНрд╣рд╛рд▓рд╛ рдЕрдзрд┐рдХ рдЙрддреНрдкрд╛рджрдХ рдмрдирд╡рдд рдирд╛рд╣реАрдд рддрд░ рдЖрдордЪреНрдпрд╛ рдХреЛрдбрдЪреА рдЧреБрдгрд╡рддреНрддрд╛ рджреЗрдЦреАрд▓ рд╕реБрдзрд╛рд░рддрд╛рдд. рдореНрд╣рдгреВрдирдЪ, рд╣реЗ рд╕реНрд╡рд╛рднрд╛рд╡рд┐рдХ рдЖрд╣реЗ рдХреА рд╕рд╛рдорд╛рдиреНрдп-рдЙрджреНрджреЗрд╢реАрдп рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рднрд╛рд╖рд╛рдВрдЪрд╛ рд╡рд╛рдкрд░ рдЖрдореНрд╣рд╛рд▓рд╛ рдЖрдгрдЦреА рдПрдХ рдорд╣рддреНрддреНрд╡рд╛рдЪрд╛ рд╕реЙрдлреНрдЯрд╡реЗрдЕрд░ рд╡рд┐рдХрд╛рд╕ рд╕рд░рд╛рд╡ рд╕рд╛рджрд░ рдХрд░рдгреНрдпрд╛рд╕ рдЕрдиреБрдорддреА рджреЗрддреЛ - рдЪрд╛рдЪрдгреА.

рдпрд╛ рд▓реЗрдЦрд╛рдд, рдЖрдореНрд╣реА рдЖрдордЪреНрдпрд╛ рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛-рдПрдЬ-рдХреЛрдб рддрдкрд╛рд╕рдгреНрдпрд╛рд╕рд╛рдареА рдкреБрд▓реБрдореА рдЖрдореНрд╣рд╛рд▓рд╛ рдХрд╢реА рдорджрдд рдХрд░рддреЗ рддреЗ рдкрд╛рд╣реВ.

рдкреБрд▓реБрдореАрд╕рд╣ рдХреЛрдб рдореНрд╣рдгреВрди рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛рдВрдЪреА рдЪрд╛рдЪрдгреА рдХрд░рдгреЗ. рднрд╛рдЧ 1

рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛рдВрдЪреА рдЪрд╛рдЪрдгреА рдХрд╛?

рддрдкрд╢рд┐рд▓рд╛рдд рдЬрд╛рдгреНрдпрд╛рдкреВрд░реНрд╡реА, рд╣рд╛ рдкреНрд░рд╢реНрди рд╡рд┐рдЪрд╛рд░рдгреЗ рдпреЛрдЧреНрдп рдЖрд╣реЗ: "рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛рдВрдЪреА рдЕрдЬрд┐рдмрд╛рдд рдЪрд╛рдЪрдгреА рдХрд╛?" рдпрд╛рдЪреА рдЕрдиреЗрдХ рдХрд╛рд░рдгреЗ рдЖрд╣реЗрдд рдЖрдгрд┐ рддреНрдпрд╛рдкреИрдХреА рдХрд╛рд╣реА рдпреЗрдереЗ рдЖрд╣реЗрдд:

  • рд╡реИрдпрдХреНрддрд┐рдХ рдлрдВрдХреНрд╢рдиреНрд╕ рдХрд┐рдВрд╡рд╛ рддреБрдордЪреНрдпрд╛ рдкреНрд░реЛрдЧреНрд░рд╛рдо рд▓реЙрдЬрд┐рдХрдЪреНрдпрд╛ рддреБрдХрдбреНрдпрд╛рдВрдЪреА рдпреБрдирд┐рдЯ рдЪрд╛рдЪрдгреА
  • рд╡рд┐рд╢рд┐рд╖реНрдЯ рдорд░реНрдпрд╛рджрд╛рдВрд╡рд┐рд░реВрджреНрдз рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛рдВрдЪреА рдЗрдЪреНрдЫрд┐рдд рд╕реНрдерд┐рддреА рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рддреЗ.
  • рд╕реНрдЯреЛрд░реЗрдЬ рдмрдХреЗрдЯрдЪреНрдпрд╛ рдПрдиреНрдХреНрд░рд┐рдкреНрд╢рдирдЪрд╛ рдЕрднрд╛рд╡ рдХрд┐рдВрд╡рд╛ рдЕрд╕реБрд░рдХреНрд╖рд┐рдд, рдЗрдВрдЯрд░рдиреЗрдЯрд╡рд░реВрди рд╡реНрд╣рд░реНрдЪреНрдпреБрдЕрд▓ рдорд╢рд┐рдиреНрд╕рдордзреНрдпреЗ рдкреНрд░рд╡реЗрд╢ рдЙрдШрдбрдгреЗ рдпрд╛рд╕рд╛рд░рдЦреНрдпрд╛ рд╕рд╛рдорд╛рдиреНрдп рддреНрд░реБрдЯреА рд╢реЛрдзрдгреЗ.
  • рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛рдВрдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рддрдкрд╛рд╕рдд рдЖрд╣реЗ.
  • рддрд░рддреВрдж рдХреЗрд▓реНрдпрд╛рдирдВрддрд░ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рддрдкрд╛рд╕рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдордЪреНрдпрд╛ "рдкреНрд░реЛрдЧреНрд░рд╛рдо рдХреЗрд▓реЗрд▓реНрдпрд╛" рдЗрдиреНрдлреНрд░рд╛рд╕реНрдЯреНрд░рдХреНрдЪрд░рдордзреНрдпреЗ рдЪрд╛рд▓реВ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдНрдкреНрд▓рд┐рдХреЗрд╢рди рд▓реЙрдЬрд┐рдХрдЪреА рд░рдирдЯрд╛рдЗрдо рдЪрд╛рдЪрдгреА рдХрд░рдгреЗ.
  • рдЬрд╕реЗ рдЖрдкрдг рдкрд╛рд╣реВ рд╢рдХрддреЛ, рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛ рдЪрд╛рдЪрдгреА рдкрд░реНрдпрд╛рдпрд╛рдВрдЪреА рд╡рд┐рд╕реНрддреГрдд рд╢реНрд░реЗрдгреА рдЖрд╣реЗ. рдкреЛрд▓реБрдореАрдХрдбреЗ рдпрд╛ рд╕реНрдкреЗрдХреНрдЯреНрд░рдорд╡рд░реАрд▓ рдкреНрд░рддреНрдпреЗрдХ рдмрд┐рдВрджреВрд╡рд░ рдЪрд╛рдЪрдгреА рдХрд░рдгреНрдпрд╛рдЪреА рдпрдВрддреНрд░рдгрд╛ рдЖрд╣реЗ. рдЪрд▓рд╛ рдкреНрд░рд╛рд░рдВрдн рдХрд░реВрдпрд╛ рдЖрдгрд┐ рддреЗ рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ рддреЗ рдкрд╛рд╣реВрдпрд╛.

рдпреБрдирд┐рдЯ рдЪрд╛рдЪрдгреА

Pulumi рдкреНрд░реЛрдЧреНрд░рд╛рдореНрд╕ JavaScript, Python, TypeScript рдХрд┐рдВрд╡рд╛ Go рд╕рд╛рд░рдЦреНрдпрд╛ рд╕рд╛рдорд╛рдиреНрдп-рдЙрджреНрджреЗрд╢реАрдп рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рднрд╛рд╖рд╛рдВрдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдЕрд╕рддрд╛рдд. рдореНрд╣рдгреВрди, рдпрд╛ рднрд╛рд╖рд╛рдВрдЪреА рд╕рдВрдкреВрд░реНрдг рд╢рдХреНрддреА, рддреНрдпрд╛рдВрдЪреНрдпрд╛ рд╕рд╛рдзрдирд╛рдВрдЪрд╛ рдЖрдгрд┐ рдЧреНрд░рдВрдерд╛рд▓рдпрд╛рдВрд╕рд╣, рдЪрд╛рдЪрдгреА рдлреНрд░реЗрдорд╡рд░реНрдХрд╕рд╣, рддреНрдпрд╛рдВрдЪреНрдпрд╛рдХрдбреЗ рдЙрдкрд▓рдмреНрдз рдЖрд╣реЗ. рдкреБрд▓реБрдореА рдорд▓реНрдЯреА-рдХреНрд▓рд╛рдЙрдб рдЖрд╣реЗ, рдпрд╛рдЪрд╛ рдЕрд░реНрде рддреЗ рдХреЛрдгрддреНрдпрд╛рд╣реА рдХреНрд▓рд╛рдЙрдб рдкреНрд░рджрд╛рддреНрдпрд╛рдХрдбреВрди рдЪрд╛рдЪрдгреАрд╕рд╛рдареА рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рдК рд╢рдХрддреЗ.

(рдпрд╛ рд▓реЗрдЦрд╛рдд, рдмрд╣реБрднрд╛рд╖рд┐рдХ рдЖрдгрд┐ рдорд▓реНрдЯреАрдХреНрд▓рд╛рдЙрдб рдЕрд╕реВрдирд╣реА, рдЖрдореНрд╣реА JavaScript рдЖрдгрд┐ Mocha рд╡рд╛рдкрд░рддреЛ рдЖрдгрд┐ AWS рд╡рд░ рд▓рдХреНрд╖ рдХреЗрдВрджреНрд░рд┐рдд рдХрд░рддреЛ. рддреБрдореНрд╣реА Python рд╡рд╛рдкрд░реВ рд╢рдХрддрд╛. unittest, рдЪрд╛рдЪрдгреА рдлреНрд░реЗрдорд╡рд░реНрдХ рдХрд┐рдВрд╡рд╛ рддреБрдореНрд╣рд╛рд▓рд╛ рдЖрд╡рдбрдд рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЗрддрд░ рдХреЛрдгрддреНрдпрд╛рд╣реА рдЪрд╛рдЪрдгреА рдлреНрд░реЗрдорд╡рд░реНрдХрд╡рд░ рдЬрд╛. рдЖрдгрд┐ рдЕрд░реНрдерд╛рддрдЪ, 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;

рд╣рд╛ рдореВрд▓рднреВрдд рдкреБрд▓реБрдореА рдкреНрд░реЛрдЧреНрд░рд╛рдо рдЖрд╣реЗ: рддреЛ рдлрдХреНрдд рдПрдХ EC2 рд╕реБрд░рдХреНрд╖рд╛ рдЧрдЯ рдЖрдгрд┐ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╡рд╛рдЯрдк рдХрд░рддреЛ. рддрдерд╛рдкрд┐, рд╣реЗ рд▓рдХреНрд╖рд╛рдд рдШреНрдпрд╛рд╡реЗ рдХреА рдпреЗрдереЗ рдЖрдкрдг рд╡рд░ рд╕рд╛рдВрдЧрд┐рддрд▓реЗрд▓реНрдпрд╛ рддреАрдирд╣реА рдирд┐рдпрдорд╛рдВрдЪреЗ рдЙрд▓реНрд▓рдВрдШрди рдХрд░рдд рдЖрд╣реЛрдд. рдЪрд▓рд╛ рдЪрд╛рдЪрдгреНрдпрд╛ рд▓рд┐рд╣реВрдпрд╛!

рд▓реЗрдЦрди рдЪрд╛рдЪрдгреНрдпрд╛

рдЖрдордЪреНрдпрд╛ рдЪрд╛рдЪрдгреНрдпрд╛рдВрдЪреА рд╕рд╛рдорд╛рдиреНрдп рд░рдЪрдирд╛ рдирд┐рдпрдорд┐рдд рдореЛрдЪрд╛ рдЪрд╛рдЪрдгреНрдпрд╛рдВрд╕рд╛рд░рдЦреА рджрд┐рд╕реЗрд▓:

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();
                }
            });
        });

рд╣реЗ рдирд┐рдпрдорд┐рдд рдЪрд╛рдЪрдгреАрд╕рд╛рд░рдЦреЗ рджрд┐рд╕рддреЗ, рдкрд░рдВрддреБ рд▓рдХреНрд╖рд╛рдд рдШреЗрдгреНрдпрд╛рд╕рд╛рд░рдЦреЗ рдХрд╛рд╣реА рд╡реИрд╢рд┐рд╖реНрдЯреНрдпрд╛рдВрд╕рд╣:

  • рдЖрдореНрд╣реА рдЙрдкрдпреЛрдЬрди рдХрд░рдгреНрдпрд╛рдкреВрд░реНрд╡реА рд╕рдВрд╕рд╛рдзрдирд╛рдЪреНрдпрд╛ рд╕реНрдерд┐рддреАрдЪреА рдЪреМрдХрд╢реА рдХрд░рдд рдЕрд╕рд▓реНрдпрд╛рдореБрд│реЗ, рдЖрдордЪреНрдпрд╛ рдЪрд╛рдЪрдгреНрдпрд╛ рдиреЗрд╣рдореА тАЬрдкреНрд▓реЕрдитАЭ (рдХрд┐рдВрд╡рд╛ тАЬрдкреВрд░реНрд╡рд╛рд╡рд▓реЛрдХрдитАЭ) рдореЛрдбрдордзреНрдпреЗ рдЪрд╛рд▓рд╡рд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд. рдЕрд╢рд╛ рдкреНрд░рдХрд╛рд░реЗ, рдЕрд╕реЗ рдмрд░реЗрдЪ рдЧреБрдгрдзрд░реНрдо рдЖрд╣реЗрдд рдЬреНрдпрд╛рдВрдЪреА рдореВрд▓реНрдпреЗ рдлрдХреНрдд рдкреБрдирд░реНрдкреНрд░рд╛рдкреНрдд рдХреЗрд▓реА рдЬрд╛рдгрд╛рд░ рдирд╛рд╣реАрдд рдХрд┐рдВрд╡рд╛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХреЗрд▓реА рдЬрд╛рдгрд╛рд░ рдирд╛рд╣реАрдд. рдпрд╛рдордзреНрдпреЗ рддреБрдордЪреНрдпрд╛ рдХреНрд▓рд╛рдЙрдб рдкреНрд░рджрд╛рддреНрдпрд╛рджреНрд╡рд╛рд░реЗ рдЧрдгрдирд╛ рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╕рд░реНрд╡ рдЖрдЙрдЯрдкреБрдЯ рдЧреБрдгрдзрд░реНрдорд╛рдВрдЪрд╛ рд╕рдорд╛рд╡реЗрд╢ рдЖрд╣реЗ. рдЖрдордЪреНрдпрд╛ рдЪрд╛рдЪрдгреНрдпрд╛рдВрд╕рд╛рдареА рд╣реЗ рд╕рд╛рдорд╛рдиреНрдп рдЖрд╣реЗ - рдЖрдореНрд╣реА рдлрдХреНрдд рдЗрдирдкреБрдЯ рдбреЗрдЯрд╛ рддрдкрд╛рд╕рддреЛ. рдПрдХрддреНрд░реАрдХрд░рдг рдЪрд╛рдЪрдгреНрдпрд╛рдВрдЪреНрдпрд╛ рдмрд╛рдмрддреАрдд рдЖрдореНрд╣реА рдирдВрддрд░ рдпрд╛ рд╕рдорд╕реНрдпреЗрдХрдбреЗ рдкрд░рдд рдпреЗрдК.
  • рд╕рд░реНрд╡ рдкреБрд▓реБрдореА рд╕рдВрд╕рд╛рдзрди рдЧреБрдгрдзрд░реНрдо рдЖрдЙрдЯрдкреБрдЯ рдЕрд╕рд▓реНрдпрд╛рдиреЗ, рдЖрдгрд┐ рддреНрдпрд╛рдкреИрдХреА рдЕрдиреЗрдХрд╛рдВрдЪреЗ рдореВрд▓реНрдпрдорд╛рдкрди рд╕рдордХрд╛рд▓рд┐рдХрдкрдгреЗ рдХреЗрд▓реЗ рдЬрд╛рддреЗ, рдЖрдореНрд╣рд╛рд▓рд╛ рдореВрд▓реНрдпрд╛рдВрдордзреНрдпреЗ рдкреНрд░рд╡реЗрд╢ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд▓рд╛рдЧреВ рдкрджреНрдзрдд рд╡рд╛рдкрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ. рд╣реЗ рд╡рдЪрди рдЖрдгрд┐ рдХрд╛рд░реНрдпрд╛рд╕рд╛рд░рдЦреЗрдЪ рдЖрд╣реЗ then .
  • рдПрд░рд░ рдореЗрд╕реЗрдЬрдордзреНрдпреЗ рд░рд┐рд╕реЛрд░реНрд╕ URN рджрд╛рдЦрд╡рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдореНрд╣реА рдЕрдиреЗрдХ рдЧреБрдгрдзрд░реНрдо рд╡рд╛рдкрд░рдд рдЕрд╕рд▓реНрдпрд╛рдиреЗ, рдЖрдореНрд╣рд╛рд▓рд╛ рдлрдВрдХреНрд╢рди рд╡рд╛рдкрд░рдгреНрдпрд╛рдЪреА рдЧрд░рдЬ рдЖрд╣реЗ pulumi.allрддреНрдпрд╛рдВрдирд╛ рдПрдХрддреНрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА.
  • рд╢реЗрд╡рдЯреА, рд╣реА рдореВрд▓реНрдпреЗ рдЕрд╕рд┐рдВрдХреНрд░реЛрдирд╕ рдкрджреНрдзрддреАрдиреЗ рдореЛрдЬрд▓реА рдЬрд╛рдд рдЕрд╕рд▓реНрдпрд╛рдиреЗ, рдЖрдореНрд╣рд╛рд▓рд╛ Mocha рдЪреЗ рдЕрдВрдЧрднреВрдд async рдХреЙрд▓рдмреЕрдХ рд╡реИрд╢рд┐рд╖реНрдЯреНрдп рд╡рд╛рдкрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓ 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();
                }
            });
        });

рдЗрддрдХрдВрдЪ. рдЖрддрд╛ рдЪрд╛рдЪрдгреНрдпрд╛ рдХрд░реВрдпрд╛!

рдЪрд╛рдЪрдгреНрдпрд╛ рдЪрд╛рд▓реВ рдЖрд╣реЗрдд

рдмрд░реНтАНрдпрд╛рдЪ рдкреНрд░рдХрд░рдгрд╛рдВрдордзреНрдпреЗ, рддреБрдореНрд╣реА рддреБрдордЪреНрдпрд╛ рдкрд╕рдВрддреАрдЪреНрдпрд╛ рдЪрд╛рдЪрдгреА рдлреНрд░реЗрдорд╡рд░реНрдХрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВрди рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдкрджреНрдзрддреАрдиреЗ рдЪрд╛рдЪрдгреНрдпрд╛ рдЪрд╛рд▓рд╡реВ рд╢рдХрддрд╛. рдкрд░рдВрддреБ рдкреБрд▓реБрдореАрдЪреЗ рдПрдХ рд╡реИрд╢рд┐рд╖реНрдЯреНрдп рдЖрд╣реЗ рдЬреНрдпрд╛рдХрдбреЗ рд▓рдХреНрд╖ рджреЗрдгреЗ рдпреЛрдЧреНрдп рдЖрд╣реЗ.
рд╕рд╛рдорд╛рдиреНрдпрддрдГ, рдкреБрд▓реБрдореА рдкреНрд░реЛрдЧреНрд░рд╛рдореНрд╕ рдЪрд╛рд▓рд╡рдгреНрдпрд╛рд╕рд╛рдареА, рдкреБрд▓рд┐рдореА рд╕реАрдПрд▓рдЖрдп (рдХрдорд╛рдВрдб рд▓рд╛рдЗрди рдЗрдВрдЯрд░рдлреЗрд╕) рд╡рд╛рдкрд░рд▓рд╛ рдЬрд╛рддреЛ, рдЬреЛ рднрд╛рд╖реЗрдЪрд╛ рд░рдирдЯрд╛рдЗрдо рдХреЙрдиреНрдлрд┐рдЧрд░ рдХрд░рддреЛ, рдкреБрд▓реБрдореА рдЗрдВрдЬрд┐рдирдЪреНрдпрд╛ рд▓реЙрдиреНрдЪрд╡рд░ рдирд┐рдпрдВрддреНрд░рдг рдареЗрд╡рддреЛ рдЬреЗрдгреЗрдХрд░реВрди рд╕рдВрд╕рд╛рдзрдирд╛рдВрд╕рд╣ рдСрдкрд░реЗрд╢рдиреНрд╕ рд░реЗрдХреЙрд░реНрдб рдХреЗрд▓реНрдпрд╛ рдЬрд╛рдК рд╢рдХрддрд╛рдд рдЖрдгрд┐ рдпреЛрдЬрдиреЗрдордзреНрдпреЗ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХреЗрд▓реНрдпрд╛ рдЬрд╛рдК рд╢рдХрддрд╛рдд. рддрдерд╛рдкрд┐, рдПрдХ рд╕рдорд╕реНрдпрд╛ рдЖрд╣реЗ. рддреБрдордЪреНрдпрд╛ рдЪрд╛рдЪрдгреА рдлреНрд░реЗрдорд╡рд░реНрдХрдЪреНрдпрд╛ рдирд┐рдпрдВрддреНрд░рдгрд╛рдЦрд╛рд▓реА рдЪрд╛рд▓рдд рдЕрд╕рддрд╛рдирд╛, CLI рдЖрдгрд┐ Pulumi рдЗрдВрдЬрд┐рдирдордзреНрдпреЗ рдХреЛрдгрддрд╛рд╣реА рд╕рдВрд╡рд╛рдж рд╣реЛрдгрд╛рд░ рдирд╛рд╣реА.

рдпрд╛ рд╕рдорд╕реНрдпреЗрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд рдЦрд╛рд▓реАрд▓ рдЧреЛрд╖реНрдЯреА рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ:

  • рдкреНрд░рдХрд▓реНрдкрд╛рдЪреЗ рдирд╛рд╡, рдЬреЗ рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓рдордзреНрдпреЗ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдЖрд╣реЗ PULUMI_NODEJS_PROJECT (рдХрд┐рдВрд╡рд╛, рдЕрдзрд┐рдХ рд╕рд╛рдорд╛рдиреНрдпрддрдГ, PULUMI__PROJECT ╨┤╨╗╤П ╨┤╤А╤Г╨│╨╕╤Е ╤П╨╖╤Л╨║╨╛╨▓).
    рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓рдордзреНрдпреЗ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╕реНрдЯреЕрдХрдЪреЗ рдирд╛рд╡ PULUMI_NODEJS_STACK (рдХрд┐рдВрд╡рд╛, рдЕрдзрд┐рдХ рд╕рд╛рдорд╛рдиреНрдпрддрдГ, PULUMI__ STACK).
    рддреБрдордЪреЗ рд╕реНрдЯреЕрдХ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕. рддреЗ рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓ рд╡рд╛рдкрд░реВрди рдорд┐рд│рд╡рддрд╛ рдпреЗрддрд╛рдд PULUMI_CONFIG рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪреЗ рд╕реНрд╡рд░реВрдк рдХреА/рдореВрд▓реНрдп рдЬреЛрдбреНрдпрд╛рдВрд╕рд╣ JSON рдирдХрд╛рд╢рд╛ рдЖрд╣реЗ.

    рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддрд╛рдирд╛ CLI/рдЗрдВрдЬрд┐рдирд╢реА рдЬреЛрдбрдгреА рдЙрдкрд▓рдмреНрдз рдирд╕рд▓реНрдпрд╛рдЪрд╛ рд╕рдВрдХреЗрдд рджреЗрдгрд╛рд░рд╛ рдХрд╛рд░реНрдпрдХреНрд░рдо рдЗрд╢рд╛рд░реЗ рдЬрд╛рд░реА рдХрд░реЗрд▓. рд╣реЗ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ рдХрд╛рд░рдг рддреБрдордЪрд╛ рдкреНрд░реЛрдЧреНрд░рд╛рдо рдкреНрд░рддреНрдпрдХреНрд╖рд╛рдд рдХрд╛рд╣реАрд╣реА рдЙрдкрдпреЛрдЬрд┐рдд рдХрд░рдгрд╛рд░ рдирд╛рд╣реА рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рддреЗ рдХрд░рд╛рдпрдЪреЗ рдирд╕реЗрд▓ рддрд░ рдЖрд╢реНрдЪрд░реНрдп рд╡рд╛рдЯреЗрд▓! рдкреБрд▓реБрдореАрд▓рд╛ рд╕рд╛рдВрдЧрдгреНрдпрд╛рд╕рд╛рдареА рдХреА рддреБрдореНрд╣рд╛рд▓рд╛ рд╣реЗрдЪ рд╣рд╡реЗ рдЖрд╣реЗ, рддреБрдореНрд╣реА рдЗрдиреНрд╕реНрдЯреЙрд▓ рдХрд░реВ рд╢рдХрддрд╛ PULUMI_TEST_MODE ╨▓ true.

    рдХрд▓реНрдкрдирд╛ рдХрд░рд╛ рдХреА рдЖрдореНрд╣рд╛рд▓рд╛ рдкреНрд░рдХрд▓реНрдкрд╛рдЪреЗ рдирд╛рд╡ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ my-ws, рд╕реНрдЯреЕрдХрдЪреЗ рдирд╛рд╡ dev, рдЖрдгрд┐ AWS рдкреНрд░рджреЗрд╢ us-west-2. рдореЛрдЪрд╛ рдЪрд╛рдЪрдгреНрдпрд╛ рдЪрд╛рд▓рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА рдХрдорд╛рдВрдб рд▓рд╛рдЗрди рдпрд╛рд╕рд╛рд░рдЦреА рджрд┐рд╕реЗрд▓:

    $ 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)

    рд╕рд░реНрд╡ рдХрд╛рд╣реА рдареАрдХ рдЪрд╛рд▓рд▓реЗ... рд╣реБрд░реНрд░реЗ! тЬУтЬУтЬУ

    рдЖрдЬрд╕рд╛рдареА рдПрд╡рдвреЗрдЪ рдЖрд╣реЗ, рдкрд░рдВрддреБ рдЖрдореНрд╣реА рднрд╛рд╖рд╛рдВрддрд░рд╛рдЪреНрдпрд╛ рджреБрд╕рд▒реНрдпрд╛ рднрд╛рдЧрд╛рдд рдЙрдкрдпреЛрдЬрди рдЪрд╛рдЪрдгреАрдмрджреНрджрд▓ рдмреЛрд▓реВ ЁЯШЙ

рд╕реНрддреНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛