oci-logging-analytics-kubernetes-discovery 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/bin/oci-loganalytics-kubernetes-discovery +102 -25
  4. data/lib/config/oci_client_retry_config.rb +11 -8
  5. data/lib/discover/infrastructure.rb +81 -29
  6. data/lib/discover/object.rb +19 -4
  7. data/lib/dto/infra/load_balancer_payload.rb +32 -0
  8. data/lib/dto/infra/node_pool_payload.rb +28 -0
  9. data/lib/dto/infra/{node_pool_entity_payload.rb → resource_payload.rb} +6 -6
  10. data/lib/dto/infra/subnet_payload.rb +34 -0
  11. data/lib/dto/infra_objects_payload.rb +3 -3
  12. data/lib/dto/kubernetes_objects_payload.rb +19 -15
  13. data/lib/dto/state.rb +7 -3
  14. data/lib/enum/auth_type_enum.rb +1 -0
  15. data/lib/enum/infrastructure_resource_discovery.rb +1 -0
  16. data/lib/enum/object_client_mapping_enum.rb +1 -1
  17. data/lib/enum/stack_job_lifecycle_state_enum.rb +14 -0
  18. data/lib/enum/stack_job_operation_enum.rb +10 -0
  19. data/lib/infra_resources.rb +142 -41
  20. data/lib/objects_resources.rb +16 -6
  21. data/lib/oci_loganalytics_resources_discovery.rb +104 -77
  22. data/lib/util/helper.rb +15 -0
  23. data/lib/util/kube_client.rb +1 -0
  24. data/lib/util/kubectl_ops.rb +1 -1
  25. data/lib/util/log_analytics.rb +2 -2
  26. data/lib/util/oci_clients.rb +222 -103
  27. data/lib/util/service_logs.rb +559 -0
  28. data/lib/util/state_manager.rb +12 -2
  29. data/lib/util/string_utils.rb +48 -0
  30. data/lib/version.rb +1 -1
  31. data/oci-logging-analytics-kubernetes-discovery.gemspec +1 -1
  32. metadata +13 -10
  33. data/lib/dto/infra/cluster_entity_payload.rb +0 -22
  34. data/lib/dto/infra/load_balancers_entity_payload.rb +0 -22
  35. data/lib/dto/infra/subnet_entity_payload.rb +0 -22
  36. data/lib/dto/infra/vcn_entity_payload.rb +0 -22
@@ -16,6 +16,7 @@ require_relative './util/kube_client'
16
16
  require_relative './util/oci_clients'
17
17
  require_relative './util/log_analytics'
18
18
  require_relative './util/state_manager'
19
+ require_relative './util/service_logs'
19
20
 
20
21
  # DTO
21
22
  require_relative './dto/payload/log_events'
@@ -39,7 +40,7 @@ module OciLogAnalyticsResourcesDiscovery
39
40
  module_function
40
41
 
41
42
  attr_accessor :cluster_config_hash, :auth_config_hash, :kube_config_hash, :app_config_hash,
42
- :auth_object, :kube_clients, :oci_clients, :snapshot_id
43
+ :auth_object, :kube_clients, :oci_clients, :oci_region, :oci_domain, :snapshot_id
43
44
 
44
45
  @@oci_la_log_source_name = 'Kubernetes Objects Discovery Payload Logs'
45
46
  @@oci_la_log_path = 'UNDEFINED'
@@ -49,58 +50,78 @@ module OciLogAnalyticsResourcesDiscovery
49
50
  @auth_config_hash = auth_config_hash
50
51
  @kube_config_hash = kube_config_hash
51
52
  @app_config_hash = app_config_hash
53
+ end
52
54
 
53
- # OCI.logger = Util::Logging.logger
55
+ def initiate_discovery
56
+ Util::OCIClients.initialize(@auth_config_hash)
54
57
 
55
- get_auth_object
58
+ @auth_object = Util::OCIClients.get_auth_config_object
59
+ @oci_clients = Util::OCIClients.get_clients
60
+ @oci_region = Util::OCIClients.get_region
61
+ @oci_domain = Util::OCIClients.get_domain
56
62
 
57
- options = { mode: app_config_hash[:mode] }
58
- get_oci_clients(options)
63
+ @kube_clients = Util::KubeClient.create_clients(@kube_config_hash)
59
64
 
60
- if @app_config_hash[:mode] == 'object'
61
- @snapshot_id = Time.now.to_i
62
- get_kube_clients
63
- Util::StateManager.init(@cluster_config_hash[:kubernetes_resourcename_prefix], @cluster_config_hash[:kubernetes_cluster_namespace])
64
- Util::KubectlOps.set_chunk_limit @app_config_hash[:chunk_limit]
65
- logger.debug("Chunk limit set to - #{@app_config_hash[:chunk_limit]}")
66
- end
67
- end
65
+ Util::StateManager.init(@cluster_config_hash[:kubernetes_resourcename_prefix], @cluster_config_hash[:kubernetes_cluster_namespace])
68
66
 
69
- def initiate_infra_discovery
70
- logger.info('Initiating Kubernetes infrastructure discovery.')
71
- begin
72
- infra_objects_payload = get_infra_resources_payload
73
- rescue StandardError => e
74
- logger.error("Error occurred while fetching infrastructure resources. Error: #{e}")
75
- raise StandardError, 'Error occurred while fetching infrastructure resources.'
76
- end
67
+ # Collect infrastructure discovery data
68
+ infra_objects_payload = nil
69
+ infra_discovery_failed = false
77
70
 
78
- logger.info('Infrastructure discovery payload created.')
79
- logger.trace("Infrastructure discovery payload: \n#{Yajl.dump(infra_objects_payload.to_hash)}")
71
+ logger.info('Initiating Kubernetes Objects discovery.')
80
72
 
81
- if @app_config_hash[:skip_upload]
82
- logger.warn('--skip_upload Flag is set: Skipping payload upload to OCI Logging Analytics.')
83
- else
84
- if !infra_objects_payload.nil?
85
- # TODO: Upload implemetation pending.
86
- else
87
- logger.warn('No infrastructure objects discovered.')
88
- end
89
- end
90
- end
73
+ # Collect platform discovery data
74
+ @snapshot_id = Time.now.to_i
75
+ Util::KubectlOps.set_chunk_limit @app_config_hash[:chunk_limit]
76
+ logger.debug("Chunk limit set to - #{@app_config_hash[:chunk_limit]}")
91
77
 
92
- def initiate_object_discovery
93
78
  kubernetes_objects_payload = get_objects_resources_payload
79
+
94
80
  if @app_config_hash[:collect_warning_events_only]
95
81
  logger.info('Only Warning event logs will be collected as the following flag is set: --collect_warning_events_only')
96
82
  end
83
+
97
84
  kubernetes_objects_payload = filter_event_logs(kubernetes_objects_payload,
98
85
  @app_config_hash[:collect_warning_events_only])
99
86
 
100
87
  logger.info('Kubernetes objects discovery payload created.')
101
- logger.trace("Object Discovery Payload: \n#{Yajl.dump(kubernetes_objects_payload[:processed_objects].to_hash)}")
102
88
  logger.trace("Object Log Collection Payload: \n#{Yajl.dump(kubernetes_objects_payload[:raw_response].to_hash)}")
103
89
 
90
+ if @cluster_config_hash[:enable_infra_discovery]
91
+ logger.info('Initiating kubernetes infrastructure discovery.')
92
+ begin
93
+ lb_ip_array = parse_lb_ips(kubernetes_objects_payload)
94
+ logger.info("Discovered Load Balancer IPs: #{lb_ip_array}")
95
+ infra_objects_payload = InfraResources.get_infra_resources(
96
+ @app_config_hash,
97
+ @cluster_config_hash,
98
+ @cluster_config_hash[:kubernetes_cluster_id],
99
+ lb_ip_array
100
+ )
101
+ logger.trace("Infrastructure discovery payload: \n#{Yajl.dump(infra_objects_payload.to_hash)}")
102
+ logger.info('Infrastructure discovery payload created.')
103
+ rescue StandardError => e
104
+ # Not raising an error as platform discovery still needs to continue.
105
+ infra_discovery_failed = true
106
+ logger.error("Error occurred while fetching infrastructure resources. Error: #{e}")
107
+ logger.info('Proceeding with object discovery.')
108
+ end
109
+ else
110
+ logger.info('--kubernetes_cluster_id not provided. Infrastructure discovery skipped.') # TODO: should be Warn ?
111
+ end
112
+
113
+ # Combine discovery and infrastructure data
114
+ processed_objects = kubernetes_objects_payload[:processed_objects]
115
+ combined_data = Dto::KubernetesObjectsPayLoad.new(
116
+ processed_objects[:cluster], processed_objects[:nodes], processed_objects[:pods], processed_objects[:services], processed_objects[:endpoint_slices],
117
+ processed_objects[:deployments], processed_objects[:replica_sets], processed_objects[:daemon_sets], processed_objects[:cron_jobs],
118
+ processed_objects[:jobs], processed_objects[:stateful_sets], processed_objects[:events], processed_objects[:namespaces], infra_objects_payload
119
+ )
120
+
121
+ logger.info('Combined discovery payload created.')
122
+ logger.trace("Combined discovery payload: \n#{Yajl.dump(combined_data.to_hash)}")
123
+
124
+ # Send collected data
104
125
  if @app_config_hash[:skip_upload]
105
126
  logger.warn('--skip_upload Flag is set: Skipping payload upload to OCI logging analytics.')
106
127
  else
@@ -118,7 +139,8 @@ module OciLogAnalyticsResourcesDiscovery
118
139
  upload_data_via_discovery_api(zip_object_log_payload, object_logs_opts) unless zip_object_log_payload.nil?
119
140
  logger.info('Successfully uploaded object logs to OCI')
120
141
 
121
- # Update the Events tracker config map only after successful objects logs upload
142
+ # DEV NOTE: Update the Events tracker config map only after successful objects logs upload
143
+ # Carefully check how last_timestamp update is managed if you wish to move the below code
122
144
  Util::StateManager.update_state_configmap
123
145
  rescue StandardError
124
146
  logger.error('Error occurred while uploading object logs to OCI')
@@ -128,34 +150,54 @@ module OciLogAnalyticsResourcesDiscovery
128
150
  unless @cluster_config_hash[:oci_la_cluster_entity_id].nil?
129
151
  begin
130
152
  # Upload object discovery payload with relevant header parameters
131
- logger.info('Uploading Object Discovery Payload to OCI')
153
+ logger.info('Uploading Discovery Payload to OCI')
132
154
  object_discovery_opts = {
133
155
  payload_type: 'JSON',
134
156
  discovery_data_type: 'K8S_OBJECTS',
135
157
  opc_meta_properties: "cluster-entity-id:#{@cluster_config_hash[:oci_la_cluster_entity_id]};sub-type:discovery_payload;cluster-id:#{@cluster_config_hash[:kubernetes_cluster_id]};cluster-name:#{@cluster_config_hash[:kubernetes_cluster_name]}"
136
158
  }
137
- json_object_discovery_payload = Yajl.dump(kubernetes_objects_payload[:processed_objects].to_hash)
159
+ json_object_discovery_payload = Yajl.dump(combined_data.to_hash)
138
160
  upload_data_via_discovery_api(json_object_discovery_payload, object_discovery_opts)
139
- logger.info('Successfully uploaded object discovery payload to OCI')
161
+ logger.info('Successfully uploaded discovery payload to OCI')
140
162
  rescue StandardError
141
- logger.error('Error occurred while uploading object discovery payload to OCI')
163
+ logger.error('Error occurred while uploading discovery payload to OCI')
142
164
  upload_failure = true
143
165
  end
144
166
  end
145
167
 
146
- raise StandardError, 'Error occurred while uploading payloads to OCI' if upload_failure
168
+ raise StandardError, 'Error uploading payloads to OCI' if upload_failure
147
169
  end
148
- end
149
170
 
150
- def get_auth_object
151
- begin
152
- Util::OCIClients.initialize_auth_config(@auth_config_hash)
153
- rescue StandardError => e
154
- logger.error("Error occurred in creating authentication object - #{e}")
155
- raise e
171
+ # Execute Resource Manager Stack
172
+ if !infra_discovery_failed && @app_config_hash[:enable_service_log]
173
+ logger.info('Initiating service logs collection.')
174
+ log_analytics_entity_details = nil
175
+ begin
176
+ log_analytics_entity_details = Util::ServiceLogs.fetch_log_analytics_entity_details(
177
+ @cluster_config_hash[:oci_la_namespace],
178
+ @cluster_config_hash[:oci_la_cluster_entity_id]
179
+ )
180
+ rescue StandardError => e
181
+ logger.error('Error fetching log analytics entity details.')
182
+ end
183
+ unless log_analytics_entity_details.nil?
184
+ begin
185
+ logger.info('Invoking resource manager stack for service log creation')
186
+ Util::ServiceLogs.invoke_resource_manager_stack(infra_objects_payload,
187
+ @app_config_hash,
188
+ @cluster_config_hash[:oci_la_log_group_id],
189
+ log_analytics_entity_details.compartment_id,
190
+ @oci_region,
191
+ @oci_domain)
192
+ rescue StandardError => e
193
+ logger.error('Error invoking/updating service logs collection')
194
+ raise e
195
+ end
196
+ end
197
+ else
198
+ log_msg = infra_discovery_failed ? 'No infrastructure objects discovered' : 'Flag --enable_service_log not provided'
199
+ logger.info("#{log_msg}. Stack operations skipped")
156
200
  end
157
- @auth_object = Util::OCIClients.get_auth_config_object
158
- logger.debug('Successfully loaded the OCI auth config.')
159
201
  end
160
202
 
161
203
  def get_kube_clients
@@ -165,33 +207,9 @@ module OciLogAnalyticsResourcesDiscovery
165
207
  logger.error("Error occurred in creating kubeclients - #{e}")
166
208
  raise e
167
209
  end
168
- @kube_clients = Util::KubeClient.get_clients
169
- logger.debug('Kubeclients created successfully.')
170
- end
171
-
172
- def get_oci_clients(options)
173
- begin
174
- Util::OCIClients.create_clients(@auth_object, options)
175
- rescue StandardError => e
176
- logger.error("Error occurred in creating OCI clients - #{e}")
177
- raise e
178
- end
179
- @oci_clients = Util::OCIClients.get_clients
180
- logger.debug('OCI clients created successfully.')
181
- end
182
-
183
- def get_infra_resources_payload
184
- logger.debug('Discovering Infrastructure Resources')
185
- infra_resources_payload = nil
186
-
187
- unless @auth_object.nil?
188
- begin
189
- infra_resources_payload = InfraResources.get_infra_resources(@auth_object, @cluster_config_hash[:oci_la_cluster_entity_id])
190
- rescue StandardError => e
191
- logger.error("Error in getting infrastructure resources: #{e}")
192
- end
193
- end
194
- infra_resources_payload
210
+ @kube_clients =
211
+ logger.debug('Kube client created successfully.')
212
+ Util::KubeClient.get_clients
195
213
  end
196
214
 
197
215
  def get_objects_resources_payload
@@ -205,6 +223,15 @@ module OciLogAnalyticsResourcesDiscovery
205
223
  )
206
224
  end
207
225
 
226
+ def parse_lb_ips(kubernetes_objects_payload)
227
+ ip_array = []
228
+ lbs = kubernetes_objects_payload[:processed_objects][:services].select { |service| service[:serviceType] == "LoadBalancer" }
229
+ lbs.each do |lb|
230
+ ip_array.append lb.loadBalancerIP unless lb.loadBalancerIP.nil?
231
+ end
232
+ ip_array
233
+ end
234
+
208
235
  def filter_event_logs(kubernetes_objects_payload, collect_warning_events_only)
209
236
  cluster_events_processed_time = Util::StateManager.state.last_timestamp
210
237
  new_processed_time = cluster_events_processed_time
@@ -0,0 +1,15 @@
1
+ module Util
2
+ module Helper
3
+ module_function
4
+ # Function to check if a value is defined in the enum
5
+ def enum_value_defined?(enum_module, value)
6
+ enum_module.constants(false).any? { |const| enum_module.const_get(const) == value }
7
+ end
8
+
9
+ # Function to get enum values as list
10
+ def enum_values(enum_module)
11
+ enum_module.constants(false).map { |const| enum_module.const_get(const) }
12
+ end
13
+
14
+ end
15
+ end
@@ -27,6 +27,7 @@ module Util
27
27
  raise e
28
28
  end
29
29
 
30
+ logger.debug('Kube clients created successfully.')
30
31
  set_clients(@core_client, @apps_client, @batch_client, @discover_v1_client)
31
32
  end
32
33
 
@@ -48,7 +48,7 @@ module Util
48
48
  case method
49
49
  when :get_objects
50
50
  if client.nil?
51
- client = Util::KubeClient.get_clients[Enum::ObjectClientMapingEnum.const_get(object_type.upcase.to_s)]
51
+ client = Util::KubeClient.get_clients[Enum::ObjectClientMappingEnum.const_get(object_type.upcase.to_s)]
52
52
  end
53
53
  logger.debug("Fetching '#{object_type}' details from all namespaces")
54
54
  method_verb = 'GET'
@@ -33,7 +33,7 @@ module Util
33
33
  upload_discovery_data_details = payload,
34
34
  opts)
35
35
  rescue StandardError => e
36
- logger.error("Error while uploading payload to 'Log Analytics': #{e}")
36
+ logger.error("Error while uploading payload to 'Log Analytics'. Error: #{e}")
37
37
  raise StandardError, "Unable to upload data to 'Log Analytics' client."
38
38
  end
39
39
  response
@@ -56,7 +56,7 @@ module Util
56
56
  region = signer.region
57
57
  else
58
58
  logger.warn("Unknown auth_type provided for Discovery API (raw request): #{auth_object[:auth_type]}")
59
- raise StandardError 'Unknown auth_type for Discovery API (raw request)'
59
+ raise StandardError, 'Unknown auth_type for Discovery API (raw request)'
60
60
  end
61
61
 
62
62
  # Construct URL protocol, subdomain, domain and port