jerakia 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hiera/backend/jerakia_backend.rb +13 -14
  3. data/lib/jerakia/answer.rb +28 -27
  4. data/lib/jerakia/cache/entry.rb +2 -6
  5. data/lib/jerakia/cache/file.rb +53 -23
  6. data/lib/jerakia/cache.rb +44 -11
  7. data/lib/jerakia/cli/lookup.rb +124 -0
  8. data/lib/jerakia/cli/server.rb +50 -0
  9. data/lib/jerakia/cli/token.rb +64 -0
  10. data/lib/jerakia/cli.rb +7 -117
  11. data/lib/jerakia/config.rb +5 -5
  12. data/lib/jerakia/datasource/dummy.rb +1 -6
  13. data/lib/jerakia/datasource/file/json.rb +1 -3
  14. data/lib/jerakia/datasource/file/yaml.rb +1 -3
  15. data/lib/jerakia/datasource/file.rb +21 -44
  16. data/lib/jerakia/datasource/http.rb +17 -23
  17. data/lib/jerakia/datasource.rb +37 -36
  18. data/lib/jerakia/dsl/lookup.rb +4 -6
  19. data/lib/jerakia/dsl/policy.rb +11 -12
  20. data/lib/jerakia/error.rb +0 -5
  21. data/lib/jerakia/launcher.rb +26 -30
  22. data/lib/jerakia/log.rb +21 -22
  23. data/lib/jerakia/lookup/plugin/hiera.rb +3 -4
  24. data/lib/jerakia/lookup/plugin.rb +5 -6
  25. data/lib/jerakia/lookup/plugin_config.rb +31 -0
  26. data/lib/jerakia/lookup/pluginfactory.rb +30 -36
  27. data/lib/jerakia/lookup.rb +31 -32
  28. data/lib/jerakia/policy.rb +60 -45
  29. data/lib/jerakia/request.rb +3 -2
  30. data/lib/jerakia/response/filter/encryption.rb +7 -12
  31. data/lib/jerakia/response/filter/strsub.rb +4 -9
  32. data/lib/jerakia/response/filter.rb +5 -5
  33. data/lib/jerakia/response.rb +7 -13
  34. data/lib/jerakia/schema.rb +23 -35
  35. data/lib/jerakia/scope/metadata.rb +0 -1
  36. data/lib/jerakia/scope/puppetdb.rb +38 -0
  37. data/lib/jerakia/scope/server.rb +60 -0
  38. data/lib/jerakia/scope/yaml.rb +3 -4
  39. data/lib/jerakia/scope.rb +0 -2
  40. data/lib/jerakia/server/auth/token.rb +35 -0
  41. data/lib/jerakia/server/auth.rb +72 -0
  42. data/lib/jerakia/server/rest.rb +140 -0
  43. data/lib/jerakia/server.rb +41 -0
  44. data/lib/jerakia/util.rb +6 -7
  45. data/lib/jerakia/version.rb +1 -3
  46. data/lib/jerakia.rb +58 -40
  47. data/lib/puppet/indirector/data_binding/jerakia.rb +9 -11
  48. data/lib/puppet/indirector/data_binding/jerakia_rest.rb +11 -13
  49. metadata +78 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 691dd2bc7adfbd791cfff7d5e1e81721567d0684
4
- data.tar.gz: 987421a97b12e0803be5562656d5c92b916ae326
3
+ metadata.gz: a9f2853d09f628caae07235bf40b294e2025dce4
4
+ data.tar.gz: b349c9f8181a4e47a44b53bf9d74602dfcbc2199
5
5
  SHA512:
6
- metadata.gz: 2e6dab70dc4cd75bd53a8e697c2dbca44a2de610f99d9f1da6875cd8b6a788caa9d2ab3ec4cd9367a1379929dc88b2e04caa3758eda7622b9097c410f8634764
7
- data.tar.gz: acf4f2caacbbdd93d65a7225b7f1ae1777e4398993b23dd05e0ba6be5af6bd9dcc7eae2a9cb080916b6cacf2439481f7f3a435ea2d25ba5f453ab77f6b5576b2
6
+ metadata.gz: 51b217b1c2a946378a4fd8947de57277bc74d2b1dfe659ca8363314b8ecf28b3905b960c04e5b0cece3063d5402fa9b862238fdde2a43755b8a2b9dd4fe38de3
7
+ data.tar.gz: 3f35a0de6e1b987c41652e572ad31ecdd4aab804e109caae2729728bdb5a319227e626c7e2e1841ce6888e94b9258a2dc4890d20d71dee6b8d4c996b48f61fdb
@@ -4,16 +4,15 @@ require 'puppet/resource'
4
4
  class Hiera
5
5
  module Backend
6
6
  class Jerakia_backend
7
-
8
7
  def initialize(config = nil)
9
8
  require 'jerakia'
10
- @config = config || Hiera::Config[:jerakia] || {}
9
+ @config = config || Hiera::Config[:jerakia] || {}
11
10
  @policy = @config[:policy] || 'default'
12
11
  @jerakia = ::Jerakia.new(@config)
13
12
  Jerakia.log.debug("[hiera] hiera backend loaded with policy #{@policy}")
14
13
  end
15
14
 
16
- def lookup(key, scope, order_override, resolution_type)
15
+ def lookup(key, scope, _order_override, resolution_type)
17
16
  lookup_type = :first
18
17
  merge_type = :none
19
18
 
@@ -29,27 +28,27 @@ class Hiera
29
28
  namespace = []
30
29
 
31
30
  if key.include?('::')
32
- lookup_key = key.split('::')
33
- key = lookup_key.pop
34
- namespace = lookup_key
31
+ lookup_key = key.split('::')
32
+ key = lookup_key.pop
33
+ namespace = lookup_key
35
34
  end
36
35
 
37
36
  Jerakia.log.debug("[hiera] backend invoked for key #{key} using namespace #{namespace}")
38
37
 
39
- metadata={}
40
- if scope.is_a?(Hash)
41
- metadata=scope.reject { |k, v| v.is_a?(Puppet::Resource) }
42
- else
43
- metadata = scope.real.to_hash.reject { |k, v| v.is_a?(Puppet::Resource) }
44
- end
38
+ metadata = {}
39
+ metadata = if scope.is_a?(Hash)
40
+ scope.reject { |_k, v| v.is_a?(Puppet::Resource) }
41
+ else
42
+ scope.real.to_hash.reject { |_k, v| v.is_a?(Puppet::Resource) }
43
+ end
45
44
 
46
45
  request = Jerakia::Request.new(
47
46
  :key => key,
48
47
  :namespace => namespace,
49
- :policy => metadata["jerakia_policy"] || @policy,
48
+ :policy => metadata['jerakia_policy'] || @policy,
50
49
  :lookup_type => lookup_type,
51
50
  :merge => merge_type,
52
- :metadata => metadata,
51
+ :metadata => metadata
53
52
  )
54
53
 
55
54
  answer = @jerakia.lookup(request)
@@ -1,29 +1,31 @@
1
- class Jerakia::Answer
1
+ class Jerakia
2
+ class Answer
3
+ require 'deep_merge'
2
4
 
3
- require 'deep_merge'
5
+ attr_accessor :payload
6
+ attr_accessor :datatype
7
+ attr_reader :lookup_type
4
8
 
5
- attr_accessor :payload
6
- attr_accessor :datatype
7
- attr_reader :lookup_type
8
-
9
- def initialize(lookup_type = :first)
10
- case lookup_type
11
- when :first
12
- @payload=nil
13
- when :cascade
14
- @payload=[]
15
- @datatype="array"
9
+ def initialize(lookup_type = :first)
10
+ case lookup_type
11
+ when :first
12
+ @payload = nil
13
+ when :cascade
14
+ @payload = []
15
+ @datatype = 'array'
16
+ end
16
17
  end
17
- end
18
18
 
19
- def flatten_payload!
20
- @payload.flatten!
21
- end
19
+ def flatten_payload!
20
+ @payload.flatten!
21
+ end
22
22
 
23
- def merge_payload!(method = :hash)
24
- payload_hash={}
25
- @payload.each do |p|
26
- if p.is_a?(Hash)
23
+ # TODO: consolidate this into less lines
24
+ #
25
+ def merge_payload!(method = :hash) # rubocop:disable Metrics/MethodLength
26
+ payload_hash = {}
27
+ @payload.each do |p|
28
+ next unless p.is_a?(Hash)
27
29
  case method
28
30
  when :hash
29
31
  payload_hash = p.merge(payload_hash)
@@ -31,13 +33,12 @@ class Jerakia::Answer
31
33
  payload_hash = p.deep_merge!(payload_hash)
32
34
  end
33
35
  end
36
+ @payload = payload_hash
37
+ set_data_type
34
38
  end
35
- @payload=payload_hash
36
- set_data_type
37
- end
38
39
 
39
- def set_data_type
40
- @data_type=@payload.class.to_s.downcase
40
+ def set_data_type
41
+ @data_type = @payload.class.to_s.downcase
42
+ end
41
43
  end
42
44
  end
43
-
@@ -1,17 +1,13 @@
1
1
  class Jerakia::Cache::Entry
2
-
3
2
  attr_accessor :key
4
3
  attr_accessor :content
5
4
  @@cache = Jerakia::Cache.new
6
5
 
7
- def initialize(key='', content='')
8
- @@cache.add(key, self)
6
+ def initialize(key = '', _content = '')
7
+ @@cache.add(key, self)
9
8
  end
10
9
 
11
10
  def valid?
12
11
  true
13
12
  end
14
13
  end
15
-
16
-
17
-
@@ -1,33 +1,63 @@
1
1
  require 'jerakia/cache'
2
2
 
3
- class Jerakia::Cache::File < Jerakia::Cache
4
-
5
-
6
- def initialize
7
- super
8
- end
3
+ class Jerakia::Cache::File
4
+ class << self
5
+ def cache
6
+ Jerakia::Cache
7
+ end
9
8
 
9
+ # Returns the latest mtime of the file if the file exists and
10
+ # nil if it doesn't exist.
11
+ #
12
+ def state(filename)
13
+ ::File.stat(filename).mtime if ::File.exist?(filename)
14
+ end
10
15
 
11
- def state(filename)
12
- if ::File.exists?(filename)
13
- ::File.stat(filename).mtime
14
- else
15
- nil
16
+ def add(index, data)
17
+ filestate = state(index)
18
+ Jerakia.log.debug("Adding #{index} to file cache with state #{filestate}")
19
+ cache.add(index, data, :state => filestate) if filestate
16
20
  end
17
- end
18
21
 
19
- def add(index,data)
20
- @@bucket[index] ||= {}
21
- @@bucket[index][:state] = state(index)
22
- super
23
- end
22
+ def valid?(index)
23
+ if cache.in_bucket?(index)
24
+ unless File.exists?(index)
25
+ cache.purge(index)
26
+ return false
27
+ end
28
+ metadata = cache.metadata(index)
29
+ if metadata
30
+ metadata[:state] == state(index)
31
+ else
32
+ false
33
+ end
34
+ else
35
+ false
36
+ end
37
+ end
24
38
 
25
- def valid?(index)
26
- if in_bucket?(index)
27
- @@bucket[index][:state] == state(index)
28
- else
29
- false
39
+ def import_file(filename)
40
+ Jerakia.log.debug("Importing file #{filename} to file cache")
41
+ File.read(filename)
42
+ end
43
+
44
+ # If the cache has a valid copy of the file, then we retrieve it, if the cache
45
+ # doesn't have a copy, or if the state has changed, then we should add it to
46
+ # the cache again and overite the existing data.
47
+ #
48
+ # Returns nil if the file doesn't exist
49
+ #
50
+ def retrieve(filename)
51
+ if valid?(filename)
52
+ Jerakia.log.debug("Using cached contents of #{filename}")
53
+ get(filename)
54
+ else
55
+ add(filename, import_file(filename)) if File.exists?(filename)
56
+ end
30
57
  end
31
- end
32
58
 
59
+ def get(index)
60
+ cache.get(index)
61
+ end
62
+ end
33
63
  end
data/lib/jerakia/cache.rb CHANGED
@@ -3,38 +3,71 @@
3
3
  #
4
4
  #
5
5
  class Jerakia::Cache
6
+ @bucket = {}
6
7
 
7
- @@bucket = {}
8
+ class << self
9
+ attr_reader :bucket
10
+ end
8
11
 
9
12
  def initialize
10
13
  end
11
14
 
12
- def add(index,data)
13
- @@bucket[index] ||= {}
15
+ def add(index, data, metadata={})
16
+ self.class.add(index,data, metadata)
17
+ end
18
+
19
+ def self.add(index, data, metadata={})
20
+ @bucket[index] ||= {}
14
21
  ## The cache bucket is a global class object, therefore we should
15
22
  ## always store a copy of the data object, not the actual object
16
23
  ## to ensure that it is not referenced and tainted by the lookup
17
24
  #
18
- @@bucket[index][:content] = Marshal::load(Marshal.dump(data))
19
- data
25
+ set_metadata(index, metadata)
26
+ @bucket[index][:content] = Marshal.load(Marshal.dump(data))
27
+ end
28
+
29
+ def self.set_metadata(index, metadata)
30
+ @bucket[index][:metadata] = Marshal.load(Marshal.dump(metadata))
20
31
  end
21
32
 
33
+ def self.metadata(index)
34
+ if in_bucket?(index)
35
+ Marshal.load(Marshal.dump(@bucket[index][:metadata]))
36
+ end
37
+ end
38
+
39
+
22
40
  def in_bucket?(index)
23
- @@bucket.has_key?(index)
41
+ self.class.in_bucket?(index)
42
+ end
43
+
44
+ def self.in_bucket?(index)
45
+ bucket.has_key?(index)
24
46
  end
25
47
 
26
48
  ## default behaviour is always validate if exists.
27
49
  def valid?(index)
28
50
  in_bucket?(index)
29
51
  end
30
-
52
+
53
+ def purge(index)
54
+ self.class.purge(index)
55
+ end
56
+
57
+ def self.purge(index)
58
+ @bucket.delete(index)
59
+ end
60
+
31
61
  def get(index)
32
- data = @@bucket[index][:content]
33
- Marshal::load(Marshal.dump(data))
62
+ self.class.get(index)
63
+ end
64
+
65
+ def self.get(index)
66
+ data = @bucket[index][:content]
67
+ Marshal.load(Marshal.dump(data))
34
68
  end
35
69
 
36
70
  def bucket
37
- @@bucket
71
+ self.class.bucket
38
72
  end
39
73
  end
40
-
@@ -0,0 +1,124 @@
1
+ class Jerakia
2
+ class CLI < Thor
3
+ module Lookup
4
+ def self.included(thor)
5
+ thor.class_eval do
6
+ desc 'lookup [KEY]', 'Lookup [KEY] with Jerakia'
7
+ option :config,
8
+ aliases: :c,
9
+ type: :string,
10
+ desc: 'Configuration file'
11
+ option :policy,
12
+ aliases: :p,
13
+ type: :string,
14
+ default: 'default',
15
+ desc: 'Lookup policy'
16
+ option :namespace,
17
+ aliases: :n,
18
+ type: :string,
19
+ default: '',
20
+ desc: 'Lookup namespace'
21
+ option :type,
22
+ aliases: :t,
23
+ type: :string,
24
+ default: 'first',
25
+ desc: 'Lookup type'
26
+ option :scope,
27
+ aliases: :s,
28
+ type: :string,
29
+ desc: 'Scope handler',
30
+ default: 'metadata'
31
+ option :scope_options,
32
+ type: :hash,
33
+ desc: 'Key/value pairs to be passed to the scope handler'
34
+ option :merge_type,
35
+ aliases: :m,
36
+ type: :string,
37
+ default: 'array',
38
+ desc: 'Merge type'
39
+ option :log_level,
40
+ aliases: :l,
41
+ type: :string,
42
+ desc: 'Log level'
43
+ option :verbose,
44
+ aliases: :v,
45
+ type: :boolean,
46
+ desc: 'Print verbose information'
47
+ option :debug,
48
+ aliases: :D,
49
+ type: :boolean,
50
+ desc: 'Debug information to console, implies --log-level debug'
51
+ option :trace,
52
+ type: :boolean,
53
+ desc: 'Output stacktrace to stdout'
54
+ option :metadata,
55
+ aliases: :d,
56
+ type: :hash,
57
+ desc: 'Key/value pairs to be used as metadata for the lookup'
58
+ option :schema,
59
+ aliases: :S,
60
+ type: :boolean,
61
+ desc: 'Enable/disable schema lookup, default true',
62
+ default: true
63
+ option :output,
64
+ aliases: :o,
65
+ type: :string,
66
+ efault: 'json',
67
+ desc: 'Output format, yaml or json'
68
+
69
+ def lookup(key)
70
+ # Thor by default now returns a frozen options hash so we
71
+ # need to dup this here to prevent problems later with
72
+ # modifying the request object
73
+ #
74
+ options_copy = options.dup
75
+
76
+ case true
77
+ when options[:verbose]
78
+ loglevel = 'verbose'
79
+ logfile = STDOUT
80
+ when options[:debug]
81
+ loglevel = 'debug'
82
+ logfile = STDOUT
83
+ else
84
+ logfile = nil
85
+ loglevel = options[:log_level]
86
+ end
87
+
88
+ begin
89
+ jac = Jerakia.new(:config => options[:config],
90
+ :logfile => logfile,
91
+ :loglevel => loglevel,
92
+ :trace => options[:trace])
93
+ req = Jerakia::Request.new(
94
+ :key => key.dup,
95
+ :namespace => options_copy[:namespace].split(/::/),
96
+ :policy => options_copy[:policy].to_sym,
97
+ :lookup_type => options_copy[:type].to_sym,
98
+ :merge => options_copy[:merge_type].to_sym,
99
+ :metadata => options_copy[:metadata] || {},
100
+ :scope => options_copy[:scope].to_sym,
101
+ :scope_options => options_copy[:scope_options],
102
+ :use_schema => options_copy[:schema]
103
+ )
104
+
105
+ answer = jac.lookup(req)
106
+ case options[:output]
107
+ when 'json'
108
+ puts answer.payload.to_json
109
+ when 'yaml'
110
+ puts answer.payload.to_yaml
111
+ else
112
+ puts answer.payload
113
+ end
114
+ rescue Jerakia::Error => e
115
+ STDERR.puts "Error(#{e.class}): #{e.message}"
116
+ STDERR.puts e.backtrace.join("\n") if options[:trace]
117
+ exit 1
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,50 @@
1
+ class Jerakia
2
+ class CLI < Thor
3
+ module Server
4
+ def self.included(thor)
5
+ thor.class_eval do
6
+ desc 'server', 'Start the Jerakia REST server'
7
+ option :config,
8
+ aliases: :c,
9
+ type: :string,
10
+ desc: 'Configuration file'
11
+ option :log_level,
12
+ aliases: :l,
13
+ type: :string,
14
+ desc: 'Log level'
15
+ option :verbose,
16
+ aliases: :v,
17
+ type: :boolean,
18
+ desc: 'Print verbose information'
19
+ option :debug,
20
+ aliases: :D,
21
+ type: :boolean,
22
+ desc: 'Debug information to console, implies --log-level debug'
23
+ def server
24
+ case true
25
+ when options[:verbose]
26
+ loglevel = 'verbose'
27
+ logfile = STDOUT
28
+ when options[:debug]
29
+ loglevel = 'debug'
30
+ logfile = STDOUT
31
+ else
32
+ logfile = nil
33
+ loglevel = options[:log_level]
34
+ end
35
+
36
+ jerakia_opts = {
37
+ :config => options[:config],
38
+ :logfile => logfile,
39
+ :loglevel => loglevel,
40
+ :trace => options[:trace]
41
+ }
42
+
43
+ require 'jerakia/server'
44
+ Jerakia::Server.start(jerakia_opts)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ class Jerakia
2
+ class CLI < Thor
3
+ module Token
4
+ def self.included(thor)
5
+ thor.class_eval do
6
+ desc 'token [SUBCOMMAND] <api id> <options>', 'Create, view and manage token access'
7
+ option :quiet,
8
+ aliases: :q,
9
+ type: :boolean,
10
+ desc: 'Supress explanatory output'
11
+
12
+ def token(subcommand, api_id=:all)
13
+ Jerakia.new
14
+ require 'jerakia/server/auth'
15
+
16
+ unless subcommand == 'list'
17
+ if api_id == :all
18
+ help :token
19
+ STDERR.puts "Error: No API ID provided"
20
+ exit 1
21
+ end
22
+ end
23
+
24
+ if ['enable', 'disable', 'regenerate', 'delete'].include?(subcommand)
25
+ unless Jerakia::Server::Auth.exists?(api_id)
26
+ STDERR.puts "No such API ID #{api_id}"
27
+ exit 1
28
+ end
29
+ end
30
+
31
+ case subcommand
32
+ when 'create'
33
+ token = Jerakia::Server::Auth.create(api_id)
34
+ unless options[:quiet]
35
+ puts "Copy the following token to the application, it must be sent in the Authorization header. This token cannot be retrieved later, if you have lost the token for an application you can create a new one with 'jerakia token regenerate <api id>'\n\n"
36
+ end
37
+ puts token
38
+
39
+ when 'list'
40
+ entries = Jerakia::Server::Auth.get_tokens
41
+ printf("%-20s %-28s %s\n\n","API Identifier","Last Seen", "Status")
42
+ entries.each do |entry|
43
+ status = entry.active ? 'active' : 'disabled'
44
+ printf("%-20s %-28s %s\n", entry.api_id, entry.last_seen.strftime('%F %X'), status)
45
+ end
46
+
47
+ when 'disable'
48
+ Jerakia::Server::Auth.disable(api_id)
49
+ when 'enable'
50
+ Jerakia::Server::Auth.enable(api_id)
51
+ when 'delete'
52
+ Jerakia::Server::Auth.destroy(api_id)
53
+ when 'regenerate'
54
+ token('delete', api_id)
55
+ token('create', api_id)
56
+ else
57
+ STDERR.puts "Unknown subcommand #{subcommand}. Valid commands are list, create, delete, regenerate, disable, enable"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end