jbbarth's

Après avoir comparé au boulot 3 outils de gestion de conf, j’en retiens les éléments suivants :

  • Cfengine : DSL spécifique et horrible à apprendre, orientations peu claire (à-la-Nagios), une communauté amorphe, pas hackable. Poubelle.
  • Puppet : en ruby, un DSL spécifique pour les confs (bof), a fait ses preuves, une bonne doc, de plus en plus hackable (la doc s’améliore de jour en jour), une communauté dynamique, et le projet est géré sous redmine. Sympa.
  • Chef : en ruby, tout en ruby, donc pas de DSL à apprendre (yeah!), une doc pas mal, hackable à l’infini, le projet est hyper mega dynamique, et ils ont des références énormes dans le monde ruby, du genre 37signals ou Engine Yard. Adopté !

Seul défaut de Chef, ça bouge vite, très vite. Et les versions 0.7 présentes dans mes distrib préférées commencent à être vraiment dépassées.

Hier je me prends donc par la main, en m’inspirant de l’article de akitaonrails, et je cherche comment installer une 0.8. Miracle, tout est dans leur wiki, et ils proposent même des dépôts pour ma Ubuntu Lucid.

Voici un script chef-install pondu en 3 secondes :

#installation
add-apt-repository ppa:jtimberman/opschef
aptitude update
aptitude -y install chef
service chef-client stop
update-rc.d chef-client disable >/dev/null

#configuration
sed -i -e 's#^file.*#file_cache_path "/tmp/chef-solo"#' \
    -e 's#^cook.*#cookbook_path ["/var/chef-solo/cookbooks"]#' /etc/chef/solo.rb

#cookbooks (install 'git-core' if needed)
mkdir -p /var/chef-solo
cd /var/chef-solo
git clone http://github.com/opscode/cookbooks.git

#hyperspace!
cat >/etc/chef/recipes.json <<EOF
{
  "resolver": {"nameservers":["192.168.0.1"], "search":"home"},
  "recipes": ["resolver"]
}
EOF

Go ?

% sudo chef-solo -j /etc/chef/recipes.json
[Tue, 23 Mar 2010 13:47:37 +0100] INFO: Starting Chef Solo Run
[Tue, 23 Mar 2010 13:47:42 +0100] WARN: Missing gem 'right_aws'
[Tue, 23 Mar 2010 13:47:42 +0100] WARN: Missing gem 'mysql'
[Tue, 23 Mar 2010 13:47:43 +0100] INFO: Updating template[/etc/resolv.conf] at /etc/resolv.conf
[Tue, 23 Mar 2010 13:47:43 +0100] INFO: Backing up template[/etc/resolv.conf] to /etc/resolv.conf.chef-20100323134743
[Tue, 23 Mar 2010 13:47:43 +0100] INFO: Chef Run complete in 5.588404 seconds

Yeeehaa !

Mon fonctionnement actuel m’empêche de m’attacher à un serveur central et ainsi fonctionner en client/serveur. Je pense que je vais donc commencer à me faire des cookbooks et les utiliser “bêtement” via chef-solo, à voir.

Enfin pour l’instant, c’est juste énorme.

Comments: 0 (view/add your own) Tags: chef, ruby, tech

En ce moment il y a une vague d’engouement pour les bases non-SQL, comme les bases orientées document CouchDB ou MongoDB. J’ai lu un article récemment qui vaut le détour : Schema Free Mysql VS NoSQL (via). Ce genre de solution permettrait sûrement de faire passer la pillule plus doucement aux gens qui s’accrochent encore au SQL pur à l’ancienne.

Au passage, juste une citation qu’on croirait destinée à certains ayatollahs du boulot :

Open your mind. FriendFeed uses something very similiar to this to handle 250 million entries. Why is it no good for the theorists? Because they don’t solve problems, they make them.

Comments: 0 (view/add your own) Tags: tech

J’utilise souvent IRB. Très souvent pour d’autres choses que Rails (à la base on me paye pour être sysadmin, pas développeur…).

Là arrive Rails 3 beta :

  • qui s’appuie sur IRB pour sa console (rails console)
  • qui gère ses dépendances via Bundler, ce qui ne permet plus par défaut de charger des Gems en dehors de son appli

Bah oui mais là ça coince. De bêtes require dans mon .irbrc ne fonctionnent plus. Comme discuté sur la liste rails france, je charge dans mon .irbrc des choses dont j’ai très souvent besoin, et qui n’ont rien à voir avec mes applis, même en Rails 3. Par exemple, il n’y a aucune raison qu’une de mes applis dépende de Wirble, une lib pour améliorer IRB.

Bref, faute de mieux pour le moment, voilà le genre d’horreur auquel Rails me pousse :

basedirs = ENV["GEM_PATH"].to_s.split(":").map{|d|"#{d}/gems/*/lib"}
Dir.glob(basedirs).each do |dir|
  $: << dir unless $:.include?(dir)
end

Humpf.

Comments: 0 (view/add your own) Tags: irb, rails3, ruby

Au boulot nous avons une instance Redmine qui tourne avec une base Sqlite3 pour nos tickets internes. Pratique, mais nous avons aussi développé une offre d’hébergement Redmine ouverte à la demande sur l’intranet, sous Mysql. D’où passage de l’instance Sqlite sous Mysql.

On ne peut bien sûr pas se contenter d’un export SQL de Sqlite à réimporter sous Mysql : ces deux moteurs ne respectent pas exactement la même syntaxe SQL, et ne stockent pas leurs types primitifs de la même manière (exemple: les booléens, stockés en “1/0” sous Mysql, et en “t/f” sous Sqlite).

C’est là qu’arrive yaml_db , une biblitothèque à installer comme une gem ou comme un plugin dans une appli Rails, qui permet de réaliser des exports ou imports de sa base sous un format neutre, YAML (wikipedia).

Pour une migration “one shot”, le plus simple sera de cloner la lib dans le répertoire plugins de vos applis et de suivre les instructions proposées dans le README :

cd /path/to/my/app
cd vendor/plugins
git clone http://github.com/ludicast/yaml_db.git
cd -
rake db:dump
#modifications éventuelles du fichier db/data.yml (chez nous l'appli change d'adresse, donc on a remplacé toutes les anciennes URLs)
#changement de database.yml
rake db:load

Un outil simple, comme on aime :)

EDIT: j’ai titré “déplacer une base Rails” car ce plugin fonctionne bien surtout avec une base ActiveRecord, ORM de Rails par défaut. Pour une base quelconque rien de garanti :)

Que j’aurais pu aussi sobrement appeler :

  • refaisons le match
  • massacre à la tronçonneuse
  • f*ck
require 'find'
require 'yaml'
require 'digest/md5'

class EvaFile
  attr_accessor :path

  def initialize(path)
    @path = path
  end
  
  def infos
    @infos ||= {:size => File.size(@path).tap{|s| def s.to_s; "#{self.dup} bytes"; end },
                :last_modified => File.mtime(@path),
                :md5_sum => Digest::MD5.hexdigest(File.read(@path))}
  end
end

class EvaDir
  def initialize(subdir)
    raise "Give me a (sub)directory !" if subdir.nil? || !File.directory?(subdir)
    @subdir = subdir
  end
  
  def files
    @files ||= Find.find(@subdir) do |f|
                 Find.prune if File.directory?(f)
                 EvaFile.new(f) if File.file?(f)
               end.compact
  end

  def write_info_file(filename)
    path = File.join(@subdir,filename)
    begin
      info = File.open(path,"w")
    rescue
      $stderr.puts "Error opening file #{path} for writing..."
    end
    info.write "Size: #{size} bytes\n"
    info.write "Files: #{nb_files}\n"
    files_hash = {}
    files.each do |file|
      files_hash.merge!(File.basename(file.path) => file.infos)
    end
    info.write files_hash.to_yaml
    info.close_write
  end

  def size
    @size = files.inject(0) do |memo, f|
              memo + f.infos[:size]
            end
  end
  
  def nb_files
    files.length
  end
end

class EvaUtil
  def initialize(dir)
    raise "Give me a directory !" if dir.nil? || !File.directory?(dir)
    @dir = dir
  end
  
  def subdirs
    return @subdirs if @subdirs
    @subdirs = [ EvaDir.new(@dir) ]
    @subdirs << Find.find(@dir) do |f|
                  EvaDir.new(f) if File.directory?(f)
                end
    @subdirs = @subdirs.compact.uniq
  end

  def generate_info_files(filename="infos.txt")
    @subdirs.each do |s|
      s.write_info_file(filename)
    end
  end
  
  def generate_meta_info_file(filename="metainfos.txt")
    path = File.join(@dir,filename)
    begin
      meta = File.open(path,"w")
    rescue
      $stderr.puts "Error opening file #{path} for writing..."
    end
    meta.write "Total size: #{size} bytes"
    meta.write "Total number of files: #{nb_files}"
    meta.write "Last modified (<24h) :\n #{last_modified.join("\n  ")}" if last_modified.any?
    meta.close_write
  end
  
  def last_modified
    @subdirs.inject([]) do |memo, subdir|
      memo << subdir.files.select{|f| File.mtime(f) < Time.at(Time.now.to_i - 86400)}.map(&:path)
      memo.flatten
      memo
    end
  end

  private
  def method_missing(symbol, *args)
    if %w(nb_files size).include?(symbol.to_s)
      @subdirs.inject(0) do |memo, subdir|
         memo + subdir.send(symbol)
      end
    else
      super
    end
  end
end

e = EvaUtil.new(ARGV[0])
e.generate_info_files
e.generate_meta_info_file

Garanti 100% non testé, 100% fait sans l’API, et surtout 100% fait avec un éditeur de texte. C’est sûrement bourré de conneries, mais au moins avec un truc comme ça j’aurais pas eu honte. Cela dit vue la longueur, je commence à me pardonner d’avoir barbouillé ma copie de blanco, c’était infaisable sans ça. Coder sur papier est définitivement un cauchemard. On se la refait dans 2 ans ;-)