001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023import java.io.File; 024import java.io.IOException; 025import java.nio.file.Files; 026import java.nio.file.Path; 027import java.nio.file.Paths; 028import java.util.Optional; 029import java.util.function.Consumer; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.client.AsyncAdmin; 032import org.apache.hadoop.hbase.client.AsyncConnection; 033import org.apache.hadoop.hbase.coprocessor.protobuf.generated.ShellExecEndpoint.ShellExecRequest; 034import org.apache.hadoop.hbase.coprocessor.protobuf.generated.ShellExecEndpoint.ShellExecResponse; 035import org.apache.hadoop.hbase.coprocessor.protobuf.generated.ShellExecEndpoint.ShellExecService; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 037import org.junit.ClassRule; 038import org.junit.Rule; 039import org.junit.Test; 040import org.junit.experimental.categories.Category; 041 042/** 043 * Test for the {@link ShellExecEndpointCoprocessor}. 044 */ 045@Category(MediumTests.class) 046public class TestShellExecEndpointCoprocessor { 047 048 @ClassRule 049 public static final HBaseClassTestRule testRule = 050 HBaseClassTestRule.forClass(TestShellExecEndpointCoprocessor.class); 051 052 @ClassRule 053 public static final MiniClusterRule miniClusterRule = MiniClusterRule.newBuilder() 054 .setConfiguration(createConfiguration()) 055 .build(); 056 057 @Rule 058 public final ConnectionRule connectionRule = 059 new ConnectionRule(miniClusterRule::createConnection); 060 061 @Test 062 public void testShellExecUnspecified() { 063 testShellExecForeground(b -> {}); 064 } 065 066 @Test 067 public void testShellExecForeground() { 068 testShellExecForeground(b -> b.setAwaitResponse(true)); 069 } 070 071 private void testShellExecForeground(final Consumer<ShellExecRequest.Builder> consumer) { 072 final AsyncConnection conn = connectionRule.getConnection(); 073 final AsyncAdmin admin = conn.getAdmin(); 074 075 final String command = "echo -n \"hello world\""; 076 final ShellExecRequest.Builder builder = ShellExecRequest.newBuilder() 077 .setCommand(command); 078 consumer.accept(builder); 079 final ShellExecResponse resp = admin 080 .<ShellExecService.Stub, ShellExecResponse>coprocessorService( 081 ShellExecService::newStub, 082 (stub, controller, callback) -> stub.shellExec(controller, builder.build(), callback)) 083 .join(); 084 assertEquals(0, resp.getExitCode()); 085 assertEquals("hello world", resp.getStdout()); 086 } 087 088 @Test 089 public void testShellExecBackground() throws IOException { 090 final AsyncConnection conn = connectionRule.getConnection(); 091 final AsyncAdmin admin = conn.getAdmin(); 092 093 final File testDataDir = ensureTestDataDirExists(miniClusterRule.getTestingUtility()); 094 final File testFile = new File(testDataDir, "shell_exec_background.txt"); 095 assertTrue(testFile.createNewFile()); 096 assertEquals(0, testFile.length()); 097 098 final String command = "echo \"hello world\" >> " + testFile.getAbsolutePath(); 099 final ShellExecRequest req = ShellExecRequest.newBuilder() 100 .setCommand(command) 101 .setAwaitResponse(false) 102 .build(); 103 final ShellExecResponse resp = admin 104 .<ShellExecService.Stub, ShellExecResponse>coprocessorService( 105 ShellExecService::newStub, 106 (stub, controller, callback) -> stub.shellExec(controller, req, callback)) 107 .join(); 108 109 assertFalse("the response from a background task should have no exit code", resp.hasExitCode()); 110 assertFalse("the response from a background task should have no stdout", resp.hasStdout()); 111 assertFalse("the response from a background task should have no stderr", resp.hasStderr()); 112 113 Waiter.waitFor(conn.getConfiguration(), 5_000, () -> testFile.length() > 0); 114 final String content = new String(Files.readAllBytes(testFile.toPath())).trim(); 115 assertEquals("hello world", content); 116 } 117 118 private static File ensureTestDataDirExists( 119 final HBaseTestingUtility testingUtility 120 ) throws IOException { 121 final Path testDataDir = Optional.of(testingUtility) 122 .map(HBaseTestingUtility::getDataTestDir) 123 .map(Object::toString) 124 .map(val -> Paths.get(val)) 125 .orElseThrow(() -> new RuntimeException("Unable to locate temp directory path.")); 126 final File testDataDirFile = Files.createDirectories(testDataDir).toFile(); 127 assertTrue(testDataDirFile.exists()); 128 return testDataDirFile; 129 } 130 131 private static Configuration createConfiguration() { 132 final Configuration conf = HBaseConfiguration.create(); 133 conf.set("hbase.coprocessor.master.classes", ShellExecEndpointCoprocessor.class.getName()); 134 return conf; 135 } 136}