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 package com.android.tradefed.config; 17 18 import com.android.tradefed.log.LogUtil.CLog; 19 import com.android.tradefed.sandbox.ISandbox; 20 import com.android.tradefed.sandbox.SandboxConfigDump; 21 import com.android.tradefed.sandbox.SandboxConfigDump.DumpCmd; 22 import com.android.tradefed.sandbox.SandboxConfigUtil; 23 import com.android.tradefed.sandbox.SandboxConfigurationException; 24 import com.android.tradefed.util.FileUtil; 25 import com.android.tradefed.util.IRunUtil; 26 import com.android.tradefed.util.keystore.IKeyStoreClient; 27 import com.android.tradefed.util.keystore.IKeyStoreFactory; 28 import com.android.tradefed.util.keystore.KeyStoreException; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 import java.util.Set; 40 import java.util.stream.Collectors; 41 42 /** 43 * Special Configuration factory to handle creation of configurations for Sandboxing purpose. 44 * 45 * <p>TODO: Split the configuration dump part to another class 46 */ 47 public class SandboxConfigurationFactory extends ConfigurationFactory { 48 49 private static SandboxConfigurationFactory sInstance = null; 50 public static final Set<String> OPTION_IGNORED_ELEMENTS = new HashSet<>(); 51 52 static { 53 OPTION_IGNORED_ELEMENTS.addAll(SandboxConfigDump.VERSIONED_ELEMENTS); 54 OPTION_IGNORED_ELEMENTS.add(Configuration.DEVICE_REQUIREMENTS_TYPE_NAME); 55 OPTION_IGNORED_ELEMENTS.add(Configuration.DEVICE_OPTIONS_TYPE_NAME); 56 OPTION_IGNORED_ELEMENTS.add(Configuration.DEVICE_RECOVERY_TYPE_NAME); 57 OPTION_IGNORED_ELEMENTS.add(Configuration.CMD_OPTIONS_TYPE_NAME); 58 } 59 60 /** Get the singleton {@link IConfigurationFactory} instance. */ getInstance()61 public static SandboxConfigurationFactory getInstance() { 62 if (sInstance == null) { 63 sInstance = new SandboxConfigurationFactory(); 64 } 65 return sInstance; 66 } 67 68 /** {@inheritDoc} */ 69 @Override getConfigurationDef( String name, boolean isGlobal, Map<String, String> templateMap)70 protected ConfigurationDef getConfigurationDef( 71 String name, boolean isGlobal, Map<String, String> templateMap) 72 throws ConfigurationException { 73 // TODO: Extend ConfigurationDef to possibly create a different IConfiguration type and 74 // handle more elegantly the parent/subprocess incompatibilities. 75 ConfigurationDef def = createConfigurationDef(name); 76 new ConfigLoader(isGlobal).loadConfiguration(name, def, null, templateMap, null); 77 return def; 78 } 79 80 /** Internal method to create {@link ConfigurationDef} */ createConfigurationDef(String name)81 protected ConfigurationDef createConfigurationDef(String name) { 82 return new ConfigurationDef(name); 83 } 84 85 /** 86 * When running the dump for a command. Create a config with specific expectations. 87 * 88 * @param arrayArgs the command line for the run. 89 * @param command The dump command in progress 90 * @return a {@link IConfiguration} valid for the VERSIONED Sandbox. 91 * @throws ConfigurationException 92 */ createConfigurationFromArgs(String[] arrayArgs, DumpCmd command)93 public IConfiguration createConfigurationFromArgs(String[] arrayArgs, DumpCmd command) 94 throws ConfigurationException { 95 // Create on a new object to avoid state on the factory. 96 SandboxConfigurationFactory loader = new RunSandboxConfigurationFactory(command); 97 return loader.createConfigurationFromArgs(arrayArgs, null, getKeyStoreClient()); 98 } 99 100 /** 101 * Create a {@link IConfiguration} based on the command line and sandbox provided. 102 * 103 * @param args the command line for the run. 104 * @param keyStoreClient the {@link IKeyStoreClient} where to load the key from. 105 * @param sandbox the {@link ISandbox} used for the run. 106 * @param runUtil the {@link IRunUtil} to run commands. 107 * @return a {@link IConfiguration} valid for the sandbox. 108 * @throws ConfigurationException 109 */ createConfigurationFromArgs( String[] args, IKeyStoreClient keyStoreClient, ISandbox sandbox, IRunUtil runUtil)110 public IConfiguration createConfigurationFromArgs( 111 String[] args, IKeyStoreClient keyStoreClient, ISandbox sandbox, IRunUtil runUtil) 112 throws ConfigurationException { 113 return createConfigurationFromArgs(args, keyStoreClient, sandbox, runUtil, null, false); 114 } 115 116 /** 117 * Create a {@link IConfiguration} based on the command line and sandbox provided. 118 * 119 * @param args the command line for the run. 120 * @param keyStoreClient the {@link IKeyStoreClient} where to load the key from. 121 * @param sandbox the {@link ISandbox} used for the run. 122 * @param runUtil the {@link IRunUtil} to run commands. 123 * @return a {@link IConfiguration} valid for the sandbox. 124 * @throws ConfigurationException 125 */ createConfigurationFromArgs( String[] args, IKeyStoreClient keyStoreClient, ISandbox sandbox, IRunUtil runUtil, File globalConfig, boolean skipJavaCheck)126 public IConfiguration createConfigurationFromArgs( 127 String[] args, 128 IKeyStoreClient keyStoreClient, 129 ISandbox sandbox, 130 IRunUtil runUtil, 131 File globalConfig, 132 boolean skipJavaCheck) 133 throws ConfigurationException { 134 IConfiguration config = null; 135 File xmlConfig = null; 136 try { 137 runUtil.unsetEnvVariable(GlobalConfiguration.GLOBAL_CONFIG_VARIABLE); 138 // Dump the NON_VERSIONED part of the configuration against the current TF and not the 139 // sandboxed environment. 140 if (globalConfig == null) { 141 globalConfig = SandboxConfigUtil.dumpFilteredGlobalConfig(new HashSet<>()); 142 } 143 xmlConfig = 144 SandboxConfigUtil.dumpConfigForVersion( 145 createClasspath(), 146 runUtil, 147 args, 148 DumpCmd.NON_VERSIONED_CONFIG, 149 globalConfig, 150 skipJavaCheck); 151 // Get the non version part of the configuration in order to do proper allocation 152 // of devices and such. 153 config = 154 super.createConfigurationFromArgs( 155 new String[] {xmlConfig.getAbsolutePath()}, null, keyStoreClient); 156 // Reset the command line to the original one. 157 config.setCommandLine(args); 158 config.setConfigurationObject(Configuration.SANDBOX_TYPE_NAME, sandbox); 159 } catch (SandboxConfigurationException e) { 160 CLog.w("Using thin launcher as fallback"); 161 // Handle the thin launcher mode: Configuration does not exists in parent version yet. 162 config = sandbox.createThinLauncherConfig(args, keyStoreClient, runUtil, globalConfig); 163 if (config == null) { 164 // Rethrow the original exception. 165 CLog.e(e); 166 throw e; 167 } 168 } catch (IOException e) { 169 CLog.e(e); 170 throw new ConfigurationException("Failed to dump global config.", e); 171 } catch (ConfigurationException e) { 172 CLog.e(e); 173 throw e; 174 } finally { 175 if (config == null) { 176 // In case of error, tear down the sandbox. 177 sandbox.tearDown(); 178 } 179 FileUtil.deleteFile(globalConfig); 180 FileUtil.deleteFile(xmlConfig); 181 } 182 return config; 183 } 184 getKeyStoreClient()185 private IKeyStoreClient getKeyStoreClient() { 186 try { 187 IKeyStoreFactory f = GlobalConfiguration.getInstance().getKeyStoreFactory(); 188 if (f != null) { 189 try { 190 return f.createKeyStoreClient(); 191 } catch (KeyStoreException e) { 192 CLog.e("Failed to create key store client"); 193 CLog.e(e); 194 } 195 } 196 } catch (IllegalStateException e) { 197 CLog.w("Global configuration has not been created, failed to get keystore"); 198 CLog.e(e); 199 } 200 return null; 201 } 202 203 /** Returns the classpath of the current running Tradefed. */ createClasspath()204 private String createClasspath() throws ConfigurationException { 205 // Get the classpath property. 206 String classpathStr = System.getProperty("java.class.path"); 207 if (classpathStr == null) { 208 throw new ConfigurationException( 209 "Could not find the classpath property: java.class.path"); 210 } 211 return classpathStr; 212 } 213 214 private class RunSandboxConfigurationFactory extends SandboxConfigurationFactory { 215 216 private DumpCmd mCommand; 217 RunSandboxConfigurationFactory(DumpCmd command)218 RunSandboxConfigurationFactory(DumpCmd command) { 219 mCommand = command; 220 } 221 222 @Override createConfigurationDef(String name)223 protected ConfigurationDef createConfigurationDef(String name) { 224 return new ConfigurationDef(name) { 225 @Override 226 protected void checkRejectedObjects( 227 Map<String, String> rejectedObjects, Throwable cause) 228 throws ClassNotFoundConfigurationException { 229 if (mCommand.equals(DumpCmd.RUN_CONFIG) || mCommand.equals(DumpCmd.TEST_MODE)) { 230 Map<String, String> copyRejected = new HashMap<>(); 231 for (Entry<String, String> item : rejectedObjects.entrySet()) { 232 if (SandboxConfigDump.VERSIONED_ELEMENTS.contains(item.getValue())) { 233 copyRejected.put(item.getKey(), item.getValue()); 234 } 235 } 236 super.checkRejectedObjects(copyRejected, cause); 237 } else { 238 super.checkRejectedObjects(rejectedObjects, cause); 239 } 240 } 241 242 @Override 243 protected void injectOptions(IConfiguration config, List<OptionDef> optionList) 244 throws ConfigurationException { 245 List<OptionDef> individualAttempt = new ArrayList<>(); 246 if (mCommand.equals(DumpCmd.RUN_CONFIG) || mCommand.equals(DumpCmd.TEST_MODE)) { 247 individualAttempt = 248 optionList 249 .stream() 250 .filter( 251 o -> 252 (o.applicableObjectType != null 253 && !OPTION_IGNORED_ELEMENTS 254 .contains( 255 o.applicableObjectType))) 256 .collect(Collectors.toList()); 257 optionList.removeAll(individualAttempt); 258 } 259 super.injectOptions(config, optionList); 260 // Try individually each "filtered-option", they will not stop the run if they 261 // cannot be set since they are not part of the versioned process. 262 for (OptionDef item : individualAttempt) { 263 List<OptionDef> tmpList = Arrays.asList(item); 264 try { 265 super.injectOptions(config, tmpList); 266 } catch (ConfigurationException e) { 267 // Ignore 268 } 269 } 270 } 271 }; 272 } 273 } 274 } 275