Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Ini transkripnya pertunjukan pada DevOps-40 2020-03-18:

Mulai dari komit kedua, kode apa pun menjadi warisan, karena ide awal mulai menyimpang dari kenyataan pahit. Hal ini tidak baik atau buruk, ini adalah suatu hal yang sulit untuk diperdebatkan dan harus dijalani. Bagian dari proses ini adalah pemfaktoran ulang. Refactoring Infrastruktur sebagai Kode. Biarkan cerita dimulai tentang bagaimana melakukan refactor Ansible dalam setahun dan tidak menjadi gila.

Lahirnya Warisan

Hari #1: Pasien Nol

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Suatu ketika ada proyek bersyarat. Itu memiliki tim pengembangan Dev dan insinyur Ops. Mereka memecahkan masalah yang sama: cara menyebarkan server dan menjalankan aplikasi. Masalahnya adalah setiap tim memecahkan masalah ini dengan caranya sendiri. Pada proyek tersebut, diputuskan untuk menggunakan Ansible untuk menyinkronkan pengetahuan antara tim Pengembang dan Operasi.

Hari #89: Lahirnya Warisan

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Tanpa mereka sadari, mereka ingin melakukannya sebaik mungkin, namun ternyata itu hanya warisan. Bagaimana ini bisa terjadi?

  • Kita punya tugas mendesak di sini, ayo lakukan peretasan kotor lalu perbaiki.
  • Anda tidak perlu menulis dokumentasi dan semuanya jelas apa yang terjadi di sini.
  • Saya tahu Ansible/Python/Bash/Terraform! Lihat bagaimana aku bisa menghindar!
  • Saya seorang Pengembang Full Stack Overflow dan menyalin ini dari stackoverflow, saya tidak tahu cara kerjanya, tapi kelihatannya keren dan menyelesaikan masalah.

Akibatnya, Anda bisa mendapatkan jenis kode yang tidak dapat dipahami yang tidak ada dokumentasinya, tidak jelas apa fungsinya, apakah diperlukan, tetapi masalahnya adalah Anda perlu mengembangkannya, memodifikasinya, menambahkan kruk dan dukungan , membuat situasi menjadi lebih buruk.

- hosts: localhost
  tasks:
    - shell: echo -n Z >> a.txt && cat a.txt
      register: output
      delay: 1
      retries: 5
      until: not output.stdout.find("ZZZ")

Hari #109: Kesadaran akan masalah

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Model IaC yang awalnya disusun dan diimplementasikan tidak lagi memenuhi persyaratan pengguna/bisnis/tim lain, dan waktu untuk melakukan perubahan pada infrastruktur tidak lagi dapat diterima. Pada saat ini, muncul pemahaman bahwa inilah saatnya untuk mengambil tindakan.

pemfaktoran ulang IaC

Hari #139: Apakah Anda benar-benar memerlukan refactoring?

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Sebelum Anda terburu-buru melakukan refactor, Anda harus menjawab sejumlah pertanyaan penting:

  1. Mengapa Anda membutuhkan semua ini?
  2. Apakah kamu punya waktu?
  3. Apakah pengetahuan cukup?

Jika Anda tidak tahu cara menjawab pertanyaan, pemfaktoran ulang akan berakhir bahkan sebelum dimulai, atau mungkin akan bertambah buruk. Karena punya pengalaman( Apa yang Saya Pelajari dari Menguji 200 Baris Kode Infrastruktur), kemudian proyek menerima permintaan bantuan untuk memperbaiki peran dan menutupinya dengan tes.

Hari #149: Mempersiapkan refactoring

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Hal pertama adalah mempersiapkan. Putuskan apa yang akan kita lakukan. Untuk melakukan ini, kami berkomunikasi, menemukan area masalah dan mencari cara untuk menyelesaikannya. Kami mencatat konsep-konsep yang dihasilkan, misalnya artikel dalam pertemuan, sehingga ketika muncul pertanyaan β€œapa yang terbaik?” atau "mana yang benar?" Kami tidak tersesat. Dalam kasus kami, kami berpegang pada gagasan itu membagi dan memerintah: kami memecah infrastruktur menjadi potongan-potongan kecil/bata. Pendekatan ini memungkinkan Anda mengambil bagian infrastruktur yang terisolasi, memahami fungsinya, melakukan pengujian, dan mengubahnya tanpa takut merusak apa pun.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Ternyata pengujian infrastruktur menjadi landasannya dan di sini patut disebutkan piramida pengujian infrastruktur. Ide yang sama persis dengan yang sedang dikembangkan, tetapi untuk infrastruktur: kita beralih dari pengujian cepat murah yang memeriksa hal-hal sederhana, seperti lekukan, ke pengujian lengkap yang mahal yang menerapkan seluruh infrastruktur.

Upaya pengujian yang mungkin dilakukan

Sebelum saya menjelaskan bagaimana kami membahas pengujian yang mungkin dilakukan pada proyek ini, saya akan menjelaskan upaya dan pendekatan yang pernah saya gunakan sebelumnya untuk memahami konteks keputusan yang dibuat.

Hari No. -997 : Pemberian SDS

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Pertama kali saya menguji Ansible adalah pada proyek pengembangan SDS (Software Defined Storage). Ada artikel terpisah tentang topik ini
Cara mematahkan sepeda karena kruk saat menguji distribusi Anda, tapi singkatnya, kami mendapatkan piramida pengujian terbalik dan pengujian kami menghabiskan 60-90 menit pada satu peran, yang merupakan waktu yang lama. Dasarnya adalah tes e2e, mis. kami menerapkan instalasi lengkap dan kemudian mengujinya. Yang lebih menjengkelkan lagi adalah penemuan sepedanya sendiri. Namun harus saya akui, solusi ini berhasil dan memungkinkan rilis yang stabil.

Hari # -701: Dapur yang memungkinkan dan diuji

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Pengembangan ide pengujian Ansible adalah dengan menggunakan alat yang sudah jadi yaitu test kitchen / kitchen-ci dan inspec. Pilihan ditentukan oleh pengetahuan tentang Ruby (untuk lebih jelasnya lihat artikel di HabrΓ©: Apakah pemrogram YML bermimpi untuk menguji Ansible?) bekerja lebih cepat, sekitar 40 menit untuk 10 peran. Kami membuat paket mesin virtual dan menjalankan pengujian di dalamnya.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Secara umum, solusinya berhasil, tetapi ada beberapa endapan karena heterogenitas. Ketika jumlah orang yang diuji ditingkatkan menjadi 13 peran dasar dan 2 peran meta yang menggabungkan peran yang lebih kecil, lalu tiba-tiba pengujian mulai berjalan selama 70 menit, yang hampir 2 kali lebih lama. Sulit untuk membicarakan praktik XP (pemrograman ekstrem) karena... tidak ada yang mau menunggu 70 menit. Inilah alasan untuk mengubah pendekatan

Hari # -601: Kemungkinan dan molekul

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Secara konseptual, ini mirip dengan testkitchen, hanya saja kami memindahkan pengujian peran ke buruh pelabuhan dan mengubah tumpukan. Hasilnya, waktu dikurangi menjadi 20-25 menit stabil untuk 7 peran.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Dengan meningkatkan jumlah peran yang diuji menjadi 17 dan linting 45 peran, kami menjalankannya dalam 28 menit pada 2 budak jenkins.

Hari #167: Menambahkan tes yang mungkin ke proyek

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Kemungkinan besar, tugas pemfaktoran ulang tidak dapat dilakukan dengan tergesa-gesa. Tugasnya harus terukur sehingga Anda bisa memecahnya menjadi potongan-potongan kecil dan memakan gajah sepotong demi sepotong dengan satu sendok teh. Harus ada pemahaman apakah Anda bergerak ke arah yang benar, berapa lama lagi.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Secara umum, tidak masalah bagaimana cara melakukannya, Anda dapat menulis di selembar kertas, Anda dapat menempelkan stiker di lemari, Anda dapat membuat tugas di Jira, atau Anda dapat membuka Google Docs dan menuliskan status saat ini. di sana. Kakinya tumbuh karena prosesnya tidak langsung, akan memakan waktu lama dan melelahkan. Tidak mungkin ada orang yang ingin Anda kehabisan ide, lelah, dan kewalahan selama melakukan refactoring.

Pemfaktoran ulangnya sederhana:

  • Makan.
  • Tidur.
  • Kode.
  • tes IaC.
  • ulangi

dan kami mengulanginya sampai kami mencapai tujuan yang diinginkan.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Mungkin tidak mungkin untuk segera mulai menguji semuanya, jadi tugas pertama kita adalah memulai dengan linting dan memeriksa sintaksisnya.

Hari #181: Ahli Pembangunan Ramah Lingkungan

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Linting adalah langkah kecil pertama menuju Green Build Master. Ini tidak akan merusak apa pun, tetapi ini akan memungkinkan Anda untuk men-debug proses dan membuat build ramah lingkungan di Jenkins. Idenya adalah untuk mengembangkan kebiasaan di antara tim:

  • Tes merah itu buruk.
  • Saya datang untuk memperbaiki sesuatu dan pada saat yang sama membuat kodenya sedikit lebih baik daripada sebelum Anda.

Hari #193: Dari linting hingga pengujian unit

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Setelah membangun proses memasukkan kode ke dalam master, Anda dapat memulai proses peningkatan langkah demi langkah - mengganti linting dengan peran peluncuran, Anda bahkan dapat melakukannya tanpa idempotensi. Anda perlu memahami cara menerapkan peran dan cara kerjanya.

Hari #211: Dari pengujian unit hingga integrasi

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Ketika sebagian besar peran tercakup dalam pengujian unit dan semuanya terkait, Anda dapat melanjutkan dengan menambahkan pengujian integrasi. Itu. tidak menguji satu pun infrastruktur, namun kombinasi keduanya, misalnya konfigurasi instance lengkap.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Dengan menggunakan jenkins, kami membuat banyak tahapan yang menghubungkan peran/buku pedoman secara paralel, kemudian pengujian unit dalam container, dan terakhir pengujian integrasi.

Jenkins + Docker + Ansible = Tes

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

  1. Periksa repo dan buat tahapan pembangunan.
  2. Jalankan tahapan playbook lint secara paralel.
  3. Jalankan tahapan peran lint secara paralel.
  4. Jalankan tahapan peran pemeriksaan sintaksis secara paralel.
  5. Jalankan tahapan peran pengujian secara paralel.
    1. Peran serat.
    2. Periksa ketergantungan pada peran lain.
    3. Periksa sintaksis.
    4. Buat instance buruh pelabuhan
    5. Jalankan molekul/default/playbook.yml.
    6. Periksa idempotensi.
  6. Jalankan tes integrasi
  7. Finish

Hari #271: Faktor Bus

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Pada awalnya, refactoring dilakukan oleh sekelompok kecil yang terdiri dari dua atau tiga orang. Mereka meninjau kode di master. Seiring waktu, tim mengembangkan pengetahuan tentang cara menulis kode dan tinjauan kode berkontribusi pada penyebaran pengetahuan tentang infrastruktur dan cara kerjanya. Yang menarik di sini adalah para reviewer dipilih satu per satu, sesuai jadwal, yaitu. dengan tingkat kemungkinan tertentu Anda akan memasuki infrastruktur baru.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Dan di sini seharusnya nyaman. Akan lebih mudah untuk melakukan review, melihat dalam kerangka tugas apa yang telah dilakukan, dan sejarah diskusi. Kami telah mengintegrasikan jenkins + bitbucket + jira.

Namun dengan demikian, ulasan bukanlah obat mujarab; entah bagaimana, kami masuk ke dalam kode master, yang membuat kami gagal dalam pengujian:

- get_url:
    url: "{{ actk_certs }}/{{ item.1 }}"
    dest: "{{ actk_src_tmp }}/"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}/{{ item.1 }}"
    dest: "{{ actk_dst_tmp }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"

Kemudian mereka memperbaikinya, tetapi sedimennya tetap ada.

get_url:
    url: "{{ actk_certs }}/{{ actk_item }}"
    dest: "{{ actk_src_tmp }}/{{ actk_item }}"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}/{{ actk_item }}"
    dest: "{{ actk_dst_tmp }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"

Hari #311: Mempercepat tes

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Seiring waktu, ada lebih banyak pengujian, pembangunan berjalan lebih lambat, hingga satu jam dalam kasus terburuk. Di salah satu retro ada ungkapan seperti β€œbagus kalau ada tes, tapi lambat.” Akibatnya, kami mengabaikan pengujian integrasi pada mesin virtual dan mengadaptasinya untuk Docker agar lebih cepat. Kami juga mengganti testinfra dengan verifikator yang memungkinkan untuk mengurangi jumlah alat yang digunakan.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Sebenarnya, ada serangkaian tindakan:

  1. Beralih ke buruh pelabuhan.
  2. Hapus pengujian peran, yang diduplikasi karena ketergantungan.
  3. Tingkatkan jumlah budak.
  4. Perintah uji coba.
  5. Kemampuan untuk serat SEMUA secara lokal dengan satu perintah.

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Hasilnya, Pipeline di jenkins juga disatukan

  1. Hasilkan tahapan pembangunan.
  2. Lint semuanya secara paralel.
  3. Jalankan tahapan peran pengujian secara paralel.
  4. Finish.

Pelajaran yang dipetik

Hindari variabel global

Ansible menggunakan variabel global, ada sebagian solusi dalam formulir private_role_vars, tapi ini bukan obat mujarab.

Izinkan saya memberi Anda sebuah contoh. Mari kita lakukan role_a ΠΈ role_b

# cat role_a/defaults/main.yml
---
msg: a

# cat role_a/tasks/main.yml
---
- debug:
    msg: role_a={{ msg }}

# cat role_b/defaults/main.yml
---
msg: b

# cat role_b/tasks/main.yml
---
- set_fact:
    msg: b
- debug:
    msg: role_b={{ msg }}

- hosts: localhost
  vars:
    msg: hello
  roles:
    - role: role_a
    - role: role_b
  tasks:
    - debug:
        msg: play={{msg}}

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Lucunya, hasil dari buku pedoman akan bergantung pada hal-hal yang tidak selalu jelas, seperti urutan peran yang dicantumkan. Sayangnya, ini adalah sifat Ansible dan hal terbaik yang dapat dilakukan adalah menggunakan semacam kesepakatan, misalnya, dalam suatu peran, gunakan hanya variabel yang dijelaskan dalam peran tersebut.

BAD: menggunakan variabel global.

# cat roles/some_role/tasks/main.yml
---
debug:
  var: java_home

BAIK: V defaults tentukan variabel yang diperlukan dan kemudian gunakan hanya variabel tersebut.

# cat roles/some_role/defaults/main.yml
---
r__java_home:
 "{{ java_home | default('/path') }}"

# cat roles/some_role/tasks/main.yml
---
debug:
  var: r__java_home

Variabel peran awalan

BAD: menggunakan variabel global.

# cat roles/some_role/defaults/main.yml
---
db_port: 5432

BAIK: Dalam peran untuk variabel, gunakan variabel yang diawali dengan nama peran; hal ini, dengan melihat inventaris, akan memudahkan untuk memahami apa yang terjadi.

# cat roles/some_role/defaults/main.yml
---
some_role__db_port: 5432

Gunakan variabel kontrol loop

BAD: Gunakan variabel standar dalam loop item, jika tugas/buku pedoman ini disertakan di suatu tempat, hal ini dapat menyebabkan perilaku yang tidak terduga

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item }}"
      loop:
        - item1
        - item2

BAIK: Mendefinisikan ulang variabel dalam satu lingkaran melalui loop_var.

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item_name }}"
      loop:
        - item1
        - item2
      loop_control:
        loop_var: item_name

Periksa variabel masukan

Kami setuju untuk menggunakan awalan variabel; tidak akan berlebihan untuk memeriksa apakah awalan tersebut didefinisikan seperti yang kami harapkan dan, misalnya, tidak diganti dengan nilai kosong

BAIK: Periksa variabel.

- name: "Verify that required string variables are defined"
  assert:
    that: ahs_var is defined and ahs_var | length > 0 and ahs_var != None
    fail_msg: "{{ ahs_var }} needs to be set for the role to work "
    success_msg: "Required variables {{ ahs_var }} is defined"
  loop_control:
    loop_var: ahs_var
  with_items:
    - ahs_item1
    - ahs_item2
    - ahs_item3

Hindari kamus hash, gunakan struktur datar

Jika suatu peran mengharapkan hash/kamus di salah satu parameternya, maka jika kita ingin mengubah salah satu parameter turunan, kita perlu mengganti seluruh hash/kamus, yang akan meningkatkan kompleksitas konfigurasi.

BAD: Gunakan hash/kamus.

---
user:
  name: admin
  group: admin

BAIK: Gunakan struktur variabel datar.

---
user_name: admin
user_group: "{{ user_name }}"

Buat buku pedoman & peran idempoten

Peran dan pedoman harus idempoten, karena mengurangi penyimpangan konfigurasi dan rasa takut merusak sesuatu. Namun jika Anda menggunakan molekul, maka ini adalah perilaku default.

Hindari menggunakan modul shell perintah

Penggunaan modul shell menghasilkan paradigma deskripsi imperatif, bukan paradigma deklaratif, yang merupakan inti dari Ansible.

Uji peran Anda melalui molekul

Molekul adalah hal yang sangat fleksibel, mari kita lihat beberapa skenario.

Molekul Beberapa contoh

Π’ molecule.yml di bagian platforms Anda dapat menjelaskan banyak host yang dapat Anda terapkan.

---
    driver:
      name: docker
    platforms:
      - name: postgresql-instance
        hostname: postgresql-instance
        image: registry.example.com/postgres10:latest
        pre_build_image: true
        override_command: false
        network_mode: host
      - name: app-instance
        hostname: app-instance
        pre_build_image: true
        image: registry.example.com/docker_centos_ansible_tests
        network_mode: host

Oleh karena itu, tuan rumah ini kemudian dapat menjadi demikian converge.yml menggunakan:

---
- name: Converge all
  hosts: all
  vars:
    ansible_user: root
  roles:
    - role: some_role

- name: Converge db
  hosts: db-instance
  roles:
    - role: some_db_role

- name: Converge app
  hosts: app-instance
  roles:
    - role: some_app_role

Pemverifikasi yang memungkinkan

Dalam molekul dimungkinkan untuk menggunakan kemungkinan untuk memeriksa apakah instance telah dikonfigurasi dengan benar, terlebih lagi, ini telah menjadi default sejak rilis 3. Ini tidak sefleksibel testinfra/inspec, tapi kita dapat memeriksa apakah isi file sesuai dengan harapan kita:

---
- name: Verify
  hosts: all
  tasks:
    - name: copy config
      copy:
        src: expected_standalone.conf
        dest: /root/wildfly/bin/standalone.conf
        mode: "0644"
        owner: root
        group: root
      register: config_copy_result

    - name: Certify that standalone.conf changed
      assert:
        that: not config_copy_result.changed

Atau terapkan layanan, tunggu hingga tersedia dan lakukan tes asap:

---
  - name: Verify
    hosts: solr
    tasks:
      - command: /blah/solr/bin/solr start -s /solr_home -p 8983 -force
      - uri:
          url: http://127.0.0.1:8983/solr
          method: GET
          status_code: 200
        register: uri_result
        until: uri_result is not failed
        retries: 12
        delay: 10
      - name: Post documents to solr
        command: /blah/solr/bin/post -c master /exampledocs/books.csv

Masukkan logika kompleks ke dalam modul & plugin

Ansible menganjurkan pendekatan deklaratif, jadi ketika Anda melakukan percabangan kode, transformasi data, modul shell, kode menjadi sulit dibaca. Untuk mengatasi hal ini dan membuatnya tetap mudah dipahami, tidak ada salahnya untuk mengatasi kompleksitas ini dengan membuat modul Anda sendiri.

Ringkaslah Tip & Trik

  1. Hindari variabel global.
  2. Variabel peran awalan.
  3. Gunakan variabel kontrol loop.
  4. Periksa variabel masukan.
  5. Hindari kamus hash, gunakan struktur datar.
  6. Buat buku pedoman & peran idempoten.
  7. Hindari menggunakan modul shell perintah.
  8. Uji peran Anda melalui molekul.
  9. Masukkan logika kompleks ke dalam modul & plugin.

Kesimpulan

Bagaimana memulai pengujian Ansible, memfaktorkan ulang proyek dalam setahun dan tidak menjadi gila

Anda tidak bisa begitu saja melakukan refaktorisasi infrastruktur pada suatu proyek, bahkan jika Anda memiliki IaC. Ini adalah proses panjang yang membutuhkan kesabaran, waktu dan pengetahuan.

UPD1 2020.05.01 20:30 β€” Untuk pembuatan profil utama buku pedoman yang dapat Anda gunakan callback_whitelist = profile_tasks untuk memahami apa yang sebenarnya berhasil untuk waktu yang lama. Lalu kita melewatinya Klasik akselerasi yang mungkin. Anda juga dapat mencoba mitogen
UPD2 2020.05.03 16:34 - Versi Bahasa Inggris

Sumber: www.habr.com

Tambah komentar