• 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.sandbox;
17 
18 import com.android.annotations.VisibleForTesting;
19 import com.android.tradefed.command.CommandRunner.ExitCode;
20 import com.android.tradefed.command.ICommandScheduler;
21 import com.android.tradefed.command.ICommandScheduler.IScheduledInvocationListener;
22 import com.android.tradefed.config.ConfigurationException;
23 import com.android.tradefed.config.GlobalConfiguration;
24 import com.android.tradefed.device.FreeDeviceState;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.device.NoDeviceException;
27 import com.android.tradefed.invoker.IInvocationContext;
28 import com.android.tradefed.invoker.InvocationContext;
29 import com.android.tradefed.invoker.proto.InvocationContext.Context;
30 import com.android.tradefed.util.FileUtil;
31 import com.android.tradefed.util.SerializationUtil;
32 
33 import org.json.JSONException;
34 import org.json.JSONObject;
35 
36 import java.io.File;
37 import java.io.FileInputStream;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.Map;
43 
44 /** Runner associated with a {@link TradefedSandbox} that will allow executing the sandbox. */
45 public class TradefedSandboxRunner {
46     public static final String EXCEPTION_KEY = "serialized_exception";
47 
48     private ICommandScheduler mScheduler;
49     private ExitCode mErrorCode = ExitCode.NO_ERROR;
50 
TradefedSandboxRunner()51     public TradefedSandboxRunner() {}
52 
getErrorCode()53     public ExitCode getErrorCode() {
54         return mErrorCode;
55     }
56 
57     /** Initialize the required global configuration. */
58     @VisibleForTesting
initGlobalConfig(String[] args)59     void initGlobalConfig(String[] args) throws ConfigurationException {
60         GlobalConfiguration.createGlobalConfiguration(args);
61     }
62 
63     /** Get the {@link ICommandScheduler} instance from the global configuration. */
64     @VisibleForTesting
getCommandScheduler()65     ICommandScheduler getCommandScheduler() {
66         return GlobalConfiguration.getInstance().getCommandScheduler();
67     }
68 
69     /** Prints the exception stack to stderr. */
70     @VisibleForTesting
printStackTrace(Throwable e)71     void printStackTrace(Throwable e) {
72         e.printStackTrace();
73         File serializedException = null;
74         try {
75             serializedException = SerializationUtil.serialize(e);
76             JSONObject json = new JSONObject();
77             json.put(EXCEPTION_KEY, serializedException.getAbsolutePath());
78             System.err.println(json.toString());
79         } catch (IOException | JSONException io) {
80             FileUtil.deleteFile(serializedException);
81         }
82     }
83 
84     /**
85      * The main method to run the command.
86      *
87      * @param args the config name to run and its options
88      */
run(String[] args)89     public void run(String[] args) {
90         List<String> argList = new ArrayList<>(Arrays.asList(args));
91         IInvocationContext context = null;
92 
93         if (argList.size() < 2) {
94             mErrorCode = ExitCode.THROWABLE_EXCEPTION;
95             printStackTrace(
96                     new RuntimeException("TradefedContainerRunner expect at least 2 args."));
97             return;
98         }
99 
100         File contextFile = new File(argList.remove(0));
101         try {
102             Context c = Context.parseDelimitedFrom(new FileInputStream(contextFile));
103             context = InvocationContext.fromProto(c);
104         } catch (IOException e) {
105             // Fallback to compatible old way
106             // TODO: Delete when parent has been deployed.
107             try {
108                 context = (IInvocationContext) SerializationUtil.deserialize(contextFile, false);
109             } catch (IOException e2) {
110                 printStackTrace(e);
111                 printStackTrace(e2);
112                 mErrorCode = ExitCode.THROWABLE_EXCEPTION;
113                 return;
114             }
115         }
116 
117         try {
118             initGlobalConfig(new String[] {});
119             mScheduler = getCommandScheduler();
120             mScheduler.start();
121             mScheduler.execCommand(
122                     context, new StubScheduledInvocationListener(), argList.toArray(new String[0]));
123         } catch (NoDeviceException e) {
124             printStackTrace(e);
125             mErrorCode = ExitCode.NO_DEVICE_ALLOCATED;
126         } catch (ConfigurationException e) {
127             printStackTrace(e);
128             mErrorCode = ExitCode.CONFIG_EXCEPTION;
129         } finally {
130             mScheduler.shutdownOnEmpty();
131         }
132         try {
133             mScheduler.join();
134             // If no error code has been raised yet, we checked the invocation error code.
135             if (ExitCode.NO_ERROR.equals(mErrorCode)) {
136                 mErrorCode = mScheduler.getLastInvocationExitCode();
137             }
138         } catch (InterruptedException e) {
139             e.printStackTrace();
140             mErrorCode = ExitCode.THROWABLE_EXCEPTION;
141         }
142         if (!ExitCode.NO_ERROR.equals(mErrorCode)
143                 && mScheduler.getLastInvocationThrowable() != null) {
144             // Print error to the stderr so that it can be recovered.
145             printStackTrace(mScheduler.getLastInvocationThrowable());
146         }
147     }
148 
main(final String[] mainArgs)149     public static void main(final String[] mainArgs) {
150         TradefedSandboxRunner console = new TradefedSandboxRunner();
151         console.run(mainArgs);
152         System.exit(console.getErrorCode().getCodeValue());
153     }
154 
155     /** A stub {@link IScheduledInvocationListener} that does nothing. */
156     public static class StubScheduledInvocationListener implements IScheduledInvocationListener {
157         @Override
invocationComplete( IInvocationContext metadata, Map<ITestDevice, FreeDeviceState> devicesStates)158         public void invocationComplete(
159                 IInvocationContext metadata, Map<ITestDevice, FreeDeviceState> devicesStates) {
160             // do nothing
161         }
162     }
163 }
164