mirror of
https://github.com/elastic/logstash.git
synced 2025-04-24 14:47:19 -04:00
Moved CloudSettingId class and tests to Java
(cherry picked from commit 2545fa45ba
)
This commit is contained in:
parent
cf7e2e308e
commit
b1e5c4f257
5 changed files with 392 additions and 308 deletions
|
@ -17,89 +17,6 @@
|
|||
|
||||
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: '<segment1>$<segment2>$<segment3>'. Received: \"#{@decoded}\".")
|
||||
end
|
||||
|
||||
segments = @decoded.split("$")
|
||||
if segments.any?(&:empty?)
|
||||
raise ArgumentError.new("Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. 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(":") if @kibana_host
|
||||
@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 ||= String.new # non-sense really to have '.my-host:443' but we're mirroring others
|
||||
@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
|
||||
module LogStash; module Util
|
||||
java_import org.logstash.util.CloudSettingId
|
||||
end; end
|
||||
|
|
|
@ -50,26 +50,26 @@ describe LogStash::Setting::Modules do
|
|||
subject { described_class.new("mycloudid", LogStash::Util::CloudSettingId) }
|
||||
context "when given a string which is not a cloud id" do
|
||||
it "should raise an exception" do
|
||||
expect { subject.set("foobarbaz") }.to raise_error(ArgumentError, /Cloud Id.*is invalid/)
|
||||
expect { subject.set("foobarbaz") }.to raise_error(IllegalArgumentException, /Cloud Id.*is invalid/)
|
||||
end
|
||||
end
|
||||
|
||||
context "when given a string which is empty" do
|
||||
it "should raise an exception" do
|
||||
expect { subject.set("") }.to raise_error(ArgumentError, /Cloud Id.*is invalid/)
|
||||
expect { subject.set("") }.to raise_error(IllegalArgumentException, /Cloud Id.*is invalid/)
|
||||
end
|
||||
end
|
||||
|
||||
context "when given a string which is has environment prefix only" do
|
||||
it "should raise an exception" do
|
||||
expect { subject.set("testing:") }.to raise_error(ArgumentError, /Cloud Id.*is invalid/)
|
||||
expect { subject.set("testing:") }.to raise_error(IllegalArgumentException, /Cloud Id.*is invalid/)
|
||||
end
|
||||
end
|
||||
|
||||
context "when given a badly formatted encoded id" do
|
||||
it "should not raise an error" do
|
||||
encoded = Base64.urlsafe_encode64("foo$$bal")
|
||||
expect { subject.set(encoded) }.to raise_error(ArgumentError, "Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"foo$$bal\".")
|
||||
expect { subject.set(encoded) }.to raise_error(IllegalArgumentException, "Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"foo$$bal\".")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
# Licensed to Elasticsearch B.V. under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright
|
||||
# ownership. Elasticsearch B.V. licenses this file to you 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
|
||||
#
|
||||
# http://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 "spec_helper"
|
||||
require "logstash/util/cloud_setting_id"
|
||||
|
||||
describe LogStash::Util::CloudSettingId do
|
||||
let(:input) { "foobar:dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRub3RhcmVhbCRpZGVudGlmaWVy" }
|
||||
subject { described_class.new(input) }
|
||||
|
||||
describe "when given unacceptable input" do
|
||||
it "a nil input does not raise an exception" do
|
||||
expect{described_class.new(nil)}.not_to raise_exception
|
||||
end
|
||||
it "when given a nil input, the accessors are all nil" do
|
||||
cloud_id = described_class.new(nil)
|
||||
expect(cloud_id.original).to be_nil
|
||||
expect(cloud_id.decoded).to be_nil
|
||||
expect(cloud_id.label).to be_nil
|
||||
expect(cloud_id.elasticsearch_host).to be_nil
|
||||
expect(cloud_id.kibana_host).to be_nil
|
||||
expect(cloud_id.elasticsearch_scheme).to be_nil
|
||||
expect(cloud_id.kibana_scheme).to be_nil
|
||||
end
|
||||
|
||||
context "when a malformed value is given" do
|
||||
let(:raw) {%w(first second)}
|
||||
let(:input) { described_class.cloud_id_encode(*raw) }
|
||||
it "raises an error" do
|
||||
expect{subject}.to raise_exception(ArgumentError, "Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"#{raw[0]}$#{raw[1]}\".")
|
||||
end
|
||||
end
|
||||
|
||||
context "when at least one segment is empty" do
|
||||
let(:raw) {["first", "", "third"]}
|
||||
let(:input) { described_class.cloud_id_encode(*raw) }
|
||||
it "raises an error" do
|
||||
expect{subject}.to raise_exception(ArgumentError, "Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"#{raw[0]}$#{raw[1]}$#{raw[2]}\".")
|
||||
end
|
||||
end
|
||||
|
||||
context "when elasticsearch segment is undefined" do
|
||||
let(:raw) {%w(us-east-1.aws.found.io undefined my-kibana)}
|
||||
let(:input) { described_class.cloud_id_encode(*raw) }
|
||||
it "raises an error" do
|
||||
expect{subject}.to raise_exception(ArgumentError, "Cloud Id, after decoding, elasticsearch segment is 'undefined', literally.")
|
||||
end
|
||||
end
|
||||
|
||||
context "when kibana segment is undefined" do
|
||||
let(:raw) {%w(us-east-1.aws.found.io my-elastic-cluster undefined)}
|
||||
let(:input) { described_class.cloud_id_encode(*raw) }
|
||||
it "raises an error" do
|
||||
expect{subject}.to raise_exception(ArgumentError, "Cloud Id, after decoding, the kibana segment is 'undefined', literally. You may need to enable Kibana in the Cloud UI.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "without a label" do
|
||||
let(:input) { "dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRub3RhcmVhbCRpZGVudGlmaWVy" }
|
||||
it "#label is empty" do
|
||||
expect(subject.label).to be_empty
|
||||
end
|
||||
it "#decode is set" do
|
||||
expect(subject.decoded).to eq("us-east-1.aws.found.io$notareal$identifier")
|
||||
end
|
||||
end
|
||||
|
||||
describe "when given acceptable input, the accessors:" do
|
||||
it '#original has a value' do
|
||||
expect(subject.original).to eq(input)
|
||||
end
|
||||
it '#decoded has a value' do
|
||||
expect(subject.decoded).to eq("us-east-1.aws.found.io$notareal$identifier")
|
||||
end
|
||||
it '#label has a value' do
|
||||
expect(subject.label).to eq("foobar")
|
||||
end
|
||||
it '#elasticsearch_host has a value' do
|
||||
expect(subject.elasticsearch_host).to eq("notareal.us-east-1.aws.found.io:443")
|
||||
end
|
||||
it '#elasticsearch_scheme has a value' do
|
||||
expect(subject.elasticsearch_scheme).to eq("https")
|
||||
end
|
||||
it '#kibana_host has a value' do
|
||||
expect(subject.kibana_host).to eq("identifier.us-east-1.aws.found.io:443")
|
||||
end
|
||||
it '#kibana_scheme has a value' do
|
||||
expect(subject.kibana_scheme).to eq("https")
|
||||
end
|
||||
it '#to_s has a value of #decoded' do
|
||||
expect(subject.to_s).to eq(subject.decoded)
|
||||
end
|
||||
end
|
||||
context "when cloud id contains port descriptions for ES and Kibana" do
|
||||
let(:input) { "different-es-kb-port:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2OjkyNDMkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA6OTI0NA==" }
|
||||
|
||||
it "decodes the elasticsearch port corretly" do
|
||||
expect(subject.elasticsearch_host).to eq("ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:9243")
|
||||
end
|
||||
it "decodes the kibana port corretly" do
|
||||
expect(subject.kibana_host).to eq("a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9244")
|
||||
end
|
||||
end
|
||||
context "when cloud id contains cloud port" do
|
||||
let(:input) { "custom-port:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvOjkyNDMkYWMzMWViYjkwMjQxNzczMTU3MDQzYzM0ZmQyNmZkNDYkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA=" }
|
||||
|
||||
it "decodes the elasticsearch port corretly" do
|
||||
expect(subject.elasticsearch_host).to eq("ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:9243")
|
||||
end
|
||||
it "decodes the kibana port corretly" do
|
||||
expect(subject.kibana_host).to eq("a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9243")
|
||||
end
|
||||
end
|
||||
context "when cloud id only defines kibana port" do
|
||||
let(:input) { "only-kb-set:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2JGE0YzA2MjMwZTQ4YzhmY2U3YmU4OGEwNzRhM2JiM2UwOjkyNDQ=" }
|
||||
|
||||
it "defaults the elasticsearch port to 443" do
|
||||
expect(subject.elasticsearch_host).to eq("ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:443")
|
||||
end
|
||||
it "decodes the kibana port corretly" do
|
||||
expect(subject.kibana_host).to eq("a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9244")
|
||||
end
|
||||
end
|
||||
context "when cloud id defines cloud port and kibana port" do
|
||||
let(:input) { "host-and-kb-set:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvOjkyNDMkYWMzMWViYjkwMjQxNzczMTU3MDQzYzM0ZmQyNmZkNDYkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA6OTI0NA==" }
|
||||
|
||||
it "sets the elasticsearch port to cloud port" do
|
||||
expect(subject.elasticsearch_host).to eq("ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:9243")
|
||||
end
|
||||
it "overrides cloud port with the kibana port" do
|
||||
expect(subject.kibana_host).to eq("a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9244")
|
||||
end
|
||||
end
|
||||
context "when cloud id defines extra data" do
|
||||
let(:input) { "extra-items:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2JGE0YzA2MjMwZTQ4YzhmY2U3YmU4OGEwNzRhM2JiM2UwJGFub3RoZXJpZCRhbmRhbm90aGVy" }
|
||||
|
||||
it "captures the elasticsearch host" do
|
||||
expect(subject.elasticsearch_host).to eq("ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:443")
|
||||
end
|
||||
it "captures the kibana host" do
|
||||
expect(subject.kibana_host).to eq("a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:443")
|
||||
end
|
||||
it "captures the remaining identifiers" do
|
||||
expect(subject.other_identifiers).to eq(["anotherid", "andanother"])
|
||||
end
|
||||
end
|
||||
|
||||
describe "when given acceptable input (with empty kibana uuid), the accessors:" do
|
||||
let(:input) { "a-test:ZWNlLmhvbWUubGFuJHRlc3Qk" } # ece.home.lan$test$
|
||||
|
||||
it '#original has a value' do
|
||||
expect(subject.original).to eq(input)
|
||||
end
|
||||
it '#decoded has a value' do
|
||||
expect(subject.decoded).to eq("ece.home.lan$test$")
|
||||
end
|
||||
it '#label has a value' do
|
||||
expect(subject.label).to eq("a-test")
|
||||
end
|
||||
it '#elasticsearch_host has a value' do
|
||||
expect(subject.elasticsearch_host).to eq("test.ece.home.lan:443")
|
||||
end
|
||||
it '#elasticsearch_scheme has a value' do
|
||||
expect(subject.elasticsearch_scheme).to eq("https")
|
||||
end
|
||||
it '#kibana_host has a value' do
|
||||
# NOTE: kibana part is not relevant -> this is how python/beats(go) code behaves
|
||||
expect(subject.kibana_host).to eq(".ece.home.lan:443")
|
||||
end
|
||||
it '#kibana_scheme has a value' do
|
||||
expect(subject.kibana_scheme).to eq("https")
|
||||
end
|
||||
it '#to_s has a value of #decoded' do
|
||||
expect(subject.to_s).to eq(subject.decoded)
|
||||
end
|
||||
end
|
||||
|
||||
describe "a lengthy real-world input, the accessors:" do
|
||||
let(:input) do
|
||||
"ZWFzdHVzMi5henVyZS5lbGFzdGljLWNsb3VkLmNvbTo5MjQzJDQwYjM0MzExNmNmYTRlYmNiNzZjMTFlZTIzMjlmOTJkJDQzZDA5MjUyNTAyYzQxODlhMzc2ZmQwY2YyY2QwODQ4"
|
||||
# eastus2.azure.elastic-cloud.com:9243$40b343116cfa4ebcb76c11ee2329f92d$43d09252502c4189a376fd0cf2cd0848
|
||||
end
|
||||
|
||||
it '#original has a value' do
|
||||
expect(subject.original).to eq(input)
|
||||
end
|
||||
it '#decoded has a value' do
|
||||
expect(subject.decoded).to eq("eastus2.azure.elastic-cloud.com:9243$40b343116cfa4ebcb76c11ee2329f92d$43d09252502c4189a376fd0cf2cd0848")
|
||||
end
|
||||
it '#label has a value' do
|
||||
expect(subject.label).to eq("")
|
||||
end
|
||||
it '#elasticsearch_host has a value' do
|
||||
expect(subject.elasticsearch_host).to eq("40b343116cfa4ebcb76c11ee2329f92d.eastus2.azure.elastic-cloud.com:9243")
|
||||
end
|
||||
it '#kibana_host has a value' do
|
||||
expect(subject.kibana_host).to eq("43d09252502c4189a376fd0cf2cd0848.eastus2.azure.elastic-cloud.com:9243")
|
||||
end
|
||||
it '#to_s has a value of #decoded' do
|
||||
expect(subject.to_s).to eq(subject.decoded)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package org.logstash.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
||||
/*
|
||||
* 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 various fields.
|
||||
*/
|
||||
public class CloudSettingId {
|
||||
|
||||
private static class HostAndPort {
|
||||
static final HostAndPort NO_HOST = new HostAndPort("", null);
|
||||
private final String host;
|
||||
private final String port;
|
||||
|
||||
private HostAndPort(String host, String port) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
String portOrDefault(String defaultPort) {
|
||||
return port == null ? defaultPort : port;
|
||||
}
|
||||
|
||||
static HostAndPort parseHostAndPort(String part, String guidanceMessageWhenHostEqualsUndefined) {
|
||||
final String[] hostParts = part.split(":");
|
||||
String host = hostParts[0];
|
||||
if ("undefined".equals(host)) {
|
||||
throw new IllegalArgumentException(guidanceMessageWhenHostEqualsUndefined);
|
||||
}
|
||||
String port = null;
|
||||
if (hostParts.length > 1) {
|
||||
port = hostParts[1];
|
||||
}
|
||||
return new HostAndPort(host, port);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String DOT_SEPARATOR = ".";
|
||||
public static final String CLOUD_PORT = "443";
|
||||
|
||||
private String original;
|
||||
private String decoded;
|
||||
private String label;
|
||||
private String elasticsearchScheme;
|
||||
private String elasticsearchHost;
|
||||
private String elasticsearchPort;
|
||||
private String kibanaScheme;
|
||||
private String kibanaHost;
|
||||
private String kibanaPort;
|
||||
private String[] otherIdentifiers = new String[0];
|
||||
|
||||
public CloudSettingId(String value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
original = value;
|
||||
final String[] parts = original.split(":");
|
||||
label = parts[0];
|
||||
String encoded = null;
|
||||
if (parts.length > 1) {
|
||||
encoded = parts[1];
|
||||
}
|
||||
if (encoded == null || encoded.isEmpty()) {
|
||||
try {
|
||||
decoded = new String(Base64.getUrlDecoder().decode(label), StandardCharsets.UTF_8);
|
||||
} catch (IllegalArgumentException iaex) {
|
||||
decoded = "";
|
||||
}
|
||||
label = "";
|
||||
} else {
|
||||
try {
|
||||
decoded = new String(Base64.getUrlDecoder().decode(encoded), StandardCharsets.UTF_8);
|
||||
} catch (IllegalArgumentException iaex) {
|
||||
decoded = "";
|
||||
}
|
||||
}
|
||||
long separatorCount = decoded.chars().filter(c -> c == '$').count();
|
||||
if (separatorCount < 2) {
|
||||
throw new IllegalArgumentException("Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"" + decoded + "\".");
|
||||
}
|
||||
final String[] segments = decoded.split("\\$");
|
||||
if (Arrays.stream(segments).anyMatch(String::isEmpty)) {
|
||||
throw new IllegalArgumentException("Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"" + decoded + "\".");
|
||||
}
|
||||
String cloudBase = segments[0];
|
||||
String cloudHost = DOT_SEPARATOR + cloudBase;
|
||||
final String[] hostParts = cloudHost.split(":");
|
||||
final HostAndPort cloud = new HostAndPort(hostParts[0], hostParts.length > 1 ? hostParts[1] : CLOUD_PORT);
|
||||
|
||||
final HostAndPort elasticsearch = HostAndPort.parseHostAndPort(segments[1], "Cloud Id, after decoding, elasticsearch segment is 'undefined', literally.");
|
||||
elasticsearchPort = elasticsearch.portOrDefault(cloud.port);
|
||||
elasticsearchHost = elasticsearch.host + cloud.host + ":" + elasticsearchPort;
|
||||
elasticsearchScheme = "https";
|
||||
|
||||
final HostAndPort kibana;
|
||||
if (segments.length > 2) {
|
||||
kibana = HostAndPort.parseHostAndPort(segments[2], "Cloud Id, after decoding, the kibana segment is 'undefined', literally. You may need to enable Kibana in the Cloud UI.");
|
||||
} else {
|
||||
// non-sense really to have '.my-host:443' but we're mirroring others
|
||||
kibana = HostAndPort.NO_HOST;
|
||||
}
|
||||
kibanaPort = kibana.portOrDefault(cloud.port);
|
||||
kibanaHost = kibana.host + cloud.host + ":" + kibanaPort;
|
||||
kibanaScheme = "https";
|
||||
|
||||
if (segments.length > 3) {
|
||||
otherIdentifiers = Arrays.copyOfRange(segments, 3, segments.length);
|
||||
}
|
||||
}
|
||||
|
||||
public String getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
public String getDecoded() {
|
||||
return decoded;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getElasticsearchScheme() {
|
||||
return elasticsearchScheme;
|
||||
}
|
||||
|
||||
public String getElasticsearchHost() {
|
||||
return elasticsearchHost;
|
||||
}
|
||||
|
||||
public String getElasticsearchPort() {
|
||||
return elasticsearchPort;
|
||||
}
|
||||
|
||||
public String getKibanaScheme() {
|
||||
return kibanaScheme;
|
||||
}
|
||||
|
||||
public String getKibanaHost() {
|
||||
return kibanaHost;
|
||||
}
|
||||
|
||||
public String getKibanaPort() {
|
||||
return kibanaPort;
|
||||
}
|
||||
|
||||
public String[] getOtherIdentifiers() {
|
||||
return otherIdentifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return decoded;
|
||||
}
|
||||
|
||||
public static String cloudIdEncode(String... args) {
|
||||
final String joinedArgs = String.join("$", args);
|
||||
return Base64.getUrlEncoder().encodeToString(joinedArgs.getBytes());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package org.logstash.util;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class CloudSettingIdTest {
|
||||
|
||||
private String input = "foobar:dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRub3RhcmVhbCRpZGVudGlmaWVy";
|
||||
private CloudSettingId sut;
|
||||
|
||||
@Rule
|
||||
public ExpectedException exceptionRule = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
sut = new CloudSettingId(input);
|
||||
}
|
||||
|
||||
// when given unacceptable input
|
||||
@Test
|
||||
public void testNullInputDoenstThrowAnException() {
|
||||
new CloudSettingId(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullInputMakesAllGettersReturnNull() {
|
||||
sut = new CloudSettingId(null);
|
||||
assertNull(sut.getOriginal());
|
||||
assertNull(sut.getDecoded());
|
||||
assertNull(sut.getLabel());
|
||||
assertNull(sut.getElasticsearchHost());
|
||||
assertNull(sut.getKibanaHost());
|
||||
assertNull(sut.getElasticsearchScheme());
|
||||
assertNull(sut.getKibanaScheme());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowExceptionWhenMalformedValueIsGiven() {
|
||||
String[] raw = new String[] {"first", "second"};
|
||||
String encoded = CloudSettingId.cloudIdEncode(raw);
|
||||
exceptionRule.expect(IllegalArgumentException.class);
|
||||
exceptionRule.expectMessage("Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"" + String.join("$", raw) + "\".");
|
||||
|
||||
new CloudSettingId(encoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowExceptionWhenAtLeatOneSegmentIsEmpty() {
|
||||
String[] raw = new String[] {"first", "", "third"};
|
||||
String encoded = CloudSettingId.cloudIdEncode(raw);
|
||||
exceptionRule.expect(IllegalArgumentException.class);
|
||||
exceptionRule.expectMessage("Cloud Id, after decoding, is invalid. Format: '<segment1>$<segment2>$<segment3>'. Received: \"" + String.join("$", raw) + "\".");
|
||||
|
||||
new CloudSettingId(encoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowExceptionWhenElasticSegmentSegmentIsUndefined() {
|
||||
String[] raw = new String[] {"us-east-1.aws.found.io", "undefined", "my-kibana"};
|
||||
String encoded = CloudSettingId.cloudIdEncode(raw);
|
||||
exceptionRule.expect(IllegalArgumentException.class);
|
||||
exceptionRule.expectMessage("Cloud Id, after decoding, elasticsearch segment is 'undefined', literally.");
|
||||
|
||||
new CloudSettingId(encoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowExceptionWhenKibanaSegmentSegmentIsUndefined() {
|
||||
String[] raw = new String[] {"us-east-1.aws.found.io", "my-elastic-cluster", "undefined"};
|
||||
String encoded = CloudSettingId.cloudIdEncode(raw);
|
||||
exceptionRule.expect(IllegalArgumentException.class);
|
||||
exceptionRule.expectMessage("Cloud Id, after decoding, the kibana segment is 'undefined', literally. You may need to enable Kibana in the Cloud UI.");
|
||||
|
||||
new CloudSettingId(encoded);
|
||||
}
|
||||
|
||||
// without a label
|
||||
@Test
|
||||
public void testDecodingWithoutLabelSegment() {
|
||||
sut = new CloudSettingId("dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRub3RhcmVhbCRpZGVudGlmaWVy");
|
||||
|
||||
assertEquals("#label is empty", "", sut.getLabel());
|
||||
assertEquals("#decode is set", "us-east-1.aws.found.io$notareal$identifier", sut.getDecoded());
|
||||
}
|
||||
|
||||
// when given acceptable input, the accessors:
|
||||
@Test
|
||||
public void testAccessorsWithAcceptableInput() {
|
||||
assertEquals("#original has a value", input, sut.getOriginal());
|
||||
assertEquals("#decoded has a value", "us-east-1.aws.found.io$notareal$identifier", sut.getDecoded());
|
||||
assertEquals("#label has a value", "foobar", sut.getLabel());
|
||||
assertEquals("#elasticsearch_host has a value", "notareal.us-east-1.aws.found.io:443", sut.getElasticsearchHost());
|
||||
assertEquals("#elasticsearch_scheme has a value", "https", sut.getElasticsearchScheme());
|
||||
assertEquals("#kibana_host has a value", "identifier.us-east-1.aws.found.io:443", sut.getKibanaHost());
|
||||
assertEquals("#kibana_scheme has a value", "https", sut.getKibanaScheme());
|
||||
assertEquals("#to_s has a value of #decoded", sut.toString(), sut.getDecoded());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenCloudIdContainsPortDescriptionForESAndKibana() {
|
||||
sut = new CloudSettingId("different-es-kb-port:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2OjkyNDMkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA6OTI0NA==");
|
||||
|
||||
assertEquals("decodes the elasticsearch port corretly", "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:9243", sut.getElasticsearchHost());
|
||||
assertEquals("decodes the kibana port corretly", "a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9244", sut.getKibanaHost());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenCloudIdContainsCloudPort() {
|
||||
sut = new CloudSettingId("custom-port:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvOjkyNDMkYWMzMWViYjkwMjQxNzczMTU3MDQzYzM0ZmQyNmZkNDYkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA=");
|
||||
|
||||
assertEquals("decodes the elasticsearch port corretly", "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:9243", sut.getElasticsearchHost());
|
||||
assertEquals("decodes the kibana port corretly", "a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9243", sut.getKibanaHost());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenCloudIdOnlyDefinesKibanaPort() {
|
||||
sut = new CloudSettingId("only-kb-set:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2JGE0YzA2MjMwZTQ4YzhmY2U3YmU4OGEwNzRhM2JiM2UwOjkyNDQ=");
|
||||
|
||||
assertEquals("defaults the elasticsearch port to 443", "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:443", sut.getElasticsearchHost());
|
||||
assertEquals("decodes the kibana port corretly", "a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9244", sut.getKibanaHost());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenCloudIdDefinesCloudPortAndKibanaPort() {
|
||||
sut = new CloudSettingId("host-and-kb-set:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvOjkyNDMkYWMzMWViYjkwMjQxNzczMTU3MDQzYzM0ZmQyNmZkNDYkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA6OTI0NA==");
|
||||
|
||||
assertEquals("sets the elasticsearch port to cloud port", "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:9243", sut.getElasticsearchHost());
|
||||
assertEquals("overrides cloud port with the kibana port", "a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:9244", sut.getKibanaHost());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenCloudIdDefinesExtraData() {
|
||||
sut = new CloudSettingId("extra-items:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2JGE0YzA2MjMwZTQ4YzhmY2U3YmU4OGEwNzRhM2JiM2UwJGFub3RoZXJpZCRhbmRhbm90aGVy");
|
||||
|
||||
assertEquals("captures the elasticsearch host", "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io:443", sut.getElasticsearchHost());
|
||||
assertEquals("captures the kibana host", "a4c06230e48c8fce7be88a074a3bb3e0.us-central1.gcp.cloud.es.io:443", sut.getKibanaHost());
|
||||
assertArrayEquals("captures the remaining identifiers", new String[] {"anotherid", "andanother"}, sut.getOtherIdentifiers());
|
||||
}
|
||||
|
||||
// when given acceptable input (with empty kibana uuid), the accessors:
|
||||
@Test
|
||||
public void testGivenAcceptableInputEmptyKibanaUUID() {
|
||||
input = "a-test:ZWNlLmhvbWUubGFuJHRlc3Qk";
|
||||
sut = new CloudSettingId(input); // ece.home.lan$test$
|
||||
|
||||
assertEquals("#original has a value", input, sut.getOriginal());
|
||||
assertEquals("#decoded has a value", "ece.home.lan$test$", sut.getDecoded());
|
||||
assertEquals("#label has a value", "a-test", sut.getLabel());
|
||||
assertEquals("#elasticsearch_host has a value", "test.ece.home.lan:443", sut.getElasticsearchHost());
|
||||
assertEquals("#elasticsearch_scheme has a value", "https", sut.getElasticsearchScheme());
|
||||
// NOTE: kibana part is not relevant -> this is how python/beats(go) code behaves
|
||||
assertEquals("#kibana_host has a value", ".ece.home.lan:443", sut.getKibanaHost());
|
||||
assertEquals("#kibana_scheme has a value", "https", sut.getKibanaScheme());
|
||||
assertEquals("#toString has a value of #decoded", sut.getDecoded(), sut.toString());
|
||||
}
|
||||
|
||||
// a lengthy real-world input, the accessors:
|
||||
@Test
|
||||
public void testWithRealWorldInput() {
|
||||
//eastus2.azure.elastic-cloud.com:9243$40b343116cfa4ebcb76c11ee2329f92d$43d09252502c4189a376fd0cf2cd0848
|
||||
input = "ZWFzdHVzMi5henVyZS5lbGFzdGljLWNsb3VkLmNvbTo5MjQzJDQwYjM0MzExNmNmYTRlYmNiNzZjMTFlZTIzMjlmOTJkJDQzZDA5MjUyNTAyYzQxODlhMzc2ZmQwY2YyY2QwODQ4";
|
||||
sut = new CloudSettingId(input);
|
||||
|
||||
assertEquals("#original has a value", input, sut.getOriginal());
|
||||
assertEquals("#decoded has a value", "eastus2.azure.elastic-cloud.com:9243$40b343116cfa4ebcb76c11ee2329f92d$43d09252502c4189a376fd0cf2cd0848", sut.getDecoded());
|
||||
assertEquals("#label has a value", "", sut.getLabel());
|
||||
assertEquals("#elasticsearch_host has a value", "40b343116cfa4ebcb76c11ee2329f92d.eastus2.azure.elastic-cloud.com:9243", sut.getElasticsearchHost());
|
||||
assertEquals("#kibana_host has a value", "43d09252502c4189a376fd0cf2cd0848.eastus2.azure.elastic-cloud.com:9243", sut.getKibanaHost());
|
||||
assertEquals("#toString has a value of #decoded", sut.getDecoded(), sut.toString());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue