Gerrit에서 custom label 생성하기

Google의 code review tool인 Gerrit 사용시 기존의 label인 Code-Review, Verified 이외에 사용자 정의 label이 필요한 경우가 있다.

내 경험으로는 CI를 위해 custom label이 필요했었는데 특정 label의 point로 build 여부를 결정짓는데 사용했다.

Custom label이 필요한지 여부를 판단하는 것은 어디까지나 SCM 정책에 따라 달라질 수 있고 그 설정과 관리는 대부분의 사용자들과는 관계가 없는 부분이다.

 

1. Project clone

Custom label을 적용하고자 하는 특정 project (저장소)를 clone 한다.

Gerrit의 branches 메뉴를 보면 refs/meta/config 라는 reference가 존재하는데 일반적으로 master branch가 항상 있는 경우 refs/meta/config를 checkout 하지 않으면 보이지 않는다.

비어있는 project를 대상으로 하면 (branch가 없음) clone 하면 바로 유일한 reference인 refs/meta/config의 내용이 나타난다.

 

2. project.config 수정

refs/meta/config에는 project.config와 groups 파일이 있는데 (모든 경우를 살펴본 것은 아니라서 다른 파일이 추가되어 있을 수도 있음)

groups 파일에는 access control에 추가되어 있는 관계된 group 정보가 포함되어 있고 custom label 설정과는 관계가 없다.

project.config 파일을 열어보면 access control에 대한 내용과 project 일반적인 설정들이 포함되어 있는데 (description 등) 추가할 label에 대한 내용을 정의한다.

보통 이런 형태로 작성하면 되는데

[label “test label”]

function = NoOp

value = 0 Do Not Build

value = +1 Build Now

추가 상세 설정은 Gerrit 문서를 참조해서 진행한다.

function을 NoOp으로 설정한 경우 submit 여부를 결정하는데 test label이 영향을 미치지 않는다. (기존 정책에 따라 submit 여부가 결정됨)

value는 설정한대로 부여할 point의 범위가 결정되는 역할을 한다.

 

3. Commit / Push

소스코드와 동일하게 저장소 변경사항이 발생했으므로 commit을 하고 push를 한다. (Push 할 때 refs/meta/config를 지정하는 것은 기본)

Gerrit의 branches 에서 refs/meta/config의 내용을 확인해 변경사항이 제대로 반영되었는지 확인이 되면 Access Control 메뉴에서 설정할 때 내가 추가한 test label 설정이 가능한 것을 확인할 수 있다.

 

4. 결론

일반 개발자들이나 사용자들은 어쩌면 알 필요가 없는 기능이다.

하지만 이런 것도 Gerrit이 제공한다.

알아두면 CI 구성이나 SCM 정책을 좀 더 유연하거나 강화시키는데 도움이 될것이라고 본다.

국내에서는 이런 custom label 사용보다 사실 Gerrit이 제공하는 기본 기능, 정책들만이라도 잘 지킬 수 있기를 희망한다.

Azure에서 VHD download 없이 VM disk resizing

Azure에서 VM 생성시 disk size가 30GB 밖에 되질 않는다.

운영체제가 올라가는 disk이고 다른 data 들을 위한 공간은 disk를 하나 더 생성해서 붙여도 되겠지만 그럴 경우 linux는 추가로 mount를 해야 하고 내가 원하는 경로로 설정하기 위해서는 다른 작업을 거쳐야 할 수 있다.

이런 경우 운영체제가 올라가 있는 disk 자체를 resizing 할 필요성이 생기는데 (아무리 그래도 사실 30GB는 작다. 왜 최초 VM 생성시에 disk size를 지정할 수 없을까?) MSDN이나 기타 여러 곳의 자료들을 보면 내용이 좀 복잡하다. 보통 이런 것 같다.

1. VM 삭제

2. VHD download

3. VHD resizing (별도의 tool을 이용)

4. VHD upload

5. VM 다시 생성

다른 과정이야 그냥 넘어간다 하더라도 download / upload가 걸린다. 30GB 짜리를 다운로드하고 늘려서 다시 업로드를 해야 한다니.

그래서 더 조사를 해봤더니 역시 나와 같은 고민을 하는 사람이 있었고 그 양반이 괜찮은 솔루션을 제시해 놨다.

그 과정은 아래와 같다.

1. VM shutdown

2. VM 삭제 (삭제할 때 drive는 유지하는 옵션을 골라야 한다)

3. VM의 disk 삭제 (삭제할 때 VHD를 유지하는 옵션을 선택한다. 그럼 VHD 파일은 storage에 그대로 남아있게 됨)

4. WindowsAzureDiskResizer로 VHD resizing (Azure storage에 올라가 있는 그대로 resizing 됨)

5. Disk 생성 (이제부터는 거꾸로 반복, resizing된 VHD로 disk를 생성한다)

6. VM 생성 (5의 disk를 선택하고 원래의 VM 내용대로 설정한다)

 

결론

VHD 파일을 분석해서 괜찮은 툴을 만들고 그걸 오픈소스로 공개한 Maarten Balliauw 덕분에 좀 편해졌다.

그래도 VM 삭제하고 다시 생성해야 하는 과정이나 애초에 disk size 선택이 불가능한 부분은 개선이 필요해 보인다.

 

참조

1. Expanding an Existing Azure VM System Drive : 여기에 설명된 과정에 백업이 있는데 별로 중요하지 않거나 귀찮다면 skip 해도 무방하다

2. Tales from the trenches: resizing a Windows Azure virtual disk the smooth way

Random number와 가중치를 고려한 추첨기능 구현하기

복수의 아이템이 들어있는 자료구조에서 추첨을 해야하는 경우가 있다. 보통 추첨을 한다하면 무작위로 선택되어야 하는데 때에 따라서는 특정 아이템의 선택 확률을 높여야 할 수도 있다.

 

언어에 따라 조금 차이가 있기는 하지만 보통 random number 생성에 관계된 API들은 수학과 관련된 package로 묶여 있거나 연산에 특화된 기능들끼리 모여있다. 따라서 단순 random number 생성 이외의 기능, 가중치를 고려하는 부분은 직접 구현할 수 밖에 없다. 또는 가중치 고려가 가능하게 만들어진 오픈소스 라이브러리가 있을지도 모르겠는데 그리 복잡하지 않을 것 같아 직접 구현하기로 했다.

 

1. 가중치를 통해 선택확률 높이기

아래의 그림을 보면 윗 부분은 가중치가 1로 동일한 상태이고 아래는 특정 아이템에 가중치를 더 높임으로써 선택될 확률을 높인 상태이다. 칸으로 구별된 bar를 다루게 될 아이템이 담겨있는 자료구조로 보자. 각 칸에는 하나의 아이템이 담겨있고 좌측을 시작으로 우측으로 갈 수록 index가 높다고 하자.
random_weight

 

이렇게 하는 것이 어떻게 선택될 확률을 높일 수 있을까?

Java를 기준으로 random number 생성을 살펴보면 단순히 Math.random() 호출시 0과 1사이에 있는 double 값을 생성해서 돌려주게 되어있다. (정확히 0.0 이상 1.0 미만의 값) Random number의 range를 0 ~ total weight로 설정하면 위 그림의 bar 상단에서 좌측에서 우측으로 증가하는 1차원 그래프를 상상해볼 수 있다. 즉 생성된 random number 가 찍히는 점의 아래에 있는 아이템이 선택될 수 있다.

Weight가 동일한 경우는 그래프에서 차지하는 영역이 동일하기 때문에 random number가 동일한 확률로 생성된다면 아이템이 선택될 확률도 동일하다고 볼 수 있다. 반면에 특정 아이템의 weight가 높은 경우 차지하는 영역이 넓어지기 때문에 그만큼 그 영역안에서 random number가 생성될 가능성이 다른 영역에 비하면 높아진다.

 

2. 그렇다면 구현은 어떻게 할까?

위 그림과 그래프를 상상해보면 구현은 간단하다. 각 아이템이 갖는 가중치를 모두 더해서 Math.random() 등으로 random number를 생성할 때 0부터 가중치합 사이의 값이 생성되도록 한다. (역시 언어에 따라 다르지만 random number 생성시 seed 값을 설정할 수 있어야 random number 생성의 중복을 방지할 수 있다. Java의 경우 Math 보다는 Random class를 사용하는게 나을 것으로 판단됨) 그렇게 생성된 random number를 가지고 자료구조에서 index를 증가시키며 각각의 아이템이 가진 가중치를 뺀다. (가중치를 빼지 않고 더해서 하는 방법도 있을텐데 그 경우 random number와의 비교구문이 약간 달라져야 할 것이다) 그렇게 진행하다보면 어느 순간 random number가 0이나 음수가 되는데 그 때의 아이템이 가중치에 따라 선택된 아이템이 된다. Pseudo code로 간단하게 작성하면 아래와 같다.


List<Item> list = getItemList();

double total = getTotalWeight(list);

for(int i = 0; i < list.size(); i++) {

    total -= list.get(i).getWeight();

    if(total <= 0) {

        selectedIndex = i;

        break;

    }

}

return list.get(selectedIndex);

Release된 android app에서 HTTPS request시 발생한 문제

아래와 같은 단순한 구조를 가진 service에서 android app (Phonegap)이 AJAX로 API server와 SSL 통신을 하고 있다. SSL 설정은 reverse proxy 역할을 하는 NginX에 되어 있는 상태였는데 개발단계에서는 나타나지 않았던 문제가 app publish 이후에 발생해서 현상과 해결책을 정리해본다.

android_ssl

 

1. 현상

App에서 API call을 했는데도 오류나 정상 응답이 떨어지지 않는다. API server 쪽에서 로그를 확인해보니 호출된 기록이 없다. 전혀 호출되지 않는다고 판단할 수 있었다. 혹시나 싶어서 NginX의 로그도 확인해봤는데 access 자체를 하지 않는 것으로 보였다. (호출 후 access log, error log 모두 기록되는게 없음)

두번째 이상한 현상은 android app의 manifest에서 debuggable 설정에 따라 API 호출이 되거나 되지 않는다는 사실이었다. debuggable이 true (debug mode)인 경우에는 API 호출에 문제가 없었는데 publish를 위해 packaging 하려고 debuggable을 false로 바꾸면 호출이 불가능했다.

 

2. 조사

Stackoverflow에서 힌트를 찾았는데 자문자답한 사용자의 얘기로는 android app의 debug mode에서는 인증서 확인을 하지 않는데 그렇지 않은 경우 SSL 인증서를 엄격하게 검사한다고 한다. (http://stackoverflow.com/questions/14363994/android-debuggable-false-causing-jquery-ajax-post-to-fail-in-cordova-phonegap-ec) 그래서 링크된 ssl checker로 API service되는 url (NginX가 처음 받는 url)을 검사해봤더니 다른건 문제가 없었는데 인증서 체인이 제대로 되어있지 않았다. Root CA 인증서가 없다는 내용의 메세지와 함께.

 

3. 해결

Startssl에서 인증서를 발급받아 사용하고 있었는데 생각해보니 도메인에 대한 인증서와 그에 맞는 private key는 발급받았는데 startssl(CA)에 대한 인증서는 없었다. Startssl에서 안내하는대로 ca.pem, sub.class1.server.ca.pem을 추가로(내가 사용하는게 startssl의 class1 certificate) 받아서 기존의 인증서를 합쳐 인증서 체인을 만들고 NginX에 설정했다. (https://www.startssl.com/?app=42)

설정 이후에 다시 SSL 검사를 해보니 아래처럼 문제있던 부분이 정상. Android app도 수정없이 제대로 호출되는 걸 확인했다.

ssl_check

 

 

4. 결론

나의 경우엔 CA의 인증서 하나를 누락한 경우지만 개발할 때 self sign된 인증서를 사용하는 경우도 있는데 이 경우에도 publish 할 때에는 반드시 교체해야 한다. 이 부분은 사실 개발할 때 문제가 없었기 때문에 간과하고 넘어가기 쉬운 부분인데 publish 이전에 packaging 해서 한 번 확인은 해봐야 할 것 같다. 아니면 Android developer site에서 발견한 StrictMode class를 이용하는 것도 방법이 될 수 있을 것 같다. (http://developer.android.com/reference/android/os/StrictMode.html)