Class: Conjur::CLI::Complete

Inherits:
Object
  • Object
show all
Defined in:
lib/conjur/complete.rb

Overview

Class for generating ‘conjur` bash completions

Defined Under Namespace

Classes: Resource

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(line, point = nil) ⇒ Complete

Returns a new instance of Complete.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/conjur/complete.rb', line 31

def initialize line, point=nil
  @line = line
  @words = tokenize_cmd @line
  point ||= @line.length
  @current_word_index=(tokenize_cmd @line.slice(0,point)).length-1
  # fix arrays for empty "current word"
  # ie "conjur group list "
  if @line.match(/[ =]$/)
    @words << ''
    @current_word_index += 1
  end
  @commands,
  @switch_words,
  @flag_words,
  @arg_words = parse_command @words, @current_word_index
  @command_words = @commands
                   .drop(1)
                   .map(&:name)
                   .map(&:to_s)
                   .unshift('conjur')
end

Instance Attribute Details

#arg_wordsObject (readonly)

Returns the value of attribute arg_words.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def arg_words
  @arg_words
end

#command_wordsObject (readonly)

Returns the value of attribute command_words.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def command_words
  @command_words
end

#commandsObject (readonly)

Returns the value of attribute commands.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def commands
  @commands
end

#current_word_indexObject (readonly)

Returns the value of attribute current_word_index.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def current_word_index
  @current_word_index
end

#flag_wordsObject (readonly)

Returns the value of attribute flag_words.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def flag_words
  @flag_words
end

#lineObject (readonly)

Returns the value of attribute line.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def line
  @line
end

#switch_wordsObject (readonly)

Returns the value of attribute switch_words.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def switch_words
  @switch_words
end

#wordsObject (readonly)

Returns the value of attribute words.



29
30
31
# File 'lib/conjur/complete.rb', line 29

def words
  @words
end

Instance Method Details

#classify_word(word, command) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/conjur/complete.rb', line 116

def classify_word word, command
  if word.start_with? '-'
    sym = flag_to_sym word
    if switches(command).member? sym
      :switch
    else
      :flag
    end
  else
    if subcommands(command).has_key? word.to_sym
      :subcommand
    else
      :argument
    end
  end
end

#complete(kind) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/conjur/complete.rb', line 133

def complete kind
  kind = kind.to_s.downcase.gsub(/[^a-z]/, '')
  case kind
  when 'resource'
    complete_resource
  when 'role'
    complete_role
  when 'file'
    complete_file current_word
  when 'hostname'
    complete_hostname
  else
    complete_resource kind if [
      'group',
      'user',
      'variable',
      'host',
      'layer',
    ].member? kind
  end or []
end

#complete_args(cmd, prev, num_args) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/conjur/complete.rb', line 176

def complete_args cmd, prev, num_args
  kind=nil
  if prev.start_with? '-'
    flag_name=flag_to_sym prev
    flag = cmd.flags[flag_name]
    desc = flag.argument_name if defined? flag.argument_name
    kind = desc.to_s.downcase
  else
    desc = cmd.arguments_description if defined? cmd.arguments_description
    kind = desc.to_s.downcase.split[num_args-1]
  end
  complete kind
end

#complete_file(word) ⇒ Object



209
210
211
212
# File 'lib/conjur/complete.rb', line 209

def complete_file word
  # use Bash's file completion for compatibility
  `bash -c "compgen -f #{word}"`.shellsplit
end

#complete_flags(cmd) ⇒ Array

generate completions for the switches and flags of a Conjur::CLI::Command

Parameters:

  • cmd (Conjur::CLI::Command)

    command for which to search for flags and switches

Returns:

  • (Array)

    completion words



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/conjur/complete.rb', line 160

def complete_flags cmd
  cmd.flags.values.map do |flag|
    candidates = [flag.name]
    candidates += flag.aliases if flag.aliases
    candidates.map do |c|
      "-#{'-' if c.length > 1}#{c}#{'=' if c.length > 1}"
    end
  end + cmd.switches.values.map do |switch|
    candidates = [switch.name]
    candidates += switch.aliases if switch.aliases
    candidates.map do |c|
      "-#{'-' if c.length > 1}#{c}"
    end
  end
end

#complete_hostnameObject



214
215
216
# File 'lib/conjur/complete.rb', line 214

def complete_hostname
  `bash -c "compgen -A hostname"`.shellsplit
end

#complete_resource(resource_kind = nil) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
# File 'lib/conjur/complete.rb', line 190

def complete_resource resource_kind=nil
  Conjur::Command.api.resources({kind: resource_kind})
    .map do |r|
    res = Resource.new r.attributes['id']
    if resource_kind
      res.name
    else
      res.to_s
    end
  end
end

#complete_roleObject



202
203
204
205
206
207
# File 'lib/conjur/complete.rb', line 202

def complete_role
  Conjur::Command.api.current_role.all
    .map { |r| Resource.new(r.roleid) }
    .reject { |r| r.kind.start_with? '@' }
    .map(&:to_s)
end

#completionsObject



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/conjur/complete.rb', line 218

def completions
  prev = current_word(-1)
  if current_word.start_with? '-'
    complete_flags @commands.last
  else
    (subcommands @commands.last).keys.map(&:to_s) +
      (complete_args @commands.last, prev, @arg_words.length)
  end.flatten
    .select do |candidate|
    candidate.start_with? current_word.sub('\:',':') end
    .map do |candidate|
    # if the current word is colon separated, strip its complete tokens
    # eg. for --as-role=user:ryanprior, we're actually only completing 'ryanprior'
    # because bash treats 'user' as a separate word
    non_escaped_colon_regex = /(?<!\\):/
    num_tokens = current_word.split(non_escaped_colon_regex).length
    if num_tokens > 1
      candidate = candidate
                  .split(non_escaped_colon_regex)
                  .drop(num_tokens-1)
                  .join(':')
    end
    "#{candidate}#{' ' if not candidate.end_with? '='}" end
end

#current_word(offset = 0) ⇒ Object



53
54
55
# File 'lib/conjur/complete.rb', line 53

def current_word offset=0
  @words[@current_word_index + offset]
end

#flag_to_sym(flag) ⇒ Object



86
87
88
# File 'lib/conjur/complete.rb', line 86

def flag_to_sym flag
  flag.match(/--?([^=]+)=?/)[1].to_sym
end

#parse_command(words, current_word_index) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/conjur/complete.rb', line 90

def parse_command words, current_word_index
  command = Conjur::CLI
  commands = [command]
  switches = []
  flags = []
  arguments = []
  index = 1
  until index >= current_word_index do
    word = words[index]
    case classify_word word, command
    when :switch
      switches.push word
    when :flag
      flags.push [word, words[index+1]]
      index += 1
    when :subcommand
      command = command.commands[word.to_sym]
      commands.push command
    when :argument
      arguments.push word
    end
    index += 1
  end
  return commands, switches, flags, arguments
end

#subcommands(cmd) ⇒ Array

Generate array of subcommands for which documentation is not hidden

Parameters:

  • cmd (Conjur::CLI::Command)

    the command to search

Returns:

  • (Array)

    the subcommands



61
62
63
64
65
# File 'lib/conjur/complete.rb', line 61

def subcommands cmd
  cmd.commands.select do |_, c|
    c.nodoc.nil?
  end
end

#switches(cmd) ⇒ Array

Generate array of symbols representing switches for cmd and their aliases

Parameters:

  • cmd (Conjur::CLI::Command)

    the command to search

Returns:

  • (Array)

    the symbols representing switches and their aliases



72
73
74
75
76
# File 'lib/conjur/complete.rb', line 72

def switches cmd
  cmd.switches.map { |_,switch|
    [switch.name] + (switch.aliases or [])
  }.flatten
end

#tokenize_cmd(line) ⇒ Array

Split line according on spaces and after ‘=’

Parameters:

  • line (String)

    to split

Returns:

  • (Array)

    the substrings



82
83
84
# File 'lib/conjur/complete.rb', line 82

def tokenize_cmd line
  line.split(/ |(?<==)/)
end