# encoding: utf-8 require "base64" module LogStash module Util class CloudSettingId def self.cloud_id_encode(*args) Base64.urlsafe_encode64(args.join("$")) end DOT_SEPARATOR = "." CLOUD_PORT = "443" attr_reader :original, :decoded, :label attr_reader :elasticsearch_host, :elasticsearch_scheme, :elasticsearch_port attr_reader :kibana_host, :kibana_scheme, :kibana_port attr_reader :other_identifiers # The constructor is expecting a 'cloud.id', a string in 2 variants. # 1 part example: 'dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRub3RhcmVhbCRpZGVudGlmaWVy' # 2 part example: 'foobar:dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRub3RhcmVhbCRpZGVudGlmaWVy' # The two part variant has a 'label' prepended with a colon separator. The label is not encoded. # The 1 part (or second section of the 2 part variant) is base64 encoded. # The original string before encoding has three segments separated by a dollar sign. # e.g. 'us-east-1.aws.found.io$notareal$identifier' # The first segment is the cloud base url, e.g. 'us-east-1.aws.found.io' # The second segment is the elasticsearch host identifier, e.g. 'notareal' # The third segment is the kibana host identifier, e.g. 'identifier' # The 'cloud.id' value decoded into the #attr_reader ivars. def initialize(value) return if value.nil? unless value.is_a?(String) raise ArgumentError.new("Cloud Id must be String. Received: #{value.class}") end @original = value @label, colon, encoded = @original.partition(":") if encoded.empty? @decoded = Base64.urlsafe_decode64(@label) rescue "" @label = "" else @decoded = Base64.urlsafe_decode64(encoded) rescue "" end @decoded = @decoded.encode(Encoding::UTF_8, :invalid => :replace, :undef => :replace) if @decoded.count("$") < 2 raise ArgumentError.new("Cloud Id, after decoding, is invalid. Format: '$$'. Received: \"#{@decoded}\".") end segments = @decoded.split("$") if segments.any?(&:empty?) raise ArgumentError.new("Cloud Id, after decoding, is invalid. Format: '$$'. Received: \"#{@decoded}\".") end cloud_base = segments.shift cloud_host = "#{DOT_SEPARATOR}#{cloud_base}" cloud_host, cloud_port = cloud_host.split(":") cloud_port ||= CLOUD_PORT @elasticsearch_host, @kibana_host, *@other_identifiers = segments @elasticsearch_host, @elasticsearch_port = @elasticsearch_host.split(":") @kibana_host, @kibana_port = @kibana_host.split(":") @elasticsearch_port ||= cloud_port @kibana_port ||= cloud_port @other_identifiers ||= [] if @elasticsearch_host == "undefined" raise ArgumentError.new("Cloud Id, after decoding, elasticsearch segment is 'undefined', literally.") end @elasticsearch_scheme = "https" @elasticsearch_host.concat(cloud_host) @elasticsearch_host.concat(":#{@elasticsearch_port}") if @kibana_host == "undefined" raise ArgumentError.new("Cloud Id, after decoding, the kibana segment is 'undefined', literally. You may need to enable Kibana in the Cloud UI.") end @kibana_scheme = "https" @kibana_host.concat(cloud_host) @kibana_host.concat(":#{@kibana_port}") end def to_s @decoded.to_s end def inspect to_s end end end end