blob: ad5e8716e321d82b8c200bb66d65749283d1b24b [file] [log] [blame]
/*
* Copyright (c) 2018 Google Inc. 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.android.vts.servlet;
import com.android.vts.util.GcsHelper;
import com.google.appengine.api.memcache.ErrorHandlers;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.Storage.BlobListOption;
import com.google.cloud.storage.StorageOptions;
import com.google.gson.Gson;
import org.apache.commons.io.IOUtils;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* A GCS log servlet read log zip file from Google Cloud Storage bucket and show the content in it
* from the zip file by unarchiving it
*/
@SuppressWarnings("serial")
public class ShowGcsLogServlet extends BaseServlet {
private static final String GCS_LOG_JSP = "WEB-INF/jsp/show_gcs_log.jsp";
/** Google Cloud Storage project's key file to access the storage */
private static String GCS_KEY_FILE;
/** Google Cloud Storage project's default bucket name for vtslab test result files */
private static String GCS_BUCKET_NAME;
/** Google Cloud Storage project's default bucket name for vtslab infra log files */
private static String GCS_INFRA_LOG_BUCKET_NAME;
/**
* This is the key file to access vtslab-gcs project. It will allow the dashboard to have a full
* control of the bucket.
*/
private InputStream keyFileInputStream;
/** This is the instance of java google storage library */
private Storage storage;
/** This is the instance of App Engine memcache service java library */
private MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
/** GCS Test Report Bucket instance */
private Bucket vtsReportBucket;
/** GCS Infra Log Bucket instance */
private Bucket vtsInfraLogBucket;
@Override
public void init(ServletConfig cfg) throws ServletException {
super.init(cfg);
GCS_KEY_FILE = systemConfigProp.getProperty("gcs.keyFile");
GCS_BUCKET_NAME = systemConfigProp.getProperty("gcs.bucketName");
GCS_INFRA_LOG_BUCKET_NAME = systemConfigProp.getProperty("gcs.infraLogBucketName");
String keyFilePath = "keys/" + GCS_KEY_FILE;
byte[] keyFileByteArray = new byte[0];
try {
keyFileByteArray =
IOUtils.toByteArray(
this.getClass().getClassLoader().getResourceAsStream(keyFilePath));
} catch (IOException e) {
e.printStackTrace();
}
this.keyFileInputStream = new ByteArrayInputStream(keyFileByteArray);
InputStream vtsReportInputStream = new ByteArrayInputStream(keyFileByteArray);
InputStream vtsInfraInputStream = new ByteArrayInputStream(keyFileByteArray);
Optional<Storage> optionalVtsReportStorage = GcsHelper.getStorage(vtsReportInputStream);
if (optionalVtsReportStorage.isPresent()) {
this.storage = optionalVtsReportStorage.get();
this.vtsReportBucket = storage.get(GCS_BUCKET_NAME);
} else {
logger.log(Level.SEVERE, "Error on getting storage instance!");
throw new ServletException("Creating storage instance exception!");
}
Optional<Storage> optionalVtsInfraStorage = GcsHelper.getStorage(vtsInfraInputStream);
if (optionalVtsInfraStorage.isPresent()) {
this.storage = optionalVtsInfraStorage.get();
this.vtsInfraLogBucket = storage.get(GCS_INFRA_LOG_BUCKET_NAME);
} else {
logger.log(Level.SEVERE, "Error on getting storage instance!");
throw new ServletException("Creating storage instance exception!");
}
syncCache.setErrorHandler(ErrorHandlers.getConsistentLogAndContinue(Level.INFO));
}
@Override
public PageType getNavParentType() {
return PageType.TOT;
}
@Override
public List<Page> getBreadcrumbLinks(HttpServletRequest request) {
return null;
}
@Override
public void doGetHandler(HttpServletRequest request, HttpServletResponse response)
throws IOException {
if (keyFileInputStream == null) {
request.setAttribute("error_title", "GCS Key file Error");
request.setAttribute("error_message", "The GCS Key file is not existed!");
RequestDispatcher dispatcher = request.getRequestDispatcher(ERROR_MESSAGE_JSP);
try {
dispatcher.forward(request, response);
} catch (ServletException e) {
logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e);
}
} else {
String pathInfo = request.getPathInfo();
if (Objects.nonNull(pathInfo)) {
if (pathInfo.equalsIgnoreCase("/download")) {
downloadHandler(request, response);
} else {
logger.log(Level.INFO, "Path Info => " + pathInfo);
logger.log(Level.WARNING, "Unknown path access!");
}
} else {
defaultHandler(request, response);
}
}
}
private void downloadHandler(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String file = request.getParameter("file") == null ? "/" : request.getParameter("file");
Path filePathInfo = Paths.get(file);
Blob blobFile = vtsInfraLogBucket.get(filePathInfo.toString());
if (blobFile.exists()) {
response.setContentType("application/octet-stream");
response.setContentLength(blobFile.getSize().intValue());
response.setHeader(
"Content-Disposition",
"attachment; filename=\"" + filePathInfo.getFileName() + "\"");
response.getOutputStream().write(blobFile.getContent());
} else {
request.setAttribute("error_title", "Infra Log File Not Found");
request.setAttribute("error_message", "Please contact the administrator!");
RequestDispatcher dispatcher = request.getRequestDispatcher(ERROR_MESSAGE_JSP);
try {
dispatcher.forward(request, response);
} catch (ServletException e) {
logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e);
}
}
}
private void defaultHandler(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String action =
request.getParameter("action") == null ? "read" : request.getParameter("action");
String path = request.getParameter("path") == null ? "/" : request.getParameter("path");
String entry = request.getParameter("entry") == null ? "" : request.getParameter("entry");
Path pathInfo = Paths.get(path);
List<String> dirList = new ArrayList<>();
List<String> fileList = new ArrayList<>();
List<String> entryList = new ArrayList<>();
Map<String, Object> resultMap = new HashMap<>();
String entryContent = "";
if (pathInfo.toString().endsWith(".zip")) {
Blob blobFile = (Blob) this.syncCache.get(path.toString());
if (blobFile == null) {
blobFile = vtsReportBucket.get(path);
this.syncCache.put(path.toString(), blobFile);
}
if (action.equalsIgnoreCase("read")) {
InputStream blobInputStream = new ByteArrayInputStream(blobFile.getContent());
ZipInputStream zipInputStream = new ZipInputStream(blobInputStream);
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
if (zipEntry.isDirectory()) {
} else {
if (entry.length() > 0) {
logger.log(Level.INFO, "param entry => " + entry);
if (zipEntry.getName().equals(entry)) {
logger.log(Level.INFO, "matched !!!! " + zipEntry.getName());
entryContent =
IOUtils.toString(
zipInputStream, StandardCharsets.UTF_8.name());
}
} else {
entryList.add(zipEntry.getName());
}
}
}
resultMap.put("entryList", entryList);
resultMap.put("entryContent", entryContent);
String json = new Gson().toJson(resultMap);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
} else {
response.setContentType("application/octet-stream");
response.setContentLength(blobFile.getSize().intValue());
response.setHeader(
"Content-Disposition",
"attachment; filename=\"" + pathInfo.getFileName() + "\"");
response.getOutputStream().write(blobFile.getContent());
}
} else {
logger.log(Level.INFO, "path info => " + pathInfo);
logger.log(Level.INFO, "path name count => " + pathInfo.getNameCount());
BlobListOption[] listOptions;
if (pathInfo.getNameCount() == 0) {
listOptions = new BlobListOption[] {BlobListOption.currentDirectory()};
} else {
String prefixPathString = path.endsWith("/") ? path : path.concat("/");
if (pathInfo.getNameCount() <= 1) {
dirList.add("/");
} else {
dirList.add(getParentDirPath(prefixPathString));
}
listOptions =
new BlobListOption[] {
BlobListOption.currentDirectory(),
BlobListOption.prefix(prefixPathString)
};
}
Iterable<Blob> blobIterable = vtsReportBucket.list(listOptions).iterateAll();
Iterator<Blob> blobIterator = blobIterable.iterator();
while (blobIterator.hasNext()) {
Blob blob = blobIterator.next();
logger.log(Level.INFO, "blob name => " + blob);
if (blob.isDirectory()) {
logger.log(Level.INFO, "directory name => " + blob.getName());
dirList.add(blob.getName());
} else {
logger.log(Level.INFO, "file name => " + blob.getName());
fileList.add(Paths.get(blob.getName()).getFileName().toString());
}
}
response.setStatus(HttpServletResponse.SC_OK);
request.setAttribute("entryList", entryList);
request.setAttribute("dirList", dirList);
request.setAttribute("fileList", fileList);
request.setAttribute("path", path);
RequestDispatcher dispatcher = request.getRequestDispatcher(GCS_LOG_JSP);
try {
dispatcher.forward(request, response);
} catch (ServletException e) {
logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e);
}
}
}
private String getParentDirPath(String fileOrDirPath) {
boolean endsWithSlashCheck = fileOrDirPath.endsWith(File.separator);
return fileOrDirPath.substring(
0,
fileOrDirPath.lastIndexOf(
File.separatorChar,
endsWithSlashCheck
? fileOrDirPath.length() - 2
: fileOrDirPath.length() - 1));
}
}