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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.io.File; 025import java.io.FileOutputStream; 026import java.io.IOException; 027import java.io.PrintWriter; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.fs.FileSystem; 030import org.apache.hadoop.fs.Path; 031import org.apache.hadoop.hbase.HealthChecker.HealthCheckerExitStatus; 032import org.apache.hadoop.hbase.testclassification.MiscTests; 033import org.apache.hadoop.hbase.testclassification.SmallTests; 034import org.apache.hadoop.util.Shell; 035import org.junit.jupiter.api.AfterEach; 036import org.junit.jupiter.api.Tag; 037import org.junit.jupiter.api.Test; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041@Tag(MiscTests.TAG) 042@Tag(SmallTests.TAG) 043public class TestNodeHealthCheckChore { 044 045 private static final Logger LOG = LoggerFactory.getLogger(TestNodeHealthCheckChore.class); 046 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 047 private static final int SCRIPT_TIMEOUT = 5000; 048 private File healthScriptFile; 049 private String eol = System.getProperty("line.separator"); 050 051 @AfterEach 052 public void cleanUp() throws IOException { 053 // delete and recreate the test directory, ensuring a clean test dir between tests 054 Path testDir = UTIL.getDataTestDir(); 055 FileSystem fs = UTIL.getTestFileSystem(); 056 fs.delete(testDir, true); 057 if (!fs.mkdirs(testDir)) throw new IOException("Failed mkdir " + testDir); 058 } 059 060 @Test 061 public void testHealthCheckerSuccess() throws Exception { 062 String normalScript = "echo \"I am all fine\""; 063 healthCheckerTest(normalScript, HealthCheckerExitStatus.SUCCESS); 064 } 065 066 @Test 067 public void testHealthCheckerFail() throws Exception { 068 String errorScript = "echo ERROR" + eol + "echo \"Node not healthy\""; 069 healthCheckerTest(errorScript, HealthCheckerExitStatus.FAILED); 070 } 071 072 @Test 073 public void testHealthCheckerTimeout() throws Exception { 074 String timeOutScript = "sleep 10" + eol + "echo \"I am fine\""; 075 healthCheckerTest(timeOutScript, HealthCheckerExitStatus.TIMED_OUT); 076 } 077 078 public void healthCheckerTest(String script, HealthCheckerExitStatus expectedStatus) 079 throws Exception { 080 Configuration config = getConfForNodeHealthScript(); 081 config.addResource(healthScriptFile.getName()); 082 String location = healthScriptFile.getAbsolutePath(); 083 long timeout = config.getLong(HConstants.HEALTH_SCRIPT_TIMEOUT, SCRIPT_TIMEOUT); 084 085 HealthChecker checker = new HealthChecker(); 086 checker.init(location, timeout); 087 088 createScript(script, true); 089 HealthReport report = checker.checkHealth(); 090 assertEquals(expectedStatus, report.getStatus()); 091 092 LOG.info("Health Status:" + report.getHealthReport()); 093 094 this.healthScriptFile.delete(); 095 } 096 097 @Test 098 public void testRSHealthChore() throws Exception { 099 Stoppable stop = new StoppableImplementation(); 100 Configuration conf = getConfForNodeHealthScript(); 101 String errorScript = "echo ERROR" + eol + " echo \"Server not healthy\""; 102 createScript(errorScript, true); 103 HealthCheckChore rsChore = new HealthCheckChore(100, stop, conf); 104 try { 105 // Default threshold is three. 106 rsChore.chore(); 107 rsChore.chore(); 108 assertFalse(stop.isStopped(), "Stoppable must not be stopped."); 109 rsChore.chore(); 110 assertTrue(stop.isStopped(), "Stoppable must have been stopped."); 111 } finally { 112 stop.stop("Finished w/ test"); 113 } 114 } 115 116 private void createScript(String scriptStr, boolean setExecutable) throws Exception { 117 if (!this.healthScriptFile.exists()) { 118 if (!healthScriptFile.createNewFile()) { 119 throw new IOException("Failed create of " + this.healthScriptFile); 120 } 121 } 122 PrintWriter pw = new PrintWriter(new FileOutputStream(healthScriptFile)); 123 try { 124 pw.println(scriptStr); 125 pw.flush(); 126 } finally { 127 pw.close(); 128 } 129 healthScriptFile.setExecutable(setExecutable); 130 LOG.info("Created " + this.healthScriptFile + ", executable=" + setExecutable); 131 } 132 133 private Configuration getConfForNodeHealthScript() throws IOException { 134 Configuration conf = UTIL.getConfiguration(); 135 File tempDir = new File(UTIL.getDataTestDir().toString()); 136 if (!tempDir.exists()) { 137 if (!tempDir.mkdirs()) { 138 throw new IOException("Failed mkdirs " + tempDir); 139 } 140 } 141 String scriptName = 142 "HealthScript" + UTIL.getRandomUUID().toString() + (Shell.WINDOWS ? ".cmd" : ".sh"); 143 healthScriptFile = new File(tempDir.getAbsolutePath(), scriptName); 144 conf.set(HConstants.HEALTH_SCRIPT_LOC, healthScriptFile.getAbsolutePath()); 145 conf.setLong(HConstants.HEALTH_FAILURE_THRESHOLD, 3); 146 conf.setLong(HConstants.HEALTH_SCRIPT_TIMEOUT, SCRIPT_TIMEOUT); 147 return conf; 148 } 149 150 /** 151 * Simple helper class that just keeps track of whether or not its stopped. 152 */ 153 private static class StoppableImplementation implements Stoppable { 154 private volatile boolean stop = false; 155 156 @Override 157 public void stop(String why) { 158 this.stop = true; 159 } 160 161 @Override 162 public boolean isStopped() { 163 return this.stop; 164 } 165 166 } 167}