# Copyright 2017 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require "digest/sha1" require "google/cloud/debugger/backoff" require "google/cloud/debugger/debuggee/app_uniquifier_generator" require "google/cloud/debugger/version" require "google/cloud/env" require "json" module Google module Cloud module Debugger ## # # Debuggee # # Represent a debuggee application. Contains information that identifies # debuggee applications from each other. Maps to gRPC struct # {Google::Devtools::Clouddebugger::V2::Debuggee}. # # It also automatically loads source context information generated from # Cloud SDK. See [Stackdriver Debugger # doc](https://cloud.google.com/debugger/docs/source-context#app_engine_standard_python) # for more information on how to generate this source context information # when used on Google Container Engine and Google Compute engine. This # step is taken care of if debuggee application is hosted on Google App # Engine Flexibile. # # To ensure the multiple instances of the application are indeed the same # application, the debuggee also compute a "uniquifier" generated from # source context or application source code. # class Debuggee ## # @private The gRPC Service object. attr_reader :service ## # Name for the debuggee application # @return [String] attr_reader :service_name ## # Version identifier for the debuggee application # @return [String] attr_reader :service_version ## # Registered Debuggee ID. Set by Stackdriver Debugger service when # a debuggee application is sucessfully registered. # @return [String] attr_reader :id ## # @private Construct a new instance of Debuggee def initialize service, service_name:, service_version: @service = service @service_name = service_name @service_version = service_version @env = Google::Cloud.env @computed_uniquifier = nil @id = nil @register_backoff = Google::Cloud::Debugger::Backoff.new end ## # Register the current application as a debuggee with Stackdriver # Debuggee service. # @return [Boolean] True if registered sucessfully; otherwise false. def register # Wait if backoff applies sleep @register_backoff.interval if @register_backoff.backing_off? begin response = service.register_debuggee to_grpc @id = response.debuggee.id rescue StandardError revoke_registration end registered = registered? registered ? @register_backoff.succeeded : @register_backoff.failed registered end ## # Check whether this debuggee is currently registered or not # @return [Boolean] True if debuggee is registered; otherwise false. def registered? !id.nil? end ## # Revoke the registration of this debuggee def revoke_registration @id = nil end ## # Convert this debuggee into a gRPC # Google::Devtools::Clouddebugger::V2::Debuggee struct. def to_grpc debuggee_args = build_request_arg Google::Devtools::Clouddebugger::V2::Debuggee.decode_json \ debuggee_args.to_json end private ## # @private Build the parameters for this debuggee def build_request_arg debuggee_args = { project: project_id_for_request_arg, description: description, labels: labels, agent_version: agent_version } debuggee_args[:id] = id if id source_context = read_app_json_file "source-context.json" if source_context debuggee_args[:source_contexts] = [source_context] debuggee_args[:ext_source_contexts] = [{ context: source_context }] end debuggee_args[:uniquifier] = compute_uniquifier debuggee_args debuggee_args end ## # @private Build labels hash for this debuggee def labels { "projectid" => String(project_id), "module" => String(service_name), "version" => String(service_version) } end ## # @private Build description string for this debuggee. In # " - " format. Or just the module # version if module name is missing. # # @return [String] A compact debuggee description. # def description if service_version.nil? || service_version.empty? service_name else "#{service_name} - #{service_version}" end end ## # @private Get debuggee project id def project_id service.project end ## # @private # Get project to send as a debuggee argument. This is the numeric # project ID if available (and if it matches the project set in the # configuration). Otherwise, use the configured project. def project_id_for_request_arg if project_id == @env.project_id numeric_id = @env.numeric_project_id return numeric_id.to_s if numeric_id end project_id end ## # @private Build debuggee agent version identifier def agent_version "google.com/ruby#{RUBY_VERSION}-#{Google::Cloud::Debugger::VERSION}" end ## # @private Generate a debuggee uniquifier from source context # information or application source code def compute_uniquifier debuggee_args return @computed_uniquifier if @computed_uniquifier sha = Digest::SHA1.new sha << debuggee_args.to_s unless debuggee_args.key?(:source_contexts) || debuggee_args.key?(:ext_source_contexts) AppUniquifierGenerator.generate_app_uniquifier sha end @computed_uniquifier = sha.hexdigest end ## # @private Helper method to parse json file def read_app_json_file file_path JSON.parse File.read(file_path), symbolize_names: true rescue StandardError nil end end end end end