Wierzę, że każdy większy projekt powinien korzystać z dobrodziejstw CI i CD. Czasami jednak podczas konfigurowania pipeline
-ów okazuje się, że nie jest to takie łatwe. W tym poście chciałbym przedstawić problem, na który natknąłem się kilka dni temu.
Gitlab i prywatne repozytoria
Narzucenie odpowiedniego kontekstu mojej historii wymaga cofnięcia się w czasie. Jakieś trzy miesiące temu przeczytałem posta na blogu Gitlab-a. Dowiedziałem się, że na ich własnej platformie www, można mieć nielimitowaną ilość prywatnych repozytoriów, które można współdzielić z nieograniczoną ilością użytkowników. Dodatkowo, całkowicie za darmo, jest dostępna usługa CI (Continuous integration).
Tak mi graj!
- powiedziałem sobie. Ruszyłem do przenoszenia projektów z Github-a i Bitbucket-a. Dzięki temu, że Gitlab ma możliwość importowania projektów z w/w serwisów, zajęło mi to maksymalnie 15 minut.
CI dla opornych
Po zaimportowaniu projektów spróbowałem swoich sił z konfiguracją pipeline-ów dla testowego projektu. Mając już jakieś doświadczenia z Travis CI obyło się bez większego bólu. Kilka minut czytania dokumentacji i skończyłem z plikiem .gitlab-ci.yml
wyglądającym tak:
image: node:6.9.4
all_tests:
script:
- npm install
- npm run test
No dobra, lecimy z tym... Gitlabie... odpalaj!
Wszystko potoczyło się bardzo szybko, commit, push, już jest na serwerze... W panelu widzę, że zadanie zostało utworzone i dodane do kolejki, co znaczy, że maszyna ruszyła.
Tak! Przeszło!
Chwilę to trwało, jednak tym razem wszystko zakończyło się happy end-em. Moje testy przeszły... ponieważ musiały. Jedyny plik testowy odpalony za pomocą Mocha
wyglądał następująco:
var module = require('../index.js');
var expect = require('chai').expect;
describe('dummy test', () => {
it('should work', () => {
expect(1).to.equal(1);
});
});
To po prostu musiało zadziałać :)
Zadowolony z małego zwycięstwa postanowiłem ustawić pipeline dla większego projektu.
Pierwsze kłody pod nogi
Problemy pojawiły się dopiero podczas próby konfiguracji CI dla projektu rozproszonego na kilka repozytoria.
Kontener Docker-a nie mógł uzyskać dostępu do paczki ładowanej przez npm-a z prywatnego repozytorium na Gitlabie.
Skutkowało to przyjściem niezbyt pożądanego mail-a z Gitlab-a
Swoją drogą jest to bardzo fajny feature. Z default-u maile są wysyłane tylko do człowieka, który wystartował pipeline-a. Dzięki temu, jak nie chcemy, to nie zaśmiecamy skrzynki pocztowej pozostałym członkom projektu.
Wszystko da się naprawić
Jestem programistą, moja praca polega na rozwiązywaniu problemów
Cały problem polegał na tym, że kontener Docker-a (utworzony przez Gitlab-a), nie miał żadnych uprawnień do ściągnięcia paczki npm-a, która była zdefiniowana w dependencjach głównego projektu.
Wszystko czego brakowało to klucz ssh, który by to umożliwił.
A może by tak stworzyć fikcyjnego użytkownika, któremu wygenerowałbym klucze ssh i podpiął do projektów?
- pomyślałem. Prawda jest taka, że nawet o ile takie rozwiązanie by zadziałało, to bezpieczeństwo by siadło. Musiałbym trzymać klucze tego fikcyjnego użytkownika w repozytorium. W przypadku testów trzeba nadpisać je na maszynie, na której zostały odpalone testy. Później, po zakończeniu testów, należałoby przywracać... Przecież CI został stworzony po to, aby ułatwiać życie programistom, a nie je utrudniać.
Wróciłem do przeszukiwania dokumentacji Gitlab-a. Trafiłem na rozdział dotyczący kluczy SSH wewnątrz kontenera. Okazała się, że klucz publiczny można dodać do zmiennej środowiskowej i za pomocą kilku linux-owych komend ustawi go jako klucz główny. Po przeczytaniu tego rozdziału, mój plik .gitlab-ci.yml
zaczął wyglądać tak:
image: node:6.9.4
before_script:
# install ssh-agent
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
# run ssh-agent
- eval $(ssh-agent -s)
# add ssh key stored in SSH_PRIVATE_KEY variable to the agent store
- ssh-add <(echo "$SSH_KEY")
# disable host key checking (NOTE: makes you susceptible to man-in-the-middle attacks)
# WARNING: use only in docker container, if you use it with shell you will overwrite your user's ssh config
- mkdir -p ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
all_tests:
script:
- npm install
- npm run test
Prawdę mówiąc, nic to nie zmieniło. Pipeline jak nie przechodził, tak nie zaczął, jednak czułem, że jestem już jeden krok bliżej do rozwiązania tej zagadki.
Pierwsze co musiałem zrobić to ustawić tą zmienną środowiskową za pomocą panelu, do którego można przejść z menu znajdującego się w prawym górnym rogu, tuż pod avatarem konta. Sekcja do jakiej musiałem się dostać nazywa się CI/CD Pipelines.
Panel jest prosty jak budowa cepa. Trzeba w nim uzupełnić następujące pola:
- Key: Nazwa naszej zmiennej środowiskowej, w moim przypadku było to "SSH_KEY"
- Value: Klucz publiczny, który zostanie wykorzystany do ściągania paczek
Następnie musiałem przejść do projektu, który służył za moją dependencję. Tam, za pomocą tego samego menu, trzeba dostać się do sekcji `Deploy Keys`, gdzie musiałem podać klucz prywatny.
Koniec walki
W ten oto sposób osiągnąłem swój cel. Projekt zaczął się budować poprawnie. Obok commit-ów miałem zielonego badge-a, który informował mnie o poprawnym przejściu testów. Maile zaczęły świecić na zielono (zamiast na czerwono). Normalnie bajka :)