• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         IConfiguration config = null;
114         File xmlConfig = null;
115         File globalConfig = null;
116         try {
117             runUtil.unsetEnvVariable(GlobalConfiguration.GLOBAL_CONFIG_VARIABLE);
118             // Dump the NON_VERSIONED part of the configuration against the current TF and not the
119             // sandboxed environment.
120             globalConfig = SandboxConfigUtil.dumpFilteredGlobalConfig(new HashSet<>());
121             xmlConfig =
122                     SandboxConfigUtil.dumpConfigForVersion(
123                             createClasspath(),
124                             runUtil,
125                             args,
126                             DumpCmd.NON_VERSIONED_CONFIG,
127                             globalConfig);
128             // Get the non version part of the configuration in order to do proper allocation
129             // of devices and such.
130             config =
131                     super.createConfigurationFromArgs(
132                             new String[] {xmlConfig.getAbsolutePath()}, null, keyStoreClient);
133             // Reset the command line to the original one.
134             config.setCommandLine(args);
135             config.setConfigurationObject(Configuration.SANDBOX_TYPE_NAME, sandbox);
136         } catch (SandboxConfigurationException e) {
137             // Handle the thin launcher mode: Configuration does not exists in parent version yet.
138             config = sandbox.createThinLauncherConfig(args, keyStoreClient, runUtil, globalConfig);
139             if (config == null) {
140                 // Rethrow the original exception.
141                 CLog.e(e);
142                 throw e;
143             }
144         } catch (IOException e) {
145             CLog.e(e);
146             throw new ConfigurationException("Failed to dump global config.", e);
147         } catch (ConfigurationException e) {
148             CLog.e(e);
149             throw e;
150         } finally {
151             if (config == null) {
152                 // In case of error, tear down the sandbox.
153                 sandbox.tearDown();
154             }
155             FileUtil.deleteFile(globalConfig);
156             FileUtil.deleteFile(xmlConfig);
157         }
158         return config;
159     }
160 
getKeyStoreClient()161     private IKeyStoreClient getKeyStoreClient() {
162         try {
163             IKeyStoreFactory f = GlobalConfiguration.getInstance().getKeyStoreFactory();
164             if (f != null) {
165                 try {
166                     return f.createKeyStoreClient();
167                 } catch (KeyStoreException e) {
168                     CLog.e("Failed to create key store client");
169                     CLog.e(e);
170                 }
171             }
172         } catch (IllegalStateException e) {
173             CLog.w("Global configuration has not been created, failed to get keystore");
174             CLog.e(e);
175         }
176         return null;
177     }
178 
179     /** Returns the classpath of the current running Tradefed. */
createClasspath()180     private String createClasspath() throws ConfigurationException {
181         // Get the classpath property.
182         String classpathStr = System.getProperty("java.class.path");
183         if (classpathStr == null) {
184             throw new ConfigurationException(
185                     "Could not find the classpath property: java.class.path");
186         }
187         return classpathStr;
188     }
189 
190     private class RunSandboxConfigurationFactory extends SandboxConfigurationFactory {
191 
192         private DumpCmd mCommand;
193 
RunSandboxConfigurationFactory(DumpCmd command)194         RunSandboxConfigurationFactory(DumpCmd command) {
195             mCommand = command;
196         }
197 
198         @Override
createConfigurationDef(String name)199         protected ConfigurationDef createConfigurationDef(String name) {
200             return new ConfigurationDef(name) {
201                 @Override
202                 protected void checkRejectedObjects(
203                         Map<String, String> rejectedObjects, Throwable cause)
204                         throws ClassNotFoundConfigurationException {
205                     if (mCommand.equals(DumpCmd.RUN_CONFIG) || mCommand.equals(DumpCmd.TEST_MODE)) {
206                         Map<String, String> copyRejected = new HashMap<>();
207                         for (Entry<String, String> item : rejectedObjects.entrySet()) {
208                             if (SandboxConfigDump.VERSIONED_ELEMENTS.contains(item.getValue())) {
209                                 copyRejected.put(item.getKey(), item.getValue());
210                             }
211                         }
212                         super.checkRejectedObjects(copyRejected, cause);
213                     } else {
214                         super.checkRejectedObjects(rejectedObjects, cause);
215                     }
216                 }
217 
218                 @Override
219                 protected void injectOptions(IConfiguration config, List<OptionDef> optionList)
220                         throws ConfigurationException {
221                     List<OptionDef> individualAttempt = new ArrayList<>();
222                     if (mCommand.equals(DumpCmd.RUN_CONFIG) || mCommand.equals(DumpCmd.TEST_MODE)) {
223                         individualAttempt =
224                                 optionList
225                                         .stream()
226                                         .filter(
227                                                 o ->
228                                                         (o.applicableObjectType != null
229                                                                 && !OPTION_IGNORED_ELEMENTS
230                                                                         .contains(
231                                                                                 o.applicableObjectType)))
232                                         .collect(Collectors.toList());
233                         optionList.removeAll(individualAttempt);
234                     }
235                     super.injectOptions(config, optionList);
236                     // Try individually each "filtered-option", they will not stop the run if they
237                     // cannot be set since they are not part of the versioned process.
238                     for (OptionDef item : individualAttempt) {
239                         List<OptionDef> tmpList = Arrays.asList(item);
240                         try {
241                             super.injectOptions(config, tmpList);
242                         } catch (ConfigurationException e) {
243                             // Ignore
244                         }
245                     }
246                 }
247             };
248         }
249     }
250 }
251