]> gitweb.fluxo.info Git - leap/leap_cli.git/commitdiff
added ability to sync support files along with hiera.yml. this way, files don't need...
authorelijah <elijah@riseup.net>
Mon, 14 Jan 2013 04:27:29 +0000 (20:27 -0800)
committerelijah <elijah@riseup.net>
Mon, 14 Jan 2013 04:27:29 +0000 (20:27 -0800)
lib/leap_cli.rb
lib/leap_cli/commands/deploy.rb
lib/leap_cli/config/manager.rb
lib/leap_cli/config/node.rb [new file with mode: 0644]
lib/leap_cli/config/object.rb
lib/leap_cli/config/tag.rb [new file with mode: 0644]
lib/leap_cli/path.rb
lib/leap_cli/remote/plugin.rb

index f65f131eb84170e558825c4de053785082f7a586..31a9d8f0fd16d71fb76b371a8ab0b8a6a563ad33 100644 (file)
@@ -18,6 +18,8 @@ require 'leap_cli/logger'
 
 require 'leap_cli/ssh_key'
 require 'leap_cli/config/object'
+require 'leap_cli/config/node'
+require 'leap_cli/config/tag'
 require 'leap_cli/config/object_list'
 require 'leap_cli/config/manager'
 
index cc2ea9603746a323a69e8fba8d57e94ece599ca7..a7f6bc31884b1c8f5bf6a7a82863823b43232c46 100644 (file)
@@ -1,3 +1,4 @@
+
 module LeapCli
   module Commands
 
@@ -23,13 +24,9 @@ module LeapCli
             ssh.leap.assert_initialized
           end
 
-          # sync hiera conf
-          ssh.leap.log :syching, "hiera.yaml" do
-            ssh.leap.rsync_update do |server|
-              node = manager.node(server.host)
-              ssh.leap.log Path.relative_path([:hiera, node.name]) + ' -> ' + node.name + ':/etc/leap/hiera.yaml'
-              {:source => Path.named_path([:hiera, node.name]), :dest => "/etc/leap/hiera.yaml"}
-            end
+          ssh.leap.log :syching, "configuration files" do
+            sync_hiera_config(ssh)
+            sync_support_files(ssh)
           end
 
           # sync puppet manifests and apply them
@@ -46,6 +43,37 @@ module LeapCli
 
     private
 
+    def sync_hiera_config(ssh)
+      dest_dir = manager.provider.hiera_sync_destination
+      ssh.leap.rsync_update do |server|
+        node = manager.node(server.host)
+        hiera_file = Path.relative_path([:hiera, node.name])
+        ssh.leap.log hiera_file + ' -> ' + node.name + ':' + dest_dir + '/hiera.yaml'
+        {:source => hiera_file, :dest => dest_dir + '/hiera.yaml'}
+      end
+    end
+
+    def sync_support_files(ssh)
+      dest_dir = manager.provider.hiera_sync_destination
+      ssh.leap.rsync_update do |server|
+        node = manager.node(server.host)
+        files_to_sync = node.file_paths.collect {|path| Path.relative_path(path, Path.provider) }
+        if files_to_sync.any?
+          ssh.leap.log(files_to_sync.join(', ') + ' -> ' + node.name + ':' + dest_dir)
+          {
+            :chdir => Path.provider,
+            :source => ".",
+            :dest => dest_dir,
+            :excludes => "*",
+            :includes => calculate_includes_from_files(files_to_sync),
+            :flags => "--relative --dirs --delete --delete-excluded --filter='protect hiera.yaml' --copy-links"
+          }
+        else
+          nil
+        end
+      end
+    end
+
     def init_submodules
       Dir.chdir Path.platform do
         statuses = assert_run! "git submodule status"
@@ -59,5 +87,28 @@ module LeapCli
       end
     end
 
+    def calculate_includes_from_files(files)
+      # prepend '/' (kind of like ^ for rsync)
+      includes = files.collect {|file| '/' + file}
+
+      # include all sub files of specified directories
+      includes.size.times do |i|
+        if includes[i] =~ /\/$/
+          includes << includes[i] + '**'
+        end
+      end
+
+      # include all parent directories
+      includes.size.times do |i|
+        path = File.dirname(includes[i])
+        while(path != '/')
+          includes << path unless includes.include?(path)
+          path = File.dirname(path)
+        end
+      end
+
+      return includes
+    end
+
   end
 end
\ No newline at end of file
index 6702fc4b589c37d6e6637cbc0c2b6f5514831a91..c860b5c73adbe5d08a14b436b1793ae0a536f6fe 100644 (file)
@@ -21,21 +21,21 @@ module LeapCli
         @provider_dir = Path.provider
 
         # load base
-        base_services = load_all_json(Path.named_path([:service_config, '*'], Path.provider_base))
-        base_tags     = load_all_json(Path.named_path([:tag_config, '*'], Path.provider_base))
-        base_common   = load_json(Path.named_path(:common_config, Path.provider_base))
-        base_provider = load_json(Path.named_path(:provider_config, Path.provider_base))
+        base_services = load_all_json(Path.named_path([:service_config, '*'], Path.provider_base), Config::Tag)
+        base_tags     = load_all_json(Path.named_path([:tag_config, '*'], Path.provider_base), Config::Tag)
+        base_common   = load_json(Path.named_path(:common_config, Path.provider_base), Config::Object)
+        base_provider = load_json(Path.named_path(:provider_config, Path.provider_base), Config::Object)
 
         # load provider
         provider_path = Path.named_path(:provider_config, @provider_dir)
         common_path = Path.named_path(:common_config, @provider_dir)
         Util::assert_files_exist!(provider_path, common_path)
-        @services = load_all_json(Path.named_path([:service_config, '*'], @provider_dir))
-        @tags     = load_all_json(Path.named_path([:tag_config, '*'],     @provider_dir))
-        @nodes    = load_all_json(Path.named_path([:node_config, '*'],    @provider_dir))
-        @common   = load_json(common_path)
-        @provider = load_json(provider_path)
-        @secrets  = load_json(Path.named_path(:secrets_config,  @provider_dir))
+        @services = load_all_json(Path.named_path([:service_config, '*'], @provider_dir), Config::Tag)
+        @tags     = load_all_json(Path.named_path([:tag_config, '*'],     @provider_dir), Config::Tag)
+        @nodes    = load_all_json(Path.named_path([:node_config, '*'],    @provider_dir), Config::Node)
+        @common   = load_json(common_path, Config::Object)
+        @provider = load_json(provider_path, Config::Object)
+        @secrets  = load_json(Path.named_path(:secrets_config,  @provider_dir), Config::Object)
 
         # inherit
         @services.inherit_from! base_services
@@ -161,10 +161,10 @@ module LeapCli
 
       private
 
-      def load_all_json(pattern)
+      def load_all_json(pattern, object_class)
         results = Config::ObjectList.new
         Dir.glob(pattern).each do |filename|
-          obj = load_json(filename)
+          obj = load_json(filename, object_class)
           if obj
             name = File.basename(filename).sub(/\.json$/,'')
             obj['name'] ||= name
@@ -174,9 +174,9 @@ module LeapCli
         results
       end
 
-      def load_json(filename)
+      def load_json(filename, object_class)
         if !File.exists?(filename)
-          return Config::Object.new(self)
+          return object_class.new(self)
         end
 
         log :loading, filename, 2
@@ -201,7 +201,7 @@ module LeapCli
           log 0, exc.to_s, :indent => 1
           return nil
         end
-        object = Config::Object.new(self)
+        object = object_class.new(self)
         object.deep_merge!(hash)
         return object
       end
@@ -226,7 +226,7 @@ module LeapCli
       # makes a node inherit options from appropriate the common, service, and tag json files.
       #
       def apply_inheritance(node)
-        new_node = Config::Object.new(self)
+        new_node = Config::Node.new(self)
         name = node.name
 
         # inherit from common
diff --git a/lib/leap_cli/config/node.rb b/lib/leap_cli/config/node.rb
new file mode 100644 (file)
index 0000000..9eea1f3
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# Configuration for a 'node' (a server in the provider's infrastructure)
+#
+
+require 'ipaddr'
+
+module LeapCli; module Config
+
+  class Node < Object
+    attr_accessor :file_paths
+
+    def initialize(manager=nil)
+      super(manager)
+      @node = self
+      @file_paths = []
+    end
+
+    #
+    # Make a copy of ourselves, except only including the specified keys.
+    #
+    # Also, the result is flattened to a single hash, so a key of 'a.b' becomes 'a_b'
+    #
+    def pick(*keys)
+      keys.map(&:to_s).inject(self.class.new(@manager)) do |hsh, key|
+        value = self.get(key)
+        if !value.nil?
+          hsh[key.gsub('.','_')] = value
+        end
+        hsh
+      end
+    end
+
+    #
+    # returns true if this node has an ip address in the range of the vagrant network
+    #
+    def vagrant?
+      begin
+        vagrant_range = IPAddr.new @manager.provider.vagrant.network
+      rescue ArgumentError => exc
+        Util::bail! { Util::log :invalid, "ip address '#{@node.ip_address}' vagrant.network" }
+      end
+
+      begin
+        ip_address = IPAddr.new @node.get('ip_address')
+      rescue ArgumentError => exc
+        Util::log :warning, "invalid ip address '#{@node.get('ip_address')}' for node '#{@node.name}'"
+      end
+      return vagrant_range.include?(ip_address)
+    end
+  end
+
+end; end
index 395ebe3f6e74cc1325b899bd97680cd9bddd139b..5c6cfd0481381b68ad062bb3421040950e69f48f 100644 (file)
@@ -1,6 +1,5 @@
 require 'erb'
 require 'json/pure'  # pure ruby implementation is required for our sorted trick to work.
-require 'ipaddr'
 
 $KCODE = 'UTF8' unless RUBY_VERSION > "1.9.0"
 require 'ya2yaml' # pure ruby yaml
@@ -17,7 +16,6 @@ module LeapCli
 
       attr_reader :node
       attr_reader :manager
-      attr_reader :node_list
       alias :global :manager
 
       def initialize(manager=nil, node=nil)
@@ -27,9 +25,6 @@ module LeapCli
 
         # an object that is a node as @node equal to self, otherwise all the child objects point back to the top level node.
         @node = node || self
-
-        # this is only used by Config::Objects that correspond to services or tags.
-        @node_list = Config::ObjectList.new
       end
 
       #
@@ -105,21 +100,6 @@ module LeapCli
       ## COPYING
       ##
 
-      #
-      # Make a copy of ourselves, except only including the specified keys.
-      #
-      # Also, the result is flattened to a single hash, so a key of 'a.b' becomes 'a_b'
-      #
-      def pick(*keys)
-        keys.map(&:to_s).inject(Config::Object.new(@manager,@node)) do |hsh, key|
-          value = self.get(key)
-          if !value.nil?
-            hsh[key.gsub('.','_')] = value
-          end
-          hsh
-        end
-      end
-
       #
       # a deep (recursive) merge with another Config::Object.
       #
@@ -195,29 +175,6 @@ module LeapCli
         self.deep_merge!(object, true)
       end
 
-      ##
-      ## NODE SPECIFIC
-      ## maybe these should be moved to a Node class.
-      ##
-
-      #
-      # returns true if this node has an ip address in the range of the vagrant network
-      #
-      def vagrant?
-        begin
-          vagrant_range = IPAddr.new @manager.provider.vagrant.network
-        rescue ArgumentError => exc
-          Util::bail! { Util::log :invalid, "ip address '#{@node.ip_address}' vagrant.network" }
-        end
-
-        begin
-          ip_address = IPAddr.new @node.get('ip_address')
-        rescue ArgumentError => exc
-          Util::log :warning, "invalid ip address '#{@node.get('ip_address')}' for node '#{@node.name}'"
-        end
-        return vagrant_range.include?(ip_address)
-      end
-
       ##
       ## MACROS
       ## these are methods used when eval'ing a value in the .json configuration
@@ -270,6 +227,43 @@ module LeapCli
         return nil
       end
 
+      #
+      # returns what the file path will be, once the file is rsynced to the server.
+      # an internal list of discovered file paths is saved, in order to rsync these files when needed.
+      #
+      # notes:
+      #
+      # * argument 'path' is relative to Path.provider/files or Path.provider_base/files
+      # * the path returned by this method is absolute
+      # * the path stored for use later by rsync is relative to Path.provider
+      # * if the path does not exist locally, but exists in provider_base, then the default file from
+      #   provider_base is copied locally.
+      #
+      def file_path(path)
+        if path.is_a? Symbol
+          path = [path, @node.name]
+        end
+        actual_path = Path.find_file(path)
+        if actual_path.nil?
+          nil
+        else
+          if actual_path =~ /^#{Regexp.escape(Path.provider_base)}/
+            # if file is under Path.provider_base, we must copy the default file to
+            # to Path.provider in order for rsync to be able to sync the file.
+            local_provider_path = actual_path.sub(/^#{Regexp.escape(Path.provider_base)}/, Path.provider)
+            FileUtils.cp_r actual_path, local_provider_path
+            Util.log :created, Path.relative_path(local_provider_path)
+            actual_path = local_provider_path
+          end
+          if Dir.exists?(actual_path) && actual_path !~ /\/$/
+            actual_path += '/' # ensure directories end with /, important for building rsync command
+          end
+          relative_path = Path.relative_path(actual_path)
+          @node.file_paths << relative_path
+          @node.manager.provider.hiera_sync_destination + '/' + relative_path
+        end
+      end
+
       #
       # inserts a named secret, generating it if needed.
       #
diff --git a/lib/leap_cli/config/tag.rb b/lib/leap_cli/config/tag.rb
new file mode 100644 (file)
index 0000000..e5e719d
--- /dev/null
@@ -0,0 +1,18 @@
+#
+#
+# A class for node services or node tags.
+#
+#
+
+module LeapCli; module Config
+
+  class Tag < Object
+    attr_reader :node_list
+
+    def initialize(manager=nil)
+      super(manager)
+      @node_list = Config::ObjectList.new
+    end
+  end
+
+end; end
index 43f2edc7b5f16579d2fe22ffffe6d45448e2c54e..e7626b52155d28192816eff85a70e50d0136ca5b 100644 (file)
@@ -87,13 +87,14 @@ module LeapCli; module Path
   # tries to find a file somewhere
   #
   def self.find_file(arg)
-    file_path = named_path(arg, Path.provider)
-    return file_path if File.exists?(file_path)
-
-    file_path = named_path(arg, Path.provider_base)
-    return file_path if File.exists?(file_path)
-
-    # give up
+    [Path.provider, Path.provider_base].each do |base|
+      file_path = named_path(arg, base)
+      return file_path if File.exists?(file_path)
+      if arg.is_a? String
+        file_path = base + '/files/' + arg
+        return file_path if File.exists?(file_path)
+      end
+    end
     return nil
   end
 
index 803ebf93b297fddeaf2f5b8278e70b4d3b29b981..213c98130fc19754b268a2261af3168dc93336f9 100644 (file)
@@ -57,22 +57,28 @@ module LeapCli; module Remote; module Plugin
     # rsync to each server
     failed_servers = []
     servers.each do |server|
+      options = yield server
+      next unless options
+
       # build rsync command
-      paths       = yield server
       remote_user = server.user || fetch(:user, ENV['USER'])
+      rsync_options = {
+        :flags => options[:flags],
+        :includes => options[:includes],
+        :excludes => options[:excludes],
+        :ssh => ssh_options.merge(server.options[:ssh_options]||{})
+      }
       rsync_cmd = SupplyDrop::Rsync.command(
-        paths[:source],
-        SupplyDrop::Rsync.remote_address(remote_user, server.host, paths[:dest]),
-        {:ssh => ssh_options.merge(server.options[:ssh_options]||{})}
+        options[:source],
+        SupplyDrop::Rsync.remote_address(remote_user, server.host, options[:dest]),
+        rsync_options
       )
 
       # run command
       logger.debug rsync_cmd
-      ok = system(rsync_cmd)
-      if ok
-        logger.log 1, "rsync #{paths[:source]} #{paths[:dest]}", server.host, :color => :green
-      else
-        failed_servers << server.host
+      Dir.chdir(options[:chdir] || '.') do
+        ok = system(rsync_cmd)
+        failed_servers << server.host unless ok
       end
     end