|
18 | 18 |
|
19 | 19 | import com.google.api.core.InternalApi; |
20 | 20 | import com.google.cloud.bigtable.data.v2.internal.csm.tracers.DirectPathCompatibleTracer; |
21 | | -import java.net.InetAddress; |
22 | 21 | import java.util.logging.Level; |
23 | 22 | import java.util.logging.Logger; |
24 | 23 |
|
25 | 24 | @InternalApi |
26 | 25 | public class DirectAccessInvestigator { |
27 | 26 | private static final Logger LOG = Logger.getLogger(DirectAccessInvestigator.class.getName()); |
28 | 27 |
|
29 | | - // Telemetry metric reason codes |
30 | | - private static final String REASON_NOT_IN_GCP = "not_in_gcp"; |
31 | | - private static final String REASON_METADATA_UNREACHABLE = "metadata_unreachable"; |
32 | | - private static final String REASON_NO_IP_ASSIGNED = "no_ip_assigned"; |
33 | | - private static final String REASON_LOOPBACK_DOWN = "loopback_misconfigured"; |
34 | | - private static final String REASON_LOOPBACK_V4_MISSING = "loopback_misconfigured_ipv4"; |
35 | | - private static final String REASON_LOOPBACK_V6_MISSING = "loopback_misconfigured_ipv6"; |
36 | | - private static final String REASON_UNKNOWN = ""; |
| 28 | + /** Metric reason codes for Direct Access failures. */ |
| 29 | + public enum FailureReason { |
| 30 | + NOT_IN_GCP("not_in_gcp"), |
| 31 | + METADATA_UNREACHABLE("metadata_unreachable"), |
| 32 | + NO_IP_ASSIGNED("no_ip_assigned"), |
| 33 | + LOOPBACK_DOWN("loopback_misconfigured"), |
| 34 | + LOOPBACK_V4_MISSING("loopback_misconfigured_ipv4"), |
| 35 | + LOOPBACK_V6_MISSING("loopback_misconfigured_ipv6"), |
| 36 | + USER_DISABLED("user_disabled"), |
| 37 | + UNKNOWN(""); |
| 38 | + |
| 39 | + private final String value; |
| 40 | + |
| 41 | + FailureReason(String value) { |
| 42 | + this.value = value; |
| 43 | + } |
| 44 | + |
| 45 | + public String getValue() { |
| 46 | + return value; |
| 47 | + } |
| 48 | + } |
37 | 49 |
|
38 | 50 | public static void investigateAndReport( |
39 | 51 | DirectPathCompatibleTracer tracer, Throwable originalError) { |
40 | 52 | try { |
41 | | - if (!GCECheck.isRunningOnGCP()) { |
42 | | - recordAndLog( |
43 | | - tracer, REASON_NOT_IN_GCP, "Direct Access investigation: not in GCP.", originalError); |
44 | | - return; |
45 | | - } |
46 | | - |
47 | | - if (!MetadataServer.isReachable()) { |
48 | | - recordAndLog( |
49 | | - tracer, |
50 | | - REASON_METADATA_UNREACHABLE, |
51 | | - "Direct Access investigation: Metadata unreachable.", |
52 | | - null); |
53 | | - return; |
54 | | - } |
55 | | - |
56 | | - InetAddress ipv4 = MetadataServer.getIPv4(); |
57 | | - InetAddress ipv6 = MetadataServer.getIPv6(); |
58 | | - |
59 | | - if (ipv4 == null && ipv6 == null) { |
60 | | - recordAndLog( |
61 | | - tracer, |
62 | | - REASON_NO_IP_ASSIGNED, |
63 | | - "Direct Access investigation: Neither IPv4 nor IPv6 assigned.", |
64 | | - null); |
65 | | - return; |
66 | | - } |
67 | | - |
68 | | - if (!LoopBackInterface.isUp()) { |
69 | | - recordAndLog( |
70 | | - tracer, |
71 | | - REASON_LOOPBACK_DOWN, |
72 | | - "Direct Access investigation: Loopback interface down.", |
73 | | - null); |
74 | | - return; |
75 | | - } |
76 | | - |
77 | | - // ONLY check for IPv4 loopback if we are using an IPv4 address |
78 | | - if (ipv4 != null && !LoopBackInterface.hasLocalIpv4Loopback()) { |
79 | | - recordAndLog( |
80 | | - tracer, |
81 | | - REASON_LOOPBACK_V4_MISSING, |
82 | | - "Direct Access investigation: IPv4 loopback missing.", |
83 | | - null); |
84 | | - return; |
85 | | - } |
86 | | - |
87 | | - // ONLY check for IPv6 loopback if we are using an IPv6 address |
88 | | - if (ipv6 != null && !LoopBackInterface.hasLocalIpv6Loopback()) { |
89 | | - recordAndLog( |
90 | | - tracer, |
91 | | - REASON_LOOPBACK_V6_MISSING, |
92 | | - "Direct Access investigation: IPv6 loopback missing.", |
93 | | - null); |
94 | | - return; |
95 | | - } |
96 | | - |
97 | | - boolean v4Plumbed = ipv4 != null && LoopBackInterface.isIpPlumbed(ipv4); |
98 | | - if (ipv4 != null && !v4Plumbed) { |
99 | | - LOG.log( |
100 | | - Level.FINE, |
101 | | - "Direct Access investigation: IPv4 assigned by metadata but not found on NIC."); |
102 | | - } |
103 | | - |
104 | | - boolean v6Plumbed = ipv6 != null && LoopBackInterface.isIpPlumbed(ipv6); |
105 | | - if (ipv6 != null && !v6Plumbed) { |
106 | | - LOG.log( |
107 | | - Level.FINE, |
108 | | - "Direct Access investigation: IPv6 assigned by metadata but not found on NIC."); |
109 | | - } |
110 | | - |
111 | | - // If the metadata server assigned IPs, but the guest OS hasn't configured any of them on an |
112 | | - // interface. |
113 | | - // Do NOT return early here, because this is how GKE pods work (relying on default kernel |
114 | | - // routing). |
115 | | - if (!v4Plumbed && !v6Plumbed) { |
116 | | - LOG.log( |
117 | | - Level.FINE, |
118 | | - "Direct Access investigation: Metadata IPs are not plumbed to local interfaces (likely containerized). Relying on kernel default routing."); |
119 | | - } |
| 53 | + // TODO: Implement checks in a future PR. |
| 54 | + // For now, default to returning "unknown". |
120 | 55 | recordAndLog( |
121 | 56 | tracer, |
122 | | - REASON_UNKNOWN, |
123 | | - "Direct Access investigation: Running on GCP, metadata reachable, IPs assigned and plumbed, but Direct Access still failed.", |
| 57 | + FailureReason.UNKNOWN, |
| 58 | + "Direct Access investigation: Defaulting to unknown failure reason for now.", |
124 | 59 | originalError); |
125 | | - |
126 | 60 | } catch (Exception e) { |
127 | 61 | LOG.log(Level.WARNING, "Failed to complete Direct Access investigation", e); |
128 | 62 | } |
129 | 63 | } |
130 | 64 |
|
131 | 65 | /** Helper method to consistently log the failure reason and record it to the tracer. */ |
132 | 66 | private static void recordAndLog( |
133 | | - DirectPathCompatibleTracer tracer, String reasonCode, String logMessage, Throwable error) { |
| 67 | + DirectPathCompatibleTracer tracer, FailureReason reason, String logMessage, Throwable error) { |
134 | 68 | if (error != null) { |
135 | 69 | LOG.log(Level.FINE, logMessage, error); |
136 | 70 | } else { |
137 | 71 | LOG.log(Level.FINE, logMessage); |
138 | 72 | } |
139 | | - tracer.recordFailure(reasonCode); |
| 73 | + tracer.recordFailure(reason.getValue()); |
140 | 74 | } |
141 | 75 | } |
0 commit comments