diff options
| -rw-r--r-- | lib/leap_cli/commands/compile.rb | 2 | ||||
| -rw-r--r-- | lib/leap_cli/commands/list.rb | 27 | ||||
| -rw-r--r-- | lib/leap_cli/commands/test.rb | 2 | ||||
| -rw-r--r-- | lib/leap_cli/config/macros.rb | 2 | ||||
| -rw-r--r-- | lib/leap_cli/config/manager.rb | 132 | ||||
| -rw-r--r-- | lib/leap_cli/util/remote_command.rb | 27 | ||||
| -rw-r--r-- | lib/leap_cli/version.rb | 4 | ||||
| -rw-r--r-- | test/leap_platform/platform.rb | 18 | ||||
| -rw-r--r-- | test/test_helper.rb | 2 | 
9 files changed, 147 insertions, 69 deletions
diff --git a/lib/leap_cli/commands/compile.rb b/lib/leap_cli/commands/compile.rb index 11e6e35..e96cb6a 100644 --- a/lib/leap_cli/commands/compile.rb +++ b/lib/leap_cli/commands/compile.rb @@ -125,7 +125,7 @@ module LeapCli        end        # all other records -      manager.environments.each do |env| +      manager.environment_names.each do |env|          next if env == 'local'          nodes = manager.nodes[:environment => env]          next unless nodes.any? diff --git a/lib/leap_cli/commands/list.rb b/lib/leap_cli/commands/list.rb index 15b91d5..5b84113 100644 --- a/lib/leap_cli/commands/list.rb +++ b/lib/leap_cli/commands/list.rb @@ -15,6 +15,11 @@ module LeapCli; module Commands      c.flag 'print', :desc => 'What attributes to print (optional)'      c.switch 'disabled', :desc => 'Include disabled nodes in the list.', :negatable => false      c.action do |global_options,options,args| +      if global_options[:color] +        colors = ['cyan', 'white'] +      else +        colors = [nil, nil] +      end        puts        if options['disabled']          manager.load(:include_disabled => true) # reload, with disabled nodes @@ -23,11 +28,11 @@ module LeapCli; module Commands          print_node_properties(manager.filter(args), options['print'])        else          if args.any? -          NodeTable.new(manager.filter(args)).run +          NodeTable.new(manager.filter(args), colors).run          else -          TagTable.new('SERVICES', manager.services).run -          TagTable.new('TAGS', manager.tags).run -          NodeTable.new(manager.nodes).run +          TagTable.new('SERVICES', manager.services, colors).run +          TagTable.new('TAGS', manager.tags, colors).run +          NodeTable.new(manager.nodes, colors).run          end        end      end @@ -57,20 +62,21 @@ module LeapCli; module Commands    class TagTable      include CommandLineReporter -    def initialize(heading, tag_list) +    def initialize(heading, tag_list, colors)        @heading = heading        @tag_list = tag_list +      @colors = colors      end      def run        tags = @tag_list.keys.sort        max_width = [20, (tags+[@heading]).inject(0) {|max,i| [i.size,max].max}].max        table :border => false do -        row :color => 'cyan'  do +        row :color => @colors[0]  do            column @heading, :align => 'right', :width => max_width            column "NODES", :width => HighLine::SystemExtensions.terminal_size.first - max_width - 2, :padding => 2          end          tags.each do |tag| -          row :color => 'white' do +          row :color => @colors[1] do              column tag              column @tag_list[tag].node_list.keys.sort.join(', ')            end @@ -85,8 +91,9 @@ module LeapCli; module Commands    #    class NodeTable      include CommandLineReporter -    def initialize(node_list) +    def initialize(node_list, colors)        @node_list = node_list +      @colors = colors      end      def run        rows = @node_list.keys.sort.collect do |node_name| @@ -102,13 +109,13 @@ module LeapCli; module Commands        max_service_width = (rows.map{|i|i[1]} + ["SERVICES"]).inject(0) {|max,i| [i.size+padding+padding,max].max}        max_tag_width     = (rows.map{|i|i[2]} + ["TAGS"]    ).inject(0) {|max,i| [i.size,max].max}        table :border => false do -        row :color => 'cyan'  do +        row :color => @colors[0]  do            column "NODES", :align => 'right', :width => max_node_width            column "SERVICES", :width => max_service_width, :padding => 2            column "TAGS", :width => max_tag_width          end          rows.each do |r| -          row :color => 'white' do +          row :color => @colors[1] do              column r[0]              column r[1]              column r[2] diff --git a/lib/leap_cli/commands/test.rb b/lib/leap_cli/commands/test.rb index 024ca25..2584a69 100644 --- a/lib/leap_cli/commands/test.rb +++ b/lib/leap_cli/commands/test.rb @@ -46,7 +46,7 @@ module LeapCli; module Commands      assert_config! 'provider.ca.client_certificates.unlimited_prefix'      assert_config! 'provider.ca.client_certificates.limited_prefix'      template = read_file! Path.find_file(:test_client_openvpn_template) -    manager.environments.each do |env| +    manager.environment_names.each do |env|        vpn_nodes = manager.nodes[:environment => env][:services => 'openvpn']['openvpn.allow_limited' => true]        if vpn_nodes.any?          generate_test_client_cert(provider.ca.client_certificates.limited_prefix) do |key, cert| diff --git a/lib/leap_cli/config/macros.rb b/lib/leap_cli/config/macros.rb index b5dc3b8..2eabdd0 100644 --- a/lib/leap_cli/config/macros.rb +++ b/lib/leap_cli/config/macros.rb @@ -25,7 +25,7 @@ module LeapCli; module Config      # grab an environment appropriate provider      #      def provider -      global.providers[@node.environment] || global.provider +      global.env(@node.environment).provider      end      # diff --git a/lib/leap_cli/config/manager.rb b/lib/leap_cli/config/manager.rb index 7969d40..1a66bff 100644 --- a/lib/leap_cli/config/manager.rb +++ b/lib/leap_cli/config/manager.rb @@ -9,16 +9,24 @@ end  module LeapCli    module Config +    class Environment +      attr_accessor :services, :tags, :provider +    end +      #      # A class to manage all the objects in all the configuration files.      #      class Manager +      def initialize +        @environments = {} # hash of `Environment` objects, keyed by name. +      end +        ##        ## ATTRIBUTES        ## -      attr_reader :services, :tags, :nodes, :provider, :providers, :common, :secrets +      attr_reader :nodes, :common, :secrets        attr_reader :base_services, :base_tags, :base_provider, :base_common        # @@ -32,10 +40,24 @@ module LeapCli        # returns an Array of all the environments defined for this provider.        # the returned array includes nil (for the default environment)        # -      def environments -        @environments ||= [nil] + self.tags.collect {|name, tag| tag['environment']}.compact +      def environment_names +        @environment_names ||= [nil] + env.tags.collect {|name, tag| tag['environment']}.compact        end +      # +      # Returns the appropriate environment variable +      # +      def env(env=nil) +        env ||= 'default' +        e = @environments[env] ||= Environment.new +        yield e if block_given? +        e +      end + +      def services; env('default').services; end +      def tags; env('default').tags; end +      def provider; env('default').provider; end +        ##        ## IMPORT EXPORT        ## @@ -48,34 +70,43 @@ module LeapCli          # load 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::Provider) +        @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::Provider)          # 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), 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::Provider) -        @secrets  = load_json(Path.named_path(:secrets_config,  @provider_dir), Config::Secrets) - -        ### BEGIN HACK -        ### remove this after it is likely that no one has any old-style secrets.json -        if @secrets['webapp_secret_token'] -          @secrets = Config::Secrets.new -          Util::log :warning, "Creating all new secrets.json (new version is scoped by environment). Make sure to do a full deploy so that new secrets take effect." +        @nodes    = load_all_json(Path.named_path([:node_config, '*'],  @provider_dir), Config::Node) +        @common   = load_json(    Path.named_path(:common_config,       @provider_dir), Config::Object) +        @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.........' +        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) +          e.provider = load_json(    Path.named_path(:provider_config,       @provider_dir), Config::Provider, :assert => true) +          e.services.inherit_from! @base_services +          e.tags.inherit_from!     @base_tags +          e.provider.inherit_from! @base_provider +          validate_provider(e.provider) +        end + +        # load provider services, tags, and provider.json, OTHER environments +        environment_names.each do |ename| +          next unless 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 +            validate_provider(e.provider) +          end          end -        ### END HACK -        # inherit -        @services.inherit_from! base_services -        @tags.inherit_from!     base_tags -        @common.inherit_from!   base_common -        @provider.inherit_from! base_provider          @nodes.each do |name, node|            Util::assert! name =~ /^[0-9a-z-]+$/, "Illegal character(s) used in node name '#{name}'"            @nodes[name] = apply_inheritance(node) @@ -84,19 +115,6 @@ module LeapCli          unless options[:include_disabled]            remove_disabled_nodes          end - -        # load optional environment specific providers -        validate_provider(@provider) -        @providers = {} -        environments.each do |env| -          if Path.defined?(:provider_env_config) -            provider_path = Path.named_path([:provider_env_config, env], @provider_dir) -            providers[env] = load_json(provider_path, Config::Provider) -            providers[env].inherit_from! @provider -            validate_provider(providers[env]) -          end -        end -        end        # @@ -232,12 +250,13 @@ module LeapCli        private -      def load_all_json(pattern, object_class) +      def load_all_json(pattern, object_class, options={})          results = Config::ObjectList.new          Dir.glob(pattern).each do |filename| +          next if options[:no_dots] && File.basename(filename) !~ /^[^\.]*\.json$/            obj = load_json(filename, object_class)            if obj -            name = File.basename(filename).force_encoding('utf-8').sub(/\.json$/,'') +            name = File.basename(filename).force_encoding('utf-8').sub(/^([^\.]+).*\.json$/,'\1')              obj['name'] ||= name              results[name] = obj            end @@ -245,7 +264,10 @@ module LeapCli          results        end -      def load_json(filename, object_class) +      def load_json(filename, object_class, options={}) +        if options[:assert] +          Util::assert_files_exist!(filename) +        end          if !File.exists?(filename)            return object_class.new(self)          end @@ -311,20 +333,32 @@ module LeapCli          new_node = Config::Node.new(self)          name = node.name +        # Guess the environment of the node from the tag names. +        # (Technically, this is wrong: a tag that sets the environment might not be +        #  named the same as the environment. This code assumes that it is). +        node_env = self.env +        if node['tags'] +          node['tags'].to_a.each do |tag| +            if self.environment_names.include?(tag) +              node_env = self.env(tag) +            end +          end +        end +          # inherit from common          new_node.deep_merge!(@common)          # inherit from services          if node['services']            node['services'].to_a.each do |node_service| -            service = @services[node_service] +            service = node_env.services[node_service]              if service.nil?                msg = 'in node "%s": the service "%s" does not exist.' % [node['name'], node_service]                log 0, :error, msg                raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions              else                new_node.deep_merge!(service) -              service.node_list.add(name, new_node) +              self.services[node_service].node_list.add(name, new_node)              end            end          end @@ -335,14 +369,14 @@ module LeapCli          end          if node['tags']            node['tags'].to_a.each do |node_tag| -            tag = @tags[node_tag] +            tag = node_env.tags[node_tag]              if tag.nil?                msg = 'in node "%s": the tag "%s" does not exist.' % [node['name'], node_tag]                log 0, :error, msg                raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions              else                new_node.deep_merge!(tag) -              tag.node_list.add(name, new_node) +              self.tags[node_tag].node_list.add(name, new_node)              end            end          end @@ -365,12 +399,12 @@ module LeapCli              @disabled_nodes[name] = node              if node['services']                node['services'].to_a.each do |node_service| -                @services[node_service].node_list.delete(node.name) +                self.services[node_service].node_list.delete(node.name)                end              end              if node['tags']                node['tags'].to_a.each do |node_tag| -                @tags[node_tag].node_list.delete(node.name) +                self.tags[node_tag].node_list.delete(node.name)                end              end            end diff --git a/lib/leap_cli/util/remote_command.rb b/lib/leap_cli/util/remote_command.rb index 6972bbb..b4b2b1f 100644 --- a/lib/leap_cli/util/remote_command.rb +++ b/lib/leap_cli/util/remote_command.rb @@ -48,9 +48,34 @@ module LeapCli; module Util; module RemoteCommand    #    # For available options, see http://net-ssh.github.com/net-ssh/classes/Net/SSH.html#method-c-start    # +  # Capistrano has some very evil behavior in it's ssh.rb: +  # +  #   ssh_options = Net::SSH.configuration_for( +  #     server.host, ssh_options.fetch(:config, true) +  #   ).merge(ssh_options) +  #   # Once we've loaded the config, we don't need Net::SSH to do it again. +  #   ssh_options[:config] = false +  # +  # Net:SSH is supposed to call Net::SSH.configuration_for, but Capistrano is doing it +  # in advance and then disabling loading of configs. +  # +  # The result of this is the following: if you have IdentityFile in your ~/.ssh/config +  # file, then the above code will transform the ssh_options by reading ~/.ssh/config +  # and adding the keys specified via IdentityFile to ssh_options... +  # AND IT WILL SET :keys_only TO TRUE. +  # +  # The problem is that :keys_only will disable Net:SSH's ability to use ssh-agent. +  # With :keys_only set to true, it will not consult the ssh-agent at all. +  # +  # So nice of capistrano to parse ~/.ssh/config for us, but then add flags to the +  # ssh_options that prevent's these options from being useful. +  # +  # The current hackaround is to force :keys_only to be false. This allows the config +  # to be read and also allows ssh-agent to still be used. +  #    def ssh_options      { -      :config => "~/.ssh/config", +      :keys_only => false, # Don't you dare change this.        :global_known_hosts_file => path(:known_hosts),        :user_known_hosts_file => '/dev/null',        :paranoid => true diff --git a/lib/leap_cli/version.rb b/lib/leap_cli/version.rb index 9f8e381..7c39e05 100644 --- a/lib/leap_cli/version.rb +++ b/lib/leap_cli/version.rb @@ -1,7 +1,7 @@  module LeapCli    unless defined?(LeapCli::VERSION) -    VERSION = '1.5.3' -    COMPATIBLE_PLATFORM_VERSION = '0.3.0'..'1.99' +    VERSION = '1.5.4' +    COMPATIBLE_PLATFORM_VERSION = '0.5.2'..'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.'      LOAD_PATHS = ['lib', 'vendor/certificate_authority/lib', 'vendor/rsync_command/lib'] diff --git a/test/leap_platform/platform.rb b/test/leap_platform/platform.rb index 9f63b4c..52bb8df 100644 --- a/test/leap_platform/platform.rb +++ b/test/leap_platform/platform.rb @@ -1,15 +1,16 @@ +# encoding: utf-8  #  # These are variables defined by this leap_platform and used by leap_cli.  #  Leap::Platform.define do -  self.version = "1.1.2" -  self.compatible_cli = "1.1.2".."1.99" +  self.version = "0.5.2" +  self.compatible_cli = "1.5.4".."1.99"    #    # the facter facts that should be gathered    # -  self.facts = ["ec2_local_ipv4"] +  self.facts = ["ec2_local_ipv4", "ec2_public_ipv4"]    #    # the named paths for this platform @@ -31,6 +32,11 @@ Leap::Platform.define do      :service_config   => 'services/#{arg}.json',      :tag_config       => 'tags/#{arg}.json', +    # input config files, environmentally scoped +    :provider_env_config  => 'provider.#{arg}.json', +    :service_env_config   => 'services/#{arg}.#{arg}.json', +    :tag_env_config       => 'tags/#{arg}.#{arg}.json', +      # input templates      :provider_json_template        => 'files/service-definitions/provider.json.erb',      :eip_service_json_template     => 'files/service-definitions/#{arg}/eip-service.json.erb', @@ -43,6 +49,8 @@ Leap::Platform.define do      :user_pgp         => 'users/#{arg}/#{arg}_pgp.pub',      :known_hosts      => 'files/ssh/known_hosts',      :authorized_keys  => 'files/ssh/authorized_keys', +    :monitor_pub_key  => 'files/ssh/monitor_ssh.pub', +    :monitor_priv_key => 'files/ssh/monitor_ssh',      :ca_key           => 'files/ca/ca.key',      :ca_cert          => 'files/ca/ca.crt',      :client_ca_key    => 'files/ca/client_ca.key', @@ -73,5 +81,9 @@ Leap::Platform.define do    self.node_files = [      :node_config, :hiera, :node_x509_cert, :node_x509_key, :node_ssh_pub_key    ] + +  self.monitor_username = 'monitor' + +  self.reserved_usernames = ['monitor']  end diff --git a/test/test_helper.rb b/test/test_helper.rb index a5eccbb..b631c23 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -32,7 +32,7 @@ class MiniTest::Unit::TestCase    end    def leap_bin(*args) -    `cd #{test_provider_path} && #{ruby_path} #{base_path}/bin/leap #{args.join ' '}` +    `cd #{test_provider_path} && #{ruby_path} #{base_path}/bin/leap --no-color #{args.join ' '}`    end    #def test_platform_path  | 
