summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2016-06-29 16:52:31 -0700
committerelijah <elijah@riseup.net>2016-06-29 16:52:31 -0700
commit64704deccddd9db46ea9ec4992207b8b2d51f1f8 (patch)
tree3be8043a8fe197c01ae75f3d96d45025efb10bd7
parentff97b58c4e599959e3c0792f8bffd419e65d0ed6 (diff)
move everything we can to leap_platform/lib/leap_cli
-rw-r--r--lib/leap/platform.rb90
-rw-r--r--lib/leap_cli.rb20
-rw-r--r--lib/leap_cli/bootstrap.rb17
-rw-r--r--lib/leap_cli/config/environment.rb180
-rw-r--r--lib/leap_cli/config/filter.rb178
-rw-r--r--lib/leap_cli/config/manager.rb422
-rw-r--r--lib/leap_cli/config/node.rb78
-rw-r--r--lib/leap_cli/config/object.rb428
-rw-r--r--lib/leap_cli/config/object_list.rb209
-rw-r--r--lib/leap_cli/config/provider.rb22
-rw-r--r--lib/leap_cli/config/secrets.rb87
-rw-r--r--lib/leap_cli/config/sources.rb11
-rw-r--r--lib/leap_cli/config/tag.rb25
-rw-r--r--lib/leap_cli/leapfile.rb64
-rw-r--r--lib/leap_cli/log.rb209
-rw-r--r--lib/leap_cli/util.rb8
-rw-r--r--lib/leap_cli/util/remote_command.rb158
-rw-r--r--lib/leap_cli/util/secret.rb55
-rw-r--r--lib/leap_cli/util/x509.rb33
19 files changed, 86 insertions, 2208 deletions
diff --git a/lib/leap/platform.rb b/lib/leap/platform.rb
deleted file mode 100644
index 9112ef3..0000000
--- a/lib/leap/platform.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-module Leap
-
- class Platform
- class << self
- #
- # configuration
- #
-
- attr_reader :version
- attr_reader :compatible_cli
- attr_accessor :facts
- attr_accessor :paths
- attr_accessor :node_files
- attr_accessor :monitor_username
- attr_accessor :reserved_usernames
-
- attr_accessor :hiera_dir
- attr_accessor :hiera_path
- attr_accessor :files_dir
- attr_accessor :leap_dir
- attr_accessor :init_path
-
- attr_accessor :default_puppet_tags
-
- def define(&block)
- # some defaults:
- @reserved_usernames = []
- @hiera_dir = '/etc/leap'
- @hiera_path = '/etc/leap/hiera.yaml'
- @leap_dir = '/srv/leap'
- @files_dir = '/srv/leap/files'
- @init_path = '/srv/leap/initialized'
- @default_puppet_tags = []
-
- self.instance_eval(&block)
-
- @version ||= Gem::Version.new("0.0")
- end
-
- def version=(version)
- @version = Gem::Version.new(version)
- end
-
- def compatible_cli=(range)
- @compatible_cli = range
- @minimum_cli_version = Gem::Version.new(range.first)
- @maximum_cli_version = Gem::Version.new(range.last)
- end
-
- #
- # return true if the cli_version is compatible with this platform.
- #
- def compatible_with_cli?(cli_version)
- cli_version = Gem::Version.new(cli_version)
- cli_version >= @minimum_cli_version && cli_version <= @maximum_cli_version
- end
-
- #
- # return true if the platform version is within the specified range.
- #
- def version_in_range?(range)
- if range.is_a? String
- range = range.split('..')
- end
- minimum_platform_version = Gem::Version.new(range.first)
- maximum_platform_version = Gem::Version.new(range.last)
- @version >= minimum_platform_version && @version <= maximum_platform_version
- end
-
- def major_version
- if @version.segments.first == 0
- @version.segments[0..1].join('.')
- else
- @version.segments.first
- end
- end
-
- def method_missing(method, *args)
- puts
- puts "WARNING:"
- puts " leap_cli is out of date and does not understand `#{method}`."
- puts " called from: #{caller.first}"
- puts " please upgrade to a newer leap_cli"
- end
-
- end
-
- end
-
-end \ No newline at end of file
diff --git a/lib/leap_cli.rb b/lib/leap_cli.rb
index 36718e3..c0e139e 100644
--- a/lib/leap_cli.rb
+++ b/lib/leap_cli.rb
@@ -1,6 +1,6 @@
module LeapCli
- module Commands; end # for commands in leap_cli/commands
- module Macro; end # for macros in leap_platform/provider_base/lib/macros
+ module Commands; end # for commands in leap_platform/lib/leap_cli/commands
+ module Macro; end # for macros in leap_platform/lib/leap_cli/macros
end
$ruby_version = RUBY_VERSION.split('.').collect{ |i| i.to_i }.extend(Comparable)
@@ -13,8 +13,6 @@ $:.unshift(File.expand_path('../leap_cli/override',__FILE__))
require 'rubygems'
gem 'gli', '~> 2.12', '>= 2.12.0'
-require 'leap/platform'
-
require 'leap_cli/version'
require 'leap_cli/exceptions'
@@ -31,27 +29,13 @@ require 'leap_cli/core_ext/yaml'
require 'leap_cli/log'
require 'leap_cli/path'
require 'leap_cli/util'
-require 'leap_cli/util/secret'
-require 'leap_cli/util/x509'
require 'leap_cli/bootstrap'
-require 'leap_cli/config/object'
-require 'leap_cli/config/node'
-require 'leap_cli/config/tag'
-require 'leap_cli/config/provider'
-require 'leap_cli/config/secrets'
-require 'leap_cli/config/object_list'
-require 'leap_cli/config/filter'
-require 'leap_cli/config/environment'
-require 'leap_cli/config/manager'
-
require 'leap_cli/markdown_document_listener'
#
# allow everyone easy access to log() command.
#
module LeapCli
- Util.send(:extend, LeapCli::LogCommand)
- Config::Manager.send(:include, LeapCli::LogCommand)
extend LeapCli::LogCommand
end
diff --git a/lib/leap_cli/bootstrap.rb b/lib/leap_cli/bootstrap.rb
index bc43115..f33aa42 100644
--- a/lib/leap_cli/bootstrap.rb
+++ b/lib/leap_cli/bootstrap.rb
@@ -40,6 +40,7 @@ module LeapCli
log_version
end
add_platform_lib_to_path
+ load_platform_libraries
load_commands(app)
load_macros
end
@@ -104,11 +105,10 @@ module LeapCli
elsif !leapfile_optional?(argv)
puts
puts " ="
- log :note, "There is no `Leapfile` in this directory, or any parent directory.\n"+
- " = "+
+ log :NOTE, "There is no `Leapfile` in this directory, or any parent directory.\n"+
+ " = "+
"Without this file, most commands will not be available."
puts " ="
- puts
end
end
@@ -204,5 +204,16 @@ module LeapCli
$LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
end
end
+
+ #
+ # loads libraries that live in the platform and should
+ # always be available.
+ #
+ def load_platform_libraries
+ if Path.platform
+ require 'leap_cli/load_libraries'
+ end
+ end
+
end
end
diff --git a/lib/leap_cli/config/environment.rb b/lib/leap_cli/config/environment.rb
deleted file mode 100644
index 398fd02..0000000
--- a/lib/leap_cli/config/environment.rb
+++ /dev/null
@@ -1,180 +0,0 @@
-#
-# All configurations files can be isolated into separate environments.
-#
-# Each config json in each environment inherits from the default environment,
-# which in term inherits from the "_base_" environment:
-#
-# _base_ -- base provider in leap_platform
-# '- default -- environment in provider dir when no env is set
-# '- production -- example environment
-#
-
-module LeapCli; module Config
-
- class Environment
- # the String name of the environment
- attr_accessor :name
-
- # the shared Manager object
- attr_accessor :manager
-
- # hashes of {name => Config::Object}
- attr_accessor :services, :tags, :partials
-
- # a Config::Provider
- attr_accessor :provider
-
- # a Config::Object
- attr_accessor :common
-
- # shared, non-inheritable
- def nodes; @@nodes; end
- def secrets; @@secrets; end
-
- def initialize(manager, name, search_dir, parent, options={})
- @@nodes ||= nil
- @@secrets ||= nil
-
- @manager = manager
- @name = name
-
- load_provider_files(search_dir, options)
-
- if parent
- @services.inherit_from! parent.services, self
- @tags.inherit_from! parent.tags , self
- @partials.inherit_from! parent.partials, self
- @common.inherit_from! parent.common
- @provider.inherit_from! parent.provider
- end
-
- if @provider
- @provider.set_env(name)
- @provider.validate!
- end
- end
-
- def load_provider_files(search_dir, options)
- #
- # load empty environment if search_dir doesn't exist
- #
- if search_dir.nil? || !Dir.exist?(search_dir)
- @services = Config::ObjectList.new
- @tags = Config::ObjectList.new
- @partials = Config::ObjectList.new
- @provider = Config::Provider.new
- @common = Config::Object.new
- return
- end
-
- #
- # inheritable
- #
- if options[:scope]
- scope = options[:scope]
- @services = load_all_json(Path.named_path([:service_env_config, '*', scope], search_dir), Config::Tag, options)
- @tags = load_all_json(Path.named_path([:tag_env_config, '*', scope], search_dir), Config::Tag, options)
- @partials = load_all_json(Path.named_path([:service_env_config, '_*', scope], search_dir), Config::Tag, options)
- @provider = load_json( Path.named_path([:provider_env_config, scope], search_dir), Config::Provider, options)
- @common = load_json( Path.named_path([:common_env_config, scope], search_dir), Config::Object, options)
- else
- @services = load_all_json(Path.named_path([:service_config, '*'], search_dir), Config::Tag, options)
- @tags = load_all_json(Path.named_path([:tag_config, '*'], search_dir), Config::Tag, options)
- @partials = load_all_json(Path.named_path([:service_config, '_*'], search_dir), Config::Tag, options)
- @provider = load_json( Path.named_path(:provider_config, search_dir), Config::Provider, options)
- @common = load_json( Path.named_path(:common_config, search_dir), Config::Object, options)
- end
-
- # remove 'name' from partials, since partials get merged with nodes
- @partials.values.each {|partial| partial.delete('name'); }
-
- #
- # shared: currently non-inheritable
- # load the first ones we find, and only those.
- #
- if @@nodes.nil? || @@nodes.empty?
- @@nodes = load_all_json(Path.named_path([:node_config, '*'], search_dir), Config::Node, options)
- end
- if @@secrets.nil? || @@secrets.empty?
- @@secrets = load_json(Path.named_path(:secrets_config, search_dir), Config::Secrets, options)
- end
- end
-
- #
- # Loads a json template file as a Hash (used only when creating a new node .json
- # file for the first time).
- #
- def template(template)
- path = Path.named_path([:template_config, template], Path.provider_base)
- if File.exist?(path)
- return load_json(path, Config::Object)
- else
- return nil
- end
- end
-
- private
-
- 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$/,'\1')
- obj['name'] ||= name
- if options[:env]
- obj.environment = options[:env]
- end
- results[name] = obj
- end
- end
- results
- end
-
- def load_json(filename, object_class, options={})
- if !File.exist?(filename)
- return object_class.new(self)
- end
-
- Util::log :loading, filename, 3
-
- #
- # Read a JSON file, strip out comments.
- #
- # UTF8 is the default encoding for JSON, but others are allowed:
- # https://www.ietf.org/rfc/rfc4627.txt
- #
- buffer = StringIO.new
- File.open(filename, "rb", :encoding => 'UTF-8') do |f|
- while (line = f.gets)
- next if line =~ /^\s*\/\//
- buffer << line
- end
- end
-
- #
- # force UTF-8
- #
- if $ruby_version >= [1,9]
- string = buffer.string.force_encoding('utf-8')
- else
- string = Iconv.conv("UTF-8//IGNORE", "UTF-8", buffer.string)
- end
-
- # parse json
- begin
- hash = JSON.parse(string, :object_class => Hash, :array_class => Array) || {}
- rescue SyntaxError, JSON::ParserError => exc
- Util::log 0, :error, 'in file "%s":' % filename
- Util::log 0, exc.to_s, :indent => 1
- return nil
- end
- object = object_class.new(self)
- object.deep_merge!(hash)
- return object
- end
-
- end # end Environment
-
-end; end \ No newline at end of file
diff --git a/lib/leap_cli/config/filter.rb b/lib/leap_cli/config/filter.rb
deleted file mode 100644
index 2c80be8..0000000
--- a/lib/leap_cli/config/filter.rb
+++ /dev/null
@@ -1,178 +0,0 @@
-#
-# Many leap_cli commands accept a list of filters to select a subset of nodes for the command to
-# be applied to. This class is a helper for manager to run these filters.
-#
-# Classes other than Manager should not use this class.
-#
-# Filter rules:
-#
-# * A filter consists of a list of tokens
-# * A token may be a service name, tag name, environment name, or node name.
-# * Each token may be optionally prefixed with a plus sign.
-# * Multiple tokens with a plus are treated as an OR condition,
-# but treated as an AND condition with the plus sign.
-#
-# For example
-#
-# * openvpn +development => all nodes with service 'openvpn' AND environment 'development'
-# * openvpn seattle => all nodes with service 'openvpn' OR tag 'seattle'.
-#
-# There can only be one environment specified. Typically, there are also tags
-# for each environment name. These name are treated as environments, not tags.
-#
-module LeapCli
- module Config
- class Filter
-
- #
- # filter -- array of strings, each one a filter
- # options -- hash, possible keys include
- # :nopin -- disregard environment pinning
- # :local -- if false, disallow local nodes
- #
- # A nil value in the filters array indicates
- # the default environment. This is in order to support
- # calls like `manager.filter(environments)`
- #
- def initialize(filters, options, manager)
- @filters = filters.nil? ? [] : filters.dup
- @environments = []
- @options = options
- @manager = manager
-
- # split filters by pulling out items that happen
- # to be environment names.
- if LeapCli.leapfile.environment.nil? || @options[:nopin]
- @environments = []
- else
- @environments = [LeapCli.leapfile.environment]
- end
- @filters.select! do |filter|
- if filter.nil?
- @environments << nil unless @environments.include?(nil)
- false
- else
- filter_text = filter.sub(/^\+/,'')
- if is_environment?(filter_text)
- if filter_text == LeapCli.leapfile.environment
- # silently ignore already pinned environments
- elsif (filter =~ /^\+/ || @filters.first == filter) && !@environments.empty?
- LeapCli::Util.bail! do
- LeapCli::Util.log "Environments are exclusive: no node is in two environments." do
- LeapCli::Util.log "Tried to filter on '#{@environments.join('\' AND \'')}' AND '#{filter_text}'"
- end
- end
- else
- @environments << filter_text
- end
- false
- else
- true
- end
- end
- end
-
- # don't let the first filter have a + prefix
- if @filters[0] =~ /^\+/
- @filters[0] = @filters[0][1..-1]
- end
- end
-
- # actually run the filter, returns a filtered list of nodes
- def nodes()
- if @filters.empty?
- return nodes_for_empty_filter
- else
- return nodes_for_filter
- end
- end
-
- private
-
- def nodes_for_empty_filter
- node_list = @manager.nodes
- if @environments.any?
- node_list = node_list[ @environments.collect{|e|[:environment, env_to_filter(e)]} ]
- end
- if @options[:local] === false
- node_list = node_list[:environment => '!local']
- end
- if @options[:disabled] === false
- node_list = node_list[:environment => '!disabled']
- end
- node_list
- end
-
- def nodes_for_filter
- node_list = Config::ObjectList.new
- @filters.each do |filter|
- if filter =~ /^\+/
- keep_list = nodes_for_name(filter[1..-1])
- node_list.delete_if do |name, node|
- if keep_list[name]
- false
- else
- true
- end
- end
- else
- node_list.merge!(nodes_for_name(filter))
- end
- end
- node_list
- end
-
- private
-
- #
- # returns a set of nodes corresponding to a single name,
- # where name could be a node name, service name, or tag name.
- #
- # For services and tags, we only include nodes for the
- # environments that are active
- #
- def nodes_for_name(name)
- if node = @manager.nodes[name]
- return Config::ObjectList.new(node)
- elsif @environments.empty?
- if @manager.services[name]
- return @manager.env('_all_').services[name].node_list
- elsif @manager.tags[name]
- return @manager.env('_all_').tags[name].node_list
- else
- LeapCli::Util.log :warning, "filter '#{name}' does not match any node names, tags, services, or environments."
- return Config::ObjectList.new
- end
- else
- node_list = Config::ObjectList.new
- if @manager.services[name]
- @environments.each do |env|
- node_list.merge!(@manager.env(env).services[name].node_list)
- end
- elsif @manager.tags[name]
- @environments.each do |env|
- node_list.merge!(@manager.env(env).tags[name].node_list)
- end
- else
- LeapCli::Util.log :warning, "filter '#{name}' does not match any node names, tags, services, or environments."
- end
- return node_list
- end
- end
-
- #
- # when pinning, we use the name 'default' to specify nodes
- # without an environment set, but when filtering, we need to filter
- # on :environment => nil.
- #
- def env_to_filter(environment)
- environment == 'default' ? nil : environment
- end
-
- def is_environment?(text)
- text == 'default' || @manager.environment_names.include?(text)
- end
-
- end
- end
-end
diff --git a/lib/leap_cli/config/manager.rb b/lib/leap_cli/config/manager.rb
deleted file mode 100644
index 80ccbad..0000000
--- a/lib/leap_cli/config/manager.rb
+++ /dev/null
@@ -1,422 +0,0 @@
-# encoding: utf-8
-
-require 'json/pure'
-
-if $ruby_version < [1,9]
- require 'iconv'
-end
-
-module LeapCli
- module Config
-
- #
- # A class to manage all the objects in all the configuration files.
- #
- class Manager
-
- def initialize
- @environments = {} # hash of `Environment` objects, keyed by name.
- Config::Object.send(:include, LeapCli::Macro)
- end
-
- ##
- ## ATTRIBUTES
- ##
-
- #
- # returns the Hash of the contents of facts.json
- #
- def facts
- @facts ||= begin
- content = Util.read_file(:facts)
- if !content || content.empty?
- content = "{}"
- end
- JSON.parse(content)
- rescue SyntaxError, JSON::ParserError => exc
- Util::bail! "Could not parse facts.json -- #{exc}"
- end
- end
-
- #
- # returns an Array of all the environments defined for this provider.
- # the returned array includes nil (for the default environment)
- #
- def environment_names
- @environment_names ||= begin
- [nil] + (env.tags.field('environment') + env.nodes.field('environment')).compact.uniq
- end
- end
-
- #
- # Returns the appropriate environment variable
- #
- def env(env=nil)
- @environments[env || 'default']
- end
-
- #
- # The default accessors
- #
- # For these defaults, use 'default' environment, or whatever
- # environment is pinned.
- #
- # I think it might be an error that these are ever used
- # and I would like to get rid of them.
- #
- def services; env(default_environment).services; end
- def tags; env(default_environment).tags; end
- def partials; env(default_environment).partials; end
- def provider; env(default_environment).provider; end
- def common; env(default_environment).common; end
- def secrets; env(default_environment).secrets; end
- def nodes; env(default_environment).nodes; end
- def template(*args)
- self.env.template(*args)
- end
-
- def default_environment
- LeapCli.leapfile.environment
- end
-
- ##
- ## IMPORT EXPORT
- ##
-
- def add_environment(args)
- if args[:inherit]
- parent = @environments[args.delete(:inherit)]
- else
- parent = nil
- end
- @environments[args[:name]] = Environment.new(
- self,
- args.delete(:name),
- args.delete(:dir),
- parent,
- args
- )
- end
-
- #
- # load .json configuration files
- #
- def load(options = {})
- @provider_dir = Path.provider
-
- # load base
- add_environment(name: '_base_', dir: Path.provider_base)
-
- # load provider
- Util::assert_files_exist!(Path.named_path(:provider_config, @provider_dir))
- add_environment(name: 'default', dir: @provider_dir,
- inherit: '_base_', no_dots: true)
-
- # create a special '_all_' environment, used for tracking
- # the union of all the environments
- add_environment(name: '_all_', inherit: 'default')
-
- # load environments
- environment_names.each do |ename|
- if ename
- log 3, :loading, '%s environment...' % ename
- add_environment(name: ename, dir: @provider_dir,
- inherit: 'default', scope: ename)
- end
- end
-
- # apply inheritance
- env.nodes.each do |name, node|
- Util::assert! name =~ /^[0-9a-z-]+$/, "Illegal character(s) used in node name '#{name}'"
- env.nodes[name] = apply_inheritance(node)
- end
-
- # do some node-list post-processing
- cleanup_node_lists(options)
-
- # apply control files
- env.nodes.each do |name, node|
- control_files(node).each do |file|
- begin
- node.eval_file file
- rescue ConfigError => exc
- if options[:continue_on_error]
- exc.log
- else
- raise exc
- end
- end
- end
- end
- end
-
- #
- # save compiled hiera .yaml files
- #
- # if a node_list is specified, only update those .yaml files.
- # otherwise, update all files, destroying files that are no longer used.
- #
- def export_nodes(node_list=nil)
- updated_hiera = []
- updated_files = []
- existing_hiera = nil
- existing_files = nil
-
- unless node_list
- node_list = env.nodes
- existing_hiera = Dir.glob(Path.named_path([:hiera, '*'], @provider_dir))
- existing_files = Dir.glob(Path.named_path([:node_files_dir, '*'], @provider_dir))
- end
-
- node_list.each_node do |node|
- filepath = Path.named_path([:node_files_dir, node.name], @provider_dir)
- hierapath = Path.named_path([:hiera, node.name], @provider_dir)
- Util::write_file!(hierapath, node.dump_yaml)
- updated_files << filepath
- updated_hiera << hierapath
- end
-
- if @disabled_nodes
- # make disabled nodes appear as if they are still active
- @disabled_nodes.each_node do |node|
- updated_files << Path.named_path([:node_files_dir, node.name], @provider_dir)
- updated_hiera << Path.named_path([:hiera, node.name], @provider_dir)
- end
- end
-
- # remove files that are no longer needed
- if existing_hiera
- (existing_hiera - updated_hiera).each do |filepath|
- Util::remove_file!(filepath)
- end
- end
- if existing_files
- (existing_files - updated_files).each do |filepath|
- Util::remove_directory!(filepath)
- end
- end
- end
-
- def export_secrets(clean_unused_secrets = false)
- if env.secrets.any?
- Util.write_file!([:secrets_config, @provider_dir], env.secrets.dump_json(clean_unused_secrets) + "\n")
- end
- end
-
- ##
- ## FILTERING
- ##
-
- #
- # returns a node list consisting only of nodes that satisfy the filter criteria.
- #
- # filter: condition [condition] [condition] [+condition]
- # condition: [node_name | service_name | tag_name | environment_name]
- #
- # if conditions is prefixed with +, then it works like an AND. Otherwise, it works like an OR.
- #
- # args:
- # filter -- array of filter terms, one per item
- #
- # 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=nil, options={})
- Filter.new(filters, options, self).nodes()
- end
-
- #
- # same as filter(), but exits if there is no matching nodes
- #
- def filter!(filters, options={})
- node_list = filter(filters, options)
- Util::assert! node_list.any?, "Could not match any nodes from '#{filters.join ' '}'"
- return node_list
- end
-
- #
- # returns a single Config::Object that corresponds to a Node.
- #
- def node(name)
- if name =~ /\./
- # probably got a fqdn, since periods are not allowed in node names.
- # so, take the part before the first period as the node name
- name = name.split('.').first
- end
- env.nodes[name]
- end
-
- #
- # returns a single node that is disabled
- #
- def disabled_node(name)
- @disabled_nodes[name]
- end
-
- #
- # yields each node, in sorted order
- #
- def each_node(&block)
- env.nodes.each_node(&block)
- end
-
- def reload_node!(node)
- env.nodes[node.name] = apply_inheritance!(node)
- end
-
- ##
- ## CONNECTIONS
- ##
-
- class ConnectionList < Array
- def add(data={})
- self << {
- "from" => data[:from],
- "to" => data[:to],
- "port" => data[:port]
- }
- end
- end
-
- def connections
- @connections ||= ConnectionList.new
- end
-
- ##
- ## PRIVATE
- ##
-
- private
-
- #
- # makes a node inherit options from appropriate the common, service, and tag json files.
- #
- def apply_inheritance(node, throw_exceptions=false)
- new_node = Config::Node.new(nil)
- node_env = guess_node_env(node)
- new_node.set_environment(node_env, new_node)
-
- # inherit from common
- new_node.deep_merge!(node_env.common)
-
- # inherit from services
- if node['services']
- node['services'].to_a.each do |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)
- end
- end
- end
-
- # inherit from tags
- if node.vagrant?
- node['tags'] = (node['tags'] || []).to_a + ['local']
- end
- if node['tags']
- node['tags'].to_a.each do |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)
- end
- end
- end
-
- # inherit from node
- new_node.deep_merge!(node)
- return new_node
- end
-
- def apply_inheritance!(node)
- apply_inheritance(node, true)
- end
-
- #
- # 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.
- #
- # Unfortunately, it is a chicken and egg problem. We need to know the nodes
- # likely environment in order to apply the inheritance that will actually
- # determine the node's properties.
- #
- def guess_node_env(node)
- if node.vagrant?
- return self.env("local")
- else
- environment = self.env(default_environment)
- if node['tags']
- node['tags'].to_a.each do |tag|
- if self.environment_names.include?(tag)
- environment = self.env(tag)
- end
- end
- end
- return environment
- end
- end
-
- #
- # 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
- env.nodes.each do |name, node|
- if node.enabled || options[:include_disabled]
- if node['services']
- node['services'].to_a.each do |node_service|
- 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|
- 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}."
- env.nodes.delete(name)
- @disabled_nodes[name] = node
- end
- end
- end
-
- #
- # returns a list of 'control' files for this node.
- # a control file is like a service or a tag JSON file, but it contains
- # raw ruby code that gets evaluated in the context of the node.
- # Yes, this entirely breaks our functional programming model
- # for JSON generation.
- #
- def control_files(node)
- files = []
- [Path.provider_base, @provider_dir].each do |provider_dir|
- [['services', :service_config], ['tags', :tag_config]].each do |attribute, path_sym|
- node[attribute].each do |attr_value|
- path = Path.named_path([path_sym, "#{attr_value}.rb"], provider_dir).sub(/\.json$/,'')
- if File.exist?(path)
- files << path
- end
- end
- end
- end
- return files
- end
-
- end
- end
-end
diff --git a/lib/leap_cli/config/node.rb b/lib/leap_cli/config/node.rb
deleted file mode 100644
index f8ec052..0000000
--- a/lib/leap_cli/config/node.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-#
-# 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(environment=nil)
- super(environment)
- @node = self
- @file_paths = []
- end
-
- #
- # returns true if this node has an ip address in the range of the vagrant network
- #
- def vagrant?
- begin
- vagrant_range = IPAddr.new LeapCli.leapfile.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
-
- #
- # Return a hash table representation of ourselves, with the key equal to the @node.name,
- # and the value equal to the fields specified in *keys.
- #
- # Also, the result is flattened to a single hash, so a key of 'a.b' becomes 'a_b'
- #
- # compare to Object#pick(*keys). This method is the sames as Config::ObjectList#pick_fields,
- # but works on a single node.
- #
- # Example:
- #
- # node.pick('domain.internal') =>
- #
- # {
- # 'node1': {
- # 'domain_internal': 'node1.example.i'
- # }
- # }
- #
- def pick_fields(*keys)
- {@node.name => self.pick(*keys)}
- end
-
- #
- # can be overridden by the platform.
- # returns a list of node names that should be tested before this node
- #
- def test_dependencies
- []
- end
-
- # returns a string list of supported ssh host key algorithms for this node.
- # or an empty string if it could not be determined
- def supported_ssh_host_key_algorithms
- require 'leap_cli/ssh'
- @host_key_algo ||= LeapCli::SSH::Key.supported_host_key_algorithms(
- Util.read_file([:node_ssh_pub_key, @node.name])
- )
- end
-
- end
-
-end; end
diff --git a/lib/leap_cli/config/object.rb b/lib/leap_cli/config/object.rb
deleted file mode 100644
index b117c2f..0000000
--- a/lib/leap_cli/config/object.rb
+++ /dev/null
@@ -1,428 +0,0 @@
-# encoding: utf-8
-
-require 'erb'
-require 'json/pure' # pure ruby implementation is required for our sorted trick to work.
-
-if $ruby_version < [1,9]
- $KCODE = 'UTF8'
-end
-require 'ya2yaml' # pure ruby yaml
-
-module LeapCli
- module Config
-
- #
- # This class represents the configuration for a single node, service, or tag.
- # Also, all the nested hashes are also of this type.
- #
- # It is called 'object' because it corresponds to an Object in JSON.
- #
- class Object < Hash
-
- attr_reader :env
- attr_reader :node
-
- def initialize(environment=nil, node=nil)
- raise ArgumentError unless environment.nil? || environment.is_a?(Config::Environment)
- @env = environment
- # 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
- end
-
- def manager
- @env.manager
- end
-
- #
- # TODO: deprecate node.global()
- #
- def global
- @env
- end
-
- def environment=(e)
- self.store('environment', e)
- end
-
- def environment
- self['environment']
- end
-
- def duplicate(env)
- new_object = self.deep_dup
- new_object.set_environment(env, new_object)
- end
-
- #
- # export YAML
- #
- # We use pure ruby yaml exporter ya2yaml instead of SYCK or PSYCH because it
- # allows us greater compatibility regardless of installed ruby version and
- # greater control over how the yaml is exported (sorted keys, in particular).
- #
- def dump_yaml
- evaluate(@node)
- sorted_ya2yaml(:syck_compatible => true)
- end
-
- #
- # export JSON
- #
- def dump_json(options={})
- evaluate(@node)
- if options[:format] == :compact
- return self.to_json
- else
- excluded = {}
- if options[:exclude]
- options[:exclude].each do |key|
- excluded[key] = self[key]
- self.delete(key)
- end
- end
- json_str = JSON.sorted_generate(self)
- if excluded.any?
- self.merge!(excluded)
- end
- return json_str
- end
- end
-
- def evaluate(context=@node)
- evaluate_everything(context)
- late_evaluate_everything(context)
- end
-
- ##
- ## FETCHING VALUES
- ##
-
- def [](key)
- get(key)
- end
-
- # Overrride some default methods in Hash that are likely to
- # be used as attributes.
- alias_method :hkey, :key
- def key; get('key'); end
-
- #
- # make hash addressable like an object (e.g. obj['name'] available as obj.name)
- #
- def method_missing(method, *args, &block)
- get!(method)
- end
-
- def get(key)
- begin
- get!(key)
- rescue NoMethodError
- nil
- end
- end
-
- # override behavior of #default() from Hash
- def default
- get!('default')
- end
-
- #
- # Like a normal Hash#[], except:
- #
- # (1) lazily eval dynamic values when we encounter them. (i.e. strings that start with "= ")
- #
- # (2) support for nested references in a single string (e.g. ['a.b'] is the same as ['a']['b'])
- # the dot path is always absolute, starting at the top-most object.
- #
- def get!(key)
- key = key.to_s
- if self.has_key?(key)
- fetch_value(key)
- elsif key =~ /\./
- # for keys with with '.' in them, we start from the root object (@node).
- keys = key.split('.')
- value = self.get!(keys.first)
- if value.is_a? Config::Object
- value.get!(keys[1..-1].join('.'))
- else
- value
- end
- else
- raise NoMethodError.new(key, "No method '#{key}' for #{self.class}")
- end
- end
-
- ##
- ## COPYING
- ##
-
- #
- # A deep (recursive) merge with another Config::Object.
- #
- # If prefer_self is set to true, the value from self will be picked when there is a conflict
- # that cannot be merged.
- #
- # Merging rules:
- #
- # - If a value is a hash, we recursively merge it.
- # - If the value is simple, like a string, the new one overwrites the value.
- # - If the value is an array:
- # - If both old and new values are arrays, the new one replaces the old.
- # - If one of the values is simple but the other is an array, the simple is added to the array.
- #
- def deep_merge!(object, prefer_self=false)
- object.each do |key,new_value|
- if self.has_key?('+'+key)
- mode = :add
- old_value = self.fetch '+'+key, nil
- self.delete('+'+key)
- elsif self.has_key?('-'+key)
- mode = :subtract
- old_value = self.fetch '-'+key, nil
- self.delete('-'+key)
- elsif self.has_key?('!'+key)
- mode = :replace
- old_value = self.fetch '!'+key, nil
- self.delete('!'+key)
- else
- mode = :normal
- old_value = self.fetch key, nil
- end
-
- # clean up boolean
- new_value = true if new_value == "true"
- new_value = false if new_value == "false"
- old_value = true if old_value == "true"
- old_value = false if old_value == "false"
-
- # force replace?
- if mode == :replace && prefer_self
- value = old_value
-
- # merge hashes
- elsif old_value.is_a?(Hash) || new_value.is_a?(Hash)
- value = Config::Object.new(@env, @node)
- old_value.is_a?(Hash) ? value.deep_merge!(old_value) : (value[key] = old_value if !old_value.nil?)
- new_value.is_a?(Hash) ? value.deep_merge!(new_value, prefer_self) : (value[key] = new_value if !new_value.nil?)
-
- # merge nil
- elsif new_value.nil?
- value = old_value
- elsif old_value.nil?
- value = new_value
-
- # merge arrays when one value is not an array
- elsif old_value.is_a?(Array) && !new_value.is_a?(Array)
- (value = (old_value.dup << new_value).compact.uniq).delete('REQUIRED')
- elsif new_value.is_a?(Array) && !old_value.is_a?(Array)
- (value = (new_value.dup << old_value).compact.uniq).delete('REQUIRED')
-
- # merge two arrays
- elsif old_value.is_a?(Array) && new_value.is_a?(Array)
- if mode == :add
- value = (old_value + new_value).sort.uniq
- elsif mode == :subtract
- value = new_value - old_value
- elsif prefer_self
- value = old_value
- else
- value = new_value
- end
-
- # catch errors
- elsif type_mismatch?(old_value, new_value)
- raise 'Type mismatch. Cannot merge %s (%s) with %s (%s). Key is "%s", name is "%s".' % [
- old_value.inspect, old_value.class,
- new_value.inspect, new_value.class,
- key, self.class
- ]
-
- # merge simple strings & numbers
- else
- if prefer_self
- value = old_value
- else
- value = new_value
- end
- end
-
- # save value
- self[key] = value
- end
- self
- end
-
- def set_environment(env, node)
- @env = env
- @node = node
- self.each do |key, value|
- if value.is_a?(Config::Object)
- value.set_environment(env, node)
- end
- end
- end
-
- #
- # like a reverse deep merge
- # (self takes precedence)
- #
- def inherit_from!(object)
- self.deep_merge!(object, true)
- 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
-
- def eval_file(filename)
- evaluate_ruby(filename, File.read(filename))
- end
-
- protected
-
- #
- # walks the object tree, eval'ing all the attributes that are dynamic ruby (e.g. value starts with '= ')
- #
- def evaluate_everything(context)
- keys.each do |key|
- obj = fetch_value(key, context)
- if is_required_value_not_set?(obj)
- Util::log 0, :warning, "required property \"#{key}\" is not set in node \"#{node.name}\"."
- elsif obj.is_a? Config::Object
- obj.evaluate_everything(context)
- end
- end
- end
-
- #
- # some keys need to be evaluated 'late', after all the other keys have been evaluated.
- #
- def late_evaluate_everything(context)
- if @late_eval_list
- @late_eval_list.each do |key, value|
- self[key] = context.evaluate_ruby(key, value)
- if is_required_value_not_set?(self[key])
- Util::log 0, :warning, "required property \"#{key}\" is not set in node \"#{node.name}\"."
- end
- end
- end
- values.each do |obj|
- if obj.is_a? Config::Object
- obj.late_evaluate_everything(context)
- end
- end
- end
-
- #
- # evaluates the string `value` as ruby in the context of self.
- # (`key` is just passed for debugging purposes)
- #
- def evaluate_ruby(key, value)
- self.instance_eval(value, key, 1)
- rescue ConfigError => exc
- raise exc # pass through
- rescue SystemStackError => exc
- Util::log 0, :error, "while evaluating node '#{self.name}'"
- Util::log 0, "offending key: #{key}", :indent => 1
- Util::log 0, "offending string: #{value}", :indent => 1
- Util::log 0, "STACK OVERFLOW, BAILING OUT. There must be an eval loop of death (variables with circular dependencies).", :indent => 1
- raise SystemExit.new(1)
- rescue FileMissing => exc
- Util::bail! do
- if exc.options[:missing]
- Util::log :missing, exc.options[:missing].gsub('$node', self.name).gsub('$file', exc.path)
- else
- Util::log :error, "while evaluating node '#{self.name}'"
- Util::log "offending key: #{key}", :indent => 1
- Util::log "offending string: #{value}", :indent => 1
- Util::log "error message: no file '#{exc}'", :indent => 1
- end
- raise exc if DEBUG
- end
- rescue AssertionFailed => exc
- Util.bail! do
- Util::log :failed, "assertion while evaluating node '#{self.name}'"
- Util::log 'assertion: %s' % exc.assertion, :indent => 1
- Util::log "offending key: #{key}", :indent => 1
- raise exc if DEBUG
- end
- rescue SyntaxError, StandardError => exc
- Util::bail! do
- Util::log :error, "while evaluating node '#{self.name}'"
- Util::log "offending key: #{key}", :indent => 1
- Util::log "offending string: #{value}", :indent => 1
- Util::log "error message: #{exc.inspect}", :indent => 1
- raise exc if DEBUG
- end
- end
-
- private
-
- #
- # fetches the value for the key, evaluating the value as ruby if it begins with '='
- #
- def fetch_value(key, context=@node)
- value = fetch(key, nil)
- if value.is_a?(String) && value =~ /^=/
- if value =~ /^=> (.*)$/
- value = evaluate_later(key, $1)
- elsif value =~ /^= (.*)$/
- value = context.evaluate_ruby(key, $1)
- end
- self[key] = value
- end
- return value
- end
-
- def evaluate_later(key, value)
- @late_eval_list ||= []
- @late_eval_list << [key, value]
- '<evaluate later>'
- end
-
- #
- # when merging, we raise an error if this method returns true for the two values.
- #
- def type_mismatch?(old_value, new_value)
- if old_value.is_a?(Boolean) && new_value.is_a?(Boolean)
- # note: FalseClass and TrueClass are different classes
- # so we can't do old_value.class == new_value.class
- return false
- elsif old_value.is_a?(String) && old_value =~ /^=/
- # pass through macros, since we don't know what the type will eventually be.
- return false
- elsif new_value.is_a?(String) && new_value =~ /^=/
- return false
- elsif old_value.class == new_value.class
- return false
- else
- return true
- end
- end
-
- #
- # returns true if the value has not been changed and the default is "REQUIRED"
- #
- def is_required_value_not_set?(value)
- if value.is_a? Array
- value == ["REQUIRED"]
- else
- value == "REQUIRED"
- end
- end
-
- end # class
- end # module
-end # module \ No newline at end of file
diff --git a/lib/leap_cli/config/object_list.rb b/lib/leap_cli/config/object_list.rb
deleted file mode 100644
index f9299a6..0000000
--- a/lib/leap_cli/config/object_list.rb
+++ /dev/null
@@ -1,209 +0,0 @@
-require 'tsort'
-
-module LeapCli
- module Config
- #
- # A list of Config::Object instances (internally stored as a hash)
- #
- class ObjectList < Hash
- include TSort
-
- def initialize(config=nil)
- if config
- self.add(config['name'], config)
- end
- end
-
- #
- # If the key is a string, the Config::Object it references is returned.
- #
- # If the key is a hash, we treat it as a condition and filter all the Config::Objects using the condition.
- # A new ObjectList is returned.
- #
- # Examples:
- #
- # nodes['vpn1']
- # node named 'vpn1'
- #
- # nodes[:public_dns => true]
- # all nodes with public dns
- #
- # nodes[:services => 'openvpn', 'location.country_code' => 'US']
- # all nodes with services containing 'openvpn' OR country code of US
- #
- # Sometimes, you want to do an OR condition with multiple conditions
- # for the same field. Since hash keys must be unique, you can use
- # an array representation instead:
- #
- # nodes[[:services, 'openvpn'], [:services, 'tor']]
- # nodes with openvpn OR tor service
- #
- # nodes[:services => 'openvpn'][:tags => 'production']
- # nodes with openvpn AND are production
- #
- def [](key)
- if key.is_a?(Hash) || key.is_a?(Array)
- filter(key)
- else
- super key.to_s
- end
- end
-
- def exclude(node)
- list = self.dup
- list.delete(node.name)
- return list
- end
-
- def each_node(&block)
- self.keys.sort.each do |node_name|
- yield self[node_name]
- end
- end
-
- #
- # filters this object list, producing a new list.
- # filter is an array or a hash. see []
- #
- def filter(filter)
- results = Config::ObjectList.new
- filter.each do |field, match_value|
- field = field.is_a?(Symbol) ? field.to_s : field
- match_value = match_value.is_a?(Symbol) ? match_value.to_s : match_value
- if match_value.is_a?(String) && match_value =~ /^!/
- operator = :not_equal
- match_value = match_value.sub(/^!/, '')
- else
- operator = :equal
- end
- each do |name, config|
- value = config[field]
- if value.is_a? Array
- if operator == :equal && value.include?(match_value)
- results[name] = config
- elsif operator == :not_equal && !value.include?(match_value)
- results[name] = config
- end
- else
- if operator == :equal && value == match_value
- results[name] = config
- elsif operator == :not_equal && value != match_value
- results[name] = config
- end
- end
- end
- end
- results
- end
-
- def add(name, object)
- self[name] = object
- end
-
- #
- # converts the hash of configs into an array of hashes, with ONLY the specified fields
- #
- def fields(*fields)
- result = []
- keys.sort.each do |name|
- result << self[name].pick(*fields)
- end
- result
- end
-
- #
- # like fields(), but returns an array of values instead of an array of hashes.
- #
- def field(field)
- field = field.to_s
- result = []
- keys.sort.each do |name|
- result << self[name].get(field)
- end
- result
- end
-
- #
- # pick_fields(field1, field2, ...)
- #
- # generates a Hash from the object list, but with only the fields that are picked.
- #
- # If there are more than one field, then the result is a Hash of Hashes.
- # If there is just one field, it is a simple map to the value.
- #
- # For example:
- #
- # "neighbors" = "= nodes_like_me[:services => :couchdb].pick_fields('domain.full', 'ip_address')"
- #
- # generates this:
- #
- # neighbors:
- # couch1:
- # domain_full: couch1.bitmask.net
- # ip_address: "10.5.5.44"
- # couch2:
- # domain_full: couch2.bitmask.net
- # ip_address: "10.5.5.52"
- #
- # But this:
- #
- # "neighbors": "= nodes_like_me[:services => :couchdb].pick_fields('domain.full')"
- #
- # will generate this:
- #
- # neighbors:
- # couch1: couch1.bitmask.net
- # couch2: couch2.bitmask.net
- #
- def pick_fields(*fields)
- self.values.inject({}) do |hsh, node|
- value = self[node.name].pick(*fields)
- if fields.size == 1
- value = value.values.first
- end
- hsh[node.name] = value
- hsh
- end
- end
-
- #
- # Applies inherit_from! to all objects.
- #
- # 'env' specifies what environment should be for
- # each object in the list.
- #
- def inherit_from!(object_list, env)
- object_list.each do |name, object|
- if self[name]
- self[name].inherit_from!(object)
- else
- self[name] = object.duplicate(env)
- end
- end
- end
-
- #
- # topographical sort based on test dependency
- #
- def tsort_each_node(&block)
- self.each_key(&block)
- end
-
- def tsort_each_child(node_name, &block)
- if self[node_name]
- self[node_name].test_dependencies.each do |test_me_first|
- if self[test_me_first] # TODO: in the future, allow for ability to optionally pull in all dependencies.
- # not just the ones that pass the node filter.
- yield(test_me_first)
- end
- end
- end
- end
-
- def names_in_test_dependency_order
- self.tsort
- end
-
- end
- end
-end
diff --git a/lib/leap_cli/config/provider.rb b/lib/leap_cli/config/provider.rb
deleted file mode 100644
index 0d8bc1f..0000000
--- a/lib/leap_cli/config/provider.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Configuration class for provider.json
-#
-
-module LeapCli; module Config
- class Provider < Object
- attr_reader :environment
- def set_env(e)
- if e == 'default'
- @environment = nil
- else
- @environment = e
- end
- end
- def provider
- self
- end
- def validate!
- # nothing here yet :(
- end
- end
-end; end
diff --git a/lib/leap_cli/config/secrets.rb b/lib/leap_cli/config/secrets.rb
deleted file mode 100644
index ca851c7..0000000
--- a/lib/leap_cli/config/secrets.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-# encoding: utf-8
-#
-# A class for the secrets.json file
-#
-
-module LeapCli; module Config
-
- class Secrets < Object
- attr_reader :node_list
-
- def initialize(manager=nil)
- super(manager)
- @discovered_keys = {}
- end
-
- # we can't use fetch() or get(), since those already have special meanings
- def retrieve(key, environment)
- environment ||= 'default'
- self.fetch(environment, {})[key.to_s]
- end
-
- def set(*args, &block)
- if block_given?
- set_with_block(*args, &block)
- else
- set_without_block(*args)
- end
- end
-
- # searches over all keys matching the regexp, checking to see if the value
- # has been already used by any of them.
- def taken?(regexp, value, environment)
- self.keys.grep(regexp).each do |key|
- return true if self.retrieve(key, environment) == value
- end
- return false
- end
-
- def set_without_block(key, value, environment)
- set_with_block(key, environment) {value}
- end
-
- def set_with_block(key, environment, &block)
- environment ||= 'default'
- key = key.to_s
- @discovered_keys[environment] ||= {}
- @discovered_keys[environment][key] = true
- self[environment] ||= {}
- self[environment][key] ||= yield
- end
-
- #
- # if clean is true, then only secrets that have been discovered
- # during this run will be exported.
- #
- # if environment is also pinned, then we will clean those secrets
- # just for that environment.
- #
- # the clean argument should only be used when all nodes have
- # been processed, otherwise secrets that are actually in use will
- # get mistakenly removed.
- #
- def dump_json(clean=false)
- pinned_env = LeapCli.leapfile.environment
- if clean
- self.each_key do |environment|
- if pinned_env.nil? || pinned_env == environment
- env = self[environment]
- if env.nil?
- raise StandardError.new("secrets.json file seems corrupted. No such environment '#{environment}'")
- end
- env.each_key do |key|
- unless @discovered_keys[environment] && @discovered_keys[environment][key]
- self[environment].delete(key)
- end
- end
- if self[environment].empty?
- self.delete(environment)
- end
- end
- end
- end
- super()
- end
- end
-
-end; end
diff --git a/lib/leap_cli/config/sources.rb b/lib/leap_cli/config/sources.rb
deleted file mode 100644
index aee860d..0000000
--- a/lib/leap_cli/config/sources.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# encoding: utf-8
-#
-# A class for the sources.json file
-#
-
-module LeapCli
- module Config
- class Sources < Object
- end
- end
-end
diff --git a/lib/leap_cli/config/tag.rb b/lib/leap_cli/config/tag.rb
deleted file mode 100644
index 6bd8d1e..0000000
--- a/lib/leap_cli/config/tag.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-#
-# A class for node services or node tags.
-#
-#
-
-module LeapCli; module Config
-
- class Tag < Object
- attr_reader :node_list
-
- def initialize(environment=nil)
- super(environment)
- @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
diff --git a/lib/leap_cli/leapfile.rb b/lib/leap_cli/leapfile.rb
index ac40237..10af224 100644
--- a/lib/leap_cli/leapfile.rb
+++ b/lib/leap_cli/leapfile.rb
@@ -3,6 +3,8 @@
#
# It is akin to a Gemfile, Rakefile, or Capfile (e.g. it is a ruby file that gets eval'ed)
#
+# Additional configuration options are defined in platform's leapfile_extensions.rb
+#
module LeapCli
def self.leapfile
@@ -10,17 +12,11 @@ module LeapCli
end
class Leapfile
- attr_accessor :platform_directory_path
- attr_accessor :provider_directory_path
- attr_accessor :custom_vagrant_vm_line
- attr_accessor :leap_version
- attr_accessor :log
- attr_accessor :vagrant_network
- attr_accessor :vagrant_basebox
- attr_accessor :environment
+ attr_reader :platform_directory_path
+ attr_reader :provider_directory_path
+ attr_reader :environment
def initialize
- @vagrant_network = '10.5.5.0/24'
end
#
@@ -61,19 +57,33 @@ module LeapCli
#
# load the platform
#
- platform_file = "#{@platform_directory_path}/platform.rb"
- unless File.exist?(platform_file)
+ platform_class = "#{@platform_directory_path}/lib/leap/platform"
+ platform_definition = "#{@platform_directory_path}/platform.rb"
+ unless File.exist?(platform_definition)
Util.bail! "ERROR: The file `#{platform_file}` does not exist. Please check the value of `@platform_directory_path` in `Leapfile` or `~/.leaprc`."
end
- require "#{@platform_directory_path}/platform.rb"
- if !Leap::Platform.compatible_with_cli?(LeapCli::VERSION) ||
- !Leap::Platform.version_in_range?(LeapCli::COMPATIBLE_PLATFORM_VERSION)
- Util.bail! "This leap command (v#{LeapCli::VERSION}) " +
- "is not compatible with the platform #{@platform_directory_path} (v#{Leap::Platform.version}).\n " +
- "You need either leap command #{Leap::Platform.compatible_cli.first} to #{Leap::Platform.compatible_cli.last} or " +
- "platform version #{LeapCli::COMPATIBLE_PLATFORM_VERSION.first} to #{LeapCli::COMPATIBLE_PLATFORM_VERSION.last}"
+ require platform_class
+ require platform_definition
+ begin
+ Leap::Platform.validate!(LeapCli::VERSION, LeapCli::COMPATIBLE_PLATFORM_VERSION, self)
+ rescue StandardError => exc
+ Util.bail! exc.to_s
+ end
+ leapfile_extensions = "#{@platform_directory_path}/lib/leap_cli/leapfile_extensions.rb"
+ if File.exist?(leapfile_extensions)
+ require leapfile_extensions
+ end
+
+ #
+ # validate
+ #
+ instance_variables.each do |var|
+ var = var.to_s.sub('@', '')
+ if !self.respond_to?(var)
+ LeapCli.log :warning, "the variable `#{var}` is set in .leaprc or Leapfile, but it is not supported."
+ end
end
- @valid = true
+ @valid = validate
return @valid
end
end
@@ -123,9 +133,8 @@ module LeapCli
def read_settings(file)
if File.exist? file
- Util::log 2, :read, file
+ LeapCli.log 2, :read, file
instance_eval(File.read(file), file)
- validate(file)
end
end
@@ -140,11 +149,16 @@ module LeapCli
return search_dir
end
- PRIVATE_IP_RANGES = /(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/
+ # to be overridden
+ def validate
+ return true
+ end
- def validate(file)
- Util::assert! vagrant_network =~ PRIVATE_IP_RANGES do
- Util::log 0, :error, "in #{file}: vagrant_network is not a local private network"
+ def method_missing(method, *args)
+ if method =~ /=$/
+ self.instance_variable_set('@' + method.to_s.sub('=',''), args.first)
+ else
+ self.instance_variable_get('@' + method.to_s)
end
end
diff --git a/lib/leap_cli/log.rb b/lib/leap_cli/log.rb
index 03789c3..5203c97 100644
--- a/lib/leap_cli/log.rb
+++ b/lib/leap_cli/log.rb
@@ -86,6 +86,9 @@ module LeapCli
message = args.grep(String).first
options = args.grep(Hash).first || {}
host = options[:host]
+ if title
+ title = title.to_s
+ end
unless message && @log_level >= level
return
end
@@ -99,21 +102,22 @@ module LeapCli
#
# apply filters
+ # LogFilter will not be defined if no platform was loaded.
#
- if title
- title, filter_flags = LogFilter.apply_title_filters(title.to_s)
- else
- message, filter_flags = LogFilter.apply_message_filters(message)
- return if message.nil?
+ if defined?(LeapCli::LogFilter)
+ if title
+ title, filter_flags = LogFilter.apply_title_filters(title)
+ else
+ message, filter_flags = LogFilter.apply_message_filters(message)
+ return if message.nil?
+ end
+ options = options.merge(filter_flags)
end
- options = options.merge(filter_flags)
#
# set line prefix
#
- prefix = ""
- prefix += "[" + options[:host] + "] " if options[:host]
- prefix += title + " " if title
+ prefix = prefix_str(host, title)
#
# write to the log file, always
@@ -129,17 +133,17 @@ module LeapCli
end
if options[:color]
if host
- host = "[" + colorize(host, options[:color], options[:style]) + "] "
+ host = colorize(host, options[:color], options[:style])
elsif title
- title = colorize(title, options[:color], options[:style]) + " "
+ title = colorize(title, options[:color], options[:style])
else
message = colorize(message, options[:color], options[:style])
end
elsif title
- title = colorize(title, :cyan, :bold) + " "
+ title = colorize(title, :cyan, :bold)
end
# new colorized prefix:
- prefix = [host, title].compact.join(' ')
+ prefix = prefix_str(host, title)
end
log_raw(:stdout, options[:indent], prefix) { message }
@@ -212,6 +216,14 @@ module LeapCli
private
+ def prefix_str(host, title)
+ prefix = ""
+ prefix += "[" + host + "] " if host
+ prefix += title + " " if title
+ prefix += " " if !prefix.empty? && prefix !~ / $/
+ return prefix
+ end
+
EFFECTS = {
:reset => 0, :nothing => 0,
:bright => 1, :bold => 1,
@@ -245,174 +257,3 @@ module LeapCli
end
end
-#
-# A module to hide, modify, and colorize log entries.
-#
-
-module LeapCli
- module LogFilter
- #
- # options for formatters:
- #
- # :match => regexp for matching a log line
- # :color => what color the line should be
- # :style => what style the line should be
- # :priority => what order the formatters are applied in. higher numbers first.
- # :match_level => only apply filter at the specified log level
- # :level => make this line visible at this log level or higher
- # :replace => replace the matched text
- # :prepend => insert text at start of message
- # :append => append text to end of message
- # :exit => force the exit code to be this (does not interrupt program, just
- # ensures a specific exit code when the program eventually exits)
- #
- FORMATTERS = [
- # TRACE
- { :match => /command finished/, :color => :white, :style => :dim, :match_level => 3, :priority => -10 },
- { :match => /executing locally/, :color => :yellow, :match_level => 3, :priority => -20 },
-
- # DEBUG
- #{ :match => /executing .*/, :color => :green, :match_level => 2, :priority => -10, :timestamp => true },
- #{ :match => /.*/, :color => :yellow, :match_level => 2, :priority => -30 },
- { :match => /^transaction:/, :level => 3 },
-
- # INFO
- { :match => /.*out\] (fatal:|ERROR:).*/, :color => :red, :match_level => 1, :priority => -10 },
- { :match => /Permission denied/, :color => :red, :match_level => 1, :priority => -20 },
- { :match => /sh: .+: command not found/, :color => :magenta, :match_level => 1, :priority => -30 },
-
- # IMPORTANT
- { :match => /^(E|e)rr ::/, :color => :red, :match_level => 0, :priority => -10, :exit => 1},
- { :match => /^ERROR:/, :color => :red, :priority => -10, :exit => 1},
- #{ :match => /.*/, :color => :blue, :match_level => 0, :priority => -20 },
-
- # CLEANUP
- #{ :match => /\s+$/, :replace => '', :priority => 0},
-
- # DEBIAN PACKAGES
- { :match => /^(Hit|Ign) /, :color => :green, :priority => -20},
- { :match => /^Err /, :color => :red, :priority => -20},
- { :match => /^W(ARNING)?: /, :color => :yellow, :priority => -20},
- { :match => /^E: /, :color => :red, :priority => -20},
- { :match => /already the newest version/, :color => :green, :priority => -20},
- { :match => /WARNING: The following packages cannot be authenticated!/, :color => :red, :level => 0, :priority => -10},
-
- # PUPPET
- { :match => /^(W|w)arning: Not collecting exported resources without storeconfigs/, :level => 2, :color => :yellow, :priority => -10},
- { :match => /^(W|w)arning: Found multiple default providers for vcsrepo:/, :level => 2, :color => :yellow, :priority => -10},
- { :match => /^(W|w)arning: .*is deprecated.*$/, :level => 2, :color => :yellow, :priority => -10},
- { :match => /^(W|w)arning: Scope.*$/, :level => 2, :color => :yellow, :priority => -10},
- #{ :match => /^(N|n)otice:/, :level => 1, :color => :cyan, :priority => -20},
- #{ :match => /^(N|n)otice:.*executed successfully$/, :level => 2, :color => :cyan, :priority => -15},
- { :match => /^(W|w)arning:/, :level => 0, :color => :yellow, :priority => -20},
- { :match => /^Duplicate declaration:/, :level => 0, :color => :red, :priority => -20},
- #{ :match => /Finished catalog run/, :level => 0, :color => :green, :priority => -10},
- { :match => /^APPLY COMPLETE \(changes made\)/, :level => 0, :color => :green, :style => :bold, :priority => -10},
- { :match => /^APPLY COMPLETE \(no changes\)/, :level => 0, :color => :green, :style => :bold, :priority => -10},
-
- # PUPPET FATAL ERRORS
- { :match => /^(E|e)rr(or|):/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^Wrapped exception:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^Failed to parse template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^Execution of.*returned/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^Parameter matches failed:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^Syntax error/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^Cannot reassign variable/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^Could not find template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
- { :match => /^APPLY COMPLETE.*fail/, :level => 0, :color => :red, :style => :bold, :priority => -1, :exit => 1},
-
- # TESTS
- { :match => /^PASS: /, :color => :green, :priority => -20},
- { :match => /^(FAIL|ERROR): /, :color => :red, :priority => -20},
- { :match => /^(SKIP|WARN): /, :color => :yellow, :priority => -20},
- { :match => /\d+ tests: \d+ passes, \d+ skips, 0 warnings, 0 failures, 0 errors/,
- :color => :green, :style => :bold, :priority => -20 },
- { :match => /\d+ tests: \d+ passes, \d+ skips, [1-9][0-9]* warnings, 0 failures, 0 errors/,
- :color => :yellow, :style => :bold, :priority => -20 },
- { :match => /\d+ tests: \d+ passes, \d+ skips, \d+ warnings, \d+ failures, [1-9][0-9]* errors/,
- :color => :red, :style => :bold, :priority => -20 },
- { :match => /\d+ tests: \d+ passes, \d+ skips, \d+ warnings, [1-9][0-9]* failures, \d+ errors/,
- :color => :red, :style => :bold, :priority => -20 },
-
- # LOG SUPPRESSION
- { :match => /^(W|w)arning: You cannot collect without storeconfigs being set/, :level => 2, :priority => 10},
- { :match => /^(W|w)arning: You cannot collect exported resources without storeconfigs being set/, :level => 2, :priority => 10}
- ]
-
- SORTED_FORMATTERS = FORMATTERS.sort_by { |i| -(i[:priority] || i[:prio] || 0) }
-
- #
- # same as normal formatters, but only applies to the title, not the message.
- #
- TITLE_FORMATTERS = [
- # red
- { :match => /error/, :color => :red, :style => :bold },
- { :match => /fatal_error/, :replace => 'fatal error:', :color => :red, :style => :bold },
- { :match => /removed/, :color => :red, :style => :bold },
- { :match => /failed/, :replace => 'FAILED', :color => :red, :style => :bold },
- { :match => /bail/, :replace => 'bailing out', :color => :red, :style => :bold },
- { :match => /invalid/, :color => :red, :style => :bold },
-
- # yellow
- { :match => /warning/, :replace => 'warning:', :color => :yellow, :style => :bold },
- { :match => /missing/, :color => :yellow, :style => :bold },
- { :match => /skipping/, :color => :yellow, :style => :bold },
-
- # green
- { :match => /created/, :color => :green, :style => :bold },
- { :match => /completed/, :color => :green, :style => :bold },
- { :match => /ran/, :color => :green, :style => :bold },
-
- # cyan
- { :match => /note/, :replace => 'NOTE:', :color => :cyan, :style => :bold },
-
- # magenta
- { :match => /nochange/, :replace => 'no change', :color => :magenta },
- { :match => /loading/, :color => :magenta },
- ]
-
- def self.apply_message_filters(message)
- return self.apply_filters(SORTED_FORMATTERS, message)
- end
-
- def self.apply_title_filters(title)
- return self.apply_filters(TITLE_FORMATTERS, title)
- end
-
- private
-
- def self.apply_filters(formatters, message)
- level = LeapCli.logger.log_level
- result = {}
- formatters.each do |formatter|
- if (formatter[:match_level] == level || formatter[:match_level].nil?)
- if message =~ formatter[:match]
- # puts "applying formatter #{formatter.inspect}"
- result[:level] = formatter[:level] if formatter[:level]
- result[:color] = formatter[:color] if formatter[:color]
- result[:style] = formatter[:style] || formatter[:attribute] # (support original cap colors)
-
- message.gsub!(formatter[:match], formatter[:replace]) if formatter[:replace]
- message.replace(formatter[:prepend] + message) unless formatter[:prepend].nil?
- message.replace(message + formatter[:append]) unless formatter[:append].nil?
- message.replace(Time.now.strftime('%Y-%m-%d %T') + ' ' + message) if formatter[:timestamp]
-
- if formatter[:exit]
- LeapCli::Util.exit_status(formatter[:exit])
- end
-
- # stop formatting, unless formatter was just for string replacement
- break unless formatter[:replace]
- end
- end
- end
-
- if result[:color] == :hide
- return [nil, {}]
- else
- return [message, result]
- end
- end
-
- end
-end
diff --git a/lib/leap_cli/util.rb b/lib/leap_cli/util.rb
index 248a59c..64b5c63 100644
--- a/lib/leap_cli/util.rb
+++ b/lib/leap_cli/util.rb
@@ -10,6 +10,10 @@ module LeapCli
@@exit_status = nil
+ def log(*args, &block)
+ LeapCli.log(*args, &block)
+ end
+
##
## QUITTING
##
@@ -44,7 +48,7 @@ module LeapCli
log 0, *message
end
log 0, :bail, ""
- raise SystemExit.new(@exit_status || 1)
+ raise SystemExit.new(exit_status || 1)
end
#
@@ -52,7 +56,7 @@ module LeapCli
#
def quit!(message='')
puts(message)
- raise SystemExit.new(@exit_status || 0)
+ raise SystemExit.new(exit_status || 0)
end
#
diff --git a/lib/leap_cli/util/remote_command.rb b/lib/leap_cli/util/remote_command.rb
deleted file mode 100644
index c2f1ace..0000000
--- a/lib/leap_cli/util/remote_command.rb
+++ /dev/null
@@ -1,158 +0,0 @@
-module LeapCli; module Util; module RemoteCommand
- extend self
-
- #
- # FYI
- # Capistrano::Logger::IMPORTANT = 0
- # Capistrano::Logger::INFO = 1
- # Capistrano::Logger::DEBUG = 2
- # Capistrano::Logger::TRACE = 3
- #
- def ssh_connect(nodes, options={}, &block)
- options ||= {}
- node_list = parse_node_list(nodes)
-
- cap = new_capistrano
- cap.logger = LeapCli::Logger.new(:level => [LeapCli.logger.log_level,3].min)
- user = options[:user] || 'root'
- cap.set :user, user
- cap.set :ssh_options, ssh_options # ssh options common to all nodes
- cap.set :use_sudo, false # we may want to change this in the future
-
- # Allow password authentication when we are bootstraping a single node
- # (and key authentication fails).
- if options[:bootstrap] && node_list.size == 1
- hostname = node_list.values.first.name
- if options[:echo]
- cap.set(:password) { ask "Root SSH password for #{user}@#{hostname}> " }
- else
- cap.set(:password) { Capistrano::CLI.password_prompt " * Typed password will be hidden (use --echo to make it visible)\nRoot SSH password for #{user}@#{hostname}> " }
- end
- end
-
- node_list.each do |name, node|
- cap.server node.domain.full, :dummy_arg, node_options(node, options[:ssh_options])
- end
-
- yield cap
- rescue Capistrano::ConnectionError => exc
- # not sure if this will work if english is not the locale??
- if exc.message =~ /Too many authentication failures/
- at_exit {ssh_config_help_message}
- end
- raise exc
- end
-
- private
-
- #
- # 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
- {
- :keys_only => false, # Don't you dare change this.
- :global_known_hosts_file => path(:known_hosts),
- :user_known_hosts_file => '/dev/null',
- :paranoid => true,
- :verbose => net_ssh_log_level
- }
- end
-
- def net_ssh_log_level
- if DEBUG
- case LeapCli.logger.log_level
- when 1 then 3
- when 2 then 2
- when 3 then 1
- else 0
- end
- else
- nil
- end
- end
-
- #
- # For notes on advanced ways to set server-specific options, see
- # http://railsware.com/blog/2011/11/02/advanced-server-definitions-in-capistrano/
- #
- # if, in the future, we want to do per-node password options, it would be done like so:
- #
- # password_proc = Proc.new {Capistrano::CLI.password_prompt "Root SSH password for #{node.name}"}
- # return {:password => password_proc}
- #
- def node_options(node, ssh_options_override=nil)
- {
- :ssh_options => {
- # :host_key_alias => node.name, << incompatible with ports in known_hosts
- :host_name => node.ip_address,
- :port => node.ssh.port
- }.merge(contingent_ssh_options_for_node(node)).merge(ssh_options_override||{})
- }
- end
-
- def new_capistrano
- # load once the library files
- @capistrano_enabled ||= begin
- require 'capistrano'
- require 'capistrano/cli'
- require 'leap_cli/lib_ext/capistrano_connections'
- require 'leap_cli/remote/leap_plugin'
- require 'leap_cli/remote/puppet_plugin'
- require 'leap_cli/remote/rsync_plugin'
- Capistrano.plugin :leap, LeapCli::Remote::LeapPlugin
- Capistrano.plugin :puppet, LeapCli::Remote::PuppetPlugin
- Capistrano.plugin :rsync, LeapCli::Remote::RsyncPlugin
- true
- end
-
- # create capistrano instance
- cap = Capistrano::Configuration.new
-
- # add tasks to capistrano instance
- cap.load File.dirname(__FILE__) + '/../remote/tasks.rb'
-
- return cap
- end
-
- def contingent_ssh_options_for_node(node)
- opts = {}
- if node.vagrant?
- opts[:keys] = [vagrant_ssh_key_file]
- opts[:keys_only] = true # only use the keys specified above, and ignore whatever keys the ssh-agent is aware of.
- opts[:paranoid] = false # we skip host checking for vagrant nodes, because fingerprint is different for everyone.
- if LeapCli.logger.log_level <= 1
- opts[:verbose] = :error # suppress all the warnings about adding host keys to known_hosts, since it is not actually doing that.
- end
- end
- if !node.supported_ssh_host_key_algorithms.empty?
- opts[:host_key] = node.supported_ssh_host_key_algorithms
- end
- return opts
- end
-
-end; end; end \ No newline at end of file
diff --git a/lib/leap_cli/util/secret.rb b/lib/leap_cli/util/secret.rb
deleted file mode 100644
index 749b959..0000000
--- a/lib/leap_cli/util/secret.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# encoding: utf-8
-#
-# A simple secret generator
-#
-# Uses OpenSSL random number generator instead of Ruby's rand function
-#
-autoload :OpenSSL, 'openssl'
-
-module LeapCli; module Util
- class Secret
- CHARS = (('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a) - "i1loO06G".split(//u)
- HEX = (0..9).to_a + ('a'..'f').to_a
-
- #
- # generate a secret with with no ambiguous characters.
- #
- # +length+ is in chars
- #
- # Only alphanumerics are allowed, in order to make these passwords work
- # for REST url calls and to allow you to easily copy and paste them.
- #
- def self.generate(length = 16)
- seed
- OpenSSL::Random.random_bytes(length).bytes.to_a.collect { |byte|
- CHARS[ byte % CHARS.length ]
- }.join
- end
-
- #
- # generates a hex secret, instead of an alphanumeric on.
- #
- # length is in bits
- #
- def self.generate_hex(length = 128)
- seed
- OpenSSL::Random.random_bytes(length/4).bytes.to_a.collect { |byte|
- HEX[ byte % HEX.length ]
- }.join
- end
-
- private
-
- def self.seed
- @pid ||= 0
- pid = $$
- if @pid != pid
- now = Time.now
- ary = [now.to_i, now.nsec, @pid, pid]
- OpenSSL::Random.seed(ary.to_s)
- @pid = pid
- end
- end
-
- end
-end; end
diff --git a/lib/leap_cli/util/x509.rb b/lib/leap_cli/util/x509.rb
deleted file mode 100644
index 787fdfa..0000000
--- a/lib/leap_cli/util/x509.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-autoload :OpenSSL, 'openssl'
-autoload :CertificateAuthority, 'certificate_authority'
-
-require 'digest'
-require 'digest/md5'
-require 'digest/sha1'
-
-module LeapCli; module X509
- extend self
-
- #
- # returns a fingerprint of a x509 certificate
- #
- def fingerprint(digest, cert_file)
- if cert_file.is_a? String
- cert = OpenSSL::X509::Certificate.new(Util.read_file!(cert_file))
- elsif cert_file.is_a? OpenSSL::X509::Certificate
- cert = cert_file
- elsif cert_file.is_a? CertificateAuthority::Certificate
- cert = cert_file.openssl_body
- end
- digester = case digest
- when "MD5" then Digest::MD5.new
- when "SHA1" then Digest::SHA1.new
- when "SHA256" then Digest::SHA256.new
- when "SHA384" then Digest::SHA384.new
- when "SHA512" then Digest::SHA512.new
- end
- digester.hexdigest(cert.to_der)
- end
-
-
-end; end