1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.util; 18 19 import java.io.BufferedReader; 20 import java.io.File; 21 import java.io.FileNotFoundException; 22 import java.io.FileReader; 23 import java.io.IOException; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import java.util.ArrayList; 27 import java.util.Base64; 28 import java.util.List; 29 import java.util.LinkedList; 30 import java.util.NoSuchElementException; 31 import java.util.regex.Pattern; 32 import java.util.regex.Matcher; 33 34 import com.android.tradefed.build.IBuildInfo; 35 import com.android.tradefed.config.Option; 36 import com.android.tradefed.config.OptionClass; 37 import com.android.tradefed.log.LogUtil.CLog; 38 import com.android.tradefed.util.CommandResult; 39 import com.android.tradefed.util.CommandStatus; 40 import com.android.tradefed.util.FileUtil; 41 import com.android.tradefed.util.IRunUtil; 42 import com.android.tradefed.util.RunUtil; 43 import com.android.tradefed.util.VtsVendorConfigFileUtil; 44 45 import com.google.api.client.auth.oauth2.Credential; 46 import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; 47 import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; 48 import com.google.api.client.json.jackson2.JacksonFactory; 49 import com.google.api.client.json.JsonFactory; 50 51 import com.android.vts.proto.VtsReportMessage.DashboardPostMessage; 52 import com.android.vts.proto.VtsReportMessage.TestPlanReportMessage; 53 54 /** 55 * Uploads the VTS test plan execution result to the web DB using a RESTful API and 56 * an OAuth2 credential kept in a json file. 57 */ 58 public class VtsDashboardUtil { 59 private static final String PLUS_ME = "https://www.googleapis.com/auth/plus.me"; 60 private static final int BASE_TIMEOUT_MSECS = 1000 * 60; 61 private static VtsVendorConfigFileUtil mConfigReader; 62 IRunUtil mRunUtil = new RunUtil(); 63 64 public VtsDashboardUtil(VtsVendorConfigFileUtil configReader) { 65 mConfigReader = configReader; 66 } 67 68 /* 69 * Returns an OAuth2 token string obtained using a service account json keyfile. 70 * 71 * Uses the service account keyfile located at config variable 'service_key_json_path' 72 * to request an OAuth2 token. 73 */ 74 private String GetToken() { 75 String keyFilePath; 76 try { 77 keyFilePath = mConfigReader.GetVendorConfigVariable("service_key_json_path"); 78 } catch (NoSuchElementException e) { 79 return null; 80 } 81 82 JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); 83 Credential credential = null; 84 try { 85 List<String> listStrings = new LinkedList<String>(); 86 listStrings.add(PLUS_ME); 87 credential = GoogleCredential.fromStream(new FileInputStream(keyFilePath)) 88 .createScoped(listStrings); 89 credential.refreshToken(); 90 return credential.getAccessToken(); 91 } catch (FileNotFoundException e) { 92 CLog.e(String.format("Service key file %s doesn't exist.", keyFilePath)); 93 } catch (IOException e) { 94 CLog.e(String.format("Can't read the service key file, %s", keyFilePath)); 95 } 96 return null; 97 } 98 99 /* 100 * Uploads the given message to the web DB. 101 * 102 * @param message, DashboardPostMessage that keeps the result to upload. 103 */ 104 public void Upload(DashboardPostMessage message) { 105 String token = GetToken(); 106 if (token == null) { 107 CLog.d("Token is not available for DashboardPostMessage."); 108 return; 109 } 110 message.setAccessToken(token); 111 try { 112 String messageFilePath = WriteToTempFile( 113 Base64.getEncoder().encodeToString(message.toByteArray()).getBytes()); 114 Upload(messageFilePath); 115 } catch (IOException e) { 116 CLog.e("Couldn't write a proto message to a temp file."); 117 } catch (NullPointerException e) { 118 CLog.e("Couldn't serialize proto message."); 119 } 120 } 121 122 /* 123 * Uploads the given message file path to the web DB. 124 * 125 * @param message, DashboardPostMessage file path that keeps the result to upload. 126 */ 127 public void Upload(String messageFilePath) { 128 try { 129 String commandTemplate = 130 mConfigReader.GetVendorConfigVariable("dashboard_post_command"); 131 commandTemplate = commandTemplate.replace("{path}", messageFilePath); 132 // removes ', while keeping any substrings quoted by "". 133 commandTemplate = commandTemplate.replace("'", ""); 134 CLog.i(String.format("Upload command: %s", commandTemplate)); 135 List<String> commandList = new ArrayList<String>(); 136 Matcher matcher = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(commandTemplate); 137 while (matcher.find()) { 138 commandList.add(matcher.group(1)); 139 } 140 CommandResult c = mRunUtil.runTimedCmd(BASE_TIMEOUT_MSECS * 3, 141 (String[]) commandList.toArray(new String[commandList.size()])); 142 if (c == null || c.getStatus() != CommandStatus.SUCCESS) { 143 CLog.e("Uploading the test plan execution result to GAE DB faiied."); 144 CLog.e("Stdout: %s", c.getStdout()); 145 CLog.e("Stderr: %s", c.getStderr()); 146 } 147 FileUtil.deleteFile(new File(messageFilePath)); 148 } catch (NoSuchElementException e) { 149 CLog.e("dashboard_post_command unspecified in vendor config."); 150 } 151 } 152 153 /* 154 * Simple wrapper to write data to a temp file. 155 * 156 * @param data, actual data to write to a file. 157 * @throws IOException 158 */ 159 private String WriteToTempFile(byte[] data) throws IOException { 160 File tempFile = File.createTempFile("tempfile", ".tmp"); 161 String filePath = tempFile.getAbsolutePath(); 162 FileOutputStream out = new FileOutputStream(filePath); 163 out.write(data); 164 out.close(); 165 return filePath; 166 } 167 } 168