Implement connectivity check for Exoblaze and create dummy connectivity check for Blaze/Bazel.

The connectivity checks return a ConnectivityStatus, which is either ok, or contains a warning and whether the build should fail if this status is present.

This cl doesn't change behavior; it introduces the functionality without incorporating it.

RELNOTES: None.
PiperOrigin-RevId: 244869251
diff --git a/src/main/java/com/google/devtools/build/lib/network/ConnectivityModule.java b/src/main/java/com/google/devtools/build/lib/network/ConnectivityModule.java
new file mode 100644
index 0000000..21e0f5c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/network/ConnectivityModule.java
@@ -0,0 +1,93 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.network;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.network.ConnectivityStatus.Status;
+import com.google.devtools.build.lib.runtime.BlazeModule;
+import com.google.devtools.build.lib.runtime.Command;
+import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.util.AbruptExitException;
+import com.google.devtools.common.options.Converters.DurationConverter;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionDocumentationCategory;
+import com.google.devtools.common.options.OptionEffectTag;
+import com.google.devtools.common.options.OptionsBase;
+import java.time.Duration;
+
+/** Stores network status for Bazel-adjacent services, usually remote build and BES. */
+public class ConnectivityModule extends BlazeModule implements ConnectivityStatusProvider {
+
+  /** Options that define the behavior of the Connectivity Modules. */
+  public static class ConnectivityOptions extends OptionsBase {
+    @Option(
+        name = "connectivity_check_frequency",
+        defaultValue = "5s",
+        documentationCategory = OptionDocumentationCategory.REMOTE,
+        effectTags = {OptionEffectTag.BAZEL_INTERNAL_CONFIGURATION},
+        converter = DurationConverter.class,
+        help =
+            "How often to perform a connectivity and reachability check for remote services "
+                + "(eg. 5s). 0 disables the check and assumes connectivity.")
+    public Duration cacheLifetime;
+  }
+
+  /**
+   * Attempts to retrieve and return the specified service's status from the cache, calculating it
+   * if it's not present in the cache.
+   *
+   * @param service the name of the service we want to determine connectivity status for
+   * @param cache a cache that stores the current connectivity status for each service
+   * @return the connectivity status for the specified service
+   */
+  protected ConnectivityStatus determineConnectivity(String service, Cache<String, Status> cache) {
+    return new ConnectivityStatus(Status.OK, /* serviceInfo= */ "");
+  }
+
+  private Duration cacheLifetime;
+  private Cache<String, Status> cache;
+
+  @Override
+  public Iterable<Class<? extends OptionsBase>> getCommandOptions(Command command) {
+    return "build".equals(command.name())
+        ? ImmutableList.of(ConnectivityOptions.class)
+        : ImmutableList.of();
+  }
+
+  @Override
+  public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
+    ConnectivityOptions options = env.getOptions().getOptions(ConnectivityOptions.class);
+    if (options == null) {
+      return;
+    }
+    Duration newCacheLifetime = options.cacheLifetime;
+    // Initialize the cache if we haven't yet, or if the options have changed.
+    // TODO(steinman): Make this a LoadingCache where load() calls determineConnectivity().
+    if (cache == null || !newCacheLifetime.equals(cacheLifetime)) {
+      cache = CacheBuilder.newBuilder().expireAfterWrite(newCacheLifetime).build();
+      cacheLifetime = newCacheLifetime;
+    }
+  }
+
+  @Override
+  public ConnectivityStatus getStatus(String service) {
+    if (cacheLifetime.isZero()) {
+      return new ConnectivityStatus(Status.OK, /* serviceInfo= */ "");
+    }
+    return determineConnectivity(service, cache);
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/network/ConnectivityStatus.java b/src/main/java/com/google/devtools/build/lib/network/ConnectivityStatus.java
new file mode 100644
index 0000000..a0ee0ee
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/network/ConnectivityStatus.java
@@ -0,0 +1,55 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.network;
+
+/** Defines connectivity problem types and their short warning messages. */
+public final class ConnectivityStatus {
+  /** Enumerates common connectivity statuses and their generic short warnings. */
+  public enum Status {
+    NO_CREDENTIALS("No credentials."),
+    NO_NETWORK("No internet connection."),
+    NOT_REACHABLE("Service not reachable."),
+    OK("");
+
+    /** Generic warning associated with this status. */
+    public final String shortWarning;
+
+    Status(String shortWarning) {
+      this.shortWarning = shortWarning;
+    }
+  }
+
+  /** Service-specific information for this status. */
+  public String serviceInfo;
+
+  /** Generic category type for this status, which contains a generic warning. */
+  public final Status status;
+
+  /** Returns the complete formatted warning for this status. */
+  public String fullWarning() {
+    return status.shortWarning + " " + serviceInfo;
+  }
+
+  /**
+   * Constructs a connectivity status with a service-specific warning.
+   *
+   * @param serviceInfo service-specific information displayed or logged in addition to the status's
+   *     short warning when this connectivityStatus is present.
+   */
+  public ConnectivityStatus(Status status, String serviceInfo) {
+    this.status = status;
+    this.serviceInfo = serviceInfo;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/network/ConnectivityStatusProvider.java b/src/main/java/com/google/devtools/build/lib/network/ConnectivityStatusProvider.java
new file mode 100644
index 0000000..720aa9a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/network/ConnectivityStatusProvider.java
@@ -0,0 +1,24 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.network;
+
+/**
+ * Interface that gives services (such as the BES or remote build) exposure to their corresponding
+ * {@link ConnectivityStatus}.
+ */
+public interface ConnectivityStatusProvider {
+  /** Returns the current connectivity status for a given service. */
+  ConnectivityStatus getStatus(String service);
+}