Class: Kitsune::Kit::Operations::EnsureService

Inherits:
Object
  • Object
show all
Defined in:
lib/kitsune/kit/operations/ensure_service.rb

Overview

Service lifecycle coordination intentionally lives together so apply and rollback share one transaction model.

Constant Summary collapse

TYPES =

rubocop:disable Metrics/ClassLength

%w[postgres redis].freeze
MARKER_DIRECTORY =
"/var/lib/kitsune/state"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config:, type:, transport_factory:, state_store:, secret_store:, clock: -> { Time.now.utc }) ⇒ EnsureService

Returns a new instance of EnsureService.

Raises:

  • (ArgumentError)


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 22

def initialize(config:, type:, transport_factory:, state_store:, secret_store:, clock: -> { Time.now.utc })
  @config = config
  @type = type.to_s
  raise ArgumentError, "unknown service type: #{@type}" unless TYPES.include?(@type)

  @service = config.services.public_send(@type)
  @transport_factory = transport_factory
  @state_store = state_store
  @secret_store = secret_store
  @resource = "service.#{@type}"
  @managed_state = ServiceState.new(state_store: state_store, environment: config.environment,
                                    resource: resource)
  @compose = ServiceCompose.new(config: config, type: @type, service: @service)
  @clock = clock
end

Instance Attribute Details

#resourceObject (readonly)

Returns the value of attribute resource.



20
21
22
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 20

def resource
  @resource
end

Instance Method Details

#apply(change) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 55

def apply(change)
  return true unless change.changed?

  @firewall_changed = false
  previous_state = managed_state
  password = validated_secret
  files.ensure_managed_or_absent!(remote_fingerprint)
  prepare_backup
  transport.execute("mkdir", arguments: ["-p", service_directory], timeout: 10).then do |result|
    raise_remote!(result, "create service directory")
  end
  @compose.documents.each do |document|
    transport.upload(
      content: document.content, remote_path: files.compose_path(document.filename), mode: "0600"
    )
  end
  transport.upload(content: env_content(password), remote_path: env_path, mode: "0600")
  validate_compose
  start_service
  verify_service
  firewall.reconcile(managed_state)
  @firewall_changed = true
  write_marker
  record_state(change)
  true
rescue StandardError => e
  recover_failed_apply(previous_state, e)
end

#backupObject



94
95
96
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 94

def backup
  backup_operation.call(managed_state)
end

#destroy_dataObject



112
113
114
115
116
117
118
119
120
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 112

def destroy_data
  previous = managed_state
  return false unless previous

  compose("down", "--volumes", "--remove-orphans", filenames: installed_compose_files)
  files.destroy(previous, marker_path: marker_path)
  @managed_state.delete("destroy_data")
  true
end

#planObject



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 38

def plan
  return pending_change unless transport_factory.server&.public_ip

  actual = remote_fingerprint
  files.ensure_managed_or_absent!(actual)
  return unchanged_change if actual == fingerprint

  Change.new(
    resource: resource,
    action: actual.empty? ? "create" : "update",
    summary: "#{actual.empty? ? 'Install' : 'Update'} #{@type} service",
    details: safe_details(actual)
  )
rescue Errors::ConnectionError
  pending_change
end

#removeObject



84
85
86
87
88
89
90
91
92
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 84

def remove
  return false unless managed?

  compose("down", "--remove-orphans", filenames: installed_compose_files)
  firewall.remove
  remove_marker
  @managed_state.record_remove
  true
end

#rollbackObject



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/kitsune/kit/operations/ensure_service.rb', line 98

def rollback
  previous = managed_state
  return false unless previous

  remove
  if files.restore_previous(previous, marker_path: marker_path)
    firewall.restore(previous["previous_state"] || {})
    start_service(filenames: previous.dig("previous_state", "compose_files")) if
      previous.dig("previous_state", "installed") != false
  end
  @managed_state.record_rollback(previous)
  true
end