diff --git a/akka-cluster-sharding/src/main/resources/reference.conf b/akka-cluster-sharding/src/main/resources/reference.conf index 26f9b7d857..4dba0a92e3 100644 --- a/akka-cluster-sharding/src/main/resources/reference.conf +++ b/akka-cluster-sharding/src/main/resources/reference.conf @@ -151,6 +151,13 @@ akka.cluster.sharding { } # //#sharding-ext-config +akka.cluster { + configuration-compatibility-check { + checkers { + akka-cluster-sharding = "akka.cluster.sharding.JoinConfigCompatCheckSharding" + } + } +} # Protobuf serializer for Cluster Sharding messages akka.actor { diff --git a/akka-cluster-sharding/src/main/scala/akka/cluster/sharding/JoinConfigCompatCheckSharding.scala b/akka-cluster-sharding/src/main/scala/akka/cluster/sharding/JoinConfigCompatCheckSharding.scala new file mode 100644 index 0000000000..4934f109ef --- /dev/null +++ b/akka-cluster-sharding/src/main/scala/akka/cluster/sharding/JoinConfigCompatCheckSharding.scala @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2009-2018 Lightbend Inc. + */ +package akka.cluster.sharding + +import akka.annotation.InternalApi +import akka.cluster.{ ConfigValidation, JoinConfigCompatChecker } +import com.typesafe.config.Config +import scala.collection.{ immutable ⇒ im } + +/** + * INTERNAL API + */ +@InternalApi +final class JoinConfigCompatCheckSharding extends JoinConfigCompatChecker { + + override def requiredKeys = im.Seq("akka.cluster.sharding.state-store-mode") + + override def check(toCheck: Config, actualConfig: Config): ConfigValidation = + JoinConfigCompatChecker.fullMatch(requiredKeys, toCheck, actualConfig) +} diff --git a/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/JoinConfigCompatCheckShardingSpec.scala b/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/JoinConfigCompatCheckShardingSpec.scala new file mode 100644 index 0000000000..e4696d0cba --- /dev/null +++ b/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/JoinConfigCompatCheckShardingSpec.scala @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2009-2018 Lightbend Inc. + */ +package akka.cluster.sharding + +import akka.actor.ActorSystem +import akka.cluster.{ Cluster, ClusterReadView } +import akka.testkit.{ AkkaSpec, LongRunningTest } +import com.typesafe.config.{ Config, ConfigFactory } +import scala.concurrent.duration._ +import scala.collection.{ immutable ⇒ im } + +class JoinConfigCompatCheckShardingSpec extends AkkaSpec() { + + def initCluster(system: ActorSystem): ClusterReadView = { + val cluster = Cluster(system) + cluster.join(cluster.selfAddress) + val clusterView = cluster.readView + awaitCond(clusterView.isSingletonCluster) + clusterView + } + + val baseConfig: Config = + ConfigFactory.parseString( + """ + akka.actor.provider = "cluster" + akka.coordinated-shutdown.terminate-actor-system = on + akka.remote.netty.tcp.port = 0 + akka.remote.artery.canonical.port = 0 + """ + ) + + "A Joining Node" must { + + /** This test verifies the built-in JoinConfigCompatCheckerSharding */ + "NOT be allowed to join a cluster using a different value for akka.cluster.sharding.state-store-mode" taggedAs LongRunningTest in { + + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # use 'persistence' for state store + sharding.state-store-mode = "persistence" + + configuration-compatibility-check { + enforce-on-join = on + } + } + """ + ) + + val seedNode = ActorSystem(system.name, baseConfig) + val joiningNode = ActorSystem(system.name, joinNodeConfig.withFallback(baseConfig)) + + val clusterView = initCluster(seedNode) + val joiningNodeCluster = Cluster(joiningNode) + + try { + // join with compatible node + joiningNodeCluster.joinSeedNodes(im.Seq(clusterView.selfAddress)) + + // node will shutdown after unsuccessful join attempt + within(5.seconds) { + awaitCond(joiningNodeCluster.readView.isTerminated) + } + + } finally { + shutdown(seedNode) + shutdown(joiningNode) + } + + } + } + +} diff --git a/akka-cluster/src/main/java/akka/cluster/protobuf/msg/ClusterMessages.java b/akka-cluster/src/main/java/akka/cluster/protobuf/msg/ClusterMessages.java index e230001a87..008195c557 100644 --- a/akka-cluster/src/main/java/akka/cluster/protobuf/msg/ClusterMessages.java +++ b/akka-cluster/src/main/java/akka/cluster/protobuf/msg/ClusterMessages.java @@ -7,6 +7,9 @@ public final class ClusterMessages { private ClusterMessages() {} public static void registerAllExtensions( akka.protobuf.ExtensionRegistry registry) { + registry.add(akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.checkConfig); + registry.add(akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.checkConfig); + registry.add(akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.checkConfig); } /** * Protobuf enum {@code ReachabilityStatus} @@ -1695,6 +1698,3035 @@ public final class ClusterMessages { // @@protoc_insertion_point(class_scope:Welcome) } + public interface InitJoinOrBuilder + extends akka.protobuf.MessageOrBuilder { + + // optional string currentConfig = 1; + /** + * optional string currentConfig = 1; + */ + boolean hasCurrentConfig(); + /** + * optional string currentConfig = 1; + */ + java.lang.String getCurrentConfig(); + /** + * optional string currentConfig = 1; + */ + akka.protobuf.ByteString + getCurrentConfigBytes(); + } + /** + * Protobuf type {@code InitJoin} + * + *
+   **
+   * InitJoin
+   * 
+ */ + public static final class InitJoin extends + akka.protobuf.GeneratedMessage + implements InitJoinOrBuilder { + // Use InitJoin.newBuilder() to construct. + private InitJoin(akka.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private InitJoin(boolean noInit) { this.unknownFields = akka.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final InitJoin defaultInstance; + public static InitJoin getDefaultInstance() { + return defaultInstance; + } + + public InitJoin getDefaultInstanceForType() { + return defaultInstance; + } + + private final akka.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final akka.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private InitJoin( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + akka.protobuf.UnknownFieldSet.Builder unknownFields = + akka.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + currentConfig_ = input.readBytes(); + break; + } + } + } + } catch (akka.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new akka.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoin_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoin_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.InitJoin.class, akka.cluster.protobuf.msg.ClusterMessages.InitJoin.Builder.class); + } + + public static akka.protobuf.Parser PARSER = + new akka.protobuf.AbstractParser() { + public InitJoin parsePartialFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return new InitJoin(input, extensionRegistry); + } + }; + + @java.lang.Override + public akka.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string currentConfig = 1; + public static final int CURRENTCONFIG_FIELD_NUMBER = 1; + private java.lang.Object currentConfig_; + /** + * optional string currentConfig = 1; + */ + public boolean hasCurrentConfig() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string currentConfig = 1; + */ + public java.lang.String getCurrentConfig() { + java.lang.Object ref = currentConfig_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + akka.protobuf.ByteString bs = + (akka.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + currentConfig_ = s; + } + return s; + } + } + /** + * optional string currentConfig = 1; + */ + public akka.protobuf.ByteString + getCurrentConfigBytes() { + java.lang.Object ref = currentConfig_; + if (ref instanceof java.lang.String) { + akka.protobuf.ByteString b = + akka.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + currentConfig_ = b; + return b; + } else { + return (akka.protobuf.ByteString) ref; + } + } + + private void initFields() { + currentConfig_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(akka.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getCurrentConfigBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += akka.protobuf.CodedOutputStream + .computeBytesSize(1, getCurrentConfigBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom( + akka.protobuf.ByteString data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom( + akka.protobuf.ByteString data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom(byte[] data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom( + byte[] data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseDelimitedFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom( + akka.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoin parseFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(akka.cluster.protobuf.msg.ClusterMessages.InitJoin prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code InitJoin} + * + *
+     **
+     * InitJoin
+     * 
+ */ + public static final class Builder extends + akka.protobuf.GeneratedMessage.Builder + implements akka.cluster.protobuf.msg.ClusterMessages.InitJoinOrBuilder { + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoin_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoin_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.InitJoin.class, akka.cluster.protobuf.msg.ClusterMessages.InitJoin.Builder.class); + } + + // Construct using akka.cluster.protobuf.msg.ClusterMessages.InitJoin.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (akka.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + currentConfig_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public akka.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoin_descriptor; + } + + public akka.cluster.protobuf.msg.ClusterMessages.InitJoin getDefaultInstanceForType() { + return akka.cluster.protobuf.msg.ClusterMessages.InitJoin.getDefaultInstance(); + } + + public akka.cluster.protobuf.msg.ClusterMessages.InitJoin build() { + akka.cluster.protobuf.msg.ClusterMessages.InitJoin result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public akka.cluster.protobuf.msg.ClusterMessages.InitJoin buildPartial() { + akka.cluster.protobuf.msg.ClusterMessages.InitJoin result = new akka.cluster.protobuf.msg.ClusterMessages.InitJoin(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.currentConfig_ = currentConfig_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(akka.protobuf.Message other) { + if (other instanceof akka.cluster.protobuf.msg.ClusterMessages.InitJoin) { + return mergeFrom((akka.cluster.protobuf.msg.ClusterMessages.InitJoin)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(akka.cluster.protobuf.msg.ClusterMessages.InitJoin other) { + if (other == akka.cluster.protobuf.msg.ClusterMessages.InitJoin.getDefaultInstance()) return this; + if (other.hasCurrentConfig()) { + bitField0_ |= 0x00000001; + currentConfig_ = other.currentConfig_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + akka.cluster.protobuf.msg.ClusterMessages.InitJoin parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (akka.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (akka.cluster.protobuf.msg.ClusterMessages.InitJoin) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string currentConfig = 1; + private java.lang.Object currentConfig_ = ""; + /** + * optional string currentConfig = 1; + */ + public boolean hasCurrentConfig() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string currentConfig = 1; + */ + public java.lang.String getCurrentConfig() { + java.lang.Object ref = currentConfig_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((akka.protobuf.ByteString) ref) + .toStringUtf8(); + currentConfig_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string currentConfig = 1; + */ + public akka.protobuf.ByteString + getCurrentConfigBytes() { + java.lang.Object ref = currentConfig_; + if (ref instanceof String) { + akka.protobuf.ByteString b = + akka.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + currentConfig_ = b; + return b; + } else { + return (akka.protobuf.ByteString) ref; + } + } + /** + * optional string currentConfig = 1; + */ + public Builder setCurrentConfig( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + currentConfig_ = value; + onChanged(); + return this; + } + /** + * optional string currentConfig = 1; + */ + public Builder clearCurrentConfig() { + bitField0_ = (bitField0_ & ~0x00000001); + currentConfig_ = getDefaultInstance().getCurrentConfig(); + onChanged(); + return this; + } + /** + * optional string currentConfig = 1; + */ + public Builder setCurrentConfigBytes( + akka.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + currentConfig_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:InitJoin) + } + + static { + defaultInstance = new InitJoin(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:InitJoin) + } + + public interface InitJoinAckOrBuilder + extends akka.protobuf.MessageOrBuilder { + + // required .Address address = 1; + /** + * required .Address address = 1; + */ + boolean hasAddress(); + /** + * required .Address address = 1; + */ + akka.cluster.protobuf.msg.ClusterMessages.Address getAddress(); + /** + * required .Address address = 1; + */ + akka.cluster.protobuf.msg.ClusterMessages.AddressOrBuilder getAddressOrBuilder(); + + // optional .ConfigCheck configCheck = 2; + /** + * optional .ConfigCheck configCheck = 2; + */ + boolean hasConfigCheck(); + /** + * optional .ConfigCheck configCheck = 2; + */ + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck getConfigCheck(); + /** + * optional .ConfigCheck configCheck = 2; + */ + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheckOrBuilder getConfigCheckOrBuilder(); + } + /** + * Protobuf type {@code InitJoinAck} + * + *
+   **
+   * InitJoinAck
+   * 
+ */ + public static final class InitJoinAck extends + akka.protobuf.GeneratedMessage + implements InitJoinAckOrBuilder { + // Use InitJoinAck.newBuilder() to construct. + private InitJoinAck(akka.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private InitJoinAck(boolean noInit) { this.unknownFields = akka.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final InitJoinAck defaultInstance; + public static InitJoinAck getDefaultInstance() { + return defaultInstance; + } + + public InitJoinAck getDefaultInstanceForType() { + return defaultInstance; + } + + private final akka.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final akka.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private InitJoinAck( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + akka.protobuf.UnknownFieldSet.Builder unknownFields = + akka.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + akka.cluster.protobuf.msg.ClusterMessages.Address.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = address_.toBuilder(); + } + address_ = input.readMessage(akka.cluster.protobuf.msg.ClusterMessages.Address.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(address_); + address_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 18: { + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = configCheck_.toBuilder(); + } + configCheck_ = input.readMessage(akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(configCheck_); + configCheck_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + } + } + } catch (akka.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new akka.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoinAck_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoinAck_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck.class, akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck.Builder.class); + } + + public static akka.protobuf.Parser PARSER = + new akka.protobuf.AbstractParser() { + public InitJoinAck parsePartialFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return new InitJoinAck(input, extensionRegistry); + } + }; + + @java.lang.Override + public akka.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // required .Address address = 1; + public static final int ADDRESS_FIELD_NUMBER = 1; + private akka.cluster.protobuf.msg.ClusterMessages.Address address_; + /** + * required .Address address = 1; + */ + public boolean hasAddress() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required .Address address = 1; + */ + public akka.cluster.protobuf.msg.ClusterMessages.Address getAddress() { + return address_; + } + /** + * required .Address address = 1; + */ + public akka.cluster.protobuf.msg.ClusterMessages.AddressOrBuilder getAddressOrBuilder() { + return address_; + } + + // optional .ConfigCheck configCheck = 2; + public static final int CONFIGCHECK_FIELD_NUMBER = 2; + private akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck configCheck_; + /** + * optional .ConfigCheck configCheck = 2; + */ + public boolean hasConfigCheck() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck getConfigCheck() { + return configCheck_; + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheckOrBuilder getConfigCheckOrBuilder() { + return configCheck_; + } + + private void initFields() { + address_ = akka.cluster.protobuf.msg.ClusterMessages.Address.getDefaultInstance(); + configCheck_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + if (!hasAddress()) { + memoizedIsInitialized = 0; + return false; + } + if (!getAddress().isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + if (hasConfigCheck()) { + if (!getConfigCheck().isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(akka.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, address_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, configCheck_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += akka.protobuf.CodedOutputStream + .computeMessageSize(1, address_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += akka.protobuf.CodedOutputStream + .computeMessageSize(2, configCheck_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom( + akka.protobuf.ByteString data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom( + akka.protobuf.ByteString data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom(byte[] data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom( + byte[] data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseDelimitedFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom( + akka.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parseFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code InitJoinAck} + * + *
+     **
+     * InitJoinAck
+     * 
+ */ + public static final class Builder extends + akka.protobuf.GeneratedMessage.Builder + implements akka.cluster.protobuf.msg.ClusterMessages.InitJoinAckOrBuilder { + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoinAck_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoinAck_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck.class, akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck.Builder.class); + } + + // Construct using akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (akka.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAddressFieldBuilder(); + getConfigCheckFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (addressBuilder_ == null) { + address_ = akka.cluster.protobuf.msg.ClusterMessages.Address.getDefaultInstance(); + } else { + addressBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + if (configCheckBuilder_ == null) { + configCheck_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDefaultInstance(); + } else { + configCheckBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public akka.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_InitJoinAck_descriptor; + } + + public akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck getDefaultInstanceForType() { + return akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck.getDefaultInstance(); + } + + public akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck build() { + akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck buildPartial() { + akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck result = new akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (addressBuilder_ == null) { + result.address_ = address_; + } else { + result.address_ = addressBuilder_.build(); + } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (configCheckBuilder_ == null) { + result.configCheck_ = configCheck_; + } else { + result.configCheck_ = configCheckBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(akka.protobuf.Message other) { + if (other instanceof akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck) { + return mergeFrom((akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck other) { + if (other == akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck.getDefaultInstance()) return this; + if (other.hasAddress()) { + mergeAddress(other.getAddress()); + } + if (other.hasConfigCheck()) { + mergeConfigCheck(other.getConfigCheck()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + if (!hasAddress()) { + + return false; + } + if (!getAddress().isInitialized()) { + + return false; + } + if (hasConfigCheck()) { + if (!getConfigCheck().isInitialized()) { + + return false; + } + } + return true; + } + + public Builder mergeFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (akka.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (akka.cluster.protobuf.msg.ClusterMessages.InitJoinAck) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // required .Address address = 1; + private akka.cluster.protobuf.msg.ClusterMessages.Address address_ = akka.cluster.protobuf.msg.ClusterMessages.Address.getDefaultInstance(); + private akka.protobuf.SingleFieldBuilder< + akka.cluster.protobuf.msg.ClusterMessages.Address, akka.cluster.protobuf.msg.ClusterMessages.Address.Builder, akka.cluster.protobuf.msg.ClusterMessages.AddressOrBuilder> addressBuilder_; + /** + * required .Address address = 1; + */ + public boolean hasAddress() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required .Address address = 1; + */ + public akka.cluster.protobuf.msg.ClusterMessages.Address getAddress() { + if (addressBuilder_ == null) { + return address_; + } else { + return addressBuilder_.getMessage(); + } + } + /** + * required .Address address = 1; + */ + public Builder setAddress(akka.cluster.protobuf.msg.ClusterMessages.Address value) { + if (addressBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + address_ = value; + onChanged(); + } else { + addressBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * required .Address address = 1; + */ + public Builder setAddress( + akka.cluster.protobuf.msg.ClusterMessages.Address.Builder builderForValue) { + if (addressBuilder_ == null) { + address_ = builderForValue.build(); + onChanged(); + } else { + addressBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * required .Address address = 1; + */ + public Builder mergeAddress(akka.cluster.protobuf.msg.ClusterMessages.Address value) { + if (addressBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + address_ != akka.cluster.protobuf.msg.ClusterMessages.Address.getDefaultInstance()) { + address_ = + akka.cluster.protobuf.msg.ClusterMessages.Address.newBuilder(address_).mergeFrom(value).buildPartial(); + } else { + address_ = value; + } + onChanged(); + } else { + addressBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * required .Address address = 1; + */ + public Builder clearAddress() { + if (addressBuilder_ == null) { + address_ = akka.cluster.protobuf.msg.ClusterMessages.Address.getDefaultInstance(); + onChanged(); + } else { + addressBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * required .Address address = 1; + */ + public akka.cluster.protobuf.msg.ClusterMessages.Address.Builder getAddressBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getAddressFieldBuilder().getBuilder(); + } + /** + * required .Address address = 1; + */ + public akka.cluster.protobuf.msg.ClusterMessages.AddressOrBuilder getAddressOrBuilder() { + if (addressBuilder_ != null) { + return addressBuilder_.getMessageOrBuilder(); + } else { + return address_; + } + } + /** + * required .Address address = 1; + */ + private akka.protobuf.SingleFieldBuilder< + akka.cluster.protobuf.msg.ClusterMessages.Address, akka.cluster.protobuf.msg.ClusterMessages.Address.Builder, akka.cluster.protobuf.msg.ClusterMessages.AddressOrBuilder> + getAddressFieldBuilder() { + if (addressBuilder_ == null) { + addressBuilder_ = new akka.protobuf.SingleFieldBuilder< + akka.cluster.protobuf.msg.ClusterMessages.Address, akka.cluster.protobuf.msg.ClusterMessages.Address.Builder, akka.cluster.protobuf.msg.ClusterMessages.AddressOrBuilder>( + address_, + getParentForChildren(), + isClean()); + address_ = null; + } + return addressBuilder_; + } + + // optional .ConfigCheck configCheck = 2; + private akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck configCheck_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDefaultInstance(); + private akka.protobuf.SingleFieldBuilder< + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheckOrBuilder> configCheckBuilder_; + /** + * optional .ConfigCheck configCheck = 2; + */ + public boolean hasConfigCheck() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck getConfigCheck() { + if (configCheckBuilder_ == null) { + return configCheck_; + } else { + return configCheckBuilder_.getMessage(); + } + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public Builder setConfigCheck(akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck value) { + if (configCheckBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + configCheck_ = value; + onChanged(); + } else { + configCheckBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public Builder setConfigCheck( + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder builderForValue) { + if (configCheckBuilder_ == null) { + configCheck_ = builderForValue.build(); + onChanged(); + } else { + configCheckBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public Builder mergeConfigCheck(akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck value) { + if (configCheckBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + configCheck_ != akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDefaultInstance()) { + configCheck_ = + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.newBuilder(configCheck_).mergeFrom(value).buildPartial(); + } else { + configCheck_ = value; + } + onChanged(); + } else { + configCheckBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public Builder clearConfigCheck() { + if (configCheckBuilder_ == null) { + configCheck_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDefaultInstance(); + onChanged(); + } else { + configCheckBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder getConfigCheckBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getConfigCheckFieldBuilder().getBuilder(); + } + /** + * optional .ConfigCheck configCheck = 2; + */ + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheckOrBuilder getConfigCheckOrBuilder() { + if (configCheckBuilder_ != null) { + return configCheckBuilder_.getMessageOrBuilder(); + } else { + return configCheck_; + } + } + /** + * optional .ConfigCheck configCheck = 2; + */ + private akka.protobuf.SingleFieldBuilder< + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheckOrBuilder> + getConfigCheckFieldBuilder() { + if (configCheckBuilder_ == null) { + configCheckBuilder_ = new akka.protobuf.SingleFieldBuilder< + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheckOrBuilder>( + configCheck_, + getParentForChildren(), + isClean()); + configCheck_ = null; + } + return configCheckBuilder_; + } + + // @@protoc_insertion_point(builder_scope:InitJoinAck) + } + + static { + defaultInstance = new InitJoinAck(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:InitJoinAck) + } + + public interface ConfigCheckOrBuilder extends + akka.protobuf.GeneratedMessage. + ExtendableMessageOrBuilder { + + // required .ConfigCheck.Type type = 1; + /** + * required .ConfigCheck.Type type = 1; + */ + boolean hasType(); + /** + * required .ConfigCheck.Type type = 1; + */ + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type getType(); + + // optional string clusterConfig = 2; + /** + * optional string clusterConfig = 2; + */ + boolean hasClusterConfig(); + /** + * optional string clusterConfig = 2; + */ + java.lang.String getClusterConfig(); + /** + * optional string clusterConfig = 2; + */ + akka.protobuf.ByteString + getClusterConfigBytes(); + } + /** + * Protobuf type {@code ConfigCheck} + */ + public static final class ConfigCheck extends + akka.protobuf.GeneratedMessage.ExtendableMessage< + ConfigCheck> implements ConfigCheckOrBuilder { + // Use ConfigCheck.newBuilder() to construct. + private ConfigCheck(akka.protobuf.GeneratedMessage.ExtendableBuilder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ConfigCheck(boolean noInit) { this.unknownFields = akka.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ConfigCheck defaultInstance; + public static ConfigCheck getDefaultInstance() { + return defaultInstance; + } + + public ConfigCheck getDefaultInstanceForType() { + return defaultInstance; + } + + private final akka.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final akka.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ConfigCheck( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + akka.protobuf.UnknownFieldSet.Builder unknownFields = + akka.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type value = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + bitField0_ |= 0x00000002; + clusterConfig_ = input.readBytes(); + break; + } + } + } + } catch (akka.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new akka.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_ConfigCheck_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_ConfigCheck_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.class, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder.class); + } + + public static akka.protobuf.Parser PARSER = + new akka.protobuf.AbstractParser() { + public ConfigCheck parsePartialFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return new ConfigCheck(input, extensionRegistry); + } + }; + + @java.lang.Override + public akka.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code ConfigCheck.Type} + */ + public enum Type + implements akka.protobuf.ProtocolMessageEnum { + /** + * UncheckedConfig = 1; + */ + UncheckedConfig(0, 1), + /** + * IncompatibleConfig = 2; + */ + IncompatibleConfig(1, 2), + /** + * CompatibleConfig = 3; + */ + CompatibleConfig(2, 3), + ; + + /** + * UncheckedConfig = 1; + */ + public static final int UncheckedConfig_VALUE = 1; + /** + * IncompatibleConfig = 2; + */ + public static final int IncompatibleConfig_VALUE = 2; + /** + * CompatibleConfig = 3; + */ + public static final int CompatibleConfig_VALUE = 3; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 1: return UncheckedConfig; + case 2: return IncompatibleConfig; + case 3: return CompatibleConfig; + default: return null; + } + } + + public static akka.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static akka.protobuf.Internal.EnumLiteMap + internalValueMap = + new akka.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final akka.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final akka.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final akka.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + akka.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:ConfigCheck.Type) + } + + private int bitField0_; + // required .ConfigCheck.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type type_; + /** + * required .ConfigCheck.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required .ConfigCheck.Type type = 1; + */ + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type getType() { + return type_; + } + + // optional string clusterConfig = 2; + public static final int CLUSTERCONFIG_FIELD_NUMBER = 2; + private java.lang.Object clusterConfig_; + /** + * optional string clusterConfig = 2; + */ + public boolean hasClusterConfig() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string clusterConfig = 2; + */ + public java.lang.String getClusterConfig() { + java.lang.Object ref = clusterConfig_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + akka.protobuf.ByteString bs = + (akka.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clusterConfig_ = s; + } + return s; + } + } + /** + * optional string clusterConfig = 2; + */ + public akka.protobuf.ByteString + getClusterConfigBytes() { + java.lang.Object ref = clusterConfig_; + if (ref instanceof java.lang.String) { + akka.protobuf.ByteString b = + akka.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clusterConfig_ = b; + return b; + } else { + return (akka.protobuf.ByteString) ref; + } + } + + private void initFields() { + type_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type.UncheckedConfig; + clusterConfig_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + if (!hasType()) { + memoizedIsInitialized = 0; + return false; + } + if (!extensionsAreInitialized()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(akka.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + akka.protobuf.GeneratedMessage + .ExtendableMessage.ExtensionWriter extensionWriter = + newExtensionWriter(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getClusterConfigBytes()); + } + extensionWriter.writeUntil(103, output); + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += akka.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += akka.protobuf.CodedOutputStream + .computeBytesSize(2, getClusterConfigBytes()); + } + size += extensionsSerializedSize(); + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom( + akka.protobuf.ByteString data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom( + akka.protobuf.ByteString data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom(byte[] data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom( + byte[] data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseDelimitedFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom( + akka.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parseFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code ConfigCheck} + */ + public static final class Builder extends + akka.protobuf.GeneratedMessage.ExtendableBuilder< + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck, Builder> implements akka.cluster.protobuf.msg.ClusterMessages.ConfigCheckOrBuilder { + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_ConfigCheck_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_ConfigCheck_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.class, akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Builder.class); + } + + // Construct using akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (akka.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type.UncheckedConfig; + bitField0_ = (bitField0_ & ~0x00000001); + clusterConfig_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public akka.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_ConfigCheck_descriptor; + } + + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck getDefaultInstanceForType() { + return akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDefaultInstance(); + } + + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck build() { + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck buildPartial() { + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck result = new akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.clusterConfig_ = clusterConfig_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(akka.protobuf.Message other) { + if (other instanceof akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck) { + return mergeFrom((akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck other) { + if (other == akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasClusterConfig()) { + bitField0_ |= 0x00000002; + clusterConfig_ = other.clusterConfig_; + onChanged(); + } + this.mergeExtensionFields(other); + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + if (!hasType()) { + + return false; + } + if (!extensionsAreInitialized()) { + + return false; + } + return true; + } + + public Builder mergeFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (akka.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // required .ConfigCheck.Type type = 1; + private akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type type_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type.UncheckedConfig; + /** + * required .ConfigCheck.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required .ConfigCheck.Type type = 1; + */ + public akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type getType() { + return type_; + } + /** + * required .ConfigCheck.Type type = 1; + */ + public Builder setType(akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * required .ConfigCheck.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck.Type.UncheckedConfig; + onChanged(); + return this; + } + + // optional string clusterConfig = 2; + private java.lang.Object clusterConfig_ = ""; + /** + * optional string clusterConfig = 2; + */ + public boolean hasClusterConfig() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string clusterConfig = 2; + */ + public java.lang.String getClusterConfig() { + java.lang.Object ref = clusterConfig_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((akka.protobuf.ByteString) ref) + .toStringUtf8(); + clusterConfig_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string clusterConfig = 2; + */ + public akka.protobuf.ByteString + getClusterConfigBytes() { + java.lang.Object ref = clusterConfig_; + if (ref instanceof String) { + akka.protobuf.ByteString b = + akka.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clusterConfig_ = b; + return b; + } else { + return (akka.protobuf.ByteString) ref; + } + } + /** + * optional string clusterConfig = 2; + */ + public Builder setClusterConfig( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clusterConfig_ = value; + onChanged(); + return this; + } + /** + * optional string clusterConfig = 2; + */ + public Builder clearClusterConfig() { + bitField0_ = (bitField0_ & ~0x00000002); + clusterConfig_ = getDefaultInstance().getClusterConfig(); + onChanged(); + return this; + } + /** + * optional string clusterConfig = 2; + */ + public Builder setClusterConfigBytes( + akka.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clusterConfig_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:ConfigCheck) + } + + static { + defaultInstance = new ConfigCheck(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:ConfigCheck) + } + + public interface UncheckedConfigOrBuilder + extends akka.protobuf.MessageOrBuilder { + } + /** + * Protobuf type {@code UncheckedConfig} + */ + public static final class UncheckedConfig extends + akka.protobuf.GeneratedMessage + implements UncheckedConfigOrBuilder { + // Use UncheckedConfig.newBuilder() to construct. + private UncheckedConfig(akka.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private UncheckedConfig(boolean noInit) { this.unknownFields = akka.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final UncheckedConfig defaultInstance; + public static UncheckedConfig getDefaultInstance() { + return defaultInstance; + } + + public UncheckedConfig getDefaultInstanceForType() { + return defaultInstance; + } + + private final akka.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final akka.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private UncheckedConfig( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + initFields(); + akka.protobuf.UnknownFieldSet.Builder unknownFields = + akka.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (akka.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new akka.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_UncheckedConfig_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_UncheckedConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.class, akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.Builder.class); + } + + public static akka.protobuf.Parser PARSER = + new akka.protobuf.AbstractParser() { + public UncheckedConfig parsePartialFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return new UncheckedConfig(input, extensionRegistry); + } + }; + + @java.lang.Override + public akka.protobuf.Parser getParserForType() { + return PARSER; + } + + private void initFields() { + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(akka.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom( + akka.protobuf.ByteString data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom( + akka.protobuf.ByteString data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom(byte[] data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom( + byte[] data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseDelimitedFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom( + akka.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parseFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code UncheckedConfig} + */ + public static final class Builder extends + akka.protobuf.GeneratedMessage.Builder + implements akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfigOrBuilder { + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_UncheckedConfig_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_UncheckedConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.class, akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.Builder.class); + } + + // Construct using akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (akka.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public akka.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_UncheckedConfig_descriptor; + } + + public akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig getDefaultInstanceForType() { + return akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.getDefaultInstance(); + } + + public akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig build() { + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig buildPartial() { + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig result = new akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig(this); + onBuilt(); + return result; + } + + public Builder mergeFrom(akka.protobuf.Message other) { + if (other instanceof akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig) { + return mergeFrom((akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig other) { + if (other == akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.getDefaultInstance()) return this; + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (akka.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + // @@protoc_insertion_point(builder_scope:UncheckedConfig) + } + + static { + defaultInstance = new UncheckedConfig(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:UncheckedConfig) + public static final int CHECKCONFIG_FIELD_NUMBER = 100; + /** + * extend .ConfigCheck { ... } + */ + public static final + akka.protobuf.GeneratedMessage.GeneratedExtension< + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck, + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig> checkConfig = akka.protobuf.GeneratedMessage + .newMessageScopedGeneratedExtension( + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.getDefaultInstance(), + 0, + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.class, + akka.cluster.protobuf.msg.ClusterMessages.UncheckedConfig.getDefaultInstance()); + } + + public interface IncompatibleConfigOrBuilder + extends akka.protobuf.MessageOrBuilder { + } + /** + * Protobuf type {@code IncompatibleConfig} + */ + public static final class IncompatibleConfig extends + akka.protobuf.GeneratedMessage + implements IncompatibleConfigOrBuilder { + // Use IncompatibleConfig.newBuilder() to construct. + private IncompatibleConfig(akka.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private IncompatibleConfig(boolean noInit) { this.unknownFields = akka.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final IncompatibleConfig defaultInstance; + public static IncompatibleConfig getDefaultInstance() { + return defaultInstance; + } + + public IncompatibleConfig getDefaultInstanceForType() { + return defaultInstance; + } + + private final akka.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final akka.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private IncompatibleConfig( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + initFields(); + akka.protobuf.UnknownFieldSet.Builder unknownFields = + akka.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (akka.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new akka.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_IncompatibleConfig_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_IncompatibleConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.class, akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.Builder.class); + } + + public static akka.protobuf.Parser PARSER = + new akka.protobuf.AbstractParser() { + public IncompatibleConfig parsePartialFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return new IncompatibleConfig(input, extensionRegistry); + } + }; + + @java.lang.Override + public akka.protobuf.Parser getParserForType() { + return PARSER; + } + + private void initFields() { + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(akka.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom( + akka.protobuf.ByteString data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom( + akka.protobuf.ByteString data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom(byte[] data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom( + byte[] data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseDelimitedFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom( + akka.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parseFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code IncompatibleConfig} + */ + public static final class Builder extends + akka.protobuf.GeneratedMessage.Builder + implements akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfigOrBuilder { + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_IncompatibleConfig_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_IncompatibleConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.class, akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.Builder.class); + } + + // Construct using akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (akka.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public akka.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_IncompatibleConfig_descriptor; + } + + public akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig getDefaultInstanceForType() { + return akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.getDefaultInstance(); + } + + public akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig build() { + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig buildPartial() { + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig result = new akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig(this); + onBuilt(); + return result; + } + + public Builder mergeFrom(akka.protobuf.Message other) { + if (other instanceof akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig) { + return mergeFrom((akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig other) { + if (other == akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.getDefaultInstance()) return this; + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (akka.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + // @@protoc_insertion_point(builder_scope:IncompatibleConfig) + } + + static { + defaultInstance = new IncompatibleConfig(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:IncompatibleConfig) + public static final int CHECKCONFIG_FIELD_NUMBER = 101; + /** + * extend .ConfigCheck { ... } + */ + public static final + akka.protobuf.GeneratedMessage.GeneratedExtension< + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck, + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig> checkConfig = akka.protobuf.GeneratedMessage + .newMessageScopedGeneratedExtension( + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.getDefaultInstance(), + 0, + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.class, + akka.cluster.protobuf.msg.ClusterMessages.IncompatibleConfig.getDefaultInstance()); + } + + public interface CompatibleConfigOrBuilder + extends akka.protobuf.MessageOrBuilder { + + // required string clusterConfig = 2; + /** + * required string clusterConfig = 2; + */ + boolean hasClusterConfig(); + /** + * required string clusterConfig = 2; + */ + java.lang.String getClusterConfig(); + /** + * required string clusterConfig = 2; + */ + akka.protobuf.ByteString + getClusterConfigBytes(); + } + /** + * Protobuf type {@code CompatibleConfig} + */ + public static final class CompatibleConfig extends + akka.protobuf.GeneratedMessage + implements CompatibleConfigOrBuilder { + // Use CompatibleConfig.newBuilder() to construct. + private CompatibleConfig(akka.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private CompatibleConfig(boolean noInit) { this.unknownFields = akka.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final CompatibleConfig defaultInstance; + public static CompatibleConfig getDefaultInstance() { + return defaultInstance; + } + + public CompatibleConfig getDefaultInstanceForType() { + return defaultInstance; + } + + private final akka.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final akka.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private CompatibleConfig( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + akka.protobuf.UnknownFieldSet.Builder unknownFields = + akka.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 18: { + bitField0_ |= 0x00000001; + clusterConfig_ = input.readBytes(); + break; + } + } + } + } catch (akka.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new akka.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_CompatibleConfig_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_CompatibleConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.class, akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.Builder.class); + } + + public static akka.protobuf.Parser PARSER = + new akka.protobuf.AbstractParser() { + public CompatibleConfig parsePartialFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return new CompatibleConfig(input, extensionRegistry); + } + }; + + @java.lang.Override + public akka.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // required string clusterConfig = 2; + public static final int CLUSTERCONFIG_FIELD_NUMBER = 2; + private java.lang.Object clusterConfig_; + /** + * required string clusterConfig = 2; + */ + public boolean hasClusterConfig() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required string clusterConfig = 2; + */ + public java.lang.String getClusterConfig() { + java.lang.Object ref = clusterConfig_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + akka.protobuf.ByteString bs = + (akka.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clusterConfig_ = s; + } + return s; + } + } + /** + * required string clusterConfig = 2; + */ + public akka.protobuf.ByteString + getClusterConfigBytes() { + java.lang.Object ref = clusterConfig_; + if (ref instanceof java.lang.String) { + akka.protobuf.ByteString b = + akka.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clusterConfig_ = b; + return b; + } else { + return (akka.protobuf.ByteString) ref; + } + } + + private void initFields() { + clusterConfig_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + if (!hasClusterConfig()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(akka.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(2, getClusterConfigBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += akka.protobuf.CodedOutputStream + .computeBytesSize(2, getClusterConfigBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom( + akka.protobuf.ByteString data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom( + akka.protobuf.ByteString data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom(byte[] data) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom( + byte[] data, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws akka.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseDelimitedFrom( + java.io.InputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom( + akka.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parseFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code CompatibleConfig} + */ + public static final class Builder extends + akka.protobuf.GeneratedMessage.Builder + implements akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfigOrBuilder { + public static final akka.protobuf.Descriptors.Descriptor + getDescriptor() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_CompatibleConfig_descriptor; + } + + protected akka.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_CompatibleConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.class, akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.Builder.class); + } + + // Construct using akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + akka.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (akka.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + clusterConfig_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public akka.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return akka.cluster.protobuf.msg.ClusterMessages.internal_static_CompatibleConfig_descriptor; + } + + public akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig getDefaultInstanceForType() { + return akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.getDefaultInstance(); + } + + public akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig build() { + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig buildPartial() { + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig result = new akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.clusterConfig_ = clusterConfig_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(akka.protobuf.Message other) { + if (other instanceof akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig) { + return mergeFrom((akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig other) { + if (other == akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.getDefaultInstance()) return this; + if (other.hasClusterConfig()) { + bitField0_ |= 0x00000001; + clusterConfig_ = other.clusterConfig_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + if (!hasClusterConfig()) { + + return false; + } + return true; + } + + public Builder mergeFrom( + akka.protobuf.CodedInputStream input, + akka.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (akka.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // required string clusterConfig = 2; + private java.lang.Object clusterConfig_ = ""; + /** + * required string clusterConfig = 2; + */ + public boolean hasClusterConfig() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * required string clusterConfig = 2; + */ + public java.lang.String getClusterConfig() { + java.lang.Object ref = clusterConfig_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((akka.protobuf.ByteString) ref) + .toStringUtf8(); + clusterConfig_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clusterConfig = 2; + */ + public akka.protobuf.ByteString + getClusterConfigBytes() { + java.lang.Object ref = clusterConfig_; + if (ref instanceof String) { + akka.protobuf.ByteString b = + akka.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clusterConfig_ = b; + return b; + } else { + return (akka.protobuf.ByteString) ref; + } + } + /** + * required string clusterConfig = 2; + */ + public Builder setClusterConfig( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + clusterConfig_ = value; + onChanged(); + return this; + } + /** + * required string clusterConfig = 2; + */ + public Builder clearClusterConfig() { + bitField0_ = (bitField0_ & ~0x00000001); + clusterConfig_ = getDefaultInstance().getClusterConfig(); + onChanged(); + return this; + } + /** + * required string clusterConfig = 2; + */ + public Builder setClusterConfigBytes( + akka.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + clusterConfig_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:CompatibleConfig) + } + + static { + defaultInstance = new CompatibleConfig(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:CompatibleConfig) + public static final int CHECKCONFIG_FIELD_NUMBER = 102; + /** + * extend .ConfigCheck { ... } + */ + public static final + akka.protobuf.GeneratedMessage.GeneratedExtension< + akka.cluster.protobuf.msg.ClusterMessages.ConfigCheck, + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig> checkConfig = akka.protobuf.GeneratedMessage + .newMessageScopedGeneratedExtension( + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.getDefaultInstance(), + 0, + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.class, + akka.cluster.protobuf.msg.ClusterMessages.CompatibleConfig.getDefaultInstance()); + } + public interface GossipEnvelopeOrBuilder extends akka.protobuf.MessageOrBuilder { @@ -14902,6 +17934,36 @@ public final class ClusterMessages { private static akka.protobuf.GeneratedMessage.FieldAccessorTable internal_static_Welcome_fieldAccessorTable; + private static akka.protobuf.Descriptors.Descriptor + internal_static_InitJoin_descriptor; + private static + akka.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_InitJoin_fieldAccessorTable; + private static akka.protobuf.Descriptors.Descriptor + internal_static_InitJoinAck_descriptor; + private static + akka.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_InitJoinAck_fieldAccessorTable; + private static akka.protobuf.Descriptors.Descriptor + internal_static_ConfigCheck_descriptor; + private static + akka.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_ConfigCheck_fieldAccessorTable; + private static akka.protobuf.Descriptors.Descriptor + internal_static_UncheckedConfig_descriptor; + private static + akka.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_UncheckedConfig_fieldAccessorTable; + private static akka.protobuf.Descriptors.Descriptor + internal_static_IncompatibleConfig_descriptor; + private static + akka.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_IncompatibleConfig_fieldAccessorTable; + private static akka.protobuf.Descriptors.Descriptor + internal_static_CompatibleConfig_descriptor; + private static + akka.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_CompatibleConfig_fieldAccessorTable; private static akka.protobuf.Descriptors.Descriptor internal_static_GossipEnvelope_descriptor; private static @@ -14994,48 +18056,61 @@ public final class ClusterMessages { "\n\025ClusterMessages.proto\"3\n\004Join\022\034\n\004node\030" + "\001 \002(\0132\016.UniqueAddress\022\r\n\005roles\030\002 \003(\t\"@\n\007" + "Welcome\022\034\n\004from\030\001 \002(\0132\016.UniqueAddress\022\027\n" + - "\006gossip\030\002 \002(\0132\007.Gossip\"d\n\016GossipEnvelope" + - "\022\034\n\004from\030\001 \002(\0132\016.UniqueAddress\022\032\n\002to\030\002 \002" + - "(\0132\016.UniqueAddress\022\030\n\020serializedGossip\030\003" + - " \002(\014\"^\n\014GossipStatus\022\034\n\004from\030\001 \002(\0132\016.Uni" + - "queAddress\022\021\n\tallHashes\030\002 \003(\t\022\035\n\007version" + - "\030\003 \002(\0132\014.VectorClock\"\317\001\n\006Gossip\022$\n\014allAd" + - "dresses\030\001 \003(\0132\016.UniqueAddress\022\020\n\010allRole", - "s\030\002 \003(\t\022\021\n\tallHashes\030\003 \003(\t\022\030\n\007members\030\004 " + - "\003(\0132\007.Member\022!\n\010overview\030\005 \002(\0132\017.GossipO" + - "verview\022\035\n\007version\030\006 \002(\0132\014.VectorClock\022\036" + - "\n\ntombstones\030\007 \003(\0132\n.Tombstone\"S\n\016Gossip" + - "Overview\022\014\n\004seen\030\001 \003(\005\0223\n\024observerReacha" + - "bility\030\002 \003(\0132\025.ObserverReachability\"p\n\024O" + - "bserverReachability\022\024\n\014addressIndex\030\001 \002(" + - "\005\022\017\n\007version\030\004 \002(\003\0221\n\023subjectReachabilit" + - "y\030\002 \003(\0132\024.SubjectReachability\"a\n\023Subject" + - "Reachability\022\024\n\014addressIndex\030\001 \002(\005\022#\n\006st", - "atus\030\003 \002(\0162\023.ReachabilityStatus\022\017\n\007versi" + - "on\030\004 \002(\003\"4\n\tTombstone\022\024\n\014addressIndex\030\001 " + - "\002(\005\022\021\n\ttimestamp\030\002 \002(\003\"i\n\006Member\022\024\n\014addr" + - "essIndex\030\001 \002(\005\022\020\n\010upNumber\030\002 \002(\005\022\035\n\006stat" + - "us\030\003 \002(\0162\r.MemberStatus\022\030\n\014rolesIndexes\030" + - "\004 \003(\005B\002\020\001\"y\n\013VectorClock\022\021\n\ttimestamp\030\001 " + - "\001(\003\022&\n\010versions\030\002 \003(\0132\024.VectorClock.Vers" + - "ion\032/\n\007Version\022\021\n\thashIndex\030\001 \002(\005\022\021\n\ttim" + - "estamp\030\002 \002(\003\"\007\n\005Empty\"K\n\007Address\022\016\n\006syst" + - "em\030\001 \002(\t\022\020\n\010hostname\030\002 \002(\t\022\014\n\004port\030\003 \002(\r", - "\022\020\n\010protocol\030\004 \001(\t\"E\n\rUniqueAddress\022\031\n\007a" + - "ddress\030\001 \002(\0132\010.Address\022\013\n\003uid\030\002 \002(\r\022\014\n\004u" + - "id2\030\003 \001(\r\"V\n\021ClusterRouterPool\022\023\n\004pool\030\001" + - " \002(\0132\005.Pool\022,\n\010settings\030\002 \002(\0132\032.ClusterR" + - "outerPoolSettings\"<\n\004Pool\022\024\n\014serializerI" + - "d\030\001 \002(\r\022\020\n\010manifest\030\002 \002(\t\022\014\n\004data\030\003 \002(\014\"" + - "\216\001\n\031ClusterRouterPoolSettings\022\026\n\016totalIn" + - "stances\030\001 \002(\r\022\033\n\023maxInstancesPerNode\030\002 \002" + - "(\r\022\031\n\021allowLocalRoutees\030\003 \002(\010\022\017\n\007useRole" + - "\030\004 \001(\t\022\020\n\010useRoles\030\005 \003(\t*D\n\022Reachability", - "Status\022\r\n\tReachable\020\000\022\017\n\013Unreachable\020\001\022\016" + - "\n\nTerminated\020\002*b\n\014MemberStatus\022\013\n\007Joinin" + - "g\020\000\022\006\n\002Up\020\001\022\013\n\007Leaving\020\002\022\013\n\007Exiting\020\003\022\010\n" + - "\004Down\020\004\022\013\n\007Removed\020\005\022\014\n\010WeaklyUp\020\006B\035\n\031ak" + - "ka.cluster.protobuf.msgH\001" + "\006gossip\030\002 \002(\0132\007.Gossip\"!\n\010InitJoin\022\025\n\rcu" + + "rrentConfig\030\001 \001(\t\"K\n\013InitJoinAck\022\031\n\007addr" + + "ess\030\001 \002(\0132\010.Address\022!\n\013configCheck\030\002 \001(\013" + + "2\014.ConfigCheck\"\226\001\n\013ConfigCheck\022\037\n\004type\030\001" + + " \002(\0162\021.ConfigCheck.Type\022\025\n\rclusterConfig" + + "\030\002 \001(\t\"I\n\004Type\022\023\n\017UncheckedConfig\020\001\022\026\n\022I" + + "ncompatibleConfig\020\002\022\024\n\020CompatibleConfig\020", + "\003*\004\010d\020g\"F\n\017UncheckedConfig23\n\013checkConfi" + + "g\022\014.ConfigCheck\030d \002(\0132\020.UncheckedConfig\"" + + "L\n\022IncompatibleConfig26\n\013checkConfig\022\014.C" + + "onfigCheck\030e \002(\0132\023.IncompatibleConfig\"_\n" + + "\020CompatibleConfig\022\025\n\rclusterConfig\030\002 \002(\t" + + "24\n\013checkConfig\022\014.ConfigCheck\030f \002(\0132\021.Co" + + "mpatibleConfig\"d\n\016GossipEnvelope\022\034\n\004from" + + "\030\001 \002(\0132\016.UniqueAddress\022\032\n\002to\030\002 \002(\0132\016.Uni" + + "queAddress\022\030\n\020serializedGossip\030\003 \002(\014\"^\n\014" + + "GossipStatus\022\034\n\004from\030\001 \002(\0132\016.UniqueAddre", + "ss\022\021\n\tallHashes\030\002 \003(\t\022\035\n\007version\030\003 \002(\0132\014" + + ".VectorClock\"\317\001\n\006Gossip\022$\n\014allAddresses\030" + + "\001 \003(\0132\016.UniqueAddress\022\020\n\010allRoles\030\002 \003(\t\022" + + "\021\n\tallHashes\030\003 \003(\t\022\030\n\007members\030\004 \003(\0132\007.Me" + + "mber\022!\n\010overview\030\005 \002(\0132\017.GossipOverview\022" + + "\035\n\007version\030\006 \002(\0132\014.VectorClock\022\036\n\ntombst" + + "ones\030\007 \003(\0132\n.Tombstone\"S\n\016GossipOverview" + + "\022\014\n\004seen\030\001 \003(\005\0223\n\024observerReachability\030\002" + + " \003(\0132\025.ObserverReachability\"p\n\024ObserverR" + + "eachability\022\024\n\014addressIndex\030\001 \002(\005\022\017\n\007ver", + "sion\030\004 \002(\003\0221\n\023subjectReachability\030\002 \003(\0132" + + "\024.SubjectReachability\"a\n\023SubjectReachabi" + + "lity\022\024\n\014addressIndex\030\001 \002(\005\022#\n\006status\030\003 \002" + + "(\0162\023.ReachabilityStatus\022\017\n\007version\030\004 \002(\003" + + "\"4\n\tTombstone\022\024\n\014addressIndex\030\001 \002(\005\022\021\n\tt" + + "imestamp\030\002 \002(\003\"i\n\006Member\022\024\n\014addressIndex" + + "\030\001 \002(\005\022\020\n\010upNumber\030\002 \002(\005\022\035\n\006status\030\003 \002(\016" + + "2\r.MemberStatus\022\030\n\014rolesIndexes\030\004 \003(\005B\002\020" + + "\001\"y\n\013VectorClock\022\021\n\ttimestamp\030\001 \001(\003\022&\n\010v" + + "ersions\030\002 \003(\0132\024.VectorClock.Version\032/\n\007V", + "ersion\022\021\n\thashIndex\030\001 \002(\005\022\021\n\ttimestamp\030\002" + + " \002(\003\"\007\n\005Empty\"K\n\007Address\022\016\n\006system\030\001 \002(\t" + + "\022\020\n\010hostname\030\002 \002(\t\022\014\n\004port\030\003 \002(\r\022\020\n\010prot" + + "ocol\030\004 \001(\t\"E\n\rUniqueAddress\022\031\n\007address\030\001" + + " \002(\0132\010.Address\022\013\n\003uid\030\002 \002(\r\022\014\n\004uid2\030\003 \001(" + + "\r\"V\n\021ClusterRouterPool\022\023\n\004pool\030\001 \002(\0132\005.P" + + "ool\022,\n\010settings\030\002 \002(\0132\032.ClusterRouterPoo" + + "lSettings\"<\n\004Pool\022\024\n\014serializerId\030\001 \002(\r\022" + + "\020\n\010manifest\030\002 \002(\t\022\014\n\004data\030\003 \002(\014\"\216\001\n\031Clus" + + "terRouterPoolSettings\022\026\n\016totalInstances\030", + "\001 \002(\r\022\033\n\023maxInstancesPerNode\030\002 \002(\r\022\031\n\021al" + + "lowLocalRoutees\030\003 \002(\010\022\017\n\007useRole\030\004 \001(\t\022\020" + + "\n\010useRoles\030\005 \003(\t*D\n\022ReachabilityStatus\022\r" + + "\n\tReachable\020\000\022\017\n\013Unreachable\020\001\022\016\n\nTermin" + + "ated\020\002*b\n\014MemberStatus\022\013\n\007Joining\020\000\022\006\n\002U" + + "p\020\001\022\013\n\007Leaving\020\002\022\013\n\007Exiting\020\003\022\010\n\004Down\020\004\022" + + "\013\n\007Removed\020\005\022\014\n\010WeaklyUp\020\006B\035\n\031akka.clust" + + "er.protobuf.msgH\001" }; akka.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new akka.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -15054,56 +18129,92 @@ public final class ClusterMessages { akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Welcome_descriptor, new java.lang.String[] { "From", "Gossip", }); - internal_static_GossipEnvelope_descriptor = + internal_static_InitJoin_descriptor = getDescriptor().getMessageTypes().get(2); + internal_static_InitJoin_fieldAccessorTable = new + akka.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_InitJoin_descriptor, + new java.lang.String[] { "CurrentConfig", }); + internal_static_InitJoinAck_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_InitJoinAck_fieldAccessorTable = new + akka.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_InitJoinAck_descriptor, + new java.lang.String[] { "Address", "ConfigCheck", }); + internal_static_ConfigCheck_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_ConfigCheck_fieldAccessorTable = new + akka.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_ConfigCheck_descriptor, + new java.lang.String[] { "Type", "ClusterConfig", }); + internal_static_UncheckedConfig_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_UncheckedConfig_fieldAccessorTable = new + akka.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_UncheckedConfig_descriptor, + new java.lang.String[] { }); + internal_static_IncompatibleConfig_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_IncompatibleConfig_fieldAccessorTable = new + akka.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_IncompatibleConfig_descriptor, + new java.lang.String[] { }); + internal_static_CompatibleConfig_descriptor = + getDescriptor().getMessageTypes().get(7); + internal_static_CompatibleConfig_fieldAccessorTable = new + akka.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_CompatibleConfig_descriptor, + new java.lang.String[] { "ClusterConfig", }); + internal_static_GossipEnvelope_descriptor = + getDescriptor().getMessageTypes().get(8); internal_static_GossipEnvelope_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_GossipEnvelope_descriptor, new java.lang.String[] { "From", "To", "SerializedGossip", }); internal_static_GossipStatus_descriptor = - getDescriptor().getMessageTypes().get(3); + getDescriptor().getMessageTypes().get(9); internal_static_GossipStatus_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_GossipStatus_descriptor, new java.lang.String[] { "From", "AllHashes", "Version", }); internal_static_Gossip_descriptor = - getDescriptor().getMessageTypes().get(4); + getDescriptor().getMessageTypes().get(10); internal_static_Gossip_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Gossip_descriptor, new java.lang.String[] { "AllAddresses", "AllRoles", "AllHashes", "Members", "Overview", "Version", "Tombstones", }); internal_static_GossipOverview_descriptor = - getDescriptor().getMessageTypes().get(5); + getDescriptor().getMessageTypes().get(11); internal_static_GossipOverview_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_GossipOverview_descriptor, new java.lang.String[] { "Seen", "ObserverReachability", }); internal_static_ObserverReachability_descriptor = - getDescriptor().getMessageTypes().get(6); + getDescriptor().getMessageTypes().get(12); internal_static_ObserverReachability_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_ObserverReachability_descriptor, new java.lang.String[] { "AddressIndex", "Version", "SubjectReachability", }); internal_static_SubjectReachability_descriptor = - getDescriptor().getMessageTypes().get(7); + getDescriptor().getMessageTypes().get(13); internal_static_SubjectReachability_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_SubjectReachability_descriptor, new java.lang.String[] { "AddressIndex", "Status", "Version", }); internal_static_Tombstone_descriptor = - getDescriptor().getMessageTypes().get(8); + getDescriptor().getMessageTypes().get(14); internal_static_Tombstone_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Tombstone_descriptor, new java.lang.String[] { "AddressIndex", "Timestamp", }); internal_static_Member_descriptor = - getDescriptor().getMessageTypes().get(9); + getDescriptor().getMessageTypes().get(15); internal_static_Member_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Member_descriptor, new java.lang.String[] { "AddressIndex", "UpNumber", "Status", "RolesIndexes", }); internal_static_VectorClock_descriptor = - getDescriptor().getMessageTypes().get(10); + getDescriptor().getMessageTypes().get(16); internal_static_VectorClock_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_VectorClock_descriptor, @@ -15115,37 +18226,37 @@ public final class ClusterMessages { internal_static_VectorClock_Version_descriptor, new java.lang.String[] { "HashIndex", "Timestamp", }); internal_static_Empty_descriptor = - getDescriptor().getMessageTypes().get(11); + getDescriptor().getMessageTypes().get(17); internal_static_Empty_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Empty_descriptor, new java.lang.String[] { }); internal_static_Address_descriptor = - getDescriptor().getMessageTypes().get(12); + getDescriptor().getMessageTypes().get(18); internal_static_Address_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Address_descriptor, new java.lang.String[] { "System", "Hostname", "Port", "Protocol", }); internal_static_UniqueAddress_descriptor = - getDescriptor().getMessageTypes().get(13); + getDescriptor().getMessageTypes().get(19); internal_static_UniqueAddress_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_UniqueAddress_descriptor, new java.lang.String[] { "Address", "Uid", "Uid2", }); internal_static_ClusterRouterPool_descriptor = - getDescriptor().getMessageTypes().get(14); + getDescriptor().getMessageTypes().get(20); internal_static_ClusterRouterPool_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_ClusterRouterPool_descriptor, new java.lang.String[] { "Pool", "Settings", }); internal_static_Pool_descriptor = - getDescriptor().getMessageTypes().get(15); + getDescriptor().getMessageTypes().get(21); internal_static_Pool_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Pool_descriptor, new java.lang.String[] { "SerializerId", "Manifest", "Data", }); internal_static_ClusterRouterPoolSettings_descriptor = - getDescriptor().getMessageTypes().get(16); + getDescriptor().getMessageTypes().get(22); internal_static_ClusterRouterPoolSettings_fieldAccessorTable = new akka.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_ClusterRouterPoolSettings_descriptor, diff --git a/akka-cluster/src/main/mima-filters/2.5.9.backwards.excludes b/akka-cluster/src/main/mima-filters/2.5.9.backwards.excludes new file mode 100644 index 0000000000..aedc02b960 --- /dev/null +++ b/akka-cluster/src/main/mima-filters/2.5.9.backwards.excludes @@ -0,0 +1,19 @@ +# #24009 Rolling update config checker +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.JoinSeedNodeProcess.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.ClusterCoreSupervisor.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.FirstSeedNodeProcess.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.ClusterDaemon.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.ClusterCoreDaemon.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.ClusterCoreDaemon.initJoin") +ProblemFilters.exclude[MissingTypesProblem]("akka.cluster.InternalClusterAction$InitJoin$") +ProblemFilters.exclude[MissingFieldProblem]("akka.cluster.InternalClusterAction#InitJoin.serialVersionUID") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoin.productElement") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoin.productArity") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoin.canEqual") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoin.productIterator") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoin.productPrefix") +ProblemFilters.exclude[FinalMethodProblem]("akka.cluster.InternalClusterAction#InitJoin.toString") +ProblemFilters.exclude[MissingTypesProblem]("akka.cluster.InternalClusterAction$InitJoinAck$") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoinAck.copy") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoinAck.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.cluster.InternalClusterAction#InitJoinAck.apply") \ No newline at end of file diff --git a/akka-cluster/src/main/protobuf/ClusterMessages.proto b/akka-cluster/src/main/protobuf/ClusterMessages.proto index 99bbfa5448..57f735d6b3 100644 --- a/akka-cluster/src/main/protobuf/ClusterMessages.proto +++ b/akka-cluster/src/main/protobuf/ClusterMessages.proto @@ -41,13 +41,50 @@ message Welcome { /** * InitJoin - * Sends Empty */ +message InitJoin { + optional string currentConfig = 1; +} /** * InitJoinAck - * Sends an Address */ +message InitJoinAck { + required Address address = 1; + optional ConfigCheck configCheck = 2; +} + +message ConfigCheck { + extensions 100 to 102; + + enum Type { + UncheckedConfig = 1; + IncompatibleConfig = 2; + CompatibleConfig = 3; + } + required Type type = 1; + + optional string clusterConfig = 2; +} + +message UncheckedConfig { + extend ConfigCheck { + required UncheckedConfig checkConfig = 100; + } +} + +message IncompatibleConfig { + extend ConfigCheck { + required IncompatibleConfig checkConfig = 101; + } +} + +message CompatibleConfig { + extend ConfigCheck { + required CompatibleConfig checkConfig = 102; + } + required string clusterConfig = 2; +} /** * InitJoinNack @@ -186,7 +223,7 @@ message VectorClock { /** * An empty message */ - message Empty { +message Empty { } /** @@ -213,18 +250,18 @@ message UniqueAddress { * Cluster routing ****************************************/ - message ClusterRouterPool { +message ClusterRouterPool { required Pool pool = 1; required ClusterRouterPoolSettings settings = 2; } - message Pool { +message Pool { required uint32 serializerId = 1; required string manifest = 2; required bytes data = 3; } - message ClusterRouterPoolSettings { +message ClusterRouterPoolSettings { required uint32 totalInstances = 1; required uint32 maxInstancesPerNode = 2; required bool allowLocalRoutees = 3; diff --git a/akka-cluster/src/main/resources/reference.conf b/akka-cluster/src/main/resources/reference.conf index a6d3b85f20..0ec58dc2a8 100644 --- a/akka-cluster/src/main/resources/reference.conf +++ b/akka-cluster/src/main/resources/reference.conf @@ -280,6 +280,40 @@ akka { verbose-gossip-logging = off } + configuration-compatibility-check { + + # Enforce configuration compatibility checks when joining a cluster. + # Set to off to allow joining nodes to join a cluster even when configuration incompatibilities are detected or + # when the cluster does not support this feature. Compatibility checks are always performed and warning and + # error messsages are logged. + # + # This is particularly useful for rolling updates on clusters that do not support that feature. Since the old + # cluster won't be able to send the compatibility confirmation to the joining node, the joining node won't be able + # to 'know' if its allowed to join. + enforce-on-join = on + + checkers { + akka-cluster = "akka.cluster.JoinConfigCompatCheckCluster" + } + + # Some configuration properties might not be appropriate to transfer between nodes + # and such properties can be excluded from the configuration compatibility check by adding + # the paths of the properties to this list. Sensitive paths are grouped by key. Modules and third-party libraries + # can define their own set of sensitive paths without clashing with each other (as long they use unique keys). + # + # All properties starting with the paths defined here are excluded, i.e. you can add the path of a whole + # section here to skip everything inside that section. + sensitive-config-paths { + akka = [ + "user.home", "user.name", "user.dir", + "socksNonProxyHosts", "http.nonProxyHosts", "ftp.nonProxyHosts", + "akka.remote.secure-cookie", + "akka.remote.netty.ssl.security", + "akka.remote.artery.ssl" + ] + } + + } } actor.deployment.default.cluster { diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index 54afe67c67..c947ab5b1f 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -62,6 +62,7 @@ class Cluster(val system: ExtendedActorSystem) extends Extension { import InfoLogger._ import settings._ + private val joinConfigCompatChecker: JoinConfigCompatChecker = JoinConfigCompatChecker.load(system, settings) /** * The address including a `uid` of this cluster member. * The `uid` is needed to be able to distinguish different @@ -167,7 +168,7 @@ class Cluster(val system: ExtendedActorSystem) extends Extension { // create supervisor for daemons under path "/system/cluster" private val clusterDaemons: ActorRef = { - system.systemActorOf(Props(classOf[ClusterDaemon], settings). + system.systemActorOf(Props(classOf[ClusterDaemon], settings, joinConfigCompatChecker). withDispatcher(UseDispatcher).withDeploy(Deploy.local), name = "cluster") } diff --git a/akka-cluster/src/main/scala/akka/cluster/ClusterDaemon.scala b/akka-cluster/src/main/scala/akka/cluster/ClusterDaemon.scala index 180c0ad593..cf1d320c98 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ClusterDaemon.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ClusterDaemon.scala @@ -10,9 +10,12 @@ import akka.cluster.MemberStatus._ import akka.cluster.ClusterEvent._ import akka.dispatch.{ RequiresMessageQueue, UnboundedMessageQueueSemantics } import akka.Done +import akka.actor.CoordinatedShutdown.Reason +import akka.cluster.ClusterUserAction.JoinTo import akka.pattern.ask import akka.remote.QuarantinedEvent import akka.util.Timeout +import com.typesafe.config.{ Config, ConfigFactory } import scala.collection.immutable import scala.concurrent.duration._ @@ -32,6 +35,7 @@ trait ClusterMessage extends Serializable * [[akka.cluster.Cluster]] extension * or JMX. */ +@InternalApi private[cluster] object ClusterUserAction { /** @@ -58,6 +62,7 @@ private[cluster] object ClusterUserAction { /** * INTERNAL API */ +@InternalApi private[cluster] object InternalClusterAction { /** @@ -92,17 +97,22 @@ private[cluster] object InternalClusterAction { */ case object JoinSeedNode extends DeadLetterSuppression - /** - * see JoinSeedNode - */ - @SerialVersionUID(1L) - case object InitJoin extends ClusterMessage with DeadLetterSuppression + sealed trait ConfigCheck + case object UncheckedConfig extends ConfigCheck + case object IncompatibleConfig extends ConfigCheck + final case class CompatibleConfig(clusterConfig: Config) extends ConfigCheck /** * see JoinSeedNode */ @SerialVersionUID(1L) - final case class InitJoinAck(address: Address) extends ClusterMessage with DeadLetterSuppression + case class InitJoin(configOfJoiningNode: Config) extends ClusterMessage with DeadLetterSuppression + + /** + * see JoinSeedNode + */ + @SerialVersionUID(1L) + final case class InitJoinAck(address: Address, configCheck: ConfigCheck) extends ClusterMessage with DeadLetterSuppression /** * see JoinSeedNode @@ -162,7 +172,8 @@ private[cluster] object InternalClusterAction { * * Supervisor managing the different Cluster daemons. */ -private[cluster] final class ClusterDaemon(settings: ClusterSettings) extends Actor with ActorLogging +@InternalApi +private[cluster] final class ClusterDaemon(settings: ClusterSettings, joinConfigCompatChecker: JoinConfigCompatChecker) extends Actor with ActorLogging with RequiresMessageQueue[UnboundedMessageQueueSemantics] { import InternalClusterAction._ // Important - don't use Cluster(context.system) in constructor because that would @@ -196,7 +207,7 @@ private[cluster] final class ClusterDaemon(settings: ClusterSettings) extends Ac } def createChildren(): Unit = { - coreSupervisor = Some(context.actorOf(Props[ClusterCoreSupervisor]. + coreSupervisor = Some(context.actorOf(Props(classOf[ClusterCoreSupervisor], joinConfigCompatChecker). withDispatcher(context.props.dispatcher), name = "core")) context.actorOf(Props[ClusterHeartbeatReceiver]. withDispatcher(context.props.dispatcher), name = "heartbeatReceiver") @@ -225,7 +236,8 @@ private[cluster] final class ClusterDaemon(settings: ClusterSettings) extends Ac * ClusterCoreDaemon and ClusterDomainEventPublisher can't be restarted because the state * would be obsolete. Shutdown the member if any those actors crashed. */ -private[cluster] final class ClusterCoreSupervisor extends Actor with ActorLogging +@InternalApi +private[cluster] final class ClusterCoreSupervisor(joinConfigCompatChecker: JoinConfigCompatChecker) extends Actor with ActorLogging with RequiresMessageQueue[UnboundedMessageQueueSemantics] { // Important - don't use Cluster(context.system) in constructor because that would @@ -238,7 +250,7 @@ private[cluster] final class ClusterCoreSupervisor extends Actor with ActorLoggi def createChildren(): Unit = { val publisher = context.actorOf(Props[ClusterDomainEventPublisher]. withDispatcher(context.props.dispatcher), name = "publisher") - coreDaemon = Some(context.watch(context.actorOf(Props(classOf[ClusterCoreDaemon], publisher). + coreDaemon = Some(context.watch(context.actorOf(Props(classOf[ClusterCoreDaemon], publisher, joinConfigCompatChecker). withDispatcher(context.props.dispatcher), name = "daemon"))) } @@ -274,7 +286,7 @@ private[cluster] object ClusterCoreDaemon { * INTERNAL API. */ @InternalApi -private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with ActorLogging +private[cluster] class ClusterCoreDaemon(publisher: ActorRef, joinConfigCompatChecker: JoinConfigCompatChecker) extends Actor with ActorLogging with RequiresMessageQueue[UnboundedMessageQueueSemantics] { import InternalClusterAction._ import ClusterCoreDaemon._ @@ -475,9 +487,9 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with case ReapUnreachableTick ⇒ reapUnreachableMembers() case LeaderActionsTick ⇒ leaderActions() case PublishStatsTick ⇒ publishInternalStats() - case InitJoin ⇒ + case InitJoin(joiningNodeConfig) ⇒ logInfo("Received InitJoin message from [{}] to [{}]", sender(), selfAddress) - initJoin() + initJoin(joiningNodeConfig) case Join(node, roles) ⇒ joining(node, roles) case ClusterUserAction.Down(address) ⇒ downing(address) case ClusterUserAction.Leave(address) ⇒ leaving(address) @@ -509,7 +521,7 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with case other ⇒ super.unhandled(other) } - def initJoin(): Unit = { + def initJoin(joiningNodeConfig: Config): Unit = { val selfStatus = latestGossip.member(selfUniqueAddress).status if (removeUnreachableWithMemberStatus.contains(selfStatus)) { // prevents a Down and Exiting node from being used for joining @@ -517,7 +529,30 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with sender() ! InitJoinNack(selfAddress) } else { logInfo("Sending InitJoinAck message from node [{}] to [{}]", selfAddress, sender()) - sender() ! InitJoinAck(selfAddress) + // run config compatibility check using config provided by + // joining node and current (full) config on cluster side + + val configWithoutSensitiveKeys = { + val allowedConfigPaths = JoinConfigCompatChecker.removeSensitiveKeys(context.system.settings.config, cluster.settings) + // build a stripped down config instead where sensitive config paths are removed + // we don't want any check to happen on those keys + JoinConfigCompatChecker.filterWithKeys(allowedConfigPaths, context.system.settings.config) + } + + joinConfigCompatChecker.check(joiningNodeConfig, configWithoutSensitiveKeys) match { + case Valid ⇒ + val nonSensitiveKeys = JoinConfigCompatChecker.removeSensitiveKeys(joiningNodeConfig, cluster.settings) + // Send back to joining node a subset of current configuration + // containing the keys initially sent by the joining node minus + // any sensitive keys as defined by this node configuration + val clusterConfig = JoinConfigCompatChecker.filterWithKeys(nonSensitiveKeys, context.system.settings.config) + sender() ! InitJoinAck(selfAddress, CompatibleConfig(clusterConfig)) + case Invalid(messages) ⇒ + // messages are only logged on the cluster side + log.warning("Found incompatible settings when [{}] tried to join: {}", sender().path.address, messages.mkString(", ")) + sender() ! InitJoinAck(selfAddress, IncompatibleConfig) + } + } } @@ -533,10 +568,10 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with // use unique name of this actor, stopSeedNodeProcess doesn't wait for termination seedNodeProcessCounter += 1 if (newSeedNodes.head == selfAddress) { - Some(context.actorOf(Props(classOf[FirstSeedNodeProcess], newSeedNodes). + Some(context.actorOf(Props(classOf[FirstSeedNodeProcess], newSeedNodes, joinConfigCompatChecker). withDispatcher(UseDispatcher), name = "firstSeedNodeProcess-" + seedNodeProcessCounter)) } else { - Some(context.actorOf(Props(classOf[JoinSeedNodeProcess], newSeedNodes). + Some(context.actorOf(Props(classOf[JoinSeedNodeProcess], newSeedNodes, joinConfigCompatChecker). withDispatcher(UseDispatcher), name = "joinSeedNodeProcess-" + seedNodeProcessCounter)) } } @@ -949,7 +984,7 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with if (latestGossip.isMultiDc) latestGossip.overview.seen.count(membershipState.isInSameDc) < latestGossip.members.count(_.dataCenter == cluster.selfDataCenter) / 2 else - (latestGossip.overview.seen.size < latestGossip.members.size / 2) + latestGossip.overview.seen.size < latestGossip.members.size / 2 } /** @@ -1274,7 +1309,7 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with versionedGossip.clearSeen() else { // Nobody else has seen this gossip but us - val seenVersionedGossip = versionedGossip onlySeen (selfUniqueAddress) + val seenVersionedGossip = versionedGossip onlySeen selfUniqueAddress // Update the state with the new gossip seenVersionedGossip } @@ -1284,7 +1319,7 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with def assertLatestGossip(): Unit = if (Cluster.isAssertInvariantsEnabled && latestGossip.version.versions.size > latestGossip.members.size) - throw new IllegalStateException(s"Too many vector clock entries in gossip state ${latestGossip}") + throw new IllegalStateException(s"Too many vector clock entries in gossip state $latestGossip") def publishMembershipState(): Unit = { if (cluster.settings.Debug.VerboseGossipLogging) @@ -1303,6 +1338,11 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with } +/** + * INTERNAL API. + */ +private[cluster] case object IncompatibleConfigurationDetected extends Reason + /** * INTERNAL API. * @@ -1315,11 +1355,13 @@ private[cluster] class ClusterCoreDaemon(publisher: ActorRef) extends Actor with * it will reply with InitJoinAck and then the first seed node will join * that other seed node to join existing cluster. */ -private[cluster] final class FirstSeedNodeProcess(seedNodes: immutable.IndexedSeq[Address]) extends Actor with ActorLogging { +@InternalApi +private[cluster] final class FirstSeedNodeProcess(seedNodes: immutable.IndexedSeq[Address], joinConfigCompatChecker: JoinConfigCompatChecker) extends Actor with ActorLogging { import InternalClusterAction._ import ClusterUserAction.JoinTo val cluster = Cluster(context.system) + import cluster.settings._ import cluster.InfoLogger._ def selfAddress = cluster.selfAddress @@ -1341,8 +1383,11 @@ private[cluster] final class FirstSeedNodeProcess(seedNodes: immutable.IndexedSe def receive = { case JoinSeedNode ⇒ if (timeout.hasTimeLeft) { + val requiredNonSensitiveKeys = JoinConfigCompatChecker.removeSensitiveKeys(joinConfigCompatChecker.requiredKeys, cluster.settings) + // configToValidate only contains the keys that are required according to JoinConfigCompatChecker on this node + val configToValidate = JoinConfigCompatChecker.filterWithKeys(requiredNonSensitiveKeys, context.system.settings.config) // send InitJoin to remaining seed nodes (except myself) - remainingSeedNodes foreach { a ⇒ context.actorSelection(context.parent.path.toStringWithAddress(a)) ! InitJoin } + remainingSeedNodes foreach { a ⇒ context.actorSelection(context.parent.path.toStringWithAddress(a)) ! InitJoin(configToValidate) } } else { // no InitJoinAck received, initialize new cluster by joining myself if (log.isDebugEnabled) @@ -1352,11 +1397,60 @@ private[cluster] final class FirstSeedNodeProcess(seedNodes: immutable.IndexedSe context.parent ! JoinTo(selfAddress) context.stop(self) } - case InitJoinAck(address) ⇒ + + case InitJoinAck(address, CompatibleConfig(clusterConfig)) ⇒ logInfo("Received InitJoinAck message from [{}] to [{}]", sender(), selfAddress) - // first InitJoinAck reply, join existing cluster + // validates config coming from cluster against this node config + joinConfigCompatChecker.check(clusterConfig, context.system.settings.config) match { + case Valid ⇒ + // first InitJoinAck reply + context.parent ! JoinTo(address) + context.stop(self) + + case Invalid(messages) if ByPassConfigCompatCheck ⇒ + log.warning("Cluster validated this node config, but sent back incompatible settings: {}. " + + "Join will be performed because compatibility check is configured to not be enforced.", messages.mkString(", ")) + context.parent ! JoinTo(address) + context.stop(self) + + case Invalid(messages) ⇒ + log.error("Cluster validated this node config, but sent back incompatible settings: {}. " + + "It's recommended to perform a full cluster shutdown in order to deploy this new version. " + + "If a cluster shutdown isn't an option, you may want to disable this protection by setting " + + "'akka.cluster.configuration-compatibility-check.enforce-on-join = off'. " + + "Note that disabling it will allow the formation of a cluster with nodes having incompatible configuration settings. " + + "This node will be shutdown!", messages.mkString(", ")) + context.stop(self) + CoordinatedShutdown(context.system).run(IncompatibleConfigurationDetected) + } + + case InitJoinAck(address, UncheckedConfig) ⇒ + logInfo("Received InitJoinAck message from [{}] to [{}]", sender(), selfAddress) + log.warning("Joining a cluster without configuration compatibility check feature.") context.parent ! JoinTo(address) context.stop(self) + + case InitJoinAck(address, IncompatibleConfig) ⇒ + // first InitJoinAck reply, but incompatible + if (ByPassConfigCompatCheck) { + // only join if set to ignore config validation + logInfo("Received InitJoinAck message from [{}] to [{}]", sender(), selfAddress) + log.warning("Joining cluster with incompatible configurations. " + + "Join will be performed because compatibility check is configured to not be enforced.") + context.parent ! JoinTo(address) + context.stop(self) + } else { + log.error( + "Couldn't join seed nodes because of incompatible cluster configuration. " + + "It's recommended to perform a full cluster shutdown in order to deploy this new version." + + "If a cluster shutdown isn't an option, you may want to disable this protection by setting " + + "'akka.cluster.configuration-compatibility-check.enforce-on-join = off'. " + + "Note that disabling it will allow the formation of a cluster with nodes having incompatible configuration settings. " + + "This node will be shutdown!") + context.stop(self) + CoordinatedShutdown(context.system).run(IncompatibleConfigurationDetected) + } + case InitJoinNack(address) ⇒ logInfo("Received InitJoinNack message from [{}] to [{}]", sender(), selfAddress) remainingSeedNodes -= address @@ -1393,33 +1487,90 @@ private[cluster] final class FirstSeedNodeProcess(seedNodes: immutable.IndexedSe * 5. seed3 retries the join procedure and gets acks from seed2 first, and then joins to seed2 * */ -private[cluster] final class JoinSeedNodeProcess(seedNodes: immutable.IndexedSeq[Address]) extends Actor with ActorLogging { +@InternalApi +private[cluster] final class JoinSeedNodeProcess(seedNodes: immutable.IndexedSeq[Address], joinConfigCompatChecker: JoinConfigCompatChecker) extends Actor with ActorLogging { import InternalClusterAction._ import ClusterUserAction.JoinTo - def selfAddress = Cluster(context.system).selfAddress + val cluster = Cluster(context.system) + import cluster.settings._ + def selfAddress = cluster.selfAddress if (seedNodes.isEmpty || seedNodes.head == selfAddress) throw new IllegalArgumentException("Join seed node should not be done") - context.setReceiveTimeout(Cluster(context.system).settings.SeedNodeTimeout) + context.setReceiveTimeout(SeedNodeTimeout) var attempt = 0 + // all seed nodes, except this one + val otherSeedNodes = seedNodes.toSet - selfAddress + override def preStart(): Unit = self ! JoinSeedNode def receive = { case JoinSeedNode ⇒ + val requiredNonSensitiveKeys = JoinConfigCompatChecker.removeSensitiveKeys(joinConfigCompatChecker.requiredKeys, cluster.settings) + // configToValidate only contains the keys that are required according to JoinConfigCompatChecker on this node + val configToValidate = JoinConfigCompatChecker.filterWithKeys(requiredNonSensitiveKeys, context.system.settings.config) // send InitJoin to all seed nodes (except myself) attempt += 1 - seedNodes.collect { - case a if a != selfAddress ⇒ context.actorSelection(context.parent.path.toStringWithAddress(a)) - } foreach { _ ! InitJoin } - case InitJoinAck(address) ⇒ - // first InitJoinAck reply + otherSeedNodes.foreach { a ⇒ context.actorSelection(context.parent.path.toStringWithAddress(a)) ! InitJoin(configToValidate) } + + case InitJoinAck(address, CompatibleConfig(clusterConfig)) ⇒ + log.info("Received InitJoinAck message from [{}] to [{}]", sender(), selfAddress) + // validates config coming from cluster against this node config + joinConfigCompatChecker.check(clusterConfig, context.system.settings.config) match { + case Valid ⇒ + // first InitJoinAck reply + context.parent ! JoinTo(address) + context.become(done) + + case Invalid(messages) if ByPassConfigCompatCheck ⇒ + log.warning("Cluster validated this node config, but sent back incompatible settings: {}. " + + "Join will be performed because compatibility check is configured to not be enforced.", messages.mkString(", ")) + context.parent ! JoinTo(address) + context.become(done) + + case Invalid(messages) ⇒ + log.error("Cluster validated this node config, but sent back incompatible settings: {}. " + + "It's recommended to perform a full cluster shutdown in order to deploy this new version. " + + "If a cluster shutdown isn't an option, you may want to disable this protection by setting " + + "'akka.cluster.configuration-compatibility-check.enforce-on-join = off'. " + + "Note that disabling it will allow the formation of a cluster with nodes having incompatible configuration settings. " + + "This node will be shutdown!", messages.mkString(", ")) + context.stop(self) + CoordinatedShutdown(context.system).run(IncompatibleConfigurationDetected) + } + + case InitJoinAck(address, UncheckedConfig) ⇒ + log.warning("Joining a cluster without configuration compatibility check feature.") context.parent ! JoinTo(address) context.become(done) + + case InitJoinAck(address, IncompatibleConfig) ⇒ + // first InitJoinAck reply, but incompatible + if (ByPassConfigCompatCheck) { + log.info("Received InitJoinAck message from [{}] to [{}]", sender(), selfAddress) + log.warning("Joining cluster with incompatible configurations. " + + "Join will be performed because compatibility check is configured to not be enforced.") + // only join if set to ignore config validation + context.parent ! JoinTo(address) + context.become(done) + } else { + log.error( + "Couldn't join seed nodes because of incompatible cluster configuration. " + + "It's recommended to perform a full cluster shutdown in order to deploy this new version." + + "If a cluster shutdown isn't an option, you may want to disable this protection by setting " + + "'akka.cluster.configuration-compatibility-check.enforce-on-join = off'. " + + "Note that disabling it will allow the formation of a cluster with nodes having incompatible configuration settings. " + + "This node will be shutdown!") + context.stop(self) + CoordinatedShutdown(context.system).run(IncompatibleConfigurationDetected) + } + case InitJoinNack(_) ⇒ // that seed was uninitialized + case ReceiveTimeout ⇒ if (attempt >= 2) log.warning( @@ -1430,8 +1581,8 @@ private[cluster] final class JoinSeedNodeProcess(seedNodes: immutable.IndexedSeq } def done: Actor.Receive = { - case InitJoinAck(_) ⇒ // already received one, skip rest - case ReceiveTimeout ⇒ context.stop(self) + case InitJoinAck(_, _) ⇒ // already received one, skip rest + case ReceiveTimeout ⇒ context.stop(self) } } @@ -1440,6 +1591,7 @@ private[cluster] final class JoinSeedNodeProcess(seedNodes: immutable.IndexedSeq * * The supplied callback will be run, once, when current cluster member come up with the same status. */ +@InternalApi private[cluster] class OnMemberStatusChangedListener(callback: Runnable, status: MemberStatus) extends Actor with ActorLogging { import ClusterEvent._ private val cluster = Cluster(context.system) @@ -1487,6 +1639,7 @@ private[cluster] class OnMemberStatusChangedListener(callback: Runnable, status: /** * INTERNAL API */ +@InternalApi @SerialVersionUID(1L) private[cluster] final case class GossipStats( receivedGossipCount: Long = 0L, @@ -1530,8 +1683,8 @@ private[cluster] final case class GossipStats( /** * INTERNAL API */ +@InternalApi @SerialVersionUID(1L) private[cluster] final case class VectorClockStats( versionSize: Int = 0, seenLatest: Int = 0) - diff --git a/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala b/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala index c62dc93687..ce72da2bea 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ClusterSettings.scala @@ -147,9 +147,9 @@ final class ClusterSettings(val config: Config, val systemName: String) { val SelfDataCenter: DataCenter = cc.getString("multi-data-center.self-data-center") val Roles: Set[String] = { - val configuredRoles = (immutableSeq(cc.getStringList("roles")).toSet) requiring ( + val configuredRoles = immutableSeq(cc.getStringList("roles")).toSet requiring ( _.forall(!_.startsWith(DcRolePrefix)), - s"Roles must not start with '${DcRolePrefix}' as that is reserved for the cluster self-data-center setting") + s"Roles must not start with '$DcRolePrefix' as that is reserved for the cluster self-data-center setting") configuredRoles + s"$DcRolePrefix$SelfDataCenter" } @@ -175,6 +175,25 @@ final class ClusterSettings(val config: Config, val systemName: String) { val SchedulerTickDuration: FiniteDuration = cc.getMillisDuration("scheduler.tick-duration") val SchedulerTicksPerWheel: Int = cc.getInt("scheduler.ticks-per-wheel") + val ByPassConfigCompatCheck: Boolean = !cc.getBoolean("configuration-compatibility-check.enforce-on-join") + val ConfigCompatCheckers: Set[String] = { + import scala.collection.JavaConverters._ + cc.getConfig("configuration-compatibility-check.checkers") + .root.unwrapped.values().asScala + .map(_.toString).toSet + } + + val SensitiveConfigPaths = { + import scala.collection.JavaConverters._ + + val sensitiveKeys = + cc.getConfig("configuration-compatibility-check.sensitive-config-paths") + .root.unwrapped.values().asScala + .flatMap(_.asInstanceOf[java.util.List[String]].asScala) + + sensitiveKeys.toSet + } + object Debug { val VerboseHeartbeatLogging: Boolean = cc.getBoolean("debug.verbose-heartbeat-logging") val VerboseGossipLogging: Boolean = cc.getBoolean("debug.verbose-gossip-logging") diff --git a/akka-cluster/src/main/scala/akka/cluster/JoinConfigCompatCheckCluster.scala b/akka-cluster/src/main/scala/akka/cluster/JoinConfigCompatCheckCluster.scala new file mode 100644 index 0000000000..44e28f041d --- /dev/null +++ b/akka-cluster/src/main/scala/akka/cluster/JoinConfigCompatCheckCluster.scala @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2009-2018 Lightbend Inc. + */ +package akka.cluster + +import akka.annotation.InternalApi +import com.typesafe.config.Config + +import scala.collection.{ immutable ⇒ im } + +/** + * INTERNAL API + */ +@InternalApi +final class JoinConfigCompatCheckCluster extends JoinConfigCompatChecker { + + override def requiredKeys = im.Seq("akka.cluster.downing-provider-class") + + override def check(toCheck: Config, actualConfig: Config): ConfigValidation = + JoinConfigCompatChecker.fullMatch(requiredKeys, toCheck, actualConfig) +} diff --git a/akka-cluster/src/main/scala/akka/cluster/JoinConfigCompatChecker.scala b/akka-cluster/src/main/scala/akka/cluster/JoinConfigCompatChecker.scala new file mode 100644 index 0000000000..a7c702a442 --- /dev/null +++ b/akka-cluster/src/main/scala/akka/cluster/JoinConfigCompatChecker.scala @@ -0,0 +1,178 @@ +/** + * Copyright (C) 2009-2018 Lightbend Inc. + */ +package akka.cluster + +import java.util + +import akka.actor.ExtendedActorSystem +import akka.annotation.{ DoNotInherit, InternalApi } +import com.typesafe.config.{ Config, ConfigFactory, ConfigValue } + +import scala.collection.JavaConverters._ +import scala.collection.{ immutable ⇒ im } + +abstract class JoinConfigCompatChecker { + + /** The configuration keys that are required for this checker */ + def requiredKeys: im.Seq[String] + + /** + * Runs the Config check. + * + * Implementers are free to define what makes Config entry compatible or not. + * We do provide some pre-build checks tough: [[JoinConfigCompatChecker.exists()]] and [[JoinConfigCompatChecker.fullMatch()]] + * + * @param toCheck - the Config instance to be checked + * @param actualConfig - the Config instance containing the actual values + * @return a [[ConfigValidation]]. Can be [[Valid]] or [[Invalid]], the later must contain a descriptive list of error messages. + */ + def check(toCheck: Config, actualConfig: Config): ConfigValidation +} + +object JoinConfigCompatChecker { + + /** + * Checks that all `requiredKeys` are available in `toCheck` Config. + * + * @param requiredKeys - a Seq of required keys + * @param toCheck - the Config instance to be checked + */ + def exists(requiredKeys: im.Seq[String], toCheck: Config): ConfigValidation = { + val allKeys = toCheck.entrySet().asScala.map(_.getKey) + // return all not found required keys + val result = + requiredKeys.collect { + case requiredKey if !allKeys.contains(requiredKey) ⇒ requiredKey + " is missing" + } + + if (result.isEmpty) Valid + else Invalid(result.to[im.Seq]) + } + + /** + * Checks that all `requiredKeys` are available in `toCheck` Config + * and its values match exactly the values in `currentConfig`. + * + * @param requiredKeys - a Seq of required keys + * @param toCheck - the Config instance to be checked + * @param actualConfig - the Config instance containing the expected values + */ + def fullMatch(requiredKeys: im.Seq[String], toCheck: Config, actualConfig: Config): ConfigValidation = { + + def checkEquality = { + + def checkCompat(entry: util.Map.Entry[String, ConfigValue]) = { + val key = entry.getKey + actualConfig.hasPath(key) && actualConfig.getValue(key) == entry.getValue + } + + // retrieve all incompatible keys + // NOTE: we only check the key if effectively required + // because config may contain more keys than required for this checker + val incompatibleKeys = + toCheck.entrySet().asScala + .collect { + case entry if requiredKeys.contains(entry.getKey) && !checkCompat(entry) ⇒ s"${entry.getKey} is incompatible" + } + + if (incompatibleKeys.isEmpty) Valid + else Invalid(incompatibleKeys.to[im.Seq]) + } + + exists(requiredKeys, toCheck) ++ checkEquality + } + + /** + * INTERNAL API + * Builds a new Config object containing only the required entries defined by `requiredKeys` + * + * This method is used from the joining side to prepare the [[Config]] instance that will be sent over the wire. + * We don't send the full config to avoid unnecessary data transfer, but also to avoid leaking any sensitive + * information that users may have added to their configuration. + */ + @InternalApi + private[cluster] def filterWithKeys(requiredKeys: im.Seq[String], config: Config): Config = { + + val filtered = + config.entrySet().asScala + .collect { + case e if requiredKeys.contains(e.getKey) ⇒ (e.getKey, e.getValue) + } + + ConfigFactory.parseMap(filtered.toMap.asJava) + } + + /** + * INTERNAL API + * Removes sensitive keys, as defined in 'akka.cluster.configuration-compatibility-check.sensitive-config-paths', + * from the passed `requiredKeys` Seq. + */ + @InternalApi + private[cluster] def removeSensitiveKeys(requiredKeys: im.Seq[String], clusterSettings: ClusterSettings): im.Seq[String] = { + requiredKeys.filter { key ⇒ + !clusterSettings.SensitiveConfigPaths.exists(s ⇒ key.startsWith(s)) + } + } + + /** + * INTERNAL API + * Builds a Seq of keys using the passed `Config` not including any sensitive keys, + * as defined in 'akka.cluster.configuration-compatibility-check.sensitive-config-paths'. + */ + @InternalApi + private[cluster] def removeSensitiveKeys(config: Config, clusterSettings: ClusterSettings): im.Seq[String] = { + val existingKeys = config.entrySet().asScala.map(_.getKey).to[im.Seq] + removeSensitiveKeys(existingKeys, clusterSettings) + } + + /** + * INTERNAL API + * + * This method loads the [[JoinConfigCompatChecker]] defined in the configuration. + * Checkers are then combined to be used whenever a join node tries to join an existing cluster. + */ + @InternalApi + private[cluster] def load(system: ExtendedActorSystem, clusterSettings: ClusterSettings): JoinConfigCompatChecker = { + + val checkers = + clusterSettings.ConfigCompatCheckers.map { fqcn ⇒ + system.dynamicAccess + .createInstanceFor[JoinConfigCompatChecker](fqcn, im.Seq.empty) + .get // can't continue if we can't load it + } + + // composite checker + new JoinConfigCompatChecker { + override val requiredKeys: im.Seq[String] = checkers.flatMap(_.requiredKeys).to[im.Seq] + override def check(toValidate: Config, clusterConfig: Config): ConfigValidation = + checkers.foldLeft(Valid: ConfigValidation) { (acc, checker) ⇒ + acc ++ checker.check(toValidate, clusterConfig) + } + } + } +} + +@DoNotInherit +sealed trait ConfigValidation { + + def ++(that: ConfigValidation) = concat(that) + + def concat(that: ConfigValidation) = { + (this, that) match { + case (Invalid(a), Invalid(b)) ⇒ Invalid(a ++ b) + case (_, i @ Invalid(_)) ⇒ i + case (i @ Invalid(_), _) ⇒ i + case _ ⇒ Valid + } + } +} + +case object Valid extends ConfigValidation { + /** + * Java API: get the singleton instance + */ + def getInstance = this +} + +final case class Invalid(errorMessages: im.Seq[String]) extends ConfigValidation diff --git a/akka-cluster/src/main/scala/akka/cluster/protobuf/ClusterMessageSerializer.scala b/akka-cluster/src/main/scala/akka/cluster/protobuf/ClusterMessageSerializer.scala index 8bee60cc1f..01161f9523 100644 --- a/akka-cluster/src/main/scala/akka/cluster/protobuf/ClusterMessageSerializer.scala +++ b/akka-cluster/src/main/scala/akka/cluster/protobuf/ClusterMessageSerializer.scala @@ -18,9 +18,10 @@ import scala.collection.JavaConverters._ import scala.concurrent.duration.Deadline import java.io.NotSerializableException -import akka.cluster.InternalClusterAction.ExitingConfirmed +import akka.cluster.InternalClusterAction._ import akka.cluster.routing.{ ClusterRouterPool, ClusterRouterPoolSettings } import akka.routing.Pool +import com.typesafe.config.{ Config, ConfigFactory, ConfigRenderOptions } /** * Protobuf serializer of cluster messages. @@ -40,7 +41,7 @@ class ClusterMessageSerializer(val system: ExtendedActorSystem) extends BaseSeri val roles = Set.empty[String] ++ m.getRolesList.asScala InternalClusterAction.Join( uniqueAddressFromProto(m.getNode), - if (roles.find(_.startsWith(ClusterSettings.DcRolePrefix)).isDefined) roles + if (roles.exists(_.startsWith(ClusterSettings.DcRolePrefix))) roles else roles + (ClusterSettings.DcRolePrefix + ClusterSettings.DefaultDataCenter) ) }, @@ -51,8 +52,28 @@ class ClusterMessageSerializer(val system: ExtendedActorSystem) extends BaseSeri }, classOf[ClusterUserAction.Leave] → (bytes ⇒ ClusterUserAction.Leave(addressFromBinary(bytes))), classOf[ClusterUserAction.Down] → (bytes ⇒ ClusterUserAction.Down(addressFromBinary(bytes))), - InternalClusterAction.InitJoin.getClass → (_ ⇒ InternalClusterAction.InitJoin), - classOf[InternalClusterAction.InitJoinAck] → (bytes ⇒ InternalClusterAction.InitJoinAck(addressFromBinary(bytes))), + classOf[InternalClusterAction.InitJoin] → { + case bytes ⇒ + val m = cm.InitJoin.parseFrom(bytes) + if (m.hasCurrentConfig) + InternalClusterAction.InitJoin(ConfigFactory.parseString(m.getCurrentConfig)) + else + InternalClusterAction.InitJoin(ConfigFactory.empty) + }, + classOf[InternalClusterAction.InitJoinAck] → { + case bytes ⇒ + val i = cm.InitJoinAck.parseFrom(bytes) + val configCheck = + if (i.hasConfigCheck) { + i.getConfigCheck.getType match { + case cm.ConfigCheck.Type.CompatibleConfig ⇒ CompatibleConfig(ConfigFactory.parseString(i.getConfigCheck.getClusterConfig)) + case cm.ConfigCheck.Type.IncompatibleConfig ⇒ IncompatibleConfig + case cm.ConfigCheck.Type.UncheckedConfig ⇒ UncheckedConfig + } + } else UncheckedConfig + + InternalClusterAction.InitJoinAck(addressFromProto(i.getAddress), configCheck) + }, classOf[InternalClusterAction.InitJoinNack] → (bytes ⇒ InternalClusterAction.InitJoinNack(addressFromBinary(bytes))), classOf[ClusterHeartbeatSender.Heartbeat] → (bytes ⇒ ClusterHeartbeatSender.Heartbeat(addressFromBinary(bytes))), classOf[ClusterHeartbeatSender.HeartbeatRsp] → (bytes ⇒ ClusterHeartbeatSender.HeartbeatRsp(uniqueAddressFromBinary(bytes))), @@ -65,19 +86,19 @@ class ClusterMessageSerializer(val system: ExtendedActorSystem) extends BaseSeri def includeManifest: Boolean = true def toBinary(obj: AnyRef): Array[Byte] = obj match { - case ClusterHeartbeatSender.Heartbeat(from) ⇒ addressToProtoByteArray(from) - case ClusterHeartbeatSender.HeartbeatRsp(from) ⇒ uniqueAddressToProtoByteArray(from) - case m: GossipEnvelope ⇒ gossipEnvelopeToProto(m).toByteArray - case m: GossipStatus ⇒ gossipStatusToProto(m).toByteArray - case InternalClusterAction.Join(node, roles) ⇒ joinToProto(node, roles).toByteArray - case InternalClusterAction.Welcome(from, gossip) ⇒ compress(welcomeToProto(from, gossip)) - case ClusterUserAction.Leave(address) ⇒ addressToProtoByteArray(address) - case ClusterUserAction.Down(address) ⇒ addressToProtoByteArray(address) - case InternalClusterAction.InitJoin ⇒ cm.Empty.getDefaultInstance.toByteArray - case InternalClusterAction.InitJoinAck(address) ⇒ addressToProtoByteArray(address) - case InternalClusterAction.InitJoinNack(address) ⇒ addressToProtoByteArray(address) - case InternalClusterAction.ExitingConfirmed(node) ⇒ uniqueAddressToProtoByteArray(node) - case rp: ClusterRouterPool ⇒ clusterRouterPoolToProtoByteArray(rp) + case ClusterHeartbeatSender.Heartbeat(from) ⇒ addressToProtoByteArray(from) + case ClusterHeartbeatSender.HeartbeatRsp(from) ⇒ uniqueAddressToProtoByteArray(from) + case m: GossipEnvelope ⇒ gossipEnvelopeToProto(m).toByteArray + case m: GossipStatus ⇒ gossipStatusToProto(m).toByteArray + case InternalClusterAction.Join(node, roles) ⇒ joinToProto(node, roles).toByteArray + case InternalClusterAction.Welcome(from, gossip) ⇒ compress(welcomeToProto(from, gossip)) + case ClusterUserAction.Leave(address) ⇒ addressToProtoByteArray(address) + case ClusterUserAction.Down(address) ⇒ addressToProtoByteArray(address) + case InternalClusterAction.InitJoin(config) ⇒ initJoinToProto(config).toByteArray + case InternalClusterAction.InitJoinAck(address, configCheck) ⇒ initJoinAckToProto(address, configCheck).toByteArray + case InternalClusterAction.InitJoinNack(address) ⇒ addressToProtoByteArray(address) + case InternalClusterAction.ExitingConfirmed(node) ⇒ uniqueAddressToProtoByteArray(node) + case rp: ClusterRouterPool ⇒ clusterRouterPoolToProtoByteArray(rp) case _ ⇒ throw new IllegalArgumentException(s"Can't serialize object of type ${obj.getClass}") } @@ -245,6 +266,33 @@ class ClusterMessageSerializer(val system: ExtendedActorSystem) extends BaseSeri private def joinToProto(node: UniqueAddress, roles: Set[String]): cm.Join = cm.Join.newBuilder().setNode(uniqueAddressToProto(node)).addAllRoles(roles.asJava).build() + private def initJoinToProto(currentConfig: Config): cm.InitJoin = { + cm.InitJoin.newBuilder() + .setCurrentConfig(currentConfig.root.render(ConfigRenderOptions.concise)) + .build() + } + + private def initJoinAckToProto(address: Address, configCheck: ConfigCheck): cm.InitJoinAck = { + + val configCheckBuilder = cm.ConfigCheck.newBuilder() + configCheck match { + case UncheckedConfig ⇒ + configCheckBuilder.setType(cm.ConfigCheck.Type.UncheckedConfig) + + case IncompatibleConfig ⇒ + configCheckBuilder.setType(cm.ConfigCheck.Type.IncompatibleConfig) + + case CompatibleConfig(conf) ⇒ + configCheckBuilder + .setType(cm.ConfigCheck.Type.CompatibleConfig) + .setClusterConfig(conf.root.render(ConfigRenderOptions.concise)) + } + cm.InitJoinAck.newBuilder(). + setAddress(addressToProto(address)). + setConfigCheck(configCheckBuilder.build()). + build() + } + private def welcomeToProto(from: UniqueAddress, gossip: Gossip): cm.Welcome = cm.Welcome.newBuilder().setFrom(uniqueAddressToProto(from)).setGossip(gossipToProto(gossip)).build() @@ -258,6 +306,7 @@ class ClusterMessageSerializer(val system: ExtendedActorSystem) extends BaseSeri val hashMapping = allHashes.zipWithIndex.toMap def mapUniqueAddress(uniqueAddress: UniqueAddress): Integer = mapWithErrorMessage(addressMapping, uniqueAddress, "address") + def mapRole(role: String): Integer = mapWithErrorMessage(roleMapping, role, "role") def memberToProto(member: Member) = @@ -419,7 +468,11 @@ class ClusterMessageSerializer(val system: ExtendedActorSystem) extends BaseSeri totalInstances = crps.getTotalInstances, maxInstancesPerNode = crps.getMaxInstancesPerNode, allowLocalRoutees = crps.getAllowLocalRoutees, - useRoles = if (crps.hasUseRole) { crps.getUseRolesList.asScala.toSet + crps.getUseRole } else { crps.getUseRolesList.asScala.toSet } + useRoles = if (crps.hasUseRole) { + crps.getUseRolesList.asScala.toSet + crps.getUseRole + } else { + crps.getUseRolesList.asScala.toSet + } ) } diff --git a/akka-cluster/src/test/scala/akka/cluster/ClusterSpec.scala b/akka-cluster/src/test/scala/akka/cluster/ClusterSpec.scala index 10694aedf5..02ae1292b6 100644 --- a/akka-cluster/src/test/scala/akka/cluster/ClusterSpec.scala +++ b/akka-cluster/src/test/scala/akka/cluster/ClusterSpec.scala @@ -73,7 +73,7 @@ class ClusterSpec extends AkkaSpec(ClusterSpec.config) with ImplicitSender { awaitAssert(clusterView.status should ===(MemberStatus.Up)) } - "publish inital state as snapshot to subscribers" in { + "publish initial state as snapshot to subscribers" in { try { cluster.subscribe(testActor, ClusterEvent.InitialStateAsSnapshot, classOf[ClusterEvent.MemberEvent]) expectMsgClass(classOf[ClusterEvent.CurrentClusterState]) @@ -82,7 +82,7 @@ class ClusterSpec extends AkkaSpec(ClusterSpec.config) with ImplicitSender { } } - "publish inital state as events to subscribers" in { + "publish initial state as events to subscribers" in { try { cluster.subscribe(testActor, ClusterEvent.InitialStateAsEvents, classOf[ClusterEvent.MemberEvent]) expectMsgClass(classOf[ClusterEvent.MemberUp]) diff --git a/akka-cluster/src/test/scala/akka/cluster/ClusterTestKit.scala b/akka-cluster/src/test/scala/akka/cluster/ClusterTestKit.scala new file mode 100644 index 0000000000..322393f79b --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/ClusterTestKit.scala @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2009-2018 Lightbend Inc. + */ +package akka.cluster + +import akka.actor.{ ActorSystem, Address } +import akka.cluster.MemberStatus.Removed +import akka.testkit.TestKitBase +import com.typesafe.config.{ Config, ConfigFactory } + +import scala.collection.{ immutable ⇒ im } + +/** + * Builds on TestKitBase to provide some extra utilities to run cluster test. + * + * All functionality is provided through ClusterTest stateful class. + */ +trait ClusterTestKit extends TestKitBase { + + /** + * Cluster test util to help manage ActorSystem creation and cluster formation. + * Useful for writing single jvm, but multi ActorSystem tests. + * + * NOTE: This class is stateful and not thread safe. A new instance must be created per test method. + */ + class ClusterTestUtil(val name: String) { + + private var actorSystems: List[ActorSystem] = List.empty + + /** + * Register an [[ActorSystem]]. + */ + def register(actorSystem: ActorSystem) = { + actorSystems = actorSystems :+ actorSystem + actorSystem + } + + /** + * Register an [[ActorSystem]]. + * + * The [ActorSystem]] will be prepended to list and be considered the first node + */ + def registerAsFirst(actorSystem: ActorSystem) = { + actorSystems = actorSystem +: actorSystems + actorSystem + } + + /** + * Creates a new [[ActorSystem]] using the passed [[Config]] and register it. + */ + def newActorSystem(config: Config): ActorSystem = + register(ActorSystem(name, config)) + + /** + * Creates a new [[ActorSystem]] using the passed [[Config]] and register it. + * + * This newly created [[ActorSystem]] will be prepended to list and be considered the first node + */ + def newActorSystemAsFirst(config: Config): ActorSystem = + registerAsFirst(ActorSystem(name, config)) + + /** + * Create a cluster using the registered [[ActorSystem]]s. + * + * The head of the list is considered the first node and will first create a cluster with itself. + * Other nodes will join the cluster after the first one. + */ + def formCluster(): Unit = { + require(actorSystems.nonEmpty, "Can't form a cluster with an empty list of ActorSystems") + + val firstCluster = Cluster(actorSystems.head) + firstCluster.join(firstCluster.selfAddress) + + val firstNode = firstCluster.readView + awaitCond(firstNode.isSingletonCluster) + + // let the others join + actorSystems + .drop(1) // <- safe tail access + .foreach(joinCluster) + } + + /** + * Makes the passed [[ActorSystem]] joins the cluster. + * The passed system must have been previously registered on this [[ClusterTestUtil]]. + */ + def joinCluster(actorSystem: ActorSystem): Unit = { + require(isRegistered(actorSystem), "Unknown actor system") + + val addresses = actorSystems.map(s ⇒ Cluster(s).selfAddress) + + val joiningNodeCluster = Cluster(actorSystem) + joiningNodeCluster.joinSeedNodes(addresses) + } + + private def isRegistered(actorSystem: ActorSystem): Boolean = + actorSystems.contains(actorSystem) + + /** Shuts down all registered [[ActorSystem]]s */ + def shutdownAll(): Unit = actorSystems.foreach(sys ⇒ shutdown(sys)) + + /** + * Force the passed [[ActorSystem]] to quit the cluster and shutdown. + * Once original system is removed, a new [[ActorSystem]] is started using the same address. + */ + def quitAndRestart(actorSystem: ActorSystem, config: Config) = { + require(isRegistered(actorSystem), "Unknown actor system") + + // is this first seed node? + val firstSeedNode = actorSystems.headOption.contains(actorSystem) + + val cluster = Cluster(actorSystem) + val port = cluster.selfAddress.port.get + + // remove old before starting the new one + cluster.leave(cluster.readView.selfAddress) + awaitCond(cluster.readView.status == Removed, message = s"awaiting node [${cluster.readView.selfAddress}] to be 'Removed'") + + shutdown(actorSystem) + awaitCond(cluster.isTerminated) + + // remove from internal list + actorSystems = actorSystems.filterNot(_ == actorSystem) + + val newConfig = ConfigFactory.parseString( + s""" + akka.remote.netty.tcp.port = $port + akka.remote.artery.canonical.port = $port + """ + ).withFallback(config) + + if (firstSeedNode) newActorSystemAsFirst(newConfig) + else newActorSystem(newConfig) + + } + + } + +} diff --git a/akka-cluster/src/test/scala/akka/cluster/JoinConfigCompatCheckerSpec.scala b/akka-cluster/src/test/scala/akka/cluster/JoinConfigCompatCheckerSpec.scala new file mode 100644 index 0000000000..669a56ba67 --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/JoinConfigCompatCheckerSpec.scala @@ -0,0 +1,655 @@ +/** + * Copyright (C) 2009-2018 Lightbend Inc. + */ +package akka.cluster + +import akka.actor.{ ActorSystem, Address } +import akka.cluster.InternalClusterAction.LeaderActionsTick +import akka.cluster.MemberStatus.{ Removed, Up } +import akka.testkit.{ AkkaSpec, LongRunningTest } +import com.typesafe.config.{ Config, ConfigFactory } +import org.scalatest.{ Matchers, WordSpec } + +import scala.concurrent.duration._ +import scala.collection.JavaConverters._ +import scala.collection.{ immutable ⇒ im } + +object JoinConfigCompatCheckerSpec { + +} + +class JoinConfigCompatCheckerSpec extends AkkaSpec() with ClusterTestKit { + + "A Joining Node" must { + + "be allowed to join a cluster when its configuration is compatible" taggedAs LongRunningTest in { + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(configWithChecker) + val joiningNode = clusterTestUtil.newActorSystem(configWithChecker) + clusterTestUtil.formCluster() + + try { + awaitCond(Cluster(joiningNode).readView.status == Up, message = "awaiting joining node to be 'Up'") + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "NOT be allowed to join a cluster when its configuration is incompatible" taggedAs LongRunningTest in { + // this config is NOT compatible with the cluster config + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is incompatible + config-compat-test = "test2" + configuration-compatibility-check { + enforce-on-join = on + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + } + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(configWithChecker) + val joiningNode = clusterTestUtil.newActorSystem(joinNodeConfig.withFallback(configWithChecker)) + + clusterTestUtil.formCluster() + + try { + + // node will shutdown after unsuccessful join attempt + within(5.seconds) { + awaitCond(Cluster(joiningNode).readView.isTerminated) + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "NOT be allowed to join a cluster when one of its required properties are not available on cluster side" taggedAs LongRunningTest in { + + // this config is NOT compatible with the cluster config + // because there is one missing required configuration property. + // This test verifies that cluster config are being sent back and checked on joining node as well + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is not available on cluster side + akka.cluster.config-compat-test-extra = on + + configuration-compatibility-check { + enforce-on-join = on + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + akka-cluster-extra = "akka.cluster.JoinConfigCompatCheckerExtraTest" + } + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(configWithChecker) + val joiningNode = clusterTestUtil.newActorSystem(joinNodeConfig.withFallback(configWithChecker)) + + clusterTestUtil.formCluster() + + try { + // node will shutdown after unsuccessful join attempt + within(5.seconds) { + awaitCond(Cluster(joiningNode).readView.isTerminated) + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "NOT be allowed to join a cluster when one of the cluster required properties are not available on the joining side" taggedAs LongRunningTest in { + + // this config is NOT compatible with the cluster config + // because there is one missing required configuration property. + // This test verifies that cluster config are being sent back and checked on joining node as well + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is required on cluster side + # config-compat-test = "test" + + configuration-compatibility-check { + enforce-on-join = on + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(configWithChecker) + val joiningNode = clusterTestUtil.newActorSystem(joinNodeConfig.withFallback(baseConfig)) + + clusterTestUtil.formCluster() + + try { + // node will shutdown after unsuccessful join attempt + within(5.seconds) { + awaitCond(Cluster(joiningNode).readView.isTerminated) + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "be allowed to join a cluster when one of its required properties are not available on cluster side but it's configured to NOT enforce it" taggedAs LongRunningTest in { + + // this config is NOT compatible with the cluster config + // because there is one missing required configuration property. + // This test verifies that validation on joining side takes 'configuration-compatibility-check.enforce-on-join' in consideration + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is not available on cluster side + akka.cluster.config-compat-test-extra = on + + configuration-compatibility-check { + enforce-on-join = off + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + akka-cluster-extra = "akka.cluster.JoinConfigCompatCheckerExtraTest" + } + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(configWithChecker) + val joiningNode = clusterTestUtil.newActorSystem(joinNodeConfig.withFallback(configWithChecker)) + + clusterTestUtil.formCluster() + + try { + // join with compatible node + awaitCond(Cluster(joiningNode).readView.status == Up, message = "awaiting joining node to be 'Up'") + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "be allowed to join a cluster when its configuration is incompatible but it's configured to NOT enforce it" taggedAs LongRunningTest in { + // this config is NOT compatible with the cluster config, + // but node will ignore the the config check and join anyway + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + configuration-compatibility-check { + # not enforcing config compat check + enforce-on-join = off + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + } + } + # this config is incompatible + config-compat-test = "test2" + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(configWithChecker) + val joiningNode = clusterTestUtil.newActorSystem(joinNodeConfig.withFallback(configWithChecker)) + + clusterTestUtil.formCluster() + + try { + // join with compatible node + awaitCond(Cluster(joiningNode).readView.status == Up, message = "awaiting joining node to be 'Up'") + } finally { + clusterTestUtil.shutdownAll() + } + } + + /** This test verifies the built-in JoinConfigCompatCheckerAkkaCluster */ + "NOT be allowed to join a cluster using a different value for akka.cluster.downing-provider-class" taggedAs LongRunningTest in { + + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # using explicit downing provider class + downing-provider-class = "akka.cluster.AutoDowning" + + configuration-compatibility-check { + enforce-on-join = on + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(baseConfig) + val joiningNode = clusterTestUtil.newActorSystem(joinNodeConfig.withFallback(baseConfig)) + + clusterTestUtil.formCluster() + + try { + // node will shutdown after unsuccessful join attempt + within(5.seconds) { + awaitCond(Cluster(joiningNode).readView.isTerminated) + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + } + + "A First Node" must { + + "be allowed to re-join a cluster when its configuration is compatible" taggedAs LongRunningTest in { + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + val firstNode = clusterTestUtil.newActorSystem(configWithChecker) + // second node + val secondNode = clusterTestUtil.newActorSystem(configWithChecker) + + clusterTestUtil.formCluster() + + try { + // we must wait second node to join the cluster before shutting down the first node + awaitCond(Cluster(secondNode).readView.status == Up, message = "awaiting second node to be 'Up'") + + val restartedNode = clusterTestUtil.quitAndRestart(firstNode, configWithChecker) + clusterTestUtil.joinCluster(restartedNode) + + within(20.seconds) { + awaitCond(Cluster(restartedNode).readView.status == Up, message = "awaiting restarted first node to be 'Up'") + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "NOT be allowed to re-join a cluster when its configuration is incompatible" taggedAs LongRunningTest in { + // this config is NOT compatible with the cluster config + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is incompatible + config-compat-test = "test2" + configuration-compatibility-check { + enforce-on-join = on + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + } + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + val firstNode = clusterTestUtil.newActorSystem(configWithChecker) + // second node + val secondNode = clusterTestUtil.newActorSystem(configWithChecker) + + clusterTestUtil.formCluster() + + try { + // we must wait second node to join the cluster before shutting down the first node + awaitCond(Cluster(secondNode).readView.status == Up, message = "awaiting second node to be 'Up'") + + val restartedNode = clusterTestUtil.quitAndRestart(firstNode, joinNodeConfig.withFallback(configWithChecker)) + clusterTestUtil.joinCluster(restartedNode) + + // node will shutdown after unsuccessful join attempt + within(20.seconds) { + awaitCond(Cluster(restartedNode).readView.isTerminated) + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "NOT be allowed to re-join a cluster when one of its required properties are not available on cluster side" taggedAs LongRunningTest in { + + // this config is NOT compatible with the cluster config + // because there is one missing required configuration property. + // This test verifies that cluster config are being sent back and checked on joining node as well + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is not available on cluster side + akka.cluster.config-compat-test-extra = on + + configuration-compatibility-check { + enforce-on-join = on + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + akka-cluster-extra = "akka.cluster.JoinConfigCompatCheckerExtraTest" + } + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + val firstNode = clusterTestUtil.newActorSystem(configWithChecker) + // second node + val secondNode = clusterTestUtil.newActorSystem(configWithChecker) + + clusterTestUtil.formCluster() + + try { + // we must wait second node to join the cluster before shutting down the first node + awaitCond(Cluster(secondNode).readView.status == Up, message = "awaiting second node to be 'Up'") + + val restartedNode = clusterTestUtil.quitAndRestart(firstNode, joinNodeConfig.withFallback(configWithChecker)) + clusterTestUtil.joinCluster(restartedNode) + + // node will shutdown after unsuccessful join attempt + within(20.seconds) { + awaitCond(Cluster(restartedNode).readView.isTerminated) + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "NOT be allowed to re-join a cluster when one of the cluster required properties are not available on the joining side" taggedAs LongRunningTest in { + + // this config is NOT compatible with the cluster config + // because there is one missing required configuration property. + // This test verifies that cluster config are being sent back and checked on joining node as well + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is required on cluster side + # config-compat-test = "test" + + configuration-compatibility-check { + enforce-on-join = on + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + val firstNode = clusterTestUtil.newActorSystem(configWithChecker) + // second node + val secondNode = clusterTestUtil.newActorSystem(configWithChecker) + + clusterTestUtil.formCluster() + + try { + // we must wait second node to join the cluster before shutting down the first node + awaitCond(Cluster(secondNode).readView.status == Up, message = "awaiting second node to be 'Up'") + + val restartedNode = clusterTestUtil.quitAndRestart(firstNode, joinNodeConfig.withFallback(baseConfig)) + clusterTestUtil.joinCluster(restartedNode) + + // node will shutdown after unsuccessful join attempt + within(20.seconds) { + awaitCond(Cluster(restartedNode).readView.isTerminated) + } + + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "be allowed to re-join a cluster when one of its required properties are not available on cluster side but it's configured to NOT enforce it" taggedAs LongRunningTest in { + + // this config is NOT compatible with the cluster config + // because there is one missing required configuration property. + // This test verifies that validation on joining side takes 'configuration-compatibility-check.enforce-on-join' in consideration + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # this config is not available on cluster side + akka.cluster.config-compat-test-extra = on + + configuration-compatibility-check { + enforce-on-join = off + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + akka-cluster-extra = "akka.cluster.JoinConfigCompatCheckerExtraTest" + } + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + val firstNode = clusterTestUtil.newActorSystem(configWithChecker) + // second node + val secondNode = clusterTestUtil.newActorSystem(configWithChecker) + + clusterTestUtil.formCluster() + + try { + // join with compatible node + // we must wait second node to join the cluster before shutting down the first node + awaitCond(Cluster(secondNode).readView.status == Up, message = "awaiting second node to be 'Up'") + + val restartedNode = clusterTestUtil.quitAndRestart(firstNode, joinNodeConfig.withFallback(configWithChecker)) + clusterTestUtil.joinCluster(restartedNode) + + // node will will have joined the cluster + within(20.seconds) { + awaitCond(Cluster(restartedNode).readView.status == Up, message = "awaiting restarted node to be 'Up'") + } + } finally { + clusterTestUtil.shutdownAll() + } + + } + + "be allowed to re-join a cluster when its configuration is incompatible but it's configured to NOT enforce it" taggedAs LongRunningTest in { + // this config is NOT compatible with the cluster config, + // but node will ignore the the config check and join anyway + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + configuration-compatibility-check { + # not enforcing config compat check + enforce-on-join = off + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + } + } + # this config is incompatible + config-compat-test = "test2" + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + val firstNode = clusterTestUtil.newActorSystem(configWithChecker) + // second node + val secondNode = clusterTestUtil.newActorSystem(configWithChecker) + + clusterTestUtil.formCluster() + + try { + // join with compatible node + // we must wait second node to join the cluster before shutting down the first node + awaitCond(Cluster(secondNode).readView.status == Up, message = "awaiting second node to be 'Up'") + + val restartedNode = clusterTestUtil.quitAndRestart(firstNode, joinNodeConfig.withFallback(configWithChecker)) + clusterTestUtil.joinCluster(restartedNode) + + // node will will have joined the cluster + within(20.seconds) { + awaitCond(Cluster(restartedNode).readView.status == Up, message = "awaiting restarted node to be 'Up'") + } + } finally { + clusterTestUtil.shutdownAll() + } + } + + } + + "A Cluster" must { + "NOT exchange sensitive config paths with joining node" taggedAs LongRunningTest in { + + // this config has sensitive properties that are not compatible with the cluster + // the cluster will ignore them, because they are on the sensitive-config-path + // the cluster won't let it be leaked back to the joining node neither which will fail the join attempt. + val joinNodeConfig = + ConfigFactory.parseString( + """ + akka.cluster { + + # these config are compatible, + # but won't be leaked back to joining node which will cause it to fail to join + sensitive.properties { + username = "abc" + password = "def" + } + + configuration-compatibility-check { + enforce-on-join = on + checkers { + # rogue checker to trick the cluster to leak sensitive data + rogue-checker = "akka.cluster.RogueJoinConfigCompatCheckerTest" + } + + # unset sensitive config paths + # this will allow the joining node to leak sensitive info and try + # get back these same properties from the cluster + sensitive-config-paths { + akka = [] + } + } + } + """ + ) + + val clusterTestUtil = new ClusterTestUtil(system.name) + // first node + clusterTestUtil.newActorSystem(configWithChecker) + val joiningNode = clusterTestUtil.newActorSystem(joinNodeConfig.withFallback(configWithChecker)) + + clusterTestUtil.formCluster() + + try { + // node will shutdown after unsuccessful join attempt + within(5.seconds) { + awaitCond(Cluster(joiningNode).readView.isTerminated) + } + } finally { + clusterTestUtil.shutdownAll() + } + } + } + + val baseConfig: Config = + ConfigFactory.parseString( + """ + akka.actor.provider = "cluster" + akka.coordinated-shutdown.terminate-actor-system = on + akka.remote.netty.tcp.port = 0 + akka.remote.artery.canonical.port = 0 + """ + ) + + val configWithChecker: Config = + ConfigFactory.parseString( + """ + akka.cluster { + config-compat-test = "test" + sensitive.properties { + username = "abc" + password = "def" + } + + configuration-compatibility-check { + enforce-on-join = on + checkers { + akka-cluster-test = "akka.cluster.JoinConfigCompatCheckerTest" + } + sensitive-config-paths { + akka = [ "akka.cluster.sensitive.properties" ] + } + } + } + """ + ).withFallback(baseConfig) + +} + +class JoinConfigCompatCheckerTest extends JoinConfigCompatChecker { + override def requiredKeys = im.Seq("akka.cluster.config-compat-test") + + override def check(toValidate: Config, actualConfig: Config): ConfigValidation = + JoinConfigCompatChecker.fullMatch(requiredKeys, toValidate, actualConfig) +} + +class JoinConfigCompatCheckerExtraTest extends JoinConfigCompatChecker { + override def requiredKeys = im.Seq("akka.cluster.config-compat-test-extra") + + override def check(toValidate: Config, actualConfig: Config): ConfigValidation = + JoinConfigCompatChecker.fullMatch(requiredKeys, toValidate, actualConfig) +} + +/** Rogue checker that tries to leak sensitive information */ +class RogueJoinConfigCompatCheckerTest extends JoinConfigCompatChecker { + + override def requiredKeys = im.Seq("akka.cluster.sensitive.properties.password", "akka.cluster.sensitive.properties.username") + + /** this check always returns Valid. The goal is to try to make the cluster leak those properties */ + override def check(toValidate: Config, actualConfig: Config): ConfigValidation = + JoinConfigCompatChecker.fullMatch(requiredKeys, toValidate, actualConfig) +} diff --git a/akka-cluster/src/test/scala/akka/cluster/JoinConfigCompatPreDefinedChecksSpec.scala b/akka-cluster/src/test/scala/akka/cluster/JoinConfigCompatPreDefinedChecksSpec.scala new file mode 100644 index 0000000000..6c107e4de6 --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/JoinConfigCompatPreDefinedChecksSpec.scala @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2009-2018 Lightbend Inc. + */ +package akka.cluster + +import com.typesafe.config.{ Config, ConfigFactory } +import org.scalatest.{ Matchers, WordSpec } + +import scala.collection.{ immutable ⇒ im } + +class JoinConfigCompatPreDefinedChecksSpec extends WordSpec with Matchers { + + // Test for some of the pre-build helpers we offer + "JoinConfigCompatChecker.exists" must { + + val requiredKeys = im.Seq( + "akka.cluster.min-nr-of-members", + "akka.cluster.retry-unsuccessful-join-after", + "akka.cluster.allow-weakly-up-members" + ) + + "pass when all required keys are provided" in { + + val result = + JoinConfigCompatChecker.exists( + requiredKeys, + config( + """ + |{ + | akka.cluster.min-nr-of-members = 1 + | akka.cluster.retry-unsuccessful-join-after = 10s + | akka.cluster.allow-weakly-up-members = on + |} + """.stripMargin) + ) + + result shouldBe Valid + } + + "fail when some required keys are NOT provided" in { + + val Invalid(incompatibleKeys) = + JoinConfigCompatChecker.exists( + requiredKeys, + config( + """ + |{ + | akka.cluster.min-nr-of-members = 1 + |} + """.stripMargin) + ) + + incompatibleKeys should have size 2 + incompatibleKeys should contain("akka.cluster.retry-unsuccessful-join-after is missing") + incompatibleKeys should contain("akka.cluster.allow-weakly-up-members is missing") + } + } + + "JoinConfigCompatChecker.fullMatch" must { + + val requiredKeys = im.Seq( + "akka.cluster.min-nr-of-members", + "akka.cluster.retry-unsuccessful-join-after", + "akka.cluster.allow-weakly-up-members" + ) + + val clusterConfig = + config( + """ + |{ + | akka.cluster.min-nr-of-members = 1 + | akka.cluster.retry-unsuccessful-join-after = 10s + | akka.cluster.allow-weakly-up-members = on + |} + """.stripMargin) + + "pass when all required keys are provided and all match cluster config" in { + + val result = + JoinConfigCompatChecker.fullMatch( + requiredKeys, + config( + """ + |{ + | akka.cluster.min-nr-of-members = 1 + | akka.cluster.retry-unsuccessful-join-after = 10s + | akka.cluster.allow-weakly-up-members = on + |} + """.stripMargin), + clusterConfig + ) + + result shouldBe Valid + } + + "fail when some required keys are NOT provided" in { + + val Invalid(incompatibleKeys) = + JoinConfigCompatChecker.fullMatch( + requiredKeys, + config( + """ + |{ + | akka.cluster.min-nr-of-members = 1 + |} + """.stripMargin), + clusterConfig + ) + + incompatibleKeys should have size 2 + incompatibleKeys should contain("akka.cluster.retry-unsuccessful-join-after is missing") + incompatibleKeys should contain("akka.cluster.allow-weakly-up-members is missing") + } + + "fail when all required keys are passed, but some values don't match cluster config" in { + + val Invalid(incompatibleKeys) = + JoinConfigCompatChecker.fullMatch( + requiredKeys, + config( + """ + |{ + | akka.cluster.min-nr-of-members = 1 + | akka.cluster.retry-unsuccessful-join-after = 15s + | akka.cluster.allow-weakly-up-members = off + |} + """.stripMargin), + clusterConfig + ) + + incompatibleKeys should have size 2 + incompatibleKeys should contain("akka.cluster.retry-unsuccessful-join-after is incompatible") + incompatibleKeys should contain("akka.cluster.allow-weakly-up-members is incompatible") + } + + "fail when all required keys are passed, but some are missing and others don't match cluster config" in { + + val Invalid(incompatibleKeys) = + JoinConfigCompatChecker.fullMatch( + requiredKeys, + config( + """ + |{ + | akka.cluster.min-nr-of-members = 1 + | akka.cluster.allow-weakly-up-members = off + |} + """.stripMargin), + clusterConfig + ) + + incompatibleKeys should have size 2 + incompatibleKeys should contain("akka.cluster.retry-unsuccessful-join-after is missing") + incompatibleKeys should contain("akka.cluster.allow-weakly-up-members is incompatible") + } + } + + def config(str: String): Config = ConfigFactory.parseString(str).resolve() + +} diff --git a/akka-cluster/src/test/scala/akka/cluster/protobuf/ClusterMessageSerializerSpec.scala b/akka-cluster/src/test/scala/akka/cluster/protobuf/ClusterMessageSerializerSpec.scala index f7791d7d9e..bd5371cf45 100644 --- a/akka-cluster/src/test/scala/akka/cluster/protobuf/ClusterMessageSerializerSpec.scala +++ b/akka-cluster/src/test/scala/akka/cluster/protobuf/ClusterMessageSerializerSpec.scala @@ -5,11 +5,13 @@ package akka.cluster.protobuf import akka.cluster._ import akka.actor.{ ActorSystem, Address, ExtendedActorSystem } +import akka.cluster.InternalClusterAction.CompatibleConfig import akka.cluster.routing.{ ClusterRouterPool, ClusterRouterPoolSettings } import akka.routing.RoundRobinPool import collection.immutable.SortedSet import akka.testkit.{ AkkaSpec, TestKit } +import com.typesafe.config.ConfigFactory class ClusterMessageSerializerSpec extends AkkaSpec( "akka.actor.provider = cluster") { @@ -52,8 +54,8 @@ class ClusterMessageSerializerSpec extends AkkaSpec( checkSerialization(InternalClusterAction.Join(uniqueAddress, Set("foo", "bar", "dc-A"))) checkSerialization(ClusterUserAction.Leave(address)) checkSerialization(ClusterUserAction.Down(address)) - checkSerialization(InternalClusterAction.InitJoin) - checkSerialization(InternalClusterAction.InitJoinAck(address)) + checkSerialization(InternalClusterAction.InitJoin(ConfigFactory.empty)) + checkSerialization(InternalClusterAction.InitJoinAck(address, CompatibleConfig(ConfigFactory.empty))) checkSerialization(InternalClusterAction.InitJoinNack(address)) checkSerialization(ClusterHeartbeatSender.Heartbeat(address)) checkSerialization(ClusterHeartbeatSender.HeartbeatRsp(uniqueAddress)) diff --git a/akka-docs/src/main/paradox/cluster-usage.md b/akka-docs/src/main/paradox/cluster-usage.md index cd73a05f72..9c994eacd8 100644 --- a/akka-docs/src/main/paradox/cluster-usage.md +++ b/akka-docs/src/main/paradox/cluster-usage.md @@ -994,3 +994,27 @@ Related config properties: `akka.cluster.use-dispatcher = akka.cluster.cluster-d Corresponding default values: `akka.cluster.use-dispatcher =`. @@@ + +### Configuration Compatibility Check + +Creating a cluster is about deploying two or more nodes and make then behave as if they were one single application. Therefore it's extremely important that all nodes in a cluster are configured with compatible settings. + +The Configuration Compatibility Check feature ensures that all nodes in a cluster have a compatible configuration. Whenever a new node is joining an existing cluster, a subset of its configuration settings (only those that are required to be checked) is sent to the nodes in the cluster for verification. Once the configuration is checked on the cluster side, the cluster sends back its own set of required configuration settings. The joining node will then verify if it's compliant with the cluster configuration. The joining node will only proceed if all checks pass, on both sides. + +New custom checkers can be added by extending `akka.cluster.JoinConfigCompatChecker` and including them in the configuration. Each checker must be associated with a unique key: + +``` +akka.cluster.configuration-compatibility-check.checkers { + my-custom-config = "com.company.MyCustomJoinConfigCompatChecker" +} +``` + +@@@ note + +Configuration Compatibility Check is enabled by default, but can be disabled by setting `akka.cluster.configuration-compatibility-check.enforce-on-join = off`. This is specially useful when performing rolling updates. Obviously this should only be done if a complete cluster shutdown isn't an option. A cluster with nodes with different configuration settings may lead to data loss or data corruption. + +This setting should only be disabled on the joining nodes. The checks are always performed on both sides, and warnings are logged. In case of incompatibilities, it is the responsibility of the joining node to decide if the process should be interrupted or not. + +If you are performing a rolling update on cluster using Akka 2.5.9 or prior (thus, not supporting this feature), the checks will not be performed because the running cluster has no means to verify the configuration sent by the joining node, nor to send back its own configuration. + +@@@