1 /* 2 * Copyright (C) 2018 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.config.gcs; 18 19 import com.android.tradefed.config.ConfigurationException; 20 import com.android.tradefed.config.IConfigurationServer; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.OptionClass; 23 import com.android.tradefed.util.GCSFileDownloader; 24 25 import com.google.common.annotations.VisibleForTesting; 26 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.net.InetAddress; 30 import java.net.UnknownHostException; 31 import java.util.Scanner; 32 33 /** Config server loads configs from Google Cloud Storage (GCS). */ 34 @OptionClass(alias = "gcs-config-server", global_namespace = false) 35 public class GCSConfigurationServer implements IConfigurationServer { 36 37 private static final String DEFAULT_MAPPING = "host-config.txt"; 38 39 @Option( 40 name = "bucket-name", 41 description = "Google Cloud Storage bucket name.", 42 mandatory = true 43 ) 44 private String mBucketName; 45 46 @Option( 47 name = "host-config-mapping-filename", 48 description = "Mapping file maps hostname to its config." 49 ) 50 private String mHostConfigMapping = DEFAULT_MAPPING; 51 52 private GCSFileDownloader mGCSFileDownloader = null; 53 private String mCurrentHostConfig = null; 54 55 /** {@inheritDoc} */ 56 @Override 57 public String getCurrentHostConfig() throws ConfigurationException { 58 if (mCurrentHostConfig == null) { 59 String currentHostname = currentHostname(); 60 System.out.format("Downloading %s.\n", mHostConfigMapping); 61 InputStream hostConfigMapping = downloadFile(mHostConfigMapping); 62 mCurrentHostConfig = getHostConfig(currentHostname, hostConfigMapping); 63 } 64 return mCurrentHostConfig; 65 } 66 67 /** 68 * Get a host's config file name from the mapping file. Each line in the host config mapping 69 * file has the format: "hostname,instance-name,host-config-file-name,host-command-file". 70 * 71 * @param hostname is the hostname to get host config for 72 * @param hostConfigMappingFile is a mapping file that maps hostname to host config name. 73 * @return host config name is the host's config name. 74 * @throws ConfigurationException 75 */ 76 private String getHostConfig(String hostname, InputStream hostConfigMappingFile) 77 throws ConfigurationException { 78 Scanner scanner = new Scanner(hostConfigMappingFile); 79 try { 80 while (scanner.hasNextLine()) { 81 // "hostname,instance-name,host-config-file-name,host-command-file" 82 String line = scanner.nextLine().trim(); 83 if (line.startsWith("#") || line.isEmpty() || line.startsWith("[")) { 84 // Ignore comments, empty line and cluster name line. 85 continue; 86 } 87 String[] hostMap = line.split(","); 88 if (hostMap.length < 3) { 89 // Ignore invalid lines. 90 continue; 91 } 92 if (sameHost(hostname, hostMap[0])) { 93 System.out.format("%s's config is %s\n", hostname, hostMap[2]); 94 return hostMap[2]; 95 } 96 } 97 throw new ConfigurationException( 98 String.format("Host %s doesn't have configure.", hostname)); 99 } finally { 100 scanner.close(); 101 } 102 } 103 104 private boolean sameHost(String currentHostname, String hostname) { 105 return currentHostname.startsWith(hostname); 106 } 107 108 /** 109 * Get current host name. 110 * 111 * @return hostname 112 * @throws ConfigurationException 113 */ 114 @VisibleForTesting 115 String currentHostname() throws ConfigurationException { 116 try { 117 return InetAddress.getLocalHost().getHostName(); 118 } catch (UnknownHostException e) { 119 throw new ConfigurationException(e.getMessage(), e.getCause()); 120 } 121 } 122 123 /** {@inheritDoc} */ 124 @Override 125 public InputStream getConfig(String name) throws ConfigurationException { 126 System.out.format("Downloading %s.\n", name); 127 return downloadFile(name); 128 } 129 130 /** 131 * Download file from GCS 132 * 133 * @param name file name 134 * @return an {@link InputStream} is the file's content. 135 * @throws ConfigurationException 136 */ 137 @VisibleForTesting 138 InputStream downloadFile(String name) throws ConfigurationException { 139 try { 140 return getFileDownloader().downloadFile(mBucketName, name); 141 } catch (IOException e) { 142 throw new ConfigurationException(e.getMessage(), e.getCause()); 143 } 144 } 145 146 private GCSFileDownloader getFileDownloader() { 147 if (mGCSFileDownloader == null) { 148 mGCSFileDownloader = new GCSFileDownloader(); 149 } 150 return mGCSFileDownloader; 151 } 152 } 153