Class: Paramsync::SyncTarget

Inherits:
Object
  • Object
show all
Defined in:
lib/paramsync/sync_target.rb

Constant Summary collapse

VALID_CONFIG_KEYS =
%w( name type region prefix path exclude chomp delete erb_enabled account )
REQUIRED_CONFIG_KEYS =
%w( prefix region )
VALID_TYPES =
[ :dir, :file ]
DEFAULT_TYPE =
:dir

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config:, account:, base_dir:) ⇒ SyncTarget

Returns a new instance of SyncTarget.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/paramsync/sync_target.rb', line 12

def initialize(config:, account:, base_dir:)
  if not config.is_a? Hash
    raise Paramsync::ConfigFileInvalid.new("Sync target entries must be specified as hashes")
  end

  if (config.keys - Paramsync::SyncTarget::VALID_CONFIG_KEYS) != []
    raise Paramsync::ConfigFileInvalid.new("Only the following keys are valid in a sync target entry: #{Paramsync::SyncTarget::VALID_CONFIG_KEYS.join(", ")}")
  end

  if (Paramsync::SyncTarget::REQUIRED_CONFIG_KEYS - config.keys) != []
    raise Paramsync::ConfigFileInvalid.new("The following keys are required in a sync target entry: #{Paramsync::SyncTarget::REQUIRED_CONFIG_KEYS.join(", ")}")
  end

  @base_dir = base_dir
  self.region = config['region']
  self.prefix = config['prefix']
  self. = config['account']
  self.path = config['path'] || config['prefix']
  self.name = config['name']
  self.type = (config['type'] || Paramsync::SyncTarget::DEFAULT_TYPE).to_sym
  unless Paramsync::SyncTarget::VALID_TYPES.include?(self.type)
    raise Paramsync::ConfigFileInvalid.new("Sync target '#{self.name || self.path}' has type '#{self.type}'. But only the following types are valid: #{Paramsync::SyncTarget::VALID_TYPES.collect(&:to_s).join(", ")}")
  end

  if self.type == :file and File.directory?(self.base_path)
    raise Paramsync::ConfigFileInvalid.new("Sync target '#{self.name || self.path}' has type 'file', but path '#{self.path}' is a directory.")
  end

  self.exclude = config['exclude'] || []
  if config.has_key?('chomp')
    @do_chomp = config['chomp'] ? true : false
  end
  if config.has_key?('delete')
    @do_delete = config['delete'] ? true : false
  else
    @do_delete = false
  end

  self.ssm = Aws::SSM::Client.new(**{
    region: region,
    credentials:  ? Aws::AssumeRoleCredentials.new(
      client: Aws::STS::Client.new(region: region),
      role_arn: ,
      role_session_name: "paramsync"
    ) : nil,
  }.compact)
  self.erb_enabled = config['erb_enabled']
end

Instance Attribute Details

#accountObject

Returns the value of attribute account.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def 
  @account
end

#erb_enabledObject

Returns the value of attribute erb_enabled.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def erb_enabled
  @erb_enabled
end

#excludeObject

Returns the value of attribute exclude.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def exclude
  @exclude
end

#nameObject

Returns the value of attribute name.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def name
  @name
end

#pathObject

Returns the value of attribute path.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def path
  @path
end

#prefixObject

Returns the value of attribute prefix.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def prefix
  @prefix
end

#regionObject

Returns the value of attribute region.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def region
  @region
end

#ssmObject

Returns the value of attribute ssm.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def ssm
  @ssm
end

#typeObject

Returns the value of attribute type.



6
7
8
# File 'lib/paramsync/sync_target.rb', line 6

def type
  @type
end

Instance Method Details

#base_pathObject



88
89
90
# File 'lib/paramsync/sync_target.rb', line 88

def base_path
  @base_path ||= File.join(@base_dir, self.path)
end

#chomp?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/paramsync/sync_target.rb', line 65

def chomp?
  @do_chomp
end

#clear_cacheObject



81
82
83
84
85
86
# File 'lib/paramsync/sync_target.rb', line 81

def clear_cache
  @base_path = nil
  @local_files = nil
  @local_items = nil
  @remote_items = nil
end

#delete?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/paramsync/sync_target.rb', line 69

def delete?
  @do_delete
end

#description(mode = :push) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/paramsync/sync_target.rb', line 73

def description(mode = :push)
  if mode == :pull
    "#{self.name.nil? ? '' : self.name.bold + "\n"}#{'ssm'.cyan}:#{self.region.green}:#{self.prefix} => #{'local'.blue}:#{self.path}"
  else
    "#{self.name.nil? ? '' : self.name.bold + "\n"}#{'local'.blue}:#{self.path} => #{'ssm'.cyan}:#{self.region.green}:#{self.prefix}"
  end
end

#diff(mode) ⇒ Object



166
167
168
# File 'lib/paramsync/sync_target.rb', line 166

def diff(mode)
  Paramsync::Diff.new(target: self, local: self.local_items, remote: self.remote_items, mode: mode)
end

#erb_enabled?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/paramsync/sync_target.rb', line 61

def erb_enabled?
  @erb_enabled
end

#load_local_file(local_file) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/paramsync/sync_target.rb', line 135

def load_local_file(local_file)
  file = File.read(local_file)

  if self.chomp?
    encoded_file = file.chomp.force_encoding(Encoding::ASCII_8BIT)
  else
    encoded_file = file.force_encoding(Encoding::ASCII_8BIT)
  end

  return ERB.new(encoded_file).result if erb_enabled?
  encoded_file
end

#local_filesObject



92
93
94
95
# File 'lib/paramsync/sync_target.rb', line 92

def local_files
  # see https://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
  @local_files ||= Dir["#{self.base_path}/**{,/*/**}/*"].select { |f| File.file?(f) }
end

#local_itemsObject



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/paramsync/sync_target.rb', line 97

def local_items
  return @local_items if not @local_items.nil?
  @local_items = {}

  case self.type
  when :dir
    self.local_files.each do |local_file|
      @local_items[local_file.sub(%r{^#{self.base_path}/?}, '')] =
        load_local_file(local_file)
    end

  when :file
    if File.exist?(self.base_path)
      @local_items = local_items_from_file
    end
  end
  @local_items.transform_values! do |val|
    is_kms = val.bytes[0] == 0x01
    if is_kms
      val = Paramsync.config.kms_client.decrypt(
        ciphertext_blob: val
      ).plaintext
    end
    [val, is_kms]
  end
  @local_items
end

#local_items_from_fileObject



125
126
127
128
129
130
131
132
133
# File 'lib/paramsync/sync_target.rb', line 125

def local_items_from_file
  if erb_enabled?
    loaded_file = YAML.load(ERB.new(File.read(self.base_path)).result)
  else
    loaded_file = YAML.load_file(self.base_path)
  end

  flatten_hash(nil, loaded_file)
end

#remote_itemsObject



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/paramsync/sync_target.rb', line 148

def remote_items
  return @remote_items if not @remote_items.nil?
  @remote_items = {}

  resp = self.ssm.get_parameters_by_path(
    path: self.prefix,
    recursive: true,
    with_decryption: true
  )

  return @remote_items if resp.values.nil?
  resp.flat_map(&:parameters).each do |param|
    @remote_items[param.name.gsub(self.prefix, '')] = [(param.value.nil? ? '' : param.value), param.type == 'SecureString']
  end

  @remote_items
end