Class: OpsWalrus::HostsFile
- Inherits:
-
Object
- Object
- OpsWalrus::HostsFile
- Defined in:
- lib/opswalrus/hosts_file.rb
Constant Summary collapse
- DEFAULT_FILE_NAME =
"hosts.yaml"
Instance Attribute Summary collapse
-
#hosts_file_path ⇒ Object
Returns the value of attribute hosts_file_path.
-
#yaml ⇒ Object
Returns the value of attribute yaml.
Class Method Summary collapse
Instance Method Summary collapse
- #decrypt(decrypted_file_path = nil) ⇒ Object
- #decrypt_secrets! ⇒ Object
- #defaults ⇒ Object
- #encrypt(encrypted_file_path = nil) ⇒ Object
- #encrypt_secrets! ⇒ Object
-
#env ⇒ Object
returns a Hash object that may have nested structures.
- #has_secret?(secret_name) ⇒ Boolean
-
#hosts ⇒ Object
returns an Array(Host).
-
#ids ⇒ Object
returns a Hash of id-name/(PublicKey | Array String ) pairs.
-
#initialize(hosts_file_path) ⇒ HostsFile
constructor
A new instance of HostsFile.
-
#read_secret(secret_name) ⇒ Object
returns the decrypted value referenced by secret_name.
-
#secrets ⇒ Object
secrets are key/value pairs in which the key is an identifier used throughout the yaml file to reference the secret’s value and the associated value is either a Hash or a String.
- #tags(host) ⇒ Object
- #to_yaml ⇒ Object
Constructor Details
#initialize(hosts_file_path) ⇒ HostsFile
Returns a new instance of HostsFile.
33 34 35 36 37 |
# File 'lib/opswalrus/hosts_file.rb', line 33 def initialize(hosts_file_path) @hosts_file_path = File.absolute_path(hosts_file_path) @yaml = Psych.safe_load(File.read(hosts_file_path), permitted_classes: [SecretRef]) if File.exist?(hosts_file_path) @cipher = AgeEncryptionCipher.new(ids, App.instance.identity_file_paths) end |
Instance Attribute Details
#hosts_file_path ⇒ Object
Returns the value of attribute hosts_file_path.
30 31 32 |
# File 'lib/opswalrus/hosts_file.rb', line 30 def hosts_file_path @hosts_file_path end |
#yaml ⇒ Object
Returns the value of attribute yaml.
31 32 33 |
# File 'lib/opswalrus/hosts_file.rb', line 31 def yaml @yaml end |
Class Method Details
.edit(file_path) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/opswalrus/hosts_file.rb', line 13 def self.edit(file_path) tempfile = Tempfile.create begin tempfile.close # we want to close the file without unlinking so that the editor can write to it HostsFile.new(file_path).decrypt(tempfile.path) if TTY::Editor.open(tempfile.path) # tempfile.open() HostsFile.new(tempfile.path).encrypt(file_path) end ensure tempfile.close rescue nil File.unlink(tempfile) # deletes the temp file end end |
Instance Method Details
#decrypt(decrypted_file_path = nil) ⇒ Object
178 179 180 181 182 183 184 |
# File 'lib/opswalrus/hosts_file.rb', line 178 def decrypt(decrypted_file_path = nil) decrypted_file_path ||= @hosts_file_path App.instance.debug "Decrypting #{@hosts_file_path} -> #{decrypted_file_path}." raise("Path to age identity not specified") if App.instance.identity_file_paths.empty? decrypt_secrets! File.write(decrypted_file_path, to_yaml) end |
#decrypt_secrets! ⇒ Object
172 173 174 175 176 |
# File 'lib/opswalrus/hosts_file.rb', line 172 def decrypt_secrets!() secrets.each do |secret_name, secret| secret.decrypt(@cipher) end end |
#defaults ⇒ Object
39 40 41 |
# File 'lib/opswalrus/hosts_file.rb', line 39 def defaults @defaults ||= (@yaml["defaults"] || @yaml["default"] || {}) end |
#encrypt(encrypted_file_path = nil) ⇒ Object
186 187 188 189 190 191 192 |
# File 'lib/opswalrus/hosts_file.rb', line 186 def encrypt(encrypted_file_path = nil) encrypted_file_path ||= @hosts_file_path App.instance.debug "Encrypting #{@hosts_file_path} -> #{encrypted_file_path}." raise("Path to age identity not specified") if App.instance.identity_file_paths.empty? encrypt_secrets! File.write(encrypted_file_path, to_yaml) end |
#encrypt_secrets! ⇒ Object
166 167 168 169 170 |
# File 'lib/opswalrus/hosts_file.rb', line 166 def encrypt_secrets!() secrets.each do |secret_name, secret| secret.encrypt(@cipher) end end |
#env ⇒ Object
returns a Hash object that may have nested structures
126 127 128 |
# File 'lib/opswalrus/hosts_file.rb', line 126 def env @env ||= (@yaml["env"] || {}) end |
#has_secret?(secret_name) ⇒ Boolean
156 157 158 |
# File 'lib/opswalrus/hosts_file.rb', line 156 def has_secret?(secret_name) secrets[secret_name] end |
#hosts ⇒ Object
returns an Array(Host)
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/opswalrus/hosts_file.rb', line 44 def hosts # @yaml is a map of the form: # { # "198.23.249.13"=>{"hostname"=>"web1", "tags"=>["monopod", "racknerd", "vps", "2.5gb", "web1", "web", "ubuntu22.04"]}, # "107.175.91.150"=>{"tags"=>["monopod", "racknerd", "vps", "2.5gb", "pbx1", "pbx", "ubuntu22.04"]}, # "198.23.249.16"=>{"tags"=>["racknerd", "vps", "4gb", "kvm", "ubuntu20.04", "minecraft"]}, # "198.211.15.34"=>{"tags"=>["racknerd", "vps", "1.5gb", "kvm", "ubuntu20.04", "blog"]}, # "homeassistant.locallan.network"=>{"tags"=>["local", "homeassistant", "home", "rpi"]}, # "synology.locallan.network"=>{"tags"=>["local", "synology", "nas"]}, # "pfsense.locallan.network"=>false, # "192.168.56.10"=>{"tags"=>["web", "vagrant"]} # } @yaml.map do |host_ref, host_attrs| next if ['default', 'defaults', 'env', 'ids', 'secrets'].include?(host_ref) host_params = host_attrs.is_a?(Hash) ? host_attrs : {} Host.new(host_ref, (host_ref), host_params, defaults, self) end.compact end |
#ids ⇒ Object
returns a Hash of id-name/(PublicKey | Array String ) pairs
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/opswalrus/hosts_file.rb', line 106 def ids @ids ||= begin id_public_key_pairs, alias_id_set_pairs = (@yaml["ids"] || {}).partition{|k,v| String === v }.map(&:to_h) named_public_keys = id_public_key_pairs.map do |id_name, public_key_string| [id_name, PublicKey.new(id_name, public_key_string)] end.to_h # named_id_sets = alias_id_set_pairs.map do |id_name, id_array| # referenced_public_keys = id_array.map {|id| named_public_keys[id] }.uniq.compact # [id_name, referenced_public_keys] # end.to_h # named_public_keys.merge(named_id_sets) named_public_keys.merge(alias_id_set_pairs) end end |
#read_secret(secret_name) ⇒ Object
returns the decrypted value referenced by secret_name
161 162 163 164 |
# File 'lib/opswalrus/hosts_file.rb', line 161 def read_secret(secret_name) secret = secrets[secret_name] secret.decrypt(@cipher) if secret end |
#secrets ⇒ Object
secrets are key/value pairs in which the key is an identifier used throughout the yaml file to reference the secret’s value and the associated value is either a Hash or a String.
-
If the secret’s value is a Hash, then the Hash must consist of two keys - ids and a secret value:
-
the ids field explicitly names the intended audience for the secret value. The value associated with the ids field is either a String value structured as a comma delimited list of ids, in which each id is a reference to an id contained within the ids section of the inventory file OR the ids field is an Array value in which each element of the array is a reference to an id contained within the ids section of the inventory file.
-
the value field is a String value storing the secret value
-
-
If the secret’s value is a String, then the string is the secret value, and is interpreted to be intended for use by an audience consisting of all of the ids listed in the ids section of the inventory file.
returns a Hash of secret-name/Secret pairs
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/opswalrus/hosts_file.rb', line 79 def secrets @secrets ||= (@yaml["secrets"] || {}).map do |secret_name, secret_attrs| audience_ids, secret_value = case secret_attrs when Hash id_names = case ids_value = secret_attrs["ids"] when String ids_value.split(',').map(&:strip) when Array ids_value.map {|elem| elem.to_s.strip } else raise "ids field beloning to secret '#{secret_name}' is of an unknown type: #{ids_value.class.name}: #{ids_value.inspect}" end value = secret_attrs["value"] [id_names, value] when String id_names = self.ids.select {|k,id_public_key_or_array_of_id_names| PublicKey === id_public_key_or_array_of_id_names }.keys value = secret_attrs [id_names, value] else raise "Secret '#{secret_name}' has an unexpected type #{secret_attrs.class.name}: #{secret_attrs.inspect}" end [secret_name, Secret.new(secret_name, secret_value, audience_ids)] end.to_h end |
#tags(host) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/opswalrus/hosts_file.rb', line 130 def (host) host_attrs = @yaml[host] case host_attrs when Array = host_attrs .compact.uniq when Hash = host_attrs["tags"] || [] .compact.uniq end || [] end |
#to_yaml ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/opswalrus/hosts_file.rb', line 143 def to_yaml hash = {} hash["defaults"] = defaults unless defaults.empty? hosts.each do |host| hash[host.host] = host.to_h end hash["secrets"] = secrets hash["ids"] = ids yaml = Psych.safe_dump(hash, permitted_classes: [SecretRef, Secret, PublicKey], line_width: 500) yaml.sub(/^---\s*/,"") # omit the leading line: ---\n end |