3333import java .security .NoSuchAlgorithmException ;
3434import java .text .ParseException ;
3535import java .text .SimpleDateFormat ;
36+ import java .time .Instant ;
3637import java .util .ArrayList ;
3738import java .util .Base64 ;
3839import java .util .Collection ;
3940import java .util .Collections ;
4041import java .util .Date ;
4142import java .util .List ;
43+ import java .util .Locale ;
4244import java .util .Map ;
4345import java .util .Optional ;
46+ import java .util .Properties ;
4447import java .util .Set ;
4548import java .util .SortedMap ;
4649import java .util .TimeZone ;
5861import javax .xml .stream .XMLStreamWriter ;
5962
6063import com .fasterxml .jackson .core .JsonParseException ;
64+ import com .fasterxml .jackson .databind .ObjectMapper ;
6165import com .fasterxml .jackson .dataformat .xml .XmlMapper ;
6266import com .google .common .base .CharMatcher ;
6367import com .google .common .base .Splitter ;
@@ -193,6 +197,9 @@ public class S3ProxyHandler {
193197 "*-./_" , /*plusForSpace=*/ false );
194198 @ SuppressWarnings ("deprecation" )
195199 private static final HashFunction MD5 = Hashing .md5 ();
200+ private static final ObjectMapper JSON_MAPPER = new ObjectMapper ();
201+ private static final String UNKNOWN_VALUE = "unknown" ;
202+ private static final StatusMetadata STATUS_METADATA = StatusMetadata .load ();
196203
197204 private final boolean anonymousIdentity ;
198205 private final AuthenticationType authenticationType ;
@@ -206,6 +213,7 @@ public class S3ProxyHandler {
206213 private final XmlMapper mapper = new XmlMapper ();
207214 private final XMLOutputFactory xmlOutputFactory =
208215 XMLOutputFactory .newInstance ();
216+ private final Instant serverStartTime ;
209217 private BlobStoreLocator blobStoreLocator ;
210218 // TODO: hack to allow per-request anonymous access
211219 private final BlobStore defaultBlobStore ;
@@ -268,6 +276,7 @@ public Map.Entry<String, BlobStore> locateBlobStore(
268276 Boolean .FALSE );
269277 this .servicePath = Strings .nullToEmpty (servicePath );
270278 this .maximumTimeSkew = maximumTimeSkew ;
279+ this .serverStartTime = Instant .now ();
271280 }
272281
273282 private static String getBlobStoreType (BlobStore blobStore ) {
@@ -293,9 +302,10 @@ public final void doHandle(HttpServletRequest baseRequest,
293302 String uri = request .getRequestURI ();
294303 String originalUri = request .getRequestURI ();
295304
296- // Check for the /version endpoint
297- if ("/healthz" .equals (uri ) && "GET" .equalsIgnoreCase (method )) {
298- handleVersionRequest (response );
305+ String healthzUri = servicePath .isEmpty () ? "/healthz" :
306+ servicePath + "/healthz" ;
307+ if (healthzUri .equals (uri ) && "GET" .equalsIgnoreCase (method )) {
308+ handleStatuszRequest (response );
299309 return ;
300310 }
301311
@@ -2035,19 +2045,62 @@ private void handlePutBlob(HttpServletRequest request,
20352045
20362046 response .addHeader (HttpHeaders .ETAG , maybeQuoteETag (eTag ));
20372047 }
2038- private void handleVersionRequest (HttpServletResponse response ) throws IOException {
2048+
2049+ private void handleStatuszRequest (HttpServletResponse response )
2050+ throws IOException {
20392051 response .setStatus (HttpServletResponse .SC_OK );
20402052 response .setContentType ("application/json" );
2041- response .setCharacterEncoding ("UTF-8" );
2042-
2043- String versionInfo = "{ \" status\" : \" OK\" }" ;
2044-
2053+ response .setCharacterEncoding (UTF_8 );
2054+
2055+ var statusBody = ImmutableMap .of ("gitHash" , STATUS_METADATA .gitHash );
2056+ String versionInfo = JSON_MAPPER .writeValueAsString (statusBody );
2057+
20452058 try (PrintWriter writer = response .getWriter ()) {
20462059 writer .write (versionInfo );
20472060 writer .flush ();
20482061 }
20492062 }
20502063
2064+ private static final class StatusMetadata {
2065+ private final String version ;
2066+ private final String gitHash ;
2067+
2068+ private StatusMetadata (String version , String gitHash ) {
2069+ this .version = version ;
2070+ this .gitHash = gitHash ;
2071+ }
2072+
2073+ static StatusMetadata load () {
2074+ Package handlerPackage = S3ProxyHandler .class .getPackage ();
2075+ String version = handlerPackage == null ?
2076+ UNKNOWN_VALUE :
2077+ handlerPackage .getImplementationVersion ();
2078+ if (Strings .isNullOrEmpty (version )) {
2079+ version = UNKNOWN_VALUE ;
2080+ }
2081+ String gitHash = loadGitHash ();
2082+ return new StatusMetadata (version , gitHash );
2083+ }
2084+
2085+ private static String loadGitHash () {
2086+ try (InputStream stream = S3ProxyHandler .class .getClassLoader ()
2087+ .getResourceAsStream ("git.properties" )) {
2088+ if (stream == null ) {
2089+ return UNKNOWN_VALUE ;
2090+ }
2091+ Properties properties = new Properties ();
2092+ properties .load (stream );
2093+ return Optional .ofNullable (
2094+ properties .getProperty ("git.commit.id.abbrev" ))
2095+ .orElseGet (() -> properties .getProperty ("git.commit.id" ,
2096+ UNKNOWN_VALUE ));
2097+ } catch (IOException ioe ) {
2098+ logger .debug ("Unable to load git.properties" , ioe );
2099+ return UNKNOWN_VALUE ;
2100+ }
2101+ }
2102+ }
2103+
20512104 private void handlePostBlob (HttpServletRequest request ,
20522105 HttpServletResponse response , InputStream is , BlobStore blobStore ,
20532106 String containerName )
0 commit comments