Class: Hippo::Stage

Inherits:
Object
  • Object
show all
Defined in:
lib/hippo/stage.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(wd, config_root, options) ⇒ Stage

Returns a new instance of Stage.



14
15
16
17
18
# File 'lib/hippo/stage.rb', line 14

def initialize(wd, config_root, options)
  @wd = wd
  @config_root = config_root
  @options = options
end

Instance Attribute Details

#config_rootObject (readonly)

Returns the value of attribute config_root.



12
13
14
# File 'lib/hippo/stage.rb', line 12

def config_root
  @config_root
end

#wdObject (readonly)

Returns the value of attribute wd.



11
12
13
# File 'lib/hippo/stage.rb', line 11

def wd
  @wd
end

Instance Method Details

#all_objectsHash

Return an array of all objects that should be managed by Hippo

Returns:



160
161
162
163
164
165
166
167
168
# File 'lib/hippo/stage.rb', line 160

def all_objects
  @all_objects ||= begin
    all = (deployments | services | configs | jobs('install') | jobs('deploy'))
    all.each_with_object({}) do |object, hash|
      hash[object.kind] ||= {}
      hash[object.kind][object.name] = object
    end
  end
end

#apply(objects) ⇒ Hash

Apply a series of objecst with

Parameters:

Returns:

Raises:



202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/hippo/stage.rb', line 202

def apply(objects)
  command = ['kubectl']
  command += ['--context', context] if context
  command += ['apply', '-f', '-']

  yaml_to_apply = objects.map(&:yaml_to_apply).join("\n")

  stdout, stderr, status = Open3.capture3(command.join(' '), stdin_data: yaml_to_apply + "\n")

  raise Error, "[kubectl] #{stderr}" unless status.success?

  Util.parse_kubectl_apply_lines(stdout)
end

#branchObject



28
29
30
# File 'lib/hippo/stage.rb', line 28

def branch
  @options['branch']
end

#configObject



44
45
46
# File 'lib/hippo/stage.rb', line 44

def config
  @options['config']
end

#configsHash<String,Hippo::ObjectDefinition>

Return an array of all configuration objects

Returns:



145
146
147
# File 'lib/hippo/stage.rb', line 145

def configs
  @configs ||= Util.create_object_definitions(objects('config'), self)
end

#contextObject



40
41
42
# File 'lib/hippo/stage.rb', line 40

def context
  @options['context']
end

#decoratorObject

Return a new decorator object that can be passed to objects that would like to decorator things.



73
74
75
76
77
78
79
80
81
82
# File 'lib/hippo/stage.rb', line 73

def decorator
  proc do |data|
    begin
      template = Liquid::Template.parse(data)
      template.render(template_vars, filters: [LiquidFilters])
    rescue Liquid::SyntaxError => e
      raise Error, "Template error: #{e.message}"
    end
  end
end

#delete(*names) ⇒ Boolean

Delete an object from the kubernetes API

Parameters:

  • names (Array<String>)

Returns:

  • (Boolean)


234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/hippo/stage.rb', line 234

def delete(*names)
  command = kubectl('delete', *names)
  stdout, stderr, status = Open3.capture3(*command)
  if status.success?
    Util.parse_kubectl_apply_lines(stdout)
    true
  else
    stderr = stderr.read
    if stderr =~ /\" not found$/
      false
    else
      raise Error, "[kutectl] #{stderr}"
    end
  end
end

#delete_pruneable_objectsvoid

This method returns an undefined value.

Remove any objects which are prunable



113
114
115
116
117
118
# File 'lib/hippo/stage.rb', line 113

def delete_pruneable_objects
  live_objects(pruneable_only: true).each do |object|
    object = object[:live]
    delete(object.kind, object.name)
  end
end

#deploymentsHash<String,Hippo::ObjectDefinition>

Return an array of all deployments for this stage

Returns:



131
132
133
# File 'lib/hippo/stage.rb', line 131

def deployments
  @deployments ||= Util.create_object_definitions(objects('deployments'), self, required_kinds: ['Deployment'])
end

#get(*names) ⇒ Array<Hippo::ObjectDefinition>

Get some data from the kubernetes API

Parameters:

  • names (Array<String>)

Returns:

Raises:



220
221
222
223
224
225
226
227
228
# File 'lib/hippo/stage.rb', line 220

def get(*names)
  command = kubectl('get', '-o', 'yaml', *names)
  stdout, stderr, status = Open3.capture3(*command)
  raise Error, "[kubectl] #{stderr}" unless status.success?

  yaml = YAML.safe_load(stdout, permitted_classes: [Time])
  yaml = yaml['items'] || [yaml]
  yaml.map { |y| ObjectDefinition.new(y, self, clean: true) }
end

#image_tagObject



32
33
34
# File 'lib/hippo/stage.rb', line 32

def image_tag
  @options['image-tag']
end

#imagesObject



48
49
50
51
52
# File 'lib/hippo/stage.rb', line 48

def images
  @images ||= manifest.images.deep_merge(@options['images'] || {}).each_with_object({}) do |(key, image), hash|
    hash[key] = Image.new(key, image)
  end
end

#jobs(type) ⇒ Hash<String,Hippo::ObjectDefinition>

Return an array of all job objects

Returns:



152
153
154
155
# File 'lib/hippo/stage.rb', line 152

def jobs(type)
  @jobs ||= {}
  @jobs[type] ||= Util.create_object_definitions(objects("jobs/#{type}"), self)
end

#kubectl(*commands) ⇒ Array<String>

Return a kubectl command ready for use within this stage’s namespace and context

Returns:

  • (Array<String>)


191
192
193
194
195
196
# File 'lib/hippo/stage.rb', line 191

def kubectl(*commands)
  prefix = ['kubectl']
  prefix += ['--context', context] if context
  prefix += ['-n', namespace]
  prefix + commands
end

#live_objects(pruneable_only: false) ⇒ Array<Hash>

Return an array of objects that currently exist on the kubernetesa API.

Returns:



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/hippo/stage.rb', line 94

def live_objects(pruneable_only: false)
  los = get(all_objects.keys.join(','), '--selector', 'app.kubernetes.io/managed-by=hippo')
  los.each_with_object([]) do |live_obj, array|
    local = all_objects.dig(live_obj.kind, live_obj.name)
    pruneable = local.nil? && (live_obj.kind != 'Secret' && live_obj.name != 'hippo-secret-key')

    next if pruneable_only && !pruneable

    array << {
      live: live_obj,
      local: local,
      pruneable: pruneable
    }
  end
end

#manifestObject



20
21
22
# File 'lib/hippo/stage.rb', line 20

def manifest
  wd.manifest
end

#nameObject



24
25
26
# File 'lib/hippo/stage.rb', line 24

def name
  @options['name']
end

#namespaceObject



36
37
38
# File 'lib/hippo/stage.rb', line 36

def namespace
  @options['namespace']
end

#objects(path) ⇒ Object



120
121
122
# File 'lib/hippo/stage.rb', line 120

def objects(path)
  manifest.objects(path, decorator: decorator)
end

#overridden_package_valuesHash

Return any package values that have been defined

Returns:



183
184
185
# File 'lib/hippo/stage.rb', line 183

def overridden_package_values
  @options['packages'] || {}
end

#packagesHash<String, Hippo::Package>

Return a hash of all packages available in the stage

Returns:



173
174
175
176
177
178
# File 'lib/hippo/stage.rb', line 173

def packages
  @packages ||= objects('packages').values.each_with_object({}) do |package_hash, hash|
    package = Package.new(package_hash.first, self)
    hash[package.name] = package
  end
end

#readmeObject



84
85
86
87
88
# File 'lib/hippo/stage.rb', line 84

def readme
  return unless manifest.readme

  decorator.call(manifest.readme)
end

#secret_managerObject



124
125
126
# File 'lib/hippo/stage.rb', line 124

def secret_manager
  @secret_manager ||= SecretManager.new(self)
end

#servicesHash<String,Hippo::ObjectDefinition>

Return an array of all services/ingresses for this stage

Returns:



138
139
140
# File 'lib/hippo/stage.rb', line 138

def services
  @services ||= Util.create_object_definitions(objects('services'), self, required_kinds: %w[Service Ingress NetworkPolicy])
end

#template_varsObject

These are the vars to represent this



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/hippo/stage.rb', line 55

def template_vars
  @template_vars ||= begin
    {
      'manifest' => manifest.template_vars,
      'stage-name' => name,
      'branch' => branch,
      'image-tag' => image_tag,
      'namespace' => namespace,
      'context' => context,
      'images' => images.values.each_with_object({}) { |image, hash| hash[image.name] = image.template_vars },
      'config' => manifest.config.deep_merge(config),
      'secrets' => secret_manager.all
    }
  end
end

#wait_for_jobs(names, times = 120) ⇒ Object

Wait for the named jobs to complete



251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/hippo/stage.rb', line 251

def wait_for_jobs(names, times = 120)
  jobs = nil
  times.times do
    jobs = get(*names)

    if jobs.all? { |j| j['status']['active'].nil? }
      return [false, jobs]
    else
      sleep 2
    end
  end

  [true, jobs]
end