]> gitweb.fluxo.info Git - leap/leap_cli.git/commitdiff
environment pinning: new commands `leap env`, `leap env pin X` and `leap env unpin...
authorelijah <elijah@riseup.net>
Tue, 30 Sep 2014 20:36:26 +0000 (13:36 -0700)
committerelijah <elijah@riseup.net>
Tue, 30 Sep 2014 20:36:26 +0000 (13:36 -0700)
lib/leap_cli/commands/compile.rb
lib/leap_cli/commands/deploy.rb
lib/leap_cli/commands/env.rb [new file with mode: 0644]
lib/leap_cli/commands/list.rb
lib/leap_cli/commands/pre.rb
lib/leap_cli/config/manager.rb
lib/leap_cli/config/tag.rb
lib/leap_cli/leapfile.rb
lib/leap_cli/version.rb

index eaedfbf73fc6b4e9d8944248c1a165c8816124a2..13fa9ac4faaf4249f48a2de8edd20fd2bfec3c42 100644 (file)
@@ -9,10 +9,13 @@ module LeapCli
       c.command :all do |all|
         all.action do |global_options,options,args|
           environment = args.first
+          if !LeapCli.leapfile.environment.nil? && environment != LeapCli.leapfile.environment
+            bail! "You cannot specify an ENVIRONMENT argument while the environment is pinned."
+          end
           if environment && manager.environment_names.include?(environment)
-            compile_hiera_files(manager.filter(args))
+            compile_hiera_files(manager.filter([environment]))
           else
-            compile_hiera_files
+            compile_hiera_files(manager.filter)
           end
         end
       end
index 553b2b18c27e2ebb182fdf2dd06affd3b342d09a..bd1f4798df6f70ffd6cc62aeca29f64119163122 100644 (file)
@@ -36,7 +36,7 @@ module LeapCli
           init_submodules
         end
 
-        nodes = filter_deploy_nodes(args)
+        nodes = manager.filter!(args)
         if nodes.size > 1
           say "Deploying to these nodes: #{nodes.keys.join(', ')}"
           if !global[:yes] && !agree("Continue? ")
@@ -253,17 +253,5 @@ module LeapCli
       tags.join(',')
     end
 
-    #
-    # for safety, we allow production deploys to be turned off in the Leapfile.
-    #
-    def filter_deploy_nodes(filter)
-      nodes = manager.filter!(filter)
-      if !leapfile.allow_production_deploy
-        nodes = nodes[:environment => "!production"]
-        assert! nodes.any?, "Skipping deploy because @allow_production_deploy is disabled."
-      end
-      nodes
-    end
-
   end
 end
diff --git a/lib/leap_cli/commands/env.rb b/lib/leap_cli/commands/env.rb
new file mode 100644 (file)
index 0000000..d81e82f
--- /dev/null
@@ -0,0 +1,53 @@
+module LeapCli
+  module Commands
+
+    desc "Manipulate and query environment information."
+    long_desc "The 'environment' node property can be used to isolate sets of nodes into entirely separate environments. "+
+      "A node in one environment will never interact with a node from another environment. "+
+      "Environment pinning works by modifying your ~/.leaprc file and is dependent on the "+
+      "absolute file path of your provider directory (pins don't apply if you move the directory)"
+    command :env do |c|
+      c.desc "List the available environments. The pinned environment, if any, will be marked with '*'."
+      c.command :ls do |ls|
+        ls.action do |global_options, options, args|
+          envs = ["default"] + manager.environment_names.compact.sort
+          envs.each do |env|
+            if env
+              if LeapCli.leapfile.environment == env
+                puts "* #{env}"
+              else
+                puts "  #{env}"
+              end
+            end
+          end
+        end
+      end
+
+      c.desc 'Pin the environment to ENVIRONMENT. All subsequent commands will only apply to nodes in this environment.'
+      c.arg_name 'ENVIRONMENT'
+      c.command :pin do |pin|
+        pin.action do |global_options,options,args|
+          environment = args.first
+          if environment == 'default' ||
+              (environment && manager.environment_names.include?(environment))
+            LeapCli.leapfile.set('environment', environment)
+            log 0, :saved, "Leapfile with environment set to #{environment}."
+          end
+        end
+      end
+
+      c.desc "Unpin the environment. All subsequent commands will apply to all nodes."
+      c.command :unpin do |unpin|
+        unpin.action do |global_options, options, args|
+          LeapCli.leapfile.unset('environment')
+          log 0, :saved, "Leapfile, removing environment property."
+        end
+      end
+
+      c.default_command :ls
+    end
+
+    protected
+
+  end
+end
\ No newline at end of file
index be9163b23c42266744f99451315af79258b083f8..b8d7739e2b864bf82f1d262c9f2649b1716537e8 100644 (file)
@@ -30,9 +30,10 @@ module LeapCli; module Commands
         if args.any?
           NodeTable.new(manager.filter(args), colors).run
         else
-          TagTable.new('SERVICES', manager.services, colors).run
-          TagTable.new('TAGS', manager.tags, colors).run
-          NodeTable.new(manager.nodes, colors).run
+          environment = LeapCli.leapfile.environment || '_all_'
+          TagTable.new('SERVICES', manager.env(environment).services, colors).run
+          TagTable.new('TAGS', manager.env(environment).tags, colors).run
+          NodeTable.new(manager.filter(), colors).run
         end
       end
     end
@@ -41,7 +42,6 @@ module LeapCli; module Commands
   private
 
   def self.print_node_properties(nodes, properties)
-    node_list = manager.nodes
     properties = properties.split(',')
     max_width = nodes.keys.inject(0) {|max,i| [i.size,max].max}
     nodes.each_node do |node|
@@ -75,6 +75,7 @@ module LeapCli; module Commands
           column "NODES", :width => HighLine::SystemExtensions.terminal_size.first - max_width - 2, :padding => 2
         end
         tags.each do |tag|
+          next if @tag_list[tag].node_list.empty?
           row :color => @colors[1] do
             column tag
             column @tag_list[tag].node_list.keys.sort.join(', ')
index 835eada9a7d4a0cd7a316f4b232e19864cb03663..2e5c34e4d8a3501b4f8abd1677eeed1f3f0aea46 100644 (file)
@@ -48,13 +48,6 @@ module LeapCli; module Commands
       bail! { log :missing, "platform directory '#{Path.platform}'" }
     end
 
-    if LeapCli.leapfile.platform_branch && LeapCli::Util.is_git_directory?(Path.platform)
-      branch = LeapCli::Util.current_git_branch(Path.platform)
-      if branch != LeapCli.leapfile.platform_branch
-        bail! "Wrong branch for #{Path.platform}. Was '#{branch}', should be '#{LeapCli.leapfile.platform_branch}'. Edit Leapfile to disable this check."
-      end
-    end
-
     #
     # set log file
     #
index 21dafd161fcce55c4f7c884f9d56d3e5010af431..e11d5c6c37fd7c20d05fb873d673558358f55553 100644 (file)
@@ -64,9 +64,24 @@ module LeapCli
         e
       end
 
-      def services; env('default').services; end
-      def tags; env('default').tags; end
-      def provider; env('default').provider; end
+      #
+      # The default accessors for services, tags, and provider.
+      # For these defaults, use 'default' environment, or whatever
+      # environment is pinned.
+      #
+      def services
+        env(default_environment).services
+      end
+      def tags
+        env(default_environment).tags
+      end
+      def provider
+        env(default_environment).provider
+      end
+
+      def default_environment
+        LeapCli.leapfile.environment
+      end
 
       ##
       ## IMPORT EXPORT
@@ -90,8 +105,8 @@ module LeapCli
         @secrets  = load_json(    Path.named_path(:secrets_config,      @provider_dir), Config::Secrets)
         @common.inherit_from! @base_common
 
-        # load provider services, tags, and provider.json, DEFAULT environment
-        log 3, :loading, 'default environment.........'
+        # For the default environment, load provider services, tags, and provider.json
+        log 3, :loading, 'default environment...'
         env('default') do |e|
           e.services = load_all_json(Path.named_path([:service_config, '*'], @provider_dir), Config::Tag, :no_dots => true)
           e.tags     = load_all_json(Path.named_path([:tag_config, '*'],     @provider_dir), Config::Tag, :no_dots => true)
@@ -102,17 +117,28 @@ module LeapCli
           validate_provider(e.provider)
         end
 
-        # load provider services, tags, and provider.json, OTHER environments
+        # create a special '_all_' environment, used for tracking the union
+        # of all the environments
+        env('_all_') do |e|
+          e.services = Config::ObjectList.new
+          e.tags     = Config::ObjectList.new
+          e.provider = Config::Provider.new
+          e.services.inherit_from! env('default').services
+          e.tags.inherit_from!     env('default').tags
+          e.provider.inherit_from! env('default').provider
+        end
+
+        # For each defined environment, load provider services, tags, and provider.json.
         environment_names.each do |ename|
           next unless ename
-          log 3, :loading, '%s environment.........' % ename
+          log 3, :loading, '%s environment...' % ename
           env(ename) do |e|
             e.services = load_all_json(Path.named_path([:service_env_config, '*', ename], @provider_dir), Config::Tag)
             e.tags     = load_all_json(Path.named_path([:tag_env_config, '*', ename],     @provider_dir), Config::Tag)
             e.provider = load_json(    Path.named_path([:provider_env_config, ename],     @provider_dir), Config::Provider)
-            e.services.inherit_from! env.services
-            e.tags.inherit_from!     env.tags
-            e.provider.inherit_from! env.provider
+            e.services.inherit_from! env('default').services
+            e.tags.inherit_from!     env('default').tags
+            e.provider.inherit_from! env('default').provider
             validate_provider(e.provider)
           end
         end
@@ -123,10 +149,8 @@ module LeapCli
           @nodes[name] = apply_inheritance(node)
         end
 
-        # remove disabled nodes
-        unless options[:include_disabled]
-          remove_disabled_nodes
-        end
+        # do some node-list post-processing
+        cleanup_node_lists(options)
 
         # apply control files
         @nodes.each do |name, node|
@@ -209,22 +233,31 @@ module LeapCli
       #
       # if conditions is prefixed with +, then it works like an AND. Otherwise, it works like an OR.
       #
+      # The environment is pinned, then all filters get an automatic +environment_name
+      # applied (whatever the name happens to be).
+      #
       # options:
       # :local -- if :local is false and the filter is empty, then local nodes are excluded.
+      # :nopin -- if true, ignore environment pinning
       #
-      def filter(filters, options={})
-        if filters.empty?
+      def filter(filters=nil, options={})
+        # handle empty filter
+        if filters.nil? || filters.empty?
+          node_list = self.nodes
+          if LeapCli.leapfile.environment
+            node_list = node_list[:environment => LeapCli.leapfile.environment_filter]
+          end
           if options[:local] === false
-            return nodes[:environment => '!local']
-          else
-            return nodes
+            node_list = node_list[:environment => '!local']
           end
+          return node_list
         end
+
+        # handle non-empty filters
         if filters[0] =~ /^\+/
           # don't let the first filter have a + prefix
           filters[0] = filters[0][1..-1]
         end
-
         node_list = Config::ObjectList.new
         filters.each do |filter|
           if filter =~ /^\+/
@@ -240,6 +273,12 @@ module LeapCli
             node_list.merge!(nodes_for_name(filter))
           end
         end
+
+        # optionally apply environment pin
+        if !options[:nopin] && LeapCli.leapfile.environment
+          node_list = node_list[:environment => environment_filter]
+        end
+
         return node_list
       end
 
@@ -414,7 +453,6 @@ module LeapCli
               raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions
             else
               new_node.deep_merge!(service)
-              self.services[node_service].node_list.add(name, new_node)
             end
           end
         end
@@ -432,7 +470,6 @@ module LeapCli
               raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions
             else
               new_node.deep_merge!(tag)
-              self.tags[node_tag].node_list.add(name, new_node)
             end
           end
         end
@@ -446,28 +483,35 @@ module LeapCli
         apply_inheritance(node, true)
       end
 
-      def remove_disabled_nodes
+      #
+      # does some final clean at the end of loading nodes.
+      # this includes removing disabled nodes, and populating
+      # the services[x].node_list and tags[x].node_list
+      #
+      def cleanup_node_lists(options)
         @disabled_nodes = Config::ObjectList.new
         @nodes.each do |name, node|
-          unless node.enabled
-            log 2, :skipping, "disabled node #{name}."
-            @nodes.delete(name)
-            @disabled_nodes[name] = node
+          if node.enabled || options[:include_disabled]
             if node['services']
               node['services'].to_a.each do |node_service|
-                self.services[node_service].node_list.delete(node.name)
+                env(node.environment).services[node_service].node_list.add(node.name, node)
+                env('_all_').services[node_service].node_list.add(node.name, node)
               end
             end
             if node['tags']
               node['tags'].to_a.each do |node_tag|
-                self.tags[node_tag].node_list.delete(node.name)
+                env(node.environment).tags[node_tag].node_list.add(node.name, node)
+                env('_all_').tags[node_tag].node_list.add(node.name, node)
               end
             end
+          elsif !options[:include_disabled]
+            log 2, :skipping, "disabled node #{name}."
+            @nodes.delete(name)
+            @disabled_nodes[name] = node
           end
         end
       end
 
-
       #
       # returns a set of nodes corresponding to a single name, where name could be a node name, service name, or tag name.
       #
index e5e719d34fa18577284107cd34a0244aa551aed8..31f4f762e3e525c7d787138e0a9514e8b89a86ea 100644 (file)
@@ -13,6 +13,13 @@ module LeapCli; module Config
       super(manager)
       @node_list = Config::ObjectList.new
     end
+
+    # don't copy the node list pointer when this object is dup'ed.
+    def initialize_copy(orig)
+      super
+      @node_list = Config::ObjectList.new
+    end
+
   end
 
 end; end
index bdf2c373cedba762a130482aabde4c3e73f11e4c..8895f4dc1fd8b11a45e3100e49bc306ba6837017 100644 (file)
@@ -16,13 +16,28 @@ module LeapCli
     attr_accessor :leap_version
     attr_accessor :log
     attr_accessor :vagrant_network
-    attr_accessor :platform_branch
-    attr_accessor :allow_production_deploy
+    attr_accessor :environment
 
     def initialize
       @vagrant_network = '10.5.5.0/24'
     end
 
+    #
+    # The way the Leapfile handles pinning of environment (self.environment) is a little tricky.
+    # If self.environment is nil, then there is no pin. If self.environment is 'default', then
+    # there is a pin to the default environment. The problem is that an environment of nil
+    # is used to indicate the default environment in node properties.
+    #
+    # This method returns the environment tag as needed when filtering nodes.
+    #
+    def environment_filter
+      if self.environment == 'default'
+        nil
+      else
+        self.environment
+      end
+    end
+
     def load(search_directory=nil)
       directory = File.expand_path(find_in_directory_tree('Leapfile', search_directory))
       if directory == '/'
@@ -33,7 +48,7 @@ module LeapCli
         #
         @provider_directory_path = directory
         read_settings(directory + '/Leapfile')
-        read_settings(ENV['HOME'] + '/.leaprc')
+        read_settings(leaprc_path)
         @platform_directory_path = File.expand_path(@platform_directory_path || '../leap_platform', @provider_directory_path)
 
         #
@@ -51,20 +66,55 @@ module LeapCli
                      "You need platform version #{LeapCli::COMPATIBLE_PLATFORM_VERSION.first} to #{LeapCli::COMPATIBLE_PLATFORM_VERSION.last}."
         end
 
-        #
-        # set defaults
-        #
-        if @allow_production_deploy.nil?
-          # by default, only allow production deploys from 'master' or if not a git repo
-          @allow_production_deploy = !LeapCli::Util.is_git_directory?(@provider_directory_path) ||
-            LeapCli::Util.current_git_branch(@provider_directory_path) == 'master'
+        unless @allow_production_deploy.nil?
+          Util::log 0, :warning, "in Leapfile: @allow_production_deploy is no longer supported."
+        end
+        unless @platform_branch.nil?
+          Util::log 0, :warning, "in Leapfile: @platform_branch is no longer supported."
         end
         return true
       end
     end
 
+    def set(property, value)
+      edit_leaprc(property, value)
+    end
+
+    def unset(property)
+      edit_leaprc(property)
+    end
+
     private
 
+    #
+    # adds or removes a line to .leaprc for this particular provider directory.
+    # if value is nil, the line is removed. if not nil, it is added or replaced.
+    #
+    def edit_leaprc(property, value=nil)
+      file_path = leaprc_path
+      lines = []
+      if File.exists?(file_path)
+        regexp = /self\.#{Regexp.escape(property)} = .*? if @provider_directory_path == '#{Regexp.escape(@provider_directory_path)}'/
+        File.readlines(file_path).each do |line|
+          unless line =~ regexp
+            lines << line
+          end
+        end
+      end
+      unless value.nil?
+        lines << "self.#{property} = #{value.inspect} if @provider_directory_path == '#{@provider_directory_path}'\n"
+      end
+      File.open(file_path, 'w') do |f|
+        f.write(lines.join)
+      end
+    rescue Errno::EACCES, IOError => exc
+      Util::bail! :error, "trying to save ~/.leaprc (#{exc})."
+    end
+
+    def leaprc_path
+      File.join(ENV['HOME'], '.leaprc')
+    end
+
     def read_settings(file)
       if File.exists? file
         Util::log 2, :read, file
index f8a725b9d3a8978ac42ca43063497bfb98aad3b9..019e267eaee7b97969d482ff9ffd1ea6c5145aa7 100644 (file)
@@ -1,6 +1,6 @@
 module LeapCli
   unless defined?(LeapCli::VERSION)
-    VERSION = '1.5.8'
+    VERSION = '1.5.9'
     COMPATIBLE_PLATFORM_VERSION = '0.5.3'..'1.99'
     SUMMARY = 'Command line interface to the LEAP platform'
     DESCRIPTION = 'The command "leap" can be used to manage a bevy of servers running the LEAP platform from the comfort of your own home.'