Jeen - Yet anothere techlog

STFUAWSC

Carton - CPAN Dependencies Manager

Jenkins 이야기를 마저 쓸려고 했는 데, 그 사이사이에 사용된 것들부터 정리하고자 우선은 Carton 이야기 부터 먼저하고자 합니다.

잘은 모르지만 Carton 은 Ruby 의 Bundler 에 영감을 받아서 만들어 진 것입니다. 어떤 Perl 로 만든 소프트웨어의 의존모듈의 관리가 편해집니다. :–) 일례로, 노트북에서 개발을 하고 이것을 서버에 올릴려고 할 때, 일일이 의존모듈 체크를 해야할 필요성이 있습니다. 그때마다 cpan MODULE 등으로 설치를 해나가야 한다면 여간 힘든 일이 아니죠.

그런 상황에서 쉽게 문제를 해결할 수 있는 것이 Carton 이 아닐까 생각합니다.

Carton 설치

Carton 은 간단하게 cpan 으로 설치할 수 있습니다.

bash $ cpanm carton

위의 커맨드로 Carton 을 설치하면 carton 이라는 커맨드가 생깁니다.

Carton 이용

Carton 을 제대로 이용하려면, 해당 Perl App 의 의존모듈이 Makefile.PL 안에서 잘 관리되고 있어야 합니다.

~~~ perl Makefile.PL use ExtUtils::MakeMaker; WriteMakefile(

'NAME'      => 'WebService::Aladdin',
'AUTHOR'    => 'JEEN <jeen@perl.kr>',
'VERSION_FROM' => 'lib/WebService/Aladdin.pm', # finds $VERSION
'PREREQ_PM' => {
    Test::Base => 0,
    version    => 0,
    Class::Accessor::Fast => 0,
    LWP::UserAgent => 0,
    URI            => 0,
    Carp           => 0,
    XML::FeedPP    => 0
},

); ~~~

우선 예제로 제가 만든 WebService::Aladdin 모듈의 Makefile.PL 이 위와 같습니다. 이는 WebService::Aladdin 모듈에서 필요로 하는 의존모듈 등의 정보들을 나타내고 있습니다.

bash $ carton install

carton install 을 통해서 Makefile.PL 에 정의된 의존모듈을 설치합니다. 이때 Carton 은 의존모듈의 의존트리까지도 설치해버립니다. 그러니 실제 의존모듈이 10개라도, 그 10개의 각각의 모듈의 의존모듈까지도 설치하기 때문에 실제로 설치되는 것은 10개 이상의 모듈들이 될 것입니다.

아무튼 carton install 이 끝났다면 carton.lock 파일이 생성됩니다. 그리고 local 이라는 디렉토리의 아래에 의존모듈들이 설치됩니다.

~~~ text carton.lock { “modules” : {

  "Algorithm::Diff" : {
     "dist" : "Algorithm-Diff-1.1902",
     "module" : "Algorithm::Diff",
     "mymeta" : {
        "abstract" : "unknown",
        "author" : \[
           "unknown"
        \],
        "dynamic_config" : 0,
        "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112621, CPAN::Meta::Converter version 2.112150",
        "license" : \[
           "unknown"
        \],
        "meta-spec" : {
           "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
           "version" : "2"
        },
        "name" : "Algorithm-Diff",
        "no_index" : {
           "directory" : \[
              "t",
              "inc"
           \]
        },
        "prereqs" : {
           "build" : {
              "requires" : {
                 "ExtUtils::MakeMaker" : 0
              }
           },
           "configure" : {
              "requires" : {
                 "ExtUtils::MakeMaker" : 0
              }
           },
           "runtime" : {
              "requires" : {}
           }
        },
        "release_status" : "stable",
        "version" : "1.1902"
     },
     "name" : "Algorithm::Diff",
     "pathname" : "T/TY/TYEMQ/Algorithm-Diff-1.1902.tar.gz",
     "provides" : {
        "Algorithm::Diff" : {
           "file" : "Algorithm/Diff.pm",
           "version" : "1.1902"
        },
        "Algorithm::DiffOld" : {
           "file" : "Algorithm/DiffOld.pm",
           "version" : "1.1"
        }
     },
     "version" : "1.1902"
  },
  ...

} } ~~~

이후 Makefile.PL 에서 또다른 의존모듈이 추가되었을 시에, 또다시

bash $ carton install

을 통해서 추가의존모듈을 설치할 수 있습니다. 이렇게 carton install 로 설치된 로컬 모듈들은

bash $ carton list IO-stringy-2.110 File-Listing-6.03 XML-FeedPP-0.43 CPAN-Meta-2.112621 …

carton list 로 확인할 수 있습니다.

그리고 carton exec 를 통해서 로컬모듈에 설치된 의존모듈을 참조해서 실행할 수 있습니다. 일례로 CatalystDancer 로 만든 WebApp 들을 위에서 설명한 대로 carton install 한다음,

bash $ carton exec — plackup -a app.psgi -p 3000 —mode production

이처럼 carton exec 를 통해서 실행할 수 있지요.

Carton 을 이용하고서…

우선은 Carton 을 이용함으로, 한 어플리케이션 당 하나의 모듈참조장소를 가지게해서 여러 어플리케이션이 혼잡한 경우에 발생할 수 있는 부작용을 예방할 수 있습니다. Makefile.PL 에서 해당 모듈의 버젼을 지정함으로 특정 버젼에 대해서만 의존을 가질 수 있기 때문에 더더욱 이런 면에서는 효과적입니다.

그래서 저같은 경우는 오히려 서비스마다 그 서비스용 계정을 만들고 perlbrew 환경을 갖추는 것보다 가볍게 서비스용 계정은 하나만 가지며 각 서비스디렉토리마다 Carton 을 올리는 쪽으로 움직이고 있습니다. 그러니 굳이 시스템 펄을 사용하는 것이 큰 거부감을 느끼지 않게 되었네요.

그 외

일단 carton.lock 에 기재된 의존모듈 정보가 많으면 많을수록 carton install 에 걸리는 시간은 더 오래걸리게 됩니다. 물론 Carton 이 기본적으로 외부미러(http://cpan.metacpan.org) 를 사용하고 있기에, 그럴 수도 있겠다 싶어서 코드를 한번 훑어봤는데, 마침 PERL_CARTON_MIRROR 라는 환경변수를 통해서 가까운 혹은 로컬 네트워크 안의 CPAN 미러를 지정해줌으로 많은 시간을 단축할 수 있지 않을까 생각해봅니다.

Conclusion

물론 Carton 은 현재 ALPHA 퀄리티라고 합니다. 버젼도 현재(2012-01-16) 로는 0.9.3 이고, 1.0 정식 릴리즈까지는 아직 시간이 남아있습니다. 몇몇 기능이 바뀔거라는 주의사항이 Carton 모듈페이지 에서 확인할 수 있습니다.

좀 더 다양한 Carton 에 대한 이야기는 작년 YAPC::Asia2011 의 Carton Talk Page 에서도 확인하실 수 있습니다.

Picasa Upload Script II

~~~ perl picasa-uploader.pl

!/usr/bin/env perl

package PicasaUploader; use Any::Moose; use namespace::autoclean; use Config::Pit; use LWP::UserAgent; use Net::Google::AuthSub; use MIME::Types ();

has user_id => (

is      => 'ro',
default => 'default',

);

has album_id => (

is      => 'ro',
default => 'default',

);

has files => (

is      => 'ro',
isa     => 'ArrayRef',
default => sub { \[\] }

);

has google_auth => (

is      => 'rw',
isa     => 'Net::Google::AuthSub',
default => sub {
    Net::Google::AuthSub->new(
        service => 'lh2',
        source  => 'my-picasauploader-0.1'
    );
}

);

has ‘_ua’ => (

is      => 'rw',
isa     => 'LWP::UserAgent',
default => sub {
    LWP::UserAgent->new( cookie_jar => {} ); 
}

);

has ‘config’ => (

is          => 'ro',
isa         => 'HashRef',
lazy_build  => 1,

);

sub BUILD {

my $self = shift;
$self->auth;
return 1;

}

sub _build_config {

Config::Pit::pit_get('google.com', require => {
   username => 'your username on google.com',
   password => 'your password on google.com' 
});    

}

sub auth {

my $self = shift;

my $res = $self->google_auth->login($self->config->{username}, $self->config->{password});
die "Login Failed: ". $res->error unless $res->is_success;

}

sub upload_files {

my $self = shift;

for my $file_path (@{ $self->files }) {
    my $link = $self->upload_file($file_path);
    print $link."\n";
}

}

sub upload_file {

my ($self, $file_path) = @_;

my $file = Path::Class::File->new($file_path);
die "File Not Found : $file" unless -f $file;

my $post_url = sprintf 'https://picasaweb.google.com/data/feed/api/user/%s/albumid/%s', $self->user_id, $self->album_id;

my ($mime_type, $encoding) = MIME::Types::by_suffix($file->basename);
my $content = $file->slurp;
my $res = $self->_ua->post($post_url,
    $self->google_auth->auth_params,
    Content_Type   => $mime_type,
    Slug           => $file->basename,
    Content        => $content,
);

die 'UPLOAD Failed : '.$res->status_line unless $res->is_success;
my ($link) = ($res->decoded_content=~/content type.*?src='(.\*?)'/gsm);
$link;

}

PACKAGE–>meta->make_immutable;

package main;

my $uploader = PicasaUploader->new( files => \@ARGV ); $uploader->upload_files; ~~~

@aanoaa 님의 글에서 Picasa Uploader 스크립트를 소개했었는데요. PicasaUploader 라고 package 로 정의해서 쓰도록 했습니다. 거기에 Config::Pit 을 써서 계정정보를 다루도록 했구요. 심심해서 그냥 약간 개량해봤습니다.

FreeBSD 8.2 STABLE - CPAN Error

회사업무차 FreeBSD 8.2 STABLE OS 를 사용하는 서버에서 CPAN 사용시 에러가 발생했습니다.

~~~ bash $ cpan Module::Install Reading ‘/home/jeen/.cpan/sources/authors/01mailrc.txt.gz’ ………………………………………………………………….DONE Reading ‘/home/jeen/.cpan/sources/modules/02packages.details.txt.gz’ Database was generated on Thu, 05 Jan 2012 05:12:02 GMT ………………………………………………………………….DONE Reading ‘/home/jeen/.cpan/sources/modules/03modlist.data.gz’ Can’t locate object method “data” via package “CPAN::Modulelist” (perhaps you forgot to load “CPAN::Modulelist”?) at (eval 24) line 1. at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/CPAN/Index.pm line 524

CPAN::Index::rd_modlist('CPAN::Index', '/home/jeen/.cpan/sources/modules/03modlist.data.gz') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/CPAN/Index.pm line 85
CPAN::Index::reload('CPAN::Index') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/CPAN.pm line 976
CPAN::exists('CPAN=HASH(0x801fe27f8)', 'CPAN::Module', 'Module::Install') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/CPAN/Shell.pm line 1256
CPAN::Shell::expandany('CPAN::Shell', 'Module::Install') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/CPAN/Shell.pm line 1681
CPAN::Shell::rematein('CPAN::Shell', 'install', 'Module::Install') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/CPAN/Shell.pm line 1977
CPAN::Shell::__ANON__('CPAN::Shell', 'Module::Install') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/App/Cpan.pm line 459
App::Cpan::__ANON__('Module::Install') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/App/Cpan.pm line 468
App::Cpan::_default('ARRAY(0x800e92780)', 'HASH(0x801fe7d38)') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/App/Cpan.pm line 386
App::Cpan::run('App::Cpan', 'Module::Install') called at /home/jeen/perl5/perlbrew/perls/perl-5.14.2/bin/cpan line 11

~~~

마땅히 어떻게 cpan 자체가 안되니, 다른 CPAN 모듈 설치도 안되고…

bash $ curl -L http://cpanmin.us | perl – —self-upgrade

그래서 일단은 그냥 cpanm 을 설치하고 필요한 모듈들을 설치했습니다. 하지만 여전히 cpan 으로 모듈을 설치하려면 위처럼 에러가 뜨니 아직도 찜찜하기 그지 없네요.

bash $ uname -a FreeBSD xxx.xxxxx.kr 8.2-STABLE FreeBSD 8.2-STABLE #1: Wed Apr 13 13:10:49 KST 2011 root@xxx.xxxx.kr:/usr/obj/usr/src/sys/GENERIC amd64

~~~ bash $ perl -V Summary of my perl5 (revision 5 version 14 subversion 2) configuration:

Platform:

osname=freebsd, osvers=8.2-stable, archname=amd64-freebsd
uname='freebsd xxx.xxxxx.kr 8.2-stable freebsd 8.2-stable #1: wed apr 13 13:10:49 kst 2011 root@xxx.xxxx.kr:usrobjusrsrcsysgeneric amd64 '
config_args='-de -Dprefix=/home/jeen/perl5/perlbrew/perls/perl-5.14.2'
hint=recommended, useposix=true, d_sigaction=define
useithreads=undef, usemultiplicity=undef
useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
use64bitint=define, use64bitall=define, uselongdouble=undef
usemymalloc=n, bincompat5005=undef

Compiler:

cc='cc', ccflags ='-DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include',
optimize='-O',
cppflags='-DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
ccversion='', gccversion='4.2.1 20070719  [FreeBSD]', gccosandvers=''
intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
alignbytes=8, prototype=define

Linker and Libraries:

ld='cc', ldflags ='-Wl,-E  -fstack-protector -L/usr/local/lib'
libpth=/usr/lib /usr/local/lib
libs=-lm -lcrypt -lutil -lc
perllibs=-lm -lcrypt -lutil -lc
libc=, so=so, useshrplib=false, libperl=libperl.a
gnulibc_version=''

Dynamic Linking:

dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' '
cccdlflags='-DPIC -fPIC', lddlflags='-shared  -L/usr/local/lib -fstack-protector'

Characteristics of this binary (from libperl): Compile-time options: PERL_DONT_CREATE_GVSV PERL_MALLOC_WRAP

                    PERL_PRESERVE_IVUV USE_64_BIT_ALL USE_64_BIT_INT
                    USE_LARGE_FILES USE_PERLIO USE_PERL_ATOF

Built under freebsd Compiled at Jan 5 2012 15:51:41 %ENV:

PERLBREW_BASHRC_VERSION="0.39"
PERLBREW_HOME="/home/jeen/.perlbrew"
PERLBREW_MANPATH="/home/jeen/perl5/perlbrew/perls/perl-5.14.2/man"
PERLBREW_PATH="/home/jeen/perl5/perlbrew/bin:/home/jeen/perl5/perlbrew/perls/perl-5.14.2/bin"
PERLBREW_PERL="perl-5.14.2"
PERLBREW_ROOT="/home/jeen/perl5/perlbrew"
PERLBREW_VERSION="0.39"

@INC:

/home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/site_perl/5.14.2/amd64-freebsd
/home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/site_perl/5.14.2
/home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2/amd64-freebsd
/home/jeen/perl5/perlbrew/perls/perl-5.14.2/lib/5.14.2
.

~~~

bash $ df -Th . Filesystem Type Size Used Avail Capacity Mounted on /dev/ar0s1f ufs 43G 5.1G 35G 13% /usr

일단 사건해결에 도움이 될 만한 정보들은 모조리 뽑아봤습니다. 천천히 살펴보며 구원을 요청해야 되겠네요.

Conclusion

#perl-kr 에서 위에 정리한 내용들로 @aer0 님께 질문을 날렸습니다. @aer0 님께서 제안하신 방법으로

  • Bundle::CPAN 을 설치
  • ~/.cpan 디렉토리를 삭제 후 다시 cpan 실행

    이었습니다.

    Bundle::CPAN 을 설치해도 사태가 해결되지 않아서, 결국은 ~/.cpan 디렉토리를 삭제한 후 다시 cpan 을 실행했습니다.

  • ~/.cpan/sources/modules/02packages.details.txt.gz

  • ~/.cpan/sources/modules/03modlist.data.gz

    같은 CPAN 미러에서 가져오는 인덱스 파일이 이상해서 꼬인 것 같다라는 @aer0 님의 총평이었습니다.

    역시 이런 사건이 발생할 때는 #perl-kr 에 들러야 됩니다. :–)