Module: Puppet_X::EnterpriseModules::Oci::Type

Defined in:
lib/puppet_x/enterprisemodules/oci/type.rb

Overview

Add Documentation

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(parent) ⇒ Object



8
9
10
11
12
13
14
15
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 8

def self.included(parent)
  parent.send(:include, EasyType)
  parent.send(:include, Settings)
  parent.send(:include, Config)
  parent.extend(Config)
  parent.extend(ClassMethods)
  parent.extend(Settings)
end

Instance Method Details

#before_createObject



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 49

def before_create
  @oci_api_data = to_hash
  #
  # In Puppet the name is the full name. OCI needs just the name.
  # Also sometimes OCI doesn't have a name but just a display_name. So we
  # fill this in too.
  #
  @oci_api_data[:name] = bare_name
  @oci_api_data[:display_name] = bare_name
  #
  # If the compartment_id is nil, it means we are at the root and use the tenant ocid
  #
  @oci_api_data[:compartment_id] ||= client.api_client.config.tenancy #
end

#before_modifyObject



118
119
120
121
122
123
124
125
126
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 118

def before_modify
  @oci_api_data = to_hash
  #
  # Although OCI allows renaming, Puppet doesn't, so we delete the name and display_name
  # attributes
  #
  @oci_api_data.delete(:name)
  @oci_api_data.delete(:display_name)
end

#handle_oci_request(oci_object_type = nil, oci_synchronized = nil, id = nil) ⇒ Object

Generates a nice error when an OCI error is raised in the yield. Also waits for the state specified in the type parameter.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 248

def handle_oci_request(oci_object_type = nil, oci_synchronized = nil, id = nil)
  oci_object_type ||= object_type
  oci_synchronized = synchronized if oci_synchronized.nil?
  begin
    operation_result = yield
  rescue OCI::Errors::ServiceError => e
    fail "#{path}: OCI raised error: #{e.message}"
  end
  wait_for_resource_id = operation_result.headers['opc-work-request-id']
  # See if we can do a synchronize
  return operation_result unless oci_synchronized

  #
  # We can synchronize, so do it.Although the post on oci_core_instance returns
  # an wait_for_resource_id, the API somehow doesn't support the wait_for_state call.
  # So that's why we just use the wait_for state call here.
  #
  if wait_for_resource_id && oci_object_type != 'instance'
    wait_for_work_request(wait_for_resource_id)
  elsif %w[bucket tag].include?(oci_object_type)
    # Do nothing. We can't sync bucket or tag operations
  elsif id.nil? # We need the id from the operation and the operation is a create or update operation
    wait_for_state(oci_object_type, operation_result.data.id, :create)
  else
    wait_for_state(oci_object_type, id, :destroy)
  end
  operation_result
end

#oci_api_dataObject

rubocop: enable Lint/OrAssignmentToConstant



39
40
41
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 39

def oci_api_data
  @oci_api_data
end

#on_createObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 64

def on_create
  Puppet.debug "create #{object_type} #{name} "
  #
  # In Puppet we use underscored names, but OCI needs camel case with a lowecase
  # first character.
  #
  @oci_api_data = @oci_api_data.to_oci
  handle_oci_request do
    @oci_api_data = case object_type
                    when 'tag'
                      tag_namespace_id = resolver.name_to_ocid(tenant, tag_namespace_name, :tagnamespace)
                      create_details = create_class.new(@oci_api_data)
                      client.create_tag(tag_namespace_id, create_details)
                    when 'instance'
                      launch_details = launch_class.new(@oci_api_data)
                      creation_data = client.launch_instance(launch_details)
                      #
                      # The creation of an instance also create's a boot image. To allow searching for it
                      # in the cache, invalidate the cache of boot volumes. This is not very efficient. Specialy
                      # in large environments.
                      # TODO: Find a way to make this more effecient
                      #
                      resolver.invalidate(tenant, :bootvolume)
                      creation_data
                    when 'bucket'
                      namespace = client.get_namespace.data
                      create_details = create_class.new(@oci_api_data)
                      client.send("create_#{object_type}", namespace, create_details)
                    when 'vcn'
                      #
                      # Version 2.18 of the sdk always sets the is_oracle_gua_allocation_enabled property. This causes the
                      # API to fail that why we set it to nil when it isn't explicitly set to true
                      #
                      create_details = create_class.new(@oci_api_data)
                      create_details.is_oracle_gua_allocation_enabled = nil unless is_oracle_gua_allocation_enabled.to_s == 'true'
                      client.send("create_#{object_type}", create_details)
                    else
                      create_details = create_class.new(@oci_api_data)
                      client.send("create_#{object_type}", create_details)
                    end
  end
  #
  # Add the created resource to the name cache so new Puppet resources can find it
  #
  resolver.add_to_cache(tenant, @oci_api_data.data)
  #
  # Report the information back to the provide
  #
  hash = @oci_api_data.data.to_hash.to_puppet
  hash['tenant'] = tenant
  hash['name'] = name
  self[:provider] = self.class.defaultprovider.map_raw_to_resource(hash)
end

#on_destroyObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 155

def on_destroy
  Puppet.debug "destroy #{object_type} #{name} "
  handle_oci_request(object_type, synchronized, provider.id) do
    case object_type
    when 'tag'
      tag_namespace_id = resolver.name_to_ocid(tenant, tag_namespace_name, :tagnamespace)
      #
      # We can only remove the tag after it has been retired. So we make
      # it one kind of operation. Maybe in the future, we can add ensure > 'retired'
      #
      client.update_tag(tag_namespace_id, tag_name, { 'isRetired' => true })
      client.delete_tag(tag_namespace_id, tag_name)
    when 'bucket'
      bucket_name = name.split('/').last
      client.send("delete_#{object_type}", provider.namespace, bucket_name)
    when 'instance'
      client.terminate_instance(provider.id)
    when 'instance_pool'
      client.terminate_instance_pool(provider.id)
    when 'vault'
      details = OCI::KeyManagement::Models::ScheduleVaultDeletionDetails.new
      client.send('schedule_vault_deletion', provider.id, details)
    when 'key'
      client.disable_key(provider.id)
    else
      client.send("delete_#{object_type}", provider.id)
    end
  end
  nil
end

#on_modifyObject



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 128

def on_modify
  Puppet.debug "modify #{object_type} #{name}"
  #
  # In Puppet we use underscored names, but OCI needs camel case with a lowecase
  # first character.
  #
  @oci_api_data = @oci_api_data.to_oci
  update_details = update_class.new(@oci_api_data)
  if update_details.to_hash == {} # There are changes here
    Puppet.debug 'No changes in main data. Defering changes to specific properties.'
  else
    handle_oci_request do
      case object_type
      when 'tag'
        tag_namespace_id = resolver.name_to_ocid(tenant, tag_namespace_name, :tagnamespace)
        client.update_tag(tag_namespace_id, tag_name, update_details)
      when 'bucket'
        bucket_name = name.split('/').last
        client.send("update_#{object_type}", provider.namespace, bucket_name, update_details)
      else
        client.send("update_#{object_type}", provider.id, update_details)
      end
    end
  end
  nil
end

#validate_reference_propery(property, resource) ⇒ Object



43
44
45
46
47
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 43

def validate_reference_propery(property, resource)
  reference_property = property.to_s.scan(/(.*)_id(s)?$/).first.join
  fail "You cannot use both #{property} and #{reference_property} together. Choose either one." \
    if resource.send(reference_property.to_sym) && resource.send(property.to_sym)
end

#wait_for_state(oci_object_type, id, type) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 209

def wait_for_state(oci_object_type, id, type)
  ready_states = type == :create ? present_states : absent_states
  ready_states << 'UNKNOWN' # Some objects don't have a state. So they are ready directly
  eval_proc = ->(response) do
    state = response.data.respond_to?(:lifecycle_state) ? response.data.lifecycle_state : 'UNKNOWN'
    if response.data.respond_to?(:puppet_name)
      Puppet.debug "Waiting for resource #{object_type} with name #{response.data.puppet_name} to become ready; Now in state #{state}."
    else
      Puppet.debug "Waiting for resource #{object_type} to become ready; Now in state #{state}."
    end
    ready_states.include?(state)
  end
  local_retry_config = case type
                       when :destroy
                         nil
                       when :create
                         #
                         # When creating a resource we allow more time and retries before we signal an error
                         # For now we use hardcoded values that are large. Means we have maximum 30 seconds for a
                         # create before we return an error.
                         #
                         retry_config(tenant, 'sleep_calc_millis' => 1500, 'max_attempts' => 20)
                       else
                         retry_config(tenant)
                       end
  waiter_result = client.send("get_#{oci_object_type}", id, :retry_config => local_retry_config)
  waiter_result.wait_until(
    :eval_proc => eval_proc,
    :max_interval_seconds => oci_wait_interval,
    :max_wait_seconds => oci_timeout
  )
rescue OCI::Errors::ServiceError => e
  fail "#{path}: OCI raised error: #{e.message}" unless e.status_code == 404 && type == :destroy
end

#wait_for_work_request(wait_for_resource_id) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/puppet_x/enterprisemodules/oci/type.rb', line 186

def wait_for_work_request(wait_for_resource_id)
  Puppet.debug "Wait on work-request with id #{wait_for_resource_id}..."
  OCI::Waiter::WorkRequest.wait_for_state(
    client,
    wait_for_resource_id,
    lambda do |work_request|
      Puppet.debug "Wait on work-request with id #{wait_for_resource_id}. Current status is #{work_request.status}"
      work_request.status == 'SUCCEEDED'
    end,
    lambda do |work_request|
      fail if work_request.status == 'FAILED'
    end,
    :max_interval_seconds => oci_wait_interval,
    :max_wait_seconds => oci_timeout
  )
rescue OCI::Errors::ServiceError => e
  #
  # If we are not autorized or the resource is not found, infer the work request
  # is done and we are finished
  #
  raise unless e.service_code == 'NotAuthorizedOrNotFound'
end